Single-source the version from pyproject.toml - #16
Merged
Conversation
The version was declared twice, in pyproject.toml and as a literal __version__ in python/__init__.py, so a release required editing both and either edit alone would leave the two disagreeing. Read it from the installed distribution metadata instead, leaving the [project] table in pyproject.toml as the single declaration. The alternative direction -- pyproject.toml declaring `dynamic = ["version"]` and reading `attr = "sbd.__version__"` -- was not taken. It relies on setuptools statically AST-parsing the assignment, and if setuptools ever falls back to importing the package it would import it before the extension modules are built, since this __init__ loads the _core_* backends at import time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was generated by Claude Opus 5 under my guidance.
The version was declared twice — once in
pyproject.tomland again as a literal__version__inpython/__init__.py. A release required editing both, and either edit alone would leave the two disagreeing with no error to signal it.This reads the version from the installed distribution metadata instead, leaving the
[project]table inpyproject.tomlas the single declaration:Note the argument is the distribution name (
sbd-eigensolver), not the import name (sbd).Why this direction
The other way to de-duplicate is to have
pyproject.tomldeclaredynamic = ["version"]and readattr = "sbd.__version__". That was not taken here. It depends on setuptools statically AST-parsing the assignment; if setuptools ever falls back to importing the package to resolve the attribute, it would importsbdbefore the extension modules are built, and this__init__loads the_core_*backends at import time. Deriving the runtime attribute from the metadata avoids any build-time import of the package.What this gives up
sbd.__version__now requires the distribution metadata to be present. That holds for bothpip install .andpip install -e .. It would break only for a source tree placed onsys.pathwithout being installed — which is not a working configuration for this package regardless, since the compiled_core_*modules must be built into the package directory; importing in that state already fails withRuntimeError: No SBD backends available.A
try/except PackageNotFoundErrorfallback was deliberately omitted: it would convert a loud, accurate failure into a silently wrong version string for a case that cannot arise in a working install.Verification
Checked in a throwaway virtualenv against a minimal package reproducing the same name mismatch and layout (
packages = ["sbd"],package-dir = {sbd = "python"}):pip install .→sbd.__version__ == "1.6.1"pip install -e .→sbd.__version__ == "1.6.1"pyproject.tomlto9.9.9and reinstalling →sbd.__version__ == "9.9.9", confirming the value tracks the single declaration with no second edittest/conftest.pyalready readssbd.__version__to build the pytest header, so the existing suite exercises this path on every run.