Skip to content

Support Python 3.14#60

Open
pniedzielski wants to merge 3 commits intobloomberg:mainfrom
pniedzielski:feat/python3.14
Open

Support Python 3.14#60
pniedzielski wants to merge 3 commits intobloomberg:mainfrom
pniedzielski:feat/python3.14

Conversation

@pniedzielski
Copy link
Copy Markdown
Collaborator

@pniedzielski pniedzielski commented Nov 5, 2025

This pull request adds support to the BlazingMQ Python SDK for Python 3.14.

While usually version upgrades are easy, in this case, it was not. Python 3.14 made an undocumented change to the behavior of sys.getrefcount, which we use to detect a potential deadlock when freeing the Cython extension's session object. Because of this, we cannot rely on the return value of sys.getrefcount. Instead of changing what return value we expect depending on the Python interpreter version the code is running on, instead we add a flag to replace the deadlock detection. In the library itself, this change is simple, but it involves changing our tests to correctly handle this flag. Please see the commit message for the fully analysis.

Closes: #59

@pniedzielski pniedzielski force-pushed the feat/python3.14 branch 3 times, most recently from 515a793 to 696e680 Compare February 2, 2026 20:38
This patch removes Python 3.7 from our supported versions list in the
README.  It also fixes a syntax error in the same list, causing
version 3.12 to be rendered as 3.1220.

Signed-off-by: Patrick M. Niedzielski <pniedzielski@bloomberg.net>
@pniedzielski pniedzielski force-pushed the feat/python3.14 branch 2 times, most recently from 574759f to c84e0bf Compare March 25, 2026 18:23
@pniedzielski pniedzielski force-pushed the feat/python3.14 branch 5 times, most recently from 11d2f73 to 4d11aee Compare April 7, 2026 20:53
@pniedzielski pniedzielski marked this pull request as ready for review April 7, 2026 21:00
@pniedzielski pniedzielski requested a review from a team as a code owner April 7, 2026 21:00
Signed-off-by: Patrick M. Niedzielski <pniedzielski@bloomberg.net>
The `on_message` callback, which is executed on one of `libbmq`’s
internal processing threads, detected a potential deadlock by checking
`sys.getrefcount(ext_session) == 2`, inferring that the callback held
the last strong reference to the Cython `ExtSession`.  If so,
returning from the callback would trigger `__dealloc__`, which calls
`stop()` on the C++ session synchronously.  But, `stop()` waits for
the callback’s own background thread to finish, causing a deadlock.

However, according to the CPython developers, the return value of
`sys.getrefcount` is not guaranteed to be any particular value, or to
be stable across versions.  In fact, Python 3.14 (particularly, with
CPYthon commit 053c285f) optimized reference counting on the
interpreter’s operand stack by skipping incrementing/decrementing when
pushing/popping objects.  This reduces the value that
`sys.getrefcount()` returns in this situation by 1 compared with
previous versions of Python.  This causes the deadlock detector to
trip spuriously in normal operation on Python 3.14.  See
python/cpython#132346 for details about this
change and the position of the CPython developers.

This patch replaces the refcount check with an explicit
`owned_by_session` flag on the Cython `ExtSession` object.  The Python
`Session` sets the flag at construction while it holds a reference to
the `ExtSession` and clears the flag in `__del__`.  The `on_message`
callback checks the flag, which answers the same question (“will
returning from this callback cause `__dealloc__`?”) without depending
on CPython refcount internals.

Additionally, this patch introduces a `make_ext_session()` helper
function for tests that construct the `ExtSession` directly, bypassing
the Python `Session` wrapper.  This helper sets the flag, mirroring
what the real `Session` does, and avoids the deadlock detection
tripping incorrectly in tests.

Signed-off-by: Patrick M. Niedzielski <pniedzielski@bloomberg.net>
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.

Support Python 3.14

1 participant