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

This commit is contained in:
WMOkiishi 2025-02-19 10:59:19 -07:00 committed by GitHub
parent a7f8c57681
commit 8de98592ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 54 additions and 22 deletions

View File

@ -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

View File

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