Skip to content

ci: use Python 3.12 as the default for single-version jobs - #815

Draft
vishal-bala wants to merge 14 commits into
refactor/asyncio-timeout-taskgroupfrom
ci/default-python-3.12
Draft

vishal-bala wants to merge 14 commits into
refactor/asyncio-timeout-taskgroupfrom
ci/default-python-3.12

Conversation

@vishal-bala

@vishal-bala vishal-bala commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

The jobs that run on a single Python version use 3.11, which is no longer where most redisvl installs are. 3.12 now holds the largest share of downloads and is not close to end of life, while 3.11 has fallen to third. The figures below are from pypistats.org, with mirrors excluded, checked on 2026-11-02:

Python Last 30 days Last 208 days
3.12 44.1% 46.2%
3.13 22.2% 19.0%
3.11 18.8% 20.1%
3.14 9.1% not checked
3.10 1.0% not checked

Each column is a share of all downloads in that window, and versions not listed make up the remainder. 3.11's share is lower over the last 30 days than over 208, so it is shrinking.

The service tests and notebook validation are the only CI runs that call live provider APIs, so they should run on the version most users have.

Changes

Default Python version moves from 3.11 to 3.12

Each workflow sets one PYTHON_VERSION for its single-version jobs. This PR changes that value, and the Read the Docs interpreter, from 3.11 to 3.12:

  • .github/workflows/test.yml: the service tests (make test-all, which calls external provider APIs), notebook validation and the docs build job.
  • .github/workflows/test-fork-pr.yml: the maintainer-triggered service tests for fork PRs.
  • .github/workflows/auto-release.yml: the canary build, the version bump step (uv lock --check) and the release build. The wheel is pure Python (py3-none-any), so this one only keeps the workflows consistent.
  • .readthedocs.yaml: build.tools.python.

The test matrix in test.yml and the style-check matrix in lint.yml still cover every supported version, 3.11 to 3.15, so no version loses coverage.

Notes

