1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
import logging
from collections.abc import Iterable
from pathlib import Path
from typing import Any, Optional, Union
import moderngl
import moderngl_window as mglw
from moderngl_window.finders import data, program, scene, texture
from moderngl_window.finders.base import BaseFilesystemFinder
from moderngl_window.meta.base import ResourceDescription
logger = logging.getLogger(__name__)
class BaseLoader:
"""Base loader class for all resources"""
kind = "unknown"
"""
The kind of resource this loaded supports.
This can be used when file extensions is not enough
to decide what loader should be selected.
"""
file_extensions: list[list[str]] = []
"""
A list defining the file extensions accepted by this loader.
Example::
# Loader will match .xyz and .xyz.gz files.
file_extensions = [
['.xyz'],
['.xyz', '.gz'],
]
"""
def __init__(self, meta: ResourceDescription) -> None:
"""Initialize loader.
Loaders take a ResourceDescription instance
containing all the parameters needed to load and initialize
this data.
Args:
meta (ResourceDescription): The resource to load
"""
self.meta = meta
if self.kind is None:
raise ValueError("Loader {} doesn't have a kind".format(self.__class__))
@classmethod
def supports_file(cls: type["BaseLoader"], meta: ResourceDescription) -> bool:
"""Check if the loader has a supported file extension.
What extensions are supported can be defined in the
:py:attr:`file_extensions` class attribute.
"""
path = Path(meta.path if meta.path is not None else "")
for ext in cls.file_extensions:
if path.suffixes[: len(ext)] == ext:
return True
return False
def load(self) -> Any:
"""Loads a resource.
When creating a loader this is the only
method that needs to be implemented.
Returns:
The loaded resource
"""
raise NotImplementedError()
def find_data(self, path: Optional[Union[str, Path]]) -> Optional[Path]:
"""Find resource using data finders.
This is mainly a shortcut method to simplify the task.
Args:
path: Path to resource
"""
return self._find(path, data.get_finders())
def find_program(self, path: Optional[Union[str, Path]]) -> Optional[Path]:
"""Find resource using program finders.
This is mainly a shortcut method to simplify the task.
Args:
path: Path to resource
"""
return self._find(path, program.get_finders())
def find_texture(self, path: Optional[Union[str, Path]]) -> Optional[Path]:
"""Find resource using texture finders.
This is mainly a shortcut method to simplify the task.
Args:
path: Path to resource
"""
return self._find(path, texture.get_finders())
def find_scene(self, path: Optional[Union[str, Path]]) -> Optional[Path]:
"""Find resource using scene finders.
This is mainly a shortcut method to simplify the task.
Args:
path: Path to resource
"""
return self._find(path, scene.get_finders())
def _find(
self, path: Optional[Union[str, Path]], finders: Iterable[BaseFilesystemFinder]
) -> Optional[Path]:
"""Find the first occurrance of this path in all finders.
If the incoming path is an absolute path we assume this
path exist and return it.
Args:
path (str): The path to find
"""
if not path:
return None
if isinstance(path, str):
path = Path(path)
if path.is_absolute():
return path
for finder in finders:
result = finder.find(path)
if result:
return result
logger.debug("No finder was able to locate: %s", path)
return None
@property
def ctx(self) -> moderngl.Context:
"""moderngl.Context: ModernGL context"""
return mglw.ctx()
|