Browse Source

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

WMOkiishi 8 months ago
parent
commit
5435579567
2 changed files with 67 additions and 24 deletions
  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
 """Defines the DirectObject class, a convenient class to inherit from if the
 object needs to be able to respond to events."""
 object needs to be able to respond to events."""
 
 
+from __future__ import annotations
+
 __all__ = ['DirectObject']
 __all__ = ['DirectObject']
 
 
+from typing import Callable
+
+from panda3d.core import AsyncTask
+
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.task.TaskManagerGlobal import taskMgr
 from .MessengerGlobal import messenger
 from .MessengerGlobal import messenger
@@ -12,6 +18,9 @@ class DirectObject:
     """
     """
     This is the class that all Direct/SAL classes should inherit from
     This is the class that all Direct/SAL classes should inherit from
     """
     """
+    _MSGRmessengerId: tuple[str, int]
+    _taskList: dict[int, AsyncTask]
+
     #def __del__(self):
     #def __del__(self):
         # This next line is useful for debugging leaks
         # This next line is useful for debugging leaks
         #print "Destructing: ", self.__class__.__name__
         #print "Destructing: ", self.__class__.__name__
@@ -19,16 +28,16 @@ class DirectObject:
     # Wrapper functions to have a cleaner, more object oriented approach to
     # Wrapper functions to have a cleaner, more object oriented approach to
     # the messenger functionality.
     # 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=[]):
     def acceptOnce(self, event, method, extraArgs=[]):
         return messenger.accept(event, self, method, extraArgs, 0)
         return messenger.accept(event, self, method, extraArgs, 0)
 
 
-    def ignore(self, event):
+    def ignore(self, event: str) -> None:
         return messenger.ignore(event, self)
         return messenger.ignore(event, self)
 
 
-    def ignoreAll(self):
+    def ignoreAll(self) -> None:
         return messenger.ignoreAll(self)
         return messenger.ignoreAll(self)
 
 
     def isAccepting(self, event):
     def isAccepting(self, event):
@@ -41,7 +50,7 @@ class DirectObject:
         return messenger.isIgnoring(event, self)
         return messenger.isIgnoring(event, self)
 
 
     #This function must be used if you want a managed task
     #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"):
         if not hasattr(self, "_taskList"):
             self._taskList = {}
             self._taskList = {}
         kwargs['owner'] = self
         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.
 :ref:`event handling <event-handlers>` that happens on the Python side.
 """
 """
 
 
+from __future__ import annotations
+
 __all__ = ['Messenger']
 __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.stdpy.threading import Lock
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from .PythonUtil import safeRepr
 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:
 class Messenger:
 
 
     notify = DirectNotifyGlobal.directNotify.newCategory("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::
         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]}}
             {'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
         """
         """
         # eventName->objMsgrId->callbackInfo
         # eventName->objMsgrId->callbackInfo
-        self.__callbacks = {}
+        self.__callbacks: dict[str, _AcceptorDict] = {}
         # objMsgrId->set(eventName)
         # objMsgrId->set(eventName)
-        self.__objectEvents = {}
+        self.__objectEvents: dict[_ObjMsgrId, dict[str, None]] = {}
         self._messengerIdGen = 0
         self._messengerIdGen = 0
         # objMsgrId->listenerObject
         # objMsgrId->listenerObject
-        self._id2object = {}
+        self._id2object: dict[_ObjMsgrId, _ListenerObject] = {}
 
 
         # A mapping of taskChain -> eventList, used for sending events
         # A mapping of taskChain -> eventList, used for sending events
         # across task chains (and therefore across threads).
         # across task chains (and therefore across threads).
-        self._eventQueuesByTaskChain = {}
+        self._eventQueuesByTaskChain: dict[str, list[_EventTuple]] = {}
 
 
         # This protects the data structures within this object from
         # This protects the data structures within this object from
         # multithreaded access.
         # multithreaded access.
@@ -51,7 +72,7 @@ class Messenger:
 
 
         if __debug__:
         if __debug__:
             self.__isWatching=0
             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
         # 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
         # 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
         # then please remove this comment and put the quiet/verbose stuff
@@ -62,7 +83,7 @@ class Messenger:
                        'collisionLoopFinished':1,
                        'collisionLoopFinished':1,
                        } # see def quiet()
                        } # see def quiet()
 
 
-    def _getMessengerId(self, object):
+    def _getMessengerId(self, object: _HasMessengerID) -> _ObjMsgrId:
         # TODO: allocate this id in DirectObject.__init__ and get derived
         # TODO: allocate this id in DirectObject.__init__ and get derived
         # classes to call down (speed optimization, assuming objects
         # classes to call down (speed optimization, assuming objects
         # accept/ignore more than once over their lifetime)
         # accept/ignore more than once over their lifetime)
@@ -73,7 +94,7 @@ class Messenger:
             self._messengerIdGen += 1
             self._messengerIdGen += 1
         return object._MSGRmessengerId
         return object._MSGRmessengerId
 
 
-    def _storeObject(self, object):
+    def _storeObject(self, object: _HasMessengerID) -> None:
         # store reference-counted reference to object in case we need to
         # store reference-counted reference to object in case we need to
         # retrieve it later.  assumes lock is held.
         # retrieve it later.  assumes lock is held.
         id = self._getMessengerId(object)
         id = self._getMessengerId(object)
@@ -82,7 +103,7 @@ class Messenger:
         else:
         else:
             self._id2object[id][0] += 1
             self._id2object[id][0] += 1
 
 
-    def _getObject(self, id):
+    def _getObject(self, id: _ObjMsgrId) -> _HasMessengerID:
         return self._id2object[id][1]
         return self._id2object[id][1]
 
 
     def _getObjects(self):
     def _getObjects(self):
@@ -101,7 +122,7 @@ class Messenger:
     def _getEvents(self):
     def _getEvents(self):
         return list(self.__callbacks.keys())
         return list(self.__callbacks.keys())
 
 
-    def _releaseObject(self, object):
+    def _releaseObject(self, object: _HasMessengerID) -> None:
         # assumes lock is held.
         # assumes lock is held.
         id = self._getMessengerId(object)
         id = self._getMessengerId(object)
         if id in self._id2object:
         if id in self._id2object:
@@ -117,7 +138,14 @@ class Messenger:
         from .EventManagerGlobal import eventMgr
         from .EventManagerGlobal import eventMgr
         return eventMgr.eventHandler.get_future(event)
         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)
         """ accept(self, string, DirectObject, Function, List, Boolean)
 
 
         Make this object accept this event. When the event is
         Make this object accept this event. When the event is
