Răsfoiți Sursa

stdpy: Annotate threading functions (#1765)

WMOkiishi 4 luni în urmă
părinte
comite
0e1a3f2ab7
3 a modificat fișierele cu 107 adăugiri și 67 ștergeri
  1. 20 10
      direct/src/stdpy/thread.py
  2. 32 14
      direct/src/stdpy/threading.py
  3. 55 43
      direct/src/stdpy/threading2.py

+ 20 - 10
direct/src/stdpy/thread.py

@@ -5,6 +5,8 @@ in some compilation models, Panda's threading constructs are
 incompatible with the OS-provided threads used by Python's thread
 module. """
 
+from __future__ import annotations
+
 __all__ = [
     'error', 'LockType',
     'start_new_thread',
@@ -19,6 +21,9 @@ __all__ = [
 from panda3d import core
 import sys
 
+from collections.abc import Callable, Iterable, Mapping
+from typing import Any
+
 if sys.platform == "win32":
     TIMEOUT_MAX = float(0xffffffff // 1000)
 else:
@@ -41,12 +46,12 @@ class LockType:
     allows a different thread to release the lock than the one that
     acquired it. """
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.__lock = core.Mutex('PythonLock')
         self.__cvar = core.ConditionVar(self.__lock)
         self.__locked = False
 
-    def acquire(self, waitflag = 1, timeout = -1):
+    def acquire(self, waitflag: bool = True, timeout: float = -1) -> bool:
         self.__lock.acquire()
         try:
             if self.__locked and not waitflag:
@@ -65,7 +70,7 @@ class LockType:
         finally:
             self.__lock.release()
 
-    def release(self):
+    def release(self) -> None:
         self.__lock.acquire()
         try:
             if not self.__locked:
@@ -90,18 +95,23 @@ class LockType:
 _counter = 0
 
 
-def _newname(template="Thread-%d"):
+def _newname(template: str = "Thread-%d") -> str:
     global _counter
     _counter = _counter + 1
     return template % _counter
 
 
-_threads = {}
+_threads: dict[int, tuple[core.Thread, dict[int, dict[str, Any]], Any | None]] = {}
 _nextThreadId = 0
 _threadsLock = core.Mutex('thread._threadsLock')
 
 
-def start_new_thread(function, args, kwargs = {}, name = None):
+def start_new_thread(
+    function: Callable[..., object],
+    args: Iterable[Any],
+    kwargs: Mapping[str, Any] = {},
+    name: str | None = None,
+) -> int:
     def threadFunc(threadId, function = function, args = args, kwargs = kwargs):
         try:
             try:
@@ -132,7 +142,7 @@ def start_new_thread(function, args, kwargs = {}, name = None):
         _threadsLock.release()
 
 
-def _add_thread(thread, wrapper):
+def _add_thread(thread: core.Thread, wrapper: Any) -> int:
     """ Adds the indicated core.Thread object, with the indicated Python
     wrapper, to the thread list.  Returns the new thread ID. """
 
@@ -150,7 +160,7 @@ def _add_thread(thread, wrapper):
         _threadsLock.release()
 
 
-def _get_thread_wrapper(thread, wrapperClass):
+def _get_thread_wrapper(thread: core.Thread, wrapperClass: Callable[[core.Thread, int], Any]) -> Any:
     """ Returns the thread wrapper for the indicated thread.  If there
     is not one, creates an instance of the indicated wrapperClass
     instead. """
@@ -222,7 +232,7 @@ def _get_thread_locals(thread, i):
             _threadsLock.release()
 
 
-def _remove_thread_id(threadId):
+def _remove_thread_id(threadId: int) -> None:
     """ Removes the thread with the indicated ID from the thread list. """
 
     # On interpreter shutdown, Python may set module globals to None.
@@ -254,7 +264,7 @@ def allocate_lock() -> LockType:
     return LockType()
 
 
-def get_ident():
+def get_ident() -> int:
     return core.Thread.getCurrentThread().this
 
 

+ 32 - 14
direct/src/stdpy/threading.py

@@ -21,12 +21,17 @@ easier to use and understand.
 It is permissible to mix-and-match both threading and threading2
 within the same application. """
 
+from __future__ import annotations
+
 from panda3d import core
 from direct.stdpy import thread as _thread
 import sys as _sys
 
 import weakref
 
+from collections.abc import Callable, Iterable, Mapping
+from typing import Any, NoReturn
+
 __all__ = [
     'Thread',
     'Lock', 'RLock',
@@ -54,10 +59,14 @@ class ThreadBase:
     """ A base class for both Thread and ExternalThread in this
     module. """
 
-    def __init__(self):
+    name: str
+    ident: int
+    daemon: bool
+
+    def __init__(self) -> None:
         pass
 
-    def getName(self):
+    def getName(self) -> str:
         return self.name
 
     def isDaemon(self):
@@ -92,7 +101,15 @@ class Thread(ThreadBase):
     object.  The wrapper is designed to emulate Python's own
     threading.Thread object. """
 
-    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None):
+    def __init__(
+        self,
+        group: None = None,
+        target: Callable[..., object] | None = None,
+        name: str | None = None,
+        args: Iterable[Any] = (),
+        kwargs: Mapping[str, Any] = {},
+        daemon: bool | None = None,
+    ) -> None:
         ThreadBase.__init__(self)
 
         assert group is None
