It is definitely possible to implement the Thread.stop method, as shown in the following code example:
import threading import sys class StopThread(StopIteration): pass threading.SystemExit = SystemExit, StopThread class Thread2(threading.Thread): def stop(self): self.__stop = True def _bootstrap(self): if threading._trace_hook is not None: raise ValueError('Cannot run thread with tracing!') self.__stop = False sys.settrace(self.__trace) super()._bootstrap() def __trace(self, frame, event, arg): if self.__stop: raise StopThread() return self.__trace class Thread3(threading.Thread): def _bootstrap(self, stop_thread=False): def stop(): nonlocal stop_thread stop_thread = True self.stop = stop def tracer(*_): if stop_thread: raise StopThread() return tracer sys.settrace(tracer) super()._bootstrap()
It appears that the Thread3 class executes code approximately 33% faster than the Thread2 class.
Application:
With enough knowledge of the Python C API and using the ctypes module, ctypes can write a much more efficient way to stop the stream if desired. The problem with using sys.settrace is that the trace function starts after each statement. If instead an asynchronous exception occurs in the thread to be aborted, no penalty for execution speed is imposed. The following code provides some flexibility in this regard:
#! /usr/bin/env python3 import _thread import ctypes as _ctypes import threading as _threading _PyThreadState_SetAsyncExc = _ctypes.pythonapi.PyThreadState_SetAsyncExc # noinspection SpellCheckingInspection _PyThreadState_SetAsyncExc.argtypes = _ctypes.c_ulong, _ctypes.py_object _PyThreadState_SetAsyncExc.restype = _ctypes.c_int # noinspection PyUnreachableCode if __debug__: # noinspection PyShadowingBuiltins def _set_async_exc(id, exc): if not isinstance(id, int): raise TypeError(f'{id!r} not an int instance') if not isinstance(exc, type): raise TypeError(f'{exc!r} not a type instance') if not issubclass(exc, BaseException): raise SystemError(f'{exc!r} not a BaseException subclass') return _PyThreadState_SetAsyncExc(id, exc) else: _set_async_exc = _PyThreadState_SetAsyncExc # noinspection PyShadowingBuiltins def set_async_exc(id, exc, *args): if args: class StateInfo(exc): def __init__(self): super().__init__(*args) return _set_async_exc(id, StateInfo) return _set_async_exc(id, exc) def interrupt(ident=None): if ident is None: _thread.interrupt_main() else: set_async_exc(ident, KeyboardInterrupt) # noinspection PyShadowingBuiltins def exit(ident=None): if ident is None: _thread.exit() else: set_async_exc(ident, SystemExit) class ThreadAbortException(SystemExit): pass class Thread(_threading.Thread): def set_async_exc(self, exc, *args): return set_async_exc(self.ident, exc, *args) def interrupt(self): self.set_async_exc(KeyboardInterrupt) def exit(self): self.set_async_exc(SystemExit) def abort(self, *args): self.set_async_exc(ThreadAbortException, *args)
Noctis skytower
source share