WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions libs/shared/shared/reports/filtered.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ def is_empty(self):

def _iter_totals(self):
for filename in self.report._files.keys():
if self.should_include(filename):
res = self.get(filename).totals
if res and res.lines > 0:
yield res
report = self.get(filename)
res = report.totals if report else None
if res and res.lines > 0:
yield res

def _process_totals(self):
"""Runs through the file network to aggregate totals
Expand Down
63 changes: 39 additions & 24 deletions libs/shared/shared/utils/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,63 @@ class Matcher:
def __init__(self, patterns: Sequence[str] | None):
self._patterns = set(patterns or [])
self._is_initialized = False
# a list of patterns that will result in `True` on a match
self._positives: list[re.Pattern] = []
# a list of patterns that will result in `False` on a match
self._negatives: list[re.Pattern] = []
self._combined_positives: re.Pattern | None = None
self._combined_negatives: re.Pattern | None = None

def _get_matchers(self) -> tuple[list[re.Pattern], list[re.Pattern]]:
def _get_matchers(self) -> tuple[re.Pattern | None, re.Pattern | None]:
if not self._is_initialized:
positive_patterns = []
negative_patterns = []
for pattern in self._patterns:
if not pattern:
continue
if pattern.startswith(("^!", "!")):
self._negatives.append(re.compile(pattern.replace("!", "")))
negative_pattern = pattern.replace("!", "")
negative_patterns.append(negative_pattern)
else:
self._positives.append(re.compile(pattern))
positive_patterns.append(pattern)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
positive_patterns = []
negative_patterns = []
for pattern in self._patterns:
if not pattern:
continue
if pattern.startswith(("^!", "!")):
self._negatives.append(re.compile(pattern.replace("!", "")))
negative_pattern = pattern.replace("!", "")
negative_patterns.append(negative_pattern)
else:
self._positives.append(re.compile(pattern))
positive_patterns.append(pattern)
_patterns = [pattern for pattern in self._patterns if pattern]
_negative_patterns_set = set([pattern for pattern in _patterns if pattern.startswith(("^!", "!"))])
positive_patterns = [pattern for pattern in _patterns if pattern not in negative_patterns_set]
negative_patterns = [pattern.replace("!", "") for pattern in _negative_patterns_set]


# Combine positive patterns into a single regex for faster matching
if len(positive_patterns) > 0:
# Combine patterns with OR, each pattern is wrapped in parentheses to preserve anchors
combined_pattern = (
"|".join(f"({p})" for p in positive_patterns)
if len(positive_patterns) > 1
else positive_patterns[0]
)
self._combined_positives = re.compile(combined_pattern)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if len(positive_patterns) > 0:
# Combine patterns with OR, each pattern is wrapped in parentheses to preserve anchors
combined_pattern = (
"|".join(f"({p})" for p in positive_patterns)
if len(positive_patterns) > 1
else positive_patterns[0]
)
self._combined_positives = re.compile(combined_pattern)
if positive_patterns:
self._combined_positives = re.compile(
"|".join(positive_patterns)
)

Copy link
Contributor Author

@calvin-codecov calvin-codecov Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not need to preserve possible anchors here in the regex pattern with parens around each, @thomasrockhu-codecov?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@calvin-codecov sorry, not sure what you mean here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh nvm, there's actually no problem here


# Combine negative patterns into a single regex for faster matching
if len(negative_patterns) > 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

# Combine patterns with OR, each pattern is wrapped in parentheses to preserve anchors
combined_pattern = (
"|".join(f"({p})" for p in negative_patterns)
if len(negative_patterns) > 1
else negative_patterns[0]
)
self._combined_negatives = re.compile(combined_pattern)

self._is_initialized = True

return self._positives, self._negatives
return self._combined_positives, self._combined_negatives

def match(self, s: str) -> bool:
if not self._patterns or s in self._patterns:
return True

positives, negatives = self._get_matchers()
combined_positives, combined_negatives = self._get_matchers()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope for this PR, but can we add a ticket to just call self._get_matchers() at __init__ time? This means we don't need to call it here and can just pull self._combined_positives etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# must not match
for pattern in negatives:
# matched a negative search
if pattern.match(s):
# Check negatives first - if any match, return False
if combined_negatives:
if combined_negatives.match(s):
return False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if combined_negatives:
if combined_negatives.match(s):
return False
if combined_negatives is not None and combined_negatives.match(s):
return False


if positives:
for pattern in positives:
# match was found
if pattern.match(s):
return True
# Check positives - if any match, return True; if none match, return False
if combined_positives:
return bool(combined_positives.match(s))

# did not match any required paths
return False

else:
# no positives: everything else is ok
return True
# No positives: everything else is ok
return True
Comment on lines 51 to 55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you COULD do

return bool(combined_positives.match(s)) if combined_positives else True

but, that's a style thing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with keeping this in this form for clarity at quick glance as each of these 3 scenarios are explicit since we also have the combined_negatives clause right above


def match_any(self, strings: Sequence[str] | None) -> bool:
if not strings:
Expand Down
Loading