@@ -131,7 +148,7 @@ class Thread(ThreadBase):
 
     isAlive = is_alive
 
-    def start(self):
+    def start(self) -> None:
         thread = self.__thread
         if thread is None or thread.is_started():
             raise RuntimeError
@@ -147,7 +164,7 @@ class Thread(ThreadBase):
 
         self.__target(*self.__args, **self.__kwargs)
 
-    def join(self, timeout = None):
+    def join(self, timeout: float | None = None) -> None:
         # We don't support a timed join here, sorry.
         assert timeout is None
         thread = self.__thread
@@ -157,7 +174,7 @@ class Thread(ThreadBase):
             self.__thread = None
             _thread._remove_thread_id(self.ident)
 
-    def setName(self, name):
+    def setName(self, name: str) -> None:
         self.__dict__['name'] = name
         self.__thread.setName(name)
 
@@ -166,7 +183,7 @@ class ExternalThread(ThreadBase):
     """ Returned for a Thread object that wasn't created by this
     interface. """
 
-    def __init__(self, extThread, threadId):
+    def __init__(self, extThread: core.Thread, threadId: int) -> None:
         ThreadBase.__init__(self)
 
         self.__thread = extThread
@@ -196,7 +213,7 @@ class ExternalThread(ThreadBase):
 class MainThread(ExternalThread):
     """ Returned for the MainThread object. """
 
-    def __init__(self, extThread, threadId):
+    def __init__(self, extThread: core.Thread, threadId: int) -> None:
         ExternalThread.__init__(self, extThread, threadId)
         self.__dict__['daemon'] = False
 
@@ -206,7 +223,7 @@ class Lock(core.Mutex):
     The wrapper is designed to emulate Python's own threading.Lock
     object. """
 
-    def __init__(self, name = "PythonLock"):
+    def __init__(self, name: str = "PythonLock") -> None:
         core.Mutex.__init__(self, name)
 
 
@@ -224,7 +241,7 @@ class Condition(core.ConditionVar):
     object.  The wrapper is designed to emulate Python's own
     threading.Condition object. """
 
-    def __init__(self, lock = None):
+    def __init__(self, lock: Lock | RLock | None = None) -> None:
         if not lock:
             lock = Lock()
 
@@ -241,7 +258,7 @@ class Condition(core.ConditionVar):
     def release(self):
         self.__lock.release()
 
-    def wait(self, timeout = None):
+    def wait(self, timeout: float | None = None) -> None:
         if timeout is None:
             core.ConditionVar.wait(self)
         else:
@@ -373,8 +390,9 @@ class Timer(Thread):
         self.finished.set()
 
 
