vcs-versioning 2.1.2__tar.gz → 2.2.1__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.2 → vcs_versioning-2.2.1}/CHANGELOG.md +20 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/MANIFEST.in +1 -0
- {vcs_versioning-2.1.2/src/vcs_versioning.egg-info → vcs_versioning-2.2.1}/PKG-INFO +6 -4
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/pyproject.toml +26 -23
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_hg.py +0 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_cli/__init__.py +9 -2
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_compat.py +31 -16
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_config.py +0 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_entrypoints.py +3 -2
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_file_finders/__init__.py +39 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_file_finders/_git.py +2 -16
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_file_finders/_hg.py +2 -12
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_file_finders/_jj.py +2 -14
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_get_version_impl.py +32 -8
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_legacy_parse.py +11 -4
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_overrides.py +15 -5
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_pyproject_reading.py +7 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_run_cmd.py +7 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_scm_metadata.py +8 -7
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_scm_version.py +9 -9
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_toml.py +17 -7
- vcs_versioning-2.2.1/src/vcs_versioning/_types.py +40 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_cls.py +9 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_schemes/_standard.py +0 -4
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_worktree_discovery.py +3 -3
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1/src/vcs_versioning.egg-info}/PKG-INFO +6 -4
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning.egg-info/SOURCES.txt +1 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning.egg-info/requires.txt +1 -1
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_file_finders.py +1 -1
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_overrides_api.py +1 -4
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_regressions.py +35 -5
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_tag_config.py +46 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_workdir_discovery.py +0 -45
- vcs_versioning-2.2.1/uv.lock +314 -0
- vcs_versioning-2.1.2/src/vcs_versioning/_types.py +0 -29
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/LICENSE.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/README.md +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/setup.cfg +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/setup.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/__init__.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/__main__.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/__init__.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_discover_vcs.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_git.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_hg_git.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_jj.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_backends/_scm_workdir.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_cli/_args.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_cli/git_archival_full.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_cli/git_archival_stable.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_discover.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_dump_version.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_environment.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_exceptions.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_fallback_workdir.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_fallbacks.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_integration.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_integrator_helpers.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_log.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_modify_version.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_node_utils.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_paths.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_project_overrides.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_protocols.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_requirement_cls.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_test_utils.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_fields.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_inference.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_schemes/__init__.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_schemes/_common.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/_version_schemes/_towncrier.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/overrides.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/py.typed +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning/test_api.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning.egg-info/dependency_links.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning.egg-info/entry_points.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/src/vcs_versioning.egg-info/top_level.txt +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/__init__.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/conftest.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_better_root_errors.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_chain_api.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_compat.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_config.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_expect_parse.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_git.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_hg_git.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_integrator_helpers.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_internal_log_level.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_jj.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_legacy_parse.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_mercurial.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_overrides_env_reader.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_project_overrides.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_project_path.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_scm_metadata.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_version.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_version_scheme_towncrier.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_version_schemes.py +0 -0
- {vcs_versioning-2.1.2 → vcs_versioning-2.2.1}/testing_vcs/test_workdir_api.py +0 -0
|
@@ -2,11 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- towncrier release notes start -->
|
|
4
4
|
|
|
5
|
+
## 2.2.1 (2026-06-29)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fix crash in `_warn_if_tracked` when the version file target is a relative path
|
|
10
|
+
by resolving it against the project root before comparison.
|
|
11
|
+
Also warn (instead of crashing) when the target resolves outside the project root. ([#1451](https://github.com/pypa/setuptools-scm/issues/1451))
|
|
12
|
+
|
|
13
|
+
## 2.2.0 (2026-06-24)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Restore Python 3.8 and 3.9 support, re-enabling use as a build dependency for projects like scikit-build that still support these versions. ([#1445](https://github.com/pypa/setuptools-scm/issues/1445))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Miscellaneous
|
|
21
|
+
|
|
22
|
+
- Move PKG-INFO discovery tests to setuptools-scm where the entry points are registered. ([#1446](https://github.com/pypa/setuptools-scm/issues/1446))
|
|
23
|
+
|
|
5
24
|
## 2.1.2 (2026-06-23)
|
|
6
25
|
|
|
7
26
|
### Fixed
|
|
8
27
|
|
|
9
28
|
- 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))
|
|
29
|
+
- Scope `list_tracked_files` to `project_root` in monorepo setups so that sdists no longer include files from sibling projects when `root=".."` is configured. ([#1440](https://github.com/pypa/setuptools-scm/issues/1440))
|
|
10
30
|
|
|
11
31
|
## 2.1.1 (2026-06-23)
|
|
12
32
|
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vcs-versioning
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: the blessed package to manage your versions by vcs metadata
|
|
5
5
|
Author-email: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
|
|
6
|
-
License
|
|
6
|
+
License: MIT
|
|
7
7
|
Project-URL: Documentation, https://github.com/pypa/setuptools-scm#readme
|
|
8
8
|
Project-URL: Issues, https://github.com/pypa/setuptools-scm/issues
|
|
9
9
|
Project-URL: Source, https://github.com/pypa/setuptools-scm
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
12
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.8
|
|
19
21
|
Description-Content-Type: text/markdown
|
|
20
22
|
License-File: LICENSE.txt
|
|
21
23
|
Requires-Dist: packaging>=20
|
|
22
24
|
Requires-Dist: tomli>=1; python_version < "3.11"
|
|
23
|
-
Requires-Dist: typing-extensions; python_version < "3.11"
|
|
25
|
+
Requires-Dist: typing-extensions>=4.1; python_version < "3.11"
|
|
24
26
|
Dynamic: license-file
|
|
25
27
|
|
|
26
28
|
# vcs-versioning
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
[build-system]
|
|
7
7
|
build-backend = "setuptools.build_meta"
|
|
8
8
|
requires = [
|
|
9
|
-
"setuptools>=
|
|
9
|
+
"setuptools>=45",
|
|
10
10
|
"packaging>=20",
|
|
11
|
-
'typing-extensions; python_version < "3.11"',
|
|
11
|
+
'typing-extensions>=4.1; python_version < "3.11"',
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
[project]
|
|
@@ -17,15 +17,17 @@ description = "the blessed package to manage your versions by vcs metadata"
|
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
keywords = [
|
|
19
19
|
]
|
|
20
|
-
license = "MIT"
|
|
20
|
+
license = {text = "MIT"}
|
|
21
21
|
authors = [
|
|
22
22
|
{ name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" },
|
|
23
23
|
]
|
|
24
|
-
requires-python = ">=3.
|
|
24
|
+
requires-python = ">=3.8"
|
|
25
25
|
classifiers = [
|
|
26
26
|
"Development Status :: 4 - Beta",
|
|
27
27
|
"Programming Language :: Python",
|
|
28
28
|
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
+
"Programming Language :: Python :: 3.8",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
29
31
|
"Programming Language :: Python :: 3.10",
|
|
30
32
|
"Programming Language :: Python :: 3.11",
|
|
31
33
|
"Programming Language :: Python :: 3.12",
|
|
@@ -38,13 +40,12 @@ dynamic = [
|
|
|
38
40
|
dependencies = [
|
|
39
41
|
"packaging>=20",
|
|
40
42
|
'tomli>=1; python_version < "3.11"',
|
|
41
|
-
'typing-extensions; python_version < "3.11"',
|
|
43
|
+
'typing-extensions>=4.1; python_version < "3.11"',
|
|
42
44
|
]
|
|
43
45
|
|
|
44
46
|
[dependency-groups]
|
|
45
47
|
test = [
|
|
46
48
|
"pytest",
|
|
47
|
-
"pytest-cov",
|
|
48
49
|
"pytest-xdist",
|
|
49
50
|
]
|
|
50
51
|
|
|
@@ -103,27 +104,29 @@ tag_regex = "^vcs-versioning-(?P<version>v?\\d+(?:\\.\\d+){0,2}[^\\+]*)(?:\\+.*)
|
|
|
103
104
|
fallback_version = "0.1.0"
|
|
104
105
|
scm.git.describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "vcs-versioning-*"]
|
|
105
106
|
|
|
106
|
-
[tool.coverage.run]
|
|
107
|
-
branch = true
|
|
108
|
-
parallel = true
|
|
109
|
-
omit = [
|
|
110
|
-
"vcs_versioning/__about__.py",
|
|
111
|
-
]
|
|
112
|
-
|
|
113
|
-
[tool.coverage.report]
|
|
114
|
-
exclude_lines = [
|
|
115
|
-
"no cov",
|
|
116
|
-
"if __name__ == .__main__.:",
|
|
117
|
-
"if TYPE_CHECKING:",
|
|
118
|
-
]
|
|
119
|
-
|
|
120
107
|
[tool.pytest.ini_options]
|
|
108
|
+
minversion = "8.2"
|
|
121
109
|
testpaths = ["testing_vcs"]
|
|
122
110
|
python_files = ["test_*.py"]
|
|
123
|
-
|
|
111
|
+
xfail_strict = true
|
|
112
|
+
addopts = ["-ra", "--strict-config", "--strict-markers", "-p", "vcs_versioning.test_api"]
|
|
113
|
+
timeout = 300
|
|
114
|
+
filterwarnings = [
|
|
115
|
+
"error",
|
|
116
|
+
"ignore:.*tool\\.setuptools_scm.*",
|
|
117
|
+
"ignore:.*git archive did not support describe output.*:UserWarning",
|
|
118
|
+
"ignore:.*unprocessed git archival found.*:UserWarning",
|
|
119
|
+
"ignore:.*without VcsEnvironment.*:DeprecationWarning",
|
|
120
|
+
"ignore:.*Passing config to workdir.*:DeprecationWarning",
|
|
121
|
+
"ignore:.*tag_regex.*is deprecated.*:DeprecationWarning",
|
|
122
|
+
"ignore:.*write_to_source.*:DeprecationWarning",
|
|
123
|
+
"ignore:.*relative_to is expected to be a file.*:UserWarning",
|
|
124
|
+
]
|
|
125
|
+
log_level = "debug"
|
|
126
|
+
log_cli_level = "info"
|
|
124
127
|
markers = [
|
|
125
|
-
"issue:
|
|
126
|
-
"no_init:
|
|
128
|
+
"issue(id): reference to github issue",
|
|
129
|
+
"no_init: skip initializing the VCS repository in the test fixture",
|
|
127
130
|
"skip_commit: allows to skip committing in the helpers",
|
|
128
131
|
]
|
|
129
132
|
|
|
@@ -4,7 +4,11 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
6
|
from collections.abc import Iterable
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
if sys.version_info >= (3, 9):
|
|
9
|
+
from importlib.resources import files
|
|
10
|
+
else:
|
|
11
|
+
from importlib.resources import read_text as _read_text
|
|
8
12
|
from pathlib import Path
|
|
9
13
|
from typing import TypedDict
|
|
10
14
|
|
|
@@ -182,7 +186,10 @@ def _create_archival_file(opts: CliNamespace, config: Configuration) -> int:
|
|
|
182
186
|
assert opts.archival_template is not None
|
|
183
187
|
|
|
184
188
|
# Load template content from package resources
|
|
185
|
-
|
|
189
|
+
if sys.version_info >= (3, 9):
|
|
190
|
+
content = files(__package__).joinpath(opts.archival_template).read_text("utf-8")
|
|
191
|
+
else:
|
|
192
|
+
content = _read_text(__package__, opts.archival_template, encoding="utf-8")
|
|
186
193
|
|
|
187
194
|
# Print appropriate message based on template
|
|
188
195
|
if opts.archival_template == "git_archival_stable.txt":
|
|
@@ -3,10 +3,38 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
|
|
6
|
+
import sys
|
|
7
|
+
from importlib.metadata import entry_points as _stdlib_entry_points
|
|
8
|
+
from typing import TYPE_CHECKING, Union
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
if sys.version_info >= (3, 10):
|
|
11
|
+
from typing import TypeAlias
|
|
12
|
+
else:
|
|
13
|
+
from typing_extensions import TypeAlias
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from importlib.metadata import EntryPoint
|
|
17
|
+
|
|
18
|
+
PathT: TypeAlias = os.PathLike[str] | str
|
|
19
|
+
else:
|
|
20
|
+
PathT: TypeAlias = Union[os.PathLike, str]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if sys.version_info >= (3, 10):
|
|
24
|
+
from importlib.metadata import entry_points as entry_points
|
|
25
|
+
else:
|
|
26
|
+
|
|
27
|
+
def entry_points(
|
|
28
|
+
**params: str,
|
|
29
|
+
) -> list[EntryPoint]:
|
|
30
|
+
"""Backport of entry_points(group=...) for Python 3.8/3.9."""
|
|
31
|
+
groups = _stdlib_entry_points()
|
|
32
|
+
group = params.get("group", "")
|
|
33
|
+
name = params.get("name")
|
|
34
|
+
eps = list(groups.get(group, [])) # type: ignore[call-overload]
|
|
35
|
+
if name is not None:
|
|
36
|
+
eps = [ep for ep in eps if ep.name == name]
|
|
37
|
+
return eps
|
|
10
38
|
|
|
11
39
|
|
|
12
40
|
def normalize_path_for_assertion(path: str) -> str:
|
|
@@ -58,19 +86,6 @@ def strip_path_suffix(
|
|
|
58
86
|
return full_path[: -len(suffix_path)]
|
|
59
87
|
|
|
60
88
|
|
|
61
|
-
# Legacy aliases for backward compatibility during transition
|
|
62
|
-
def assert_path_endswith(
|
|
63
|
-
full_path: str, suffix_path: str, error_msg: str | None = None
|
|
64
|
-
) -> None:
|
|
65
|
-
"""Legacy alias - use strip_path_suffix instead."""
|
|
66
|
-
strip_path_suffix(full_path, suffix_path, error_msg)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def compute_path_prefix(full_path: str, suffix_path: str) -> str:
|
|
70
|
-
"""Legacy alias - use strip_path_suffix instead."""
|
|
71
|
-
return strip_path_suffix(full_path, suffix_path)
|
|
72
|
-
|
|
73
|
-
|
|
74
89
|
def norm_real(path: PathT) -> str:
|
|
75
90
|
"""Normalize and resolve a path (combining normcase and realpath).
|
|
76
91
|
|
|
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from collections.abc import Callable, Iterator
|
|
5
5
|
from importlib import metadata as im
|
|
6
|
-
from importlib.metadata import entry_points
|
|
7
6
|
from typing import TYPE_CHECKING, Any, cast
|
|
8
7
|
|
|
8
|
+
from ._compat import entry_points
|
|
9
|
+
|
|
9
10
|
__all__ = [
|
|
10
11
|
"entry_points",
|
|
11
12
|
"im",
|
|
@@ -65,7 +66,7 @@ def _iter_version_schemes(
|
|
|
65
66
|
or _get_from_object_reference_str(scheme_value, entrypoint),
|
|
66
67
|
)
|
|
67
68
|
|
|
68
|
-
if isinstance(scheme_value, list
|
|
69
|
+
if isinstance(scheme_value, (list, tuple)):
|
|
69
70
|
for variant in scheme_value:
|
|
70
71
|
if variant not in _memo:
|
|
71
72
|
_memo.add(variant)
|
|
@@ -2,8 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
-
|
|
6
|
-
from
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
7
|
+
|
|
8
|
+
if sys.version_info >= (3, 10):
|
|
9
|
+
from typing import TypeGuard
|
|
10
|
+
else:
|
|
11
|
+
from typing_extensions import TypeGuard
|
|
7
12
|
|
|
8
13
|
from .. import _types as _t
|
|
9
14
|
from .._compat import norm_real
|
|
@@ -135,4 +140,35 @@ def find_files(path: _t.PathT = "") -> list[str]:
|
|
|
135
140
|
return []
|
|
136
141
|
|
|
137
142
|
|
|
138
|
-
|
|
143
|
+
def collect_files_and_dirs(
|
|
144
|
+
raw_names: Iterable[str], toplevel: str
|
|
145
|
+
) -> tuple[set[str], set[str]]:
|
|
146
|
+
"""Normalize VCS file listings into absolute ``(files, dirs)`` sets.
|
|
147
|
+
|
|
148
|
+
Each backend produces a list of relative paths from its own command
|
|
149
|
+
(``git ls-files``, ``hg files``, ``jj file list``). This helper
|
|
150
|
+
normalizes case and separators, joins with *toplevel*, and collects
|
|
151
|
+
the directory ancestry — the same loop that was previously duplicated
|
|
152
|
+
in every backend.
|
|
153
|
+
"""
|
|
154
|
+
files: set[str] = set()
|
|
155
|
+
dirs: set[str] = {toplevel}
|
|
156
|
+
for name in raw_names:
|
|
157
|
+
if not name:
|
|
158
|
+
continue
|
|
159
|
+
name = os.path.normcase(name).replace("/", os.path.sep)
|
|
160
|
+
fullname = os.path.join(toplevel, name)
|
|
161
|
+
files.add(fullname)
|
|
162
|
+
dirname = os.path.dirname(fullname)
|
|
163
|
+
while len(dirname) > len(toplevel) and dirname not in dirs:
|
|
164
|
+
dirs.add(dirname)
|
|
165
|
+
dirname = os.path.dirname(dirname)
|
|
166
|
+
return files, dirs
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
__all__ = [
|
|
170
|
+
"scm_find_files",
|
|
171
|
+
"is_toplevel_acceptable",
|
|
172
|
+
"find_files",
|
|
173
|
+
"collect_files_and_dirs",
|
|
174
|
+
]
|
|
@@ -10,7 +10,7 @@ from .._backends._git import run_git
|
|
|
10
10
|
from .._compat import norm_real, strip_path_suffix
|
|
11
11
|
from .._integration import data_from_mime
|
|
12
12
|
from .._run_cmd import run as _run
|
|
13
|
-
from . import is_toplevel_acceptable, scm_find_files
|
|
13
|
+
from . import collect_files_and_dirs, is_toplevel_acceptable, scm_find_files
|
|
14
14
|
|
|
15
15
|
log = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -85,22 +85,8 @@ 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
88
|
toplevel = norm_real(toplevel)
|
|
91
|
-
|
|
92
|
-
git_dirs: set[str] = {toplevel}
|
|
93
|
-
for name in res.stdout.rstrip("\0").split("\0"):
|
|
94
|
-
if not name:
|
|
95
|
-
continue
|
|
96
|
-
name = os.path.normcase(name).replace("/", os.path.sep)
|
|
97
|
-
fullname = os.path.join(toplevel, name)
|
|
98
|
-
git_files.add(fullname)
|
|
99
|
-
dirname = os.path.dirname(fullname)
|
|
100
|
-
while len(dirname) > len(toplevel) and dirname not in git_dirs:
|
|
101
|
-
git_dirs.add(dirname)
|
|
102
|
-
dirname = os.path.dirname(dirname)
|
|
103
|
-
return git_files, git_dirs
|
|
89
|
+
return collect_files_and_dirs(res.stdout.rstrip("\0").split("\0"), toplevel)
|
|
104
90
|
|
|
105
91
|
|
|
106
92
|
def git_find_files(path: _t.PathT = "") -> list[str]:
|
|
@@ -8,7 +8,7 @@ from .. import _types as _t
|
|
|
8
8
|
from .._backends._hg import run_hg
|
|
9
9
|
from .._compat import norm_real
|
|
10
10
|
from .._integration import data_from_mime
|
|
11
|
-
from . import is_toplevel_acceptable, scm_find_files
|
|
11
|
+
from . import collect_files_and_dirs, is_toplevel_acceptable, scm_find_files
|
|
12
12
|
|
|
13
13
|
log = logging.getLogger(__name__)
|
|
14
14
|
|
|
@@ -34,20 +34,10 @@ def _hg_ls_files_and_dirs(
|
|
|
34
34
|
hg_command: str | None = None,
|
|
35
35
|
timeout: int | None = None,
|
|
36
36
|
) -> tuple[set[str], set[str]]:
|
|
37
|
-
hg_files: set[str] = set()
|
|
38
|
-
hg_dirs = {toplevel}
|
|
39
37
|
res = run_hg(["files"], cwd=toplevel, hg_command=hg_command, timeout=timeout)
|
|
40
38
|
if res.returncode:
|
|
41
39
|
return set(), set()
|
|
42
|
-
|
|
43
|
-
name = os.path.normcase(name).replace("/", os.path.sep)
|
|
44
|
-
fullname = os.path.join(toplevel, name)
|
|
45
|
-
hg_files.add(fullname)
|
|
46
|
-
dirname = os.path.dirname(fullname)
|
|
47
|
-
while len(dirname) > len(toplevel) and dirname not in hg_dirs:
|
|
48
|
-
hg_dirs.add(dirname)
|
|
49
|
-
dirname = os.path.dirname(dirname)
|
|
50
|
-
return hg_files, hg_dirs
|
|
40
|
+
return collect_files_and_dirs(res.stdout.splitlines(), toplevel)
|
|
51
41
|
|
|
52
42
|
|
|
53
43
|
def hg_find_files(path: str = "") -> list[str]:
|
|
@@ -13,7 +13,7 @@ import subprocess
|
|
|
13
13
|
from .. import _types as _t
|
|
14
14
|
from .._compat import norm_real
|
|
15
15
|
from .._run_cmd import run as _run
|
|
16
|
-
from . import is_toplevel_acceptable, scm_find_files
|
|
16
|
+
from . import collect_files_and_dirs, is_toplevel_acceptable, scm_find_files
|
|
17
17
|
|
|
18
18
|
log = logging.getLogger(__name__)
|
|
19
19
|
|
|
@@ -51,19 +51,7 @@ def _jj_ls_files_and_dirs(
|
|
|
51
51
|
log.error("listing jj files failed - pretending there aren't any")
|
|
52
52
|
return set(), set()
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
jj_dirs: set[str] = {toplevel}
|
|
56
|
-
for name in res.stdout.strip().split("\n"):
|
|
57
|
-
if not name:
|
|
58
|
-
continue
|
|
59
|
-
name = os.path.normcase(name).replace("/", os.path.sep)
|
|
60
|
-
fullname = os.path.join(toplevel, name)
|
|
61
|
-
jj_files.add(fullname)
|
|
62
|
-
dirname = os.path.dirname(fullname)
|
|
63
|
-
while len(dirname) > len(toplevel) and dirname not in jj_dirs:
|
|
64
|
-
jj_dirs.add(dirname)
|
|
65
|
-
dirname = os.path.dirname(dirname)
|
|
66
|
-
return jj_files, jj_dirs
|
|
54
|
+
return collect_files_and_dirs(res.stdout.strip().split("\n"), toplevel)
|
|
67
55
|
|
|
68
56
|
|
|
69
57
|
def jj_find_files(path: _t.PathT = "") -> list[str]:
|
|
@@ -6,13 +6,18 @@ import re
|
|
|
6
6
|
import warnings
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from re import Pattern
|
|
9
|
-
from typing import Any, NoReturn
|
|
9
|
+
from typing import TYPE_CHECKING, Any, NoReturn
|
|
10
10
|
|
|
11
11
|
from . import _config
|
|
12
12
|
from . import _types as _t
|
|
13
13
|
from ._config import Configuration, TagConfiguration
|
|
14
14
|
from ._environment import resolve_runtime_env
|
|
15
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ._config import ParseFunction
|
|
18
|
+
from ._environment import VcsEnvironment
|
|
19
|
+
from ._version_cls import Version as _VersionType
|
|
20
|
+
|
|
16
21
|
# Backward-compat re-export used by vcs-versioning/setup.py
|
|
17
22
|
from ._legacy_parse import (
|
|
18
23
|
resolved_fallback_root as resolved_fallback_root, # noqa: F401
|
|
@@ -90,15 +95,31 @@ def _resolve_version(config: Configuration) -> ScmVersion | None:
|
|
|
90
95
|
return None
|
|
91
96
|
|
|
92
97
|
|
|
93
|
-
def _warn_if_tracked(target: Path, root: Path, config: Configuration) ->
|
|
98
|
+
def _warn_if_tracked(target: Path, root: Path, config: Configuration) -> bool:
|
|
94
99
|
"""Warn when *target* is tracked in version control (#468).
|
|
95
100
|
|
|
96
101
|
Writing a version file that is tracked makes ``git describe --dirty``
|
|
97
102
|
report a dirty tree on tag checkouts, causing wrong version numbers.
|
|
103
|
+
|
|
104
|
+
Returns False if target resolves outside root (caller should skip the write).
|
|
98
105
|
"""
|
|
99
106
|
from ._backends._git import GitWorkdir
|
|
100
107
|
from ._backends._hg import HgWorkdir
|
|
101
108
|
|
|
109
|
+
if not target.is_absolute():
|
|
110
|
+
target = root / target
|
|
111
|
+
resolved_target = target.resolve()
|
|
112
|
+
resolved_root = root.resolve()
|
|
113
|
+
try:
|
|
114
|
+
resolved_target.relative_to(resolved_root)
|
|
115
|
+
except ValueError:
|
|
116
|
+
# todo: emit as GitHub Actions warning via ::warning:: syntax
|
|
117
|
+
warnings.warn(
|
|
118
|
+
f"version file target {target} resolves outside of the project root {root}",
|
|
119
|
+
stacklevel=2,
|
|
120
|
+
)
|
|
121
|
+
return False
|
|
122
|
+
|
|
102
123
|
for workdir_cls in (GitWorkdir, HgWorkdir):
|
|
103
124
|
try:
|
|
104
125
|
wd = workdir_cls.from_potential_worktree(root, config)
|
|
@@ -113,7 +134,8 @@ def _warn_if_tracked(target: Path, root: Path, config: Configuration) -> None:
|
|
|
113
134
|
" See https://github.com/pypa/setuptools-scm/issues/468",
|
|
114
135
|
stacklevel=3,
|
|
115
136
|
)
|
|
116
|
-
return
|
|
137
|
+
return True
|
|
138
|
+
return True
|
|
117
139
|
|
|
118
140
|
|
|
119
141
|
def write_version_files(
|
|
@@ -125,7 +147,8 @@ def write_version_files(
|
|
|
125
147
|
|
|
126
148
|
write_to = Path(config.write_to)
|
|
127
149
|
target = root / write_to if not write_to.is_absolute() else write_to
|
|
128
|
-
_warn_if_tracked(target, root, config)
|
|
150
|
+
if not _warn_if_tracked(target, root, config):
|
|
151
|
+
return
|
|
129
152
|
|
|
130
153
|
dump_version(
|
|
131
154
|
root=config.root,
|
|
@@ -142,7 +165,8 @@ def write_version_files(
|
|
|
142
165
|
# todo: use a better name than fallback root
|
|
143
166
|
assert config.relative_to is not None
|
|
144
167
|
target = Path(config.relative_to).parent.joinpath(version_file)
|
|
145
|
-
_warn_if_tracked(target, root, config)
|
|
168
|
+
if not _warn_if_tracked(target, root, config):
|
|
169
|
+
return
|
|
146
170
|
|
|
147
171
|
write_version_to_path(
|
|
148
172
|
target,
|
|
@@ -252,14 +276,14 @@ def get_version(
|
|
|
252
276
|
parentdir_prefix_version: str | None = None,
|
|
253
277
|
fallback_version: str | None = None,
|
|
254
278
|
fallback_root: _t.PathT = ".",
|
|
255
|
-
parse:
|
|
279
|
+
parse: ParseFunction | None = None,
|
|
256
280
|
git_describe_command: _t.CMD_TYPE | None = None,
|
|
257
281
|
dist_name: str | None = None,
|
|
258
|
-
version_cls:
|
|
282
|
+
version_cls: type[_VersionType] | str | None = None,
|
|
259
283
|
normalize: bool = True,
|
|
260
284
|
search_parent_directories: bool = False,
|
|
261
285
|
scm: dict[str, Any] | None = None,
|
|
262
|
-
_env:
|
|
286
|
+
_env: VcsEnvironment | None = None,
|
|
263
287
|
) -> str:
|
|
264
288
|
"""
|
|
265
289
|
If supplied, relative_to should be a file from which root may
|
|
@@ -13,8 +13,6 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
import warnings
|
|
16
|
-
from dataclasses import dataclass
|
|
17
|
-
from dataclasses import field as dc_field
|
|
18
16
|
from pathlib import Path
|
|
19
17
|
from typing import TYPE_CHECKING
|
|
20
18
|
|
|
@@ -29,7 +27,6 @@ if TYPE_CHECKING:
|
|
|
29
27
|
log = logging.getLogger(__name__)
|
|
30
28
|
|
|
31
29
|
|
|
32
|
-
@dataclass()
|
|
33
30
|
class LegacyParseWorkdir(ScmWorkdir):
|
|
34
31
|
"""Wraps a legacy ``config.parse`` callable as a workdir.
|
|
35
32
|
|
|
@@ -38,7 +35,17 @@ class LegacyParseWorkdir(ScmWorkdir):
|
|
|
38
35
|
entry-point group.
|
|
39
36
|
"""
|
|
40
37
|
|
|
41
|
-
parse_fn: ParseFunction
|
|
38
|
+
parse_fn: ParseFunction
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
path: Path,
|
|
43
|
+
*,
|
|
44
|
+
parse_fn: ParseFunction,
|
|
45
|
+
_config: Configuration | None = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
super().__init__(path=path, _config=_config)
|
|
48
|
+
self.parse_fn = parse_fn
|
|
42
49
|
|
|
43
50
|
def get_scm_version(self) -> ScmVersion | None:
|
|
44
51
|
warnings.warn(
|
|
@@ -19,7 +19,7 @@ from packaging.utils import canonicalize_name
|
|
|
19
19
|
|
|
20
20
|
from . import _config
|
|
21
21
|
from . import _types as _t
|
|
22
|
-
from ._scm_version import ScmVersion, meta
|
|
22
|
+
from ._scm_version import ScmVersion, meta
|
|
23
23
|
from ._version_cls import Version as _Version
|
|
24
24
|
|
|
25
25
|
log = logging.getLogger(__name__)
|
|
@@ -207,15 +207,25 @@ def _apply_metadata_overrides(
|
|
|
207
207
|
|
|
208
208
|
log.info("Applying metadata overrides: %s", metadata_overrides)
|
|
209
209
|
|
|
210
|
-
# Get
|
|
211
|
-
|
|
210
|
+
# Get valid field names from PretendMetadataDict for validation.
|
|
211
|
+
# Use __annotations__ keys directly instead of get_type_hints() to avoid
|
|
212
|
+
# evaluating PEP 604 forward references on Python <3.10.
|
|
213
|
+
valid_fields = frozenset(PretendMetadataDict.__annotations__)
|
|
214
|
+
|
|
215
|
+
# Try to resolve actual types for runtime validation (best-effort)
|
|
216
|
+
try:
|
|
217
|
+
field_types: dict[str, Any] | None = get_type_hints(PretendMetadataDict)
|
|
218
|
+
except TypeError:
|
|
219
|
+
field_types = None
|
|
212
220
|
|
|
213
221
|
# Apply each override individually using dataclasses.replace
|
|
214
222
|
result = scm_version
|
|
215
223
|
|
|
216
224
|
for field, value in metadata_overrides.items():
|
|
217
|
-
|
|
218
|
-
|
|
225
|
+
if field not in valid_fields:
|
|
226
|
+
continue
|
|
227
|
+
# Runtime type validation (only when type hints are resolvable)
|
|
228
|
+
if field_types is not None and field in field_types:
|
|
219
229
|
expected_type = field_types[field]
|
|
220
230
|
# Handle Optional/Union types (e.g., str | None)
|
|
221
231
|
if hasattr(expected_type, "__args__"):
|
|
@@ -9,7 +9,11 @@ import warnings
|
|
|
9
9
|
from collections.abc import Sequence
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
if sys.version_info >= (3, 10):
|
|
14
|
+
from typing import TypeAlias
|
|
15
|
+
else:
|
|
16
|
+
from typing_extensions import TypeAlias
|
|
13
17
|
|
|
14
18
|
if sys.version_info >= (3, 11):
|
|
15
19
|
from typing import Self
|
|
@@ -158,7 +162,7 @@ class PyProjectData:
|
|
|
158
162
|
|
|
159
163
|
# Testing injection type for configuration reading
|
|
160
164
|
GivenPyProjectResult: TypeAlias = (
|
|
161
|
-
PyProjectData | InvalidTomlError | FileNotFoundError | None
|
|
165
|
+
"PyProjectData | InvalidTomlError | FileNotFoundError | None"
|
|
162
166
|
)
|
|
163
167
|
|
|
164
168
|
|
|
@@ -201,7 +205,7 @@ def read_pyproject(
|
|
|
201
205
|
if _given_result is not None:
|
|
202
206
|
if isinstance(_given_result, PyProjectData):
|
|
203
207
|
return _given_result
|
|
204
|
-
if isinstance(_given_result, InvalidTomlError
|
|
208
|
+
if isinstance(_given_result, (InvalidTomlError, FileNotFoundError)):
|
|
205
209
|
raise _given_result
|
|
206
210
|
|
|
207
211
|
if _given_definition is not None:
|