diff --git a/direct/src/showbase/VFSImporter.py b/direct/src/showbase/VFSImporter.py index 76964dfc77..3bfa2336f1 100644 --- a/direct/src/showbase/VFSImporter.py +++ b/direct/src/showbase/VFSImporter.py @@ -5,9 +5,11 @@ Calling the :func:`register()` function to register the import hooks should be sufficient to enable this functionality. """ +from __future__ import annotations + __all__ = ['register'] -from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem +from panda3d.core import Filename, VirtualFile, VirtualFileSystem, VirtualFileMountSystem from panda3d.core import OFileStream, copy_stream import sys import marshal @@ -16,14 +18,17 @@ import atexit from importlib.abc import Loader, SourceLoader from importlib.util import MAGIC_NUMBER, decode_source from importlib.machinery import ModuleSpec, EXTENSION_SUFFIXES, BYTECODE_SUFFIXES +from types import ModuleType +from typing import Any vfs = VirtualFileSystem.get_global_ptr() -def _make_spec(fullname, loader, *, is_package): +def _make_spec(fullname: str, loader: VFSLoader, *, is_package: bool) -> ModuleSpec: filename = loader._vfile.get_filename() spec = ModuleSpec(fullname, loader, origin=filename.to_os_specific(), is_package=is_package) if is_package: + assert spec.submodule_search_locations is not None spec.submodule_search_locations.append(Filename(filename.get_dirname()).to_os_specific()) spec.has_location = True return spec @@ -35,16 +40,15 @@ class VFSFinder: which allows loading Python source files from mounted .mf files (among other places). """ - def __init__(self, path): + def __init__(self, path: str) -> None: self.path = path - def find_spec(self, fullname, path, target=None): - if path is None: - path = self.path - + def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None: #print(f"find_spec({fullname}), dir_path = {dir_path}", file=sys.stderr) basename = fullname.split('.')[-1] - filename = Filename(Filename.from_os_specific(path), basename) + filename = Filename(Filename.from_os_specific(self.path), basename) + + loader: VFSLoader # First, look for Python files. vfile = vfs.get_file(filename + '.py', True) @@ -84,6 +88,7 @@ class VFSFinder: # Consider a namespace package. if vfs.is_directory(filename): spec = ModuleSpec(fullname, VFSNamespaceLoader(), is_package=True) + assert spec.submodule_search_locations is not None spec.submodule_search_locations.append(filename.to_os_specific()) return spec @@ -92,7 +97,7 @@ class VFSFinder: class VFSLoader(Loader): - def __init__(self, fullname, vfile): + def __init__(self, fullname: str, vfile: VirtualFile) -> None: self.name = fullname self._vfile = vfile @@ -105,22 +110,22 @@ class VFSLoader(Loader): tail_name = fullname.rpartition('.')[2] return filename_base == '__init__' and tail_name != '__init__' - def create_module(self, spec): + def create_module(self, spec: ModuleSpec) -> ModuleType | None: """Use default semantics for module creation.""" - def exec_module(self, module): + def exec_module(self, module: ModuleType) -> None: """Execute the module.""" - code = self.get_code(module.__name__) + code = self.get_code(module.__name__) # type: ignore[attr-defined] exec(code, module.__dict__) - def get_filename(self, fullname): + def get_filename(self, fullname: str) -> str: if fullname is not None and self.name != fullname: raise ImportError return self._vfile.get_filename().to_os_specific() @staticmethod - def get_data(path): + def get_data(path: str) -> bytes: vfile = vfs.get_file(Filename.from_os_specific(path)) if vfile: return vfile.read_file(True) @@ -128,7 +133,7 @@ class VFSLoader(Loader): raise OSError @staticmethod - def path_stats(path): + def path_stats(path: str) -> dict[str, Any]: vfile = vfs.get_file(Filename.from_os_specific(path)) if vfile: return {'mtime': vfile.get_timestamp(), 'size': vfile.get_file_size()} @@ -231,11 +236,11 @@ class VFSExtensionLoader(VFSLoader): return None -class VFSNamespaceLoader: - def create_module(self, spec): +class VFSNamespaceLoader(Loader): + def create_module(self, spec: ModuleSpec) -> ModuleType | None: """Use default semantics for module creation.""" - def exec_module(self, module): + def exec_module(self, module: ModuleType) -> None: pass def is_package(self, fullname): @@ -248,7 +253,7 @@ class VFSNamespaceLoader: return compile('', '', 'exec', dont_inherit=True) -def _path_hook(entry): +def _path_hook(entry: str) -> VFSFinder: # If this is a directory in the VFS, create a VFSFinder for this entry. vfile = vfs.get_file(Filename.from_os_specific(entry), False) if vfile and vfile.is_directory() and not isinstance(vfile.get_mount(), VirtualFileMountSystem): @@ -259,7 +264,7 @@ def _path_hook(entry): _registered = False -def register(): +def register() -> None: """ Register the VFSFinder on the path_hooks, if it has not already been registered, so that future Python import statements will vector through here (and therefore will take advantage of