@@ -67,18 +67,22 @@ def render_tag(pattern: str, *, name: str, version: str) -> str:
6767 return result
6868
6969
70- def build_tag_regex (pattern : str ) -> re .Pattern [str ]:
70+ def build_tag_regex (pattern : str , * , name : str | None = None ) -> re .Pattern [str ]:
7171 """Return a compiled regex that captures ``{version}`` from a tag.
7272
7373 Literal text in *pattern* is escaped so that special regex characters
7474 (e.g. dots, parens) are matched verbatim. ``{version}`` becomes a
7575 named capture group ``(?P<version>...)`` matching a semver-like
76- string. ``{name}`` becomes a non-capturing wildcard ``[^/]+``.
76+ string. ``{name}`` becomes a non-capturing wildcard ``[^/]+``, or
77+ the literal *name* when provided (for monorepo per-package tags).
7778
7879 Parameters
7980 ----------
8081 pattern:
8182 Tag pattern string, e.g. ``"v{version}"``.
83+ name:
84+ When set and the pattern contains ``{name}``, match only this
85+ package name instead of any ``[^/]+`` segment.
8286
8387 Returns
8488 -------
@@ -116,20 +120,28 @@ def build_tag_regex(pattern: str) -> re.Pattern[str]:
116120 )
117121
118122 escaped = escaped .replace (re .escape (_sentinel_version ), _VERSION_RX )
119- escaped = escaped .replace (re .escape (_sentinel_name ), r"[^/]+" )
123+ if _PLACEHOLDER_NAME in pattern and name :
124+ name_rx = re .escape (name )
125+ else :
126+ name_rx = r"[^/]+"
127+ escaped = escaped .replace (re .escape (_sentinel_name ), name_rx )
120128
121129 return re .compile (r"^" + escaped + r"$" )
122130
123131
124132def infer_tag_pattern (tag : str , package_name : str = "" ) -> str | None :
125133 """Return the first default pattern that matches *tag*, or ``None``.
126134
127- *package_name* is accepted for API symmetry with :func:`render_tag` but
128- is not required for matching because ``{name}`` is a wildcard .
135+ When *package_name* is set, patterns containing ``{name}`` only match
136+ tags for that package (monorepo-safe) .
129137 """
130- del package_name # reserved for callers that pass the package display name
131138 for pattern in DEFAULT_TAG_PATTERNS :
132- if build_tag_regex (pattern ).match (tag ):
139+ rx = (
140+ build_tag_regex (pattern , name = package_name )
141+ if _PLACEHOLDER_NAME in pattern and package_name
142+ else build_tag_regex (pattern )
143+ )
144+ if rx .match (tag ):
133145 return pattern
134146 return None
135147
@@ -146,12 +158,19 @@ def infer_tag_pattern_from_refs(refs: list, package_name: str = "") -> str | Non
146158 return None
147159
148160
149- def parse_tag_version (tag : str , pattern : str ) -> str | None :
161+ def parse_tag_version (tag : str , pattern : str , * , name : str | None = None ) -> str | None :
150162 """Extract the semver substring from *tag* using *pattern*."""
151- match = build_tag_regex (pattern ).match (tag )
163+ if _PLACEHOLDER_VERSION not in pattern :
164+ return None
165+ rx = (
166+ build_tag_regex (pattern , name = name )
167+ if _PLACEHOLDER_NAME in pattern and name
168+ else build_tag_regex (pattern )
169+ )
170+ match = rx .match (tag )
152171 if match is None :
153172 return None
154- return match .group ("version" )
173+ return match .groupdict (). get ("version" )
155174
156175
157176def is_version_tag_ref (ref : str , package_name : str | None = None ) -> bool :
0 commit comments