setuptools-git-versioning 3.0.0__tar.gz → 3.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {setuptools_git_versioning-3.0.0/setuptools_git_versioning.egg-info → setuptools_git_versioning-3.1.0}/PKG-INFO +35 -12
  2. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/README.rst +33 -10
  3. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/pyproject.toml +9 -5
  4. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/__init__.py +3 -0
  5. setuptools_git_versioning-3.1.0/setuptools_git_versioning/archival.py +225 -0
  6. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/git.py +6 -0
  7. setuptools_git_versioning-3.1.0/setuptools_git_versioning/scikit_metadata.py +81 -0
  8. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/version.py +14 -0
  9. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0/setuptools_git_versioning.egg-info}/PKG-INFO +35 -12
  10. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning.egg-info/SOURCES.txt +2 -0
  11. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/LICENSE +0 -0
  12. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setup.cfg +0 -0
  13. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/__main__.py +0 -0
  14. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/cli.py +0 -0
  15. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/defaults.py +0 -0
  16. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/factories.py +0 -0
  17. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/log.py +0 -0
  18. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/setup.py +0 -0
  19. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning/subst.py +0 -0
  20. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning.egg-info/dependency_links.txt +0 -0
  21. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning.egg-info/entry_points.txt +0 -0
  22. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning.egg-info/requires.txt +0 -0
  23. {setuptools_git_versioning-3.0.0 → setuptools_git_versioning-3.1.0}/setuptools_git_versioning.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: setuptools-git-versioning
3
- Version: 3.0.0
3
+ Version: 3.1.0
4
4
  Summary: Use git repo data for building a version number according to PEP-440
5
5
  Author-email: dolfinus <martinov.m.s.8@gmail.com>
6
6
  License: MIT
@@ -8,7 +8,6 @@ Project-URL: Homepage, https://setuptools-git-versioning.readthedocs.io
8
8
  Project-URL: Documentation, https://setuptools-git-versioning.readthedocs.io
9
9
  Project-URL: Source, https://github.com/dolfinus/setuptools-git-versioning
10
10
  Project-URL: CI/CD, https://github.com/dolfinus/setuptools-git-versioning/actions
11
- Project-URL: Coverage, https://app.codecov.io/gh/dolfinus/setuptools-git-versioning
12
11
  Project-URL: Tracker, https://github.com/dolfinus/setuptools-git-versioning/issues
13
12
  Keywords: setuptools,git,versioning,pep-440
14
13
  Classifier: Framework :: Setuptools Plugin
@@ -21,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Operating System :: OS Independent
25
25
  Requires-Python: >=3.7
26
26
  Description-Content-Type: text/x-rst
@@ -50,8 +50,9 @@ setuptools-git-versioning
50
50
  :target: https://setuptools-git-versioning.readthedocs.io
51
51
  .. |Build| image:: https://github.com/dolfinus/setuptools-git-versioning/workflows/Tests/badge.svg
52
52
  :target: https://github.com/dolfinus/setuptools-git-versioning/actions
53
- .. |Coverage| image:: https://codecov.io/gh/dolfinus/setuptools-git-versioning/branch/master/graph/badge.svg?token=GIMVHUTNW4
54
- :target: https://codecov.io/gh/dolfinus/setuptools-git-versioning
53
+ .. |Coverage| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/
54
+ dolfinus/38b81533c2b8c389db378e3bae7df034/raw/setuptools_git_versioning_badge.json
55
+ :target: https://github.com/dolfinus/setuptools-git-versioning/actions
55
56
  .. |pre-commit.ci| image:: https://results.pre-commit.ci/badge/github/dolfinus/setuptools-git-versioning/master.svg
56
57
  :target: https://results.pre-commit.ci/latest/github/dolfinus/setuptools-git-versioning/master
57
58
 
@@ -81,7 +82,8 @@ between ``setuptools-git-versioning`` and other tools.
81
82
 
82
83
  - Only Git v2 is supported
83
84
 
