vcs-versioning 2.0.1__tar.gz → 2.1.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.0.1 → vcs_versioning-2.1.1}/CHANGELOG.md +18 -0
- {vcs_versioning-2.0.1/src/vcs_versioning.egg-info → vcs_versioning-2.1.1}/PKG-INFO +1 -1
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/__init__.py +9 -3
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_config.py +19 -19
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_environment.py +82 -3
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_get_version_impl.py +26 -3
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_integrator_helpers.py +11 -5
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_overrides.py +18 -18
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_run_cmd.py +1 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_inference.py +9 -2
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_worktree_discovery.py +20 -8
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1/src/vcs_versioning.egg-info}/PKG-INFO +1 -1
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_chain_api.py +103 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_config.py +16 -1
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_regressions.py +24 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_tag_config.py +21 -30
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_workdir_discovery.py +34 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/LICENSE.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/MANIFEST.in +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/README.md +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/pyproject.toml +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/setup.cfg +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/setup.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/__main__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/__init__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_discover_vcs.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_git.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_hg.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_hg_git.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_jj.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_backends/_scm_workdir.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_cli/__init__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_cli/_args.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_cli/git_archival_full.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_cli/git_archival_stable.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_compat.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_discover.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_dump_version.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_entrypoints.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_exceptions.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_fallback_workdir.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_fallbacks.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_file_finders/__init__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_file_finders/_git.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_file_finders/_hg.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_file_finders/_jj.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_integration.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_legacy_parse.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_log.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_modify_version.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_node_utils.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_paths.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_project_overrides.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_protocols.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_pyproject_reading.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_requirement_cls.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_scm_metadata.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_scm_version.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_test_utils.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_toml.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_types.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_cls.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_fields.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/__init__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/_common.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/_standard.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/_towncrier.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/overrides.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/py.typed +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/test_api.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning.egg-info/SOURCES.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning.egg-info/dependency_links.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning.egg-info/entry_points.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning.egg-info/requires.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning.egg-info/top_level.txt +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/__init__.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/conftest.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_better_root_errors.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_compat.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_expect_parse.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_file_finders.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_git.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_hg_git.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_integrator_helpers.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_internal_log_level.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_jj.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_legacy_parse.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_mercurial.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_overrides_api.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_overrides_env_reader.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_project_overrides.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_project_path.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_scm_metadata.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_version.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_version_scheme_towncrier.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_version_schemes.py +0 -0
- {vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/testing_vcs/test_workdir_api.py +0 -0
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- towncrier release notes start -->
|
|
4
4
|
|
|
5
|
+
## 2.1.1 (2026-06-23)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fix spurious ``DeprecationWarning`` for ``tag_regex`` when using default value via ``get_version()``. ([#1434](https://github.com/pypa/setuptools-scm/issues/1434))
|
|
10
|
+
|
|
11
|
+
## 2.1.0 (2026-06-22)
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Add `VcsEnvironment.build_config_from_pyproject`, `build_config_from_data`, and `pyproject_tool_names` methods for canonical env-first configuration creation. ([#1424](https://github.com/pypa/setuptools-scm/issues/1424))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fix DeprecationWarning leak in pretend API by ensuring all public APIs attach VcsEnvironment to Configuration before accessing env-dependent properties. ([#1424](https://github.com/pypa/setuptools-scm/issues/1424))
|
|
21
|
+
- Fix fallback discovery so an unprocessed `.git_archival.txt` no longer shadows a valid `PKG-INFO` in PyPI sdists. ([#1431](https://github.com/pypa/setuptools-scm/issues/1431))
|
|
22
|
+
|
|
5
23
|
## 2.0.1 (2026-06-22)
|
|
6
24
|
|
|
7
25
|
### Fixed
|
|
@@ -25,6 +25,7 @@ def build_configuration_from_pyproject(
|
|
|
25
25
|
pyproject_data: PyProjectData,
|
|
26
26
|
*,
|
|
27
27
|
dist_name: str | None = None,
|
|
28
|
+
env: VcsEnvironment | None = None,
|
|
28
29
|
**integrator_overrides: Any,
|
|
29
30
|
) -> Configuration:
|
|
30
31
|
"""Build Configuration from PyProjectData with full workflow.
|
|
@@ -36,7 +37,7 @@ def build_configuration_from_pyproject(
|
|
|
36
37
|
2. Determine dist_name (argument > pyproject.project_name)
|
|
37
38
|
3. Apply integrator overrides (override config file)
|
|
38
39
|
4. Apply environment TOML overrides (highest priority)
|
|
39
|
-
5. Create and validate Configuration instance
|
|
40
|
+
5. Create and validate Configuration instance with VcsEnvironment attached
|
|
40
41
|
|
|
41
42
|
Integrators create PyProjectData themselves:
|
|
42
43
|
|
|
@@ -73,6 +74,8 @@ def build_configuration_from_pyproject(
|
|
|
73
74
|
Args:
|
|
74
75
|
pyproject_data: Parsed pyproject data (integrator creates this)
|
|
75
76
|
dist_name: Distribution name (overrides pyproject_data.project_name)
|
|
77
|
+
env: Optional VcsEnvironment. If None, resolves from the active
|
|
78
|
+
GlobalOverrides context or process environment.
|
|
76
79
|
**integrator_overrides: Integrator-provided config overrides
|
|
77
80
|
(override config file, but overridden by env)
|
|
78
81
|
|
|
@@ -88,9 +91,12 @@ def build_configuration_from_pyproject(
|
|
|
88
91
|
This allows integrators to provide their own transformations
|
|
89
92
|
while still respecting user environment variable overrides.
|
|
90
93
|
"""
|
|
91
|
-
from .
|
|
94
|
+
from ._environment import resolve_runtime_env
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
if env is None:
|
|
97
|
+
env = resolve_runtime_env()
|
|
98
|
+
|
|
99
|
+
return env.build_config_from_pyproject(
|
|
94
100
|
pyproject_data=pyproject_data,
|
|
95
101
|
dist_name=dist_name,
|
|
96
102
|
**integrator_overrides,
|
|
@@ -342,32 +342,23 @@ class Configuration:
|
|
|
342
342
|
"Cannot specify both 'tag_regex' (deprecated) and "
|
|
343
343
|
"'tag.regex'. Please use only 'tag.regex'."
|
|
344
344
|
)
|
|
345
|
-
# Replace with a new TagConfiguration to avoid mutating shared objects
|
|
346
345
|
self.tag = dataclasses.replace(
|
|
347
346
|
self.tag, regex=_check_tag_regex(tag_regex)
|
|
348
347
|
)
|
|
349
348
|
|
|
349
|
+
# TODO(#1429): re-introduce these warnings with non-conflicting logic
|
|
350
350
|
if self.tag.strict is None:
|
|
351
|
-
|
|
352
|
-
"tag.strict is not set
|
|
353
|
-
"tag matching). In a future major version the default will change "
|
|
354
|
-
"to True (require tags to contain a dot). "
|
|
355
|
-
"Set tag.strict = true or tag.strict = false explicitly in your "
|
|
356
|
-
"[tool.setuptools_scm] / [tool.vcs-versioning] config to silence "
|
|
357
|
-
"this warning.",
|
|
358
|
-
FutureWarning,
|
|
359
|
-
stacklevel=2,
|
|
351
|
+
log.debug(
|
|
352
|
+
"tag.strict is not set — defaults to False (permissive tag matching)"
|
|
360
353
|
)
|
|
361
354
|
|
|
362
355
|
if (
|
|
363
356
|
self.tag.prefix or self.tag.strict is not None
|
|
364
357
|
) and self.scm.git.describe_command is not None:
|
|
365
|
-
|
|
358
|
+
log.debug(
|
|
366
359
|
"Both tag.prefix/tag.strict and scm.git.describe_command are set. "
|
|
367
360
|
"The explicit describe_command takes precedence; tag.prefix and "
|
|
368
|
-
"tag.strict will have no effect on the git describe match pattern."
|
|
369
|
-
UserWarning,
|
|
370
|
-
stacklevel=2,
|
|
361
|
+
"tag.strict will have no effect on the git describe match pattern."
|
|
371
362
|
)
|
|
372
363
|
|
|
373
364
|
self._resolved_paths = resolve_paths(
|
|
@@ -450,6 +441,7 @@ class Configuration:
|
|
|
450
441
|
*,
|
|
451
442
|
tool_names: tuple[str, ...] | None = None,
|
|
452
443
|
env: Mapping[str, str] | None = None,
|
|
444
|
+
_env: VcsEnvironment | None = None,
|
|
453
445
|
**kwargs: Any,
|
|
454
446
|
) -> Configuration:
|
|
455
447
|
"""
|
|
@@ -462,6 +454,7 @@ class Configuration:
|
|
|
462
454
|
- dist_name: name of the distribution
|
|
463
455
|
- tool_names: env-var prefix order for TOML overrides
|
|
464
456
|
- env: environment mapping for TOML overrides (default: os.environ)
|
|
457
|
+
- _env: VcsEnvironment to attach to the resulting Configuration
|
|
465
458
|
- **kwargs: additional keyword arguments to pass to the Configuration constructor
|
|
466
459
|
"""
|
|
467
460
|
|
|
@@ -485,14 +478,20 @@ class Configuration:
|
|
|
485
478
|
args.update(project_overrides)
|
|
486
479
|
|
|
487
480
|
# Env overrides: highest priority
|
|
488
|
-
|
|
489
|
-
read_toml_overrides(args["dist_name"]
|
|
490
|
-
|
|
491
|
-
|
|
481
|
+
if _env is not None:
|
|
482
|
+
args.update(_env.read_toml_overrides(args["dist_name"]))
|
|
483
|
+
else:
|
|
484
|
+
args.update(
|
|
485
|
+
read_toml_overrides(args["dist_name"], tool_names=tool_names, env=env)
|
|
486
|
+
)
|
|
487
|
+
return cls.from_data(relative_to=relative_to, data=args, _env=_env)
|
|
492
488
|
|
|
493
489
|
@classmethod
|
|
494
490
|
def from_data(
|
|
495
|
-
cls,
|
|
491
|
+
cls,
|
|
492
|
+
relative_to: str | os.PathLike[str],
|
|
493
|
+
data: dict[str, Any],
|
|
494
|
+
_env: VcsEnvironment | None = None,
|
|
496
495
|
) -> Configuration:
|
|
497
496
|
"""
|
|
498
497
|
given configuration data
|
|
@@ -526,6 +525,7 @@ class Configuration:
|
|
|
526
525
|
version_cls=version_cls,
|
|
527
526
|
tag=tag_config,
|
|
528
527
|
scm=scm_config,
|
|
528
|
+
_env=_env,
|
|
529
529
|
**data,
|
|
530
530
|
)
|
|
531
531
|
|
|
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Any, Literal
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from pytest import MonkeyPatch
|
|
24
24
|
|
|
25
|
-
from . import _config, overrides
|
|
25
|
+
from . import _config, _overrides, overrides
|
|
26
26
|
|
|
27
27
|
log = logging.getLogger(__name__)
|
|
28
28
|
|
|
@@ -248,7 +248,86 @@ class VcsEnvironment:
|
|
|
248
248
|
from ._config import Configuration
|
|
249
249
|
|
|
250
250
|
config = Configuration.from_file(
|
|
251
|
-
tool_names=self.tool_names, env=self._env, **kwargs
|
|
251
|
+
tool_names=self.tool_names, env=self._env, _env=self, **kwargs
|
|
252
252
|
)
|
|
253
|
-
object.__setattr__(config, "_env", self)
|
|
254
253
|
return config
|
|
254
|
+
|
|
255
|
+
def build_config_from_data(
|
|
256
|
+
self,
|
|
257
|
+
relative_to: str | os.PathLike[str],
|
|
258
|
+
data: dict[str, Any],
|
|
259
|
+
) -> _config.Configuration:
|
|
260
|
+
"""Create a ``Configuration`` from pre-assembled data dict.
|
|
261
|
+
|
|
262
|
+
Use this when you have already extracted and merged configuration
|
|
263
|
+
data (e.g. from pyproject section + overrides) and want to build
|
|
264
|
+
a validated Configuration without re-reading files.
|
|
265
|
+
"""
|
|
266
|
+
from ._config import Configuration
|
|
267
|
+
|
|
268
|
+
return Configuration.from_data(relative_to=relative_to, data=data, _env=self)
|
|
269
|
+
|
|
270
|
+
def build_config_from_pyproject(
|
|
271
|
+
self,
|
|
272
|
+
pyproject_data: Any,
|
|
273
|
+
*,
|
|
274
|
+
dist_name: str | None = None,
|
|
275
|
+
**integrator_overrides: Any,
|
|
276
|
+
) -> _config.Configuration:
|
|
277
|
+
"""Create a ``Configuration`` from PyProjectData with full workflow.
|
|
278
|
+
|
|
279
|
+
Canonical entry point for integrators. Orchestrates:
|
|
280
|
+
1. Extract config from pyproject_data.section
|
|
281
|
+
2. Determine dist_name
|
|
282
|
+
3. Apply integrator overrides
|
|
283
|
+
4. Apply environment TOML overrides
|
|
284
|
+
5. Build and validate Configuration with this env attached
|
|
285
|
+
"""
|
|
286
|
+
from ._integrator_helpers import build_configuration_from_pyproject_internal
|
|
287
|
+
|
|
288
|
+
return build_configuration_from_pyproject_internal(
|
|
289
|
+
pyproject_data=pyproject_data,
|
|
290
|
+
dist_name=dist_name,
|
|
291
|
+
env=self,
|
|
292
|
+
**integrator_overrides,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def pyproject_tool_names(self) -> list[str]:
|
|
296
|
+
"""Derive TOML section names from env-var prefixes.
|
|
297
|
+
|
|
298
|
+
Maps env-var prefixes to their canonical pyproject [tool.X] section
|
|
299
|
+
names. The ``VCS_VERSIONING`` prefix always maps to ``vcs-versioning``
|
|
300
|
+
(with dash). Other prefixes are lowercased with underscores preserved.
|
|
301
|
+
|
|
302
|
+
Examples:
|
|
303
|
+
- ``SETUPTOOLS_SCM`` -> ``setuptools_scm``
|
|
304
|
+
- ``VCS_VERSIONING`` -> ``vcs-versioning``
|
|
305
|
+
- ``HATCH_VCS`` -> ``hatch_vcs``
|
|
306
|
+
|
|
307
|
+
.. todo::
|
|
308
|
+
This uses special-case mapping (VCS_VERSIONING -> vcs-versioning).
|
|
309
|
+
The tool names should be made properly configurable via an explicit
|
|
310
|
+
mapping parameter on VcsEnvironment rather than guessing from
|
|
311
|
+
env-var prefix casing conventions.
|
|
312
|
+
"""
|
|
313
|
+
result: list[str] = []
|
|
314
|
+
for name in self.tool_names:
|
|
315
|
+
if name == "VCS_VERSIONING":
|
|
316
|
+
result.append("vcs-versioning")
|
|
317
|
+
else:
|
|
318
|
+
result.append(name.lower())
|
|
319
|
+
return result
|
|
320
|
+
|
|
321
|
+
def read_toml_overrides(
|
|
322
|
+
self, dist_name: str | None
|
|
323
|
+
) -> _overrides.ConfigOverridesDict:
|
|
324
|
+
"""Read TOML config overrides from environment variables.
|
|
325
|
+
|
|
326
|
+
Uses this environment's tool_names and env dict, delegating to
|
|
327
|
+
the standalone ``read_toml_overrides`` function.
|
|
328
|
+
"""
|
|
329
|
+
from ._overrides import read_toml_overrides as _read_toml_overrides
|
|
330
|
+
|
|
331
|
+
return _read_toml_overrides(
|
|
332
|
+
dist_name, tool_names=self.tool_names, env=self._env
|
|
333
|
+
)
|
|
@@ -10,7 +10,7 @@ from typing import Any, NoReturn
|
|
|
10
10
|
|
|
11
11
|
from . import _config
|
|
12
12
|
from . import _types as _t
|
|
13
|
-
from ._config import Configuration
|
|
13
|
+
from ._config import Configuration, TagConfiguration
|
|
14
14
|
from ._environment import resolve_runtime_env
|
|
15
15
|
|
|
16
16
|
# Backward-compat re-export used by vcs-versioning/setup.py
|
|
@@ -29,6 +29,18 @@ EMPTY_TAG_REGEX_DEPRECATION = DeprecationWarning(
|
|
|
29
29
|
log = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def parse_version(config: Configuration) -> ScmVersion | None:
|
|
33
|
+
"""Backward-compat shim for setuptools-scm <=10.0.x.
|
|
34
|
+
|
|
35
|
+
Those releases import ``parse_version`` from this module. The function
|
|
36
|
+
was inlined during the 10.1 / vcs-versioning 2.0 refactor, but we keep
|
|
37
|
+
the name importable so that older setuptools-scm pins still work with
|
|
38
|
+
newer vcs-versioning releases.
|
|
39
|
+
"""
|
|
40
|
+
scm_version = _resolve_version(config)
|
|
41
|
+
return _apply_metadata_overrides(scm_version, config)
|
|
42
|
+
|
|
43
|
+
|
|
32
44
|
def _finalize(
|
|
33
45
|
scm_version: ScmVersion,
|
|
34
46
|
config: Configuration,
|
|
@@ -258,7 +270,18 @@ def get_version(
|
|
|
258
270
|
|
|
259
271
|
version_cls = _validate_version_cls(version_cls, normalize)
|
|
260
272
|
del normalize
|
|
261
|
-
|
|
273
|
+
|
|
274
|
+
if tag_regex is _config.DEFAULT_TAG_REGEX:
|
|
275
|
+
tag_config = TagConfiguration()
|
|
276
|
+
else:
|
|
277
|
+
warnings.warn(
|
|
278
|
+
"get_version() parameter 'tag_regex' is deprecated. "
|
|
279
|
+
"Use 'tag.regex' in pyproject.toml or pass a TagConfiguration "
|
|
280
|
+
"via Configuration(tag=...) instead.",
|
|
281
|
+
DeprecationWarning,
|
|
282
|
+
stacklevel=2,
|
|
283
|
+
)
|
|
284
|
+
tag_config = TagConfiguration(regex=parse_tag_regex(tag_regex))
|
|
262
285
|
|
|
263
286
|
scm_config = _config.ScmConfiguration.from_data(data=scm)
|
|
264
287
|
|
|
@@ -274,7 +297,6 @@ def get_version(
|
|
|
274
297
|
version_file=version_file,
|
|
275
298
|
version_file_template=version_file_template,
|
|
276
299
|
relative_to=relative_to,
|
|
277
|
-
tag_regex=tag_regex,
|
|
278
300
|
parentdir_prefix_version=parentdir_prefix_version,
|
|
279
301
|
fallback_version=fallback_version,
|
|
280
302
|
fallback_root=fallback_root,
|
|
@@ -284,6 +306,7 @@ def get_version(
|
|
|
284
306
|
version_cls=version_cls,
|
|
285
307
|
search_parent_directories=search_parent_directories,
|
|
286
308
|
scm=scm_config,
|
|
309
|
+
tag=tag_config,
|
|
287
310
|
_env=_env,
|
|
288
311
|
)
|
|
289
312
|
maybe_version = _get_version(config, force_write_version_files=True)
|
|
@@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from ._config import Configuration
|
|
16
|
+
from ._environment import VcsEnvironment
|
|
16
17
|
from ._pyproject_reading import PyProjectData
|
|
17
18
|
|
|
18
19
|
log = logging.getLogger(__name__)
|
|
@@ -22,6 +23,7 @@ def build_configuration_from_pyproject_internal(
|
|
|
22
23
|
pyproject_data: PyProjectData,
|
|
23
24
|
*,
|
|
24
25
|
dist_name: str | None = None,
|
|
26
|
+
env: VcsEnvironment | None = None,
|
|
25
27
|
**integrator_overrides: Any,
|
|
26
28
|
) -> Configuration:
|
|
27
29
|
"""Build Configuration with complete workflow orchestration.
|
|
@@ -34,7 +36,7 @@ def build_configuration_from_pyproject_internal(
|
|
|
34
36
|
2. Determine dist_name (argument > pyproject.project_name)
|
|
35
37
|
3. Merge integrator overrides (override config file)
|
|
36
38
|
4. Read and apply env TOML overrides (highest priority)
|
|
37
|
-
5. Build Configuration with proper validation
|
|
39
|
+
5. Build Configuration with proper validation and VcsEnvironment attached
|
|
38
40
|
|
|
39
41
|
Priority order (highest to lowest):
|
|
40
42
|
1. Environment TOML overrides (TOOL_OVERRIDES_FOR_DIST, TOOL_OVERRIDES)
|
|
@@ -45,6 +47,8 @@ def build_configuration_from_pyproject_internal(
|
|
|
45
47
|
Args:
|
|
46
48
|
pyproject_data: Parsed pyproject data from PyProjectData.from_file() or manual composition
|
|
47
49
|
dist_name: Distribution name for env var lookups (overrides pyproject_data.project_name)
|
|
50
|
+
env: Optional VcsEnvironment. If None, resolves from the active
|
|
51
|
+
GlobalOverrides context or process environment.
|
|
48
52
|
**integrator_overrides: Integrator-provided config overrides
|
|
49
53
|
(override config file, but overridden by env)
|
|
50
54
|
|
|
@@ -67,9 +71,12 @@ def build_configuration_from_pyproject_internal(
|
|
|
67
71
|
"""
|
|
68
72
|
# Import here to avoid circular dependencies
|
|
69
73
|
from ._config import Configuration
|
|
70
|
-
from .
|
|
74
|
+
from ._environment import resolve_runtime_env
|
|
71
75
|
from ._pyproject_reading import get_args_for_pyproject
|
|
72
76
|
|
|
77
|
+
if env is None:
|
|
78
|
+
env = resolve_runtime_env()
|
|
79
|
+
|
|
73
80
|
# Step 1: Get base config from pyproject section
|
|
74
81
|
# This also handles dist_name resolution
|
|
75
82
|
log.debug(
|
|
@@ -96,8 +103,7 @@ def build_configuration_from_pyproject_internal(
|
|
|
96
103
|
config_data.update(integrator_overrides)
|
|
97
104
|
|
|
98
105
|
# Step 4: Apply environment TOML overrides (highest priority)
|
|
99
|
-
|
|
100
|
-
env_overrides = read_toml_overrides(actual_dist_name, tool_names=tool_names)
|
|
106
|
+
env_overrides = env.read_toml_overrides(actual_dist_name)
|
|
101
107
|
if env_overrides:
|
|
102
108
|
log.debug("Applying environment TOML overrides: %s", list(env_overrides.keys()))
|
|
103
109
|
config_data.update(env_overrides)
|
|
@@ -106,7 +112,7 @@ def build_configuration_from_pyproject_internal(
|
|
|
106
112
|
relative_to = pyproject_data.path
|
|
107
113
|
log.debug("Building Configuration with relative_to=%s", relative_to)
|
|
108
114
|
|
|
109
|
-
return Configuration.from_data(relative_to=relative_to, data=config_data)
|
|
115
|
+
return Configuration.from_data(relative_to=relative_to, data=config_data, _env=env)
|
|
110
116
|
|
|
111
117
|
|
|
112
118
|
__all__ = [
|
|
@@ -153,18 +153,18 @@ def _read_pretended_metadata_for(
|
|
|
153
153
|
Returns a dictionary with metadata field overrides like:
|
|
154
154
|
{"node": "g1337beef", "distance": 4}
|
|
155
155
|
"""
|
|
156
|
-
|
|
156
|
+
log.debug("dist name: %s", config.dist_name)
|
|
157
157
|
|
|
158
158
|
if env is None:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
reader = config.env.make_reader(config.dist_name)
|
|
160
|
+
else:
|
|
161
|
+
from .overrides import EnvReader
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
reader = EnvReader(
|
|
164
|
+
tools_names=config.env.tool_names,
|
|
165
|
+
env=env,
|
|
166
|
+
dist_name=config.dist_name,
|
|
167
|
+
)
|
|
168
168
|
|
|
169
169
|
try:
|
|
170
170
|
metadata_overrides = reader.read_toml(
|
|
@@ -259,18 +259,18 @@ def _read_pretended_version_for(
|
|
|
259
259
|
tries ``SETUPTOOLS_SCM_PRETEND_VERSION``
|
|
260
260
|
and ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_$UPPERCASE_DIST_NAME``
|
|
261
261
|
"""
|
|
262
|
-
|
|
262
|
+
log.debug("dist name: %s", config.dist_name)
|
|
263
263
|
|
|
264
264
|
if env is None:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
reader = config.env.make_reader(config.dist_name)
|
|
266
|
+
else:
|
|
267
|
+
from .overrides import EnvReader
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
269
|
+
reader = EnvReader(
|
|
270
|
+
tools_names=config.env.tool_names,
|
|
271
|
+
env=env,
|
|
272
|
+
dist_name=config.dist_name,
|
|
273
|
+
)
|
|
274
274
|
pretended = reader.read("PRETEND_VERSION")
|
|
275
275
|
|
|
276
276
|
if pretended:
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
+
from ._environment import VcsEnvironment
|
|
8
9
|
from ._pyproject_reading import PyProjectData
|
|
9
10
|
|
|
10
11
|
|
|
@@ -14,6 +15,7 @@ def infer_version_string(
|
|
|
14
15
|
overrides: dict[str, Any] | None = None,
|
|
15
16
|
*,
|
|
16
17
|
force_write_version_files: bool = False,
|
|
18
|
+
env: VcsEnvironment | None = None,
|
|
17
19
|
) -> str:
|
|
18
20
|
"""
|
|
19
21
|
Compute the inferred version string from the given inputs.
|
|
@@ -27,6 +29,8 @@ def infer_version_string(
|
|
|
27
29
|
pyproject_data: Parsed PyProjectData (may be constructed via for_testing())
|
|
28
30
|
overrides: Optional override configuration (same keys as [tool.setuptools_scm])
|
|
29
31
|
force_write_version_files: When True, apply write_to/version_file effects
|
|
32
|
+
env: Optional VcsEnvironment. If None, resolves from the active
|
|
33
|
+
GlobalOverrides context or process environment.
|
|
30
34
|
|
|
31
35
|
Returns:
|
|
32
36
|
The computed version string.
|
|
@@ -34,10 +38,13 @@ def infer_version_string(
|
|
|
34
38
|
Raises:
|
|
35
39
|
SystemExit: If version cannot be determined (via _version_missing)
|
|
36
40
|
"""
|
|
37
|
-
from .
|
|
41
|
+
from ._environment import resolve_runtime_env
|
|
38
42
|
from ._get_version_impl import _get_version, _version_missing
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
if env is None:
|
|
45
|
+
env = resolve_runtime_env()
|
|
46
|
+
|
|
47
|
+
config = env.build_config(
|
|
41
48
|
dist_name=dist_name, pyproject_data=pyproject_data, **(overrides or {})
|
|
42
49
|
)
|
|
43
50
|
|
|
@@ -70,7 +70,10 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
70
70
|
- ScmWorkdir result: verify project_path, return immediately.
|
|
71
71
|
- FallbackWorkdir result: stash as candidate, keep probing for SCM.
|
|
72
72
|
2. Fallback phase: probe ``project_dir`` (if different from scm root).
|
|
73
|
-
3.
|
|
73
|
+
3. Try each stashed FallbackWorkdir in discovery order; return the first
|
|
74
|
+
whose ``get_scm_version()`` is not None. This prevents an
|
|
75
|
+
unprocessed ``.git_archival.txt`` from shadowing a valid ``PKG-INFO``
|
|
76
|
+
(see :issue:`1431`).
|
|
74
77
|
4. Try StaticWorkdir from config.fallback_version / parentdir_prefix_version.
|
|
75
78
|
5. Return None.
|
|
76
79
|
"""
|
|
@@ -92,7 +95,7 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
92
95
|
project_dir = config._resolved_paths.project_dir
|
|
93
96
|
scm_root_hint = config._resolved_paths.scm_probe_root
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
fallback_candidates: list[FallbackWorkdir] = []
|
|
96
99
|
|
|
97
100
|
def _accept_scm(result: ScmWorkdir, ep_name: str) -> ScmWorkdir:
|
|
98
101
|
result.project_root = project_dir
|
|
@@ -109,7 +112,6 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
109
112
|
return result
|
|
110
113
|
|
|
111
114
|
def _probe_dir(current_dir: Path, *, accept_scm: bool) -> ScmWorkdir | None:
|
|
112
|
-
nonlocal fallback_candidate
|
|
113
115
|
for ep_name, factory in factories:
|
|
114
116
|
try:
|
|
115
117
|
result = factory(current_dir, config=config)
|
|
@@ -122,7 +124,7 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
122
124
|
continue
|
|
123
125
|
if accept_scm and isinstance(result, ScmWorkdir):
|
|
124
126
|
return _accept_scm(result, ep_name)
|
|
125
|
-
if isinstance(result, FallbackWorkdir)
|
|
127
|
+
if isinstance(result, FallbackWorkdir):
|
|
126
128
|
result._config = config
|
|
127
129
|
log.debug(
|
|
128
130
|
"stashed fallback workdir %s from factory %s at %s",
|
|
@@ -130,7 +132,7 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
130
132
|
ep_name,
|
|
131
133
|
current_dir,
|
|
132
134
|
)
|
|
133
|
-
|
|
135
|
+
fallback_candidates.append(result)
|
|
134
136
|
return None
|
|
135
137
|
|
|
136
138
|
# Phase 1: SCM probes at scm_root_hint (the declared root) and optionally parents.
|
|
@@ -146,9 +148,19 @@ def discover_workdir(config: Configuration) -> AnyWorkdir | None:
|
|
|
146
148
|
if project_dir != scm_root_hint:
|
|
147
149
|
_probe_dir(project_dir, accept_scm=False)
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
# Try each fallback candidate until one can provide a version (#1431).
|
|
152
|
+
# Earlier discovery code stashed all matching fallback workdirs; an
|
|
153
|
+
# unprocessed .git_archival.txt (raw $Format placeholders) would
|
|
154
|
+
# shadow a valid PKG-INFO if we only kept the first candidate.
|
|
155
|
+
for candidate in fallback_candidates:
|
|
156
|
+
if candidate.get_scm_version() is not None:
|
|
157
|
+
log.info("using fallback workdir %s", type(candidate).__name__)
|
|
158
|
+
return candidate
|
|
159
|
+
if fallback_candidates:
|
|
160
|
+
log.debug(
|
|
161
|
+
"all %d fallback candidates returned None for get_scm_version",
|
|
162
|
+
len(fallback_candidates),
|
|
163
|
+
)
|
|
152
164
|
|
|
153
165
|
static = StaticWorkdir(path=project_dir, _config=config)
|
|
154
166
|
if static.get_scm_version() is not None:
|
|
@@ -304,3 +304,106 @@ class TestFrozenLegacyConfig:
|
|
|
304
304
|
frozen = FrozenLegacyConfig(config)
|
|
305
305
|
with pytest.raises((AttributeError, dataclasses.FrozenInstanceError)):
|
|
306
306
|
frozen.version_scheme = "something" # type: ignore[attr-defined]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class TestInferVersionStringEnv:
|
|
310
|
+
"""Tests that infer_version_string properly resolves and attaches VcsEnvironment."""
|
|
311
|
+
|
|
312
|
+
def _pyproject(self) -> PyProjectData:
|
|
313
|
+
return PyProjectData.for_testing(
|
|
314
|
+
tool_name="vcs-versioning",
|
|
315
|
+
is_required=True,
|
|
316
|
+
section_present=True,
|
|
317
|
+
project_present=True,
|
|
318
|
+
project_name="test-pkg",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
def test_no_deprecation_warning_with_pretend_version(
|
|
322
|
+
self, monkeypatch: pytest.MonkeyPatch
|
|
323
|
+
) -> None:
|
|
324
|
+
"""infer_version_string must not leak DeprecationWarning for missing env."""
|
|
325
|
+
import warnings
|
|
326
|
+
|
|
327
|
+
from vcs_versioning._version_inference import infer_version_string
|
|
328
|
+
|
|
329
|
+
monkeypatch.setenv("SETUPTOOLS_SCM_PRETEND_VERSION", "1.2.3")
|
|
330
|
+
|
|
331
|
+
with warnings.catch_warnings():
|
|
332
|
+
warnings.filterwarnings("error", category=DeprecationWarning)
|
|
333
|
+
result = infer_version_string("test-pkg", self._pyproject())
|
|
334
|
+
|
|
335
|
+
assert result == "1.2.3"
|
|
336
|
+
|
|
337
|
+
def test_resolves_env_from_global_overrides_context(
|
|
338
|
+
self, monkeypatch: pytest.MonkeyPatch
|
|
339
|
+
) -> None:
|
|
340
|
+
"""infer_version_string respects the active GlobalOverrides context."""
|
|
341
|
+
from vcs_versioning._version_inference import infer_version_string
|
|
342
|
+
|
|
343
|
+
monkeypatch.setenv("MYTOOL_PRETEND_VERSION", "9.8.7")
|
|
344
|
+
|
|
345
|
+
with GlobalOverrides.from_env("MYTOOL"):
|
|
346
|
+
result = infer_version_string("test-pkg", self._pyproject())
|
|
347
|
+
|
|
348
|
+
assert result == "9.8.7"
|
|
349
|
+
|
|
350
|
+
def test_accepts_explicit_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
351
|
+
"""infer_version_string uses an explicitly passed VcsEnvironment."""
|
|
352
|
+
from vcs_versioning._version_inference import infer_version_string
|
|
353
|
+
|
|
354
|
+
env_mapping = {"VCS_VERSIONING_PRETEND_VERSION": "4.5.6"}
|
|
355
|
+
env = VcsEnvironment.from_env(env=env_mapping)
|
|
356
|
+
|
|
357
|
+
result = infer_version_string("test-pkg", self._pyproject(), env=env)
|
|
358
|
+
assert result == "4.5.6"
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class TestBuildConfigurationFromPyprojectEnv:
|
|
362
|
+
"""Tests that build_configuration_from_pyproject attaches VcsEnvironment."""
|
|
363
|
+
|
|
364
|
+
def _pyproject(self) -> PyProjectData:
|
|
365
|
+
return PyProjectData.for_testing(
|
|
366
|
+
tool_name="vcs-versioning",
|
|
367
|
+
is_required=True,
|
|
368
|
+
section_present=True,
|
|
369
|
+
project_present=True,
|
|
370
|
+
project_name="test-pkg",
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def test_no_deprecation_warning(self) -> None:
|
|
374
|
+
"""build_configuration_from_pyproject must not leak DeprecationWarning."""
|
|
375
|
+
import warnings
|
|
376
|
+
|
|
377
|
+
from vcs_versioning import build_configuration_from_pyproject
|
|
378
|
+
|
|
379
|
+
with warnings.catch_warnings():
|
|
380
|
+
warnings.filterwarnings("error", category=DeprecationWarning)
|
|
381
|
+
config = build_configuration_from_pyproject(
|
|
382
|
+
self._pyproject(), dist_name="test-pkg"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
assert config._env is not None
|
|
386
|
+
|
|
387
|
+
def test_resolves_env_from_global_overrides_context(self) -> None:
|
|
388
|
+
"""build_configuration_from_pyproject uses active GlobalOverrides."""
|
|
389
|
+
from vcs_versioning import build_configuration_from_pyproject
|
|
390
|
+
|
|
391
|
+
with GlobalOverrides.from_env("MYTOOL", env={}):
|
|
392
|
+
config = build_configuration_from_pyproject(
|
|
393
|
+
self._pyproject(), dist_name="test-pkg"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
assert config._env is not None
|
|
397
|
+
assert "MYTOOL" in config._env.tool_names
|
|
398
|
+
|
|
399
|
+
def test_accepts_explicit_env(self) -> None:
|
|
400
|
+
"""build_configuration_from_pyproject passes explicit env through."""
|
|
401
|
+
from vcs_versioning import build_configuration_from_pyproject
|
|
402
|
+
|
|
403
|
+
env = VcsEnvironment.from_env("CUSTOM", env={})
|
|
404
|
+
|
|
405
|
+
config = build_configuration_from_pyproject(
|
|
406
|
+
self._pyproject(), dist_name="test-pkg", env=env
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
assert config._env is env
|
|
@@ -7,7 +7,7 @@ import warnings
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
from vcs_versioning import Configuration
|
|
10
|
-
from vcs_versioning._config import TagConfiguration
|
|
10
|
+
from vcs_versioning._config import DEFAULT_TAG_REGEX, TagConfiguration
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@pytest.mark.parametrize(
|
|
@@ -60,6 +60,21 @@ def test_deprecated_tag_regex_init_var() -> None:
|
|
|
60
60
|
assert conf.tag.regex is tag_regex
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
@pytest.mark.issue(1434)
|
|
64
|
+
def test_omitting_tag_regex_no_deprecation_warning() -> None:
|
|
65
|
+
"""Omitting tag_regex (None default) should not emit DeprecationWarning."""
|
|
66
|
+
with warnings.catch_warnings():
|
|
67
|
+
warnings.simplefilter("error", DeprecationWarning)
|
|
68
|
+
Configuration()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.issue(1434)
|
|
72
|
+
def test_explicit_default_tag_regex_warns() -> None:
|
|
73
|
+
"""Explicitly passing DEFAULT_TAG_REGEX still warns (deprecated param)."""
|
|
74
|
+
with pytest.warns(DeprecationWarning, match="Use 'tag.regex' instead"):
|
|
75
|
+
Configuration(tag_regex=DEFAULT_TAG_REGEX)
|
|
76
|
+
|
|
77
|
+
|
|
63
78
|
def test_tag_regex_conflict() -> None:
|
|
64
79
|
"""Cannot set both tag_regex= and tag.regex simultaneously."""
|
|
65
80
|
with pytest.raises(
|
|
@@ -138,6 +138,30 @@ def test_no_warn_when_version_file_not_tracked(tmp_path: Path, scm: str) -> None
|
|
|
138
138
|
assert tracked_warnings == []
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
@pytest.mark.issue(1423)
|
|
142
|
+
def test_parse_version_importable_from_get_version_impl() -> None:
|
|
143
|
+
"""setuptools-scm <=10.0.x imports parse_version from this module."""
|
|
144
|
+
from vcs_versioning._get_version_impl import parse_version
|
|
145
|
+
|
|
146
|
+
assert callable(parse_version)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@pytest.mark.issue(1423)
|
|
150
|
+
def test_parse_version_shim_returns_version(tmp_path: Path) -> None:
|
|
151
|
+
"""The compat shim should resolve and return a ScmVersion."""
|
|
152
|
+
wd = WorkDir(tmp_path).setup_git()
|
|
153
|
+
wd.add_and_commit("initial")
|
|
154
|
+
wd("git tag -a v1.0.0 -m v1.0.0")
|
|
155
|
+
|
|
156
|
+
c = Configuration(root=tmp_path)
|
|
157
|
+
|
|
158
|
+
from vcs_versioning._get_version_impl import parse_version
|
|
159
|
+
from vcs_versioning._scm_version import ScmVersion
|
|
160
|
+
|
|
161
|
+
result = parse_version(c)
|
|
162
|
+
assert isinstance(result, ScmVersion)
|
|
163
|
+
|
|
164
|
+
|
|
141
165
|
@pytest.mark.parametrize(
|
|
142
166
|
("input", "expected"),
|
|
143
167
|
[
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import warnings
|
|
6
6
|
|
|
7
|
-
import pytest
|
|
8
7
|
from vcs_versioning import Configuration
|
|
9
8
|
from vcs_versioning._config import TagConfiguration
|
|
10
9
|
from vcs_versioning._scm_version import tag_to_version
|
|
@@ -59,8 +58,10 @@ class TestDescribeMatchGlob:
|
|
|
59
58
|
|
|
60
59
|
|
|
61
60
|
class TestTagStrictWarning:
|
|
62
|
-
def
|
|
63
|
-
|
|
61
|
+
def test_none_strict_no_warning(self) -> None:
|
|
62
|
+
"""tag.strict=None no longer warns (suppressed per #1422, see #1429)."""
|
|
63
|
+
with warnings.catch_warnings():
|
|
64
|
+
warnings.simplefilter("error", FutureWarning)
|
|
64
65
|
Configuration(tag=TagConfiguration(strict=None))
|
|
65
66
|
|
|
66
67
|
def test_true_strict_no_warning(self) -> None:
|
|
@@ -78,17 +79,13 @@ class TestTagPrefixStripping:
|
|
|
78
79
|
"""Test that tag.prefix is stripped before tag_regex matching."""
|
|
79
80
|
|
|
80
81
|
def test_prefix_stripped_from_tag(self) -> None:
|
|
81
|
-
|
|
82
|
-
warnings.simplefilter("ignore", FutureWarning)
|
|
83
|
-
config = Configuration(tag=TagConfiguration(prefix="hatchling-v"))
|
|
82
|
+
config = Configuration(tag=TagConfiguration(prefix="hatchling-v"))
|
|
84
83
|
version = tag_to_version("hatchling-v1.0.0", config)
|
|
85
84
|
assert version is not None
|
|
86
85
|
assert str(version) == "1.0.0"
|
|
87
86
|
|
|
88
87
|
def test_no_prefix_no_stripping(self) -> None:
|
|
89
|
-
|
|
90
|
-
warnings.simplefilter("ignore", FutureWarning)
|
|
91
|
-
config = Configuration(tag=TagConfiguration(prefix=""))
|
|
88
|
+
config = Configuration(tag=TagConfiguration(prefix=""))
|
|
92
89
|
version = tag_to_version("v1.0.0", config)
|
|
93
90
|
assert version is not None
|
|
94
91
|
assert str(version) == "1.0.0"
|
|
@@ -100,17 +97,13 @@ class TestTagPrefixStripping:
|
|
|
100
97
|
may still parse -- but the prefix is NOT stripped, meaning
|
|
101
98
|
git describe --match would have already filtered it out in practice.
|
|
102
99
|
"""
|
|
103
|
-
|
|
104
|
-
warnings.simplefilter("ignore", FutureWarning)
|
|
105
|
-
config = Configuration(tag=TagConfiguration(prefix="other-"))
|
|
100
|
+
config = Configuration(tag=TagConfiguration(prefix="other-"))
|
|
106
101
|
version = tag_to_version("hatchling-v1.0.0", config)
|
|
107
102
|
# Default tag_regex matches and extracts "v1.0.0" from dashed prefix
|
|
108
103
|
assert version is not None
|
|
109
104
|
|
|
110
105
|
def test_prefix_v_strips_v(self) -> None:
|
|
111
|
-
|
|
112
|
-
warnings.simplefilter("ignore", FutureWarning)
|
|
113
|
-
config = Configuration(tag=TagConfiguration(prefix="v"))
|
|
106
|
+
config = Configuration(tag=TagConfiguration(prefix="v"))
|
|
114
107
|
version = tag_to_version("v1.2.3", config)
|
|
115
108
|
assert version is not None
|
|
116
109
|
assert str(version) == "1.2.3"
|
|
@@ -120,20 +113,19 @@ class TestConfigFromData:
|
|
|
120
113
|
"""Test that Configuration.from_data correctly parses tag config."""
|
|
121
114
|
|
|
122
115
|
def test_tag_in_from_data(self) -> None:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
},
|
|
131
|
-
)
|
|
116
|
+
config = Configuration.from_data(
|
|
117
|
+
relative_to=".",
|
|
118
|
+
data={
|
|
119
|
+
"dist_name": "test",
|
|
120
|
+
"tag": {"prefix": "mylib-", "strict": True},
|
|
121
|
+
},
|
|
122
|
+
)
|
|
132
123
|
assert config.tag.prefix == "mylib-"
|
|
133
124
|
assert config.tag.strict is True
|
|
134
125
|
|
|
135
126
|
def test_no_tag_in_from_data(self) -> None:
|
|
136
|
-
with
|
|
127
|
+
with warnings.catch_warnings():
|
|
128
|
+
warnings.simplefilter("error", FutureWarning)
|
|
137
129
|
config = Configuration.from_data(
|
|
138
130
|
relative_to=".",
|
|
139
131
|
data={"dist_name": "test"},
|
|
@@ -143,13 +135,12 @@ class TestConfigFromData:
|
|
|
143
135
|
|
|
144
136
|
|
|
145
137
|
class TestDescribeCommandConflictWarning:
|
|
146
|
-
def
|
|
138
|
+
def test_no_warning_when_prefix_and_describe_command_both_set(self) -> None:
|
|
139
|
+
"""Suppressed per #1422, see #1429 for follow-up."""
|
|
147
140
|
from vcs_versioning._config import GitConfiguration, ScmConfiguration
|
|
148
141
|
|
|
149
|
-
with
|
|
150
|
-
UserWarning
|
|
151
|
-
match="Both tag.prefix/tag.strict and scm.git.describe_command are set",
|
|
152
|
-
):
|
|
142
|
+
with warnings.catch_warnings():
|
|
143
|
+
warnings.simplefilter("error", UserWarning)
|
|
153
144
|
Configuration(
|
|
154
145
|
tag=TagConfiguration(prefix="v", strict=True),
|
|
155
146
|
scm=ScmConfiguration(
|
|
@@ -177,6 +177,40 @@ class TestProjectPathVerificationInDiscovery:
|
|
|
177
177
|
discover_workdir(config)
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
class TestFallbackPriority:
|
|
181
|
+
@pytest.mark.issue(1431)
|
|
182
|
+
def test_unprocessed_archival_falls_through_to_pkginfo(
|
|
183
|
+
self, tmp_path: Path
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Unprocessed .git_archival.txt must not shadow a valid PKG-INFO.
|
|
186
|
+
|
|
187
|
+
PyPI sdists contain both files: a .git_archival.txt with raw
|
|
188
|
+
``$Format:...`` placeholders (never substituted because the sdist
|
|
189
|
+
was built by setuptools, not ``git archive``) and a PKG-INFO with
|
|
190
|
+
the correct version. Before the fix, the archival fallback was
|
|
191
|
+
stashed as the sole candidate and its ``get_scm_version()`` returned
|
|
192
|
+
None, causing a LookupError.
|
|
193
|
+
"""
|
|
194
|
+
(tmp_path / ".git_archival.txt").write_text(
|
|
195
|
+
"node: $Format:%H$\n"
|
|
196
|
+
"node-date: $Format:%cI$\n"
|
|
197
|
+
"describe-name: $Format:%(describe:tags=true)$\n"
|
|
198
|
+
"ref-names: $Format:%D$\n",
|
|
199
|
+
encoding="utf-8",
|
|
200
|
+
)
|
|
201
|
+
(tmp_path / "PKG-INFO").write_text(
|
|
202
|
+
"Metadata-Version: 2.1\nName: my-pkg\nVersion: 1.2.3\n",
|
|
203
|
+
encoding="utf-8",
|
|
204
|
+
)
|
|
205
|
+
config = Configuration(relative_to=str(tmp_path / "pyproject.toml"))
|
|
206
|
+
result = discover_workdir(config)
|
|
207
|
+
assert result is not None
|
|
208
|
+
assert isinstance(result, PkgInfoWorkdir)
|
|
209
|
+
version = result.get_scm_version()
|
|
210
|
+
assert version is not None
|
|
211
|
+
assert str(version.tag) == "1.2.3"
|
|
212
|
+
|
|
213
|
+
|
|
180
214
|
class TestFallbackWorkdirDiscoveryFactories:
|
|
181
215
|
def test_discover_archival_git(self, tmp_path: Path) -> None:
|
|
182
216
|
from vcs_versioning._fallback_workdir import discover_archival
|
|
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.0.1 → vcs_versioning-2.1.1}/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
|
{vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/__init__.py
RENAMED
|
File without changes
|
{vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/_common.py
RENAMED
|
File without changes
|
{vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/src/vcs_versioning/_version_schemes/_standard.py
RENAMED
|
File without changes
|
{vcs_versioning-2.0.1 → vcs_versioning-2.1.1}/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.0.1 → vcs_versioning-2.1.1}/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
|