build: setuptools + setup.py for vcs-versioning and setuptools-scm (#1302)#1325
Conversation
Build vcs-versioning with setuptools instead of hatchling to avoid the bootstrap cycle (hatchling → pluggy → setuptools-scm → vcs-versioning) reported in pypa#1302. Use setuptools.build_meta for setuptools-scm and drop the custom build_meta wrapper; supply dynamic versions from each package setup.py with logic inlined and guarded by if __name__ == "__main__". Exclude package setup.py from mypy (duplicate module name) and from the mypy pre-commit hook. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Composer <composer@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR changes both subprojects’ build backends to setuptools.build_meta and moves self-version computation into setup.py to break the downstream bootstrap cycle (hatchling → pluggy → setuptools-scm → vcs-versioning).
Changes:
- Switch
vcs-versioningfrom hatchling tosetuptools.build_meta, with dynamic version computed byvcs-versioning/setup.py. - Switch
setuptools-scmto standardsetuptools.build_meta, dropping the custom_own_version_helperbuild backend and addingsetuptools-scm/setup.pyfor dynamic versioning. - Adjust repo tooling (mypy/ruff/pre-commit) and add a towncrier changelog fragment.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| vcs-versioning/setup.py | New setuptools entry point that computes the package version at build time. |
| vcs-versioning/pyproject.toml | Switch build backend to setuptools and configure setuptools package discovery. |
| vcs-versioning/changelog.d/1302.misc.md | Towncrier fragment documenting the bootstrap-cycle fix. |
| setuptools-scm/setup.py | New setuptools entry point that computes the package version at build time. |
| setuptools-scm/pyproject.toml | Switch to setuptools.build_meta, remove backend-path and dynamic-version attr indirection. |
| setuptools-scm/_own_version_helper.py | Removes the previous custom version/build helper module. |
| pyproject.toml | Tooling adjustments (mypy/ruff) for new setup.py scripts. |
| .pre-commit-config.yaml | Exclude setup.py scripts from the mypy pre-commit hook. |
Comments suppressed due to low confidence (2)
vcs-versioning/setup.py:67
- In this setup.py,
_repo_root = _root.parentpoints outside an extracted sdist (wheresetup.pysits at the sdist root). That can cause version inference to skipPKG-INFO(because your custom_parseruns againstconfig.absolute_root) and fall through tofallback_version, producing an incorrect version when building from an sdist. Use the project directory asroot(e.g.,_root) and rely onsearch_parent_directories=True(or equivalent) to find the VCS root when building from a monorepo checkout, so sdist builds still seePKG-INFO/fallbacks under the sdist root.
vcs-versioning/setup.py:75 fallback_versionhere ("0.1.1+pre.tag") is inconsistent with the configured fallback invcs-versioning/pyproject.toml([tool.vcs-versioning].fallback_version = "0.1.0"). This can lead to different versions depending on which code path is used (e.g., building from sdist vs. calling the library). Consider sourcing the fallback version from the same configuration or making them match to avoid surprising version outputs.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return get_version( | ||
| root=_root.parent, | ||
| version_scheme="guess-next-dev", | ||
| local_scheme=local_scheme, | ||
| tag_regex=r"^setuptools-scm-(?P<version>v?\d+(?:\.\d+){0,2}[^\+]*)(?:\+.*)?$", | ||
| scm={"git": {"describe_command": make_describe_command("setuptools-scm-*")}}, |
There was a problem hiding this comment.
root=_root.parent will resolve to the directory above an extracted sdist (since setup.py is at the sdist root), which can break SCM discovery and makes error messages/root paths misleading during sdist wheel builds. Prefer root=_root (the project root) and enable parent-directory discovery (search_parent_directories=True) so monorepo checkouts still find the repo root while sdists stay self-contained.
Pass relative_to and fallback_root so SCM fallbacks do not resolve against the process cwd (e.g. workspace root in CI). Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Composer <composer@cursor.com>
Use root=".." and fallback_root="." (anchored with relative_to pyproject.toml) for SCM vs sdist metadata. Add resolved_fallback_root() so setuptools_scm.parse_scm_fallback finds PKG-INFO under the package tree; vcs-versioning setup.py routes parse_pkginfo through the same path. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Composer <composer@cursor.sh>
Include vcs_versioning._cli git archival templates and setuptools_scm .git_archival.txt in wheels via tool.setuptools.package-data. Co-authored-by: Cursor AI <ai@cursor.sh> Co-authored-by: Composer <composer@cursor.sh>
The circular build dependency (setuptools-scm → vcs-versioning → hatchling → pluggy → setuptools-scm) has been fixed upstream. vcs-versioning 1.1.0 switched its build backend from hatchling to setuptools, breaking the cycle. See: pypa/setuptools-scm#1325 Reverts: python-wheel-build#983 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Lalatendu Mohanty <lmohanty@redhat.com>
Summary
Addresses the distribution bootstrap cycle described in #1302:
vcs-versioningwas built with hatchling, while hatchling → pluggy → setuptools-scm →vcs-versioningmade clean bootstrapping impossible for some packagers.Changes
setuptools.build_metainstead of hatchling; supply the dynamic version fromsetup.py(logic inlined,if __name__ == "__main__").setuptools.build_meta(drop the custombuild_metawrapper /backend-path); version fromsetup.pythe same way.setup.pyfiles from mypy / the mypy pre-commit hook (duplicate module namesetup; runtimesys.pathhacks are not modeled).vcs-versioning/changelog.d/1302.misc.md(towncrier).Notes
vcs-versioningonce the pluggy/setuptools-scm edge of the graph no longer applies (see issue discussion).