This PR is stacked on top of the Python version stack (#768, #810, #811, #812) rather than branched from main, so it lands after the 3.10 drop and the version-range changes it sits with. It does not depend on any code in those PRs: the diff against #812 is the four lines above.

The Makefile's black --target-version py311 stays as it is. That flag is not a runtime default: it tells black which Python grammars its output must stay valid for, and it overrides the target-version list in pyproject.toml. With 3.10 dropped, py311 is exactly the supported minimum, and raising it to py312 would let black emit syntax that 3.11 cannot parse. On the current tree black 26.5.1 produces identical output for py311 and py312.

Reviewer Brief

Start here: .github/workflows/test.yml, env.PYTHON_VERSION. It drives the service tests and notebook validation, the only CI runs that call live provider APIs, as well as the docs job.

Where it could fail

  • Service tests on 3.12: a provider SDK pinned in uv.lock that misbehaves on 3.12 only when it calls its live API would surface here first, since the matrix legs on 3.12 skip the API tests.

Not verified: the service tests and the API-dependent notebook cells, because no provider keys were available locally; the Read the Docs build on Read the Docs itself; the release workflow, which runs only after merge.

Beyond CI: on Python 3.12.12, tests.test_imports passes with only the base dependencies installed, and uv build succeeds (the wheel is py3-none-any); uv lock --check passes under the pinned uv 0.13.1. The docs build in a uv sync --group docs environment gives the same 12 Sphinx warnings on 3.11.9 and 3.12.12. The notebooks against a local redis:8.4 give identical per-cell results on 3.11.9 and 3.12.12. Fifteen cells in 03_llmcache, 04_vectorizers and 06_rerankers fail on both for want of provider API keys; six !rvl CLI cells hit the 60-second cell timeout on both while the two runs shared the machine, and pass when their six notebooks are re-run alone on 3.12.

Reviewed ad90047 · Claude Code (claude-opus-5-5) · lenses: correctness, security, design, tests, docs, Redis behaviour

Lift the Requires-Python cap to <3.16 and add 3.15 to the classifiers,
black target versions and the test and lint CI matrices.

- Floor beartype at 0.23.1 in the mcp extra. Earlier releases import
  typing.no_type_check_decorator, which 3.15 removed, so fastmcp failed
  to import and the MCP server fell back to its stub.
- Relock: rpds-py 2026.9.1 (0.30.0 cannot build on 3.15) and beartype
  0.23.1. The re-split forks also move 3.14 onto google-cloud-aiplatform
  1.154.0, grpcio 1.84.0 and protobuf 6.33.6.
- Bump uv to 0.13.1, the first line that ships CPython 3.15.0 final.
- torch has no cp315 wheels on PyPI yet, so the 3.15 legs install every
  extra except sentence-transformers and all. Tests marked requires_hf
  now skip when sentence-transformers is not installed, and the HF tests
  that lacked the marker carry it.
- Loosen the ** TypeError assertion; 3.15 reworded the message.
- Ignore pytest-asyncio 0.23's asyncio deprecation warnings. On 3.15
  they number in the millions and slow the suite about twentyfold.
- Mark test_different_vector_dtypes requires_hf. It builds the default HF
  vectorizer, and its bare except turned the ImportError on 3.15 into a
  misleading "Redis version too low" skip.
- Give the SemanticRouter repr tests a stub vectorizer instead of the
  requires_hf marker, so they keep running on 3.15. __repr__ never touches
  the vectorizer; only the field's HFTextVectorizer default needed it.
- Note in the installation guide and CONTRIBUTING that the
  sentence-transformers and all extras cannot install on 3.15 yet, with
  the uv sync command that leaves them out.
torch 2.15.0 publishes cp315 wheels, so relock torch, scipy and
scikit-learn (the locked scipy 1.17.1 had no cp315 wheel either). Python
3.11+ now resolves torch 2.15.0, scipy 1.18.1 (3.12+) and scikit-learn
1.9.1; 3.10 keeps its existing versions.

With sentence-transformers installable everywhere, the 3.15-only CI
exclusion of the sentence-transformers and all extras goes, as does the
conftest skip for missing sentence-transformers, so requires_hf is an
informational marker again and the HF tests run on 3.15.

The install-guide note now covers only the upstream Python bounds that
make pip refuse voyageai (<3.15) and sql-redis (<3.14).
Make Python 3.11 the minimum supported version. Python 3.10 reached end
of life in October 2026, and its share of redisvl downloads has fallen
to about 1%.

- pyproject.toml: requires-python >=3.11,<3.16, drop the 3.10 classifier
  and black's py310 target
- CI: remove 3.10 from the lint and test matrices
- uv.lock: re-lock without upgrades, removing the python < 3.11 fork
- Use typing.Self instead of typing_extensions.Self, and for methods
  that return their own class
- Drop the dead sys.version_info monkeypatching in the MCP CLI tests
- Docs: state 3.11 as the minimum
str() and f-strings on a (str, Enum) member give "Cls.MEMBER" on Python
3.11+, so error messages and query strings built from members printed the
class name. StrEnum returns the value. Serialisation, YAML output and the
FT.CREATE arguments sent to Redis already used the value and are unchanged.

Drops the getattr(..., "value", ...) workarounds in the MCP tools and the
.value calls that existed only to format a member, and pins the visible
string form in tests/unit/test_str_enums.py.
The hybrid-policy, stored-role and MCP error-code checks passed under
(str, Enum) as well and duplicate existing coverage; the integration test
already pins the stored ChatRole bytes.
_connect_redis_client wrapped the connection and the first INFO in
separate asyncio.wait_for calls, so a slow server could hold startup for
up to twice runtime.startup_timeout_seconds before failing. Both now run
under one asyncio.timeout block. The raised exception is still
TimeoutError, which map_exception already handles.

A client that connected but then failed or timed out on INFO was never
closed; it is now closed before the error propagates.
async_multi_worker_quantize ran its workers under asyncio.gather, which
raised the first worker's exception while the others kept reading from
and writing to Redis after the caller had been told the run failed. It
now runs them in an asyncio.TaskGroup, so the siblings are cancelled and
have closed their connections before the error propagates.

Callers keep the old contract. The TaskGroup's ExceptionGroup is
unwrapped and the first worker exception is raised as is; any others
that failed in the same instant are logged. Without that, the async
executor's report would read 'unhandled errors in a TaskGroup' instead
of the worker's own message.

A cancelled worker stops at an awaited pipeline call, and every backup
write between those awaits is synchronous, so its shard records only
whole batches. Resume re-reads an unrecorded dump batch and re-converts
an unrecorded quantize batch from the stored originals, which is
idempotent. Tests cover both cancellation points and the resume.
The MCP guide now names the three startup steps that
runtime.startup_timeout_seconds bounds, since the native-hybrid probe
runs without one. The migration guide says cancellation applies with
--async and that the sync executor lets sibling workers finish.

The shared-deadline test drops a wall-clock bound that the TimeoutError
already proves, and the simultaneous-failure test pins that the first
worker's error is the one raised.
make test passes --log-level=CRITICAL, which hid the ERROR records the
simultaneous-failure test counts. The test now sets the level itself.
The service tests, notebook validation, docs build, fork-PR service tests, release builds and Read the Docs now run on 3.12 instead of 3.11. 3.12 accounts for the largest share of redisvl installs; the test and lint matrices still cover 3.10 to 3.14.
@vishal-bala
vishal-bala added this pull request to stack #769 November 2, 2026 13:25
@vishal-bala vishal-bala added the auto:ci Updates to CI/CD workflows and processes label Nov 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto:ci Updates to CI/CD workflows and processes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant