PyQt6 on Python 3.14: emitting None through a pyqtSignal changes the refcount of None (immortal)

Phil Thompson phil at riverbankcomputing.com
Wed Oct 21 17:57:34 BST 2026


On 19/10/2026 06:11, Darren Powierski wrote:
> Rename test file.
> Thank you
> 
> From: Darren Powierski
> Sent: Monday, 19 October 2026 4:06 PM
> To: 'pyqt at riverbankcomputing.com' <pyqt at riverbankcomputing.com>
> Subject: PyQt6 on Python 3.14: emitting None through a pyqtSignal
> changes the refcount of None (immortal)
> 
> Hello,
> 
> On Python 3.14, emitting a signal that carries None in an `object`
> argument changes the reference count of None. None is immortal since
> Python 3.12, so its refcount should never change. With identical
> PyQt6, Qt and sip packages on Python 3.13 the count is unchanged.
> 
> Environment
> - Windows x64, embeddable Python distributions
> - Python 3.14.5 (bug) and Python 3.13.16 (no bug)
> - PyQt6 6.11.0, PyQt6-Qt6 6.11.1, PyQt6_sip 13.11.1
> - Same result with PyQt6_sip 13.13.0
> 
> Reproducer (attached: repro_none_refcount.py)
> It creates a QObject with pyqtSignal(str, object, object), connects it
> to a pyqtSlot, emits ("k", None, None) 100,000 times, and reads the
> raw refcount of None through ctypes before and after.
> 
> Result
> - Python 3.13.16: delta 0 (RESULT: OK)
> - Python 3.14.5: delta +200,000, i.e. 2 None args x 100,000 emits 
> (RESULT: BUG)
> 
> Other observations on Python 3.14.5, from a longer test that I can 
> supply
> - Queued cross-thread emit of (str, None, None), 50,000 emits: +150,100
> - QTimer.singleShot(0, lambda: None), 100,000 calls: +20
> - No change for json.dumps with None, importing PyQt6, creating
> QApplication, or QPointF/QRect/QColor
> 
> Possible impact (suspected, not proven)
> In a long-running application on 3.14, after several hours we saw a
> TypeError raised from json.dumps before any network I/O, then
> "__init__() should return None, not NoneType", then worker threads
> stuck. The same application ran about 33 hours on 3.13.16 with no
> errors. I have not shown that the refcount change causes these
> failures.
> 
> Questions
> 1. Is the refcount change in PyQt6 or in PyQt6_sip?
> 2. Is a change planned for Python 3.14 immortal objects?
> 3. Is there a recommended workaround, other than avoiding None in
> signals declared with object?
> 
> Attachments: repro_none_refcount.py, repro_output_3_13_16.txt,
> repro_output_3_14_5.txt
> 
> Thank you,
> Darren Powierski

Extensions don't need to handle ref counts of immortal objects any 
differently to ordinary objects, that's up to Python to do the right 
thing. If I've understood you correctly the only difference between your 
test working and not is the version of Python. That suggests a bug in 
Python rather than SIP or PyQt.

Phil


More information about the PyQt mailing list