@@ -174,7 +202,7 @@ class Messenger:
         finally:
         finally:
             self.lock.release()
             self.lock.release()
 
 
-    def ignore(self, event, object):
+    def ignore(self, event: str, object: _HasMessengerID) -> None:
         """ ignore(self, string, DirectObject)
         """ ignore(self, string, DirectObject)
         Make this object no longer respond to this event.
         Make this object no longer respond to this event.
         It is safe to call even if it was not already accepting
         It is safe to call even if it was not already accepting
@@ -208,7 +236,7 @@ class Messenger:
         finally:
         finally:
             self.lock.release()
             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
         Make this object no longer respond to any events it was accepting
         Useful for cleanup
         Useful for cleanup
@@ -283,7 +311,7 @@ class Messenger:
         """
         """
         return not self.isAccepting(event, object)
         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.
         Send this event, optionally passing in arguments.
 
 
@@ -305,12 +333,12 @@ class Messenger:
 
 
         self.lock.acquire()
         self.lock.acquire()
         try:
         try:
-            foundWatch = 0
+            foundWatch = False
             if __debug__:
             if __debug__:
                 if self.__isWatching:
                 if self.__isWatching:
                     for i in self.__watching:
                     for i in self.__watching:
                         if str(event).find(i) >= 0:
                         if str(event).find(i) >= 0:
-                            foundWatch = 1
+                            foundWatch = True
                             break
                             break
             acceptorDict = self.__callbacks.get(event)
             acceptorDict = self.__callbacks.get(event)
             if not acceptorDict:
             if not acceptorDict:
@@ -336,7 +364,7 @@ class Messenger:
         finally:
         finally:
             self.lock.release()
             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
         """ This task is spawned each time an event is sent across
         task chains.  Its job is to empty the task events on the queue
         task chains.  Its job is to empty the task events on the queue
         for this particular task chain.  This guarantees that events
         for this particular task chain.  This guarantees that events
@@ -365,7 +393,13 @@ class Messenger:
 
 
         return task.done
         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()):
         for id in list(acceptorDict.keys()):
             # We have to make this apparently redundant check, because
             # We have to make this apparently redundant check, because
             # it is possible that one object removes its own hooks
             # it is possible that one object removes its own hooks