ソースを参照

showbase: Annotate basic `Messenger` methods (#1726)

WMOkiishi 8 ヶ月 前
コミット
5435579567
2 ファイル変更67 行追加24 行削除
  1. 14 5
      direct/src/showbase/DirectObject.py
  2. 53 19
      direct/src/showbase/Messenger.py

+ 14 - 5
direct/src/showbase/DirectObject.py

@@ -1,8 +1,14 @@
 """Defines the DirectObject class, a convenient class to inherit from if the
 object needs to be able to respond to events."""
 
+from __future__ import annotations
+
 __all__ = ['DirectObject']
 
+from typing import Callable
+
+from panda3d.core import AsyncTask
+
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.task.TaskManagerGlobal import taskMgr
 from .MessengerGlobal import messenger
@@ -12,6 +18,9 @@ class DirectObject:
     """
     This is the class that all Direct/SAL classes should inherit from
     """
+    _MSGRmessengerId: tuple[str, int]
+    _taskList: dict[int, AsyncTask]
+
     #def __del__(self):
         # This next line is useful for debugging leaks
         #print "Destructing: ", self.__class__.__name__
@@ -19,16 +28,16 @@ class DirectObject:
     # Wrapper functions to have a cleaner, more object oriented approach to
     # the messenger functionality.
 
-    def accept(self, event, method, extraArgs=[]):
-        return messenger.accept(event, self, method, extraArgs, 1)
+    def accept(self, event: str, method: Callable, extraArgs: list = []) -> None:
+        return messenger.accept(event, self, method, extraArgs, True)
 
     def acceptOnce(self, event, method, extraArgs=[]):
         return messenger.accept(event, self, method, extraArgs, 0)
 
-    def ignore(self, event):
+    def ignore(self, event: str) -> None:
         return messenger.ignore(event, self)
 
-    def ignoreAll(self):
+    def ignoreAll(self) -> None:
         return messenger.ignoreAll(self)
 
     def isAccepting(self, event):
@@ -41,7 +50,7 @@ class DirectObject:
         return messenger.isIgnoring(event, self)
 
     #This function must be used if you want a managed task
-    def addTask(self, *args, **kwargs):
+    def addTask(self, *args, **kwargs) -> AsyncTask:
         if not hasattr(self, "_taskList"):
             self._taskList = {}
         kwargs['owner'] = self

+ 53 - 19
direct/src/showbase/Messenger.py

@@ -2,19 +2,40 @@
 :ref:`event handling <event-handlers>` that happens on the Python side.
 """
 
+from __future__ import annotations
+
 __all__ = ['Messenger']
 
+import types
+from collections.abc import Callable
+from typing import Protocol
+# These can be replaced with their builtin counterparts once support for Python 3.8 is dropped.
+from typing import Dict, Tuple
+
+from panda3d.core import AsyncTask
+
 from direct.stdpy.threading import Lock
 from direct.directnotify import DirectNotifyGlobal
 from .PythonUtil import safeRepr
-import types
+
+# The following variables are typing constructs used in annotations
+# to succinctly express complex type structures.
+_ObjMsgrId = Tuple[str, int]
+_CallbackInfo = list  # [Callable, list, bool]
+_ListenerObject = list  # [int, DirectObject]
+_AcceptorDict = Dict[_ObjMsgrId, _CallbackInfo]
+_EventTuple = Tuple[_AcceptorDict, str, list, bool]
+
+
+class _HasMessengerID(Protocol):
+    _MSGRmessengerId: _ObjMsgrId
 
 
 class Messenger:
 
     notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
 
-    def __init__(self):
+    def __init__(self) -> None:
         """
         One is keyed off the event name. It has the following structure::
 
@@ -34,16 +55,16 @@ class Messenger:
             {'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
         """
         # eventName->objMsgrId->callbackInfo
-        self.__callbacks = {}
+        self.__callbacks: dict[str, _AcceptorDict] = {}
         # objMsgrId->set(eventName)
-        self.__objectEvents = {}
+        self.__objectEvents: dict[_ObjMsgrId, dict[str, None]] = {}
         self._messengerIdGen = 0
         # objMsgrId->listenerObject
-        self._id2object = {}
+        self._id2object: dict[_ObjMsgrId, _ListenerObject] = {}
 
         # A mapping of taskChain -> eventList, used for sending events
         # across task chains (and therefore across threads).
-        self._eventQueuesByTaskChain = {}
+        self._eventQueuesByTaskChain: dict[str, list[_EventTuple]] = {}
 
         # This protects the data structures within this object from
         # multithreaded access.
@@ -51,7 +72,7 @@ class Messenger:
 
         if __debug__:
             self.__isWatching=0
-            self.__watching={}
+            self.__watching: dict[str, bool] = {}
         # I'd like this to be in the __debug__, but I fear that someone will
         # want this in a release build.  If you're sure that that will not be
         # then please remove this comment and put the quiet/verbose stuff
@@ -62,7 +83,7 @@ class Messenger:
                        'collisionLoopFinished':1,
                        } # see def quiet()
 
-    def _getMessengerId(self, object):
+    def _getMessengerId(self, object: _HasMessengerID) -> _ObjMsgrId:
         # TODO: allocate this id in DirectObject.__init__ and get derived
         # classes to call down (speed optimization, assuming objects
         # accept/ignore more than once over their lifetime)
@@ -73,7 +94,7 @@ class Messenger:
             self._messengerIdGen += 1
         return object._MSGRmessengerId
 
-    def _storeObject(self, object):
+    def _storeObject(self, object: _HasMessengerID) -> None:
         # store reference-counted reference to object in case we need to
         # retrieve it later.  assumes lock is held.
         id = self._getMessengerId(object)
@@ -82,7 +103,7 @@ class Messenger:
         else:
             self._id2object[id][0] += 1
 
-    def _getObject(self, id):
+    def _getObject(self, id: _ObjMsgrId) -> _HasMessengerID:
         return self._id2object[id][1]
 
     def _getObjects(self):
@@ -101,7 +122,7 @@ class Messenger:
     def _getEvents(self):
         return list(self.__callbacks.keys())
 
-    def _releaseObject(self, object):
+    def _releaseObject(self, object: _HasMessengerID) -> None:
         # assumes lock is held.
         id = self._getMessengerId(object)
         if id in self._id2object:
@@ -117,7 +138,14 @@ class Messenger:
         from .EventManagerGlobal import eventMgr
         return eventMgr.eventHandler.get_future(event)
 
-    def accept(self, event, object, method, extraArgs=[], persistent=1):
+    def accept(
+        self,
+        event: str,
+        object: _HasMessengerID,
+        method: Callable,
+        extraArgs: list = [],
+        persistent: bool = True,
+    ) -> None:
         """ accept(self, string, DirectObject, Function, List, Boolean)
 
         Make this object accept this event. When the event is
@@ -174,7 +202,7 @@ class Messenger:
         finally:
             self.lock.release()
 
-    def ignore(self, event, object):
+    def ignore(self, event: str, object: _HasMessengerID) -> None:
         """ ignore(self, string, DirectObject)
         Make this object no longer respond to this event.
         It is safe to call even if it was not already accepting
@@ -208,7 +236,7 @@ class Messenger:
         finally:
             self.lock.release()
 
-    def ignoreAll(self, object):
+    def ignoreAll(self, object: _HasMessengerID) -> None:
         """
         Make this object no longer respond to any events it was accepting
         Useful for cleanup
@@ -283,7 +311,7 @@ class Messenger:
         """
         return not self.isAccepting(event, object)
 
-    def send(self, event, sentArgs=[], taskChain=None):
+    def send(self, event: str, sentArgs: list = [], taskChain: str | None = None) -> None:
         """
         Send this event, optionally passing in arguments.
 
@@ -305,12 +333,12 @@ class Messenger:
 
         self.lock.acquire()
         try:
-            foundWatch = 0
+            foundWatch = False
             if __debug__:
                 if self.__isWatching:
                     for i in self.__watching:
                         if str(event).find(i) >= 0:
-                            foundWatch = 1
+                            foundWatch = True
                             break
             acceptorDict = self.__callbacks.get(event)
             if not acceptorDict:
@@ -336,7 +364,7 @@ class Messenger:
         finally:
             self.lock.release()
 
-    def __taskChainDispatch(self, taskChain, task):
+    def __taskChainDispatch(self, taskChain: str, task: AsyncTask) -> int:
         """ This task is spawned each time an event is sent across
         task chains.  Its job is to empty the task events on the queue
         for this particular task chain.  This guarantees that events
@@ -365,7 +393,13 @@ class Messenger:
 
         return task.done
 
-    def __dispatch(self, acceptorDict, event, sentArgs, foundWatch):
+    def __dispatch(
+        self,
+        acceptorDict: _AcceptorDict,
+        event: str,
+        sentArgs: list,
+        foundWatch: bool,
+    ) -> None:
         for id in list(acceptorDict.keys()):
             # We have to make this apparently redundant check, because
             # it is possible that one object removes its own hooks