build: support Python 3.11+ and verify the range in CI - #39
Merged
Conversation
The Pipfile pinned `python_version = "3.13"`, so pipenv refused to build a virtualenv on any other interpreter — including 3.14, where every dependency already has wheels. The pin was arbitrary: nothing in the codebase or the dependency set required 3.13. Widen the floor to the version the dependencies actually impose (numpy 2.4.6 declares `requires_python >=3.11`) and prove the range in CI instead of asserting it. - Pipfile: `python_version = ">=3.11"`. Pipenv resolves PEP 440 specifiers natively via `_find_python_for_specifier` (pipenv/utils/virtualenv.py), so this selects the best installed match rather than demanding one version. - pyproject: ruff `target-version = "py311"`. Targeting the floor is what makes the linter reject 3.12+ syntax that would break the oldest supported interpreter; leaving it at py313 left the floor unguarded. - ci.yml: matrix the test job over 3.11–3.14 with `fail-fast: false`. The setup-pipenv action already accepted a `python-version` input and keyed its cache on it, so no change was needed there. - Pipfile.lock: `[requires]` participates in pipenv's Pipfile hash (`_PIPFILE_SECTIONS`, project.py), so editing it invalidated `_meta.hash` and would have failed `pipenv install --deploy`. Updated `_meta.hash` and `_meta.requires` only — no re-resolution, so no dependency version churn. - Docs: 3.13 -> 3.11+ across README and the docs site, plus a PEP 668 note (`pipx install pipenv`) for externally-managed distros where `pip install pipenv` fails, and `cp -n` so the .env step cannot clobber an existing file. Verified: ruff passes at py311 across src/tests/setups; pipenv's own matcher accepts 3.11-3.14 and refuses 3.10; torch 2.12.0+cpu, numpy 2.4.6, hdbscan 0.8.43 and pymupdf all publish cp311-cp314 wheels. Claude-Session: https://claude.ai/code/session_01KYPdxd7zY8xNT9p9yxnnaS
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.
Problem
Pipfilepinnedpython_version = "3.13", so pipenv refused to create a virtualenv on any other interpreter:The pin was arbitrary. Nothing in the codebase needs 3.13, and every dependency already ships wheels well past it.
Evidence
Scanned all 155 files / 25,402 lines for version-gated features — no 3.12+ or 3.13+ constructs: no PEP 695
type X =/def f[T], noitertools.batched, notyping.override, noPath.walk, nowarnings.deprecated, nocopy.replace. The codebase doesn't even use the 3.11 additions (TaskGroup,StrEnum,tomllib,Self).Wheel availability at the currently locked versions:
requires_pythonnumpy>=3.11is the real floor, so that is where the floor now sits.Changes
Pipfile—python_version = ">=3.11". Pipenv resolves PEP 440 specifiers natively (_find_python_for_specifier,pipenv/utils/virtualenv.py), selecting the best installed match instead of demanding one exact version.pyproject.toml— rufftarget-version = "py311". Targeting the floor is what makes the linter reject 3.12+ syntax that would break the oldest supported interpreter. Atpy313the new floor would have been unguarded..github/workflows/ci.yml— test job matrixed over 3.11 / 3.12 / 3.13 / 3.14 withfail-fast: false, so one broken interpreter never masks the others. Thesetup-pipenvcomposite action already took apython-versioninput and already keyed its cache on it, so it needed no change.Pipfile.lock—[requires]participates in pipenv's Pipfile hash (_PIPFILE_SECTIONS,project.py), so editing it invalidated_meta.hashand would have failed CI'spipenv install --deploy. Updated_meta.hashand_meta.requiresonly — recomputed with pipenv's owncalculate_pipfile_hash(), not hand-rolled. No re-resolution, so no dependency version churn: a 2-line diff with all 165 default + 38 develop packages intact.pipx install pipenv) for externally-managed distros wherepip install pipenvfails outright, andcp -nso the.envstep cannot clobber an existing file.Verification
ruff check src tests setupspasses attarget-version = "py311".Not verified locally: a real
pipenv installwas not run. This checkout is flagged by a local repo scanner, so I did not install its dependencies. The CI matrix is the first end-to-end proof, and the 3.11 leg is the one to watch — a pipenv lock is a single resolution, not a universal one.Follow-ups (not in this PR)
CHANGELOG.mdexists in the repo, so no[Unreleased]bullet was added rather than inventing the convention.[project]table withrequires-pythonwas considered and deliberately skipped: with asrc/layout plustests/,setups/,scripts/,evals/at root, adding[project]under the setuptools backend risks a "multiple top-level packages discovered" build error. It deserves its own PR with proper[tool.setuptools]config.