vcs-versioning 2.1.1__tar.gz → 2.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/CHANGELOG.md +6 -0
- {vcs_versioning-2.1.1/src/vcs_versioning.egg-info → vcs_versioning-2.1.2}/PKG-INFO +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_git.py +6 -2
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_hg.py +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_hg_git.py +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_jj.py +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_scm_workdir.py +33 -7
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_fallback_workdir.py +18 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_file_finders/_git.py +3 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_get_version_impl.py +4 -4
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_legacy_parse.py +2 -8
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_protocols.py +10 -3
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_scm_version.py +10 -14
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_worktree_discovery.py +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2/src/vcs_versioning.egg-info}/PKG-INFO +1 -1
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_legacy_parse.py +5 -5
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_workdir_discovery.py +63 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/LICENSE.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/MANIFEST.in +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/README.md +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/pyproject.toml +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/setup.cfg +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/setup.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/__main__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_backends/_discover_vcs.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_cli/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_cli/_args.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_cli/git_archival_full.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_cli/git_archival_stable.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_compat.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_config.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_discover.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_dump_version.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_entrypoints.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_environment.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_exceptions.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_fallbacks.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_file_finders/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_file_finders/_hg.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_file_finders/_jj.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_integration.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_integrator_helpers.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_log.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_modify_version.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_node_utils.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_overrides.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_paths.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_project_overrides.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_pyproject_reading.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_requirement_cls.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_run_cmd.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_scm_metadata.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_test_utils.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_toml.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_types.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_cls.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_fields.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_inference.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_common.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_standard.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_towncrier.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/overrides.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/py.typed +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/test_api.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/SOURCES.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/dependency_links.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/entry_points.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/requires.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/top_level.txt +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/__init__.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/conftest.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_better_root_errors.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_chain_api.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_compat.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_config.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_expect_parse.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_file_finders.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_git.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_hg_git.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_integrator_helpers.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_internal_log_level.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_jj.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_mercurial.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_overrides_api.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_overrides_env_reader.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_project_overrides.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_project_path.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_regressions.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_scm_metadata.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_tag_config.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_version.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_version_scheme_towncrier.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_version_schemes.py +0 -0
- {vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/testing_vcs/test_workdir_api.py +0 -0
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- towncrier release notes start -->
|
|
4
4
|
|
|
5
|
+
## 2.1.2 (2026-06-23)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fix MetadataWorkdir crash when using custom `tag_regex` — stored tags are already parsed version strings and no longer re-parsed through the tag regex. ([#1439](https://github.com/pypa/setuptools-scm/issues/1439))
|
|
10
|
+
|
|
5
11
|
## 2.1.1 (2026-06-23)
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
@@ -238,11 +238,15 @@ class GitWorkdir(Workdir):
|
|
|
238
238
|
return _git_parse_inner(config, self, pre_parse=effective_pre_parse)
|
|
239
239
|
|
|
240
240
|
def list_tracked_files(self, path: Path | str = "") -> list[str]:
|
|
241
|
-
"""List files tracked by git, honoring export-ignore.
|
|
241
|
+
"""List files tracked by git, honoring export-ignore.
|
|
242
|
+
|
|
243
|
+
When no path is given, scopes to ``project_root`` (not the VCS root)
|
|
244
|
+
so that monorepo projects only list their own files.
|
|
245
|
+
"""
|
|
242
246
|
from .._file_finders import scm_find_files
|
|
243
247
|
from .._file_finders._git import _git_ls_files_and_dirs
|
|
244
248
|
|
|
245
|
-
base = str(path) if path else str(self.
|
|
249
|
+
base = str(path) if path else str(self.project_root)
|
|
246
250
|
git_files, git_dirs = _git_ls_files_and_dirs(
|
|
247
251
|
str(self.path), timeout=self._subprocess_timeout
|
|
248
252
|
)
|
|
@@ -293,7 +293,7 @@ class HgWorkdir(Workdir):
|
|
|
293
293
|
from .._file_finders import scm_find_files
|
|
294
294
|
from .._file_finders._hg import _hg_ls_files_and_dirs
|
|
295
295
|
|
|
296
|
-
base = str(path) if path else str(self.
|
|
296
|
+
base = str(path) if path else str(self.project_root)
|
|
297
297
|
hg_files, hg_dirs = _hg_ls_files_and_dirs(
|
|
298
298
|
str(self.path),
|
|
299
299
|
hg_command=self._hg_command,
|
|
@@ -95,7 +95,7 @@ class GitWorkdirHgClient(GitWorkdir, HgWorkdir):
|
|
|
95
95
|
from .._file_finders import scm_find_files
|
|
96
96
|
from .._file_finders._hg import _hg_ls_files_and_dirs
|
|
97
97
|
|
|
98
|
-
base = str(path) if path else str(self.
|
|
98
|
+
base = str(path) if path else str(self.project_root)
|
|
99
99
|
hg_files, hg_dirs = _hg_ls_files_and_dirs(
|
|
100
100
|
str(self.path),
|
|
101
101
|
hg_command=self._hg_command,
|
|
@@ -263,7 +263,7 @@ class JjWorkdir(Workdir):
|
|
|
263
263
|
from .._file_finders import scm_find_files
|
|
264
264
|
from .._file_finders._jj import _jj_ls_files_and_dirs
|
|
265
265
|
|
|
266
|
-
base = str(path) if path else str(self.
|
|
266
|
+
base = str(path) if path else str(self.project_root)
|
|
267
267
|
jj_files, jj_dirs = _jj_ls_files_and_dirs(
|
|
268
268
|
str(self.path), timeout=self._subprocess_timeout
|
|
269
269
|
)
|
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from dataclasses import field as dc_field
|
|
6
6
|
from datetime import date, datetime, timezone
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import TYPE_CHECKING, overload
|
|
9
9
|
|
|
10
10
|
from .._scm_version import ScmVersion
|
|
11
11
|
|
|
@@ -16,6 +16,37 @@ if TYPE_CHECKING:
|
|
|
16
16
|
log = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class _ProjectRootDescriptor:
|
|
20
|
+
"""Descriptor for ``project_root`` that defaults to ``path``.
|
|
21
|
+
|
|
22
|
+
Acts as default when no value has been set on the instance.
|
|
23
|
+
Stores explicitly assigned values in the instance ``__dict__``.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __set_name__(self, owner: type, name: str) -> None:
|
|
27
|
+
self._name = name
|
|
28
|
+
|
|
29
|
+
@overload
|
|
30
|
+
def __get__(self, obj: None, objtype: type) -> _ProjectRootDescriptor: ...
|
|
31
|
+
|
|
32
|
+
@overload
|
|
33
|
+
def __get__(self, obj: ScmWorkdir, objtype: type | None = None) -> Path: ...
|
|
34
|
+
|
|
35
|
+
def __get__(
|
|
36
|
+
self, obj: ScmWorkdir | None, objtype: type | None = None
|
|
37
|
+
) -> Path | _ProjectRootDescriptor:
|
|
38
|
+
if obj is None:
|
|
39
|
+
return self
|
|
40
|
+
value: Path | None = obj.__dict__.get(self._name)
|
|
41
|
+
if value is None:
|
|
42
|
+
return obj.path
|
|
43
|
+
return value
|
|
44
|
+
|
|
45
|
+
def __set__(self, obj: ScmWorkdir, value: Path | None) -> None:
|
|
46
|
+
if isinstance(value, Path):
|
|
47
|
+
obj.__dict__[self._name] = value
|
|
48
|
+
|
|
49
|
+
|
|
19
50
|
def get_latest_file_mtime(changed_files: list[str], base_path: Path) -> date | None:
|
|
20
51
|
"""Get the latest modification time of the given files.
|
|
21
52
|
|
|
@@ -61,15 +92,11 @@ class ScmWorkdir:
|
|
|
61
92
|
"""
|
|
62
93
|
|
|
63
94
|
path: Path
|
|
64
|
-
project_root: Path
|
|
95
|
+
project_root: Path = _ProjectRootDescriptor() # type: ignore[assignment]
|
|
65
96
|
|
|
66
97
|
_config: Configuration | None = dc_field(default=None, repr=False, compare=False)
|
|
67
98
|
"""Back-reference to the ``Configuration`` that discovered this workdir."""
|
|
68
99
|
|
|
69
|
-
def __post_init__(self) -> None:
|
|
70
|
-
if self.project_root is None:
|
|
71
|
-
self.project_root = self.path
|
|
72
|
-
|
|
73
100
|
@property
|
|
74
101
|
def _subprocess_timeout(self) -> int | None:
|
|
75
102
|
"""Subprocess timeout from ``config.env``.
|
|
@@ -95,7 +122,6 @@ class ScmWorkdir:
|
|
|
95
122
|
@property
|
|
96
123
|
def project_path(self) -> str:
|
|
97
124
|
"""Discovered relative path from VCS root to project directory."""
|
|
98
|
-
assert self.project_root is not None
|
|
99
125
|
if self.path == self.project_root:
|
|
100
126
|
return ""
|
|
101
127
|
from .._paths import relative_project_path
|
|
@@ -34,6 +34,11 @@ class FallbackWorkdir:
|
|
|
34
34
|
_config: Configuration | None = dc_field(default=None, repr=False, compare=False)
|
|
35
35
|
"""Back-reference to the ``Configuration`` that discovered this workdir."""
|
|
36
36
|
|
|
37
|
+
@property
|
|
38
|
+
def project_root(self) -> Path:
|
|
39
|
+
"""Fallback workdirs always live at the project root."""
|
|
40
|
+
return self.path
|
|
41
|
+
|
|
37
42
|
@property
|
|
38
43
|
def config(self) -> Configuration:
|
|
39
44
|
if self._config is None:
|
|
@@ -76,8 +81,20 @@ class MetadataWorkdir(FallbackWorkdir):
|
|
|
76
81
|
data.node_date,
|
|
77
82
|
self.metadata_dir,
|
|
78
83
|
)
|
|
84
|
+
# The tag in scm_version.json is already a parsed version string
|
|
85
|
+
# (e.g. "1.5.5"), not a raw VCS tag (e.g. "cuda-pathfinder-v1.5.5").
|
|
86
|
+
# Convert to version_cls so _parse_tag skips tag_regex matching.
|
|
87
|
+
try:
|
|
88
|
+
tag = self.config.version_cls(data.tag)
|
|
89
|
+
except Exception:
|
|
90
|
+
log.warning(
|
|
91
|
+
"cannot parse stored tag %r in metadata at %s",
|
|
92
|
+
data.tag,
|
|
93
|
+
self.metadata_dir,
|
|
94
|
+
)
|
|
95
|
+
return None
|
|
79
96
|
return meta(
|
|
80
|
-
tag=
|
|
97
|
+
tag=tag,
|
|
81
98
|
distance=data.distance,
|
|
82
99
|
node=data.node,
|
|
83
100
|
dirty=data.dirty,
|
|
@@ -85,6 +85,9 @@ def _git_ls_files_and_dirs(
|
|
|
85
85
|
log.error("listing git files failed - pretending there aren't any")
|
|
86
86
|
return set(), set()
|
|
87
87
|
|
|
88
|
+
# Normalize toplevel to match what scm_find_files expects:
|
|
89
|
+
# absolute, symlinks resolved, case-normalized (normcase).
|
|
90
|
+
toplevel = norm_real(toplevel)
|
|
88
91
|
git_files: set[str] = set()
|
|
89
92
|
git_dirs: set[str] = {toplevel}
|
|
90
93
|
for name in res.stdout.rstrip("\0").split("\0"):
|
|
@@ -90,7 +90,7 @@ def _resolve_version(config: Configuration) -> ScmVersion | None:
|
|
|
90
90
|
return None
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
def _warn_if_tracked(target: Path, root: Path) -> None:
|
|
93
|
+
def _warn_if_tracked(target: Path, root: Path, config: Configuration) -> None:
|
|
94
94
|
"""Warn when *target* is tracked in version control (#468).
|
|
95
95
|
|
|
96
96
|
Writing a version file that is tracked makes ``git describe --dirty``
|
|
@@ -101,7 +101,7 @@ def _warn_if_tracked(target: Path, root: Path) -> None:
|
|
|
101
101
|
|
|
102
102
|
for workdir_cls in (GitWorkdir, HgWorkdir):
|
|
103
103
|
try:
|
|
104
|
-
wd = workdir_cls.from_potential_worktree(root)
|
|
104
|
+
wd = workdir_cls.from_potential_worktree(root, config)
|
|
105
105
|
except Exception:
|
|
106
106
|
continue
|
|
107
107
|
if wd is not None and wd.is_file_tracked(target):
|
|
@@ -125,7 +125,7 @@ def write_version_files(
|
|
|
125
125
|
|
|
126
126
|
write_to = Path(config.write_to)
|
|
127
127
|
target = root / write_to if not write_to.is_absolute() else write_to
|
|
128
|
-
_warn_if_tracked(target, root)
|
|
128
|
+
_warn_if_tracked(target, root, config)
|
|
129
129
|
|
|
130
130
|
dump_version(
|
|
131
131
|
root=config.root,
|
|
@@ -142,7 +142,7 @@ def write_version_files(
|
|
|
142
142
|
# todo: use a better name than fallback root
|
|
143
143
|
assert config.relative_to is not None
|
|
144
144
|
target = Path(config.relative_to).parent.joinpath(version_file)
|
|
145
|
-
_warn_if_tracked(target, root)
|
|
145
|
+
_warn_if_tracked(target, root, config)
|
|
146
146
|
|
|
147
147
|
write_version_to_path(
|
|
148
148
|
target,
|
|
@@ -38,12 +38,7 @@ class LegacyParseWorkdir(ScmWorkdir):
|
|
|
38
38
|
entry-point group.
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def __post_init__(self) -> None:
|
|
44
|
-
super().__post_init__()
|
|
45
|
-
if self._parse_fn is None:
|
|
46
|
-
raise TypeError("LegacyParseWorkdir requires a _parse_fn")
|
|
41
|
+
parse_fn: ParseFunction = dc_field(repr=False, kw_only=True)
|
|
47
42
|
|
|
48
43
|
def get_scm_version(self) -> ScmVersion | None:
|
|
49
44
|
warnings.warn(
|
|
@@ -53,8 +48,7 @@ class LegacyParseWorkdir(ScmWorkdir):
|
|
|
53
48
|
DeprecationWarning,
|
|
54
49
|
stacklevel=2,
|
|
55
50
|
)
|
|
56
|
-
|
|
57
|
-
result = self._parse_fn(self.config.absolute_root, config=self.config)
|
|
51
|
+
result = self.parse_fn(self.config.absolute_root, config=self.config)
|
|
58
52
|
if result is not None and not isinstance(result, ScmVersion):
|
|
59
53
|
raise TypeError(
|
|
60
54
|
f"version parse result was {result!r}\n"
|
|
@@ -32,6 +32,16 @@ class WorkdirProtocol(Protocol):
|
|
|
32
32
|
@property
|
|
33
33
|
def path(self) -> Path: ...
|
|
34
34
|
|
|
35
|
+
@property
|
|
36
|
+
def project_root(self) -> Path:
|
|
37
|
+
"""The project directory (where pyproject.toml lives).
|
|
38
|
+
|
|
39
|
+
For SCM workdirs this may differ from ``path`` (the VCS root).
|
|
40
|
+
For fallback workdirs this is always equal to ``path``.
|
|
41
|
+
Guaranteed non-None after construction.
|
|
42
|
+
"""
|
|
43
|
+
...
|
|
44
|
+
|
|
35
45
|
def get_scm_version(self) -> ScmVersion | None:
|
|
36
46
|
"""Raw version metadata before overrides / formatting."""
|
|
37
47
|
...
|
|
@@ -49,9 +59,6 @@ class WorkdirProtocol(Protocol):
|
|
|
49
59
|
class ScmWorkdirProtocol(WorkdirProtocol, Protocol):
|
|
50
60
|
"""Live VCS checkout: adds monorepo / nested-project fields."""
|
|
51
61
|
|
|
52
|
-
@property
|
|
53
|
-
def project_root(self) -> Path: ...
|
|
54
|
-
|
|
55
62
|
@property
|
|
56
63
|
def project_path(self) -> str:
|
|
57
64
|
"""POSIX relative path from VCS root to project_root (empty when equal)."""
|
|
@@ -347,20 +347,14 @@ class ScmVersion:
|
|
|
347
347
|
|
|
348
348
|
def _parse_tag(
|
|
349
349
|
tag: _Version | str, preformatted: bool, config: _config.Configuration
|
|
350
|
-
) -> _Version:
|
|
350
|
+
) -> _Version | None:
|
|
351
351
|
if preformatted:
|
|
352
|
-
# For preformatted versions, tag should already be validated as a version object
|
|
353
|
-
# String validation is handled in meta function before calling this
|
|
354
352
|
if isinstance(tag, str):
|
|
355
|
-
# This should not happen with enhanced meta, but kept for safety
|
|
356
353
|
return _v.NonNormalizedVersion(tag)
|
|
357
354
|
else:
|
|
358
|
-
# Already a version object (including test mocks), return as-is
|
|
359
355
|
return tag
|
|
360
356
|
elif not isinstance(tag, config.version_cls):
|
|
361
|
-
|
|
362
|
-
assert version is not None
|
|
363
|
-
return version
|
|
357
|
+
return tag_to_version(tag, config)
|
|
364
358
|
else:
|
|
365
359
|
return tag
|
|
366
360
|
|
|
@@ -389,18 +383,20 @@ def meta(
|
|
|
389
383
|
node_date: date | None = None,
|
|
390
384
|
time: datetime | None = None,
|
|
391
385
|
) -> ScmVersion:
|
|
392
|
-
parsed_version: _Version
|
|
393
|
-
# Enhanced string validation for preformatted versions
|
|
386
|
+
parsed_version: _Version | None
|
|
394
387
|
if preformatted and isinstance(tag, str):
|
|
395
|
-
# Validate PEP 440 compliance using NonNormalizedVersion
|
|
396
|
-
# Let validation errors bubble up to the caller
|
|
397
388
|
parsed_version = _v.NonNormalizedVersion(tag)
|
|
398
389
|
else:
|
|
399
|
-
# Use existing _parse_tag logic for non-preformatted or already validated inputs
|
|
400
390
|
parsed_version = _parse_tag(tag, preformatted, config)
|
|
401
391
|
|
|
392
|
+
if parsed_version is None:
|
|
393
|
+
raise ValueError(
|
|
394
|
+
f"Can't parse version from tag {tag!r}"
|
|
395
|
+
f" (tag_regex={config.tag.regex.pattern!r},"
|
|
396
|
+
f" tag_prefix={config.tag.prefix!r})"
|
|
397
|
+
)
|
|
398
|
+
|
|
402
399
|
log.info("version %s -> %s", tag, parsed_version)
|
|
403
|
-
assert parsed_version is not None, f"Can't parse version {tag}"
|
|
404
400
|
|
|
405
401
|
kwargs: _ScmVersionKwargs = {
|
|
406
402
|
"distance": distance,
|
|
@@ -34,8 +34,8 @@ class TestLegacyParseWorkdir:
|
|
|
34
34
|
return Configuration.from_file(pyproject)
|
|
35
35
|
|
|
36
36
|
def test_requires_parse_fn(self, tmp_path: Path) -> None:
|
|
37
|
-
with pytest.raises(TypeError, match="
|
|
38
|
-
LegacyParseWorkdir(path=tmp_path)
|
|
37
|
+
with pytest.raises(TypeError, match="parse_fn"):
|
|
38
|
+
LegacyParseWorkdir(path=tmp_path) # type: ignore[call-arg]
|
|
39
39
|
|
|
40
40
|
def test_get_scm_version_emits_deprecation(self, tmp_path: Path) -> None:
|
|
41
41
|
config = self._make_config(tmp_path)
|
|
@@ -43,7 +43,7 @@ class TestLegacyParseWorkdir:
|
|
|
43
43
|
def fake_parse(root: _t.PathT, *, config: Configuration) -> ScmVersion | None:
|
|
44
44
|
return ScmVersion(tag=Version("1.0.0"), config=config)
|
|
45
45
|
|
|
46
|
-
wd = LegacyParseWorkdir(path=tmp_path,
|
|
46
|
+
wd = LegacyParseWorkdir(path=tmp_path, parse_fn=fake_parse)
|
|
47
47
|
wd._config = config
|
|
48
48
|
|
|
49
49
|
with warnings.catch_warnings(record=True) as w:
|
|
@@ -62,7 +62,7 @@ class TestLegacyParseWorkdir:
|
|
|
62
62
|
def fake_parse(root: _t.PathT, *, config: Configuration) -> ScmVersion | None:
|
|
63
63
|
return None
|
|
64
64
|
|
|
65
|
-
wd = LegacyParseWorkdir(path=tmp_path,
|
|
65
|
+
wd = LegacyParseWorkdir(path=tmp_path, parse_fn=fake_parse)
|
|
66
66
|
wd._config = config
|
|
67
67
|
|
|
68
68
|
with warnings.catch_warnings(record=True):
|
|
@@ -75,7 +75,7 @@ class TestLegacyParseWorkdir:
|
|
|
75
75
|
def bad_parse(root: _t.PathT, *, config: Configuration) -> ScmVersion | None:
|
|
76
76
|
return "not-a-version" # type: ignore[return-value]
|
|
77
77
|
|
|
78
|
-
wd = LegacyParseWorkdir(path=tmp_path,
|
|
78
|
+
wd = LegacyParseWorkdir(path=tmp_path, parse_fn=bad_parse)
|
|
79
79
|
wd._config = config
|
|
80
80
|
|
|
81
81
|
with warnings.catch_warnings(record=True):
|
|
@@ -86,6 +86,48 @@ class TestDiscoverWorkdirGit:
|
|
|
86
86
|
assert isinstance(result, ScmWorkdir)
|
|
87
87
|
assert result.path == tmp_path
|
|
88
88
|
|
|
89
|
+
@pytest.mark.issue(1440)
|
|
90
|
+
def test_list_tracked_files_scoped_to_project_root(self, tmp_path: Path) -> None:
|
|
91
|
+
"""In a monorepo, list_tracked_files() must only return files under project_root."""
|
|
92
|
+
# Resolve to real path to avoid Windows 8.3 short name mismatches
|
|
93
|
+
tmp_path = tmp_path.resolve()
|
|
94
|
+
_git_init(tmp_path)
|
|
95
|
+
|
|
96
|
+
# Create files in two sibling projects
|
|
97
|
+
proj_a = tmp_path / "project-a"
|
|
98
|
+
proj_b = tmp_path / "project-b"
|
|
99
|
+
proj_a.mkdir()
|
|
100
|
+
proj_b.mkdir()
|
|
101
|
+
(proj_a / "a.py").write_text("# a", encoding="utf-8")
|
|
102
|
+
(proj_b / "b.py").write_text("# b", encoding="utf-8")
|
|
103
|
+
|
|
104
|
+
subprocess.run(
|
|
105
|
+
["git", "add", "."], cwd=tmp_path, check=True, capture_output=True
|
|
106
|
+
)
|
|
107
|
+
subprocess.run(
|
|
108
|
+
["git", "commit", "-m", "add projects"],
|
|
109
|
+
cwd=tmp_path,
|
|
110
|
+
check=True,
|
|
111
|
+
capture_output=True,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Discover from project-a with root=".." (monorepo pattern)
|
|
115
|
+
config = Configuration(
|
|
116
|
+
relative_to=str(proj_a / "pyproject.toml"),
|
|
117
|
+
root="..",
|
|
118
|
+
)
|
|
119
|
+
result = discover_workdir(config)
|
|
120
|
+
assert result is not None
|
|
121
|
+
assert isinstance(result, ScmWorkdir)
|
|
122
|
+
assert result.path == tmp_path
|
|
123
|
+
assert result.project_root == proj_a
|
|
124
|
+
|
|
125
|
+
files = list(result.list_tracked_files())
|
|
126
|
+
# Should only contain files under project-a, not project-b or repo root
|
|
127
|
+
assert any("a.py" in f for f in files), f"expected a.py in {files}"
|
|
128
|
+
assert not any("b.py" in f for f in files), f"unexpected b.py in {files}"
|
|
129
|
+
assert not any("dummy" in f for f in files), f"unexpected dummy in {files}"
|
|
130
|
+
|
|
89
131
|
|
|
90
132
|
class TestDiscoverWorkdirFallback:
|
|
91
133
|
def test_discovers_pkginfo(self, tmp_path: Path) -> None:
|
|
@@ -261,6 +303,27 @@ class TestMetadataWorkdir:
|
|
|
261
303
|
files = wd.list_tracked_files()
|
|
262
304
|
assert files == ["src/pkg/__init__.py"]
|
|
263
305
|
|
|
306
|
+
@pytest.mark.issue(1439)
|
|
307
|
+
def test_custom_tag_regex_does_not_break_metadata(self, tmp_path: Path) -> None:
|
|
308
|
+
"""Stored tags are already parsed; custom tag_regex must not re-parse them."""
|
|
309
|
+
data = ScmVersionData(
|
|
310
|
+
tag="1.5.5",
|
|
311
|
+
distance=0,
|
|
312
|
+
node="gabc1234",
|
|
313
|
+
dirty=False,
|
|
314
|
+
branch="main",
|
|
315
|
+
node_date=None,
|
|
316
|
+
)
|
|
317
|
+
write_scm_version_data(tmp_path, data)
|
|
318
|
+
|
|
319
|
+
config = Configuration(
|
|
320
|
+
tag_regex=r"^cuda-pathfinder-(?P<version>v\d+\.\d+\.\d+(?:[ab]\d+)?)",
|
|
321
|
+
)
|
|
322
|
+
wd = MetadataWorkdir(path=tmp_path, metadata_dir=tmp_path, _config=config)
|
|
323
|
+
version = wd.get_scm_version()
|
|
324
|
+
assert version is not None
|
|
325
|
+
assert str(version.tag) == "1.5.5"
|
|
326
|
+
|
|
264
327
|
def test_missing_metadata_returns_none(self, tmp_path: Path) -> None:
|
|
265
328
|
config = Configuration()
|
|
266
329
|
wd = MetadataWorkdir(path=tmp_path, metadata_dir=tmp_path, _config=config)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_cli/git_archival_stable.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/__init__.py
RENAMED
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_common.py
RENAMED
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_standard.py
RENAMED
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning/_version_schemes/_towncrier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vcs_versioning-2.1.1 → vcs_versioning-2.1.2}/src/vcs_versioning.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|