84
- - Only Setuptools build backend is supported (no Poetry & others)
85
+ - Setuptools and `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backends are
86
+ supported (no Poetry & others)
85
87
 
86
88
  - Currently does not support automatic exporting of package version to a file for runtime use
87
89
  (but you can use ``setuptools-git-versioning > file`` redirect instead)
@@ -93,13 +95,6 @@ Documentation
93
95
 
94
96
  See https://setuptools-git-versioning.readthedocs.io/en/stable/
95
97
 
96
- .. contribution
97
-
98
- Contribution Guide
99
- ------------------
100
-
101
- See ./CONTRIBUTING.rst
102
-
103
98
  .. installation
104
99
 
105
100
  Install
@@ -180,3 +175,31 @@ and then add new argument ``setuptools_git_versioning`` with config options:
180
175
  )
181
176
 
182
177
  Commands are the same as above, plus ``python -m setup.py`` returns the same version.
178
+
179
+ ``scikit-build-core``
180
+ ~~~~~~~~~~~~~~~~~~~~~
181
+
182
+ If your project uses the `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backend,
183
+ add ``setuptools-git-versioning`` to ``build-system.requires``,
184
+ mark the ``version`` field as dynamic,
185
+ register the dynamic-metadata provider,
186
+ and configure options under the usual ``[tool.setuptools-git-versioning]`` section:
187
+
188
+ .. code:: toml
189
+
190
+ [build-system]
191
+ requires = [ "scikit-build-core", "setuptools-git-versioning>=3.0,<4", ]
192
+ build-backend = "scikit_build_core.build"
193
+
194
+ [project]
195
+ name = "mypackage"
196
+ dynamic = ["version"]
197
+
198
+ [tool.scikit-build.metadata.version]
199
+ provider = "setuptools_git_versioning.scikit_metadata"
200
+
201
+ [tool.setuptools-git-versioning]
202
+ enabled = true
203
+
204
+ All options under ``[tool.setuptools-git-versioning]`` work exactly as the for ``setuptools`` backend.
205
+ Inline configuration under ``[tool.scikit-build.metadata.version]`` is not supported.
@@ -18,8 +18,9 @@ setuptools-git-versioning
18
18
  :target: https://setuptools-git-versioning.readthedocs.io
19
19
  .. |Build| image:: https://github.com/dolfinus/setuptools-git-versioning/workflows/Tests/badge.svg
20
20
  :target: https://github.com/dolfinus/setuptools-git-versioning/actions
21
- .. |Coverage| image:: https://codecov.io/gh/dolfinus/setuptools-git-versioning/branch/master/graph/badge.svg?token=GIMVHUTNW4
22
- :target: https://codecov.io/gh/dolfinus/setuptools-git-versioning
21
+ .. |Coverage| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/
22
+ dolfinus/38b81533c2b8c389db378e3bae7df034/raw/setuptools_git_versioning_badge.json
23
+ :target: https://github.com/dolfinus/setuptools-git-versioning/actions
23
24
  .. |pre-commit.ci| image:: https://results.pre-commit.ci/badge/github/dolfinus/setuptools-git-versioning/master.svg
24
25
  :target: https://results.pre-commit.ci/latest/github/dolfinus/setuptools-git-versioning/master
25
26
 
@@ -49,7 +50,8 @@ between ``setuptools-git-versioning`` and other tools.
49
50
 
50
51
  - Only Git v2 is supported
51
52
 
