Skip to content

fix(deps): make the declared dependency floors installable on every supported interpreter - #185

Merged
lesnik512 merged 2 commits into
mainfrom
fix/asyncpg-floor
Sep 20, 2026
Merged

lesnik512 merged 2 commits into
mainfrom
fix/asyncpg-floor

Conversation

@lesnik512

@lesnik512 lesnik512 commented Sep 20, 2026

Copy link
Copy Markdown
Member

Found while validating the proposal in modern-python/.github#107 by hand. Three floors, all of which claimed support this package could not deliver.

Nothing reports any of this today: every gate — PR checks and the daily scheduled check alike — resolves highest, so a floor is never exercised.

1. asyncpg

asyncpg>=0.29 was declared once for every interpreter, but this package claims Python 3.11 through 3.14 plus free-threaded 3.14t, and asyncpg's wheel coverage does not stretch that far:

version released CPython ABI tags
0.29.0 2023-11-05 cp38, cp39, cp310, cp311, cp312
0.30.0 2024-10-20 + cp313
0.31.0 2025-11-24 + cp314, cp314t

Every release declares requires-python >=3.9, so a resolver asked for the lowest satisfying version does not report a conflict on 3.13 — it picks 0.29.0, falls through to the sdist, and the build fails.

"asyncpg>=0.29; python_version < '3.13'",
"asyncpg>=0.30; python_version == '3.13'",
"asyncpg>=0.31; python_version >= '3.14'",

Marked rather than raised outright to 0.31, because 0.29 is genuinely fine on 3.11 and 3.12 and there is no reason to cut those users off. This follows how lite-bootstrap already handles the same class of problem.

2. typing-extensions

>=4.12.0 is below what pydantic itself requires on 3.13+ (>=4.12.2), so pinning 4.12.0 dragged pydantic back to a build whose pydantic-core==2.18.4 has no cp313 wheel. Raised to >=4.12.2.

3. pydantic

pydantic reaches this package only transitively, through faststreamfast-depends[pydantic], and is not imported here. Its floor still has to be declared, because pydantic-core ships no cp314 wheel before pydantic 2.12, so the lowest declared combination had no installable pydantic on 3.14 even after fix 2. Marked for the same reason as asyncpg — 2.11 is fine everywhere else.

"pydantic>=2.12; python_version >= '3.14'",

Result

uv pip install --resolution lowest-direct ".[all]", before and after:

interpreter before after
3.11 builds builds, import faststream_outbox OK
3.12 builds builds, import faststream_outbox OK
3.13 build fails (asyncpg 0.29.0) builds
3.14 build fails (asyncpg 0.29.0) builds
3.14t build fails (asyncpg 0.29.0) builds

Every build failure is gone, on every supported interpreter.

At the normal (highest) resolution nothing changes — uv.lock still picks asyncpg 0.31.0, pydantic 2.13.5, typing-extensions 4.16.0 — so this is inert for anyone not installing at the lower bound.

A fourth floor, not fixed here

The lower-bound install still fails on 3.13+, now at import rather than build:

AssertionError: Class <class 'sqlalchemy.sql.elements.SQLCoreOperations'> directly inherits
TypingOnly but has additional attributes {'__static_attributes__', '__firstlineno__'}

sqlalchemy[asyncio]>=2.0 resolves to 2.0.0, which predates SQLAlchemy's handling of the __static_attributes__ and __firstlineno__ attributes Python 3.13 added. Bisected on 3.13:

version import on 3.13
2.0.0, 2.0.25, 2.0.28, 2.0.30 fails
2.0.31 OK
2.0.36 OK

2.0.31 also imports cleanly on 3.14, so sqlalchemy[asyncio]>=2.0.31; python_version >= '3.13' would close it. Left out because this PR was scoped to the three floors above; it is a one-line follow-up.

No test

There is nothing to assert in the suite: the defect is that a declared range does not resolve, which only a resolver on a given interpreter can show. A unit test reading a floor back out of pyproject.toml would restate the string, not check it. The tables above are the verification, and modern-python/.github#107 is the job that would make this a standing gate rather than a one-off audit.

Verification

just lint-ci clean (eof-fixer, ruff format, ruff check, ty). Full suite against Postgres 17: 624 passed, 100% coverage.

asyncpg declares requires-python >=3.9 throughout but ships no cp313 wheel
before 0.30 and no cp314/cp314t before 0.31, so `asyncpg>=0.29` resolved to an
sdist that cannot build on 3.13 and 3.14 rather than reporting a conflict. The
floor was unsatisfiable on half the supported range.

Marked rather than raised outright: 0.29 installs fine on 3.11 and 3.12.
@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Benchmark gate

✅ gate passed

scenario msg/s delete/msg WALrec/msg WALB/msg fpi upd del dead_tup
consumer/w1/b10 874 1.000 6.77 919 242 5000 5000 10000
consumer/w1/b100 959 1.000 6.82 920 243 5000 5000 10000
consumer/w2/b10 1090 1.000 6.79 956 242 5000 5000 10000
consumer/w2/b100 1264 1.000 6.78 995 263 5000 5000 10000
consumer/w4/b10 982 1.000 6.67 918 243 5000 5000 10000
consumer/w4/b100 1497 1.000 6.87 1037 243 5000 5000 10000
consumer/w1/b100/tfbs100 5049 0.010 6.07 1116 243 5000 5000 10000
producer/w1/b100 2028 0.000 3.04 584 0 0 0 0

Gated (fails the build): delete_calls + tuple counters (upd/del/ins) + the producer's insert_calls, exact; select_calls within +2; wal_records within a 10% band. msg/s, WAL bytes and total calls are informational (timing/FPI noise).

typing-extensions 4.12.0 is below pydantic's own requirement on 3.13+, so the
lowest declared combination dragged pydantic back to a build whose
pydantic-core has no cp313 wheel. pydantic itself reaches this package only
through faststream, but its floor has to be declared anyway: pydantic-core
ships no cp314 wheel before pydantic 2.12.

With these the lowest declared combination installs on every supported
interpreter. A stale sqlalchemy floor still breaks it at import on 3.13+.
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.

1 participant