<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:"Yu Gothic";
        panose-1:2 11 4 0 0 0 0 0 0 0;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Aptos;}
@font-face
        {font-family:"\@Yu Gothic";
        panose-1:2 11 4 0 0 0 0 0 0 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        font-size:12.0pt;
        font-family:"Aptos",sans-serif;
        mso-ligatures:standardcontextual;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Aptos",sans-serif;
        color:windowtext;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;
        mso-ligatures:none;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-AU" link="#467886" vlink="#96607D" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Rename test file.<o:p></o:p></p>
<p class="MsoNormal">Thank you<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0cm 0cm 0cm">
<p class="MsoNormal"><b><span lang="EN-US" style="font-size:11.0pt;font-family:"Calibri",sans-serif;mso-ligatures:none">From:</span></b><span lang="EN-US" style="font-size:11.0pt;font-family:"Calibri",sans-serif;mso-ligatures:none"> Darren Powierski
<br>
<b>Sent:</b> Monday, 19 October 2026 4:06 PM<br>
<b>To:</b> 'pyqt@riverbankcomputing.com' <pyqt@riverbankcomputing.com><br>
<b>Subject:</b> PyQt6 on Python 3.14: emitting None through a pyqtSignal changes the refcount of None (immortal)<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Hello,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">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.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Environment<o:p></o:p></p>
<p class="MsoNormal">- Windows x64, embeddable Python distributions<o:p></o:p></p>
<p class="MsoNormal">- Python 3.14.5 (bug) and Python 3.13.16 (no bug)<o:p></o:p></p>
<p class="MsoNormal">- PyQt6 6.11.0, PyQt6-Qt6 6.11.1, PyQt6_sip 13.11.1<o:p></o:p></p>
<p class="MsoNormal">- Same result with PyQt6_sip 13.13.0<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Reproducer (attached: repro_none_refcount.py)<o:p></o:p></p>
<p class="MsoNormal">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.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Result<o:p></o:p></p>
<p class="MsoNormal">- Python 3.13.16: delta 0 (RESULT: OK)<o:p></o:p></p>
<p class="MsoNormal">- Python 3.14.5: delta +200,000, i.e. 2 None args x 100,000 emits (RESULT: BUG)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Other observations on Python 3.14.5, from a longer test that I can supply<o:p></o:p></p>
<p class="MsoNormal">- Queued cross-thread emit of (str, None, None), 50,000 emits: +150,100<o:p></o:p></p>
<p class="MsoNormal">- QTimer.singleShot(0, lambda: None), 100,000 calls: +20<o:p></o:p></p>
<p class="MsoNormal">- No change for json.dumps with None, importing PyQt6, creating QApplication, or QPointF/QRect/QColor<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Possible impact (suspected, not proven)<o:p></o:p></p>
<p class="MsoNormal">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.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Questions<o:p></o:p></p>
<p class="MsoNormal">1. Is the refcount change in PyQt6 or in PyQt6_sip?<o:p></o:p></p>
<p class="MsoNormal">2. Is a change planned for Python 3.14 immortal objects?<o:p></o:p></p>
<p class="MsoNormal">3. Is there a recommended workaround, other than avoiding None in signals declared with object?<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Attachments: repro_none_refcount.py, repro_output_3_13_16.txt, repro_output_3_14_5.txt<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Thank you,<o:p></o:p></p>
<p class="MsoNormal">Darren Powierski<o:p></o:p></p>
</div>
</body>
</html>