Browse Source

direct: Get direct to pass a mypy check

Closes #1476
WMOkiishi 2 years ago
parent
commit
32edfa43fd
44 changed files with 364 additions and 256 deletions
  1. 23 0
      .github/workflows/mypy.yml
  2. 1 1
      direct/src/directbase/DirectStart.py
  3. 1 1
      direct/src/directbase/TestStart.py
  4. 1 1
      direct/src/directbase/ThreeUpStart.py
  5. 6 2
      direct/src/directdevices/DirectJoybox.py
  6. 3 1
      direct/src/distributed/AsyncRequest.py
  7. 7 0
      direct/src/distributed/CachedDOData.py
  8. 2 2
      direct/src/distributed/DistributedObjectAI.py
  9. 1 1
      direct/src/distributed/DistributedObjectUD.py
  10. 4 2
      direct/src/distributed/DoInterestManager.py
  11. 4 2
      direct/src/distributed/GridParent.py
  12. 139 137
      direct/src/distributed/MsgTypes.py
  13. 15 18
      direct/src/distributed/MsgTypesCMU.py
  14. 3 1
      direct/src/fsm/ClassicFSM.py
  15. 3 1
      direct/src/fsm/State.py
  16. 2 0
      direct/src/fsm/StatePush.py
  17. 3 1
      direct/src/gui/DirectDialog.py
  18. 3 1
      direct/src/gui/DirectGuiBase.py
  19. 1 1
      direct/src/gui/DirectGuiGlobals.py
  20. 3 1
      direct/src/interval/FunctionInterval.py
  21. 6 6
      direct/src/interval/IntervalTest.py
  22. 1 1
      direct/src/leveleditor/LevelEditorStart.py
  23. 2 1
      direct/src/leveleditor/testData.py
  24. 3 1
      direct/src/motiontrail/MotionTrail.py
  25. 3 1
      direct/src/showbase/ContainerLeakDetector.py
  26. 3 1
      direct/src/showbase/ContainerReport.py
  27. 1 1
      direct/src/showbase/DConfig.py
  28. 3 1
      direct/src/showbase/DistancePhasedNode.py
  29. 1 1
      direct/src/showbase/ExceptionVarDump.py
  30. 4 2
      direct/src/showbase/ObjectReport.py
  31. 53 50
      direct/src/showbase/PythonUtil.py
  32. 1 1
      direct/src/showbase/ShowBase.py
  33. 4 2
      direct/src/showbase/ShowBaseGlobal.py
  34. 1 1
      direct/src/showbase/VerboseImport.py
  35. 2 2
      direct/src/stdpy/pickle.py
  36. 2 2
      direct/src/stdpy/threading.py
  37. 1 1
      direct/src/stdpy/threading2.py
  38. 1 1
      direct/src/task/Task.py
  39. 2 2
      direct/src/tkwidgets/AppShell.py
  40. 4 2
      direct/src/wxwidgets/ViewPort.py
  41. 2 1
      direct/src/wxwidgets/WxPandaStart.py
  42. 1 1
      direct/src/wxwidgets/WxPandaWindow.py
  43. 13 0
      mypy.ini
  44. 25 0
      tests/run_mypy.py

+ 23 - 0
.github/workflows/mypy.yml

@@ -0,0 +1,23 @@
+name: Run Mypy
+on: [push, pull_request]
+
+jobs:
+  mypy:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+        python-version: ['3.8', '3.11']
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v4
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install mypy==1.4.0
+      - name: Run mypy on direct
+        run: python tests/run_mypy.py

+ 1 - 1
direct/src/directbase/DirectStart.py

@@ -15,7 +15,7 @@ to and may be replaced by the following code:
    base = ShowBase()
 """
 
-__all__ = []
+__all__ = ()
 
 if __debug__:
     print('Using deprecated DirectStart interface.')

+ 1 - 1
direct/src/directbase/TestStart.py

@@ -6,7 +6,7 @@ from direct.showbase import ShowBase
 base = ShowBase.ShowBase()
 
 # Put an axis in the world:
-base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
 
 base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)

+ 1 - 1
direct/src/directbase/ThreeUpStart.py

@@ -8,7 +8,7 @@ from direct.showbase import ThreeUpShow
 base = ThreeUpShow.ThreeUpShow()
 
 # Put an axis in the world:
-base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
 
 base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)

+ 6 - 2
direct/src/directdevices/DirectJoybox.py

@@ -41,8 +41,12 @@ class DirectJoybox(DirectObject):
     xyzMultiplier = 1.0
     hprMultiplier = 1.0
 
-    def __init__(self, device = 'CerealBox', nodePath = base.direct.camera,
-                 headingNP = base.direct.camera):
+    def __init__(self, device = 'CerealBox', nodePath = None, headingNP = None):
+        from direct.showbase.ShowBaseGlobal import base
+        if nodePath is None:
+            nodePath = base.direct.camera
+        if headingNP is None:
+            headingNP = base.direct.camera
         # See if device manager has been initialized
         if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()

+ 3 - 1
direct/src/distributed/AsyncRequest.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 #from otp.ai.AIBaseGlobal import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase.DirectObject import DirectObject
@@ -39,7 +41,7 @@ class AsyncRequest(DirectObject):
     will be called again when the new self.neededObjects is complete.  You
     may repeat this as necessary.
     """
-    _asyncRequests = {}
+    _asyncRequests: dict[int, AsyncRequest] = {}
 
     notify = DirectNotifyGlobal.directNotify.newCategory('AsyncRequest')
 

+ 7 - 0
direct/src/distributed/CachedDOData.py

@@ -20,3 +20,10 @@ class CachedDOData:
         # override and destroy the cached data
         # cached data is typically created by the DistributedObject and destroyed here
         pass
+
+    # These next two methods tell mypy to allow arbitrary attributes.
+    def __getattribute__(self, name: str):
+        return object.__getattribute__(self, name)
+
+    def __setattr__(self, name: str, value) -> None:
+        object.__setattr__(self, name, value)

+ 2 - 2
direct/src/distributed/DistributedObjectAI.py

@@ -299,7 +299,7 @@ class DistributedObjectAI(DistributedObjectBase):
         # setLocation destroys self._zoneData if we move away to
         # a different zone
         if self._zoneData is None:
-            from otp.ai.AIZoneData import AIZoneData
+            from otp.ai.AIZoneData import AIZoneData  # type: ignore[import]
             self._zoneData = AIZoneData(self.air, self.parentId, self.zoneId)
         return self._zoneData
 
@@ -489,7 +489,7 @@ class DistributedObjectAI(DistributedObjectBase):
         # simultaneously on different lists of avatars, although they
         # should have different names.
 