-def _create_thread_wrapper(t, threadId):
+def _create_thread_wrapper(t: core.Thread, threadId: int) -> ExternalThread:
     """ Creates a thread wrapper for the indicated external thread. """
+    pyt: ExternalThread
     if isinstance(t, core.MainThread):
         pyt = MainThread(t, threadId)
     else:
@@ -383,7 +401,7 @@ def _create_thread_wrapper(t, threadId):
     return pyt
 
 
-def current_thread():
+def current_thread() -> ThreadBase:
     t = core.Thread.getCurrentThread()
     return _thread._get_thread_wrapper(t, _create_thread_wrapper)
 
@@ -430,5 +448,5 @@ def setprofile(func):
     _setprofile_func = func
 
 
-def stack_size(size = None):
+def stack_size(size: object = None) -> NoReturn:
     raise ThreadError

+ 55 - 43
direct/src/stdpy/threading2.py

@@ -13,6 +13,8 @@ to import Panda's thread reimplementation instead of the system thread
 module, and so it is therefore layered on top of Panda's thread
 implementation. """
 
+from __future__ import annotations
+
 import sys as _sys
 import atexit as _atexit
 
@@ -21,8 +23,10 @@ from direct.stdpy.thread import stack_size, _newname, _local as local
 from panda3d import core
 _sleep = core.Thread.sleep
 
+from collections.abc import Callable, Iterable, Mapping
 from time import time as _time
 from traceback import format_exc as _format_exc
+from typing import Any
 
 __all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
            'enumerate', 'main_thread', 'TIMEOUT_MAX',
@@ -51,12 +55,12 @@ if __debug__:
 
     class _Verbose(object):
 
-        def __init__(self, verbose=None):
+        def __init__(self, verbose: bool | None = None) -> None:
             if verbose is None:
                 verbose = _VERBOSE
             self.__verbose = verbose
 
-        def _note(self, format, *args):
+        def _note(self, format: str, *args: Any) -> None:
             if self.__verbose:
                 format = format % args
                 format = "%s: %s\n" % (
@@ -66,9 +70,9 @@ if __debug__:
 else:
     # Disable this when using "python -O"
     class _Verbose(object):  # type: ignore[no-redef]
-        def __init__(self, verbose=None):
+        def __init__(self, verbose: bool | None = None) -> None:
             pass
-        def _note(self, *args):
+        def _note(self, *args) -> None:
             pass
 
 # Support for profile and trace hooks
@@ -88,15 +92,15 @@ def settrace(func):
 
 Lock = _allocate_lock
 
-def RLock(*args, **kwargs):
-    return _RLock(*args, **kwargs)
+def RLock(verbose: bool | None = None) -> _RLock:
+    return _RLock(verbose)
 
 class _RLock(_Verbose):
 
-    def __init__(self, verbose=None):
+    def __init__(self, verbose: bool | None = None) -> None:
         _Verbose.__init__(self, verbose)
         self.__block = _allocate_lock()
-        self.__owner = None
+        self.__owner: Thread | None = None
         self.__count = 0
 
     def __repr__(self):
@@ -105,13 +109,13 @@ class _RLock(_Verbose):
                 self.__owner and self.__owner.getName(),
                 self.__count)
 
-    def acquire(self, blocking=1):
+    def acquire(self, blocking: bool = True) -> bool:
         me = currentThread()
         if self.__owner is me:
             self.__count = self.__count + 1
             if __debug__:
                 self._note("%s.acquire(%s): recursive success", self, blocking)
-            return 1
+            return True
         rc = self.__block.acquire(blocking)
         if rc:
             self.__owner = me
@@ -125,7 +129,7 @@ class _RLock(_Verbose):
 
     __enter__ = acquire
 
-    def release(self):
+    def release(self) -> None:
         me = currentThread()
         assert self.__owner is me, "release() of un-acquire()d lock"
         self.__count = count = self.__count - 1
@@ -163,12 +167,12 @@ class _RLock(_Verbose):
         return self.__owner is currentThread()
 
 
-def Condition(*args, **kwargs):
-    return _Condition(*args, **kwargs)
+def Condition(lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> _Condition:
+    return _Condition(lock, verbose)
 
 class _Condition(_Verbose):
 
-    def __init__(self, lock=None, verbose=None):
+    def __init__(self, lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> None:
         _Verbose.__init__(self, verbose)
         if lock is None:
             lock = RLock()
@@ -180,18 +184,18 @@ class _Condition(_Verbose):
         # these override the default implementations (which just call
         # release() and acquire() on the lock).  Ditto for _is_owned().
         try:
-            self._release_save = lock._release_save
+            self._release_save = lock._release_save  # type: ignore[method-assign, union-attr]
         except AttributeError:
             pass
         try:
-            self._acquire_restore = lock._acquire_restore
+            self._acquire_restore = lock._acquire_restore  # type: ignore[method-assign, union-attr]
         except AttributeError:
             pass
         try:
-            self._is_owned = lock._is_owned
+            self._is_owned = lock._is_owned  # type: ignore[method-assign, union-attr]
         except AttributeError:
             pass
-        self.__waiters = []
+        self.__waiters: list[_thread.LockType] = []
 
     def __enter__(self):
         return self.__lock.__enter__()
@@ -202,22 +206,22 @@ class _Condition(_Verbose):
     def __repr__(self):
         return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
 
-    def _release_save(self): # pylint: disable=method-hidden
+    def _release_save(self) -> Any: # pylint: disable=method-hidden
         self.__lock.release()           # No state to save
 
-    def _acquire_restore(self, x): # pylint: disable=method-hidden
+    def _acquire_restore(self, x) -> None: # pylint: disable=method-hidden
         self.__lock.acquire()           # Ignore saved state
 
-    def _is_owned(self): # pylint: disable=method-hidden
+    def _is_owned(self) -> bool: # pylint: disable=method-hidden
         # Return True if lock is owned by currentThread.
         # This method is called only if __lock doesn't have _is_owned().
-        if self.__lock.acquire(0):
+        if self.__lock.acquire(False):
             self.__lock.release()
             return False
         else:
             return True
 
-    def wait(self, timeout=None):
+    def wait(self, timeout: float | None = None) -> None:
         assert self._is_owned(), "wait() of un-acquire()d lock"
         waiter = _allocate_lock()
         waiter.acquire()
@@ -237,7 +241,7 @@ class _Condition(_Verbose):
                 endtime = _time() + timeout
                 delay = 0.0005 # 500 us -> initial delay of 1 ms
                 while True:
-                    gotit = waiter.acquire(0)
+                    gotit = waiter.acquire(False)
                     if gotit:
                         break
                     remaining = endtime - _time()
@@ -258,7 +262,7 @@ class _Condition(_Verbose):
         finally:
             self._acquire_restore(saved_state)
 
-    def notify(self, n=1):
+    def notify(self, n: int = 1) -> None:
         assert self._is_owned(), "notify() of un-acquire()d lock"
         __waiters = self.__waiters
         waiters = __waiters[:n]
@@ -275,7 +279,7 @@ class _Condition(_Verbose):
             except ValueError:
                 pass
 
-    def notifyAll(self):
+    def notifyAll(self) -> None:
         self.notify(len(self.__waiters))
 
 
@@ -381,7 +385,7 @@ class _Event(_Verbose):
 
 # Active thread administration
 _active_limbo_lock = _allocate_lock()
-_active = {}    # maps thread id to Thread object
+_active: dict[int, Thread] = {}    # maps thread id to Thread object
 _limbo = {}
 
 
@@ -400,8 +404,16 @@ class Thread(_Verbose):
     # Protected by _active_limbo_lock.
     __registered_atexit = False
 
-    def __init__(self, group=None, target=None, name=None,
-                 args=(), kwargs=None, verbose=None, daemon=None):
+    def __init__(
+        self,
+        group: None = None,
+        target: Callable[..., object] | None = None,
+        name: object = None,
+        args: Iterable[Any] = (),
+        kwargs: Mapping[str, Any] | None = None,
+        verbose: bool | None = None,
+        daemon: bool | None = None,
+    ) -> None:
         assert group is None, "group argument must be None for now"
         _Verbose.__init__(self, verbose)
         if kwargs is None:
@@ -422,7 +434,7 @@ class Thread(_Verbose):
         # sys.exc_info since it can be changed between instances
         self.__stderr = _sys.stderr
 
-    def _set_daemon(self):
+    def _set_daemon(self) -> bool:
         # Overridden in _MainThread and _DummyThread
         return currentThread().isDaemon()
 
@@ -437,7 +449,7 @@ class Thread(_Verbose):
             status = status + " daemon"
         return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
 
-    def start(self):
+    def start(self) -> None:
         assert self.__initialized, "Thread.__init__() not called"
         assert not self.__started, "thread already started"
         if __debug__:
@@ -457,11 +469,11 @@ class Thread(_Verbose):
         self.__started = True
         _sleep(0.000001)    # 1 usec, to let the thread run (Solaris hack)
 
-    def run(self):
+    def run(self) -> None:
         if self.__target:
             self.__target(*self.__args, **self.__kwargs)
 
-    def __bootstrap(self):
+    def __bootstrap(self) -> None:
         try:
             self.__started = True
             _active_limbo_lock.acquire()
@@ -497,7 +509,7 @@ class Thread(_Verbose):
                     # Do the best job possible w/o a huge amt. of code to
                     # approximate a traceback (code ideas from
                     # Lib/traceback.py)
-                    exc_type, exc_value, exc_tb = self.__exc_info()
+                    exc_type, exc_value, exc_tb = self.__exc_info()  # type: ignore[misc]
                     try:
                         self.__stderr.write("Exception in thread " + self.getName() +
                             " (most likely raised during interpreter shutdown):\n")
@@ -523,13 +535,13 @@ class Thread(_Verbose):
             except:
                 pass
 
-    def __stop(self):
+    def __stop(self) -> None:
         self.__block.acquire()
         self.__stopped = True
         self.__block.notifyAll()
         self.__block.release()
 
-    def __delete(self):
+    def __delete(self) -> None:
         "Remove current thread from the dict of currently running threads."
 
         # Notes about running with dummy_thread:
@@ -563,7 +575,7 @@ class Thread(_Verbose):
         finally:
             _active_limbo_lock.release()
 
-    def join(self, timeout=None):
+    def join(self, timeout: float | None = None) -> None:
         assert self.__initialized, "Thread.__init__() not called"
         assert self.__started, "cannot join thread before it is started"
         assert self is not currentThread(), "cannot join current thread"
@@ -592,11 +604,11 @@ class Thread(_Verbose):
         finally:
             self.__block.release()
 
-    def getName(self):
+    def getName(self) -> str:
         assert self.__initialized, "Thread.__init__() not called"
         return self.__name
 
-    def setName(self, name):
+    def setName(self, name: object) -> None:
         assert self.__initialized, "Thread.__init__() not called"
         self.__name = str(name)
 
@@ -606,7 +618,7 @@ class Thread(_Verbose):
 
     isAlive = is_alive
 
-    def isDaemon(self):
+    def isDaemon(self) -> bool:
         assert self.__initialized, "Thread.__init__() not called"
         return self.__daemonic
 
@@ -688,13 +700,13 @@ class _MainThread(Thread):
 
 class _DummyThread(Thread):
 
-    def __init__(self):
+    def __init__(self) -> None:
         Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True)
 
         # Thread.__block consumes an OS-level locking primitive, which
         # can never be used by a _DummyThread.  Since a _DummyThread
         # instance is immortal, that's bad, so release this resource.
-        del self._Thread__block
+        del self._Thread__block  # type: ignore[attr-defined]
 
         self._Thread__started = True
         _active_limbo_lock.acquire()
@@ -710,7 +722,7 @@ class _DummyThread(Thread):
 
 # Global API functions
 
-def current_thread():
+def current_thread() -> Thread:
     try:
         return _active[get_ident()]
     except KeyError: