فهرست منبع

showbase: Annotate basic `Loader` methods (#1723)

WMOkiishi 9 ماه پیش
والد
کامیت
8de98592ca
2فایلهای تغییر یافته به همراه54 افزوده شده و 22 حذف شده
  1. 51 21
      direct/src/showbase/Loader.py
  2. 3 1
      tests/showbase/test_Loader.py

+ 51 - 21
direct/src/showbase/Loader.py

@@ -2,9 +2,13 @@
 sound, music, shaders and fonts from disk.
 """
 
+from __future__ import annotations
+
 __all__ = ['Loader']
 
 from panda3d.core import (
+    AsyncTask,
+    AudioManager,
     ConfigVariableBool,
     Filename,
     FontPool,
@@ -24,13 +28,15 @@ from panda3d.core import (
 from panda3d.core import Loader as PandaLoader
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.DirectObject import DirectObject
+from . import ShowBase
 import warnings
 import sys
+from typing import Any, Callable, Iterable
 
 # You can specify a phaseChecker callback to check
 # a modelPath to see if it is being loaded in the correct
 # phase
-phaseChecker = None
+phaseChecker: Callable[[str, LoaderOptions], object] | None = None
 
 
 class Loader(DirectObject):
@@ -49,16 +55,23 @@ class Loader(DirectObject):
         # This indicates that this class behaves like a Future.
         _asyncio_future_blocking = False
 
-        def __init__(self, loader, numObjects, gotList, callback, extraArgs):
+        def __init__(
+            self,
+            loader: Loader | None,
+            numObjects: int,
+            gotList: bool,
+            callback: Callable[..., object] | None,
+            extraArgs: list,
+        ) -> None:
             self._loader = loader
-            self.objects = [None] * numObjects
+            self.objects: list[Any | None] = [None] * numObjects
             self.gotList = gotList
             self.callback = callback
             self.extraArgs = extraArgs
-            self.requests = set()
-            self.requestList = []
+            self.requests: set[AsyncTask] = set()
+            self.requestList: list[AsyncTask] = []
 
-        def gotObject(self, index, object):
+        def gotObject(self, index: int, object) -> None:
             self.objects[index] = object
 
             if not self.requests:
@@ -79,7 +92,7 @@ class Loader(DirectObject):
                 self.requests = None
                 self.requestList = None
 
-        def cancelled(self):
+        def cancelled(self) -> bool:
             "Returns true if the request was cancelled."
             return self.requestList is None
 
@@ -87,7 +100,7 @@ class Loader(DirectObject):
             "Returns true if all the requests were finished or cancelled."
             return not self.requests
 
-        def result(self):
+        def result(self) -> Any:
             "Returns the results, suspending the thread to wait if necessary."
             for r in list(self.requests):
                 r.wait()
@@ -126,26 +139,26 @@ class Loader(DirectObject):
                 yield await req
 
     # special methods
-    def __init__(self, base=None):
+    def __init__(self, base: ShowBase.ShowBase | None = None) -> None:
         self.base = base
         self.loader = PandaLoader.getGlobalPtr()
 
-        self._requests = {}
+        self._requests: dict[AsyncTask, tuple[Loader._Callback, int]] = {}
 
         self.hook = "async_loader_%s" % (Loader.loaderIndex)
         Loader.loaderIndex += 1
 
-    def destroy(self):
+    def destroy(self) -> None:
         self.ignore(self.hook)
         self.loader.stopThreads()
         del self.base
 
-    def _init_base(self, base):
+    def _init_base(self, base: ShowBase.ShowBase) -> None:
         self.base = base
         self.accept(self.hook, self.__gotAsyncObject)
 
     @classmethod
-    def _loadPythonFileTypes(cls):
+    def _loadPythonFileTypes(cls) -> None:
         if cls._loadedPythonFileTypes:
             return
 
@@ -168,10 +181,18 @@ class Loader(DirectObject):
             cls._loadedPythonFileTypes = True
 
     # model loading funcs
-    def loadModel(self, modelPath, loaderOptions = None, noCache = None,
-                  allowInstance = False, okMissing = None,
-                  callback = None, extraArgs = [], priority = None,
-                  blocking = None):
+    def loadModel(
+        self,
+        modelPath: str | list[str] | tuple[str, ...] | set[str],
+        loaderOptions: LoaderOptions | None = None,
+        noCache: bool | None = None,
+        allowInstance: bool = False,
+        okMissing: bool | None = None,
+        callback: Callable[..., object] | None = None,
+        extraArgs: list = [],
+        priority: int | None = None,
+        blocking: bool | None = None,
+    ) -> Any:
         """
         Attempts to load a model or models from one or more relative
         pathnames.  If the input modelPath is a string (a single model
@@ -255,6 +276,7 @@ class Loader(DirectObject):
         if allowInstance:
             loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFAllowInstance)
 
+        modelList: Iterable[str]
         if not isinstance(modelPath, (tuple, list, set)):
             # We were given a single model pathname.
             modelList = [modelPath]
@@ -949,7 +971,7 @@ class Loader(DirectObject):
         TexturePool.releaseTexture(texture)
 
     # sound loading funcs
-    def loadSfx(self, *args, **kw):
+    def loadSfx(self, *args, **kw) -> Any:
         """Loads one or more sound files, specifically designated as a
         "sound effect" file (that is, uses the sfxManager to load the
         sound).  There is no distinction between sound effect files
@@ -959,6 +981,7 @@ class Loader(DirectObject):
         independently of the other group."""
 
         # showbase-created sfxManager should always be at front of list
+        assert self.base is not None
         if self.base.sfxManagerList:
             return self.loadSound(self.base.sfxManagerList[0], *args, **kw)
         return None
@@ -976,8 +999,14 @@ class Loader(DirectObject):
         else:
             return None
 
-    def loadSound(self, manager, soundPath, positional = False,
-                  callback = None, extraArgs = []):
+    def loadSound(
+        self,
+        manager: AudioManager,
+        soundPath: str | tuple[str, ...] | list[str] | set[str],
+        positional: bool = False,
+        callback: Callable[..., object] | None = None,
+        extraArgs: list = [],
+    ) -> Any:
         """Loads one or more sound files, specifying the particular
         AudioManager that should be used to load them.  The soundPath
         may be either a single filename, or a list of filenames.  If a
@@ -987,6 +1016,7 @@ class Loader(DirectObject):
 
         from panda3d.core import AudioLoadRequest
 
+        soundList: Iterable[str]
         if not isinstance(soundPath, (tuple, list, set)):
             # We were given a single sound pathname or a MovieAudio instance.
             soundList = [soundPath]
@@ -1112,7 +1142,7 @@ class Loader(DirectObject):
             else:
                 callback(*(origModelList + extraArgs))
 
-    def __gotAsyncObject(self, request):
+    def __gotAsyncObject(self, request: AsyncTask) -> None:
         """A model or sound file or some such thing has just been
         loaded asynchronously by the sub-thread.  Add it to the list
         of loaded objects, and call the appropriate callback when it's

+ 3 - 1
tests/showbase/test_Loader.py

@@ -6,7 +6,9 @@ import sys
 
 @pytest.fixture
 def loader():
-    return Loader(base=None)
+    loader = Loader(base=None)
+    yield loader
+    loader.destroy()
 
 
 @pytest.fixture