-        from otp.ai import Barrier
+        from otp.ai import Barrier  # type: ignore[import]
         context = self.__nextBarrierContext
         # We assume the context number is passed as a uint16.
         self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

+ 1 - 1
direct/src/distributed/DistributedObjectUD.py

@@ -424,7 +424,7 @@ class DistributedObjectUD(DistributedObjectBase):
         # simultaneously on different lists of avatars, although they
         # should have different names.
 
-        from otp.ai import Barrier
+        from otp.ai import Barrier  # type: ignore[import]
         context = self.__nextBarrierContext
         # We assume the context number is passed as a uint16.
         self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

+ 4 - 2
direct/src/distributed/DoInterestManager.py

@@ -7,6 +7,8 @@ zone, remove interest in that zone.
 p.s. A great deal of this code is just code moved from ClientRepository.py.
 """
 
+from __future__ import annotations
+
 from panda3d.core import ConfigVariableBool
 from .MsgTypes import CLIENT_ADD_INTEREST, CLIENT_ADD_INTEREST_MULTIPLE, CLIENT_REMOVE_INTEREST
 from direct.showbase import DirectObject
@@ -98,9 +100,9 @@ class DoInterestManager(DirectObject.DirectObject):
     _ContextIdSerialNum = 100
     _ContextIdMask = 0x3FFFFFFF # avoid making Python create a long
 
-    _interests = {}
+    _interests: dict[int, InterestState] = {}
     if __debug__:
-        _debug_interestHistory = []
+        _debug_interestHistory: list[tuple] = []
         _debug_maxDescriptionLen = 40
 
     _SerialGen = SerialNumGen()

+ 4 - 2
direct/src/distributed/GridParent.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from panda3d.core import NodePath
 
 #
@@ -13,8 +15,8 @@ from panda3d.core import NodePath
 class GridParent:
 
     # this lets GridParents share CellOrigins
-    GridZone2CellOrigin = {}
-    GridZone2count = {}
+    GridZone2CellOrigin: dict[tuple, NodePath] = {}
+    GridZone2count: dict[tuple, int] = {}
     @staticmethod
     def getCellOrigin(grid, zoneId):
         tup = (grid, zoneId)

+ 139 - 137
direct/src/distributed/MsgTypes.py

@@ -1,143 +1,145 @@
 """MsgTypes module: contains distributed object message types"""
 
+from __future__ import annotations
+
 from direct.showbase.PythonUtil import invertDictLossless
 
-MsgName2Id = {
-    'CLIENT_HELLO':                                  1,
-    'CLIENT_HELLO_RESP':                             2,
-
-    # Sent by the client when it's leaving.
-    'CLIENT_DISCONNECT':                             3,
-
-    # Sent by the server when it is dropping the connection deliberately.
-    'CLIENT_EJECT':                                  4,
-
-    'CLIENT_HEARTBEAT':                              5,
-
-    'CLIENT_OBJECT_SET_FIELD':                       120,
-    'CLIENT_OBJECT_SET_FIELDS':                      121,
-    'CLIENT_OBJECT_LEAVING':                         132,
-    'CLIENT_OBJECT_LEAVING_OWNER':                   161,
-    'CLIENT_ENTER_OBJECT_REQUIRED':                  142,
-    'CLIENT_ENTER_OBJECT_REQUIRED_OTHER':            143,
-    'CLIENT_ENTER_OBJECT_REQUIRED_OWNER':            172,
-    'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER':      173,
-
-    'CLIENT_DONE_INTEREST_RESP':                     204,
-
-    'CLIENT_ADD_INTEREST':                           200,
-    'CLIENT_ADD_INTEREST_MULTIPLE':                  201,
-    'CLIENT_REMOVE_INTEREST':                        203,
-    'CLIENT_OBJECT_LOCATION':                        140,
-
-
-    # These are sent internally inside the Astron cluster.
-
-    # Message Director control messages:
-    'CONTROL_CHANNEL':                                  1,
-    'CONTROL_ADD_CHANNEL':                              9000,
-    'CONTROL_REMOVE_CHANNEL':                           9001,
-    'CONTROL_ADD_RANGE':                                9002,
-    'CONTROL_REMOVE_RANGE':                             9003,
-    'CONTROL_ADD_POST_REMOVE':                          9010,
-    'CONTROL_CLEAR_POST_REMOVES':                       9011,
-
-    # State Server control messages:
-    'STATESERVER_CREATE_OBJECT_WITH_REQUIRED':          2000,
-    'STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER':    2001,
-    'STATESERVER_DELETE_AI_OBJECTS':                    2009,
-    'STATESERVER_OBJECT_GET_FIELD':                     2010,
-    'STATESERVER_OBJECT_GET_FIELD_RESP':                2011,
-    'STATESERVER_OBJECT_GET_FIELDS':                    2012,
-    'STATESERVER_OBJECT_GET_FIELDS_RESP':               2013,
-    'STATESERVER_OBJECT_GET_ALL':                       2014,
-    'STATESERVER_OBJECT_GET_ALL_RESP':                  2015,
-    'STATESERVER_OBJECT_SET_FIELD':                     2020,
-    'STATESERVER_OBJECT_SET_FIELDS':                    2021,
-    'STATESERVER_OBJECT_DELETE_FIELD_RAM':              2030,
-    'STATESERVER_OBJECT_DELETE_FIELDS_RAM':             2031,
-    'STATESERVER_OBJECT_DELETE_RAM':                    2032,
-    'STATESERVER_OBJECT_SET_LOCATION':                          2040,
-    'STATESERVER_OBJECT_CHANGING_LOCATION':                     2041,
-    'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED':          2042,
-    'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER':    2043,
-    'STATESERVER_OBJECT_GET_LOCATION':                          2044,
-    'STATESERVER_OBJECT_GET_LOCATION_RESP':                     2045,
-    'STATESERVER_OBJECT_SET_AI':                                2050,
-    'STATESERVER_OBJECT_CHANGING_AI':                           2051,
-    'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED':                2052,
-    'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER':          2053,
-    'STATESERVER_OBJECT_GET_AI':                                2054,
-    'STATESERVER_OBJECT_GET_AI_RESP':                           2055,
-    'STATESERVER_OBJECT_SET_OWNER':                             2060,
-    'STATESERVER_OBJECT_CHANGING_OWNER':                        2061,
-    'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED':             2062,
-    'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER':       2063,
-    'STATESERVER_OBJECT_GET_OWNER':                             2064,
-    'STATESERVER_OBJECT_GET_OWNER_RESP':                        2065,
-    'STATESERVER_OBJECT_GET_ZONE_OBJECTS':              2100,
-    'STATESERVER_OBJECT_GET_ZONES_OBJECTS':             2102,
-    'STATESERVER_OBJECT_GET_CHILDREN':                  2104,
-    'STATESERVER_OBJECT_GET_ZONE_COUNT':                2110,
-    'STATESERVER_OBJECT_GET_ZONE_COUNT_RESP':           2111,
-    'STATESERVER_OBJECT_GET_ZONES_COUNT':               2112,
-    'STATESERVER_OBJECT_GET_ZONES_COUNT_RESP':          2113,
-    'STATESERVER_OBJECT_GET_CHILD_COUNT':               2114,
-    'STATESERVER_OBJECT_GET_CHILD_COUNT_RESP':          2115,
-    'STATESERVER_OBJECT_DELETE_ZONE':                   2120,
-    'STATESERVER_OBJECT_DELETE_ZONES':                  2122,
-    'STATESERVER_OBJECT_DELETE_CHILDREN':               2124,
-    # DBSS-backed-object messages:
-    'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS':        2200,
-    'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER':  2201,
-    'DBSS_OBJECT_GET_ACTIVATED':                 2207,
-    'DBSS_OBJECT_GET_ACTIVATED_RESP':            2208,
-    'DBSS_OBJECT_DELETE_FIELD_DISK':             2230,
-    'DBSS_OBJECT_DELETE_FIELDS_DISK':            2231,
-    'DBSS_OBJECT_DELETE_DISK':                   2232,
-
-    # Database Server control messages:
-    'DBSERVER_CREATE_OBJECT':                       3000,
-    'DBSERVER_CREATE_OBJECT_RESP':                  3001,
-    'DBSERVER_OBJECT_GET_FIELD':                    3010,
-    'DBSERVER_OBJECT_GET_FIELD_RESP':               3011,
-    'DBSERVER_OBJECT_GET_FIELDS':                   3012,
-    'DBSERVER_OBJECT_GET_FIELDS_RESP':              3013,
-    'DBSERVER_OBJECT_GET_ALL':                      3014,
-    'DBSERVER_OBJECT_GET_ALL_RESP':                 3015,
-    'DBSERVER_OBJECT_SET_FIELD':                    3020,
-    'DBSERVER_OBJECT_SET_FIELDS':                   3021,
-    'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS':          3022,
-    'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP':     3023,
-    'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS':         3024,
-    'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP':    3025,
-    'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY':           3026,
-    'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP':      3027,
-    'DBSERVER_OBJECT_DELETE_FIELD':                 3030,
-    'DBSERVER_OBJECT_DELETE_FIELDS':                3031,
-    'DBSERVER_OBJECT_DELETE':                       3032,
-
-    # Client Agent control messages:
-    'CLIENTAGENT_SET_STATE':                        1000,
-    'CLIENTAGENT_SET_CLIENT_ID':                    1001,
-    'CLIENTAGENT_SEND_DATAGRAM':                    1002,
-    'CLIENTAGENT_EJECT':                            1004,
-    'CLIENTAGENT_DROP':                             1005,
-    'CLIENTAGENT_GET_NETWORK_ADDRESS':              1006,
-    'CLIENTAGENT_GET_NETWORK_ADDRESS_RESP':         1007,
-    'CLIENTAGENT_DECLARE_OBJECT':                   1010,
-    'CLIENTAGENT_UNDECLARE_OBJECT':                 1011,
-    'CLIENTAGENT_ADD_SESSION_OBJECT':               1012,
-    'CLIENTAGENT_REMOVE_SESSION_OBJECT':            1013,
-    'CLIENTAGENT_SET_FIELDS_SENDABLE':              1014,
-    'CLIENTAGENT_OPEN_CHANNEL':                     1100,
-    'CLIENTAGENT_CLOSE_CHANNEL':                    1101,
-    'CLIENTAGENT_ADD_POST_REMOVE':                  1110,
-    'CLIENTAGENT_CLEAR_POST_REMOVES':               1111,
-    'CLIENTAGENT_ADD_INTEREST':                     1200,
-    'CLIENTAGENT_ADD_INTEREST_MULTIPLE':            1201,
-    'CLIENTAGENT_REMOVE_INTEREST':                  1203,
-}
+CLIENT_HELLO =                                 1
+CLIENT_HELLO_RESP =                            2
+
+# Sent by the client when it's leaving.
+CLIENT_DISCONNECT =                            3
+
+# Sent by the server when it is dropping the connection deliberately.
+CLIENT_EJECT =                                 4
+
+CLIENT_HEARTBEAT =                             5
+
+CLIENT_OBJECT_SET_FIELD =                      120
+CLIENT_OBJECT_SET_FIELDS =                     121
+CLIENT_OBJECT_LEAVING =                        132
+CLIENT_OBJECT_LEAVING_OWNER =                  161
+CLIENT_ENTER_OBJECT_REQUIRED =                 142
+CLIENT_ENTER_OBJECT_REQUIRED_OTHER =           143
+CLIENT_ENTER_OBJECT_REQUIRED_OWNER =           172
+CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER =     173
+
+CLIENT_DONE_INTEREST_RESP =                    204
+
+CLIENT_ADD_INTEREST =                          200
+CLIENT_ADD_INTEREST_MULTIPLE =                 201
+CLIENT_REMOVE_INTEREST =                       203
+CLIENT_OBJECT_LOCATION =                       140
+
+
+# These are sent internally inside the Astron cluster.
+
+# Message Director control messages:
+CONTROL_CHANNEL =                                 1
+CONTROL_ADD_CHANNEL =                             9000
+CONTROL_REMOVE_CHANNEL =                          9001
+CONTROL_ADD_RANGE =                               9002
+CONTROL_REMOVE_RANGE =                            9003
+CONTROL_ADD_POST_REMOVE =                         9010
+CONTROL_CLEAR_POST_REMOVES =                      9011
+
+# State Server control messages:
+STATESERVER_CREATE_OBJECT_WITH_REQUIRED =         2000
+STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER =   2001
+STATESERVER_DELETE_AI_OBJECTS =                   2009
+STATESERVER_OBJECT_GET_FIELD =                    2010
+STATESERVER_OBJECT_GET_FIELD_RESP =               2011
+STATESERVER_OBJECT_GET_FIELDS =                   2012
+STATESERVER_OBJECT_GET_FIELDS_RESP =              2013
+STATESERVER_OBJECT_GET_ALL =                      2014
+STATESERVER_OBJECT_GET_ALL_RESP =                 2015
+STATESERVER_OBJECT_SET_FIELD =                    2020
+STATESERVER_OBJECT_SET_FIELDS =                   2021
+STATESERVER_OBJECT_DELETE_FIELD_RAM =             2030
+STATESERVER_OBJECT_DELETE_FIELDS_RAM =            2031
+STATESERVER_OBJECT_DELETE_RAM =                   2032
+STATESERVER_OBJECT_SET_LOCATION =                         2040
+STATESERVER_OBJECT_CHANGING_LOCATION =                    2041
+STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED =         2042
+STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER =   2043
+STATESERVER_OBJECT_GET_LOCATION =                         2044
+STATESERVER_OBJECT_GET_LOCATION_RESP =                    2045
+STATESERVER_OBJECT_SET_AI =                               2050
+STATESERVER_OBJECT_CHANGING_AI =                          2051
+STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED =               2052
+STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER =         2053
+STATESERVER_OBJECT_GET_AI =                               2054
+STATESERVER_OBJECT_GET_AI_RESP =                          2055
+STATESERVER_OBJECT_SET_OWNER =                            2060
+STATESERVER_OBJECT_CHANGING_OWNER =                       2061
+STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED =            2062
+STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER =      2063
+STATESERVER_OBJECT_GET_OWNER =                            2064
+STATESERVER_OBJECT_GET_OWNER_RESP =                       2065
+STATESERVER_OBJECT_GET_ZONE_OBJECTS =             2100
+STATESERVER_OBJECT_GET_ZONES_OBJECTS =            2102
+STATESERVER_OBJECT_GET_CHILDREN =                 2104
+STATESERVER_OBJECT_GET_ZONE_COUNT =               2110
+STATESERVER_OBJECT_GET_ZONE_COUNT_RESP =          2111
+STATESERVER_OBJECT_GET_ZONES_COUNT =              2112
+STATESERVER_OBJECT_GET_ZONES_COUNT_RESP =         2113
+STATESERVER_OBJECT_GET_CHILD_COUNT =              2114
+STATESERVER_OBJECT_GET_CHILD_COUNT_RESP =         2115
+STATESERVER_OBJECT_DELETE_ZONE =                  2120
+STATESERVER_OBJECT_DELETE_ZONES =                 2122
+STATESERVER_OBJECT_DELETE_CHILDREN =              2124
+# DBSS-backed-object messages:
+DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS =       2200
+DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER = 2201
+DBSS_OBJECT_GET_ACTIVATED =                2207
+DBSS_OBJECT_GET_ACTIVATED_RESP =           2208
+DBSS_OBJECT_DELETE_FIELD_DISK =            2230
+DBSS_OBJECT_DELETE_FIELDS_DISK =           2231
+DBSS_OBJECT_DELETE_DISK =                  2232
+
+# Database Server control messages:
+DBSERVER_CREATE_OBJECT =                      3000
+DBSERVER_CREATE_OBJECT_RESP =                 3001
+DBSERVER_OBJECT_GET_FIELD =                   3010
+DBSERVER_OBJECT_GET_FIELD_RESP =              3011
+DBSERVER_OBJECT_GET_FIELDS =                  3012
+DBSERVER_OBJECT_GET_FIELDS_RESP =             3013
+DBSERVER_OBJECT_GET_ALL =                     3014
+DBSERVER_OBJECT_GET_ALL_RESP =                3015
+DBSERVER_OBJECT_SET_FIELD =                   3020
+DBSERVER_OBJECT_SET_FIELDS =                  3021
+DBSERVER_OBJECT_SET_FIELD_IF_EQUALS =         3022
+DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP =    3023
+DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS =        3024
+DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP =   3025
+DBSERVER_OBJECT_SET_FIELD_IF_EMPTY =          3026
+DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP =     3027
+DBSERVER_OBJECT_DELETE_FIELD =                3030
+DBSERVER_OBJECT_DELETE_FIELDS =               3031
+DBSERVER_OBJECT_DELETE =                      3032
+
+# Client Agent control messages:
+CLIENTAGENT_SET_STATE =                       1000
+CLIENTAGENT_SET_CLIENT_ID =                   1001
+CLIENTAGENT_SEND_DATAGRAM =                   1002
+CLIENTAGENT_EJECT =                           1004
+CLIENTAGENT_DROP =                            1005
+CLIENTAGENT_GET_NETWORK_ADDRESS =             1006
+CLIENTAGENT_GET_NETWORK_ADDRESS_RESP =        1007
+CLIENTAGENT_DECLARE_OBJECT =                  1010
+CLIENTAGENT_UNDECLARE_OBJECT =                1011
+CLIENTAGENT_ADD_SESSION_OBJECT =              1012
+CLIENTAGENT_REMOVE_SESSION_OBJECT =           1013
+CLIENTAGENT_SET_FIELDS_SENDABLE =             1014
+CLIENTAGENT_OPEN_CHANNEL =                    1100
+CLIENTAGENT_CLOSE_CHANNEL =                   1101
+CLIENTAGENT_ADD_POST_REMOVE =                 1110
+CLIENTAGENT_CLEAR_POST_REMOVES =              1111
+CLIENTAGENT_ADD_INTEREST =                    1200
+CLIENTAGENT_ADD_INTEREST_MULTIPLE =           1201
+CLIENTAGENT_REMOVE_INTEREST =                 1203
+
+MsgName2Id = {name: value for name, value in globals().items() if isinstance(value, int)}
 
 # create id->name table for debugging
 MsgId2Names = invertDictLossless(MsgName2Id)
@@ -146,7 +148,7 @@ MsgId2Names = invertDictLossless(MsgName2Id)
 globals().update(MsgName2Id)
 
 # These messages are ignored when the client is headed to the quiet zone
-QUIET_ZONE_IGNORED_LIST = [
+QUIET_ZONE_IGNORED_LIST: list[int] = [
 
     # We mustn't ignore updates, because some updates for localToon
     # are always important.

+ 15 - 18
direct/src/distributed/MsgTypesCMU.py

@@ -5,25 +5,22 @@ implementation. """
 
 from direct.showbase.PythonUtil import invertDictLossless
 
