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..00c2c32c 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring.py @@ -255,7 +255,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 +275,11 @@ 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') - + + 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): @@ -285,11 +287,18 @@ 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: + # 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 94e70e71..1afae88a 100644 --- a/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx +++ b/_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx @@ -247,7 +247,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 +267,11 @@ cdef class ThreadInfo: self.thread_ident = thread_ident self.additional_info = additional_info self.trace = trace - self._use_is_stopped = hasattr(thread, '_is_stopped') - + + 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): @@ -277,11 +279,18 @@ cdef class ThreadInfo: # def is_thread_alive(self): # ENDIF # fmt: on - if self._use_is_stopped: - return not self.thread._is_stopped - else: + # 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: """