Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
31 changes: 18 additions & 13 deletions conan/api/model/refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,26 +173,31 @@ def matches(self, pattern, is_consumer):
:parameter str pattern: the pattern to match against, it can contain wildcards,
and can start with ``!`` or ``~`` to negate the match.
A special value of ``&`` will return a match only of ``is_consumer`` is ``True``
One can provide multiple patterns separated by | character.
The negation is supported only for the whole pattern colletion.
:parameter bool is_consumer: if ``True``, the pattern ``&`` will match this reference.
"""
negate = False
if pattern.startswith("!") or pattern.startswith("~"):
pattern = pattern[1:]
negate = True

no_user_channel = False
if pattern.endswith("@"): # it means we want to match only without user/channel
pattern = pattern[:-1]
no_user_channel = True
elif "@#" in pattern:
pattern = pattern.replace("@#", "#")
no_user_channel = True

condition = ((pattern == "&" and is_consumer) or
fnmatch.fnmatchcase(str(self), pattern) or
fnmatch.fnmatchcase(self.repr_notime(), pattern))
if no_user_channel:
condition = condition and not self.user and not self.channel
condition = False
for pattern_part in pattern.split("|"):
no_user_channel = False
if pattern_part.endswith("@"): # it means we want to match only without user/channel
pattern_part = pattern_part[:-1]
no_user_channel = True
elif "@#" in pattern_part:
pattern_part = pattern_part.replace("@#", "#")
no_user_channel = True

condition_part = ((pattern_part == "&" and is_consumer) or
fnmatch.fnmatchcase(str(self), pattern_part) or
fnmatch.fnmatchcase(self.repr_notime(), pattern_part))
if no_user_channel:
condition_part = condition_part and not self.user and not self.channel
condition |= condition_part
if negate:
return not condition
return condition
Expand Down
12 changes: 12 additions & 0 deletions test/unittests/model/test_recipe_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,15 @@ def test_error_pref():
with pytest.raises(ConanException) as exc:
r1.validate_ref()
assert "Invalid recipe reference 'pkg/1.0:pid' is a package reference" in str(exc)


@pytest.mark.parametrize("pattern", ["pkg/*", "pkg/*|other/*", "other/*|pkg/*", "!other/*", "!other1/*|other2/*", "!&", "&|pkg/*"])
def test_pattern_match(pattern):
r = RecipeReference.loads("pkg/0.1")
assert r.matches(pattern, False)


@pytest.mark.parametrize("pattern", ["other/*", "!pkg/*", "!other/*|pkg/*", "!pkg/*|other/*", "!&|pkg/*"])
def test_pattern_no_match(pattern):
r = RecipeReference.loads("pkg/0.1")
assert not r.matches(pattern, False)