52
- - Only Setuptools build backend is supported (no Poetry & others)
53
+ - Setuptools and `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backends are
54
+ supported (no Poetry & others)
53
55
 
54
56
  - Currently does not support automatic exporting of package version to a file for runtime use
55
57
  (but you can use ``setuptools-git-versioning > file`` redirect instead)
@@ -61,13 +63,6 @@ Documentation
61
63
 
62
64
  See https://setuptools-git-versioning.readthedocs.io/en/stable/
63
65
 
64
- .. contribution
65
-
66
- Contribution Guide
67
- ------------------
68
-
69
- See ./CONTRIBUTING.rst
70
-
71
66
  .. installation
72
67
 
73
68
  Install
@@ -148,3 +143,31 @@ and then add new argument ``setuptools_git_versioning`` with config options:
148
143
  )
149
144
 
150
145
  Commands are the same as above, plus ``python -m setup.py`` returns the same version.
146
+
147
+ ``scikit-build-core``
148
+ ~~~~~~~~~~~~~~~~~~~~~
149
+
150
+ If your project uses the `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backend,
151
+ add ``setuptools-git-versioning`` to ``build-system.requires``,
152
+ mark the ``version`` field as dynamic,
153
+ register the dynamic-metadata provider,
154
+ and configure options under the usual ``[tool.setuptools-git-versioning]`` section:
155
+
156
+ .. code:: toml
157
+
158
+ [build-system]
159
+ requires = [ "scikit-build-core", "setuptools-git-versioning>=3.0,<4", ]
160
+ build-backend = "scikit_build_core.build"
161
+
162
+ [project]
163
+ name = "mypackage"
164
+ dynamic = ["version"]
165
+
166
+ [tool.scikit-build.metadata.version]
167
+ provider = "setuptools_git_versioning.scikit_metadata"
168
+
169
+ [tool.setuptools-git-versioning]
170
+ enabled = true
171
+
172
+ All options under ``[tool.setuptools-git-versioning]`` work exactly as the for ``setuptools`` backend.
173
+ Inline configuration under ``[tool.scikit-build.metadata.version]`` is not supported.
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta:__legacy__"
9
9
 
10
10
  [project]
11
11
  name = "setuptools-git-versioning"
12
- version = "3.0.0"
12
+ version = "3.1.0"
13
13
  authors = [{name = "dolfinus", email = "martinov.m.s.8@gmail.com"}]
14
14
  license = {text = "MIT"}
15
15
  description = "Use git repo data for building a version number according to PEP-440"
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Programming Language :: Python :: 3.11",
26
26
  "Programming Language :: Python :: 3.12",
27
27
  "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
28
29
  "Operating System :: OS Independent",
29
30
  ]
30
31
  requires-python = ">=3.7"
@@ -43,7 +44,6 @@ Homepage = "https://setuptools-git-versioning.readthedocs.io"
43
44
  Documentation = "https://setuptools-git-versioning.readthedocs.io"
44
45
  Source = "https://github.com/dolfinus/setuptools-git-versioning"
45
46
  "CI/CD" = "https://github.com/dolfinus/setuptools-git-versioning/actions"
46
- Coverage = "https://app.codecov.io/gh/dolfinus/setuptools-git-versioning"
47
47
  Tracker = "https://github.com/dolfinus/setuptools-git-versioning/issues"
48
48
 
49
49
  [project.entry-points."distutils.setup_keywords"]
@@ -66,8 +66,8 @@ dev_template = "{tag}.post{ccount}"
66
66
 
67
67
  [dependency-groups]
68
68
  dev = [
69
- "ruff~=0.14.11",
70
- "prek~=0.2.28",
69
+ "ruff~=0.15.13",
70
+ "prek~=0.4.0",
71
71
  ]
