Repository: pypa/setuptools_scm
Pull request #904 (2023-09-19 06:41:06)

prepare safer backward compatibility
* restore direct imports and names of helper functions
* use config passover for version scheme formatting


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0349213..9189d69 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -8,7 +8,7 @@ repos:
       args: [--safe, --quiet]
       exclude: docs/examples/
 -   repo: https://github.com/asottile/reorder-python-imports
-    rev: v3.11.0
+    rev: v3.10.0
     hooks:
     -   id: reorder-python-imports
         args: [ "--application-directories=.:src" , --py38-plus, --add-import, 'from __future__ import annotations']
@@ -23,7 +23,7 @@ repos:
     hooks:
     -   id: flake8
 -   repo: https://github.com/asottile/pyupgrade
-    rev: v3.11.0
+    rev: v3.10.1
     hooks:
     - id: pyupgrade
       args: [--py38-plus]
diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py
index 954751e..a6a8236 100644
--- a/src/setuptools_scm/__init__.py
+++ b/src/setuptools_scm/__init__.py
@@ -4,70 +4,20 @@
 """
 from __future__ import annotations
 
-from typing import Any
-from typing import Pattern
-
-from . import _config
-from . import _types as _t
 from ._config import Configuration
+from ._get_version_impl import _get_version  # soft deprecated
+from ._get_version_impl import get_version  # soft deprecated
+from ._integration.dump_version import dump_version  # soft deprecated
 from ._version_cls import NonNormalizedVersion
 from ._version_cls import Version
 from .version import ScmVersion
 
 
-def dump_version(
-    root: _t.PathT,
-    version: str,
-    write_to: _t.PathT,
-    template: str | None = None,
-    scm_version: ScmVersion | None = None,
-) -> None:
-    """soft deprecated helper to write the version file in the cwd
-
-    still used by hatch-vcs, will be removed after upstream uses the modern patterns
-    """
-    from ._integration.dump_version import dump_version as real
-
-    return real(root, version, write_to, template, scm_version)
-
-
-def get_version(
-    root: _t.PathT = ".",
-    version_scheme: _t.VERSION_SCHEME = _config.DEFAULT_VERSION_SCHEME,
-    local_scheme: _t.VERSION_SCHEME = _config.DEFAULT_LOCAL_SCHEME,
-    write_to: _t.PathT | None = None,
-    write_to_template: str | None = None,
-    version_file: _t.PathT | None = None,
-    version_file_template: str | None = None,
-    relative_to: _t.PathT | None = None,
-    tag_regex: str | Pattern[str] = _config.DEFAULT_TAG_REGEX,
-    parentdir_prefix_version: str | None = None,
-    fallback_version: str | None = None,
-    fallback_root: _t.PathT = ".",
-    parse: Any | None = None,
-    git_describe_command: _t.CMD_TYPE | None = None,
-    dist_name: str | None = None,
-    version_cls: Any | None = None,
-    normalize: bool = True,
-    search_parent_directories: bool = False,
-) -> str:
-    """
-    soft deprecated helper to obtain the vcs version
-    its misused at runtime in numerous projects and has to stay for now
-
-    a replacement supporting editable installation in hatch/setuptools is planned
-    """
-
-    params = {**locals()}
-    from ._get_version import get_version
-
-    return get_version(**params)
-
-
 # Public API
 __all__ = [
-    "get_version",
-    "dump_version",
+    "get_version",  # deprecated imported for backward compatibility
+    "_get_version",  # deprecated imported for backward compatibility
+    "dump_version",  # deprecated imported for backward compatibility
     "Configuration",
     "Version",
     "ScmVersion",
diff --git a/src/setuptools_scm/_cli.py b/src/setuptools_scm/_cli.py
index 4786ec2..560650c 100644
--- a/src/setuptools_scm/_cli.py
+++ b/src/setuptools_scm/_cli.py
@@ -6,7 +6,7 @@ import sys
 
 from setuptools_scm import Configuration
 from setuptools_scm._file_finders import find_files
-from setuptools_scm._get_version import _get_version
+from setuptools_scm._get_version_impl import _get_version
 from setuptools_scm.discover import walk_potential_roots
 
 
diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py
index e3be053..a78f78d 100644
--- a/src/setuptools_scm/_entrypoints.py
+++ b/src/setuptools_scm/_entrypoints.py
@@ -106,20 +106,29 @@ def _iter_version_schemes(
 
 @overload
 def _call_version_scheme(
-    version: version.ScmVersion, entrypoint: str, given_value: str, default: str
+    version: version.ScmVersion,
+    entrypoint: str,
+    given_value: _t.VERSION_SCHEMES,
+    default: str,
 ) -> str:
     ...
 
 
 @overload
 def _call_version_scheme(
-    version: version.ScmVersion, entrypoint: str, given_value: str, default: None
+    version: version.ScmVersion,
+    entrypoint: str,
+    given_value: _t.VERSION_SCHEMES,
+    default: None,
 ) -> str | None:
     ...
 
 
 def _call_version_scheme(
-    version: version.ScmVersion, entrypoint: str, given_value: str, default: str | None
+    version: version.ScmVersion,
+    entrypoint: str,
+    given_value: _t.VERSION_SCHEMES,
+    default: str | None,
 ) -> str | None:
     for scheme in _iter_version_schemes(entrypoint, given_value):
         result = scheme(version)
diff --git a/src/setuptools_scm/_get_version.py b/src/setuptools_scm/_get_version_impl.py
similarity index 95%
rename from src/setuptools_scm/_get_version.py
rename to src/setuptools_scm/_get_version_impl.py
index d2fe9e1..a5015c7 100644
--- a/src/setuptools_scm/_get_version.py
+++ b/src/setuptools_scm/_get_version_impl.py
@@ -46,7 +46,7 @@ def parse_fallback_version(config: Configuration) -> ScmVersion | None:
     return _entrypoints.version_from_entrypoint(config, entrypoint, root)
 
 
-def _do_parse(config: Configuration) -> ScmVersion | None:
+def parse_version(config: Configuration) -> ScmVersion | None:
     return (
         _read_pretended_version_for(config)
         or parse_scm_version(config)
@@ -86,14 +86,10 @@ def write_version_files(
 def _get_version(
     config: Configuration, force_write_version_files: bool = False
 ) -> str | None:
-    parsed_version = _do_parse(config)
+    parsed_version = parse_version(config)
     if parsed_version is None:
         return None
-    version_string = _format_version(
-        parsed_version,
-        version_scheme=config.version_scheme,
-        local_scheme=config.local_scheme,
-    )
+    version_string = _format_version(parsed_version)
     if force_write_version_files:
         write_version_files(config, version=version_string, scm_version=parsed_version)
 
diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py
index 33a78a8..23f5c22 100644
--- a/src/setuptools_scm/_integration/setuptools.py
+++ b/src/setuptools_scm/_integration/setuptools.py
@@ -48,7 +48,7 @@ Suggested workaround if applicable:
 def _assign_version(
     dist: setuptools.Distribution, config: _config.Configuration
 ) -> None:
-    from .._get_version import _get_version, _version_missing
+    from .._get_version_impl import _get_version, _version_missing
 
     # todo: build time plugin
     maybe_version = _get_version(config, force_write_version_files=True)
diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py
index 3a5dba0..d4097fb 100644
--- a/src/setuptools_scm/version.py
+++ b/src/setuptools_scm/version.py
@@ -419,19 +419,19 @@ def postrelease_version(version: ScmVersion) -> str:
         return version.format_with("{tag}.post{distance}")
 
 
-def format_version(version: ScmVersion, **config: Any) -> str:
+def format_version(version: ScmVersion) -> str:
     log.debug("scm version %s", version)
-    log.debug("config %s", config)
+    log.debug("config %s", version.config)
     if version.preformatted:
         assert isinstance(version.tag, str)
         return version.tag
     main_version = _entrypoints._call_version_scheme(
-        version, "setuptools_scm.version_scheme", config["version_scheme"], None
+        version, "setuptools_scm.version_scheme", version.config.version_scheme, None
     )
     log.debug("version %s", main_version)
     assert main_version is not None
     local_version = _entrypoints._call_version_scheme(
-        version, "setuptools_scm.local_scheme", config["local_scheme"], "+unknown"
+        version, "setuptools_scm.local_scheme", version.config.local_scheme, "+unknown"
     )
     log.debug("local_version %s", local_version)
     return main_version + local_version


Test patch:
diff --git a/testing/test_basic_api.py b/testing/test_basic_api.py
index bfcc47c..fa35e2c 100644
--- a/testing/test_basic_api.py
+++ b/testing/test_basic_api.py
@@ -56,7 +56,7 @@ def assert_root(monkeypatch: pytest.MonkeyPatch, expected_root: str) -> None:
         assert config.absolute_root == expected_root
         return ScmVersion("1.0", config=config)
 
-    monkeypatch.setattr(setuptools_scm._get_version, "_do_parse", assertion)
+    monkeypatch.setattr(setuptools_scm._get_version_impl, "parse_version", assertion)
 
 
 def test_root_parameter_creation(monkeypatch: pytest.MonkeyPatch) -> None:
diff --git a/testing/test_functions.py b/testing/test_functions.py
index ad8b167..baf84d3 100644
--- a/testing/test_functions.py
+++ b/testing/test_functions.py
@@ -62,13 +62,16 @@ VERSIONS = {
 def test_format_version(
     version: str, version_scheme: str, local_scheme: str, expected: str
 ) -> None:
+    from dataclasses import replace
+
     scm_version = VERSIONS[version]
-    assert (
-        format_version(
-            scm_version, version_scheme=version_scheme, local_scheme=local_scheme
-        )
-        == expected
+    configured_version = replace(
+        scm_version,
+        config=replace(
+            scm_version.config, version_scheme=version_scheme, local_scheme=local_scheme
+        ),
     )
+    assert format_version(configured_version) == expected
 
 
 def test_dump_version_doesnt_bail_on_value_error(tmp_path: Path) -> None:
diff --git a/testing/test_git.py b/testing/test_git.py
index 8bebd1e..8f7ac9c 100644
--- a/testing/test_git.py
+++ b/testing/test_git.py
@@ -547,15 +547,12 @@ def test_git_getdate_signed_commit(signed_commit_wd: WorkDir) -> None:
 )
 @pytest.mark.filterwarnings("ignore:git archive did not support describe output")
 def test_git_archival_to_version(expected: str, from_data: dict[str, str]) -> None:
-    config = Configuration()
+    config = Configuration(
+        version_scheme="guess-next-dev", local_scheme="node-and-date"
+    )
     version = archival_to_version(from_data, config=config)
     assert version is not None
-    assert (
-        format_version(
-            version, version_scheme="guess-next-dev", local_scheme="node-and-date"
-        )
-        == expected
-    )
+    assert format_version(version) == expected
 
 
 @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/727")
diff --git a/testing/test_mercurial.py b/testing/test_mercurial.py
index 3aa8594..1ca35be 100644
--- a/testing/test_mercurial.py
+++ b/testing/test_mercurial.py
@@ -43,14 +43,11 @@ archival_mapping = {
 
 @pytest.mark.parametrize("expected,data", sorted(archival_mapping.items()))
 def test_archival_to_version(expected: str, data: dict[str, str]) -> None:
-    config = Configuration()
-    version = archival_to_version(data, config=config)
-    assert (
-        format_version(
-            version, version_scheme="guess-next-dev", local_scheme="node-and-date"
-        )
-        == expected
+    config = Configuration(
+        version_scheme="guess-next-dev", local_scheme="node-and-date"
     )
+    version = archival_to_version(data, config=config)
+    assert format_version(version) == expected
 
 
 def test_hg_gone(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> None:
diff --git a/testing/test_version.py b/testing/test_version.py
index 2485417..54f397f 100644
--- a/testing/test_version.py
+++ b/testing/test_version.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+from dataclasses import replace
 from datetime import date
 from datetime import timedelta
 from typing import Any
@@ -209,21 +210,30 @@ def test_version_bump_bad() -> None:
 
 
 def test_format_version_schemes() -> None:
-    version = meta("1.0", config=c)
-    format_version(
-        version,
-        local_scheme="no-local-version",
-        version_scheme=[lambda v: None, "guess-next-dev"],
+    version = meta(
+        "1.0",
+        config=replace(
+            c,
+            local_scheme="no-local-version",
+            version_scheme=[  # type: ignore[arg-type]
+                lambda v: None,
+                "guess-next-dev",
+            ],
+        ),
     )
+    assert format_version(version) == "1.0"
 
 
 def test_custom_version_schemes() -> None:
-    version = meta("1.0", config=c)
-    custom_computed = format_version(
-        version,
-        local_scheme="no-local-version",
-        version_scheme="setuptools_scm.version:no_guess_dev_version",
+    version = meta(
+        "1.0",
+        config=replace(
+            c,
+            local_scheme="no-local-version",
+            version_scheme="setuptools_scm.version:no_guess_dev_version",
+        ),
     )
+    custom_computed = format_version(version)
     assert custom_computed == no_guess_dev_version(version)
 
 
@@ -338,12 +348,12 @@ def test_calver_by_date(version: ScmVersion, expected_next: str) -> None:
 @pytest.mark.parametrize(
     "version, expected_next",
     [
-        pytest.param(meta("1.0.0", config=c), "1.0.0", id="SemVer exact"),
+        pytest.param(meta("1.0.0", config=c), "1.0.0", id="SemVer exact stays"),
         pytest.param(
-            meta("1.0.0", config=c, dirty=True),
-            "1.0.0",
-            id="SemVer dirty",
-            marks=pytest.mark.xfail,
+            meta("1.0.0", config=c_non_normalize, dirty=True),
+            "09.02.13.1.dev0",
+            id="SemVer dirty is replaced by date",
+            marks=pytest.mark.filterwarnings("ignore:.*legacy version.*:UserWarning"),
         ),
     ],
 )