From 2eef53ab3d3d44191d88710256504ec2aab23e9d Mon Sep 17 00:00:00 2001 From: Pavel Karateev Date: Wed, 21 May 2025 21:58:39 +0200 Subject: [PATCH 1/2] add Python 3.14 compatible logic to check if a thread is alive --- _pydev_bundle/pydev_is_thread_alive.py | 7 ++++++- _pydevd_sys_monitoring/_pydevd_sys_monitoring.py | 10 +++------- .../_pydevd_sys_monitoring_cython.pyx | 10 +++------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/_pydev_bundle/pydev_is_thread_alive.py b/_pydev_bundle/pydev_is_thread_alive.py index 3f2483a1..e2887f51 100644 --- a/_pydev_bundle/pydev_is_thread_alive.py +++ b/_pydev_bundle/pydev_is_thread_alive.py @@ -5,7 +5,12 @@ # It is required to debug threads started by start_new_thread in Python 3.4 _temp = threading.Thread() -if hasattr(_temp, "_handle") and hasattr(_temp, "_started"): # Python 3.13 and later has this +if hasattr(_temp, "_os_thread_handle") and hasattr(_temp, "_started"): # Python 3.14 has no `_handle` + + def is_thread_alive(t): + return not t._os_thread_handle.is_done() + +elif hasattr(_temp, "_handle") and hasattr(_temp, "_started"): # Python 3.13 and later has this def is_thread_alive(t): return not t._handle.is_done() diff --git a/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py b/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py index 5d97a67d..5c8ca4fb 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py @@ -13,6 +13,7 @@ from os.path import basename, splitext from _pydev_bundle import pydev_log +from _pydev_bundle.pydev_is_thread_alive import is_thread_alive from _pydevd_bundle import pydevd_dont_trace from _pydevd_bundle.pydevd_constants import ( IS_PY313_OR_GREATER, @@ -255,7 +256,6 @@ def _get_unhandled_exception_frame(exc, depth: int) -> Optional[FrameType]: # cdef PyDBAdditionalThreadInfo additional_info # thread: threading.Thread # trace: bool -# _use_is_stopped: bool # ELSE class ThreadInfo: additional_info: PyDBAdditionalThreadInfo @@ -276,8 +276,7 @@ def __init__(self, thread: threading.Thread, thread_ident: int, trace: bool, add self.thread_ident = thread_ident self.additional_info = additional_info self.trace = trace - self._use_is_stopped = hasattr(thread, '_is_stopped') - + # fmt: off # IFDEF CYTHON # cdef bint is_thread_alive(self): @@ -285,10 +284,7 @@ def __init__(self, thread: threading.Thread, thread_ident: int, trace: bool, add def is_thread_alive(self): # ENDIF # fmt: on - if self._use_is_stopped: - return not self.thread._is_stopped - else: - return not self.thread._handle.is_done() + return is_thread_alive(self.thread) class _DeleteDummyThreadOnDel: diff --git a/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx b/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx index 94e70e71..9f41f482 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx @@ -19,6 +19,7 @@ from typing import Dict, Optional, Tuple, Any from os.path import basename, splitext from _pydev_bundle import pydev_log +from _pydev_bundle.pydev_is_thread_alive import is_thread_alive from _pydevd_bundle import pydevd_dont_trace from _pydevd_bundle.pydevd_constants import ( IS_PY313_OR_GREATER, @@ -247,7 +248,6 @@ cdef class ThreadInfo: cdef PyDBAdditionalThreadInfo additional_info thread: threading.Thread trace: bool - _use_is_stopped: bool # ELSE # class ThreadInfo: # additional_info: PyDBAdditionalThreadInfo @@ -268,8 +268,7 @@ cdef class ThreadInfo: self.thread_ident = thread_ident self.additional_info = additional_info self.trace = trace - self._use_is_stopped = hasattr(thread, '_is_stopped') - + # fmt: off # IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated) cdef bint is_thread_alive(self): @@ -277,10 +276,7 @@ cdef class ThreadInfo: # def is_thread_alive(self): # ENDIF # fmt: on - if self._use_is_stopped: - return not self.thread._is_stopped - else: - return not self.thread._handle.is_done() + return is_thread_alive(self.thread) class _DeleteDummyThreadOnDel: From 5142a1ce6bd25e77b1699b735e24afc0efc6d134 Mon Sep 17 00:00:00 2001 From: Pavel Karateev Date: Thu, 22 May 2025 11:21:08 +0200 Subject: [PATCH 2/2] optimize checking thread alive status - don't import extra pure-Python code - cache `hasattr` calls --- .../_pydevd_sys_monitoring.py | 17 +++++++++++++++-- .../_pydevd_sys_monitoring_cython.pyx | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py b/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py index 5c8ca4fb..00c2c32c 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py @@ -13,7 +13,6 @@ from os.path import basename, splitext from _pydev_bundle import pydev_log -from _pydev_bundle.pydev_is_thread_alive import is_thread_alive from _pydevd_bundle import pydevd_dont_trace from _pydevd_bundle.pydevd_constants import ( IS_PY313_OR_GREATER, @@ -277,6 +276,10 @@ def __init__(self, thread: threading.Thread, thread_ident: int, trace: bool, add self.additional_info = additional_info self.trace = trace + self._use_handle = hasattr(thread, "_handle") + self._use_started = hasattr(thread, "_started") + self._use_os_thread_handle = hasattr(thread, "_os_thread_handle") + # fmt: off # IFDEF CYTHON # cdef bint is_thread_alive(self): @@ -284,7 +287,17 @@ def __init__(self, thread: threading.Thread, thread_ident: int, trace: bool, add def is_thread_alive(self): # ENDIF # fmt: on - return is_thread_alive(self.thread) + # Python 3.14 + if self._use_os_thread_handle and self._use_started: + return not self.thread._os_thread_handle.is_done() + + # Python 3.13 + elif self._use_handle and self._use_started: + return not self.thread._handle.is_done() + + # Python 3.12 + else: + return not self.thread._is_stopped class _DeleteDummyThreadOnDel: diff --git a/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx b/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx index 9f41f482..1afae88a 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx @@ -19,7 +19,6 @@ from typing import Dict, Optional, Tuple, Any from os.path import basename, splitext from _pydev_bundle import pydev_log -from _pydev_bundle.pydev_is_thread_alive import is_thread_alive from _pydevd_bundle import pydevd_dont_trace from _pydevd_bundle.pydevd_constants import ( IS_PY313_OR_GREATER, @@ -269,6 +268,10 @@ cdef class ThreadInfo: self.additional_info = additional_info self.trace = trace + self._use_handle = hasattr(thread, "_handle") + self._use_started = hasattr(thread, "_started") + self._use_os_thread_handle = hasattr(thread, "_os_thread_handle") + # fmt: off # IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated) cdef bint is_thread_alive(self): @@ -276,7 +279,17 @@ cdef class ThreadInfo: # def is_thread_alive(self): # ENDIF # fmt: on - return is_thread_alive(self.thread) + # Python 3.14 + if self._use_os_thread_handle and self._use_started: + return not self.thread._os_thread_handle.is_done() + + # Python 3.13 + elif self._use_handle and self._use_started: + return not self.thread._handle.is_done() + + # Python 3.12 + else: + return not self.thread._is_stopped class _DeleteDummyThreadOnDel: