Skip to content

build!: drop Python 3.11 and upgrade the spatialdata cluster - #120

Merged
Tomatokeftes merged 1 commit into
mainfrom
build/drop-py311-upgrade-cluster
Jul 31, 2026
Merged

build!: drop Python 3.11 and upgrade the spatialdata cluster#120
Tomatokeftes merged 1 commit into
mainfrom
build/drop-py311-upgrade-cluster

Conversation

@Tomatokeftes

Copy link
Copy Markdown
Collaborator

Closes #117. Shrinks #114.

Stacked on #119. Base it there or merge #119 first; the diff shown here
includes #119's two commits.

This is a breaking change and the commit is marked BREAKING CHANGE:, so
semantic-release will cut 2.0.0. If you would rather ship it as a minor,
reword the footer before merging.

Why

spatialdata 0.8.0 and anndata 0.13 both require Python >= 3.12. That single
constraint was blocking the entire dependency cluster, and with it the removal
of the pandas 3 string-dtype coercion. Dropping 3.11 unblocks all of it at
once.

Pins

package before after resolved
python >=3.11, <3.14 >=3.12, <3.14 3.13
dask >=2025.12.0, <2026.2 >=2026.3.0, <2026.8 2026.7.1
spatialdata >=0.7.3, <0.8 >=0.8.0, <0.9 0.8.0
ome-zarr >=0.14.0, <0.16 >=0.16.0, <0.19 0.18.0
anndata >=0.11.0, <0.13 >=0.13.2, <0.14 0.13.2
zarr >=3.0.0, <3.2 unchanged 3.1.3

zarr did not need to move, which was not obvious going in.

The anndata floor is 0.13.2, not 0.13.0, deliberately. anndata 0.13.0
imports typing_extensions.sentinel (needs typing-extensions >= 4.16) but
declares no floor for it. A resolver therefore prefers 0.13.0 -- looser
metadata is easier to satisfy -- pairs it with typing-extensions 4.14.1, and
produces an environment that installs cleanly and then dies at import:

File "anndata/acc/__init__.py", line 13, in <module>
    from typing_extensions import sentinel
ImportError: cannot import name 'sentinel' from 'typing_extensions'

A floor of >=0.13 would have shipped that. 0.13.2 declares it correctly.
This is exactly the failure mode the clean-venv CI job exists to catch.

What the coercion removal rests on

_coerce_table_strings_to_object existed because anndata could not serialize
pandas' str dtype. anndata 0.13.0 fixed that (anndata #2221, via #2133).
Verified directly before removing it -- writing an obs frame with a string
index, a string column and a string-backed categorical:

pandas pyarrow anndata write_zarr
2.3.2 + infer_string yes 0.12.2 IORegistryError
3.0.5 yes 0.12.2 IORegistryError
2.3.2 + infer_string yes 0.13.2 PASS
3.0.5 yes 0.13.2 PASS

Tests

pass main this branch
unit 643 passed, 1 xfailed 641 passed
integration 4 failed, 14 passed 4 failed, 14 passed

641 = 643 - 3 deleted coercion tests + 1 converted xfail. The four integration
failures are identical to main and unrelated to this change: three tests pass a
'linear' resampling method the code now rejects, one is a COO region
mismatch. They were failing before this PR and still are.

Test changes:

  • Deleted the three tests asserting the coercion's own behaviour.
  • Kept the write-path tests under future.infer_string=True for all three
    paths. They now cover anndata's handling rather than Thyra's workaround, and
    nothing else in the suite runs with string inference on.
  • Converted the xfail removal trigger into a plain assertion, so a regression
    in anndata's string support is still caught at the smallest scope.

mkdocs --strict clean. black / isort / flake8 / mypy / bandit / pydocstyle
clean via pre-commit.

Also updated

CI matrices in tests.yml, release.yml and complexity-monitoring.yml;
classifiers; [tool.mypy] python_version; and the docs and README badges that
advertised 3.11 -- several of which said "3.11 or 3.12" and were stale already,
since 3.13 has been supported for a while.

The dependency-cluster comments are re-pointed too: the pandas comment cited a
coercion that no longer exists, the ome-zarr comment explained a ceiling that
has moved, and the cluster preamble used a worked example that is now the
combination we ship.

Known, not introduced here

A repo-wide poetry run mypy thyra reports 30 errors across 9 files. None are
in code this PR touches, and none are gated: mypy is not in CI at all, and the
pre-commit hook runs with --ignore-missing-imports --no-strict-optional in an
isolated env that cannot see spatialdata or anndata. Some of the union-attr
signals likely widened because anndata 0.13 types X more richly. Worth a
follow-up, out of scope here.

Follow-up for #114

This moves the spatialdata ceiling to 0.8.0. When scverse/spatialdata#1055
eventually lands, #114 is reduced to a floor bump rather than the whole cluster
migration it describes today.

spatialdata 0.8.0 and anndata 0.13 both require Python >= 3.12, so the
cluster cannot move while 3.11 is supported. Dropping 3.11 unblocks all of
it and makes the pandas 3 string-dtype coercion dead code.

Pins, all exercised together:

    python      >=3.12, <3.14
    dask        >=2026.3.0, <2026.8   (2026.7.1)
    spatialdata >=0.8.0, <0.9         (0.8.0)
    ome-zarr    >=0.16.0, <0.19       (0.18.0)
    anndata     >=0.13.2, <0.14       (0.13.2)
    zarr        unchanged             (3.1.3)

The anndata floor is 0.13.2 rather than 0.13.0 deliberately. 0.13.0 imports
typing_extensions.sentinel but declares no floor for typing-extensions, so a
resolver prefers it, pairs it with 4.14.1 and yields an environment that
installs cleanly then fails at import. 0.13.2 declares the floor correctly.

Removes _coerce_table_strings_to_object and its call in _save_output: anndata
0.13 serializes pandas' str dtype natively. The three tests asserting the
coercion's behaviour go with it; the write-path tests under infer_string stay,
now covering anndata's handling, and the xfail removal trigger becomes a plain
assertion so a regression in anndata is still caught.

Also updates the CI matrices, classifiers, mypy python_version, and the docs
and README that still advertised 3.11 (several also predated 3.13 support).

Unit 641 passed / 11 skipped. Integration 4 failed / 14 passed, the same four
that fail on main for unrelated reasons (a 'linear' resampling method the code
rejects, and a COO region mismatch). mkdocs --strict clean.

BREAKING CHANGE: Python 3.11 is no longer supported. The minimum is now 3.12,
required by spatialdata 0.8.0 and anndata 0.13.
@Tomatokeftes
Tomatokeftes changed the base branch from docs/anndata-hatch-precision to main July 31, 2026 06:25
@Tomatokeftes Tomatokeftes reopened this Jul 31, 2026
@github-actions

Copy link
Copy Markdown

Complexity Monitoring Report

Threshold: 15
Total Violations: 0

Excellent! No complexity violations found.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Tomatokeftes
Tomatokeftes merged commit 2a3b161 into main Jul 31, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Delete _coerce_table_strings_to_object by raising the anndata ceiling to >= 0.13

2 participants