Repository navigation
Add Python 3.15 support - #866
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgEia1ZreHugm7HhSBhBN3
Add py315 to the tox section that runs pytest with --asyncio-mode=auto, which the unmarked fastmcp tests rely on. Python 3.15's contextlib.ContextDecorator wraps coroutine functions, so instrument/WebTransaction/BackgroundTransaction used without .async_ on an async function now keep the span open while the coroutine runs. Update the *_async_misconfigured tests to expect the nested span on 3.15+. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgEia2eCsEQTpSH8BMfR1d
d7c6bcd to
69acc6c
Compare
"Build python 3.8+" predates dropping 3.8/3.9; cibuildwheel builds whatever python_requires allows (currently 3.10-3.15). Rename the job to build_linux_wheels with no version in its name so it can't go stale again. Drop the single-entry matrix from the pure wheel job so its check name no longer reads "(3.14)", which looked like a target version. Also remove CIBW_SKIP, which referenced a matrix key removed in #826 and has evaluated to an empty string since. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgEia2eCsEQTpSH8BMfR1d
jrothrock
left a comment
There was a problem hiding this comment.
Approving — looks good. I left a few nits inline; they're all optional, none change behavior, and none need to be addressed before merging.
tests/integration/test_api.py:42, 181, 239— the"""Test case where .async_ isn't used…"""docstrings now read as if the result is always wrong, but on 3.15+ the plain decorator is correct. Maybe add "(only misbehaves on Python < 3.15)". (docstring accuracy)
| if sys.version_info >= (3, 15): | ||
| # Python 3.15's ContextDecorator wraps coroutine functions, so the | ||
| # span stays open while the coroutine runs, same as with .async_. | ||
| assert len(tracked_request.complete_spans) == 2 | ||
| assert tracked_request.complete_spans[0].operation == "Custom/Foo" | ||
| assert tracked_request.complete_spans[1].operation == "Custom/Bar" | ||
| else: | ||
| assert len(tracked_request.complete_spans) == 1 | ||
| assert tracked_request.complete_spans[0].operation == "Custom/Bar" |
There was a problem hiding this comment.
nit: this if sys.version_info >= (3, 15): … else: … block (and its comment) is repeated at L194-202 and L252-260. The rest of the suite gates on versions with pytest.mark.skipif markers rather than branching inside a test body (e.g. the shared markers in tests/tools.py, tests/unit/test_fastmcp.py:11). You could add a marker like skip_if_context_decorator_wraps_coroutines to tests/tools.py and split each test into a < 3.15 case and a >= 3.15 case. The 3.15 branch asserts the same thing as the matching .async_ test, so the >= 3.15 case could even be dropped. (sibling convention: no conditional logic in tests)
Address review feedback: instead of branching on sys.version_info inside the *_async_misconfigured tests, gate them with a shared marker in tests/tools.py, matching the rest of the suite. Add separate *_without_async_ tests for Python 3.15+ that check the plain decorator now times the whole coroutine, and note in the misconfigured tests' docstrings that they only misbehave below 3.15. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NgEia2eCsEQTpSH8BMfR1d
Summary
main.yml).py315-django52tox env. Like the older Pythons, it runs pytest with--asyncio-mode=auto, which the fastmcp integration tests rely on.Programming Language :: Python :: 3.15classifier.The release workflow already builds
cp315wheels: cibuildwheel builds every CPython version that matchespython_requires. This PR also cleans up stale labels inrelease.yml:cibuildwheel_py38plus/ "Build python 3.8+ … wheels" becomesbuild_linux_wheels/ "Build Linux … wheels". It actually builds 3.10 through 3.15, and the name no longer includes a version that can go stale.py3-none-anywheel works on every version.CIBW_SKIP: ${{ matrix.cibw_skip }}line is removed. It referenced a matrix key removed in build with python 3.14 #826, so it was always empty.Behavior change on Python 3.15: plain
@instrumentnow works on async functionsIn Python 3.15,
contextlib.ContextDecoratorwraps coroutine functions itself (the long-standing bpo-37398, referenced inAsyncDecoratorMixin). So on 3.15+, usinginstrument,WebTransactionorBackgroundTransactionwithout.async_on an async function now keeps the span open while the coroutine runs. Nested spans show up correctly, the same as with.async_. On earlier Pythons the span still closes as soon as the coroutine is created, so.async_is still needed there.The three
*_async_misconfiguredtests intests/integration/test_api.pyasserted the pre-3.15 behavior. They now check the Python version, and the CHANGELOG has an entry about the change.Related
cp314tandcp315twheels are already shipped, but_objtracere-enables the GIL). That's tracked separately, so this PR doesn't addtjobs.🤖 Generated with Claude Code
https://claude.ai/code/session_01NgEia2eCsEQTpSH8BMfR1d