-
Notifications
You must be signed in to change notification settings - Fork 182
Description
Summary
When debugging a PySide6 application using debugpy 1.8.19 (via VS Code Python Debugger extension 2025.18.0) on Python 3.13.4, debugpy’s sys‑monitoring creates _DummyThread objects while inspecting a QThread that performs HTTP requests (requests/urllib3).
During interpreter shutdown, Python 3.13 attempts to finalize these _DummyThread objects and crashes with:
Exception ignored in: <function _DeleteDummyThreadOnDel.__del__>
TypeError: 'NoneType' object does not support the context manager protocol
This does not occur:
- when running the same code outside VS Code (no debugpy)
- when using only Python threads
- when using QThread without debugpy
- when using requests/urllib3 without debugpy
The issue is reproducible with a minimal PySide6 + requests script.
I have not tested this on Python 3.12, so I cannot confirm whether the issue is specific to Python 3.13 or also affects earlier versions
Environment
- Python: 3.13.4 (Windows x64)
- VS Code Python Debugger extension: 2025.18.0
- debugpy version: 1.8.19
(verified viaimport debugpy; debugpy.__version__) - OS: Windows 11
- PySide6: 6.10.1
- requests: 2.32.5
- urllib3: 2.6.0
Minimal pip freeze:
certifi==2025.11.12
charset-normalizer==3.4.4
idna==3.11
PySide6==6.10.1
PySide6_Addons==6.10.1
PySide6_Essentials==6.10.1
requests==2.32.5
shiboken6==6.10.1
urllib3==2.6.0
Minimal Reproduction
qt_debugpy_repro.py:
import sys
import time
import threading
import requests
from PySide6.QtCore import QObject, QThread, Signal
from PySide6.QtWidgets import QApplication
import debugpy
print(f"debugpy version: {debugpy.__version__}")
class Worker(QObject):
tick = Signal()
def run(self):
# Simulate Spotipy/urllib3 behavior: property access on HTTPResponse
for _ in range(3):
r = requests.get("https://httpbin.org/get")
_ = r.raw.closed # <-- triggers urllib3.response.closed
time.sleep(0.1)
print("worker done")
def dump_threads(label):
print(f"\nTHREADS {label}:")
for t in threading.enumerate():
print(" -", t, "daemon=", t.daemon, "type=", type(t))
def main():
app = QApplication(sys.argv)
worker = Worker()
thread = QThread()
worker.moveToThread(thread)
thread.started.connect(worker.run)
thread.start()
QThread.msleep(500)
dump_threads("BEFORE SHUTDOWN")
# Stop QThread without joining (mirrors real-world teardown timing)
thread.quit()
dump_threads("AFTER QUIT")
print("exiting app")
sys.exit(0)
if __name__ == "__main__":
main()Steps to Reproduce
- Open the script in VS Code
- Select Python 3.13.4 interpreter
- Use this launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python Debug Repro",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/qt_debugpy_repro.py",
"console": "integratedTerminal",
"python": "${workspaceFolder}/venv/Scripts/python"
}
]
}- Run with Start Debugging
- Observe thread list and shutdown behavior
Observed Behavior (under debugpy)
In the minimal reproduction code listed above, debugpy creates a _DummyThread when inspecting a PySide6 QThread.
The minimal repro does not crash Python, but it clearly shows the unexpected _DummyThread created by debugpy:
THREADS BEFORE SHUTDOWN:
- <_MainThread(MainThread, ...)>
- <_DummyThread(Dummy-6, ...)>
THREADS AFTER QUIT:
- <_MainThread(MainThread, ...)>
- <_DummyThread(Dummy-6, ...)>
exiting app
QThread: Destroyed while thread '' is still running
Behavior in the full application
In my real application (which uses the same pattern but performs more work inside the QThread), the _DummyThread created by debugpy survives until interpreter shutdown.
On Python 3.13.4 this leads to a teardown crash:
Exception ignored in: <function _DeleteDummyThreadOnDel.__del__>
TypeError: 'NoneType' object has no support for the context manager protocol
This crash does not occur outside debugpy.
The _DummyThread is created by debugpy’s sys‑monitoring:
Example captured stack:
=== DummyThread created ===
File "qt_debugpy_repro.py", line XX, in run
File "...debugpy/_pydevd_sys_monitoring.py", line 330, in _create_thread_info
t = threading.current_thread()
File "threading.py", line 1439, in current_thread
return _DummyThread()
=== end DummyThread stack ===
Expected Behavior
- No
_DummyThreadshould be created for QThreads - No crash during interpreter shutdown
- Behavior should match Python 3.12 and non-debugpy execution
Analysis
- QThread is not a Python
threading.Thread - debugpy’s sys‑monitoring inspects thread state during property access (
urllib3.response.closed) threading.current_thread()returns_DummyThread()when the thread is not inthreading._active- Python 3.13 changed teardown order
_DummyThread.__del__now runs after parts ofthreadingare already destroyed- This leads to the observed
TypeErrorduring shutdown
This issue does not occur with:
- Python threads
- QThreads without debugpy
- requests/urllib3 without debugpy
Only the combination of debugpy + QThread + urllib3 + Python 3.13 triggers it.
Possible Fix Direction
- Avoid calling
threading.current_thread()on non‑Python threads - Or guard
_create_thread_info()against QThreads - Or avoid creating
_DummyThreadobjects during sys‑monitoring - Or ensure
_DummyThreadis not left alive until interpreter teardown
Thank you
Happy to test patches or provide additional instrumentation if needed.