72
72
  test = [
73
73
  "build>=1.1.1",
@@ -78,19 +78,23 @@ test = [
78
78
  # Some tests need to write TOML, but tomli and tomllib are read-only, and toml package is unmaintained
79
79
  "tomli-w>=1.0.0",
80
80
  "wheel>=0.42.0",
81
+ "scikit-build-core>=0.5",
81
82
  ]
82
83
  docs = [
83
84
  "furo~=2025.12.19",
84
85
  "numpydoc~=1.10.0",
86
+ # https://github.com/sphinx-toolbox/sphinx-toolbox/issues/201
85
87
  "sphinx~=8.2.3",
86
88
  "sphinx-argparse~=0.5.2",
89
+ # https://github.com/sphinx-toolbox/sphinx-toolbox/pull/209
87
90
  "sphinx-autodoc-typehints~=3.5.2",
88
91
  "sphinx-copybutton~=0.5.2",
89
92
  "sphinx-last-updated-by-git~=0.3.8",
93
+ # https://github.com/sphinx-toolbox/sphinx-toolbox/issues/189
90
94
  "sphinx-tabs~=3.4.5",
91
95
  "sphinxcontrib-towncrier~=0.5.0a0",
92
96
  "towncrier~=25.8.0",
93
- "sphinx-toolbox~=4.1.0",
97
+ "sphinx-toolbox~=4.1.2",
94
98
  ]
95
99
 
96
100
  [tool.uv]
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
+ from setuptools_git_versioning.archival import parse_archival_file, version_from_archival
5
6
  from setuptools_git_versioning.git import (
6
7
  count_since,
7
8
  get_all_tags,
@@ -37,5 +38,7 @@ __all__ = [
37
38
  "get_version",
38
39
  "infer_version",
39
40
  "is_dirty",
41
+ "parse_archival_file",
42
+ "version_from_archival",
40
43
  "version_from_git",
41
44
  ]
@@ -0,0 +1,225 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os # noqa: TC003
5
+ import re
6
+ from dataclasses import dataclass
7
+ from email.parser import HeaderParser
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+
11
+ from setuptools_git_versioning.defaults import (
12
+ DEFAULT_DEV_TEMPLATE,
13
+ DEFAULT_DIRTY_TEMPLATE,
14
+ DEFAULT_TEMPLATE,
15
+ )
16
+ from setuptools_git_versioning.factories import create_branch_formatter, create_tag_formatter
17
+ from setuptools_git_versioning.log import DEBUG, INFO
18
+ from setuptools_git_versioning.subst import resolve_substitutions
19
+
20
+ if TYPE_CHECKING:
21
+ from typing import Callable
22
+
23
+ from packaging.version import Version
24
+
25
+ ARCHIVAL_FILENAME = ".git_archival.txt"
26
+ DESCRIBE_UNSUPPORTED = "%(describe"
27
+ FORMAT_UNSUBSTITUTED = "$Format"
28
+ DESCRIBE_PARTS = 3 # tag-N-gSHA
29
+
30
+ REF_TAG_RE = re.compile(r"(?<=\btag: )([^,]+)\b")
31
+ REF_HEAD_RE = re.compile(r"HEAD\s*->\s*([^,]+)")
32
+ FULL_SHA_RE = re.compile(r"^([0-9a-f]{40}|[0-9a-f]{64})$") # SHA-1 or SHA-256
33
+
34
+ log = logging.getLogger(__name__)
35
+
36
+
37
+ @dataclass
38
+ class ArchivalData:
39
+ tag: str
40
+ ccount: int
41
+ sha: str
42
+ full_sha: str
43
+ dirty: bool
44
+ branch: str | None
45
+
46
+
47
+ def parse_archival_file(path: str | os.PathLike) -> dict[str, str]:
48
+ """Read a .git_archival.txt file and return its key/value pairs.
49
+
50
+ Keys are normalized to lowercase so lookups behave consistently
51
+ regardless of whether the file uses `node:` or `Node:` etc.
52
+ """
53
+ content = Path(path).read_text(encoding="utf-8")
54
+ log.log(DEBUG, "'%s' content:\n%s", ARCHIVAL_FILENAME, content)
55
+ message = HeaderParser().parsestr(content)
56
+
57
+ # HeaderParser treats the first blank line as the end of headers.
58
+ # Anything after it ends up in the message body and is silently
59
+ # dropped from .items(). Warn the user instead of losing fields.
60
+ payload = message.get_payload()
61
+ if isinstance(payload, str) and payload.strip():
62
+ log.warning(
63
+ "'%s' contains content after a blank line; those fields will be ignored",
64
+ ARCHIVAL_FILENAME,
65
+ )
66
+
67
+ return {key.lower(): value for key, value in message.items()}
68
+
69
+
70
+ def _parse_describe(describe: str) -> tuple[str, int, str | None, bool]:
71
+ """Parse a `git describe`-style string into (tag, ccount, short_sha, dirty)."""
72
+ dirty = False
73
+ if describe.endswith("-dirty"):
74
+ dirty = True
75
+ describe = describe[: -len("-dirty")]
76
+
77
+ parts = describe.rsplit("-", 2)
78
+ if len(parts) < DESCRIBE_PARTS:
79
+ return describe, 0, None, dirty
80
+
81
+ tag, ccount_str, gnode = parts
82
+ try:
83
+ ccount = int(ccount_str)
84
+ except ValueError:
85
+ return describe, 0, None, dirty
86
+
87
+ short_sha = gnode[1:] if gnode.startswith("g") else gnode
88
+ return tag, ccount, short_sha, dirty
89
+
90
+
91
+ def _branch_from_ref_names(ref_names: str) -> str | None:
92
+ match = REF_HEAD_RE.search(ref_names)
93
+ if match:
94
+ return match.group(1).strip()
95
+ return None
96
+
97
+
98
+ def get_data_from_archival_file(data: dict[str, str]) -> ArchivalData | None:
99
+ """Convert parsed archival data into structured version info, or None.
100
+
101
+ Returns None when the file looks unsubstituted or otherwise unusable so
102
+ the caller can fall through to live git.
103
+ """
104
+ if any(FORMAT_UNSUBSTITUTED in value for value in data.values()):
105
+ log.warning(
106
+ "'%s' contains unprocessed '$Format:...$' placeholders, skipping",
107
+ ARCHIVAL_FILENAME,
108
+ )
109
+ return None
110
+
111
+ node = data.get("node", "").strip()
112
+ full_sha = node if FULL_SHA_RE.match(node) else ""
113
+ ref_names = data.get("ref-names", "")
114
+ branch = _branch_from_ref_names(ref_names)
115
+ describe = data.get("describe-name", "").strip()
116
+
117
+ describe_tag: str | None = None
118
+ ccount = 0
119
+ short_sha = ""
120
+ dirty = False
121
+
122
+ if describe and DESCRIBE_UNSUPPORTED not in describe:
123
+ describe_tag, ccount, parsed_sha, dirty = _parse_describe(describe)
124
+ if parsed_sha:
125
+ short_sha = parsed_sha
126
+ elif describe:
127
+ log.warning(
128
+ "git archive did not expand %(describe...) (git <2.32), falling back to ref-names",
129
+ )
130
+
131
+ if describe_tag is not None:
132
+ tag = describe_tag
133
+ else:
134
+ tags = REF_TAG_RE.findall(ref_names)
135
+ if not tags:
136
+ log.log(
137
+ INFO,
138
+ "'%s' has no usable describe-name or tag in ref-names",
139
+ ARCHIVAL_FILENAME,
140
+ )
141
+ return None
142
+ tag = tags[0].strip()
143
+
144
+ # Prefer the full SHA when available so {sha} matches the live-git
145
+ # path's `full_sha[:8]` rendering. Fall back to the short SHA from
146
+ # describe-name only when no valid `node` field is present.
147
+ if full_sha:
148
+ short_sha = full_sha[:8]
149
+ elif short_sha:
150
+ full_sha = short_sha
151
+
152
+ return ArchivalData(
153
+ tag=tag,
154
+ ccount=ccount,
155
+ sha=short_sha[:8],
156
+ full_sha=full_sha,
157
+ dirty=dirty,
158
+ branch=branch,
159
+ )
160
+
161
+
162
+ def version_from_archival( # noqa: PLR0913
163
+ project_root: str | os.PathLike,
164
+ *,
165
+ package_name: str | None = None,
166
+ template: str = DEFAULT_TEMPLATE,
167
+ dev_template: str = DEFAULT_DEV_TEMPLATE,
168
+ dirty_template: str = DEFAULT_DIRTY_TEMPLATE,
169
+ tag_formatter: Callable[[str], str] | str | None = None,
170
+ branch_formatter: Callable[[str], str] | str | None = None,
171
+ ) -> Version | None:
172
+ """Return a Version derived from .git_archival.txt, or None if unavailable."""
173
+ archival_path = Path(project_root).joinpath(ARCHIVAL_FILENAME)
174
+ if not archival_path.exists():
175
+ log.log(DEBUG, "No '%s' present at '%s'", ARCHIVAL_FILENAME, project_root)
176
+ return None
177
+
178
+ log.log(INFO, "File '%s' is found, reading its content", archival_path)
179
+ data = parse_archival_file(archival_path)
180
+ info = get_data_from_archival_file(data)
181
+ if info is None:
182
+ return None
183
+
184
+ log.log(DEBUG, "Parsed archival data: %r", info)
185
+
186
+ tag = info.tag
187
+ if tag_formatter is not None:
188
+ tag_format_callback = create_tag_formatter(tag_formatter, package_name=package_name, root=project_root)
189
+ tag = tag_format_callback(tag)
190
+ log.log(DEBUG, "Tag after formatting: %r", tag)
191
+
192
+ if info.dirty:
193
+ log.log(INFO, "Using template from 'dirty_template' option")
194
+ chosen = dirty_template
195
+ elif info.ccount > 0:
196
+ log.log(INFO, "Using template from 'dev_template' option")
197
+ chosen = dev_template
198
+ else:
199
+ log.log(INFO, "Using template from 'template' option")
200
+ chosen = template
201
+
202
+ # When ref-names is absent or doesn't reveal a current branch, default
203
+ # to the literal "HEAD" so `{branch}` substitution mirrors what
204
+ # `git rev-parse --abbrev-ref HEAD` produces in detached-HEAD state.
205
+ branch = info.branch if info.branch is not None else "HEAD"
206
+ if branch_formatter is not None:
207
+ branch_format_callback = create_branch_formatter(branch_formatter, package_name=package_name, root=project_root)
208
+ branch = branch_format_callback(branch)
209
+ log.log(INFO, "Branch after formatting: %r", branch)
210
+
211
+ rendered = resolve_substitutions(
212
+ chosen,
213
+ sha=info.sha,
214
+ tag=tag,
215
+ ccount=info.ccount,
216
+ branch=branch,
217
+ full_sha=info.full_sha,
218
+ )
219
+ log.log(INFO, "Version number after resolving substitutions: %r", rendered)
220
+
221
+ # Deferred to avoid a top-level circular import:
222
+ # `version.py` imports `version_from_archival` from this module.
223
+ from setuptools_git_versioning.version import sanitize_version
224
+
225
+ return sanitize_version(rendered)
@@ -18,7 +18,13 @@ def _exec(*cmd: str, root: str | os.PathLike | None = None) -> list[str]:
18
18
  try:
19
19
  stdout = subprocess.check_output(cmd, text=True, cwd=root) # noqa: S603
20
20
  except subprocess.CalledProcessError as e:
21
+ log.log(DEBUG, "Subprocess exited with code %d: %r", e.returncode, e.output)
21
22
  stdout = e.output
23
+ except OSError as e:
24
+ # Handle case where git executable is not found
25
+ # FileNotFoundError on Unix, OSError on some other systems
26
+ log.log(DEBUG, "Command not found: %r", e)
27
+ stdout = ""
22
28
  lines = stdout.splitlines()
23
29
  return [line.rstrip() for line in lines if line.rstrip()]
24
30
 
@@ -0,0 +1,81 @@
1
+ """Dynamic metadata provider for the scikit-build-core build backend."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from setuptools_git_versioning.defaults import set_default_options
9
+ from setuptools_git_versioning.setup import read_toml
10
+ from setuptools_git_versioning.version import version_from_git
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Mapping
14
+
15
+ __all__ = ["dynamic_metadata", "get_requires_for_dynamic_metadata"]
16
+
17
+
18
+ def dynamic_metadata(
19
+ field: str,
20
+ settings: Mapping[str, Any] | None = None,
21
+ ) -> str:
22
+ if field != "version":
23
+ msg = f"Only the 'version' field is supported, got {field!r}"
24
+ raise ValueError(msg)
25
+
26
+ if settings:
27
+ msg = (
28
+ "Inline configuration under [tool.scikit-build.metadata.version] is not supported. "
29
+ "Configure setuptools-git-versioning under [tool.setuptools-git-versioning] instead."
30
+ )
31
+ raise ValueError(msg)
32
+
33
+ root = Path.cwd()
34
+
35
+ config = read_toml(root=root)
36
+ if not config:
37
+ msg = (
38
+ "Missing [tool.setuptools-git-versioning] section in pyproject.toml. "
39
+ "Add it (with at minimum 'enabled = true') to use this provider."
40
+ )
41
+ raise ValueError(msg)
42
+
43
+ if not config.pop("enabled", True):
44
+ msg = (
45
+ "[tool.setuptools-git-versioning] has 'enabled = false' but the scikit-build-core "
46
+ "metadata provider for setuptools-git-versioning was selected. "
47
+ "Either remove the provider or set 'enabled = true'."
48
+ )
49
+ raise ValueError(msg)
50
+
51
+ set_default_options(config)
52
+
53
+ package_name = _read_project_name(root)
54
+
55
+ return str(version_from_git(package_name, **config, root=root))
56
+
57
+
58
+ def _read_project_name(root: Path) -> str | None:
59
+ pyproject = root / "pyproject.toml"
60
+ if not pyproject.is_file():
61
+ return None
62
+
63
+ try:
64
+ import tomllib
65
+
66
+ with pyproject.open("rb") as file:
67
+ data = tomllib.load(file)
68
+ except ImportError:
69
+ import tomli
70
+
71
+ with pyproject.open("rb") as file:
72
+ data = tomli.load(file)
73
+
74
+ name = data.get("project", {}).get("name")
75
+ return name if isinstance(name, str) else None
76
+
77
+
78
+ def get_requires_for_dynamic_metadata(
79
+ _settings: Mapping[str, Any] | None = None,
80
+ ) -> list[str]:
81
+ return ["setuptools-git-versioning"]
@@ -11,6 +11,7 @@ from typing import Callable
11
11
  # where 'packaging' is not installed yet
12
12
  from packaging.version import Version
13
13
 
14
+ from setuptools_git_versioning.archival import version_from_archival
14
15
  from setuptools_git_versioning.defaults import (
15
16
  DEFAULT_DEV_TEMPLATE,
16
17
  DEFAULT_DIRTY_TEMPLATE,
@@ -114,6 +115,19 @@ def version_from_git( # noqa: PLR0915, PLR0912, PLR0913, C901
114
115
  # running on sdist package, do not sanitize
115
116
  return Version(version_str)
116
117
 
118
+ archival_version = version_from_archival(
119
+ project_root,
120
+ package_name=package_name,
121
+ template=template,
122
+ dev_template=dev_template,
123
+ dirty_template=dirty_template,
124
+ tag_formatter=tag_formatter,
125
+ branch_formatter=branch_formatter,
126
+ )
127
+ if archival_version is not None:
128
+ log.log(INFO, "Resolved version from '.git_archival.txt': %s", archival_version)
129
+ return archival_version
130
+
117
131
  if version_callback is not None:
118
132
  if version_file is not None:
119
133
  msg = "Either 'version_file' or 'version_callback' can be passed, but not both at the same time"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: setuptools-git-versioning
3
- Version: 3.0.0
3
+ Version: 3.1.0
4
4
  Summary: Use git repo data for building a version number according to PEP-440
5
5
  Author-email: dolfinus <martinov.m.s.8@gmail.com>
6
6
  License: MIT
@@ -8,7 +8,6 @@ Project-URL: Homepage, https://setuptools-git-versioning.readthedocs.io
8
8
  Project-URL: Documentation, https://setuptools-git-versioning.readthedocs.io
9
9
  Project-URL: Source, https://github.com/dolfinus/setuptools-git-versioning
10
10
  Project-URL: CI/CD, https://github.com/dolfinus/setuptools-git-versioning/actions
11
- Project-URL: Coverage, https://app.codecov.io/gh/dolfinus/setuptools-git-versioning
12
11
  Project-URL: Tracker, https://github.com/dolfinus/setuptools-git-versioning/issues
13
12
  Keywords: setuptools,git,versioning,pep-440
14
13
  Classifier: Framework :: Setuptools Plugin
@@ -21,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Operating System :: OS Independent
25
25
  Requires-Python: >=3.7
26
26
  Description-Content-Type: text/x-rst
@@ -50,8 +50,9 @@ setuptools-git-versioning
50
50
  :target: https://setuptools-git-versioning.readthedocs.io
51
51
  .. |Build| image:: https://github.com/dolfinus/setuptools-git-versioning/workflows/Tests/badge.svg
52
52
  :target: https://github.com/dolfinus/setuptools-git-versioning/actions
53
- .. |Coverage| image:: https://codecov.io/gh/dolfinus/setuptools-git-versioning/branch/master/graph/badge.svg?token=GIMVHUTNW4
54
- :target: https://codecov.io/gh/dolfinus/setuptools-git-versioning
53
+ .. |Coverage| image:: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/
54
+ dolfinus/38b81533c2b8c389db378e3bae7df034/raw/setuptools_git_versioning_badge.json
55
+ :target: https://github.com/dolfinus/setuptools-git-versioning/actions
55
56
  .. |pre-commit.ci| image:: https://results.pre-commit.ci/badge/github/dolfinus/setuptools-git-versioning/master.svg
56
57
  :target: https://results.pre-commit.ci/latest/github/dolfinus/setuptools-git-versioning/master
57
58
 
@@ -81,7 +82,8 @@ between ``setuptools-git-versioning`` and other tools.
81
82
 
82
83
  - Only Git v2 is supported
83
84
 
84
- - Only Setuptools build backend is supported (no Poetry & others)
85
+ - Setuptools and `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backends are
86
+ supported (no Poetry & others)
85
87
 
86
88
  - Currently does not support automatic exporting of package version to a file for runtime use
87
89
  (but you can use ``setuptools-git-versioning > file`` redirect instead)
@@ -93,13 +95,6 @@ Documentation
93
95
 
94
96
  See https://setuptools-git-versioning.readthedocs.io/en/stable/
95
97
 
96
- .. contribution
97
-
98
- Contribution Guide
99
- ------------------
100
-
101
- See ./CONTRIBUTING.rst
102
-
103
98
  .. installation
104
99
 
105
100
  Install
@@ -180,3 +175,31 @@ and then add new argument ``setuptools_git_versioning`` with config options:
180
175
  )
181
176
 
182
177
  Commands are the same as above, plus ``python -m setup.py`` returns the same version.
178
+
179
+ ``scikit-build-core``
180
+ ~~~~~~~~~~~~~~~~~~~~~
181
+
182
+ If your project uses the `scikit-build-core <https://scikit-build-core.readthedocs.io>`_ build backend,
183
+ add ``setuptools-git-versioning`` to ``build-system.requires``,
184
+ mark the ``version`` field as dynamic,
185
+ register the dynamic-metadata provider,
186
+ and configure options under the usual ``[tool.setuptools-git-versioning]`` section:
187
+
188
+ .. code:: toml
189
+
190
+ [build-system]
191
+ requires = [ "scikit-build-core", "setuptools-git-versioning>=3.0,<4", ]
192
+ build-backend = "scikit_build_core.build"
193
+
194
+ [project]
195
+ name = "mypackage"
196
+ dynamic = ["version"]
197
+
198
+ [tool.scikit-build.metadata.version]
199
+ provider = "setuptools_git_versioning.scikit_metadata"
200
+
201
+ [tool.setuptools-git-versioning]
202
+ enabled = true
203
+
204
+ All options under ``[tool.setuptools-git-versioning]`` work exactly as the for ``setuptools`` backend.
205
+ Inline configuration under ``[tool.scikit-build.metadata.version]`` is not supported.
@@ -3,11 +3,13 @@ README.rst
3
3
  pyproject.toml
4
4
  setuptools_git_versioning/__init__.py
5
5
  setuptools_git_versioning/__main__.py
6
+ setuptools_git_versioning/archival.py
6
7
  setuptools_git_versioning/cli.py
7
8
  setuptools_git_versioning/defaults.py
8
9
  setuptools_git_versioning/factories.py
9
10
  setuptools_git_versioning/git.py
10
11
  setuptools_git_versioning/log.py
12
+ setuptools_git_versioning/scikit_metadata.py
11
13
  setuptools_git_versioning/setup.py
12
14
  setuptools_git_versioning/subst.py
13
15
  setuptools_git_versioning/version.py