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

Darren Powierski darren.powierski at tmeic.com.au
Fri Oct 23 09:02:41 BST 2026


Hi Phil,
I am just wondering did you get the attachment file example ?
Please rename it : repro_none_refcount._py
Kind Regards,
Darren

-----Original Message-----
From: Phil Thompson <phil at riverbankcomputing.com> 
Sent: Thursday, 22 October 2026 3:58 AM
To: Darren Powierski <darren.powierski at tmeic.com.au>
Cc: pyqt at riverbankcomputing.com
Subject: Re: PyQt6 on Python 3.14: emitting None through a pyqtSignal changes the refcount of None (immortal)

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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: repro_none_refcount._py
Type: application/octet-stream
Size: 1243 bytes
Desc: repro_none_refcount._py
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20261023/1a20798d/attachment.obj>


More information about the PyQt mailing list