From ce1b04e1a57b2c993ffb7a51bbb0848c644b5971 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Tue, 20 Sep 2022 19:27:15 -0500 Subject: [PATCH 1/6] refactor(url): Matcher -> Rule, MatcherRegistry -> Rules --- README.md | 2 +- src/libvcs/url/base.py | 56 +++++++++++++------------- src/libvcs/url/git.py | 90 ++++++++++++++++++++---------------------- src/libvcs/url/hg.py | 50 +++++++++++------------ src/libvcs/url/svn.py | 42 ++++++++++---------- tests/url/test_git.py | 10 ++--- tests/url/test_hg.py | 6 +-- tests/url/test_svn.py | 6 +-- 8 files changed, 127 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 7ffd24ee8..4a06c9408 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ GitURL(url=git@github.com:vcs-python/libvcs.git, hostname=github.com, path=vcs-python/libvcs, suffix=.git, - matcher=core-git-scp) + rule=core-git-scp) ``` Switch repo libvcs -> vcspull: diff --git a/src/libvcs/url/base.py b/src/libvcs/url/base.py index d79545b75..ac784d3cd 100644 --- a/src/libvcs/url/base.py +++ b/src/libvcs/url/base.py @@ -23,8 +23,8 @@ def is_valid(self, url: str, is_explicit: Optional[bool] = None) -> bool: @dataclasses.dataclass(repr=False) -class Matcher(SkipDefaultFieldsReprMixin): - """Structure for a matcher""" +class Rule(SkipDefaultFieldsReprMixin): + """Structure for a rule""" label: str """Computer readable name / ID""" @@ -38,12 +38,12 @@ class Matcher(SkipDefaultFieldsReprMixin): @dataclasses.dataclass(repr=False) -class MatcherRegistry(SkipDefaultFieldsReprMixin): +class Rules(SkipDefaultFieldsReprMixin): """Pattern matching and parsing capabilities for URL parsers, e.g. GitURL""" - _matchers: dict[str, Matcher] = dataclasses.field(default_factory=dict) + _rules: dict[str, Rule] = dataclasses.field(default_factory=dict) - def register(self, cls: Matcher) -> None: + def register(self, cls: Rule) -> None: r""" .. currentmodule:: libvcs.url.git @@ -72,7 +72,7 @@ def register(self, cls: Matcher) -> None: GitURL(url=github:org/repo, hostname=github, path=org/repo, - matcher=core-git-scp) + rule=core-git-scp) >>> GitURL(url="github:org/repo").to_url() 'git@github:org/repo' @@ -84,7 +84,7 @@ def register(self, cls: Matcher) -> None: **Extending matching capability:** - >>> class GitHubPrefix(Matcher): + >>> class GitHubPrefix(Rule): ... label = 'gh-prefix' ... description ='Matches prefixes like github:org/repo' ... pattern = r'^github:(?P.*)$' @@ -97,8 +97,8 @@ def register(self, cls: Matcher) -> None: >>> @dataclasses.dataclass(repr=False) ... class GitHubURL(GitURL): - ... matchers: MatcherRegistry = MatcherRegistry( - ... _matchers={'github_prefix': GitHubPrefix} + ... rules: Rules = Rules( + ... _rules={'github_prefix': GitHubPrefix} ... ) >>> GitHubURL.is_valid(url='github:vcs-python/libvcs') @@ -114,7 +114,7 @@ def register(self, cls: Matcher) -> None: scheme=https, hostname=github.com, path=vcs-python/libvcs, - matcher=gh-prefix) + rule=gh-prefix) >>> GitHubURL(url='github:vcs-python/libvcs').to_url() 'https://github.com/vcs-python/libvcs' @@ -122,7 +122,7 @@ def register(self, cls: Matcher) -> None: >>> GitHubURL.is_valid(url='gitlab:vcs-python/libvcs') False - ``GitHubURL`` sees this as invalid since it only has one matcher, + ``GitHubURL`` sees this as invalid since it only has one rule, ``GitHubPrefix``. >>> GitURL.is_valid(url='gitlab:vcs-python/libvcs') @@ -130,10 +130,10 @@ def register(self, cls: Matcher) -> None: Same story, getting caught in ``git(1)``'s own liberal scp-style URL: - >>> GitURL(url='gitlab:vcs-python/libvcs').matcher + >>> GitURL(url='gitlab:vcs-python/libvcs').rule 'core-git-scp' - >>> class GitLabPrefix(Matcher): + >>> class GitLabPrefix(Rule): ... label = 'gl-prefix' ... description ='Matches prefixes like gitlab:org/repo' ... pattern = r'^gitlab:(?P)' @@ -143,12 +143,12 @@ def register(self, cls: Matcher) -> None: ... 'suffix': '.git' ... } - Option 1: Create a brand new matcher + Option 1: Create a brand new rule >>> @dataclasses.dataclass(repr=False) ... class GitLabURL(GitURL): - ... matchers: MatcherRegistry = MatcherRegistry( - ... _matchers={'gitlab_prefix': GitLabPrefix} + ... rules: Rules = Rules( + ... _rules={'gitlab_prefix': GitLabPrefix} ... ) >>> GitLabURL.is_valid(url='gitlab:vcs-python/libvcs') @@ -161,12 +161,12 @@ def register(self, cls: Matcher) -> None: Are we home free, though? Remember our issue with vague matches. - >>> GitURL(url='gitlab:vcs-python/libvcs').matcher + >>> GitURL(url='gitlab:vcs-python/libvcs').rule 'core-git-scp' Register: - >>> GitURL.matchers.register(GitLabPrefix) + >>> GitURL.rules.register(GitLabPrefix) >>> GitURL.is_valid(url='gitlab:vcs-python/libvcs') True @@ -183,8 +183,8 @@ def register(self, cls: Matcher) -> None: >>> @dataclasses.dataclass(repr=False) ... class GitURLWithPip(GitBaseURL): - ... matchers: MatcherRegistry = MatcherRegistry( - ... _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + ... rules: Rules = Rules( + ... _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ... ) >>> GitURLWithPip.is_valid(url="git+ssh://git@github.com/tony/AlgoXY.git") @@ -197,19 +197,19 @@ def register(self, cls: Matcher) -> None: hostname=github.com, path=tony/AlgoXY, suffix=.git, - matcher=pip-url) + rule=pip-url) """ # NOQA: E501 - if cls.label not in self._matchers: - self._matchers[cls.label] = cls + if cls.label not in self._rules: + self._rules[cls.label] = cls def unregister(self, label: str) -> None: - if label in self._matchers: - del self._matchers[label] + if label in self._rules: + del self._rules[label] def __iter__(self) -> Iterator[str]: - return self._matchers.__iter__() + return self._rules.__iter__() def values( self, # https://github.com/python/typing/discussions/1033 - ) -> "dict_values[str, Matcher]": - return self._matchers.values() + ) -> "dict_values[str, Rule]": + return self._rules.values() diff --git a/src/libvcs/url/git.py b/src/libvcs/url/git.py index 76636cda0..c29b7e2b2 100644 --- a/src/libvcs/url/git.py +++ b/src/libvcs/url/git.py @@ -12,8 +12,8 @@ - Strict ``git(1)`` compatibility: :class:`GitBaseURL`. - Output ``git(1)`` URL: :meth:`GitBaseURL.to_url()` -- Extendable via :class:`~libvcs.url.base.MatcherRegistry`, - :class:`~libvcs.url.base.Matcher` +- Extendable via :class:`~libvcs.url.base.Rules`, + :class:`~libvcs.url.base.Rule` """ import dataclasses @@ -22,7 +22,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Matcher, MatcherRegistry, URLProtocol +from .base import Rule, Rules, URLProtocol # Credit, pip (license: MIT): # https://github.com/pypa/pip/blob/22.1.2/src/pip/_internal/vcs/git.py#L39-L52 @@ -60,8 +60,8 @@ # Some https repos have .git at the end, e.g. https://github.com/org/repo.git -DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="core-git-https", description="Vanilla git pattern, URL ending with optional .git suffix", pattern=re.compile( @@ -76,7 +76,7 @@ ), # ends with .git. Including ones starting with https:// # e.g. https://github.com/vcs-python/libvcs.git - Matcher( + Rule( label="core-git-scp", description="Vanilla scp(1) / ssh(1) type URL", pattern=re.compile( @@ -125,8 +125,8 @@ """ -PIP_DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +PIP_DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="pip-url", description="pip-style git URL", pattern=re.compile( @@ -141,7 +141,7 @@ ), is_explicit=True, ), - Matcher( + Rule( label="pip-scp-url", description="pip-style git ssh/scp URL", pattern=re.compile( @@ -156,7 +156,7 @@ is_explicit=True, ), # file://, RTC 8089, File:// https://datatracker.ietf.org/doc/html/rfc8089 - Matcher( + Rule( label="pip-file-url", description="pip-style git+file:// URL", pattern=re.compile( @@ -191,7 +191,7 @@ - https://pip.pypa.io/en/stable/topics/vcs-support/ """ # NOQA: E501 -NPM_DEFAULT_MATCHERS: list[Matcher] = [] +NPM_DEFAULT_MATCHERS: list[Rule] = [] """NPM-style git URLs. Git URL pattern (from docs.npmjs.com):: @@ -224,7 +224,7 @@ class GitBaseURL(URLProtocol, SkipDefaultFieldsReprMixin): hostname=github.com, path=vcs-python/libvcs, suffix=.git, - matcher=core-git-https) + rule=core-git-https) >>> myrepo = GitBaseURL(url='https://github.com/myproject/myrepo.git') @@ -240,15 +240,15 @@ class GitBaseURL(URLProtocol, SkipDefaultFieldsReprMixin): hostname=github.com, path=vcs-python/libvcs, suffix=.git, - matcher=core-git-scp) + rule=core-git-scp) - Compatibility checking: :meth:`GitBaseURL.is_valid()` - URLs compatible with ``git(1)``: :meth:`GitBaseURL.to_url()` Attributes ---------- - matcher : str - name of the :class:`~libvcs.url.base.Matcher` + rule : str + name of the :class:`~libvcs.url.base.Rule` """ url: str @@ -261,25 +261,23 @@ class GitBaseURL(URLProtocol, SkipDefaultFieldsReprMixin): # Decoration suffix: Optional[str] = None - matcher: Optional[str] = None - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in DEFAULT_MATCHERS} - ) + rule: Optional[str] = None + rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for matcher in self.matchers.values(): - match = re.match(matcher.pattern, url) + for rule in self.rules.values(): + match = re.match(rule.pattern, url) if match is None: continue groups = match.groupdict() - setattr(self, "matcher", matcher.label) + setattr(self, "rule", rule.label) for k, v in groups.items(): setattr(self, k, v) - for k, v in matcher.pattern_defaults.items(): + for k, v in rule.pattern_defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, matcher.pattern_defaults[k]) + setattr(self, k, rule.pattern_defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: @@ -312,11 +310,11 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: """ if is_explicit is not None: return any( - re.search(matcher.pattern, url) - for matcher in cls.matchers.values() - if matcher.is_explicit == is_explicit + re.search(rule.pattern, url) + for rule in cls.rules.values() + if rule.is_explicit == is_explicit ) - return any(re.search(matcher.pattern, url) for matcher in cls.matchers.values()) + return any(re.search(rule.pattern, url) for rule in cls.rules.values()) def to_url(self) -> str: """Return a ``git(1)``-compatible URL. Can be used with ``git clone``. @@ -332,7 +330,7 @@ def to_url(self) -> str: hostname=github.com, path=vcs-python/libvcs, suffix=.git, - matcher=core-git-scp) + rule=core-git-scp) Switch repo libvcs -> vcspull: @@ -372,9 +370,7 @@ class GitPipURL(GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): # commit-ish (rev): tag, branch, ref rev: Optional[str] = None - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in PIP_DEFAULT_MATCHERS} - ) + rules: Rules = Rules(_rules={m.label: m for m in PIP_DEFAULT_MATCHERS}) def to_url(self) -> str: """Exports a pip-compliant URL. @@ -394,7 +390,7 @@ def to_url(self) -> str: port=7999, path=PROJ/repo, suffix=.git, - matcher=pip-url) + rule=pip-url) >>> git_url.path = 'libvcs/vcspull' @@ -413,7 +409,7 @@ def to_url(self) -> str: hostname=github.com, path=vcs-python/libvcs, suffix=.git, - matcher=pip-url, + rule=pip-url, rev=v0.10.0) >>> git_url.path = 'libvcs/vcspull' @@ -456,7 +452,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: **Explicit VCS detection** - Pip-style URLs are prefixed with the VCS name in front, so its matchers can + Pip-style URLs are prefixed with the VCS name in front, so its rules can unambigously narrow the type of VCS: >>> GitPipURL.is_valid( @@ -482,13 +478,13 @@ class GitURL(GitPipURL, GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): - :meth:`GitBaseURL.to_url` """ - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rules: Rules = Rules( + _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: - r"""Whether URL is compatible included Git URL matchers or not. + r"""Whether URL is compatible included Git URL rules or not. Examples -------- @@ -514,7 +510,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: **Explicit VCS detection** - Pip-style URLs are prefixed with the VCS name in front, so its matchers can + Pip-style URLs are prefixed with the VCS name in front, so its rules can unambigously narrow the type of VCS: >>> GitURL.is_valid( @@ -530,12 +526,12 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: ... ) False - You could create a GitHub matcher that consider github.com hostnames to be + You could create a GitHub rule that consider github.com hostnames to be exclusively git: - >>> GitHubMatcher = Matcher( + >>> GitHubRule = Rule( ... # Since github.com exclusively serves git repos, make explicit - ... label='gh-matcher', + ... label='gh-rule', ... description='Matches github.com https URLs, exact VCS match', ... pattern=re.compile( ... rf''' @@ -553,21 +549,21 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: ... } ... ) - >>> GitURL.matchers.register(GitHubMatcher) + >>> GitURL.rules.register(GitHubRule) >>> GitURL.is_valid( ... url='git@github.com:vcs-python/libvcs.git', is_explicit=True ... ) True - >>> GitURL(url='git@github.com:vcs-python/libvcs.git').matcher - 'gh-matcher' + >>> GitURL(url='git@github.com:vcs-python/libvcs.git').rule + 'gh-rule' This is just us cleaning up: - >>> GitURL.matchers.unregister('gh-matcher') + >>> GitURL.rules.unregister('gh-rule') - >>> GitURL(url='git@github.com:vcs-python/libvcs.git').matcher + >>> GitURL(url='git@github.com:vcs-python/libvcs.git').rule 'core-git-scp' """ return super().is_valid(url=url, is_explicit=is_explicit) diff --git a/src/libvcs/url/hg.py b/src/libvcs/url/hg.py index 412f1cee6..560724f1f 100644 --- a/src/libvcs/url/hg.py +++ b/src/libvcs/url/hg.py @@ -6,8 +6,8 @@ compare to :class:`urllib.parse.ParseResult` - Output ``hg(1)`` URL: :meth:`HgURL.to_url()` -- Extendable via :class:`~libvcs.url.base.MatcherRegistry`, - :class:`~libvcs.url.base.Matcher` +- Extendable via :class:`~libvcs.url.base.Rules`, + :class:`~libvcs.url.base.Rule` .. Note:: @@ -23,7 +23,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Matcher, MatcherRegistry, URLProtocol +from .base import Rule, Rules, URLProtocol RE_PATH = r""" ((?P\w+)@)? @@ -43,8 +43,8 @@ ) """ -DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="core-hg", description="Vanilla hg pattern", pattern=re.compile( @@ -74,8 +74,8 @@ ) """ -PIP_DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +PIP_DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="pip-url", description="pip-style hg URL", pattern=re.compile( @@ -88,7 +88,7 @@ ), ), # file://, RTC 8089, File:// https://datatracker.ietf.org/doc/html/rfc8089 - Matcher( + Rule( label="pip-file-url", description="pip-style hg+file:// URL", pattern=re.compile( @@ -129,8 +129,8 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): Attributes ---------- - matcher : str - name of the :class:`~libvcs.url.base.Matcher` + rule : str + name of the :class:`~libvcs.url.base.Rule` Examples -------- @@ -139,7 +139,7 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): scheme=https, hostname=hg.mozilla.org, path=mozilla-central/, - matcher=core-hg) + rule=core-hg) >>> myrepo = HgURL(url='https://hg.mozilla.org/mozilla-central/') @@ -155,7 +155,7 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): user=username, hostname=machinename, path=path/to/repo, - matcher=core-hg) + rule=core-hg) - Compatibility checking: :meth:`HgURL.is_valid()` - URLs compatible with ``hg(1)``: :meth:`HgURL.to_url()` @@ -174,26 +174,24 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): # ref: Optional[str] = None - matcher: Optional[str] = None - # name of the :class:`Matcher` - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in DEFAULT_MATCHERS} - ) + rule: Optional[str] = None + # name of the :class:`Rule` + rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for matcher in self.matchers.values(): - match = re.match(matcher.pattern, url) + for rule in self.rules.values(): + match = re.match(rule.pattern, url) if match is None: continue groups = match.groupdict() - setattr(self, "matcher", matcher.label) + setattr(self, "rule", rule.label) for k, v in groups.items(): setattr(self, k, v) - for k, v in matcher.pattern_defaults.items(): + for k, v in rule.pattern_defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, matcher.pattern_defaults[k]) + setattr(self, k, rule.pattern_defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: @@ -213,7 +211,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: >>> HgURL.is_valid(url='notaurl') False """ - return any(re.search(matcher.pattern, url) for matcher in cls.matchers.values()) + return any(re.search(rule.pattern, url) for rule in cls.rules.values()) def to_url(self) -> str: """Return a ``hg(1)``-compatible URL. Can be used with ``hg clone``. @@ -228,7 +226,7 @@ def to_url(self) -> str: scheme=https, hostname=hg.mozilla.org, path=mozilla-central, - matcher=core-hg) + rule=core-hg) Switch repo libvcs -> vcspull: @@ -255,7 +253,7 @@ def to_url(self) -> str: hostname=hugin.hg.sourceforge.net, port=8000, path=hgroot/hugin/hugin, - matcher=core-hg) + rule=core-hg) >>> hugin.to_url() 'http://hugin.hg.sourceforge.net:8000/hgroot/hugin/hugin' @@ -272,7 +270,7 @@ def to_url(self) -> str: user=yourid, hostname=hg.GraphicsMagick.org, path=/hg/GraphicsMagick, - matcher=core-hg) + rule=core-hg) >>> graphicsmagick.to_url() 'ssh://yourid@hg.GraphicsMagick.org//hg/GraphicsMagick' diff --git a/src/libvcs/url/svn.py b/src/libvcs/url/svn.py index ddafb3a8d..f58210a7d 100644 --- a/src/libvcs/url/svn.py +++ b/src/libvcs/url/svn.py @@ -6,8 +6,8 @@ compare to :class:`urllib.parse.ParseResult` - Output ``svn(1)`` URL: :meth:`SvnURL.to_url()` -- Extendable via :class:`~libvcs.url.base.MatcherRegistry`, - :class:`~libvcs.url.base.Matcher` +- Extendable via :class:`~libvcs.url.base.Rules`, + :class:`~libvcs.url.base.Rule` .. Note:: @@ -24,7 +24,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Matcher, MatcherRegistry, URLProtocol +from .base import Rule, Rules, URLProtocol RE_PATH = r""" ((?P.*)@)? @@ -47,8 +47,8 @@ ) """ -DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="core-svn", description="Vanilla svn pattern", pattern=re.compile( @@ -78,8 +78,8 @@ ) """ -PIP_DEFAULT_MATCHERS: list[Matcher] = [ - Matcher( +PIP_DEFAULT_MATCHERS: list[Rule] = [ + Rule( label="pip-url", description="pip-style svn URL", pattern=re.compile( @@ -92,7 +92,7 @@ ), ), # file://, RTC 8089, File:// https://datatracker.ietf.org/doc/html/rfc8089 - Matcher( + Rule( label="pip-file-url", description="pip-style svn+file:// URL", pattern=re.compile( @@ -135,7 +135,7 @@ class SvnURL(URLProtocol, SkipDefaultFieldsReprMixin): scheme=svn+ssh, hostname=svn.debian.org, path=svn/aliothproj/path/in/project/repository, - matcher=core-svn) + rule=core-svn) >>> myrepo = SvnURL( ... url='svn+ssh://svn.debian.org/svn/aliothproj/path/in/project/repository' @@ -152,8 +152,8 @@ class SvnURL(URLProtocol, SkipDefaultFieldsReprMixin): Attributes ---------- - matcher : str - name of the :class:`~libvcs.url.base.Matcher` + rule : str + name of the :class:`~libvcs.url.base.Rule` """ url: str @@ -169,25 +169,23 @@ class SvnURL(URLProtocol, SkipDefaultFieldsReprMixin): # ref: Optional[str] = None - matcher: Optional[str] = None - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in DEFAULT_MATCHERS} - ) + rule: Optional[str] = None + rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for matcher in self.matchers.values(): - match = re.match(matcher.pattern, url) + for rule in self.rules.values(): + match = re.match(rule.pattern, url) if match is None: continue groups = match.groupdict() - setattr(self, "matcher", matcher.label) + setattr(self, "rule", rule.label) for k, v in groups.items(): setattr(self, k, v) - for k, v in matcher.pattern_defaults.items(): + for k, v in rule.pattern_defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, matcher.pattern_defaults[k]) + setattr(self, k, rule.pattern_defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: @@ -204,7 +202,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: >>> SvnURL.is_valid(url='notaurl') False """ - return any(re.search(matcher.pattern, url) for matcher in cls.matchers.values()) + return any(re.search(rule.pattern, url) for rule in cls.rules.values()) def to_url(self) -> str: """Return a ``svn(1)``-compatible URL. Can be used with ``svn checkout``. @@ -222,7 +220,7 @@ def to_url(self) -> str: user=my-username, hostname=my-server, path=vcs-python/libvcs, - matcher=core-svn) + rule=core-svn) Switch repo libvcs -> vcspull: diff --git a/tests/url/test_git.py b/tests/url/test_git.py index 76102863d..e7d007a56 100644 --- a/tests/url/test_git.py +++ b/tests/url/test_git.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.git import GitSync -from libvcs.url.base import MatcherRegistry +from libvcs.url.base import Rules from libvcs.url.git import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, GitBaseURL, GitURL @@ -142,8 +142,8 @@ def test_git_url_extension_pip( git_repo: GitSync, ) -> None: class GitURLWithPip(GitBaseURL): - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rules: Rules = Rules( + _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) git_url_kwargs["url"] = git_url_kwargs["url"].format(local_repo=git_repo.dir) @@ -255,8 +255,8 @@ def test_git_revs( git_url_kwargs: GitURLKwargs, ) -> None: class GitURLWithPip(GitURL): - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rules: Rules = Rules( + _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) git_url = GitURLWithPip(**git_url_kwargs) diff --git a/tests/url/test_hg.py b/tests/url/test_hg.py index 283a4e1a5..3d5e0576f 100644 --- a/tests/url/test_hg.py +++ b/tests/url/test_hg.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.hg import HgSync -from libvcs.url.base import MatcherRegistry +from libvcs.url.base import Rules from libvcs.url.hg import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, HgURL @@ -107,8 +107,8 @@ def test_hg_url_extension_pip( hg_repo: HgSync, ) -> None: class HgURLWithPip(HgURL): - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rules: Rules = Rules( + _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) hg_url_kwargs["url"] = hg_url_kwargs["url"].format(local_repo=hg_repo.dir) diff --git a/tests/url/test_svn.py b/tests/url/test_svn.py index 0a607c7a6..104c9c4ec 100644 --- a/tests/url/test_svn.py +++ b/tests/url/test_svn.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.svn import SvnSync -from libvcs.url.base import MatcherRegistry +from libvcs.url.base import Rules from libvcs.url.svn import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, SvnURL @@ -124,8 +124,8 @@ def test_svn_url_extension_pip( svn_repo: SvnSync, ) -> None: class SvnURLWithPip(SvnURL): - matchers: MatcherRegistry = MatcherRegistry( - _matchers={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rules: Rules = Rules( + _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) svn_url_kwargs["url"] = svn_url_kwargs["url"].format(local_repo=svn_repo.dir) From b9bd0f5dbbf2663e6797b02cdab1e42df9bcbfe4 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Tue, 20 Sep 2022 19:44:44 -0500 Subject: [PATCH 2/6] refactor!(url): rules -> rule_map --- src/libvcs/url/base.py | 30 +++++++++++++++--------------- src/libvcs/url/git.py | 28 ++++++++++++++-------------- src/libvcs/url/hg.py | 10 +++++----- src/libvcs/url/svn.py | 10 +++++----- tests/url/test_git.py | 10 +++++----- tests/url/test_hg.py | 6 +++--- tests/url/test_svn.py | 6 +++--- 7 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/libvcs/url/base.py b/src/libvcs/url/base.py index ac784d3cd..5ac446008 100644 --- a/src/libvcs/url/base.py +++ b/src/libvcs/url/base.py @@ -38,10 +38,10 @@ class Rule(SkipDefaultFieldsReprMixin): @dataclasses.dataclass(repr=False) -class Rules(SkipDefaultFieldsReprMixin): +class RuleMap(SkipDefaultFieldsReprMixin): """Pattern matching and parsing capabilities for URL parsers, e.g. GitURL""" - _rules: dict[str, Rule] = dataclasses.field(default_factory=dict) + _rule_map: dict[str, Rule] = dataclasses.field(default_factory=dict) def register(self, cls: Rule) -> None: r""" @@ -97,8 +97,8 @@ def register(self, cls: Rule) -> None: >>> @dataclasses.dataclass(repr=False) ... class GitHubURL(GitURL): - ... rules: Rules = Rules( - ... _rules={'github_prefix': GitHubPrefix} + ... rule_map: RuleMap = RuleMap( + ... _rule_map={'github_prefix': GitHubPrefix} ... ) >>> GitHubURL.is_valid(url='github:vcs-python/libvcs') @@ -147,8 +147,8 @@ def register(self, cls: Rule) -> None: >>> @dataclasses.dataclass(repr=False) ... class GitLabURL(GitURL): - ... rules: Rules = Rules( - ... _rules={'gitlab_prefix': GitLabPrefix} + ... rule_map: RuleMap = RuleMap( + ... _rule_map={'gitlab_prefix': GitLabPrefix} ... ) >>> GitLabURL.is_valid(url='gitlab:vcs-python/libvcs') @@ -166,7 +166,7 @@ def register(self, cls: Rule) -> None: Register: - >>> GitURL.rules.register(GitLabPrefix) + >>> GitURL.rule_map.register(GitLabPrefix) >>> GitURL.is_valid(url='gitlab:vcs-python/libvcs') True @@ -183,8 +183,8 @@ def register(self, cls: Rule) -> None: >>> @dataclasses.dataclass(repr=False) ... class GitURLWithPip(GitBaseURL): - ... rules: Rules = Rules( - ... _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + ... rule_map: RuleMap = RuleMap( + ... _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ... ) >>> GitURLWithPip.is_valid(url="git+ssh://git@github.com/tony/AlgoXY.git") @@ -199,17 +199,17 @@ def register(self, cls: Rule) -> None: suffix=.git, rule=pip-url) """ # NOQA: E501 - if cls.label not in self._rules: - self._rules[cls.label] = cls + if cls.label not in self._rule_map: + self._rule_map[cls.label] = cls def unregister(self, label: str) -> None: - if label in self._rules: - del self._rules[label] + if label in self._rule_map: + del self._rule_map[label] def __iter__(self) -> Iterator[str]: - return self._rules.__iter__() + return self._rule_map.__iter__() def values( self, # https://github.com/python/typing/discussions/1033 ) -> "dict_values[str, Rule]": - return self._rules.values() + return self._rule_map.values() diff --git a/src/libvcs/url/git.py b/src/libvcs/url/git.py index c29b7e2b2..2891d7495 100644 --- a/src/libvcs/url/git.py +++ b/src/libvcs/url/git.py @@ -12,7 +12,7 @@ - Strict ``git(1)`` compatibility: :class:`GitBaseURL`. - Output ``git(1)`` URL: :meth:`GitBaseURL.to_url()` -- Extendable via :class:`~libvcs.url.base.Rules`, +- Extendable via :class:`~libvcs.url.base.RuleMap`, :class:`~libvcs.url.base.Rule` """ @@ -22,7 +22,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Rule, Rules, URLProtocol +from .base import Rule, RuleMap, URLProtocol # Credit, pip (license: MIT): # https://github.com/pypa/pip/blob/22.1.2/src/pip/_internal/vcs/git.py#L39-L52 @@ -262,11 +262,11 @@ class GitBaseURL(URLProtocol, SkipDefaultFieldsReprMixin): suffix: Optional[str] = None rule: Optional[str] = None - rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for rule in self.rules.values(): + for rule in self.rule_map.values(): match = re.match(rule.pattern, url) if match is None: continue @@ -311,10 +311,10 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: if is_explicit is not None: return any( re.search(rule.pattern, url) - for rule in cls.rules.values() + for rule in cls.rule_map.values() if rule.is_explicit == is_explicit ) - return any(re.search(rule.pattern, url) for rule in cls.rules.values()) + return any(re.search(rule.pattern, url) for rule in cls.rule_map.values()) def to_url(self) -> str: """Return a ``git(1)``-compatible URL. Can be used with ``git clone``. @@ -370,7 +370,7 @@ class GitPipURL(GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): # commit-ish (rev): tag, branch, ref rev: Optional[str] = None - rules: Rules = Rules(_rules={m.label: m for m in PIP_DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in PIP_DEFAULT_MATCHERS}) def to_url(self) -> str: """Exports a pip-compliant URL. @@ -452,7 +452,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: **Explicit VCS detection** - Pip-style URLs are prefixed with the VCS name in front, so its rules can + Pip-style URLs are prefixed with the VCS name in front, so its rule_map can unambigously narrow the type of VCS: >>> GitPipURL.is_valid( @@ -478,13 +478,13 @@ class GitURL(GitPipURL, GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): - :meth:`GitBaseURL.to_url` """ - rules: Rules = Rules( - _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rule_map: RuleMap = RuleMap( + _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: - r"""Whether URL is compatible included Git URL rules or not. + r"""Whether URL is compatible included Git URL rule_map or not. Examples -------- @@ -510,7 +510,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: **Explicit VCS detection** - Pip-style URLs are prefixed with the VCS name in front, so its rules can + Pip-style URLs are prefixed with the VCS name in front, so its rule_map can unambigously narrow the type of VCS: >>> GitURL.is_valid( @@ -549,7 +549,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: ... } ... ) - >>> GitURL.rules.register(GitHubRule) + >>> GitURL.rule_map.register(GitHubRule) >>> GitURL.is_valid( ... url='git@github.com:vcs-python/libvcs.git', is_explicit=True @@ -561,7 +561,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: This is just us cleaning up: - >>> GitURL.rules.unregister('gh-rule') + >>> GitURL.rule_map.unregister('gh-rule') >>> GitURL(url='git@github.com:vcs-python/libvcs.git').rule 'core-git-scp' diff --git a/src/libvcs/url/hg.py b/src/libvcs/url/hg.py index 560724f1f..373eb44be 100644 --- a/src/libvcs/url/hg.py +++ b/src/libvcs/url/hg.py @@ -6,7 +6,7 @@ compare to :class:`urllib.parse.ParseResult` - Output ``hg(1)`` URL: :meth:`HgURL.to_url()` -- Extendable via :class:`~libvcs.url.base.Rules`, +- Extendable via :class:`~libvcs.url.base.RuleMap`, :class:`~libvcs.url.base.Rule` .. Note:: @@ -23,7 +23,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Rule, Rules, URLProtocol +from .base import Rule, RuleMap, URLProtocol RE_PATH = r""" ((?P\w+)@)? @@ -176,11 +176,11 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): rule: Optional[str] = None # name of the :class:`Rule` - rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for rule in self.rules.values(): + for rule in self.rule_map.values(): match = re.match(rule.pattern, url) if match is None: continue @@ -211,7 +211,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: >>> HgURL.is_valid(url='notaurl') False """ - return any(re.search(rule.pattern, url) for rule in cls.rules.values()) + return any(re.search(rule.pattern, url) for rule in cls.rule_map.values()) def to_url(self) -> str: """Return a ``hg(1)``-compatible URL. Can be used with ``hg clone``. diff --git a/src/libvcs/url/svn.py b/src/libvcs/url/svn.py index f58210a7d..63cc1ba4a 100644 --- a/src/libvcs/url/svn.py +++ b/src/libvcs/url/svn.py @@ -6,7 +6,7 @@ compare to :class:`urllib.parse.ParseResult` - Output ``svn(1)`` URL: :meth:`SvnURL.to_url()` -- Extendable via :class:`~libvcs.url.base.Rules`, +- Extendable via :class:`~libvcs.url.base.RuleMap`, :class:`~libvcs.url.base.Rule` .. Note:: @@ -24,7 +24,7 @@ from libvcs._internal.dataclasses import SkipDefaultFieldsReprMixin -from .base import Rule, Rules, URLProtocol +from .base import Rule, RuleMap, URLProtocol RE_PATH = r""" ((?P.*)@)? @@ -170,11 +170,11 @@ class SvnURL(URLProtocol, SkipDefaultFieldsReprMixin): ref: Optional[str] = None rule: Optional[str] = None - rules: Rules = Rules(_rules={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) def __post_init__(self) -> None: url = self.url - for rule in self.rules.values(): + for rule in self.rule_map.values(): match = re.match(rule.pattern, url) if match is None: continue @@ -202,7 +202,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: >>> SvnURL.is_valid(url='notaurl') False """ - return any(re.search(rule.pattern, url) for rule in cls.rules.values()) + return any(re.search(rule.pattern, url) for rule in cls.rule_map.values()) def to_url(self) -> str: """Return a ``svn(1)``-compatible URL. Can be used with ``svn checkout``. diff --git a/tests/url/test_git.py b/tests/url/test_git.py index e7d007a56..9d62afbac 100644 --- a/tests/url/test_git.py +++ b/tests/url/test_git.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.git import GitSync -from libvcs.url.base import Rules +from libvcs.url.base import RuleMap from libvcs.url.git import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, GitBaseURL, GitURL @@ -142,8 +142,8 @@ def test_git_url_extension_pip( git_repo: GitSync, ) -> None: class GitURLWithPip(GitBaseURL): - rules: Rules = Rules( - _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rule_map: RuleMap = RuleMap( + _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) git_url_kwargs["url"] = git_url_kwargs["url"].format(local_repo=git_repo.dir) @@ -255,8 +255,8 @@ def test_git_revs( git_url_kwargs: GitURLKwargs, ) -> None: class GitURLWithPip(GitURL): - rules: Rules = Rules( - _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rule_map: RuleMap = RuleMap( + _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) git_url = GitURLWithPip(**git_url_kwargs) diff --git a/tests/url/test_hg.py b/tests/url/test_hg.py index 3d5e0576f..a8eb75434 100644 --- a/tests/url/test_hg.py +++ b/tests/url/test_hg.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.hg import HgSync -from libvcs.url.base import Rules +from libvcs.url.base import RuleMap from libvcs.url.hg import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, HgURL @@ -107,8 +107,8 @@ def test_hg_url_extension_pip( hg_repo: HgSync, ) -> None: class HgURLWithPip(HgURL): - rules: Rules = Rules( - _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rule_map: RuleMap = RuleMap( + _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) hg_url_kwargs["url"] = hg_url_kwargs["url"].format(local_repo=hg_repo.dir) diff --git a/tests/url/test_svn.py b/tests/url/test_svn.py index 104c9c4ec..b757f961a 100644 --- a/tests/url/test_svn.py +++ b/tests/url/test_svn.py @@ -3,7 +3,7 @@ import pytest from libvcs.sync.svn import SvnSync -from libvcs.url.base import Rules +from libvcs.url.base import RuleMap from libvcs.url.svn import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, SvnURL @@ -124,8 +124,8 @@ def test_svn_url_extension_pip( svn_repo: SvnSync, ) -> None: class SvnURLWithPip(SvnURL): - rules: Rules = Rules( - _rules={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + rule_map: RuleMap = RuleMap( + _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} ) svn_url_kwargs["url"] = svn_url_kwargs["url"].format(local_repo=svn_repo.dir) From 543e609a57956d2b2aedddef05d30f4c514663e1 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Tue, 20 Sep 2022 19:55:46 -0500 Subject: [PATCH 3/6] docs(url): Update docstring --- src/libvcs/url/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libvcs/url/base.py b/src/libvcs/url/base.py index 5ac446008..202680fdf 100644 --- a/src/libvcs/url/base.py +++ b/src/libvcs/url/base.py @@ -24,7 +24,7 @@ def is_valid(self, url: str, is_explicit: Optional[bool] = None) -> bool: @dataclasses.dataclass(repr=False) class Rule(SkipDefaultFieldsReprMixin): - """Structure for a rule""" + """A Rule represents an eligible pattern mapping to URL.""" label: str """Computer readable name / ID""" From 8287b4b3ae85b534f5f66c1c025cf644e9364633 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Tue, 20 Sep 2022 19:56:34 -0500 Subject: [PATCH 4/6] refactor!(url): pattern_defaults -> defaults --- src/libvcs/url/base.py | 8 ++++---- src/libvcs/url/git.py | 8 ++++---- src/libvcs/url/hg.py | 4 ++-- src/libvcs/url/svn.py | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libvcs/url/base.py b/src/libvcs/url/base.py index 202680fdf..af413f6f2 100644 --- a/src/libvcs/url/base.py +++ b/src/libvcs/url/base.py @@ -32,7 +32,7 @@ class Rule(SkipDefaultFieldsReprMixin): """Human readable description""" pattern: Pattern[str] """Regex pattern""" - pattern_defaults: dict[str, str] = dataclasses.field(default_factory=dict) + defaults: dict[str, str] = dataclasses.field(default_factory=dict) """Is the match unambiguous with other VCS systems? e.g. git+ prefix""" is_explicit: bool = False @@ -88,7 +88,7 @@ def register(self, cls: Rule) -> None: ... label = 'gh-prefix' ... description ='Matches prefixes like github:org/repo' ... pattern = r'^github:(?P.*)$' - ... pattern_defaults = { + ... defaults = { ... 'hostname': 'github.com', ... 'scheme': 'https' ... } @@ -107,7 +107,7 @@ def register(self, cls: Rule) -> None: >>> GitHubURL.is_valid(url='github:vcs-python/libvcs', is_explicit=True) True - Notice how ``pattern_defaults`` neatly fills the values for us. + Notice how ``defaults`` neatly fills the values for us. >>> GitHubURL(url='github:vcs-python/libvcs') GitHubURL(url=github:vcs-python/libvcs, @@ -137,7 +137,7 @@ def register(self, cls: Rule) -> None: ... label = 'gl-prefix' ... description ='Matches prefixes like gitlab:org/repo' ... pattern = r'^gitlab:(?P)' - ... pattern_defaults = { + ... defaults = { ... 'hostname': 'gitlab.com', ... 'scheme': 'https', ... 'suffix': '.git' diff --git a/src/libvcs/url/git.py b/src/libvcs/url/git.py index 2891d7495..2f0d61385 100644 --- a/src/libvcs/url/git.py +++ b/src/libvcs/url/git.py @@ -87,7 +87,7 @@ """, re.VERBOSE, ), - pattern_defaults={"username": "git"}, + defaults={"username": "git"}, ), # SCP-style URLs, e.g. git@ ] @@ -275,9 +275,9 @@ def __post_init__(self) -> None: for k, v in groups.items(): setattr(self, k, v) - for k, v in rule.pattern_defaults.items(): + for k, v in rule.defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, rule.pattern_defaults[k]) + setattr(self, k, rule.defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: @@ -544,7 +544,7 @@ def is_valid(cls, url: str, is_explicit: Optional[bool] = None) -> bool: ... re.VERBOSE, ... ), ... is_explicit=True, - ... pattern_defaults={ + ... defaults={ ... 'hostname': 'github.com' ... } ... ) diff --git a/src/libvcs/url/hg.py b/src/libvcs/url/hg.py index 373eb44be..00af58293 100644 --- a/src/libvcs/url/hg.py +++ b/src/libvcs/url/hg.py @@ -189,9 +189,9 @@ def __post_init__(self) -> None: for k, v in groups.items(): setattr(self, k, v) - for k, v in rule.pattern_defaults.items(): + for k, v in rule.defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, rule.pattern_defaults[k]) + setattr(self, k, rule.defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: diff --git a/src/libvcs/url/svn.py b/src/libvcs/url/svn.py index 63cc1ba4a..fbda96546 100644 --- a/src/libvcs/url/svn.py +++ b/src/libvcs/url/svn.py @@ -183,9 +183,9 @@ def __post_init__(self) -> None: for k, v in groups.items(): setattr(self, k, v) - for k, v in rule.pattern_defaults.items(): + for k, v in rule.defaults.items(): if getattr(self, k, None) is None: - setattr(self, k, rule.pattern_defaults[k]) + setattr(self, k, rule.defaults[k]) @classmethod def is_valid(cls, url: str, is_explicit: Optional[bool] = False) -> bool: From 7315799708a1a13617f2af3116d7fd36126f94a6 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Wed, 21 Sep 2022 06:07:08 -0500 Subject: [PATCH 5/6] refactor!(url): MATCHERS -> RULES --- src/libvcs/url/base.py | 6 +++--- src/libvcs/url/git.py | 12 ++++++------ src/libvcs/url/hg.py | 6 +++--- src/libvcs/url/svn.py | 6 +++--- tests/url/test_git.py | 6 +++--- tests/url/test_hg.py | 4 ++-- tests/url/test_svn.py | 4 ++-- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libvcs/url/base.py b/src/libvcs/url/base.py index af413f6f2..de316daa0 100644 --- a/src/libvcs/url/base.py +++ b/src/libvcs/url/base.py @@ -173,18 +173,18 @@ def register(self, cls: Rule) -> None: **Example: git URLs + pip-style git URLs:** - This is already in :class:`GitURL` via :data:`PIP_DEFAULT_MATCHERS`. For the + This is already in :class:`GitURL` via :data:`PIP_DEFAULT_RULES`. For the sake of showing how extensibility works, here is a recreation based on :class:`GitBaseURL`: >>> from libvcs.url.git import GitBaseURL - >>> from libvcs.url.git import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS + >>> from libvcs.url.git import DEFAULT_RULES, PIP_DEFAULT_RULES >>> @dataclasses.dataclass(repr=False) ... class GitURLWithPip(GitBaseURL): ... rule_map: RuleMap = RuleMap( - ... _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + ... _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ... ) >>> GitURLWithPip.is_valid(url="git+ssh://git@github.com/tony/AlgoXY.git") diff --git a/src/libvcs/url/git.py b/src/libvcs/url/git.py index 2f0d61385..470ea7b45 100644 --- a/src/libvcs/url/git.py +++ b/src/libvcs/url/git.py @@ -60,7 +60,7 @@ # Some https repos have .git at the end, e.g. https://github.com/org/repo.git -DEFAULT_MATCHERS: list[Rule] = [ +DEFAULT_RULES: list[Rule] = [ Rule( label="core-git-https", description="Vanilla git pattern, URL ending with optional .git suffix", @@ -125,7 +125,7 @@ """ -PIP_DEFAULT_MATCHERS: list[Rule] = [ +PIP_DEFAULT_RULES: list[Rule] = [ Rule( label="pip-url", description="pip-style git URL", @@ -191,7 +191,7 @@ - https://pip.pypa.io/en/stable/topics/vcs-support/ """ # NOQA: E501 -NPM_DEFAULT_MATCHERS: list[Rule] = [] +NPM_DEFAULT_RULES: list[Rule] = [] """NPM-style git URLs. Git URL pattern (from docs.npmjs.com):: @@ -262,7 +262,7 @@ class GitBaseURL(URLProtocol, SkipDefaultFieldsReprMixin): suffix: Optional[str] = None rule: Optional[str] = None - rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_RULES}) def __post_init__(self) -> None: url = self.url @@ -370,7 +370,7 @@ class GitPipURL(GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): # commit-ish (rev): tag, branch, ref rev: Optional[str] = None - rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in PIP_DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in PIP_DEFAULT_RULES}) def to_url(self) -> str: """Exports a pip-compliant URL. @@ -479,7 +479,7 @@ class GitURL(GitPipURL, GitBaseURL, URLProtocol, SkipDefaultFieldsReprMixin): """ rule_map: RuleMap = RuleMap( - _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ) @classmethod diff --git a/src/libvcs/url/hg.py b/src/libvcs/url/hg.py index 00af58293..74917b2d9 100644 --- a/src/libvcs/url/hg.py +++ b/src/libvcs/url/hg.py @@ -43,7 +43,7 @@ ) """ -DEFAULT_MATCHERS: list[Rule] = [ +DEFAULT_RULES: list[Rule] = [ Rule( label="core-hg", description="Vanilla hg pattern", @@ -74,7 +74,7 @@ ) """ -PIP_DEFAULT_MATCHERS: list[Rule] = [ +PIP_DEFAULT_RULES: list[Rule] = [ Rule( label="pip-url", description="pip-style hg URL", @@ -176,7 +176,7 @@ class HgURL(URLProtocol, SkipDefaultFieldsReprMixin): rule: Optional[str] = None # name of the :class:`Rule` - rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_RULES}) def __post_init__(self) -> None: url = self.url diff --git a/src/libvcs/url/svn.py b/src/libvcs/url/svn.py index fbda96546..1e32a8772 100644 --- a/src/libvcs/url/svn.py +++ b/src/libvcs/url/svn.py @@ -47,7 +47,7 @@ ) """ -DEFAULT_MATCHERS: list[Rule] = [ +DEFAULT_RULES: list[Rule] = [ Rule( label="core-svn", description="Vanilla svn pattern", @@ -78,7 +78,7 @@ ) """ -PIP_DEFAULT_MATCHERS: list[Rule] = [ +PIP_DEFAULT_RULES: list[Rule] = [ Rule( label="pip-url", description="pip-style svn URL", @@ -170,7 +170,7 @@ class SvnURL(URLProtocol, SkipDefaultFieldsReprMixin): ref: Optional[str] = None rule: Optional[str] = None - rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_MATCHERS}) + rule_map: RuleMap = RuleMap(_rule_map={m.label: m for m in DEFAULT_RULES}) def __post_init__(self) -> None: url = self.url diff --git a/tests/url/test_git.py b/tests/url/test_git.py index 9d62afbac..ebf1397a8 100644 --- a/tests/url/test_git.py +++ b/tests/url/test_git.py @@ -4,7 +4,7 @@ from libvcs.sync.git import GitSync from libvcs.url.base import RuleMap -from libvcs.url.git import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, GitBaseURL, GitURL +from libvcs.url.git import DEFAULT_RULES, PIP_DEFAULT_RULES, GitBaseURL, GitURL class GitURLFixture(typing.NamedTuple): @@ -143,7 +143,7 @@ def test_git_url_extension_pip( ) -> None: class GitURLWithPip(GitBaseURL): rule_map: RuleMap = RuleMap( - _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ) git_url_kwargs["url"] = git_url_kwargs["url"].format(local_repo=git_repo.dir) @@ -256,7 +256,7 @@ def test_git_revs( ) -> None: class GitURLWithPip(GitURL): rule_map: RuleMap = RuleMap( - _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ) git_url = GitURLWithPip(**git_url_kwargs) diff --git a/tests/url/test_hg.py b/tests/url/test_hg.py index a8eb75434..6de7c64b9 100644 --- a/tests/url/test_hg.py +++ b/tests/url/test_hg.py @@ -4,7 +4,7 @@ from libvcs.sync.hg import HgSync from libvcs.url.base import RuleMap -from libvcs.url.hg import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, HgURL +from libvcs.url.hg import DEFAULT_RULES, PIP_DEFAULT_RULES, HgURL class HgURLFixture(typing.NamedTuple): @@ -108,7 +108,7 @@ def test_hg_url_extension_pip( ) -> None: class HgURLWithPip(HgURL): rule_map: RuleMap = RuleMap( - _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ) hg_url_kwargs["url"] = hg_url_kwargs["url"].format(local_repo=hg_repo.dir) diff --git a/tests/url/test_svn.py b/tests/url/test_svn.py index b757f961a..d86a33125 100644 --- a/tests/url/test_svn.py +++ b/tests/url/test_svn.py @@ -4,7 +4,7 @@ from libvcs.sync.svn import SvnSync from libvcs.url.base import RuleMap -from libvcs.url.svn import DEFAULT_MATCHERS, PIP_DEFAULT_MATCHERS, SvnURL +from libvcs.url.svn import DEFAULT_RULES, PIP_DEFAULT_RULES, SvnURL class SvnURLFixture(typing.NamedTuple): @@ -125,7 +125,7 @@ def test_svn_url_extension_pip( ) -> None: class SvnURLWithPip(SvnURL): rule_map: RuleMap = RuleMap( - _rule_map={m.label: m for m in [*DEFAULT_MATCHERS, *PIP_DEFAULT_MATCHERS]} + _rule_map={m.label: m for m in [*DEFAULT_RULES, *PIP_DEFAULT_RULES]} ) svn_url_kwargs["url"] = svn_url_kwargs["url"].format(local_repo=svn_repo.dir) From a0385711456b15433be3470ec2e314fedffa1594 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Wed, 21 Sep 2022 06:15:01 -0500 Subject: [PATCH 6/6] docs(CHANGES): Note URL renames --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index 0a992cf85..5f3890bd9 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,15 @@ $ pip install --user --upgrade --pre libvcs - _Add your latest changes from PRs here_ +### Breaking changes + +URL renamings (#417): + +- `Matcher` -> `Rule`, `MatcherRegistry` -> `Rules` +- `matches` -> `rule_map` +- `default_patterns` -> `patterns` +- `MATCHERS` -> `RULES` + ## libvcs 0.16.5 (2022-09-21) ### Bug fixes