From 8313086a9f704991b9994432befd3857ada5bd21 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:16:53 +0100 Subject: chore: trim down mypy config --- mypy.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mypy.ini b/mypy.ini index fb09211..9b38795 100644 --- a/mypy.ini +++ b/mypy.ini @@ -3,8 +3,4 @@ python_version = 3.7 warn_return_any = True warn_unused_configs = True mypy_path = $MYPY_CONFIG_FILE_DIR/src - -[mypy-setuptools_scm.*] -# disabled as it will take a bit -# disallow_untyped_defs = True strict = true -- cgit v1.2.1 From 3b09bb5935accc8a7dfd4ceaa1ef0c0c576f27ec Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:18:04 +0100 Subject: git tests: expect main as possible default branch --- testing/test_git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_git.py b/testing/test_git.py index 412ce75..2dc8b0d 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -149,7 +149,7 @@ def test_version_from_git(wd: WorkDir) -> None: assert wd.version == "0.1.dev0" parsed = git.parse(str(wd.cwd), git.DEFAULT_DESCRIBE) - assert parsed is not None and parsed.branch == "master" + assert parsed is not None and parsed.branch in ("master", "main") wd.commit_testfile() assert wd.version.startswith("0.1.dev1+g") -- cgit v1.2.1 From acf0303e16a9bb5e92676fa1be1a1309c04396e0 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:35:12 +0100 Subject: typing: consolidate version schemes types --- src/setuptools_scm/_types.py | 6 ++++-- src/setuptools_scm/version.py | 10 ++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/setuptools_scm/_types.py b/src/setuptools_scm/_types.py index 6c6bdf8..44cfbac 100644 --- a/src/setuptools_scm/_types.py +++ b/src/setuptools_scm/_types.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any from typing import Callable from typing import List +from typing import Tuple from typing import TYPE_CHECKING from typing import TypeVar from typing import Union @@ -14,11 +15,12 @@ if TYPE_CHECKING: from typing_extensions import ParamSpec, TypeAlias, Protocol -PathT = Union["os.PathLike[str]", str] +PathT: TypeAlias = Union["os.PathLike[str]", str] CMD_TYPE: TypeAlias = Union[List[str], str] -VERSION_SCHEME = Union[str, Callable[["version.ScmVersion"], str]] +VERSION_SCHEME: TypeAlias = Union[str, Callable[["version.ScmVersion"], str]] +VERSION_SCHEMES: TypeAlias = Union[List[str], Tuple[str, ...], VERSION_SCHEME] class EntrypointProtocol(Protocol): diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 114e16b..c36d4bb 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -10,10 +10,8 @@ from typing import Any from typing import Callable from typing import cast from typing import Iterator -from typing import List from typing import Match from typing import overload -from typing import Tuple from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -497,18 +495,14 @@ def _get_ep(group: str, name: str) -> Any | None: def _iter_version_schemes( entrypoint: str, - scheme_value: str - | list[str] - | tuple[str, ...] - | Callable[[ScmVersion], str] - | None, + scheme_value: _t.VERSION_SCHEMES, _memo: set[object] | None = None, ) -> Iterator[Callable[[ScmVersion], str]]: if _memo is None: _memo = set() if isinstance(scheme_value, str): scheme_value = cast( - 'str|List[str]|Tuple[str, ...]|Callable[["ScmVersion"], str]|None', + "_t.VERSION_SCHEMES", _get_ep(entrypoint, scheme_value), ) -- cgit v1.2.1 From 451f050bf7a8f1153aa1270233167a5303d8fca2 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:35:57 +0100 Subject: version: use precompiled regex for date_ver_match --- src/setuptools_scm/version.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index c36d4bb..b064c75 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -366,15 +366,13 @@ def no_guess_dev_version(version: ScmVersion) -> str: return version.format_next_version(_dont_guess_next_version) +_DATE_REGEX = re.compile( + r"^(?P(?P\d{2}|\d{4})(?:\.\d{1,2}){2})(?:\.(?P\d*))?$" +) + + def date_ver_match(ver: str) -> Match[str] | None: - match = re.match( - ( - r"^(?P(?P\d{2}|\d{4})(?:\.\d{1,2}){2})" - r"(?:\.(?P\d*)){0,1}?$" - ), - ver, - ) - return match + return _DATE_REGEX.match(ver) def guess_next_date_ver( -- cgit v1.2.1 From 7f7cd295f86ab196c0213fbd9f3841795a5081c9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:37:42 +0100 Subject: Version class: turn into dataclass --- src/setuptools_scm/version.py | 65 ++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index b064c75..5a822b8 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses import os import re import warnings @@ -70,7 +71,6 @@ def tag_to_version( ) -> _VersionT | None: """ take a tag that might be prefixed with a keyword and return only the version part - :param config: optional configuration object """ trace("tag", tag) @@ -114,46 +114,31 @@ def tags_to_versions( return result +def _source_epoch_or_utc_now() -> datetime: + if "SOURCE_DATE_EPOCH" in os.environ: + date_epoch = int(os.environ["SOURCE_DATE_EPOCH"]) + return datetime.fromtimestamp(date_epoch, timezone.utc) + else: + return datetime.now(timezone.utc) + + +@dataclasses.dataclass class ScmVersion: - def __init__( - self, - tag_version: Any, - config: Configuration, - distance: int | None = None, - node: str | None = None, - dirty: bool = False, - preformatted: bool = False, - branch: str | None = None, - node_date: date | None = None, - **kw: object, - ): - if kw: - trace("unknown args", kw) - self.tag = tag_version - if dirty and distance is None: - distance = 0 - self.distance = distance - self.node = node - self.node_date = node_date - if "SOURCE_DATE_EPOCH" in os.environ: - date_epoch = int(os.environ["SOURCE_DATE_EPOCH"]) - self.time = datetime.fromtimestamp(date_epoch, timezone.utc) - else: - self.time = datetime.now(timezone.utc) - self._extra = kw - self.dirty = dirty - self.preformatted = preformatted - self.branch = branch - self.config = config + tag: Any + config: Configuration + distance: int | None = None + node: str | None = None + dirty: bool = False + preformatted: bool = False + branch: str | None = None + node_date: date | None = None + time: datetime = dataclasses.field( + init=False, default_factory=_source_epoch_or_utc_now + ) - @property - def extra(self) -> dict[str, Any]: - warnings.warn( - "ScmVersion.extra is deprecated and will be removed in future", - category=DeprecationWarning, - stacklevel=2, - ) - return self._extra + def __post_init__(self) -> None: + if self.dirty and self.distance is None: + self.distance = 0 @property def exact(self) -> bool: @@ -213,7 +198,6 @@ def meta( branch: str | None = None, config: Configuration | None = None, node_date: date | None = None, - **kw: Any, ) -> ScmVersion: if not config: warnings.warn( @@ -233,7 +217,6 @@ def meta( branch=branch, config=config, node_date=node_date, - **kw, ) -- cgit v1.2.1 From 770d5dd8d9366679865a8d25e71a381b527833d6 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 17 Jan 2023 22:46:21 +0100 Subject: scmversion: stricter typing for tag --- src/setuptools_scm/version.py | 4 +++- testing/test_version.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 5a822b8..d177ef5 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -20,7 +20,9 @@ if TYPE_CHECKING: from . import _types as _t + from ._version_cls import Version as PkgVersion +from . import _version_cls as _v from .config import Configuration from .config import _VersionT from .utils import trace @@ -124,7 +126,7 @@ def _source_epoch_or_utc_now() -> datetime: @dataclasses.dataclass class ScmVersion: - tag: Any + tag: _v.Version | _v.NonNormalizedVersion | str config: Configuration distance: int | None = None node: str | None = None diff --git a/testing/test_version.py b/testing/test_version.py index 0a07095..5059ab7 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -183,7 +183,7 @@ def test_tag_regex1(tag: str, expected: str) -> None: result = meta(tag, config=c) else: result = meta(tag, config=c) - + assert not isinstance(result.tag, str) assert result.tag.public == expected -- cgit v1.2.1 From 91061f90e292e7aacc71bc8045b38ee1261f4c07 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 07:57:29 +0100 Subject: move private helpers to a private modue --- src/setuptools_scm/_entrypoints.py | 10 +++++ src/setuptools_scm/_modify_version.py | 64 ++++++++++++++++++++++++++++++ src/setuptools_scm/version.py | 75 ++++------------------------------- 3 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 src/setuptools_scm/_modify_version.py diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index 2efb9f8..d790c46 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -97,3 +97,13 @@ def iter_entry_points( if name is None: return iter(eps) return (ep for ep in eps if ep.name == name) + + +def _get_ep(group: str, name: str) -> Any | None: + from ._entrypoints import iter_entry_points + + for ep in iter_entry_points(group, name): + trace("ep found:", ep.name) + return ep.load() + else: + return None diff --git a/src/setuptools_scm/_modify_version.py b/src/setuptools_scm/_modify_version.py new file mode 100644 index 0000000..dc80c1a --- /dev/null +++ b/src/setuptools_scm/_modify_version.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import re +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .version import ScmVersion + + +def _strip_local(version_string: str) -> str: + public, sep, local = version_string.partition("+") + return public + + +def _add_post(version: str) -> str: + if "post" in version: + raise ValueError( + f"{version} already is a post release, refusing to guess the update" + ) + return f"{version}.post1" + + +def _bump_dev(version: str) -> str | None: + if ".dev" not in version: + return None + + prefix, tail = version.rsplit(".dev", 1) + if tail != "0": + raise ValueError( + "choosing custom numbers for the `.devX` distance " + "is not supported.\n " + f"The {version} can't be bumped\n" + "Please drop the tag or create a new supported one ending in .dev0" + ) + return prefix + + +def _bump_regex(version: str) -> str: + match = re.match(r"(.*?)(\d+)$", version) + if match is None: + raise ValueError( + "{version} does not end with a number to bump, " + "please correct or use a custom version scheme".format(version=version) + ) + else: + prefix, tail = match.groups() + return "%s%d" % (prefix, int(tail) + 1) + + +def _format_local_with_time(version: ScmVersion, time_format: str) -> str: + + if version.exact or version.node is None: + return version.format_choice( + "", "+d{time:{time_format}}", time_format=time_format + ) + else: + return version.format_choice( + "+{node}", "+{node}.d{time:{time_format}}", time_format=time_format + ) + + +def _dont_guess_next_version(tag_version: ScmVersion) -> str: + version = _strip_local(str(tag_version.tag)) + return _bump_dev(version) or _add_post(version) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index d177ef5..7f5c298 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -15,6 +15,13 @@ from typing import Match from typing import overload from typing import TYPE_CHECKING +from ._entrypoints import _get_ep +from ._modify_version import _bump_dev +from ._modify_version import _bump_regex +from ._modify_version import _dont_guess_next_version +from ._modify_version import _format_local_with_time +from ._modify_version import _strip_local + if TYPE_CHECKING: from typing_extensions import Concatenate @@ -193,6 +200,7 @@ def _parse_tag( def meta( tag: str | _VersionT, + *, distance: int | None = None, dirty: bool = False, node: str | None = None, @@ -227,51 +235,6 @@ def guess_next_version(tag_version: ScmVersion) -> str: return _bump_dev(version) or _bump_regex(version) -def _dont_guess_next_version(tag_version: ScmVersion) -> str: - version = _strip_local(str(tag_version.tag)) - return _bump_dev(version) or _add_post(version) - - -def _strip_local(version_string: str) -> str: - public, sep, local = version_string.partition("+") - return public - - -def _add_post(version: str) -> str: - if "post" in version: - raise ValueError( - f"{version} already is a post release, refusing to guess the update" - ) - return f"{version}.post1" - - -def _bump_dev(version: str) -> str | None: - if ".dev" not in version: - return None - - prefix, tail = version.rsplit(".dev", 1) - if tail != "0": - raise ValueError( - "choosing custom numbers for the `.devX` distance " - "is not supported.\n " - f"The {version} can't be bumped\n" - "Please drop the tag or create a new supported one ending in .dev0" - ) - return prefix - - -def _bump_regex(version: str) -> str: - match = re.match(r"(.*?)(\d+)$", version) - if match is None: - raise ValueError( - "{version} does not end with a number to bump, " - "please correct or use a custom version scheme".format(version=version) - ) - else: - prefix, tail = match.groups() - return "%s%d" % (prefix, int(tail) + 1) - - def guess_next_dev_version(version: ScmVersion) -> str: if version.exact: return version.format_with("{tag}") @@ -431,18 +394,6 @@ def calver_by_date(version: ScmVersion) -> str: ) -def _format_local_with_time(version: ScmVersion, time_format: str) -> str: - - if version.exact or version.node is None: - return version.format_choice( - "", "+d{time:{time_format}}", time_format=time_format - ) - else: - return version.format_choice( - "+{node}", "+{node}.d{time:{time_format}}", time_format=time_format - ) - - def get_local_node_and_date(version: ScmVersion) -> str: return _format_local_with_time(version, time_format="%Y%m%d") @@ -466,16 +417,6 @@ def postrelease_version(version: ScmVersion) -> str: return version.format_with("{tag}.post{distance}") -def _get_ep(group: str, name: str) -> Any | None: - from ._entrypoints import iter_entry_points - - for ep in iter_entry_points(group, name): - trace("ep found:", ep.name) - return ep.load() - else: - return None - - def _iter_version_schemes( entrypoint: str, scheme_value: _t.VERSION_SCHEMES, -- cgit v1.2.1 From fd04e244cdebae5398e37aa7c70bace604012673 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 08:06:11 +0100 Subject: move entrypoints helpers to the belonging module --- src/setuptools_scm/_entrypoints.py | 68 +++++++++++++++++++++++++++++++++----- src/setuptools_scm/version.py | 51 +--------------------------- 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index d790c46..5ebc9ca 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -2,13 +2,15 @@ from __future__ import annotations import warnings from typing import Any +from typing import Callable +from typing import cast from typing import Iterator from typing import overload from typing import TYPE_CHECKING +from . import version from .utils import function_has_arg from .utils import trace -from .version import ScmVersion if TYPE_CHECKING: from .config import Configuration @@ -25,17 +27,19 @@ class MaybeConfigFunction(Protocol): __name__: str @overload - def __call__(self, root: _t.PathT, config: Configuration) -> ScmVersion | None: + def __call__( + self, root: _t.PathT, config: Configuration + ) -> version.ScmVersion | None: pass @overload - def __call__(self, root: _t.PathT) -> ScmVersion | None: + def __call__(self, root: _t.PathT) -> version.ScmVersion | None: pass def _call_entrypoint_fn( root: _t.PathT, config: Configuration, fn: MaybeConfigFunction -) -> ScmVersion | None: +) -> version.ScmVersion | None: if function_has_arg(fn, "config"): return fn(root, config=config) else: @@ -51,7 +55,7 @@ def _call_entrypoint_fn( def _version_from_entrypoints( config: Configuration, fallback: bool = False -) -> ScmVersion | None: +) -> version.ScmVersion | None: if fallback: entrypoint = "setuptools_scm.parse_scm_fallback" root = config.fallback_root @@ -63,10 +67,12 @@ def _version_from_entrypoints( trace("version_from_ep", entrypoint, root) for ep in iter_matching_entrypoints(root, entrypoint, config): - version: ScmVersion | None = _call_entrypoint_fn(root, config, ep.load()) + maybe_version: version.ScmVersion | None = _call_entrypoint_fn( + root, config, ep.load() + ) trace(ep, version) - if version: - return version + if maybe_version is not None: + return maybe_version return None @@ -107,3 +113,49 @@ def _get_ep(group: str, name: str) -> Any | None: return ep.load() else: return None + + +def _iter_version_schemes( + entrypoint: str, + scheme_value: _t.VERSION_SCHEMES, + _memo: set[object] | None = None, +) -> Iterator[Callable[[version.ScmVersion], str]]: + if _memo is None: + _memo = set() + if isinstance(scheme_value, str): + scheme_value = cast( + "_t.VERSION_SCHEMES", + _get_ep(entrypoint, scheme_value), + ) + + if isinstance(scheme_value, (list, tuple)): + for variant in scheme_value: + if variant not in _memo: + _memo.add(variant) + yield from _iter_version_schemes(entrypoint, variant, _memo=_memo) + elif callable(scheme_value): + yield scheme_value + + +@overload +def _call_version_scheme( + version: version.ScmVersion, entypoint: str, given_value: str, default: str +) -> str: + ... + + +@overload +def _call_version_scheme( + version: version.ScmVersion, entypoint: str, given_value: str, default: None +) -> str | None: + ... + + +def _call_version_scheme( + version: version.ScmVersion, entypoint: str, given_value: str, default: str | None +) -> str | None: + for scheme in _iter_version_schemes(entypoint, given_value): + result = scheme(version) + if result is not None: + return result + return default diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 7f5c298..53c849f 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -9,13 +9,10 @@ from datetime import datetime from datetime import timezone from typing import Any from typing import Callable -from typing import cast -from typing import Iterator from typing import Match -from typing import overload from typing import TYPE_CHECKING -from ._entrypoints import _get_ep +from ._entrypoints import _call_version_scheme from ._modify_version import _bump_dev from ._modify_version import _bump_regex from ._modify_version import _dont_guess_next_version @@ -417,52 +414,6 @@ def postrelease_version(version: ScmVersion) -> str: return version.format_with("{tag}.post{distance}") -def _iter_version_schemes( - entrypoint: str, - scheme_value: _t.VERSION_SCHEMES, - _memo: set[object] | None = None, -) -> Iterator[Callable[[ScmVersion], str]]: - if _memo is None: - _memo = set() - if isinstance(scheme_value, str): - scheme_value = cast( - "_t.VERSION_SCHEMES", - _get_ep(entrypoint, scheme_value), - ) - - if isinstance(scheme_value, (list, tuple)): - for variant in scheme_value: - if variant not in _memo: - _memo.add(variant) - yield from _iter_version_schemes(entrypoint, variant, _memo=_memo) - elif callable(scheme_value): - yield scheme_value - - -@overload -def _call_version_scheme( - version: ScmVersion, entypoint: str, given_value: str, default: str -) -> str: - ... - - -@overload -def _call_version_scheme( - version: ScmVersion, entypoint: str, given_value: str, default: None -) -> str | None: - ... - - -def _call_version_scheme( - version: ScmVersion, entypoint: str, given_value: str, default: str | None -) -> str | None: - for scheme in _iter_version_schemes(entypoint, given_value): - result = scheme(version) - if result is not None: - return result - return default - - def format_version(version: ScmVersion, **config: Any) -> str: trace("scm version", version) trace("config", config) -- cgit v1.2.1 From 4208223520c72bad5ab47f31dbc4ab166874c27b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 08:29:55 +0100 Subject: move version type validation to version class --- src/setuptools_scm/_version_cls.py | 37 ++++++++++++++++++++++++++++++ src/setuptools_scm/config.py | 46 ++++---------------------------------- src/setuptools_scm/version.py | 3 +-- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/setuptools_scm/_version_cls.py b/src/setuptools_scm/_version_cls.py index 39e66b2..ada1af9 100644 --- a/src/setuptools_scm/_version_cls.py +++ b/src/setuptools_scm/_version_cls.py @@ -1,6 +1,9 @@ from __future__ import annotations from logging import getLogger +from typing import cast +from typing import Type +from typing import Union from packaging.version import InvalidVersion from packaging.version import Version as Version @@ -46,3 +49,37 @@ def _version_as_tuple(version_str: str) -> tuple[int | str, ...]: if parsed_version.local is not None: version_fields += (parsed_version.local,) return version_fields + + +_VersionT = Union[Version, NonNormalizedVersion] + + +def import_name(name: str) -> object: + import importlib + + pkg_name, cls_name = name.rsplit(".", 1) + pkg = importlib.import_module(pkg_name) + return getattr(pkg, cls_name) + + +def _validate_version_cls( + version_cls: type[_VersionT] | str | None, normalize: bool +) -> type[_VersionT]: + if not normalize: + if version_cls is not None: + raise ValueError( + "Providing a custom `version_cls` is not permitted when " + "`normalize=False`" + ) + return NonNormalizedVersion + else: + # Use `version_cls` if provided, default to packaging or pkg_resources + if version_cls is None: + return Version + elif isinstance(version_cls, str): + try: + return cast(Type[_VersionT], import_name(version_cls)) + except: # noqa + raise ValueError(f"Unable to import version_cls='{version_cls}'") + else: + return version_cls diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 3bf250a..ebc11ca 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -6,24 +6,20 @@ import re import warnings from typing import Any from typing import Callable -from typing import cast from typing import Pattern -from typing import Type from typing import TYPE_CHECKING -from typing import Union from ._integration.pyproject_reading import ( get_args_for_pyproject as _get_args_for_pyproject, ) from ._integration.pyproject_reading import read_pyproject as _read_pyproject -from ._version_cls import NonNormalizedVersion -from ._version_cls import Version +from ._version_cls import _validate_version_cls +from ._version_cls import _VersionT from .utils import trace if TYPE_CHECKING: from . import _types as _t - from setuptools_scm.version import ScmVersion DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" DEFAULT_VERSION_SCHEME = "guess-next-dev" @@ -71,38 +67,6 @@ def _check_absolute_root(root: _t.PathT, relative_to: _t.PathT | None) -> str: return os.path.abspath(root) -_VersionT = Union[Version, NonNormalizedVersion] - - -def _validate_version_cls( - version_cls: type[_VersionT] | str | None, normalize: bool -) -> type[_VersionT]: - if not normalize: - # `normalize = False` means `version_cls = NonNormalizedVersion` - if version_cls is not None: - raise ValueError( - "Providing a custom `version_cls` is not permitted when " - "`normalize=False`" - ) - return NonNormalizedVersion - else: - # Use `version_cls` if provided, default to packaging or pkg_resources - if version_cls is None: - return Version - elif isinstance(version_cls, str): - try: - # Not sure this will work in old python - import importlib - - pkg, cls_name = version_cls.rsplit(".", 1) - version_cls_host = importlib.import_module(pkg) - return cast(Type[_VersionT], getattr(version_cls_host, cls_name)) - except: # noqa - raise ValueError(f"Unable to import version_cls='{version_cls}'") - else: - return version_cls - - class Configuration: """Global configuration model""" @@ -115,10 +79,8 @@ class Configuration: self, relative_to: _t.PathT | None = None, root: _t.PathT = ".", - version_scheme: ( - str | Callable[[ScmVersion], str | None] - ) = DEFAULT_VERSION_SCHEME, - local_scheme: (str | Callable[[ScmVersion], str | None]) = DEFAULT_LOCAL_SCHEME, + version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME, + local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME, write_to: _t.PathT | None = None, write_to_template: str | None = None, tag_regex: str | Pattern[str] = DEFAULT_TAG_REGEX, diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 53c849f..20a406e 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -25,10 +25,9 @@ if TYPE_CHECKING: from . import _types as _t -from ._version_cls import Version as PkgVersion +from ._version_cls import Version as PkgVersion, _VersionT from . import _version_cls as _v from .config import Configuration -from .config import _VersionT from .utils import trace SEMVER_MINOR = 2 -- cgit v1.2.1 From 8e4e22fa18fc274f1839965bd7855716d72b8363 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 13:45:08 +0100 Subject: drop the deprecated version_from_scm helper --- src/setuptools_scm/__init__.py | 12 ------------ testing/test_basic_api.py | 5 ----- 2 files changed, 17 deletions(-) diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index aeb4ab6..a9e129e 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -5,7 +5,6 @@ from __future__ import annotations import os -import warnings from typing import Any from typing import Callable from typing import TYPE_CHECKING @@ -45,16 +44,6 @@ __version_tuple__ = version_tuple = {version_tuple!r} } -def version_from_scm(root: _t.PathT) -> ScmVersion | None: - warnings.warn( - "version_from_scm is deprecated please use get_version", - category=DeprecationWarning, - stacklevel=2, - ) - config = Configuration(root=root) - return _version_from_entrypoints(config) - - def dump_version( root: _t.PathT, version: str, @@ -173,7 +162,6 @@ def _get_version(config: Configuration) -> str | None: __all__ = [ "get_version", "dump_version", - "version_from_scm", "Configuration", "DEFAULT_VERSION_SCHEME", "DEFAULT_LOCAL_SCHEME", diff --git a/testing/test_basic_api.py b/testing/test_basic_api.py index ba11a23..d14ad1d 100644 --- a/testing/test_basic_api.py +++ b/testing/test_basic_api.py @@ -57,11 +57,6 @@ def test_root_parameter_creation(monkeypatch: pytest.MonkeyPatch) -> None: setuptools_scm.get_version() -def test_version_from_scm(wd: WorkDir) -> None: - with pytest.warns(DeprecationWarning, match=".*version_from_scm.*"): - setuptools_scm.version_from_scm(str(wd)) - - def test_root_parameter_pass_by( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: -- cgit v1.2.1 From 45ad9c82ec68096ff76a3a195598d98a7e426423 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 13:52:31 +0100 Subject: cut down on the exposed helpers in __init__.py --- src/setuptools_scm/__init__.py | 34 ++++++++++++++-------------------- src/setuptools_scm/_types.py | 1 + 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index a9e129e..385552b 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -6,7 +6,7 @@ from __future__ import annotations import os from typing import Any -from typing import Callable +from typing import Pattern from typing import TYPE_CHECKING from ._entrypoints import _call_entrypoint_fn @@ -22,15 +22,10 @@ from .config import DEFAULT_LOCAL_SCHEME from .config import DEFAULT_TAG_REGEX from .config import DEFAULT_VERSION_SCHEME from .discover import iter_matching_entrypoints -from .utils import function_has_arg -from .utils import trace from .version import format_version -from .version import meta -from .version import ScmVersion if TYPE_CHECKING: from typing import NoReturn - from . import _types as _t TEMPLATES = { @@ -67,30 +62,32 @@ def dump_version( fp.write(template.format(version=version, version_tuple=version_tuple)) -def _do_parse(config: Configuration) -> ScmVersion | None: +def _do_parse(config: Configuration) -> _t.SCMVERSION | None: + from .version import ScmVersion + pretended = _read_pretended_version_for(config) if pretended is not None: return pretended - + parsed_version: ScmVersion | None if config.parse: parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) if isinstance(parse_result, str): raise TypeError( f"version parse result was {str!r}\nplease return a parsed version" ) - version: ScmVersion | None + if parse_result: assert isinstance(parse_result, ScmVersion) - version = parse_result + parsed_version = parse_result else: - version = _version_from_entrypoints(config, fallback=True) + parsed_version = _version_from_entrypoints(config, fallback=True) else: # include fallbacks after dropping them from the main entrypoint - version = _version_from_entrypoints(config) or _version_from_entrypoints( + parsed_version = _version_from_entrypoints(config) or _version_from_entrypoints( config, fallback=True ) - return version + return parsed_version def _version_missing(config: Configuration) -> NoReturn: @@ -108,17 +105,17 @@ def _version_missing(config: Configuration) -> NoReturn: def get_version( root: str = ".", - version_scheme: Callable[[ScmVersion], str] | str = DEFAULT_VERSION_SCHEME, - local_scheme: Callable[[ScmVersion], str] | str = DEFAULT_LOCAL_SCHEME, + version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME, + local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME, write_to: _t.PathT | None = None, write_to_template: str | None = None, relative_to: str | None = None, - tag_regex: str = DEFAULT_TAG_REGEX, + tag_regex: str | Pattern[str] = 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: Any | None = None, + git_describe_command: _t.CMD_TYPE | None = None, dist_name: str | None = None, version_cls: Any | None = None, normalize: bool = True, @@ -171,9 +168,6 @@ __all__ = [ "Version", "NonNormalizedVersion", # TODO: are the symbols below part of public API ? - "function_has_arg", - "trace", "format_version", - "meta", "iter_matching_entrypoints", ] diff --git a/src/setuptools_scm/_types.py b/src/setuptools_scm/_types.py index 44cfbac..c19bf35 100644 --- a/src/setuptools_scm/_types.py +++ b/src/setuptools_scm/_types.py @@ -21,6 +21,7 @@ CMD_TYPE: TypeAlias = Union[List[str], str] VERSION_SCHEME: TypeAlias = Union[str, Callable[["version.ScmVersion"], str]] VERSION_SCHEMES: TypeAlias = Union[List[str], Tuple[str, ...], VERSION_SCHEME] +SCMVERSION: TypeAlias = "version.ScmVersion" class EntrypointProtocol(Protocol): -- cgit v1.2.1 From bf0e8134e6b2101031acc3b3bb7b9bb4d2e6dd1b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 14:03:55 +0100 Subject: remove the legacy version parser api config is no longer optional --- src/setuptools_scm/__init__.py | 3 +-- src/setuptools_scm/_entrypoints.py | 36 ++---------------------------------- src/setuptools_scm/git.py | 6 ++---- src/setuptools_scm/hacks.py | 8 ++------ src/setuptools_scm/hg.py | 11 +++-------- src/setuptools_scm/version.py | 9 ++------- testing/test_mercurial.py | 6 ++++-- testing/test_version.py | 3 ++- 8 files changed, 18 insertions(+), 64 deletions(-) diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index 385552b..11756ba 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -9,7 +9,6 @@ from typing import Any from typing import Pattern from typing import TYPE_CHECKING -from ._entrypoints import _call_entrypoint_fn from ._entrypoints import _version_from_entrypoints from ._overrides import _read_pretended_version_for from ._overrides import PRETEND_KEY @@ -70,7 +69,7 @@ def _do_parse(config: Configuration) -> _t.SCMVERSION | None: return pretended parsed_version: ScmVersion | None if config.parse: - parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse) + parse_result = config.parse(config.absolute_root, config=config) if isinstance(parse_result, str): raise TypeError( f"version parse result was {str!r}\nplease return a parsed version" diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index 5ebc9ca..e771750 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -9,7 +9,6 @@ from typing import overload from typing import TYPE_CHECKING from . import version -from .utils import function_has_arg from .utils import trace if TYPE_CHECKING: @@ -23,36 +22,6 @@ else: pass -class MaybeConfigFunction(Protocol): - __name__: str - - @overload - def __call__( - self, root: _t.PathT, config: Configuration - ) -> version.ScmVersion | None: - pass - - @overload - def __call__(self, root: _t.PathT) -> version.ScmVersion | None: - pass - - -def _call_entrypoint_fn( - root: _t.PathT, config: Configuration, fn: MaybeConfigFunction -) -> version.ScmVersion | None: - if function_has_arg(fn, "config"): - return fn(root, config=config) - else: - warnings.warn( - f"parse function {fn.__module__}.{fn.__name__}" - " are required to provide a named argument" - " 'config', setuptools_scm>=8.0 will remove support.", - category=DeprecationWarning, - stacklevel=2, - ) - return fn(root) - - def _version_from_entrypoints( config: Configuration, fallback: bool = False ) -> version.ScmVersion | None: @@ -67,9 +36,8 @@ def _version_from_entrypoints( trace("version_from_ep", entrypoint, root) for ep in iter_matching_entrypoints(root, entrypoint, config): - maybe_version: version.ScmVersion | None = _call_entrypoint_fn( - root, config, ep.load() - ) + fn = ep.load() + maybe_version: version.ScmVersion | None = fn(root, config=config) trace(ep, version) if maybe_version is not None: return maybe_version diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index 16ca378..a0a7955 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -286,7 +286,7 @@ def search_parent(dirname: _t.PathT) -> GitWorkdir | None: def archival_to_version( - data: dict[str, str], config: Configuration | None = None + data: dict[str, str], config: Configuration ) -> ScmVersion | None: node: str | None trace("data", data) @@ -315,9 +315,7 @@ def archival_to_version( return meta("0.0", node=node, config=config) -def parse_archival( - root: _t.PathT, config: Configuration | None = None -) -> ScmVersion | None: +def parse_archival(root: _t.PathT, config: Configuration) -> ScmVersion | None: archival = os.path.join(root, ".git_archival.txt") data = data_from_mime(archival) return archival_to_version(data, config=config) diff --git a/src/setuptools_scm/hacks.py b/src/setuptools_scm/hacks.py index 9ca0df9..6d2f1f9 100644 --- a/src/setuptools_scm/hacks.py +++ b/src/setuptools_scm/hacks.py @@ -15,9 +15,7 @@ from .version import tag_to_version _UNKNOWN = "UNKNOWN" -def parse_pkginfo( - root: _t.PathT, config: Configuration | None = None -) -> ScmVersion | None: +def parse_pkginfo(root: _t.PathT, config: Configuration) -> ScmVersion | None: pkginfo = os.path.join(root, "PKG-INFO") trace("pkginfo", pkginfo) @@ -29,9 +27,7 @@ def parse_pkginfo( return None -def parse_pip_egg_info( - root: _t.PathT, config: Configuration | None = None -) -> ScmVersion | None: +def parse_pip_egg_info(root: _t.PathT, config: Configuration) -> ScmVersion | None: pipdir = os.path.join(root, "pip-egg-info") if not os.path.isdir(pipdir): return None diff --git a/src/setuptools_scm/hg.py b/src/setuptools_scm/hg.py index 3616b1a..cf5b51e 100644 --- a/src/setuptools_scm/hg.py +++ b/src/setuptools_scm/hg.py @@ -143,10 +143,7 @@ class HgWorkdir(Workdir): return bool(self.hg_log(revset, ".")) -def parse(root: _t.PathT, config: Configuration | None = None) -> ScmVersion | None: - if not config: - config = Configuration(root=root) - +def parse(root: _t.PathT, config: Configuration) -> ScmVersion | None: if os.path.exists(os.path.join(root, ".hg/git")): paths, _, ret = do_ex("hg path", root) if not ret: @@ -169,9 +166,7 @@ def parse(root: _t.PathT, config: Configuration | None = None) -> ScmVersion | N return wd.get_meta(config) -def archival_to_version( - data: dict[str, str], config: Configuration | None = None -) -> ScmVersion: +def archival_to_version(data: dict[str, str], config: Configuration) -> ScmVersion: trace("data", data) node = data.get("node", "")[:12] if node: @@ -189,7 +184,7 @@ def archival_to_version( return meta("0.0", node=node, config=config) -def parse_archival(root: _t.PathT, config: Configuration | None = None) -> ScmVersion: +def parse_archival(root: _t.PathT, config: Configuration) -> ScmVersion: archival = os.path.join(root, ".hg_archival.txt") data = data_from_mime(archival) return archival_to_version(data, config=config) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 20a406e..16992d3 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -202,15 +202,10 @@ def meta( node: str | None = None, preformatted: bool = False, branch: str | None = None, - config: Configuration | None = None, + config: Configuration, node_date: date | None = None, ) -> ScmVersion: - if not config: - warnings.warn( - "meta invoked without explicit configuration," - " will use defaults where required." - ) - config = Configuration() + parsed_version = _parse_tag(tag, preformatted, config) trace("version", tag, "->", parsed_version) assert parsed_version is not None, "Can't parse version %s" % tag diff --git a/testing/test_mercurial.py b/testing/test_mercurial.py index 5e0bc02..9ed6df6 100644 --- a/testing/test_mercurial.py +++ b/testing/test_mercurial.py @@ -54,8 +54,9 @@ def test_archival_to_version(expected: str, data: dict[str, str]) -> None: def test_hg_gone(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("PATH", str(wd.cwd / "not-existing")) + config = Configuration() with pytest.raises(EnvironmentError, match="'hg' was not found"): - parse(str(wd.cwd)) + parse(str(wd.cwd), config=config) def test_find_files_stop_at_root_hg( @@ -129,7 +130,8 @@ def test_version_in_merge(wd: WorkDir) -> None: @pytest.mark.issue(128) def test_parse_no_worktree(tmp_path: Path) -> None: - ret = parse(os.fspath(tmp_path)) + config = Configuration() + ret = parse(os.fspath(tmp_path), config) assert ret is None diff --git a/testing/test_version.py b/testing/test_version.py index 5059ab7..2901158 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -348,7 +348,8 @@ def test_custom_version_cls() -> None: def __repr__(self) -> str: return "MyVersion" % self.tag - scm_version = meta("1.0.0-foo", config=Configuration(version_cls=MyVersion)) + config = Configuration(version_cls=MyVersion) + scm_version = meta("1.0.0-foo", config=config) assert isinstance(scm_version.tag, MyVersion) assert str(scm_version.tag) == "Custom 1.0.0-foo" -- cgit v1.2.1 From 78e7d6bb470e3c0ae5fd744e572f6e6085401a33 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 14:08:38 +0100 Subject: potential breaking: Configuration as dataclass --- src/setuptools_scm/__init__.py | 8 ++- src/setuptools_scm/config.py | 114 +++++++++----------------------------- src/setuptools_scm/integration.py | 6 +- src/setuptools_scm/version.py | 6 +- testing/test_version.py | 4 +- 5 files changed, 44 insertions(+), 94 deletions(-) diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index 11756ba..98c7e61 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -5,6 +5,7 @@ from __future__ import annotations import os +import re from typing import Any from typing import Pattern from typing import TYPE_CHECKING @@ -13,6 +14,7 @@ from ._entrypoints import _version_from_entrypoints from ._overrides import _read_pretended_version_for from ._overrides import PRETEND_KEY from ._overrides import PRETEND_KEY_NAMED +from ._version_cls import _validate_version_cls from ._version_cls import _version_as_tuple from ._version_cls import NonNormalizedVersion from ._version_cls import Version @@ -126,9 +128,13 @@ def get_version( in the root of the repository to direct setuptools_scm to the root of the repository by supplying ``__file__``. """ - + version_cls = _validate_version_cls(version_cls, normalize) + del normalize + if isinstance((tag_regex), str): + tag_regex = re.compile(tag_regex) config = Configuration(**locals()) maybe_version = _get_version(config) + if maybe_version is None: _version_missing(config) return maybe_version diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index ebc11ca..0394eba 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -1,6 +1,7 @@ """ configuration """ from __future__ import annotations +import dataclasses import os import re import warnings @@ -13,23 +14,26 @@ from ._integration.pyproject_reading import ( get_args_for_pyproject as _get_args_for_pyproject, ) from ._integration.pyproject_reading import read_pyproject as _read_pyproject -from ._version_cls import _validate_version_cls from ._version_cls import _VersionT +from ._version_cls import Version as _Version from .utils import trace if TYPE_CHECKING: from . import _types as _t -DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" +DEFAULT_TAG_REGEX = re.compile( + r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" +) DEFAULT_VERSION_SCHEME = "guess-next-dev" DEFAULT_LOCAL_SCHEME = "node-and-date" def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]: if not value: - value = DEFAULT_TAG_REGEX - regex = re.compile(value) + regex = DEFAULT_TAG_REGEX + else: + regex = re.compile(value) group_names = regex.groupindex.keys() if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names): @@ -67,95 +71,31 @@ def _check_absolute_root(root: _t.PathT, relative_to: _t.PathT | None) -> str: return os.path.abspath(root) +@dataclasses.dataclass class Configuration: """Global configuration model""" - parent: _t.PathT | None - _root: str - _relative_to: str | None - version_cls: type[_VersionT] - - def __init__( - self, - relative_to: _t.PathT | None = None, - root: _t.PathT = ".", - version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME, - local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME, - write_to: _t.PathT | None = None, - write_to_template: str | None = None, - tag_regex: str | Pattern[str] = 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: type[_VersionT] | type | str | None = None, - normalize: bool = True, - search_parent_directories: bool = False, - ): - # TODO: - self._relative_to = None if relative_to is None else os.fspath(relative_to) - self._root = "." - - self.root = os.fspath(root) - self.version_scheme = version_scheme - self.local_scheme = local_scheme - self.write_to = write_to - self.write_to_template = write_to_template - self.parentdir_prefix_version = parentdir_prefix_version - self.fallback_version = fallback_version - self.fallback_root = fallback_root # type: ignore - self.parse = parse - self.tag_regex = tag_regex # type: ignore - self.git_describe_command = git_describe_command - self.dist_name = dist_name - self.search_parent_directories = search_parent_directories - self.parent = None - - self.version_cls = _validate_version_cls(version_cls, normalize) - - @property - def fallback_root(self) -> str: - return self._fallback_root - - @fallback_root.setter - def fallback_root(self, value: _t.PathT) -> None: - self._fallback_root = os.path.abspath(value) + relative_to: _t.PathT | None = None + root: _t.PathT = "." + version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME + local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME + tag_regex: Pattern[str] = DEFAULT_TAG_REGEX + parentdir_prefix_version: str | None = None + fallback_version: str | None = None + fallback_root: _t.PathT = "." + write_to: _t.PathT | None = None + write_to_template: str | None = None + parse: Any | None = None + git_describe_command: _t.CMD_TYPE | None = None + dist_name: str | None = None + version_cls: type[_VersionT] = _Version + search_parent_directories: bool = False + + parent: _t.PathT | None = None @property def absolute_root(self) -> str: - return self._absolute_root - - @property - def relative_to(self) -> str | None: - return self._relative_to - - @relative_to.setter - def relative_to(self, value: _t.PathT) -> None: - self._absolute_root = _check_absolute_root(self._root, value) - self._relative_to = os.fspath(value) - trace("root", repr(self._absolute_root)) - trace("relative_to", repr(value)) - - @property - def root(self) -> str: - return self._root - - @root.setter - def root(self, value: _t.PathT) -> None: - self._absolute_root = _check_absolute_root(value, self._relative_to) - self._root = os.fspath(value) - trace("root", repr(self._absolute_root)) - trace("relative_to", repr(self._relative_to)) - - @property - def tag_regex(self) -> Pattern[str]: - return self._tag_regex - - @tag_regex.setter - def tag_regex(self, value: str | Pattern[str]) -> None: - self._tag_regex = _check_tag_regex(value) + return _check_absolute_root(self.root, self.relative_to) @classmethod def from_file( diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index 2134ff1..b866caa 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -15,6 +15,7 @@ from ._entrypoints import iter_entry_points from ._integration.setuptools import ( read_dist_name_from_setup_cfg as _read_dist_name_from_setup_cfg, ) +from ._version_cls import _validate_version_cls from .config import Configuration from .utils import do from .utils import trace @@ -87,7 +88,10 @@ def version_keyword( dist_name = dist.metadata.name # type: str | None if dist_name is None: dist_name = _read_dist_name_from_setup_cfg() - config = Configuration(dist_name=dist_name, **value) + version_cls = value.pop("version_cls", None) + normalize = value.pop("normalize", True) + final_version = _validate_version_cls(version_cls, normalize) + config = Configuration(dist_name=dist_name, version_cls=final_version, **value) _assign_version(dist, config) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 16992d3..bbd28a6 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -12,7 +12,7 @@ from typing import Callable from typing import Match from typing import TYPE_CHECKING -from ._entrypoints import _call_version_scheme +from . import _entrypoints from ._modify_version import _bump_dev from ._modify_version import _bump_regex from ._modify_version import _dont_guess_next_version @@ -414,12 +414,12 @@ def format_version(version: ScmVersion, **config: Any) -> str: if version.preformatted: assert isinstance(version.tag, str) return version.tag - main_version = _call_version_scheme( + main_version = _entrypoints._call_version_scheme( version, "setuptools_scm.version_scheme", config["version_scheme"], None ) trace("version", main_version) assert main_version is not None - local_version = _call_version_scheme( + local_version = _entrypoints._call_version_scheme( version, "setuptools_scm.local_scheme", config["local_scheme"], "+unknown" ) trace("local_version", local_version) diff --git a/testing/test_version.py b/testing/test_version.py index 2901158..4f25603 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -204,7 +204,7 @@ def test_version_bump_bad() -> None: def __str__(self) -> str: return self.val - config = Configuration(version_cls=YikesVersion) + config = Configuration(version_cls=YikesVersion) # type: ignore[arg-type] with pytest.raises( ValueError, match=".*does not end with a number to bump, " @@ -348,7 +348,7 @@ def test_custom_version_cls() -> None: def __repr__(self) -> str: return "MyVersion" % self.tag - config = Configuration(version_cls=MyVersion) + config = Configuration(version_cls=MyVersion) # type: ignore[arg-type] scm_version = meta("1.0.0-foo", config=config) assert isinstance(scm_version.tag, MyVersion) -- cgit v1.2.1 From ccd461b70adf6d8417a1a3474d63d6d72ee27761 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 15:55:54 +0100 Subject: drop unnecessary type-checking guards --- src/setuptools_scm/_modify_version.py | 8 +++----- src/setuptools_scm/_types.py | 11 +++++------ src/setuptools_scm/config.py | 6 +----- src/setuptools_scm/discover.py | 4 +--- src/setuptools_scm/file_finder.py | 6 ++---- src/setuptools_scm/file_finder_git.py | 5 +---- src/setuptools_scm/file_finder_hg.py | 5 +---- src/setuptools_scm/git.py | 8 ++------ 8 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/setuptools_scm/_modify_version.py b/src/setuptools_scm/_modify_version.py index dc80c1a..0be79c2 100644 --- a/src/setuptools_scm/_modify_version.py +++ b/src/setuptools_scm/_modify_version.py @@ -1,10 +1,8 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from .version import ScmVersion +from . import _types as _t def _strip_local(version_string: str) -> str: @@ -47,7 +45,7 @@ def _bump_regex(version: str) -> str: return "%s%d" % (prefix, int(tail) + 1) -def _format_local_with_time(version: ScmVersion, time_format: str) -> str: +def _format_local_with_time(version: _t.SCMVERSION, time_format: str) -> str: if version.exact or version.node is None: return version.format_choice( @@ -59,6 +57,6 @@ def _format_local_with_time(version: ScmVersion, time_format: str) -> str: ) -def _dont_guess_next_version(tag_version: ScmVersion) -> str: +def _dont_guess_next_version(tag_version: _t.SCMVERSION) -> str: version = _strip_local(str(tag_version.tag)) return _bump_dev(version) or _add_post(version) diff --git a/src/setuptools_scm/_types.py b/src/setuptools_scm/_types.py index c19bf35..4dbfddf 100644 --- a/src/setuptools_scm/_types.py +++ b/src/setuptools_scm/_types.py @@ -1,19 +1,18 @@ from __future__ import annotations +import os from typing import Any from typing import Callable from typing import List from typing import Tuple -from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +from typing_extensions import ParamSpec +from typing_extensions import Protocol +from typing_extensions import TypeAlias -if TYPE_CHECKING: - from setuptools_scm import version - import os - -from typing_extensions import ParamSpec, TypeAlias, Protocol +from setuptools_scm import version PathT: TypeAlias = Union["os.PathLike[str]", str] diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 0394eba..3cb6aa0 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -8,8 +8,8 @@ import warnings from typing import Any from typing import Callable from typing import Pattern -from typing import TYPE_CHECKING +from . import _types as _t from ._integration.pyproject_reading import ( get_args_for_pyproject as _get_args_for_pyproject, ) @@ -18,10 +18,6 @@ from ._version_cls import _VersionT from ._version_cls import Version as _Version from .utils import trace - -if TYPE_CHECKING: - from . import _types as _t - DEFAULT_TAG_REGEX = re.compile( r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" ) diff --git a/src/setuptools_scm/discover.py b/src/setuptools_scm/discover.py index f7843ee..87533fd 100644 --- a/src/setuptools_scm/discover.py +++ b/src/setuptools_scm/discover.py @@ -3,10 +3,8 @@ from __future__ import annotations import os from typing import Iterable from typing import Iterator -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from . import _types as _t +from . import _types as _t from .config import Configuration from .utils import trace diff --git a/src/setuptools_scm/file_finder.py b/src/setuptools_scm/file_finder.py index f14a946..93d87b1 100644 --- a/src/setuptools_scm/file_finder.py +++ b/src/setuptools_scm/file_finder.py @@ -1,12 +1,10 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from typing_extensions import TypeGuard - from . import _types as _t +from typing_extensions import TypeGuard +from . import _types as _t from .utils import trace diff --git a/src/setuptools_scm/file_finder_git.py b/src/setuptools_scm/file_finder_git.py index 775c49d..65aa999 100644 --- a/src/setuptools_scm/file_finder_git.py +++ b/src/setuptools_scm/file_finder_git.py @@ -5,17 +5,14 @@ import os import subprocess import tarfile from typing import IO -from typing import TYPE_CHECKING +from . import _types as _t from .file_finder import is_toplevel_acceptable from .file_finder import scm_find_files from .utils import data_from_mime from .utils import do_ex from .utils import trace -if TYPE_CHECKING: - from . import _types as _t - log = logging.getLogger(__name__) diff --git a/src/setuptools_scm/file_finder_hg.py b/src/setuptools_scm/file_finder_hg.py index 2ce974f..b750fea 100644 --- a/src/setuptools_scm/file_finder_hg.py +++ b/src/setuptools_scm/file_finder_hg.py @@ -2,17 +2,14 @@ from __future__ import annotations import os import subprocess -from typing import TYPE_CHECKING +from . import _types as _t from .file_finder import is_toplevel_acceptable from .file_finder import scm_find_files from .utils import data_from_mime from .utils import do_ex from .utils import trace -if TYPE_CHECKING: - from . import _types as _t - def _hg_toplevel(path: str) -> str | None: try: diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index a0a7955..1da5577 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -9,8 +9,8 @@ from os.path import isfile from os.path import join from os.path import samefile from typing import Callable -from typing import TYPE_CHECKING +from . import _types as _t from .config import Configuration from .scm_workdir import Workdir from .utils import _CmdResult @@ -21,11 +21,7 @@ from .utils import trace from .version import meta from .version import ScmVersion from .version import tags_to_versions - -if TYPE_CHECKING: - from . import _types as _t - - from setuptools_scm.hg_git import GitWorkdirHgClient +from setuptools_scm.hg_git import GitWorkdirHgClient REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b") DESCRIBE_UNSUPPORTED = "%(describe" -- cgit v1.2.1 From 223a0c6fd5149e90de82b6b647944c14045bfb13 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 18 Jan 2023 17:16:58 +0100 Subject: introduce config overries toml based env vars --- .../_integration/pyproject_reading.py | 11 +++-- src/setuptools_scm/_overrides.py | 47 +++++++++++++++------- src/setuptools_scm/config.py | 18 ++++++++- src/setuptools_scm/git.py | 10 +++-- testing/test_config.py | 23 +++++++++++ 5 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/setuptools_scm/_integration/pyproject_reading.py b/src/setuptools_scm/_integration/pyproject_reading.py index d9208f1..8730c0e 100644 --- a/src/setuptools_scm/_integration/pyproject_reading.py +++ b/src/setuptools_scm/_integration/pyproject_reading.py @@ -1,17 +1,16 @@ from __future__ import annotations +import os import sys import warnings from typing import Any from typing import Callable from typing import Dict from typing import NamedTuple -from typing import TYPE_CHECKING -from .setuptools import read_dist_name_from_setup_cfg +from typing_extensions import TypeAlias -if TYPE_CHECKING: - from typing_extensions import TypeAlias +from .setuptools import read_dist_name_from_setup_cfg _ROOT = "root" TOML_RESULT: TypeAlias = Dict[str, Any] @@ -19,7 +18,7 @@ TOML_LOADER: TypeAlias = Callable[[str], TOML_RESULT] class PyProjectData(NamedTuple): - name: str + name: str | os.PathLike[str] tool_name: str project: TOML_RESULT section: TOML_RESULT @@ -39,7 +38,7 @@ def lazy_toml_load(data: str) -> TOML_RESULT: def read_pyproject( - name: str = "pyproject.toml", + name: str | os.PathLike[str] = "pyproject.toml", tool_name: str = "setuptools_scm", _load_toml: TOML_LOADER | None = None, ) -> PyProjectData: diff --git a/src/setuptools_scm/_overrides.py b/src/setuptools_scm/_overrides.py index f18b82c..e0142fa 100644 --- a/src/setuptools_scm/_overrides.py +++ b/src/setuptools_scm/_overrides.py @@ -1,38 +1,55 @@ from __future__ import annotations import os +from typing import Any -from .config import Configuration +from . import config +from . import version +from ._integration.pyproject_reading import lazy_toml_load from .utils import trace -from .version import meta -from .version import ScmVersion - PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION" PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" -def _read_pretended_version_for(config: Configuration) -> ScmVersion | None: +def read_named_env( + *, tool: str = "SETUPTOOLS_SCM", name: str, dist_name: str | None +) -> str | None: + + if dist_name is not None: + val = os.environ.get(f"{tool}_{name}_FOR_{dist_name.upper()}") + if val is not None: + return val + return os.environ.get(f"{tool}_{name}") + + +def _read_pretended_version_for( + config: config.Configuration, +) -> version.ScmVersion | None: """read a a overridden version from the environment tries ``SETUPTOOLS_SCM_PRETEND_VERSION`` and ``SETUPTOOLS_SCM_PRETEND_VERSION_FOR_$UPPERCASE_DIST_NAME`` """ trace("dist name:", config.dist_name) - pretended: str | None - if config.dist_name is not None: - pretended = os.environ.get( - PRETEND_KEY_NAMED.format(name=config.dist_name.upper()) - ) - else: - pretended = None - if pretended is None: - pretended = os.environ.get(PRETEND_KEY) + pretended = read_named_env(name="PRETEND_VERSION", dist_name=config.dist_name) if pretended: # we use meta here since the pretended version # must adhere to the pep to begin with - return meta(tag=pretended, preformatted=True, config=config) + return version.meta(tag=pretended, preformatted=True, config=config) else: return None + + +def read_toml_overrides(dist_name: str | None) -> dict[str, Any]: + data = read_named_env(name="OVERRIDES", dist_name=dist_name) + if data: + if data[0] == "{": + data = "cheat=" + data + loaded = lazy_toml_load(data) + return loaded["cheat"] # type: ignore[no-any-return] + return lazy_toml_load(data) + else: + return {} diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 3cb6aa0..6a3eae7 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -14,6 +14,8 @@ from ._integration.pyproject_reading import ( get_args_for_pyproject as _get_args_for_pyproject, ) from ._integration.pyproject_reading import read_pyproject as _read_pyproject +from ._overrides import read_toml_overrides +from ._version_cls import _validate_version_cls from ._version_cls import _VersionT from ._version_cls import Version as _Version from .utils import trace @@ -96,7 +98,7 @@ class Configuration: @classmethod def from_file( cls, - name: str = "pyproject.toml", + name: str | os.PathLike[str] = "pyproject.toml", dist_name: str | None = None, _load_toml: Callable[[str], dict[str, Any]] | None = None, **kwargs: Any, @@ -111,4 +113,16 @@ class Configuration: pyproject_data = _read_pyproject(name, _load_toml=_load_toml) args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) - return cls(relative_to=name, **args) + args.update(read_toml_overrides(args["dist_name"])) + return cls.from_data(relative_to=name, data=args) + + @classmethod + def from_data( + cls, relative_to: str | os.PathLike[str], data: dict[str, Any] + ) -> Configuration: + if "tag_regex" in data: + data["tag_regex"] = _check_tag_regex(data["tag_regex"]) + version_cls = _validate_version_cls( + data.pop("version_cls", None), data.pop("normalize", True) + ) + return cls(relative_to, version_cls=version_cls, **data) diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index 1da5577..01649c7 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -9,6 +9,7 @@ from os.path import isfile from os.path import join from os.path import samefile from typing import Callable +from typing import TYPE_CHECKING from . import _types as _t from .config import Configuration @@ -21,7 +22,10 @@ from .utils import trace from .version import meta from .version import ScmVersion from .version import tags_to_versions -from setuptools_scm.hg_git import GitWorkdirHgClient + +if TYPE_CHECKING: + from . import hg_git + REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b") DESCRIBE_UNSUPPORTED = "%(describe" @@ -183,8 +187,8 @@ def parse( def _git_parse_inner( config: Configuration, - wd: GitWorkdir | GitWorkdirHgClient, - pre_parse: None | (Callable[[GitWorkdir | GitWorkdirHgClient], None]) = None, + wd: GitWorkdir | hg_git.GitWorkdirHgClient, + pre_parse: None | (Callable[[GitWorkdir | hg_git.GitWorkdirHgClient], None]) = None, describe_command: _t.CMD_TYPE | None = None, ) -> ScmVersion: if pre_parse: diff --git a/testing/test_config.py b/testing/test_config.py index 97bb36e..cd12faa 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -74,3 +74,26 @@ def test_config_from_file_protects_relative_to(tmp_path: Path) -> None: " as its always relative to the config file", ): assert Configuration.from_file(str(fn)) + + +def test_config_overrides(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + fn = tmp_path / "pyproject.toml" + fn.write_text( + textwrap.dedent( + """ + [tool.setuptools_scm] + root = "." + [project] + name = "test_a" + """ + ), + encoding="utf-8", + ) + pristine = Configuration.from_file(fn) + monkeypatch.setenv( + "SETUPTOOLS_SCM_OVERRIDES_FOR_TEST_A", '{root="..", fallback_root=".."}' + ) + overriden = Configuration.from_file(fn) + + assert pristine.root != overriden.root + assert pristine.fallback_root != overriden.fallback_root -- cgit v1.2.1 From 64a18408bba4ad6598eb2e85c877158e2015f0f9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 31 Jan 2023 10:54:11 +0100 Subject: ensure tag regex is a regex --- src/setuptools_scm/config.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 6a3eae7..84f7227 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -120,9 +120,13 @@ class Configuration: def from_data( cls, relative_to: str | os.PathLike[str], data: dict[str, Any] ) -> Configuration: - if "tag_regex" in data: - data["tag_regex"] = _check_tag_regex(data["tag_regex"]) + tag_regex = _check_tag_regex(data.pop("tag_regex", None)) version_cls = _validate_version_cls( data.pop("version_cls", None), data.pop("normalize", True) ) - return cls(relative_to, version_cls=version_cls, **data) + return cls( + relative_to, + version_cls=version_cls, + tag_regex=tag_regex, + **data, + ) -- cgit v1.2.1 From 9eefd1527765918153b440e1f729b52748b178de Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 31 Jan 2023 13:03:23 +0100 Subject: private configruation module --- setup.py | 2 +- src/setuptools_scm/__init__.py | 8 +-- src/setuptools_scm/_cli.py | 2 +- src/setuptools_scm/_config.py | 132 +++++++++++++++++++++++++++++++++++++ src/setuptools_scm/_entrypoints.py | 2 +- src/setuptools_scm/_overrides.py | 4 +- src/setuptools_scm/_types.py | 2 +- src/setuptools_scm/config.py | 132 ------------------------------------- src/setuptools_scm/discover.py | 2 +- src/setuptools_scm/file_finder.py | 4 +- src/setuptools_scm/git.py | 6 +- src/setuptools_scm/hacks.py | 2 +- src/setuptools_scm/hg.py | 4 +- src/setuptools_scm/integration.py | 2 +- src/setuptools_scm/version.py | 23 +++---- testing/test_basic_api.py | 2 +- testing/test_config.py | 2 +- testing/test_functions.py | 4 +- testing/test_mercurial.py | 2 +- testing/test_version.py | 2 +- 20 files changed, 169 insertions(+), 170 deletions(-) create mode 100644 src/setuptools_scm/_config.py delete mode 100644 src/setuptools_scm/config.py diff --git a/setup.py b/setup.py index 6667003..124b944 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def scm_version() -> str: from setuptools_scm import git from setuptools_scm import hg from setuptools_scm.version import guess_next_dev_version, get_local_node_and_date - from setuptools_scm.config import Configuration + from setuptools_scm import Configuration from setuptools_scm.version import ScmVersion diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index 98c7e61..764b51e 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -10,6 +10,10 @@ from typing import Any from typing import Pattern from typing import TYPE_CHECKING +from ._config import Configuration +from ._config import DEFAULT_LOCAL_SCHEME +from ._config import DEFAULT_TAG_REGEX +from ._config import DEFAULT_VERSION_SCHEME from ._entrypoints import _version_from_entrypoints from ._overrides import _read_pretended_version_for from ._overrides import PRETEND_KEY @@ -18,10 +22,6 @@ from ._version_cls import _validate_version_cls from ._version_cls import _version_as_tuple from ._version_cls import NonNormalizedVersion from ._version_cls import Version -from .config import Configuration -from .config import DEFAULT_LOCAL_SCHEME -from .config import DEFAULT_TAG_REGEX -from .config import DEFAULT_VERSION_SCHEME from .discover import iter_matching_entrypoints from .version import format_version diff --git a/src/setuptools_scm/_cli.py b/src/setuptools_scm/_cli.py index 8e01f24..297302b 100644 --- a/src/setuptools_scm/_cli.py +++ b/src/setuptools_scm/_cli.py @@ -5,7 +5,7 @@ import os import sys from setuptools_scm import _get_version -from setuptools_scm.config import Configuration +from setuptools_scm import Configuration from setuptools_scm.discover import walk_potential_roots from setuptools_scm.integration import find_files diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py new file mode 100644 index 0000000..84f7227 --- /dev/null +++ b/src/setuptools_scm/_config.py @@ -0,0 +1,132 @@ +""" configuration """ +from __future__ import annotations + +import dataclasses +import os +import re +import warnings +from typing import Any +from typing import Callable +from typing import Pattern + +from . import _types as _t +from ._integration.pyproject_reading import ( + get_args_for_pyproject as _get_args_for_pyproject, +) +from ._integration.pyproject_reading import read_pyproject as _read_pyproject +from ._overrides import read_toml_overrides +from ._version_cls import _validate_version_cls +from ._version_cls import _VersionT +from ._version_cls import Version as _Version +from .utils import trace + +DEFAULT_TAG_REGEX = re.compile( + r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" +) +DEFAULT_VERSION_SCHEME = "guess-next-dev" +DEFAULT_LOCAL_SCHEME = "node-and-date" + + +def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]: + if not value: + regex = DEFAULT_TAG_REGEX + else: + regex = re.compile(value) + + group_names = regex.groupindex.keys() + if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names): + warnings.warn( + "Expected tag_regex to contain a single match group or a group named" + " 'version' to identify the version part of any tag." + ) + + return regex + + +def _check_absolute_root(root: _t.PathT, relative_to: _t.PathT | None) -> str: + trace("abs root", repr(locals())) + if relative_to: + if ( + os.path.isabs(root) + and os.path.isabs(relative_to) + and not os.path.commonpath([root, relative_to]) == root + ): + warnings.warn( + "absolute root path '%s' overrides relative_to '%s'" + % (root, relative_to) + ) + if os.path.isdir(relative_to): + warnings.warn( + "relative_to is expected to be a file," + " its the directory %r\n" + "assuming the parent directory was passed" % (relative_to,) + ) + trace("dir", relative_to) + root = os.path.join(relative_to, root) + else: + trace("file", relative_to) + root = os.path.join(os.path.dirname(relative_to), root) + return os.path.abspath(root) + + +@dataclasses.dataclass +class Configuration: + """Global configuration model""" + + relative_to: _t.PathT | None = None + root: _t.PathT = "." + version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME + local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME + tag_regex: Pattern[str] = DEFAULT_TAG_REGEX + parentdir_prefix_version: str | None = None + fallback_version: str | None = None + fallback_root: _t.PathT = "." + write_to: _t.PathT | None = None + write_to_template: str | None = None + parse: Any | None = None + git_describe_command: _t.CMD_TYPE | None = None + dist_name: str | None = None + version_cls: type[_VersionT] = _Version + search_parent_directories: bool = False + + parent: _t.PathT | None = None + + @property + def absolute_root(self) -> str: + return _check_absolute_root(self.root, self.relative_to) + + @classmethod + def from_file( + cls, + name: str | os.PathLike[str] = "pyproject.toml", + dist_name: str | None = None, + _load_toml: Callable[[str], dict[str, Any]] | None = None, + **kwargs: Any, + ) -> Configuration: + """ + Read Configuration from pyproject.toml (or similar). + Raises exceptions when file is not found or toml is + not installed or the file has invalid format or does + not contain the [tool.setuptools_scm] section. + """ + + pyproject_data = _read_pyproject(name, _load_toml=_load_toml) + args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) + + args.update(read_toml_overrides(args["dist_name"])) + return cls.from_data(relative_to=name, data=args) + + @classmethod + def from_data( + cls, relative_to: str | os.PathLike[str], data: dict[str, Any] + ) -> Configuration: + tag_regex = _check_tag_regex(data.pop("tag_regex", None)) + version_cls = _validate_version_cls( + data.pop("version_cls", None), data.pop("normalize", True) + ) + return cls( + relative_to, + version_cls=version_cls, + tag_regex=tag_regex, + **data, + ) diff --git a/src/setuptools_scm/_entrypoints.py b/src/setuptools_scm/_entrypoints.py index e771750..c236434 100644 --- a/src/setuptools_scm/_entrypoints.py +++ b/src/setuptools_scm/_entrypoints.py @@ -12,7 +12,7 @@ from . import version from .utils import trace if TYPE_CHECKING: - from .config import Configuration + from ._config import Configuration from typing_extensions import Protocol from . import _types as _t else: diff --git a/src/setuptools_scm/_overrides.py b/src/setuptools_scm/_overrides.py index e0142fa..3dea9ea 100644 --- a/src/setuptools_scm/_overrides.py +++ b/src/setuptools_scm/_overrides.py @@ -3,7 +3,7 @@ from __future__ import annotations import os from typing import Any -from . import config +from . import _config from . import version from ._integration.pyproject_reading import lazy_toml_load from .utils import trace @@ -24,7 +24,7 @@ def read_named_env( def _read_pretended_version_for( - config: config.Configuration, + config: _config.Configuration, ) -> version.ScmVersion | None: """read a a overridden version from the environment diff --git a/src/setuptools_scm/_types.py b/src/setuptools_scm/_types.py index 4dbfddf..5c2cd33 100644 --- a/src/setuptools_scm/_types.py +++ b/src/setuptools_scm/_types.py @@ -12,7 +12,7 @@ from typing_extensions import ParamSpec from typing_extensions import Protocol from typing_extensions import TypeAlias -from setuptools_scm import version +from . import version PathT: TypeAlias = Union["os.PathLike[str]", str] diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py deleted file mode 100644 index 84f7227..0000000 --- a/src/setuptools_scm/config.py +++ /dev/null @@ -1,132 +0,0 @@ -""" configuration """ -from __future__ import annotations - -import dataclasses -import os -import re -import warnings -from typing import Any -from typing import Callable -from typing import Pattern - -from . import _types as _t -from ._integration.pyproject_reading import ( - get_args_for_pyproject as _get_args_for_pyproject, -) -from ._integration.pyproject_reading import read_pyproject as _read_pyproject -from ._overrides import read_toml_overrides -from ._version_cls import _validate_version_cls -from ._version_cls import _VersionT -from ._version_cls import Version as _Version -from .utils import trace - -DEFAULT_TAG_REGEX = re.compile( - r"^(?:[\w-]+-)?(?P[vV]?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$" -) -DEFAULT_VERSION_SCHEME = "guess-next-dev" -DEFAULT_LOCAL_SCHEME = "node-and-date" - - -def _check_tag_regex(value: str | Pattern[str] | None) -> Pattern[str]: - if not value: - regex = DEFAULT_TAG_REGEX - else: - regex = re.compile(value) - - group_names = regex.groupindex.keys() - if regex.groups == 0 or (regex.groups > 1 and "version" not in group_names): - warnings.warn( - "Expected tag_regex to contain a single match group or a group named" - " 'version' to identify the version part of any tag." - ) - - return regex - - -def _check_absolute_root(root: _t.PathT, relative_to: _t.PathT | None) -> str: - trace("abs root", repr(locals())) - if relative_to: - if ( - os.path.isabs(root) - and os.path.isabs(relative_to) - and not os.path.commonpath([root, relative_to]) == root - ): - warnings.warn( - "absolute root path '%s' overrides relative_to '%s'" - % (root, relative_to) - ) - if os.path.isdir(relative_to): - warnings.warn( - "relative_to is expected to be a file," - " its the directory %r\n" - "assuming the parent directory was passed" % (relative_to,) - ) - trace("dir", relative_to) - root = os.path.join(relative_to, root) - else: - trace("file", relative_to) - root = os.path.join(os.path.dirname(relative_to), root) - return os.path.abspath(root) - - -@dataclasses.dataclass -class Configuration: - """Global configuration model""" - - relative_to: _t.PathT | None = None - root: _t.PathT = "." - version_scheme: _t.VERSION_SCHEME = DEFAULT_VERSION_SCHEME - local_scheme: _t.VERSION_SCHEME = DEFAULT_LOCAL_SCHEME - tag_regex: Pattern[str] = DEFAULT_TAG_REGEX - parentdir_prefix_version: str | None = None - fallback_version: str | None = None - fallback_root: _t.PathT = "." - write_to: _t.PathT | None = None - write_to_template: str | None = None - parse: Any | None = None - git_describe_command: _t.CMD_TYPE | None = None - dist_name: str | None = None - version_cls: type[_VersionT] = _Version - search_parent_directories: bool = False - - parent: _t.PathT | None = None - - @property - def absolute_root(self) -> str: - return _check_absolute_root(self.root, self.relative_to) - - @classmethod - def from_file( - cls, - name: str | os.PathLike[str] = "pyproject.toml", - dist_name: str | None = None, - _load_toml: Callable[[str], dict[str, Any]] | None = None, - **kwargs: Any, - ) -> Configuration: - """ - Read Configuration from pyproject.toml (or similar). - Raises exceptions when file is not found or toml is - not installed or the file has invalid format or does - not contain the [tool.setuptools_scm] section. - """ - - pyproject_data = _read_pyproject(name, _load_toml=_load_toml) - args = _get_args_for_pyproject(pyproject_data, dist_name, kwargs) - - args.update(read_toml_overrides(args["dist_name"])) - return cls.from_data(relative_to=name, data=args) - - @classmethod - def from_data( - cls, relative_to: str | os.PathLike[str], data: dict[str, Any] - ) -> Configuration: - tag_regex = _check_tag_regex(data.pop("tag_regex", None)) - version_cls = _validate_version_cls( - data.pop("version_cls", None), data.pop("normalize", True) - ) - return cls( - relative_to, - version_cls=version_cls, - tag_regex=tag_regex, - **data, - ) diff --git a/src/setuptools_scm/discover.py b/src/setuptools_scm/discover.py index 87533fd..58384aa 100644 --- a/src/setuptools_scm/discover.py +++ b/src/setuptools_scm/discover.py @@ -5,7 +5,7 @@ from typing import Iterable from typing import Iterator from . import _types as _t -from .config import Configuration +from ._config import Configuration from .utils import trace diff --git a/src/setuptools_scm/file_finder.py b/src/setuptools_scm/file_finder.py index 93d87b1..27196c0 100644 --- a/src/setuptools_scm/file_finder.py +++ b/src/setuptools_scm/file_finder.py @@ -76,7 +76,9 @@ def is_toplevel_acceptable(toplevel: str | None) -> TypeGuard[str]: if toplevel is None: return False - ignored = os.environ.get("SETUPTOOLS_SCM_IGNORE_VCS_ROOTS", "").split(os.pathsep) + ignored: list[str] = os.environ.get("SETUPTOOLS_SCM_IGNORE_VCS_ROOTS", "").split( + os.pathsep + ) ignored = [os.path.normcase(p) for p in ignored] trace(toplevel, ignored) diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index 01649c7..ccd8ce7 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -12,7 +12,7 @@ from typing import Callable from typing import TYPE_CHECKING from . import _types as _t -from .config import Configuration +from . import Configuration from .scm_workdir import Workdir from .utils import _CmdResult from .utils import data_from_mime @@ -301,7 +301,9 @@ def archival_to_version( distance=None if number == 0 else number, node=node, ) - versions = tags_to_versions(REF_TAG_RE.findall(data.get("ref-names", ""))) + versions = tags_to_versions( + REF_TAG_RE.findall(data.get("ref-names", "")), config=config + ) if versions: return meta(versions[0], config=config) else: diff --git a/src/setuptools_scm/hacks.py b/src/setuptools_scm/hacks.py index 6d2f1f9..5c1e34b 100644 --- a/src/setuptools_scm/hacks.py +++ b/src/setuptools_scm/hacks.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from . import _types as _t -from .config import Configuration +from . import Configuration from .utils import data_from_mime from .utils import trace from .version import meta diff --git a/src/setuptools_scm/hg.py b/src/setuptools_scm/hg.py index cf5b51e..2c418e4 100644 --- a/src/setuptools_scm/hg.py +++ b/src/setuptools_scm/hg.py @@ -5,8 +5,8 @@ import os from pathlib import Path from typing import TYPE_CHECKING +from . import Configuration from ._version_cls import Version -from .config import Configuration from .scm_workdir import Workdir from .utils import data_from_mime from .utils import do_ex @@ -69,7 +69,7 @@ class HgWorkdir(Workdir): tags.remove("tip") if tags: - tag = tag_to_version(tags[0]) + tag = tag_to_version(tags[0], config) if tag: return meta(tag, dirty=dirty, branch=branch, config=config) diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index b866caa..a5d2c02 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -11,12 +11,12 @@ import setuptools from . import _get_version from . import _version_missing +from . import Configuration from ._entrypoints import iter_entry_points from ._integration.setuptools import ( read_dist_name_from_setup_cfg as _read_dist_name_from_setup_cfg, ) from ._version_cls import _validate_version_cls -from .config import Configuration from .utils import do from .utils import trace diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index bbd28a6..11ed5aa 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -27,7 +27,7 @@ if TYPE_CHECKING: from ._version_cls import Version as PkgVersion, _VersionT from . import _version_cls as _v -from .config import Configuration +from . import _config from .utils import trace SEMVER_MINOR = 2 @@ -36,7 +36,7 @@ SEMVER_LEN = 3 def _parse_version_tag( - tag: str | object, config: Configuration + tag: str | object, config: _config.Configuration ) -> dict[str, str] | None: tagstring = tag if isinstance(tag, str) else str(tag) match = config.tag_regex.match(tagstring) @@ -72,16 +72,13 @@ def callable_or_entrypoint(group: str, callable_or_name: str | Any) -> Any: def tag_to_version( - tag: _VersionT | str, config: Configuration | None = None + tag: _VersionT | str, config: _config.Configuration ) -> _VersionT | None: """ take a tag that might be prefixed with a keyword and return only the version part """ trace("tag", tag) - if not config: - config = Configuration() - tagdict = _parse_version_tag(tag, config) if not isinstance(tagdict, dict) or not tagdict.get("version", None): warnings.warn(f"tag {tag!r} no version found") @@ -97,15 +94,13 @@ def tag_to_version( ) ) - version = config.version_cls(version_str) + version: _VersionT = config.version_cls(version_str) trace("version", repr(version)) return version -def tags_to_versions( - tags: list[str], config: Configuration | None = None -) -> list[_VersionT]: +def tags_to_versions(tags: list[str], config: _config.Configuration) -> list[_VersionT]: """ take tags that might be prefixed with a keyword and return only the version part :param tags: an iterable of tags @@ -130,7 +125,7 @@ def _source_epoch_or_utc_now() -> datetime: @dataclasses.dataclass class ScmVersion: tag: _v.Version | _v.NonNormalizedVersion | str - config: Configuration + config: _config.Configuration distance: int | None = None node: str | None = None dirty: bool = False @@ -182,11 +177,11 @@ class ScmVersion: def _parse_tag( - tag: _VersionT | str, preformatted: bool, config: Configuration | None + tag: _VersionT | str, preformatted: bool, config: _config.Configuration ) -> _VersionT | str: if preformatted: return tag - elif config is None or not isinstance(tag, config.version_cls): + elif not isinstance(tag, config.version_cls): version = tag_to_version(tag, config) assert version is not None return version @@ -202,7 +197,7 @@ def meta( node: str | None = None, preformatted: bool = False, branch: str | None = None, - config: Configuration, + config: _config.Configuration, node_date: date | None = None, ) -> ScmVersion: diff --git a/testing/test_basic_api.py b/testing/test_basic_api.py index d14ad1d..6ebf10d 100644 --- a/testing/test_basic_api.py +++ b/testing/test_basic_api.py @@ -8,8 +8,8 @@ from pathlib import Path import pytest import setuptools_scm +from setuptools_scm import Configuration from setuptools_scm import dump_version -from setuptools_scm.config import Configuration from setuptools_scm.utils import data_from_mime from setuptools_scm.utils import do from setuptools_scm.version import ScmVersion diff --git a/testing/test_config.py b/testing/test_config.py index cd12faa..211a853 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -6,7 +6,7 @@ from pathlib import Path import pytest -from setuptools_scm.config import Configuration +from setuptools_scm import Configuration @pytest.mark.parametrize( diff --git a/testing/test_functions.py b/testing/test_functions.py index 53bc92b..ceb6cd3 100644 --- a/testing/test_functions.py +++ b/testing/test_functions.py @@ -4,10 +4,10 @@ from pathlib import Path import pytest +from setuptools_scm import Configuration from setuptools_scm import dump_version from setuptools_scm import get_version from setuptools_scm import PRETEND_KEY -from setuptools_scm.config import Configuration from setuptools_scm.utils import has_command from setuptools_scm.version import format_version from setuptools_scm.version import guess_next_version @@ -103,5 +103,5 @@ def test_has_command() -> None: ], ) def test_tag_to_version(tag: str, expected_version: str) -> None: - version = str(tag_to_version(tag)) + version = str(tag_to_version(tag, c)) assert version == expected_version diff --git a/testing/test_mercurial.py b/testing/test_mercurial.py index 9ed6df6..28a2242 100644 --- a/testing/test_mercurial.py +++ b/testing/test_mercurial.py @@ -5,9 +5,9 @@ from pathlib import Path import pytest +from setuptools_scm import Configuration from setuptools_scm import format_version from setuptools_scm import integration -from setuptools_scm.config import Configuration from setuptools_scm.hg import archival_to_version from setuptools_scm.hg import parse from setuptools_scm.utils import has_command diff --git a/testing/test_version.py b/testing/test_version.py index 4f25603..d7fefe4 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -6,7 +6,7 @@ from typing import Any import pytest -from setuptools_scm.config import Configuration +from setuptools_scm import Configuration from setuptools_scm.version import calver_by_date from setuptools_scm.version import format_version from setuptools_scm.version import guess_next_version -- cgit v1.2.1 From f7c075a0d9ba724106ebe522831087c16dc15241 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 31 Jan 2023 15:43:13 +0100 Subject: correct git workdir passover and setuptools double call in --- MANIFEST.in | 3 ++- setup.cfg | 2 +- src/setuptools_scm/__init__.py | 2 ++ src/setuptools_scm/git.py | 27 ++++++++++++--------------- src/setuptools_scm/integration.py | 12 ++++++++++-- src/setuptools_scm/version.py | 14 -------------- testing/test_git.py | 24 ++++++++++++------------ testing/test_regressions.py | 3 ++- testing/test_version.py | 7 ------- tox.ini | 17 ++++++----------- 10 files changed, 47 insertions(+), 64 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index c758a17..54e9473 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,5 +8,6 @@ include *.rst include LICENSE include *.toml include mypy.ini -include testing/Dockerfile.busted-buster +include testing/Dockerfile.* +include src/setuptools_scm/.git_archival.txt recursive-include testing *.bash diff --git a/setup.cfg b/setup.cfg index 795bb21..b69fc6f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ url = https://github.com/pypa/setuptools_scm/ author = Ronny Pfannschmidt author_email = opensource@ronnypfannschmidt.de license = MIT -license_file = LICENSE +license_files = LICENSE classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index 764b51e..91f88ed 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -50,7 +50,9 @@ def dump_version( target = os.path.normpath(os.path.join(root, write_to)) ext = os.path.splitext(target)[1] template = template or TEMPLATES.get(ext) + from .utils import trace + trace("dump", write_to, version) if template is None: raise ValueError( "bad file format: '{}' (of {}) \nonly *.txt and *.py are supported".format( diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index ccd8ce7..3a3a1b9 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -21,7 +21,7 @@ from .utils import require_command from .utils import trace from .version import meta from .version import ScmVersion -from .version import tags_to_versions +from .version import tag_to_version if TYPE_CHECKING: from . import hg_git @@ -150,33 +150,30 @@ def fail_on_shallow(wd: GitWorkdir) -> None: ) -def get_working_directory(config: Configuration) -> GitWorkdir | None: +def get_working_directory(config: Configuration, root: str) -> GitWorkdir | None: """ Return the working directory (``GitWorkdir``). """ - if config.parent: + if config.parent: # todo broken return GitWorkdir.from_potential_worktree(config.parent) if config.search_parent_directories: - return search_parent(config.absolute_root) + return search_parent(root) - return GitWorkdir.from_potential_worktree(config.absolute_root) + return GitWorkdir.from_potential_worktree(root) def parse( root: str, + config: Configuration, describe_command: str | list[str] | None = None, pre_parse: Callable[[GitWorkdir], None] = warn_on_shallow, - config: Configuration | None = None, ) -> ScmVersion | None: """ :param pre_parse: experimental pre_parse action, may change at any time """ - if not config: - config = Configuration(root=root) - - wd = get_working_directory(config) + wd = get_working_directory(config, root) if wd: return _git_parse_inner( config, wd, describe_command=describe_command, pre_parse=pre_parse @@ -301,11 +298,11 @@ def archival_to_version( distance=None if number == 0 else number, node=node, ) - versions = tags_to_versions( - REF_TAG_RE.findall(data.get("ref-names", "")), config=config - ) - if versions: - return meta(versions[0], config=config) + + for ref in REF_TAG_RE.findall(data.get("ref-names", "")): + version = tag_to_version(ref, config) + if version is not None: + return meta(version, config=config) else: node = data.get("node") if node is None: diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index a5d2c02..e9c6c12 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -63,6 +63,7 @@ def _assign_version(dist: setuptools.Distribution, config: Configuration) -> Non if maybe_version is None: _version_missing(config) else: + assert dist.metadata.version is None dist.metadata.version = maybe_version @@ -80,12 +81,16 @@ def version_keyword( assert ( "dist_name" not in value ), "dist_name may not be specified in the setup keyword " - + dist_name: str | None = dist.metadata.name + if dist.metadata.version is not None: + warnings.warn(f"version of {dist_name} already set") + return trace( "version keyword", vars(dist.metadata), ) - dist_name = dist.metadata.name # type: str | None + trace("dist", id(dist), id(dist.metadata)) + if dist_name is None: dist_name = _read_dist_name_from_setup_cfg() version_cls = value.pop("version_cls", None) @@ -116,6 +121,9 @@ def infer_version(dist: setuptools.Distribution) -> None: "finalize hook", vars(dist.metadata), ) + trace("dist", id(dist), id(dist.metadata)) + if dist.metadata.version is not None: + return # metadata already added by hook dist_name = dist.metadata.name if dist_name is None: dist_name = _read_dist_name_from_setup_cfg() diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 11ed5aa..5da3f82 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -100,20 +100,6 @@ def tag_to_version( return version -def tags_to_versions(tags: list[str], config: _config.Configuration) -> list[_VersionT]: - """ - take tags that might be prefixed with a keyword and return only the version part - :param tags: an iterable of tags - :param config: optional configuration object - """ - result: list[_VersionT] = [] - for tag in tags: - parsed = tag_to_version(tag, config=config) - if parsed: - result.append(parsed) - return result - - def _source_epoch_or_utc_now() -> datetime: if "SOURCE_DATE_EPOCH" in os.environ: date_epoch = int(os.environ["SOURCE_DATE_EPOCH"]) diff --git a/testing/test_git.py b/testing/test_git.py index 2dc8b0d..ab2cfed 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -91,7 +91,7 @@ setup(use_scm_version={"search_parent_directories": True}) def test_git_gone(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("PATH", str(wd.cwd / "not-existing")) with pytest.raises(EnvironmentError, match="'git' was not found"): - git.parse(str(wd.cwd), git.DEFAULT_DESCRIBE) + git.parse(str(wd.cwd), Configuration(), git.DEFAULT_DESCRIBE) @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/298") @@ -105,7 +105,7 @@ def test_file_finder_no_history(wd: WorkDir, caplog: pytest.LogCaptureFixture) - @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/281") def test_parse_call_order(wd: WorkDir) -> None: - git.parse(str(wd.cwd), git.DEFAULT_DESCRIBE) + git.parse(str(wd.cwd), Configuration(), git.DEFAULT_DESCRIBE) @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/707") @@ -130,7 +130,7 @@ def test_not_owner(wd: WorkDir) -> None: stdin=subprocess.DEVNULL, check=True, ) - assert git.parse(str(wd.cwd)) + assert git.parse(str(wd.cwd), Configuration()) finally: # Restore the ownership subprocess.run( @@ -148,7 +148,7 @@ def test_not_owner(wd: WorkDir) -> None: def test_version_from_git(wd: WorkDir) -> None: assert wd.version == "0.1.dev0" - parsed = git.parse(str(wd.cwd), git.DEFAULT_DESCRIBE) + parsed = git.parse(str(wd.cwd), Configuration(), git.DEFAULT_DESCRIBE) assert parsed is not None and parsed.branch in ("master", "main") wd.commit_testfile() @@ -226,8 +226,8 @@ def test_git_version_unnormalized_setuptools( the version is not normalized in write_to files, but still normalized by setuptools for the final dist metadata. """ - monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") - + # monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") + monkeypatch.chdir(wd.cwd) wd.write("setup.py", dedent(setup_py_txt)) # do git operations and tag @@ -303,23 +303,23 @@ def shallow_wd(wd: WorkDir, tmp_path: Path) -> Path: def test_git_parse_shallow_warns( shallow_wd: Path, recwarn: pytest.WarningsRecorder ) -> None: - git.parse(str(shallow_wd)) + git.parse(str(shallow_wd), Configuration()) msg = recwarn.pop() assert "is shallow and may cause errors" in str(msg.message) def test_git_parse_shallow_fail(shallow_wd: Path) -> None: with pytest.raises(ValueError, match="git fetch"): - git.parse(str(shallow_wd), pre_parse=git.fail_on_shallow) + git.parse(str(shallow_wd), Configuration(), pre_parse=git.fail_on_shallow) def test_git_shallow_autocorrect( shallow_wd: Path, recwarn: pytest.WarningsRecorder ) -> None: - git.parse(str(shallow_wd), pre_parse=git.fetch_on_shallow) + git.parse(str(shallow_wd), Configuration(), pre_parse=git.fetch_on_shallow) msg = recwarn.pop() assert "git fetch was used to rectify" in str(msg.message) - git.parse(str(shallow_wd), pre_parse=git.fail_on_shallow) + git.parse(str(shallow_wd), Configuration(), pre_parse=git.fail_on_shallow) def test_find_files_stop_at_root_git(wd: WorkDir) -> None: @@ -332,7 +332,7 @@ def test_find_files_stop_at_root_git(wd: WorkDir) -> None: @pytest.mark.issue(128) def test_parse_no_worktree(tmp_path: Path) -> None: - ret = git.parse(str(tmp_path)) + ret = git.parse(str(tmp_path), Configuration(root=str(tmp_path))) assert ret is None @@ -452,7 +452,7 @@ def test_git_getdate(wd: WorkDir) -> None: today = date.today() def parse_date() -> date: - parsed = git.parse(os.fspath(wd.cwd)) + parsed = git.parse(os.fspath(wd.cwd), Configuration()) assert parsed is not None assert parsed.node_date is not None return parsed.node_date diff --git a/testing/test_regressions.py b/testing/test_regressions.py index 6de7141..bc378f5 100644 --- a/testing/test_regressions.py +++ b/testing/test_regressions.py @@ -7,6 +7,7 @@ from pathlib import Path import pytest +from setuptools_scm import Configuration from setuptools_scm import get_version from setuptools_scm.git import parse from setuptools_scm.utils import do @@ -100,5 +101,5 @@ def test_case_mismatch_on_windows_git(tmp_path: Path) -> None: camel_case_path = tmp_path / "CapitalizedDir" camel_case_path.mkdir() do("git init", camel_case_path) - res = parse(str(camel_case_path).lower()) + res = parse(str(camel_case_path).lower(), Configuration()) assert res is not None diff --git a/testing/test_version.py b/testing/test_version.py index d7fefe4..29d58c9 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -15,7 +15,6 @@ from setuptools_scm.version import no_guess_dev_version from setuptools_scm.version import release_branch_semver_version from setuptools_scm.version import ScmVersion from setuptools_scm.version import simplified_semver_version -from setuptools_scm.version import tags_to_versions c = Configuration() @@ -187,12 +186,6 @@ def test_tag_regex1(tag: str, expected: str) -> None: assert result.tag.public == expected -@pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/286") -def test_tags_to_versions() -> None: - versions = tags_to_versions(["1.0", "2.0", "3.0"], config=c) - assert isinstance(versions, list) # enable subscription - - @pytest.mark.issue("https://github.com/pypa/setuptools_scm/issues/471") def test_version_bump_bad() -> None: class YikesVersion: diff --git a/tox.ini b/tox.ini index ae69897..581f62c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py{37,38,39,310,311}-{test,selfcheck},check_readme,check-dist,check-bootstrap +envlist=py{37,38,39,310,311}-{test,selfcheck},check_readme,check-dist [pytest] testpaths=testing @@ -37,16 +37,18 @@ commands= [testenv:check_readme] skip_install=True -setenv = SETUPTOOLS_SCM_PRETEND_VERSION=2.0 deps= check-manifest docutils pygments + setuptools>45 + typing_extensions commands= rst2html.py README.rst {envlogdir}/README.html --strict [] - check-manifest + check-manifest --no-build-isolation [testenv:check_dist] +skip_install = true deps= build twine @@ -54,14 +56,7 @@ commands= python -m build twine check dist/* -[testenv:check-bootstrap] -deps = - setuptools > 45 - packaging>20 -skip_install = true -recreate = true -commands = - python setup.py bdist_wheel + #XXX: envs for hg versions -- cgit v1.2.1 From 3dff72a889b80c879b964dc71fa3572a206bbefb Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 9 Feb 2023 21:40:21 +0100 Subject: prepare next gen package for pypi registration --- nextgen/vcs-versioning/LICENSE.txt | 9 ++++ nextgen/vcs-versioning/README.md | 21 ++++++++ nextgen/vcs-versioning/pyproject.toml | 58 ++++++++++++++++++++++ nextgen/vcs-versioning/tests/__init__.py | 1 + nextgen/vcs-versioning/vcs_versioning/__about__.py | 3 ++ nextgen/vcs-versioning/vcs_versioning/__init__.py | 1 + 6 files changed, 93 insertions(+) create mode 100644 nextgen/vcs-versioning/LICENSE.txt create mode 100644 nextgen/vcs-versioning/README.md create mode 100644 nextgen/vcs-versioning/pyproject.toml create mode 100644 nextgen/vcs-versioning/tests/__init__.py create mode 100644 nextgen/vcs-versioning/vcs_versioning/__about__.py create mode 100644 nextgen/vcs-versioning/vcs_versioning/__init__.py diff --git a/nextgen/vcs-versioning/LICENSE.txt b/nextgen/vcs-versioning/LICENSE.txt new file mode 100644 index 0000000..5b48e3b --- /dev/null +++ b/nextgen/vcs-versioning/LICENSE.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023-present Ronny Pfannschmidt + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/nextgen/vcs-versioning/README.md b/nextgen/vcs-versioning/README.md new file mode 100644 index 0000000..78fe5d7 --- /dev/null +++ b/nextgen/vcs-versioning/README.md @@ -0,0 +1,21 @@ +# vcs-versioning + +[![PyPI - Version](https://img.shields.io/pypi/v/vcs-versioning.svg)](https://pypi.org/project/vcs-versioning) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/vcs-versioning.svg)](https://pypi.org/project/vcs-versioning) + +----- + +**Table of Contents** + +- [Installation](#installation) +- [License](#license) + +## Installation + +```console +pip install vcs-versioning +``` + +## License + +`vcs-versioning` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. diff --git a/nextgen/vcs-versioning/pyproject.toml b/nextgen/vcs-versioning/pyproject.toml new file mode 100644 index 0000000..ae8e901 --- /dev/null +++ b/nextgen/vcs-versioning/pyproject.toml @@ -0,0 +1,58 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "vcs-versioning" +description = 'the blessed package to manage your versions by vcs metadata' +readme = "README.md" +requires-python = ">=3.8" +license = "MIT" +keywords = [] +authors = [ + { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" }, +] +classifiers = [ + "Development Status :: 1 - Planning", + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +dependencies = [] +dynamic = ["version"] + +[project.urls] +Documentation = "https://github.com/unknown/vcs-versioning#readme" +Issues = "https://github.com/unknown/vcs-versioning/issues" +Source = "https://github.com/unknown/vcs-versioning" + +[tool.hatch.version] +path = "vcs_versioning/__about__.py" + +[tool.hatch.envs.default] +dependencies = [ + "pytest", + "pytest-cov", +] +[tool.hatch.envs.default.scripts] +cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=vcs_versioning --cov=tests {args}" +no-cov = "cov --no-cov {args}" + +[[tool.hatch.envs.test.matrix]] +python = ["38", "39", "310", "311"] + +[tool.coverage.run] +branch = true +parallel = true +omit = [ + "vcs_versioning/__about__.py", +] + +[tool.coverage.report] +exclude_lines = [ + "no cov", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", +] diff --git a/nextgen/vcs-versioning/tests/__init__.py b/nextgen/vcs-versioning/tests/__init__.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/nextgen/vcs-versioning/tests/__init__.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/nextgen/vcs-versioning/vcs_versioning/__about__.py b/nextgen/vcs-versioning/vcs_versioning/__about__.py new file mode 100644 index 0000000..eba4921 --- /dev/null +++ b/nextgen/vcs-versioning/vcs_versioning/__about__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +__version__ = "0.0.1" diff --git a/nextgen/vcs-versioning/vcs_versioning/__init__.py b/nextgen/vcs-versioning/vcs_versioning/__init__.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/nextgen/vcs-versioning/vcs_versioning/__init__.py @@ -0,0 +1 @@ +from __future__ import annotations -- cgit v1.2.1 From a5fdfcf7ff83eeeb1e77a932536e865ef774afe8 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 12 Feb 2023 16:51:15 +0100 Subject: pre-commit updates --- .pre-commit-config.yaml | 4 ++-- src/setuptools_scm/__init__.py | 10 +++------- src/setuptools_scm/_cli.py | 1 - src/setuptools_scm/_integration/setuptools.py | 1 - src/setuptools_scm/_modify_version.py | 1 - src/setuptools_scm/_overrides.py | 1 - src/setuptools_scm/_version_cls.py | 1 - src/setuptools_scm/git.py | 1 - src/setuptools_scm/hacks.py | 1 - src/setuptools_scm/hg.py | 4 ---- src/setuptools_scm/utils.py | 2 -- src/setuptools_scm/version.py | 1 - testing/conftest.py | 2 +- testing/test_basic_api.py | 1 - testing/test_cli.py | 2 -- testing/test_git.py | 2 +- testing/test_integration.py | 2 +- testing/test_mercurial.py | 3 +-- testing/test_setuptools_support.py | 1 - testing/test_version.py | 2 -- 20 files changed, 9 insertions(+), 34 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f629400..63fb75b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3.9 repos: - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black args: [--safe, --quiet] @@ -33,7 +33,7 @@ repos: - id: setup-cfg-fmt args: [ --include-version-classifiers ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.991' + rev: 'v1.0.0' hooks: - id: mypy args: [--strict] diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index 91f88ed..d6def65 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -22,8 +22,7 @@ from ._version_cls import _validate_version_cls from ._version_cls import _version_as_tuple from ._version_cls import NonNormalizedVersion from ._version_cls import Version -from .discover import iter_matching_entrypoints -from .version import format_version +from .version import format_version as _format_version if TYPE_CHECKING: from typing import NoReturn @@ -132,7 +131,7 @@ def get_version( """ version_cls = _validate_version_cls(version_cls, normalize) del normalize - if isinstance((tag_regex), str): + if isinstance(tag_regex, str): tag_regex = re.compile(tag_regex) config = Configuration(**locals()) maybe_version = _get_version(config) @@ -146,7 +145,7 @@ def _get_version(config: Configuration) -> str | None: parsed_version = _do_parse(config) if parsed_version is None: return None - version_string = format_version( + version_string = _format_version( parsed_version, version_scheme=config.version_scheme, local_scheme=config.local_scheme, @@ -174,7 +173,4 @@ __all__ = [ "PRETEND_KEY_NAMED", "Version", "NonNormalizedVersion", - # TODO: are the symbols below part of public API ? - "format_version", - "iter_matching_entrypoints", ] diff --git a/src/setuptools_scm/_cli.py b/src/setuptools_scm/_cli.py index 297302b..953c471 100644 --- a/src/setuptools_scm/_cli.py +++ b/src/setuptools_scm/_cli.py @@ -17,7 +17,6 @@ def main(args: list[str] | None = None) -> None: pyproject = opts.config or _find_pyproject(inferred_root) try: - config = Configuration.from_file( pyproject, root=(os.path.abspath(opts.root) if opts.root is not None else None), diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py index 306ff73..21f9591 100644 --- a/src/setuptools_scm/_integration/setuptools.py +++ b/src/setuptools_scm/_integration/setuptools.py @@ -7,7 +7,6 @@ from typing import IO def read_dist_name_from_setup_cfg( input: str | os.PathLike[str] | IO[str] = "setup.cfg", ) -> str | None: - # minimal effort to read dist_name off setup.cfg metadata import configparser diff --git a/src/setuptools_scm/_modify_version.py b/src/setuptools_scm/_modify_version.py index 0be79c2..a364adc 100644 --- a/src/setuptools_scm/_modify_version.py +++ b/src/setuptools_scm/_modify_version.py @@ -46,7 +46,6 @@ def _bump_regex(version: str) -> str: def _format_local_with_time(version: _t.SCMVERSION, time_format: str) -> str: - if version.exact or version.node is None: return version.format_choice( "", "+d{time:{time_format}}", time_format=time_format diff --git a/src/setuptools_scm/_overrides.py b/src/setuptools_scm/_overrides.py index 3dea9ea..9040487 100644 --- a/src/setuptools_scm/_overrides.py +++ b/src/setuptools_scm/_overrides.py @@ -15,7 +15,6 @@ PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}" def read_named_env( *, tool: str = "SETUPTOOLS_SCM", name: str, dist_name: str | None ) -> str | None: - if dist_name is not None: val = os.environ.get(f"{tool}_{name}_FOR_{dist_name.upper()}") if val is not None: diff --git a/src/setuptools_scm/_version_cls.py b/src/setuptools_scm/_version_cls.py index ada1af9..f6e87a8 100644 --- a/src/setuptools_scm/_version_cls.py +++ b/src/setuptools_scm/_version_cls.py @@ -38,7 +38,6 @@ def _version_as_tuple(version_str: str) -> tuple[int | str, ...]: try: parsed_version = Version(version_str) except InvalidVersion: - log = getLogger("setuptools_scm") log.exception("failed to parse version %s", version_str) return (version_str,) diff --git a/src/setuptools_scm/git.py b/src/setuptools_scm/git.py index 3a3a1b9..1859671 100644 --- a/src/setuptools_scm/git.py +++ b/src/setuptools_scm/git.py @@ -266,7 +266,6 @@ def search_parent(dirname: _t.PathT) -> GitWorkdir | None: curpath = os.path.abspath(dirname) while curpath: - try: wd = GitWorkdir.from_potential_worktree(curpath) except Exception: diff --git a/src/setuptools_scm/hacks.py b/src/setuptools_scm/hacks.py index 5c1e34b..1ddcdfa 100644 --- a/src/setuptools_scm/hacks.py +++ b/src/setuptools_scm/hacks.py @@ -16,7 +16,6 @@ _UNKNOWN = "UNKNOWN" def parse_pkginfo(root: _t.PathT, config: Configuration) -> ScmVersion | None: - pkginfo = os.path.join(root, "PKG-INFO") trace("pkginfo", pkginfo) data = data_from_mime(pkginfo) diff --git a/src/setuptools_scm/hg.py b/src/setuptools_scm/hg.py index 2c418e4..a6f5725 100644 --- a/src/setuptools_scm/hg.py +++ b/src/setuptools_scm/hg.py @@ -21,7 +21,6 @@ if TYPE_CHECKING: class HgWorkdir(Workdir): - COMMAND = "hg" @classmethod @@ -33,7 +32,6 @@ class HgWorkdir(Workdir): return cls(root) def get_meta(self, config: Configuration) -> ScmVersion | None: - node: str tags_str: str bookmark: str @@ -122,13 +120,11 @@ class HgWorkdir(Workdir): return tag def get_distance_revs(self, rev1: str, rev2: str = ".") -> int: - revset = f"({rev1}::{rev2})" out = self.hg_log(revset, ".") return len(out) - 1 def check_changes_since_tag(self, tag: str | None) -> bool: - if tag == "0.0" or tag is None: return True diff --git a/src/setuptools_scm/utils.py b/src/setuptools_scm/utils.py index 7c690b8..52aa5e2 100644 --- a/src/setuptools_scm/utils.py +++ b/src/setuptools_scm/utils.py @@ -18,7 +18,6 @@ from typing import NamedTuple from typing import TYPE_CHECKING if TYPE_CHECKING: - from . import _types as _t DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) @@ -174,7 +173,6 @@ def require_command(name: str) -> None: def iter_entry_points( group: str, name: str | None = None ) -> Iterator[_t.EntrypointProtocol]: - from ._entrypoints import iter_entry_points return iter_entry_points(group, name) diff --git a/src/setuptools_scm/version.py b/src/setuptools_scm/version.py index 5da3f82..7c6abd3 100644 --- a/src/setuptools_scm/version.py +++ b/src/setuptools_scm/version.py @@ -186,7 +186,6 @@ def meta( config: _config.Configuration, node_date: date | None = None, ) -> ScmVersion: - parsed_version = _parse_tag(tag, preformatted, config) trace("version", tag, "->", parsed_version) assert parsed_version is not None, "Can't parse version %s" % tag diff --git a/testing/conftest.py b/testing/conftest.py index d29b5dd..3928f75 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -23,7 +23,7 @@ def pytest_report_header() -> list[str]: try: from importlib.metadata import version # type: ignore except ImportError: - from importlib_metadata import version # type: ignore + from importlib_metadata import version res = [] for pkg in VERSION_PKGS: pkg_version = version(pkg) diff --git a/testing/test_basic_api.py b/testing/test_basic_api.py index 6ebf10d..4bd72fa 100644 --- a/testing/test_basic_api.py +++ b/testing/test_basic_api.py @@ -152,7 +152,6 @@ def test_root_relative_to(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> No def test_dump_version(tmp_path: Path) -> None: - dump_version(tmp_path, "1.0", "first.txt") def read(name: str) -> str: diff --git a/testing/test_cli.py b/testing/test_cli.py index 1eb9ead..cc5a0ef 100644 --- a/testing/test_cli.py +++ b/testing/test_cli.py @@ -17,7 +17,6 @@ PYPROJECT_ROOT = '[tool.setuptools_scm]\nroot=".."' def get_output(args: list[str]) -> str: - with redirect_stdout(io.StringIO()) as out: main(args) return out.getvalue() @@ -52,7 +51,6 @@ def test_cli_find_pyproject( print(get_output(["-c", PYPROJECT_TOML])) with exits_with_not_found, warns_absolute_root_override: - get_output(["-c", PYPROJECT_TOML, "--root=.."]) with warns_cli_root_override: diff --git a/testing/test_git.py b/testing/test_git.py index ab2cfed..35ea1ec 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -18,7 +18,6 @@ import pytest from .conftest import DebugMode from .wd_wrapper import WorkDir from setuptools_scm import Configuration -from setuptools_scm import format_version from setuptools_scm import git from setuptools_scm import integration from setuptools_scm import NonNormalizedVersion @@ -26,6 +25,7 @@ from setuptools_scm.file_finder_git import git_find_files from setuptools_scm.git import archival_to_version from setuptools_scm.utils import do from setuptools_scm.utils import has_command +from setuptools_scm.version import format_version pytestmark = pytest.mark.skipif( not has_command("git", warn=False), reason="git executable not found" diff --git a/testing/test_integration.py b/testing/test_integration.py index b110fa3..0ab1548 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -149,7 +149,7 @@ def test_distribution_procides_extras() -> None: try: from importlib.metadata import distribution # type: ignore except ImportError: - from importlib_metadata import distribution # type: ignore + from importlib_metadata import distribution dist = distribution("setuptools_scm") assert sorted(dist.metadata.get_all("Provides-Extra")) == ["test", "toml"] diff --git a/testing/test_mercurial.py b/testing/test_mercurial.py index 28a2242..144e423 100644 --- a/testing/test_mercurial.py +++ b/testing/test_mercurial.py @@ -6,11 +6,11 @@ from pathlib import Path import pytest from setuptools_scm import Configuration -from setuptools_scm import format_version from setuptools_scm import integration from setuptools_scm.hg import archival_to_version from setuptools_scm.hg import parse from setuptools_scm.utils import has_command +from setuptools_scm.version import format_version from testing.wd_wrapper import WorkDir @@ -191,7 +191,6 @@ def test_latest_tag_detection(wd: WorkDir) -> None: @pytest.mark.usefixtures("version_1_0") def test_feature_branch_increments_major(wd: WorkDir) -> None: - wd.commit_testfile() assert wd.get_version(version_scheme="python-simplified-semver").startswith("1.0.1") wd("hg branch feature/fun") diff --git a/testing/test_setuptools_support.py b/testing/test_setuptools_support.py index 212b481..319000b 100644 --- a/testing/test_setuptools_support.py +++ b/testing/test_setuptools_support.py @@ -101,7 +101,6 @@ main() def check(venv: Venv, expected_version: str, **env: str) -> None: - subprocess.check_call( [venv.python, "-c", SCRIPT, expected_version], env=dict(os.environ, **env), diff --git a/testing/test_version.py b/testing/test_version.py index 29d58c9..7c68d42 100644 --- a/testing/test_version.py +++ b/testing/test_version.py @@ -58,7 +58,6 @@ def test_next_semver(version: ScmVersion, expected_next: str) -> None: def test_next_semver_bad_tag() -> None: - version = meta("1.0.0-foo", preformatted=True, config=c) with pytest.raises( ValueError, match=r"1\.0\.0-foo.* can't be parsed as numeric version" @@ -203,7 +202,6 @@ def test_version_bump_bad() -> None: match=".*does not end with a number to bump, " "please correct or use a custom version scheme", ): - guess_next_version(tag_version=meta("2.0.0-alpha.5-PMC", config=config)) -- cgit v1.2.1 From 1e224324604f910d20ef625e18a29b254305417f Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 12 Feb 2023 21:50:17 +0100 Subject: migrate setuptools config to pyproject.toml --- pyproject.toml | 93 +++++++++++++++++++++++++++++++++++++++++++-- setup.cfg | 78 ------------------------------------- testing/test_regressions.py | 22 +++++++++++ 3 files changed, 111 insertions(+), 82 deletions(-) delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 1d11e4c..24dcf32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,92 @@ [build-system] +build-backend = "setuptools.build_meta" requires = [ - "setuptools>=45", - "packaging>=20.0", - "typing_extensions", + "packaging>=20", + "setuptools>=45", + "typing_extensions", ] -build-backend = "setuptools.build_meta" + +[project] +dynamic = ["version"] +name = "setuptools-scm" +description = "the blessed package to manage your versions by scm tags" +readme = "README.rst" +license.file = "LICENSE" +authors = [ + {name="Ronny Pfannschmidt", email="opensource@ronnypfannschmidt.de"} +] +requires-python = ">=3.7" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Version Control", + "Topic :: System :: Software Distribution", + "Topic :: Utilities", +] +dependencies = [ + 'importlib-metadata; python_version < "3.8"', + "packaging>=20", + "setuptools", + 'tomli>=1; python_version < "3.11"', + "typing-extensions", +] +[project.urls] +repository = "https://github.com/pypa/setuptools_scm/" + +[project.entry-points."distutils.setup_keywords"] +use_scm_version = "setuptools_scm.integration:version_keyword" + +[project.entry-points."setuptools.file_finders"] +setuptools_scm = "setuptools_scm.integration:find_files" + +[project.entry-points."setuptools.finalize_distribution_options"] +setuptools_scm = "setuptools_scm.integration:infer_version" + +[project.entry-points."setuptools_scm.files_command"] +".git" = "setuptools_scm.file_finder_git:git_find_files" +".hg" = "setuptools_scm.file_finder_hg:hg_find_files" + +[project.entry-points."setuptools_scm.files_command_fallback"] +".git_archival.txt" = "setuptools_scm.file_finder_git:git_archive_find_files" +".hg_archival.txt" = "setuptools_scm.file_finder_hg:hg_archive_find_files" + +[project.entry-points."setuptools_scm.local_scheme"] +dirty-tag = "setuptools_scm.version:get_local_dirty_tag" +no-local-version = "setuptools_scm.version:get_no_local_node" +node-and-date = "setuptools_scm.version:get_local_node_and_date" +node-and-timestamp = "setuptools_scm.version:get_local_node_and_timestamp" + +[project.entry-points."setuptools_scm.parse_scm"] +".git" = "setuptools_scm.git:parse" +".hg" = "setuptools_scm.hg:parse" + +[project.entry-points."setuptools_scm.parse_scm_fallback"] +".git_archival.txt" = "setuptools_scm.git:parse_archival" +".hg_archival.txt" = "setuptools_scm.hg:parse_archival" +PKG-INFO = "setuptools_scm.hacks:parse_pkginfo" +pip-egg-info = "setuptools_scm.hacks:parse_pip_egg_info" +"pyproject.toml" = "setuptools_scm.hacks:fallback_version" +"setup.py" = "setuptools_scm.hacks:fallback_version" + +[project.entry-points."setuptools_scm.version_scheme"] +"calver-by-date" = "setuptools_scm.version:calver_by_date" +"guess-next-dev" = "setuptools_scm.version:guess_next_dev_version" +"no-guess-dev" = "setuptools_scm.version:no_guess_dev_version" +"post-release" = "setuptools_scm.version:postrelease_version" +"python-simplified-semver" = "setuptools_scm.version:simplified_semver_version" +"release-branch-semver" = "setuptools_scm.version:release_branch_semver_version" + + + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index b69fc6f..0000000 --- a/setup.cfg +++ /dev/null @@ -1,78 +0,0 @@ -[metadata] -name = setuptools_scm -description = the blessed package to manage your versions by scm tags -long_description = file: README.rst -long_description_content_type = text/x-rst -url = https://github.com/pypa/setuptools_scm/ -author = Ronny Pfannschmidt -author_email = opensource@ronnypfannschmidt.de -license = MIT -license_files = LICENSE -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - License :: OSI Approved :: MIT License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Software Development :: Libraries - Topic :: Software Development :: Version Control - Topic :: System :: Software Distribution - Topic :: Utilities - -[options] -packages = find: -install_requires = - packaging>=20.0 - setuptools - typing-extensions - importlib-metadata;python_version < '3.8' - tomli>=1.0.0;python_version < '3.11' # keep in sync -python_requires = >=3.7 -package_dir = - =src -zip_safe = true - -[options.packages.find] -where = src - -[options.entry_points] -distutils.setup_keywords = - use_scm_version = setuptools_scm.integration:version_keyword -setuptools.file_finders = - setuptools_scm = setuptools_scm.integration:find_files -setuptools.finalize_distribution_options = - setuptools_scm = setuptools_scm.integration:infer_version -setuptools_scm.files_command = - .hg = setuptools_scm.file_finder_hg:hg_find_files - .git = setuptools_scm.file_finder_git:git_find_files -setuptools_scm.files_command_fallback = - .hg_archival.txt = setuptools_scm.file_finder_hg:hg_archive_find_files - .git_archival.txt = setuptools_scm.file_finder_git:git_archive_find_files -setuptools_scm.local_scheme = - node-and-date = setuptools_scm.version:get_local_node_and_date - node-and-timestamp = setuptools_scm.version:get_local_node_and_timestamp - dirty-tag = setuptools_scm.version:get_local_dirty_tag - no-local-version = setuptools_scm.version:get_no_local_node -setuptools_scm.parse_scm = - .hg = setuptools_scm.hg:parse - .git = setuptools_scm.git:parse -setuptools_scm.parse_scm_fallback = - .hg_archival.txt = setuptools_scm.hg:parse_archival - .git_archival.txt = setuptools_scm.git:parse_archival - PKG-INFO = setuptools_scm.hacks:parse_pkginfo - pip-egg-info = setuptools_scm.hacks:parse_pip_egg_info - setup.py = setuptools_scm.hacks:fallback_version - pyproject.toml = setuptools_scm.hacks:fallback_version -setuptools_scm.version_scheme = - guess-next-dev = setuptools_scm.version:guess_next_dev_version - post-release = setuptools_scm.version:postrelease_version - python-simplified-semver = setuptools_scm.version:simplified_semver_version - release-branch-semver = setuptools_scm.version:release_branch_semver_version - no-guess-dev = setuptools_scm.version:no_guess_dev_version - calver-by-date = setuptools_scm.version:calver_by_date diff --git a/testing/test_regressions.py b/testing/test_regressions.py index bc378f5..6cbabcc 100644 --- a/testing/test_regressions.py +++ b/testing/test_regressions.py @@ -1,8 +1,16 @@ from __future__ import annotations import os +import pprint import subprocess import sys +from typing import Callable + +if sys.version_info >= (3, 8): + distribution: Callable[[str], EntryPoint] + from importlib.metadata import distribution, EntryPoint +else: + from importlib_metadata import distribution, EntryPoint from pathlib import Path import pytest @@ -103,3 +111,17 @@ def test_case_mismatch_on_windows_git(tmp_path: Path) -> None: do("git init", camel_case_path) res = parse(str(camel_case_path).lower(), Configuration()) assert res is not None + + +def test_entrypoints_load() -> None: + d = distribution("setuptools-scm") # type: ignore [no-untyped-call] + + eps = d.entry_points + failed: list[tuple[EntryPoint, Exception]] = [] + for ep in eps: + try: + ep.load() + except Exception as e: + failed.append((ep, e)) + if failed: + pytest.fail(pprint.pformat(failed)) -- cgit v1.2.1 From b4df9efb65854616a1ed79486340b4920f4fddaa Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 12 Feb 2023 22:03:28 +0100 Subject: add pyproject-fmt --- .pre-commit-config.yaml | 10 +++++----- nextgen/vcs-versioning/pyproject.toml | 20 +++++++++++++------- pyproject.toml | 4 +++- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 63fb75b..ced36e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,11 +27,11 @@ repos: hooks: - id: pyupgrade args: [--py37-plus] -- repo: https://github.com/asottile/setup-cfg-fmt - rev: v2.2.0 - hooks: - - id: setup-cfg-fmt - args: [ --include-version-classifiers ] +- repo: https://github.com/tox-dev/pyproject-fmt + rev: "0.8.0" + hooks: + - id: pyproject-fmt + - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v1.0.0' hooks: diff --git a/nextgen/vcs-versioning/pyproject.toml b/nextgen/vcs-versioning/pyproject.toml index ae8e901..c7e5097 100644 --- a/nextgen/vcs-versioning/pyproject.toml +++ b/nextgen/vcs-versioning/pyproject.toml @@ -1,17 +1,20 @@ [build-system] -requires = ["hatchling"] build-backend = "hatchling.build" +requires = [ + "hatchling", +] [project] name = "vcs-versioning" -description = 'the blessed package to manage your versions by vcs metadata' +description = "the blessed package to manage your versions by vcs metadata" readme = "README.md" -requires-python = ">=3.8" +keywords = [ +] license = "MIT" -keywords = [] authors = [ { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" }, ] +requires-python = ">=3.8" classifiers = [ "Development Status :: 1 - Planning", "Programming Language :: Python", @@ -20,14 +23,17 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ] -dependencies = [] -dynamic = ["version"] - +dynamic = [ + "version", +] +dependencies = [ +] [project.urls] Documentation = "https://github.com/unknown/vcs-versioning#readme" Issues = "https://github.com/unknown/vcs-versioning/issues" Source = "https://github.com/unknown/vcs-versioning" + [tool.hatch.version] path = "vcs_versioning/__about__.py" diff --git a/pyproject.toml b/pyproject.toml index 24dcf32..7f1219f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ requires = [ ] [project] -dynamic = ["version"] name = "setuptools-scm" description = "the blessed package to manage your versions by scm tags" readme = "README.rst" @@ -33,6 +32,9 @@ classifiers = [ "Topic :: System :: Software Distribution", "Topic :: Utilities", ] +dynamic = [ + "version", +] dependencies = [ 'importlib-metadata; python_version < "3.8"', "packaging>=20", -- cgit v1.2.1 From 933e33898dbd9344b0860e463d752a15eee4e642 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 12 Feb 2023 22:41:18 +0100 Subject: drop self check action for legacy setuptools --- .github/workflows/python-tests.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 2753333..af4126b 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -85,30 +85,6 @@ jobs: architecture: x64 - run: pip install -e .[toml,test] pytest virtualenv - run: pytest --test-legacy testing/test_setuptools_support.py || true # ignore fail flaky on ci - check_selfinstall: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python_version: [ '3.7', '3.9', 'pypy-3.8' ] - installer: ["pip install"] - name: check self install - Python ${{ matrix.python_version }} via ${{ matrix.installer }} - steps: - - uses: actions/checkout@v3 - - name: Setup python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python_version }} - architecture: x64 - # self install testing needs some clarity - # so its being executed without any other tools running - # setuptools smaller 52 is needed to do easy_install - - run: pip install -U "setuptools<52" tomli packaging typing_extensions importlib_metadata - - run: python setup.py egg_info - - run: python setup.py sdist - - run: ${{ matrix.installer }} dist/* - - run: python testing/check_self_install.py - dist: runs-on: ubuntu-latest -- cgit v1.2.1 From d78d9ccf062ad114e727718edd7a0d386bed3f85 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 12 Feb 2023 23:35:24 +0100 Subject: drop testing for old setuptools versions --- testing/test_setuptools_support.py | 201 ------------------------------------- 1 file changed, 201 deletions(-) delete mode 100644 testing/test_setuptools_support.py diff --git a/testing/test_setuptools_support.py b/testing/test_setuptools_support.py deleted file mode 100644 index 319000b..0000000 --- a/testing/test_setuptools_support.py +++ /dev/null @@ -1,201 +0,0 @@ -""" -integration tests that check setuptools version support -""" -from __future__ import annotations - -import os -import pathlib -import subprocess -import sys -from typing import Any -from typing import Callable - -import _pytest.config -import pytest - - -def cli_run(*k: Any, **kw: Any) -> None: - """this defers the virtualenv import - it helps to avoid warnings from the furthermore imported setuptools - """ - global cli_run - from virtualenv.run import cli_run # type: ignore - - cli_run(*k, **kw) - - -pytestmark = pytest.mark.filterwarnings( - r"ignore:.*tool\.setuptools_scm.*", r"always:.*setup.py install is deprecated.*" -) - - -ROOT = pathlib.Path(__file__).parent.parent - - -class Venv: - location: pathlib.Path - - def __init__(self, location: pathlib.Path): - self.location = location - - @property - def python(self) -> pathlib.Path: - return self.location / "bin/python" - - -class VenvMaker: - def __init__(self, base: pathlib.Path): - self.base = base - - def __repr__(self) -> str: - return f"" - - def get_venv( - self, python: str, pip: str, setuptools: str, prefix: str = "scm" - ) -> Venv: - name = f"{prefix}-py={python}-pip={pip}-setuptools={setuptools}" - path = self.base / name - if not path.is_dir(): - cli_run( - [ - str(path), - "--python", - python, - "--pip", - pip, - "--setuptools", - setuptools, - ], - setup_logging=False, - ) - venv = Venv(path) - subprocess.run([venv.python, "-m", "pip", "install", "-e", str(ROOT)]) - # fixup pip - subprocess.check_call([venv.python, "-m", "pip", "install", f"pip=={pip}"]) - subprocess.check_call( - [venv.python, "-m", "pip", "install", f"setuptools~={setuptools}"] - ) - return venv - - -@pytest.fixture -def venv_maker(pytestconfig: _pytest.config.Config) -> VenvMaker: - if not pytestconfig.getoption("--test-legacy"): - pytest.skip( - "testing on legacy setuptools disabled, pass --test-legacy to run them" - ) - assert pytestconfig.cache is not None - path = pytestconfig.cache.mkdir("setuptools_scm_venvs") - return VenvMaker(path) - - -SCRIPT = """ -from __future__ import print_function -import sys -import setuptools -print(setuptools.__version__, 'expected', sys.argv[1]) -import setuptools_scm.version -from setuptools_scm.__main__ import main -main() -""" - - -def check(venv: Venv, expected_version: str, **env: str) -> None: - subprocess.check_call( - [venv.python, "-c", SCRIPT, expected_version], - env=dict(os.environ, **env), - ) - - -@pytest.mark.skipif( - sys.version_info[:2] >= (3, 10), reason="old setuptools won't work on python 3.10" -) -def test_distlib_setuptools_works(venv_maker: VenvMaker) -> None: - venv = venv_maker.get_venv(setuptools="45.0.0", pip="9.0", python="3.6") - subprocess.run([venv.python, "-m", "pip", "install", "-e", str(ROOT)]) - - check(venv, "45.0.0") - - -SETUP_PY_NAME = """ -from setuptools import setup -setup(name='setuptools_scm_test_package') -""" - -SETUP_PY_KEYWORD = """ -from setuptools import setup -setup(use_scm_version={"write_to": "pkg_version.py"}) -""" - -PYPROJECT_TOML_WITH_KEY = """ -[build-system] -# Minimum requirements for the build system to execute. -requires = ["setuptools>45"] # PEP 508 specifications. -[tool.setuptools_scm] -write_to = "pkg_version.py" -""" - -SETUP_CFG_NAME = """ -[metadata] -name = setuptools_scm_test_package -""" - - -def prepare_expecting_pyproject_support(pkg: pathlib.Path) -> None: - pkg.mkdir() - pkg.joinpath("setup.py").write_text(SETUP_PY_NAME) - pkg.joinpath("pyproject.toml").write_text(PYPROJECT_TOML_WITH_KEY) - pkg.joinpath("PKG-INFO").write_text("Version: 1.0.0") - - -def prepare_setup_py_config(pkg: pathlib.Path) -> None: - pkg.mkdir() - pkg.joinpath("setup.py").write_text(SETUP_PY_KEYWORD) - pkg.joinpath("setup.cfg").write_text(SETUP_CFG_NAME) - - pkg.joinpath("PKG-INFO").write_text("Version: 1.0.0") - - -@pytest.mark.skipif( - sys.version_info[:2] >= (3, 10), reason="old setuptools won't work on python 3.10" -) -@pytest.mark.parametrize("setuptools", [f"{v}.0" for v in range(31, 45)]) -@pytest.mark.parametrize( - "project_create", - [ - pytest.param( - prepare_expecting_pyproject_support, - marks=pytest.mark.xfail(reason="pyproject requires setuptools > 42"), - ), - prepare_setup_py_config, - ], -) -def test_on_old_setuptools( - venv_maker: VenvMaker, - tmp_path: pathlib.Path, - setuptools: str, - project_create: Callable[[pathlib.Path], None], -) -> None: - pkg = tmp_path.joinpath("pkg") - project_create(pkg) - venv = venv_maker.get_venv(setuptools=setuptools, pip="9.0", python="3.6") - - # monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG", raising=False) - - def run_and_output(cmd: list[str | pathlib.Path]) -> bytes: - res = subprocess.run(cmd, cwd=str(pkg), stdout=subprocess.PIPE) - if not res.returncode: - return res.stdout.strip() - else: - print(res.stdout) - pytest.fail(str(cmd), pytrace=False) - - version = run_and_output([venv.python, "setup.py", "--version"]) - name = run_and_output([venv.python, "setup.py", "--name"]) - assert (name, version) == (b"setuptools_scm_test_package", b"1.0.0") - - # monkeypatch.setenv( - # "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_setuptools_scm_test_package", "2.0,0") - - # version_pretend = run_and_output([venv.python, "setup.py", "--version"]) - # assert version_pretend == b"2.0.0" -- cgit v1.2.1 From ee48e95a5b40a421e3745f857cf88b255e5c23fc Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 13 Feb 2023 10:12:09 +0100 Subject: set minimal setuptools version higher for build --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7f1219f..93a1e44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "setuptools.build_meta" requires = [ "packaging>=20", - "setuptools>=45", + "setuptools>=55", "typing_extensions", ] -- cgit v1.2.1 From 850542e2c8ddabc601e4611dc97f238a9930c132 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 13 Feb 2023 10:12:30 +0100 Subject: changelog --- CHANGELOG.rst | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3c731cc..456c281 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,29 @@ +v8.0.0 +====== + + +breaking +-------- +* remove legacy version parser api - config arg always required +* turn Configuration into a dataclass +* require confiuration to always pass into helpers + +features +-------- + +* git: expect main as possible default branch +* drop version_from_scm helper +* trim down exposed public api +* no longer self-call twice in setuptools +* chores + + * migrate own metadata to pyproject.toml + * consolidate version schemes + * stricter tag typing + * pre-compiled regex + * move helpers to private modules + + v7.1.0 ====== @@ -45,7 +71,7 @@ v7.0.0 * drop python 3.6 support * include git archival support -* fix #707: support git version detection even when git protects against mistmatched owners +* fix #707: support git version detection even when git protects against mismatched owners (common with misconfigured containers, thanks @chrisburr ) v6.4.3 @@ -410,7 +436,7 @@ v1.16.0 * avoid shlex.split on windows * fix #218 - better handling of mercurial edge-cases with tag commits being considered as the tagged commit -* fix #223 - remove the dependency on the interal SetupttoolsVersion +* fix #223 - remove the dependency on the internal ``SetuptoolsVersion`` as it was removed after long-standing deprecation v1.15.7 @@ -612,20 +638,20 @@ v1.5.0 * moved setuptools integration related code to own file * support storing version strings into a module/text file - using the :code:`write_to` coniguration parameter + using the :code:`write_to` configuration parameter v1.4.0 ====== * proper handling for sdist * fix file-finder failure from windows -* resuffle docs +* reshuffle docs v1.3.0 ====== * support setuptools easy_install egg creation details - by hardwireing the version in the sdist + by hardwire-ing the version in the sdist v1.2.0 ====== -- cgit v1.2.1