-MsgName2Id = {
-    'SET_DOID_RANGE_CMU'                      : 9001,
-    'CLIENT_OBJECT_GENERATE_CMU'              : 9002,
-    'OBJECT_GENERATE_CMU'                     : 9003,
-    'OBJECT_UPDATE_FIELD_CMU'                 : 9004,
-    'OBJECT_DISABLE_CMU'                      : 9005,
-    'OBJECT_DELETE_CMU'                       : 9006,
-    'REQUEST_GENERATES_CMU'                   : 9007,
-    'CLIENT_DISCONNECT_CMU'                   : 9008,
-    'CLIENT_SET_INTEREST_CMU'                 : 9009,
-    'OBJECT_SET_ZONE_CMU'                     : 9010,
-    'CLIENT_HEARTBEAT_CMU'                    : 9011,
-    'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU'  : 9011,
+SET_DOID_RANGE_CMU                      = 9001
+CLIENT_OBJECT_GENERATE_CMU              = 9002
+OBJECT_GENERATE_CMU                     = 9003
+OBJECT_UPDATE_FIELD_CMU                 = 9004
+OBJECT_DISABLE_CMU                      = 9005
+OBJECT_DELETE_CMU                       = 9006
+REQUEST_GENERATES_CMU                   = 9007
+CLIENT_DISCONNECT_CMU                   = 9008
+CLIENT_SET_INTEREST_CMU                 = 9009
+OBJECT_SET_ZONE_CMU                     = 9010
+CLIENT_HEARTBEAT_CMU                    = 9011
+CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU  = 9011
 
-    'CLIENT_OBJECT_UPDATE_FIELD' : 120,  # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
-}
+CLIENT_OBJECT_UPDATE_FIELD = 120  # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
+
+MsgName2Id = {name: value for name, value in globals().items() if isinstance(value, int)}
 
 # create id->name table for debugging
 MsgId2Names = invertDictLossless(MsgName2Id)
