summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer Chang <schang412@gmail.com>2023-02-13 09:39:00 -0800
committerSpencer Chang <schang412@gmail.com>2023-02-13 09:39:00 -0800
commit1ba446f053d6fd9197c96b16ac1c18f4b9ecc954 (patch)
tree3107282d0349e6eb0c1ebb5963f4f50ae0367407
parent68592c34dc8026b401718754e7e0fc529cb14415 (diff)
parentf62576831ef12647fbc61e4fd40ac3c0d8399c04 (diff)
downloadsetuptools-scm-1ba446f053d6fd9197c96b16ac1c18f4b9ecc954.tar.gz
Merge branch 'main' into feature/custom_version_scheme_outside_setuppy
-rw-r--r--.github/workflows/python-tests.yml24
-rw-r--r--.pre-commit-config.yaml14
-rw-r--r--CHANGELOG.rst36
-rw-r--r--MANIFEST.in3
-rw-r--r--mypy.ini4
-rw-r--r--nextgen/vcs-versioning/LICENSE.txt9
-rw-r--r--nextgen/vcs-versioning/README.md21
-rw-r--r--nextgen/vcs-versioning/pyproject.toml64
-rw-r--r--nextgen/vcs-versioning/tests/__init__.py1
-rw-r--r--nextgen/vcs-versioning/vcs_versioning/__about__.py3
-rw-r--r--nextgen/vcs-versioning/vcs_versioning/__init__.py1
-rw-r--r--pyproject.toml95
-rw-r--r--setup.cfg78
-rw-r--r--setup.py2
-rw-r--r--src/setuptools_scm/__init__.py75
-rw-r--r--src/setuptools_scm/_cli.py3
-rw-r--r--src/setuptools_scm/_config.py132
-rw-r--r--src/setuptools_scm/_entrypoints.py112
-rw-r--r--src/setuptools_scm/_integration/pyproject_reading.py11
-rw-r--r--src/setuptools_scm/_integration/setuptools.py1
-rw-r--r--src/setuptools_scm/_modify_version.py61
-rw-r--r--src/setuptools_scm/_overrides.py46
-rw-r--r--src/setuptools_scm/_types.py18
-rw-r--r--src/setuptools_scm/_version_cls.py38
-rw-r--r--src/setuptools_scm/config.py216
-rw-r--r--src/setuptools_scm/discover.py6
-rw-r--r--src/setuptools_scm/file_finder.py10
-rw-r--r--src/setuptools_scm/file_finder_git.py5
-rw-r--r--src/setuptools_scm/file_finder_hg.py5
-rw-r--r--src/setuptools_scm/git.py42
-rw-r--r--src/setuptools_scm/hacks.py11
-rw-r--r--src/setuptools_scm/hg.py19
-rw-r--r--src/setuptools_scm/integration.py20
-rw-r--r--src/setuptools_scm/utils.py2
-rw-r--r--src/setuptools_scm/version.py265
-rw-r--r--testing/conftest.py2
-rw-r--r--testing/test_basic_api.py8
-rw-r--r--testing/test_cli.py2
-rw-r--r--testing/test_config.py25
-rw-r--r--testing/test_functions.py4
-rw-r--r--testing/test_git.py28
-rw-r--r--testing/test_integration.py2
-rw-r--r--testing/test_mercurial.py11
-rw-r--r--testing/test_regressions.py25
-rw-r--r--testing/test_setuptools_support.py202
-rw-r--r--testing/test_version.py18
-rw-r--r--tox.ini17
47 files changed, 799 insertions, 998 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
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f629400..ced36e7 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]
@@ -27,13 +27,13 @@ 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: 'v0.991'
+ rev: 'v1.0.0'
hooks:
- id: mypy
args: [--strict]
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
======
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/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
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..c7e5097
--- /dev/null
+++ b/nextgen/vcs-versioning/pyproject.toml
@@ -0,0 +1,64 @@
+[build-system]
+build-backend = "hatchling.build"
+requires = [
+ "hatchling",
+]
+
+[project]
+name = "vcs-versioning"
+description = "the blessed package to manage your versions by vcs metadata"
+readme = "README.md"
+keywords = [
+]
+license = "MIT"
+authors = [
+ { name = "Ronny Pfannschmidt", email = "opensource@ronnypfannschmidt.de" },
+]
+requires-python = ">=3.8"
+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",
+]
+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"
+
+[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
diff --git a/pyproject.toml b/pyproject.toml
index 1d11e4c..93a1e44 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,94 @@
[build-system]
+build-backend = "setuptools.build_meta"
requires = [
- "setuptools>=45",
- "packaging>=20.0",
- "typing_extensions",
+ "packaging>=20",
+ "setuptools>=55",
+ "typing_extensions",
]
-build-backend = "setuptools.build_meta"
+
+[project]
+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",
+]
+dynamic = [
+ "version",
+]
+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 795bb21..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_file = 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/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 aeb4ab6..d6def65 100644
--- a/src/setuptools_scm/__init__.py
+++ b/src/setuptools_scm/__init__.py
@@ -5,33 +5,27 @@
from __future__ import annotations
import os
-import warnings
+import re
from typing import Any
-from typing import Callable
+from typing import Pattern
from typing import TYPE_CHECKING
-from ._entrypoints import _call_entrypoint_fn
+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
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
-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 .utils import function_has_arg
-from .utils import trace
-from .version import format_version
-from .version import meta
-from .version import ScmVersion
+from .version import format_version as _format_version
if TYPE_CHECKING:
from typing import NoReturn
-
from . import _types as _t
TEMPLATES = {
@@ -45,16 +39,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,
@@ -65,7 +49,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(
@@ -78,30 +64,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)
+ 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"
)
- 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:
@@ -119,17 +107,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,
@@ -141,9 +129,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
@@ -153,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,
@@ -173,7 +165,6 @@ def _get_version(config: Configuration) -> str | None:
__all__ = [
"get_version",
"dump_version",
- "version_from_scm",
"Configuration",
"DEFAULT_VERSION_SCHEME",
"DEFAULT_LOCAL_SCHEME",
@@ -182,10 +173,4 @@ __all__ = [
"PRETEND_KEY_NAMED",
"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/_cli.py b/src/setuptools_scm/_cli.py
index 8e01f24..953c471 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
@@ -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/_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<version>[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 2efb9f8..4f4ba8b 100644
--- a/src/setuptools_scm/_entrypoints.py
+++ b/src/setuptools_scm/_entrypoints.py
@@ -2,16 +2,17 @@ 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 .utils import function_has_arg
+from . import version
from .utils import trace
-from .version import ScmVersion
if TYPE_CHECKING:
- from .config import Configuration
+ from ._config import Configuration
from typing_extensions import Protocol
from . import _types as _t
else:
@@ -21,37 +22,9 @@ else:
pass
-class MaybeConfigFunction(Protocol):
- __name__: str
-
- @overload
- def __call__(self, root: _t.PathT, config: Configuration) -> ScmVersion | None:
- pass
-
- @overload
- def __call__(self, root: _t.PathT) -> ScmVersion | None:
- pass
-
-
-def _call_entrypoint_fn(
- root: _t.PathT, config: Configuration, fn: MaybeConfigFunction
-) -> 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
-) -> ScmVersion | None:
+) -> version.ScmVersion | None:
if fallback:
entrypoint = "setuptools_scm.parse_scm_fallback"
root = config.fallback_root
@@ -63,10 +36,11 @@ 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())
+ fn = ep.load()
+ maybe_version: version.ScmVersion | None = fn(root, config=config)
trace(ep, version)
- if version:
- return version
+ if maybe_version is not None:
+ return maybe_version
return None
@@ -97,3 +71,71 @@ 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
+
+
+def _get_from_object_reference_str(path: str) -> Any | None:
+ try:
+ from importlib.metadata import EntryPoint
+ except ImportError:
+ from importlib_metadata import EntryPoint
+ try:
+ return EntryPoint(path, path, None).load()
+ except (AttributeError, ModuleNotFoundError):
+ 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)
+ or _get_from_object_reference_str(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/_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/_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
new file mode 100644
index 0000000..a364adc
--- /dev/null
+++ b/src/setuptools_scm/_modify_version.py
@@ -0,0 +1,61 @@
+from __future__ import annotations
+
+import re
+
+from . import _types as _t
+
+
+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: _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
+ )
+ else:
+ return version.format_choice(
+ "+{node}", "+{node}.d{time:{time_format}}", time_format=time_format
+ )
+
+
+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/_overrides.py b/src/setuptools_scm/_overrides.py
index f18b82c..9040487 100644
--- a/src/setuptools_scm/_overrides.py
+++ b/src/setuptools_scm/_overrides.py
@@ -1,38 +1,54 @@
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/_types.py b/src/setuptools_scm/_types.py
index 6c6bdf8..5c2cd33 100644
--- a/src/setuptools_scm/_types.py
+++ b/src/setuptools_scm/_types.py
@@ -1,24 +1,26 @@
from __future__ import annotations
+import os
from typing import Any
from typing import Callable
from typing import List
-from typing import TYPE_CHECKING
+from typing import Tuple
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 . import version
-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]
+SCMVERSION: TypeAlias = "version.ScmVersion"
class EntrypointProtocol(Protocol):
diff --git a/src/setuptools_scm/_version_cls.py b/src/setuptools_scm/_version_cls.py
index 39e66b2..f6e87a8 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
@@ -35,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,)
@@ -46,3 +48,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
deleted file mode 100644
index 3bf250a..0000000
--- a/src/setuptools_scm/config.py
+++ /dev/null
@@ -1,216 +0,0 @@
-""" configuration """
-from __future__ import annotations
-
-import os
-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 .utils import trace
-
-
-if TYPE_CHECKING:
- from . import _types as _t
- from setuptools_scm.version import ScmVersion
-
-DEFAULT_TAG_REGEX = r"^(?:[\w-]+-)?(?P<version>[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)
-
- 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)
-
-
-_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"""
-
- 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: (
- str | Callable[[ScmVersion], str | None]
- ) = DEFAULT_VERSION_SCHEME,
- local_scheme: (str | Callable[[ScmVersion], str | None]) = 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)
-
- @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)
-
- @classmethod
- def from_file(
- cls,
- name: 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)
-
- return cls(relative_to=name, **args)
diff --git a/src/setuptools_scm/discover.py b/src/setuptools_scm/discover.py
index f7843ee..58384aa 100644
--- a/src/setuptools_scm/discover.py
+++ b/src/setuptools_scm/discover.py
@@ -3,11 +3,9 @@ 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 .config import Configuration
+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..27196c0 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
@@ -78,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/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 16ca378..1859671 100644
--- a/src/setuptools_scm/git.py
+++ b/src/setuptools_scm/git.py
@@ -11,7 +11,8 @@ from os.path import samefile
from typing import Callable
from typing import TYPE_CHECKING
-from .config import Configuration
+from . import _types as _t
+from . import Configuration
from .scm_workdir import Workdir
from .utils import _CmdResult
from .utils import data_from_mime
@@ -20,12 +21,11 @@ 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 _types as _t
+ from . import hg_git
- from setuptools_scm.hg_git import GitWorkdirHgClient
REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b")
DESCRIBE_UNSUPPORTED = "%(describe"
@@ -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
@@ -187,8 +184,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:
@@ -269,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:
@@ -286,7 +282,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)
@@ -301,9 +297,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", "")))
- 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:
@@ -315,9 +313,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..1ddcdfa 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
@@ -15,10 +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)
data = data_from_mime(pkginfo)
@@ -29,9 +26,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..a6f5725 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
@@ -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
@@ -69,7 +67,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)
@@ -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
@@ -143,10 +139,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 +162,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 +180,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/integration.py b/src/setuptools_scm/integration.py
index 2134ff1..e9c6c12 100644
--- a/src/setuptools_scm/integration.py
+++ b/src/setuptools_scm/integration.py
@@ -11,11 +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 .config import Configuration
+from ._version_cls import _validate_version_cls
from .utils import do
from .utils import trace
@@ -62,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
@@ -79,15 +81,22 @@ 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()
- 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)
@@ -112,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/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 d4073f7..7c6abd3 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
@@ -8,22 +9,25 @@ 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 List
from typing import Match
-from typing import overload
-from typing import Tuple
from typing import TYPE_CHECKING
+from . import _entrypoints
+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
from . import _types as _t
-from ._version_cls import Version as PkgVersion
-from .config import Configuration
-from .config import _VersionT
+
+from ._version_cls import Version as PkgVersion, _VersionT
+from . import _version_cls as _v
+from . import _config
from .utils import trace
SEMVER_MINOR = 2
@@ -32,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)
@@ -68,17 +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
- :param config: optional configuration object
"""
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")
@@ -94,68 +94,37 @@ 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]:
- """
- 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"])
+ 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: _v.Version | _v.NonNormalizedVersion | str
+ config: _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:
@@ -194,11 +163,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
@@ -208,21 +177,15 @@ def _parse_tag(
def meta(
tag: str | _VersionT,
+ *,
distance: int | None = None,
dirty: bool = False,
node: str | None = None,
preformatted: bool = False,
branch: str | None = None,
- config: Configuration | None = None,
+ config: _config.Configuration,
node_date: date | None = None,
- **kw: Any,
) -> 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
@@ -235,7 +198,6 @@ def meta(
branch=branch,
config=config,
node_date=node_date,
- **kw,
)
@@ -244,51 +206,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}")
@@ -368,15 +285,13 @@ def no_guess_dev_version(version: ScmVersion) -> str:
return version.format_next_version(_dont_guess_next_version)
+_DATE_REGEX = re.compile(
+ r"^(?P<date>(?P<year>\d{2}|\d{4})(?:\.\d{1,2}){2})(?:\.(?P<patch>\d*))?$"
+)
+
+
def date_ver_match(ver: str) -> Match[str] | None:
- match = re.match(
- (
- r"^(?P<date>(?P<year>\d{2}|\d{4})(?:\.\d{1,2}){2})"
- r"(?:\.(?P<patch>\d*)){0,1}?$"
- ),
- ver,
- )
- return match
+ return _DATE_REGEX.match(ver)
def guess_next_date_ver(
@@ -450,18 +365,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")
@@ -485,90 +388,18 @@ 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 _get_from_object_reference_str(path: str) -> Any | None:
- try:
- from importlib.metadata import EntryPoint
- except ImportError:
- from importlib_metadata import EntryPoint
- try:
- return EntryPoint(path, path, None).load()
- except (AttributeError, ModuleNotFoundError):
- return None
-
-
-def _iter_version_schemes(
- entrypoint: str,
- scheme_value: str
- | list[str]
- | tuple[str, ...]
- | Callable[[ScmVersion], str]
- | None,
- _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',
- _get_ep(entrypoint, scheme_value)
- or _get_from_object_reference_str(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)
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/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 ba11a23..4bd72fa 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
@@ -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:
@@ -157,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_config.py b/testing/test_config.py
index 97bb36e..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(
@@ -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
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_git.py b/testing/test_git.py
index 412ce75..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"
@@ -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,8 +148,8 @@ 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)
- assert parsed is not None and parsed.branch == "master"
+ parsed = git.parse(str(wd.cwd), Configuration(), git.DEFAULT_DESCRIBE)
+ assert parsed is not None and parsed.branch in ("master", "main")
wd.commit_testfile()
assert wd.version.startswith("0.1.dev1+g")
@@ -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_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 5e0bc02..144e423 100644
--- a/testing/test_mercurial.py
+++ b/testing/test_mercurial.py
@@ -5,12 +5,12 @@ from pathlib import Path
import pytest
-from setuptools_scm import format_version
+from setuptools_scm import Configuration
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
+from setuptools_scm.version import format_version
from testing.wd_wrapper import WorkDir
@@ -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
@@ -189,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_regressions.py b/testing/test_regressions.py
index 6de7141..6cbabcc 100644
--- a/testing/test_regressions.py
+++ b/testing/test_regressions.py
@@ -1,12 +1,21 @@
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
+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 +109,19 @@ 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
+
+
+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))
diff --git a/testing/test_setuptools_support.py b/testing/test_setuptools_support.py
deleted file mode 100644
index 212b481..0000000
--- a/testing/test_setuptools_support.py
+++ /dev/null
@@ -1,202 +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"<VenvMaker base={self.base}>"
-
- 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"
diff --git a/testing/test_version.py b/testing/test_version.py
index 0a07095..7c68d42 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
@@ -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()
@@ -59,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"
@@ -183,16 +181,10 @@ 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
-@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:
@@ -204,13 +196,12 @@ 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, "
"please correct or use a custom version scheme",
):
-
guess_next_version(tag_version=meta("2.0.0-alpha.5-PMC", config=config))
@@ -348,7 +339,8 @@ def test_custom_version_cls() -> None:
def __repr__(self) -> str:
return "MyVersion<Custom%s>" % self.tag
- scm_version = meta("1.0.0-foo", 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)
assert str(scm_version.tag) == "Custom 1.0.0-foo"
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