Skip to content

Commit d49399b

Browse files
merge main
2 parents 9b6f031 + 5f20c3e commit d49399b

File tree

5 files changed

+104
-36
lines changed

5 files changed

+104
-36
lines changed

src/setuptools_scm/_config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]:
4343

4444
group_names = regex.groupindex.keys()
4545
if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names):
46-
warnings.warn(
47-
"Expected tag_regex to contain a single match group or a group named"
48-
" 'version' to identify the version part of any tag."
46+
raise ValueError(
47+
f"Expected tag_regex '{regex.pattern}' to contain a single match group or"
48+
" a group named 'version' to identify the version part of any tag."
4949
)
5050

5151
return regex
@@ -106,6 +106,9 @@ class Configuration:
106106

107107
parent: _t.PathT | None = None
108108

109+
def __post_init__(self) -> None:
110+
self.tag_regex = _check_tag_regex(self.tag_regex)
111+
109112
@property
110113
def absolute_root(self) -> str:
111114
return _check_absolute_root(self.root, self.relative_to)
@@ -140,13 +143,11 @@ def from_data(
140143
given configuration data
141144
create a config instance after validating tag regex/version class
142145
"""
143-
tag_regex = _check_tag_regex(data.pop("tag_regex", None))
144146
version_cls = _validate_version_cls(
145147
data.pop("version_cls", None), data.pop("normalize", True)
146148
)
147149
return cls(
148150
relative_to=relative_to,
149151
version_cls=version_cls,
150-
tag_regex=tag_regex,
151152
**data,
152153
)

src/setuptools_scm/_entrypoints.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from typing import Callable
88
from typing import Iterator
99
from typing import cast
10-
from typing import overload
1110

1211
from . import _log
1312
from . import version
@@ -108,32 +107,26 @@ def _iter_version_schemes(
108107
yield scheme_value
109108

110109

111-
@overload
112110
def _call_version_scheme(
113111
version: version.ScmVersion,
114112
entrypoint: str,
115113
given_value: _t.VERSION_SCHEMES,
116-
default: str,
117-
) -> str: ...
118-
119-
120-
@overload
121-
def _call_version_scheme(
122-
version: version.ScmVersion,
123-
entrypoint: str,
124-
given_value: _t.VERSION_SCHEMES,
125-
default: None,
126-
) -> str | None: ...
127-
128-
129-
def _call_version_scheme(
130-
version: version.ScmVersion,
131-
entrypoint: str,
132-
given_value: _t.VERSION_SCHEMES,
133-
default: str | None,
134-
) -> str | None:
114+
default: str | None = None,
115+
) -> str:
116+
found_any_implementation = False
135117
for scheme in _iter_version_schemes(entrypoint, given_value):
118+
found_any_implementation = True
136119
result = scheme(version)
137120
if result is not None:
138121
return result
139-
return default
122+
if not found_any_implementation:
123+
raise ValueError(
124+
f'Couldn\'t find any implementations for entrypoint "{entrypoint}"'
125+
f' with value "{given_value}".'
126+
)
127+
if default is not None:
128+
return default
129+
raise ValueError(
130+
f'None of the "{entrypoint}" entrypoints matching "{given_value}"'
131+
" returned a value."
132+
)

src/setuptools_scm/version.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,21 @@ def _parse_version_tag(
6262
log.debug(
6363
"key %s data %s, %s, %r", key, match.groupdict(), match.groups(), full
6464
)
65-
result = _TagDict(
66-
version=match.group(key),
67-
prefix=full[: match.start(key)],
68-
suffix=full[match.end(key) :],
69-
)
7065

71-
log.debug("tag %r parsed to %r", tag, result)
72-
assert result["version"]
73-
return result
66+
if version := match.group(key):
67+
result = _TagDict(
68+
version=version,
69+
prefix=full[: match.start(key)],
70+
suffix=full[match.end(key) :],
71+
)
72+
73+
log.debug("tag %r parsed to %r", tag, result)
74+
return result
75+
76+
raise ValueError(
77+
f'The tag_regex "{config.tag_regex.pattern}" matched tag "{tag}", '
78+
"however the matched group has no value."
79+
)
7480
else:
7581
log.debug("tag %r did not parse", tag)
7682

@@ -442,8 +448,9 @@ def format_version(version: ScmVersion) -> str:
442448
if version.preformatted:
443449
assert isinstance(version.tag, str)
444450
return version.tag
451+
445452
main_version = _entrypoints._call_version_scheme(
446-
version, "setuptools_scm.version_scheme", version.config.version_scheme, None
453+
version, "setuptools_scm.version_scheme", version.config.version_scheme
447454
)
448455
log.debug("version %s", main_version)
449456
assert main_version is not None

testing/test_config.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,23 @@ def test_config_overrides(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> No
9898

9999
assert pristine.root != overridden.root
100100
assert pristine.fallback_root != overridden.fallback_root
101+
102+
103+
@pytest.mark.parametrize(
104+
"tag_regex",
105+
[
106+
r".*",
107+
r"(.+)(.+)",
108+
r"((.*))",
109+
],
110+
)
111+
def test_config_bad_regex(tag_regex: str) -> None:
112+
with pytest.raises(
113+
ValueError,
114+
match=(
115+
f"Expected tag_regex '{re.escape(tag_regex)}' to contain a single match"
116+
" group or a group named 'version' to identify the version part of any"
117+
" tag."
118+
),
119+
):
120+
Configuration(tag_regex=re.compile(tag_regex))

testing/test_version.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import re
4+
35
from dataclasses import replace
46
from datetime import date
57
from datetime import timedelta
@@ -221,6 +223,17 @@ def test_tag_regex1(tag: str, expected: str) -> None:
221223
assert result.tag.public == expected
222224

223225

226+
def test_regex_match_but_no_version() -> None:
227+
with pytest.raises(
228+
ValueError,
229+
match=(
230+
r'The tag_regex "\(\?P<version>\)\.\*" matched tag "v1",'
231+
" however the matched group has no value"
232+
),
233+
):
234+
meta("v1", config=replace(c, tag_regex=re.compile("(?P<version>).*")))
235+
236+
224237
@pytest.mark.issue("https://github.com/pypa/setuptools-scm/issues/471")
225238
def test_version_bump_bad() -> None:
226239
class YikesVersion:
@@ -442,3 +455,37 @@ def __repr__(self) -> str:
442455

443456
assert isinstance(scm_version.tag, MyVersion)
444457
assert str(scm_version.tag) == "Custom 1.0.0-foo"
458+
459+
460+
@pytest.mark.parametrize("config_key", ["version_scheme", "local_scheme"])
461+
def test_no_matching_entrypoints(config_key: str) -> None:
462+
version = meta(
463+
"1.0",
464+
config=replace(c, **{config_key: "nonexistant"}), # type: ignore
465+
)
466+
with pytest.raises(
467+
ValueError,
468+
match=(
469+
r'Couldn\'t find any implementations for entrypoint "setuptools_scm\..*?"'
470+
' with value "nonexistant"'
471+
),
472+
):
473+
format_version(version)
474+
475+
476+
def test_all_entrypoints_return_none() -> None:
477+
version = meta(
478+
"1.0",
479+
config=replace(
480+
c,
481+
version_scheme=lambda v: None, # type: ignore
482+
),
483+
)
484+
with pytest.raises(
485+
ValueError,
486+
match=(
487+
'None of the "setuptools_scm.version_scheme" entrypoints matching'
488+
r" .*? returned a value."
489+
),
490+
):
491+
format_version(version)

0 commit comments

Comments
 (0)