-
-# put msg names in module scope, assigned to msg value
-globals().update(MsgName2Id)

+ 3 - 1
direct/src/fsm/ClassicFSM.py

@@ -5,6 +5,8 @@ Note:
     existing code.  New code should use the :mod:`.FSM` module instead.
 """
 
+from __future__ import annotations
+
 __all__ = ['ClassicFSM']
 
 from direct.directnotify.DirectNotifyGlobal import directNotify
@@ -13,7 +15,7 @@ from direct.showbase.MessengerGlobal import messenger
 import weakref
 
 if __debug__:
-    _debugFsms = {}
+    _debugFsms: dict[str, weakref.ref] = {}
 
     def printDebugFsmList():
         for k in sorted(_debugFsms.keys()):

+ 3 - 1
direct/src/fsm/State.py

@@ -1,5 +1,7 @@
 """State module: contains State class"""
 
+from __future__ import annotations
+
 __all__ = ['State']
 
 from direct.directnotify.DirectNotifyGlobal import directNotify
@@ -18,7 +20,7 @@ class State(DirectObject):
     # should not cause any leaks.
     if __debug__:
         import weakref
-        States = weakref.WeakKeyDictionary()
+        States: weakref.WeakKeyDictionary[State, int] = weakref.WeakKeyDictionary()
 
         @classmethod
         def replaceMethod(cls, oldFunction, newFunction):

+ 2 - 0
direct/src/fsm/StatePush.py

@@ -1,6 +1,8 @@
 # classes for event-driven programming
 # http://en.wikipedia.org/wiki/Event-driven_programming
 
+from __future__ import annotations
+
 __all__ = ['StateVar', 'FunctionCall', 'EnterExit', 'Pulse', 'EventPulse',
            'EventArgument', ]
 

+ 3 - 1
direct/src/gui/DirectDialog.py

@@ -4,6 +4,8 @@ See the :ref:`directdialog` page in the programming manual for a more
 in-depth explanation and an example of how to use this class.
 """
 
+from __future__ import annotations
+
 __all__ = [
     'findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog',
     'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog',
@@ -45,7 +47,7 @@ def cleanupDialog(uniqueName):
 
 class DirectDialog(DirectFrame):
 
-    AllDialogs = {}
+    AllDialogs: dict[str, DirectDialog] = {}
     PanelIndex = 0
 
     def __init__(self, parent=None, **kw):

+ 3 - 1
direct/src/gui/DirectGuiBase.py

@@ -84,6 +84,8 @@ Code overview:
     see if any keywords are left unused.  If so, an error is raised.
 """
 
+from __future__ import annotations
+
 __all__ = ['DirectGuiBase', 'DirectGuiWidget']
 
 
@@ -680,7 +682,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     else:
         inactiveInitState = DGG.DISABLED
 
-    guiDict = {}
+    guiDict: dict[str, DirectGuiWidget] = {}
 
     def __init__(self, parent = None, **kw):
         # Direct gui widgets are node paths

+ 1 - 1
direct/src/gui/DirectGuiGlobals.py

@@ -3,7 +3,7 @@ Global definitions used by Direct Gui Classes and handy constants
 that can be used during widget construction
 """
 
-__all__ = []
+__all__ = ()
 
 from panda3d.core import (
     KeyboardButton,

+ 3 - 1
direct/src/interval/FunctionInterval.py

@@ -1,5 +1,7 @@
 """FunctionInterval module: contains the FunctionInterval class"""
 
+from __future__ import annotations
+
 __all__ = ['FunctionInterval', 'EventInterval', 'AcceptInterval', 'IgnoreInterval', 'ParentInterval', 'WrtParentInterval', 'PosInterval', 'HprInterval', 'ScaleInterval', 'PosHprInterval', 'HprScaleInterval', 'PosHprScaleInterval', 'Func', 'Wait']
 
 from panda3d.direct import WaitInterval
@@ -23,7 +25,7 @@ class FunctionInterval(Interval.Interval):
     # should not cause any leaks.
     if __debug__:
         import weakref
-        FunctionIntervals = weakref.WeakKeyDictionary()
+        FunctionIntervals: weakref.WeakKeyDictionary[FunctionInterval, int] = weakref.WeakKeyDictionary()
 
         @classmethod
         def replaceMethod(cls, oldFunction, newFunction):

+ 6 - 6
direct/src/interval/IntervalTest.py

@@ -1,6 +1,6 @@
 """Undocumented Module"""
 
-__all__ = []
+__all__ = ()
 
 
 if __name__ == "__main__":
@@ -26,7 +26,7 @@ if __name__ == "__main__":
     base = ShowBase()
 
     boat = base.loader.loadModel('models/misc/smiley')
-    boat.reparentTo(render)
+    boat.reparentTo(base.render)
 
     donald = Actor()
     donald.loadModel("phase_6/models/char/donald-wheel-1000")
@@ -34,7 +34,7 @@ if __name__ == "__main__":
     donald.reparentTo(boat)
 
     dock = base.loader.loadModel('models/misc/smiley')
-    dock.reparentTo(render)
+    dock.reparentTo(base.render)
 
     sound = base.loader.loadSfx('phase_6/audio/sfx/SZ_DD_waterlap.mp3')
     foghorn = base.loader.loadSfx('phase_6/audio/sfx/SZ_DD_foghorn.mp3')
@@ -93,7 +93,7 @@ if __name__ == "__main__":
     foghornSound = SoundInterval(foghorn, name='foghorn')
     soundTrack2 = Track([(foghornStartTime, foghornSound)], 'soundtrack2')
 
-    mtrack = MultiTrack([boatTrack, dockTrack, soundTrack, soundTrack2, waterEventTrack,
+    mtrack = MultiTrack([boatTrack, dockTrack, soundTrack, soundTrack2, waterEventTrack,  # type: ignore[name-defined]
                          donaldSteerTrack])
     # Print out MultiTrack parameters
     print(mtrack)
@@ -175,11 +175,11 @@ if __name__ == "__main__":
     # Just to take time
     i2 = LerpPosInterval(base.camera, 2.0, Point3(0, 10, 5))
     # This will be relative to end of camera move
-    i3 = FunctionInterval(printPreviousEnd)
+    i3 = FunctionInterval(printPreviousEnd)  # type: ignore[assignment]
     # Just to take time
     i4 = LerpPosInterval(base.camera, 2.0, Point3(0, 0, 5))
     # This will be relative to the start of the camera move
-    i5 = FunctionInterval(printPreviousStart)
+    i5 = FunctionInterval(printPreviousStart)  # type: ignore[assignment]
     # This will be relative to track start
     i6 = FunctionInterval(printTrackStart)
     # This will print some arguments

+ 1 - 1
direct/src/leveleditor/LevelEditorStart.py

@@ -4,7 +4,7 @@ if __name__ == '__main__':
     from direct.showbase.ShowBase import ShowBase
     base = ShowBase()
 
-    base.le = LevelEditor.LevelEditor()
+    base.le = LevelEditor.LevelEditor()  # type: ignore[attr-defined]
     # You should define LevelEditor instance as
     # base.le so it can be reached in global scope
 

+ 2 - 1
direct/src/leveleditor/testData.py

@@ -1,4 +1,5 @@
 from panda3d.core import Point3, VBase3
+from direct.showbase.ShowBaseGlobal import base
 
 if hasattr(base, 'le'):
     objectMgr = base.le.objectMgr
@@ -6,7 +7,7 @@ if hasattr(base, 'le'):
     ui.sceneGraphUI.reset()
 
 else:
-    objectMgr = base.objectMgr
+    objectMgr = base.objectMgr  # type: ignore[attr-defined]
 # temporary place holder for nodepath
 objects = {}
 

+ 3 - 1
direct/src/motiontrail/MotionTrail.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from panda3d.core import (
     BoundingVolume,
     ColorBlendAttrib,
@@ -92,7 +94,7 @@ class MotionTrail(NodePath, DirectObject):
     notify = directNotify.newCategory("MotionTrail")
 
     task_added = False
-    motion_trail_list = []
+    motion_trail_list: list[MotionTrail] = []
     motion_trail_task_name = "motion_trail_task"
 
     global_enable = True

+ 3 - 1
direct/src/showbase/ContainerLeakDetector.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from direct.directnotify.DirectNotifyGlobal import directNotify
 import direct.showbase.DConfig as config
 from direct.showbase.PythonUtil import makeFlywheelGen, flywheel
@@ -982,7 +984,7 @@ class ContainerLeakDetector(Job):
     """
     notify = directNotify.newCategory("ContainerLeakDetector")
     # set of containers that should not be examined
-    PrivateIds = set()
+    PrivateIds: set[int] = set()
 
     def __init__(self, name, firstCheckDelay = None):
         Job.__init__(self, name)

+ 3 - 1
direct/src/showbase/ContainerReport.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.PythonUtil import Queue, invertDictLossless
 from direct.showbase.PythonUtil import safeRepr
@@ -11,7 +13,7 @@ import io
 class ContainerReport(Job):
     notify = directNotify.newCategory("ContainerReport")
     # set of containers that should not be included in the report
-    PrivateIds = set()
+    PrivateIds: set[int] = set()
 
     def __init__(self, name, log=False, limit=None, threaded=False):
         Job.__init__(self, name)

+ 1 - 1
direct/src/showbase/DConfig.py

@@ -1,6 +1,6 @@
 "This module contains a deprecated shim emulating the old DConfig API."
 
-__all__ = []
+__all__ = ()
 
 from panda3d.core import (ConfigFlags, ConfigVariableBool, ConfigVariableInt,
                           ConfigVariableDouble, ConfigVariableString)

+ 3 - 1
direct/src/showbase/DistancePhasedNode.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from direct.showbase.DirectObject import DirectObject
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from panda3d.core import (
@@ -62,7 +64,7 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
 
     notify = directNotify.newCategory("DistancePhasedObject")
     __InstanceSequence = 0
-    __InstanceDeque = []
+    __InstanceDeque: list[int] = []
 
     @staticmethod
     def __allocateId():

+ 1 - 1
direct/src/showbase/ExceptionVarDump.py

@@ -69,7 +69,7 @@ oldExcepthook = None
 # from its main exception handler
 wantStackDumpLog = False
 wantStackDumpUpload = False
-variableDumpReasons = []
+variableDumpReasons: list = []
 dumpOnExceptionInit = False
 
 

+ 4 - 2
direct/src/showbase/ObjectReport.py

@@ -11,6 +11,8 @@
 >>> o.diff(o2)
 """
 
+from __future__ import annotations
+
 __all__ = ['ExclusiveObjectPool', 'ObjectReport']
 
 from direct.directnotify.DirectNotifyGlobal import directNotify
@@ -26,8 +28,8 @@ import builtins
 class ExclusiveObjectPool(DirectObject):
     # ObjectPool specialization that excludes particular objects
     # IDs of objects to globally exclude from reporting
-    _ExclObjs = []
-    _ExclObjIds = {}
+    _ExclObjs: list[object] = []
+    _ExclObjIds: dict[int, int] = {}
     _SyncMaster = Sync('ExclusiveObjectPool.ExcludedObjectList')
     _SerialNumGen = SerialNumGen()
 

+ 53 - 50
direct/src/showbase/PythonUtil.py

@@ -1,5 +1,7 @@
 """Contains miscellaneous utility functions and classes."""
 
+from __future__ import annotations
+
 __all__ = [
 
     'indent', 'doc', 'adjust', 'difference', 'intersection', 'union',
@@ -39,6 +41,7 @@ import time
 import builtins
 import importlib
 import functools
+from typing import Callable
 
 __report_indent = 3
 
@@ -699,9 +702,9 @@ if __debug__:
         return profileDecorator
 
     # intercept profile-related file operations to avoid disk access
-    movedOpenFuncs = []
-    movedDumpFuncs = []
-    movedLoadFuncs = []
+    movedOpenFuncs: list[Callable] = []
+    movedDumpFuncs: list[Callable] = []
+    movedLoadFuncs: list[Callable] = []
     profileFilenames = set()
     profileFilenameList = Stack()
     profileFilename2file = {}
@@ -1899,7 +1902,7 @@ class SubframeCall:
 
 
 class PStatScope:
-    collectors = {}
+    collectors: dict = {}
 
     def __init__(self, level = None):
         self.levels = []
@@ -2618,52 +2621,52 @@ class PriorityCallbacks:
             callback()
 
 
-builtins.Functor = Functor
-builtins.Stack = Stack
-builtins.Queue = Queue
-builtins.SerialNumGen = SerialNumGen
-builtins.SerialMaskedGen = SerialMaskedGen
-builtins.ScratchPad = ScratchPad
-builtins.uniqueName = uniqueName
-builtins.serialNum = serialNum
+builtins.Functor = Functor  # type: ignore[attr-defined]
+builtins.Stack = Stack  # type: ignore[attr-defined]
+builtins.Queue = Queue  # type: ignore[attr-defined]
+builtins.SerialNumGen = SerialNumGen  # type: ignore[attr-defined]
+builtins.SerialMaskedGen = SerialMaskedGen  # type: ignore[attr-defined]
+builtins.ScratchPad = ScratchPad  # type: ignore[attr-defined]
+builtins.uniqueName = uniqueName  # type: ignore[attr-defined]
+builtins.serialNum = serialNum  # type: ignore[attr-defined]
 if __debug__:
-    builtins.profiled = profiled
-    builtins.exceptionLogged = exceptionLogged
-builtins.itype = itype
-builtins.appendStr = appendStr
-builtins.bound = bound
-builtins.clamp = clamp
-builtins.lerp = lerp
-builtins.makeList = makeList
-builtins.makeTuple = makeTuple
+    builtins.profiled = profiled  # type: ignore[attr-defined]
+    builtins.exceptionLogged = exceptionLogged  # type: ignore[attr-defined]
+builtins.itype = itype  # type: ignore[attr-defined]
+builtins.appendStr = appendStr  # type: ignore[attr-defined]
+builtins.bound = bound  # type: ignore[attr-defined]
+builtins.clamp = clamp  # type: ignore[attr-defined]
+builtins.lerp = lerp  # type: ignore[attr-defined]
+builtins.makeList = makeList  # type: ignore[attr-defined]
+builtins.makeTuple = makeTuple  # type: ignore[attr-defined]
 if __debug__:
-    builtins.printStack = printStack
-    builtins.printReverseStack = printReverseStack
-    builtins.printVerboseStack = printVerboseStack
-builtins.DelayedCall = DelayedCall
-builtins.DelayedFunctor = DelayedFunctor
-builtins.FrameDelayedCall = FrameDelayedCall
-builtins.SubframeCall = SubframeCall
-builtins.invertDict = invertDict
-builtins.invertDictLossless = invertDictLossless
-builtins.getBase = getBase
-builtins.getRepository = getRepository
-builtins.safeRepr = safeRepr
-builtins.fastRepr = fastRepr
-builtins.nullGen = nullGen
-builtins.flywheel = flywheel
-builtins.loopGen = loopGen
+    builtins.printStack = printStack  # type: ignore[attr-defined]
+    builtins.printReverseStack = printReverseStack  # type: ignore[attr-defined]
+    builtins.printVerboseStack = printVerboseStack  # type: ignore[attr-defined]
+builtins.DelayedCall = DelayedCall  # type: ignore[attr-defined]
+builtins.DelayedFunctor = DelayedFunctor  # type: ignore[attr-defined]
+builtins.FrameDelayedCall = FrameDelayedCall  # type: ignore[attr-defined]
+builtins.SubframeCall = SubframeCall  # type: ignore[attr-defined]
+builtins.invertDict = invertDict  # type: ignore[attr-defined]
+builtins.invertDictLossless = invertDictLossless  # type: ignore[attr-defined]
+builtins.getBase = getBase  # type: ignore[attr-defined]
+builtins.getRepository = getRepository  # type: ignore[attr-defined]
+builtins.safeRepr = safeRepr  # type: ignore[attr-defined]
+builtins.fastRepr = fastRepr  # type: ignore[attr-defined]
+builtins.nullGen = nullGen  # type: ignore[attr-defined]
+builtins.flywheel = flywheel  # type: ignore[attr-defined]
+builtins.loopGen = loopGen  # type: ignore[attr-defined]
 if __debug__:
-    builtins.StackTrace = StackTrace
-builtins.report = report
-builtins.pstatcollect = pstatcollect
-builtins.MiniLog = MiniLog
-builtins.MiniLogSentry = MiniLogSentry
-builtins.logBlock = logBlock
-builtins.HierarchyException = HierarchyException
-builtins.deeptype = deeptype
-builtins.Default = Default
-builtins.configIsToday = configIsToday
-builtins.typeName = typeName
-builtins.safeTypeName = safeTypeName
-builtins.histogramDict = histogramDict
+    builtins.StackTrace = StackTrace  # type: ignore[attr-defined]
+builtins.report = report  # type: ignore[attr-defined]
+builtins.pstatcollect = pstatcollect  # type: ignore[attr-defined]
+builtins.MiniLog = MiniLog  # type: ignore[attr-defined]
+builtins.MiniLogSentry = MiniLogSentry  # type: ignore[attr-defined]
+builtins.logBlock = logBlock  # type: ignore[attr-defined]
+builtins.HierarchyException = HierarchyException  # type: ignore[attr-defined]
+builtins.deeptype = deeptype  # type: ignore[attr-defined]
+builtins.Default = Default  # type: ignore[attr-defined]
+builtins.configIsToday = configIsToday  # type: ignore[attr-defined]
+builtins.typeName = typeName  # type: ignore[attr-defined]
+builtins.safeTypeName = safeTypeName  # type: ignore[attr-defined]
+builtins.histogramDict = histogramDict  # type: ignore[attr-defined]

+ 1 - 1
direct/src/showbase/ShowBase.py

@@ -120,7 +120,7 @@ from direct.extensions_native import NodePath_extensions # pylint: disable=unuse
 # This needs to be available early for DirectGUI imports
 import sys
 import builtins
-builtins.config = DConfig
+builtins.config = DConfig  # type: ignore[attr-defined]
 
 from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify
 from .MessengerGlobal import messenger

+ 4 - 2
direct/src/showbase/ShowBaseGlobal.py

@@ -11,7 +11,7 @@ Note that you cannot directly import :data:`~builtins.base` from this module
 since ShowBase may not have been created yet; instead, ShowBase dynamically
 adds itself to this module's scope when instantiated."""
 
-__all__ = []
+__all__ = ()
 
 from .ShowBase import ShowBase, WindowControls # pylint: disable=unused-import
 from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify # pylint: disable=unused-import
@@ -23,6 +23,8 @@ import warnings
 
 __dev__ = ConfigVariableBool('want-dev', __debug__).value
 
+base: ShowBase
+
 #: The global instance of the :ref:`virtual-file-system`, as obtained using
 #: :meth:`panda3d.core.VirtualFileSystem.getGlobalPtr()`.
 vfs = VirtualFileSystem.getGlobalPtr()
@@ -81,7 +83,7 @@ def inspect(anObject):
 
 
 import builtins
-builtins.inspect = inspect
+builtins.inspect = inspect  # type: ignore[attr-defined]
 
 # this also appears in AIBaseGlobal
 if (not __debug__) and __dev__:

+ 1 - 1
direct/src/showbase/VerboseImport.py

@@ -3,7 +3,7 @@ This module hooks into Python's import mechanism to print out all imports to
 the standard output as they happen.
 """
 
-__all__ = []
+__all__ = ()
 
 
 import sys

+ 2 - 2
direct/src/stdpy/pickle.py

@@ -45,7 +45,7 @@ BasePickler = pickle._Pickler
 BaseUnpickler = pickle._Unpickler
 
 
-class Pickler(BasePickler):
+class Pickler(BasePickler):  # type: ignore[misc, valid-type]
 
     def __init__(self, *args, **kw):
         self.bamWriter = BamWriter()
@@ -148,7 +148,7 @@ class Pickler(BasePickler):
         self.save_reduce(obj=obj, *rv)
 
 
-class Unpickler(BaseUnpickler):
+class Unpickler(BaseUnpickler):  # type: ignore[misc, valid-type]
 
     def __init__(self, *args, **kw):
         self.bamReader = BamReader()

+ 2 - 2
direct/src/stdpy/threading.py

@@ -83,8 +83,8 @@ class ThreadBase:
 # Copy these static methods from Panda's Thread object.  These are
 # useful if you may be running in Panda's SIMPLE_THREADS compilation
 # mode.
-ThreadBase.forceYield = core.Thread.forceYield
-ThreadBase.considerYield = core.Thread.considerYield
+ThreadBase.forceYield = core.Thread.forceYield  # type: ignore[attr-defined]
+ThreadBase.considerYield = core.Thread.considerYield  # type: ignore[attr-defined]
 
 
 class Thread(ThreadBase):

+ 1 - 1
direct/src/stdpy/threading2.py

@@ -65,7 +65,7 @@ if __debug__:
 
 else:
     # Disable this when using "python -O"
-    class _Verbose(object):
+    class _Verbose(object):  # type: ignore[no-redef]
         def __init__(self, verbose=None):
             pass
         def _note(self, *args):

+ 1 - 1
direct/src/task/Task.py

@@ -24,7 +24,7 @@ if hasattr(sys, 'getandroidapilevel'):
     signal = None
 else:
     try:
-        import _signal as signal
+        import _signal as signal  # type: ignore[import, no-redef]
     except ImportError:
         signal = None
 

+ 2 - 2
direct/src/tkwidgets/AppShell.py

@@ -21,11 +21,11 @@ import builtins
 
 # Create toplevel widget dictionary
 if not hasattr(builtins, "widgetDict"):
-    builtins.widgetDict = {}
+    builtins.widgetDict = {}  # type: ignore[attr-defined]
 
 # Create toplevel variable dictionary
 if not hasattr(builtins, "variableDict"):
-    builtins.variableDict = {}
+    builtins.variableDict = {}  # type: ignore[attr-defined]
 
 
 def resetWidgetDict():

+ 4 - 2
direct/src/wxwidgets/ViewPort.py

@@ -6,6 +6,8 @@ Modified by gjeon.
 Modified by Summer 2010 Carnegie Mellon University ETC PandaLE team: fixed a bug in Viewport.Close
 """
 
+from __future__ import annotations
+
 __all__ = ["Viewport", "ViewportManager"]
 
 from panda3d.core import (
@@ -36,7 +38,7 @@ VPPERSPECTIVE = 13
 
 class ViewportManager:
     """Manages the global viewport stuff."""
-    viewports = []
+    viewports: list[Viewport] = []
     gsg = None
 
     @staticmethod
@@ -58,7 +60,7 @@ class ViewportManager:
             v.Layout(*args, **kwargs)
 
 
-class Viewport(WxPandaWindow, DirectObject):
+class Viewport(WxPandaWindow, DirectObject):  # type: ignore[misc, valid-type]
     """Class representing a 3D Viewport."""
     CREATENEW  = CREATENEW
     VPLEFT     = VPLEFT

+ 2 - 1
direct/src/wxwidgets/WxPandaStart.py

@@ -1,2 +1,3 @@
+from direct.showbase.ShowBaseGlobal import base
 from .WxPandaShell import WxPandaShell
-base.app = WxPandaShell()
+base.app = WxPandaShell()  # type: ignore[attr-defined]

+ 1 - 1
direct/src/wxwidgets/WxPandaWindow.py

@@ -81,7 +81,7 @@ class EmbeddedPandaWindow(wx.Window):
 if not hasattr(wxgl, 'GLCanvas'):
     OpenGLPandaWindow = None
 else:
-    class OpenGLPandaWindow(wxgl.GLCanvas):
+    class OpenGLPandaWindow(wxgl.GLCanvas):  # type: ignore[no-redef]
         """ This class implements a Panda3D "window" that actually draws
         within the wx GLCanvas object.  It is supported whenever OpenGL is
         Panda's rendering engine, and GLCanvas is available in wx. """

+ 13 - 0
mypy.ini

@@ -0,0 +1,13 @@
+[mypy]
+explicit_package_bases = True
+exclude = /(dist|directscripts)/
+warn_unused_ignores = True
+
+[mypy-panda3d.*]
+ignore_missing_imports = True
+
+[mypy-wx.*]
+ignore_missing_imports = True
+
+[mypy-Pmw.*]
+ignore_missing_imports = True

+ 25 - 0
tests/run_mypy.py

@@ -0,0 +1,25 @@
+import os
+import pathlib
+import shutil
+import subprocess
+import tempfile
+
+
+def main():
+    root = pathlib.Path(__file__).parent.parent
+    direct_src = root / 'direct' / 'src'
+    mypy_config = root / 'mypy.ini'
+    with tempfile.TemporaryDirectory() as temp_dir:
+        os.environ['MYPYPATH'] = temp_dir
+        direct_copy = pathlib.Path(temp_dir, 'direct')
+        shutil.copytree(direct_src, direct_copy)
+        subprocess.run([
+            'mypy',
+            str(direct_copy),
+            '--config-file',
+            str(mypy_config),
+        ])
+
+
+if __name__ == '__main__':
+    main()