# Source code for moderngl_window.resources.base

    """
    Base registry class
    """

    import inspect
    from functools import lru_cache
    from typing import Any, Generator

    from moderngl_window.conf import settings
    from moderngl_window.exceptions import ImproperlyConfigured
    from moderngl_window.loaders.base import BaseLoader
    from moderngl_window.meta.base import ResourceDescription
    from moderngl_window.utils.module_loading import import_string

    class BaseRegistry:
        """Base class for all resource pools"""

        settings_attr = ""
        """str: The name of the attribute in :py:class:`~moderngl_window.conf.Settings`
        containting a list of loader classes.
        """

        def __init__(self) -> None:
            """Initialize internal attributes"""
            self._resources: list[ResourceDescription] = []

        @property
        def count(self) -> int:
            """int: The number of resource descriptions added.
            This is only relevant when using `add` and `load_pool`.
            """
            return len(self._resources)

        @property
        def loaders(self) -> Generator[type[BaseLoader], None, None]:
            """Generator: Loader classes for this resource type"""
            for loader in getattr(settings, self.settings_attr):
                yield self._loader_cls(loader)

        @lru_cache(maxsize=None)
        def _loader_cls(self, python_path: str) -> type[BaseLoader]:
            cls = import_string(python_path)
            assert issubclass(cls, BaseLoader), f"{python_path} does not lead to a Loader"
            return cls

        def load(self, meta: ResourceDescription) -> Any:
            """
            Loads a resource using the configured finders and loaders.

            Args:
                meta (ResourceDescription): The resource description
            """
            self._check_meta(meta)
            self.resolve_loader(meta)
            cls = meta.loader_cls(meta)
            assert cls is not None, f"Could not load {meta}, no arributes named 'loader_cls'"
            return cls.load()

        def add(self, meta: ResourceDescription) -> None:
            """
            Adds a resource description without loading it.
            The resource is loaded and returned when ``load_pool()`` is called.

            Args:
                meta (ResourceDescription): The resource description
            """
            self._check_meta(meta)
            self.resolve_loader(meta)
            self._resources.append(meta)

        def load_pool(self) -> Generator[tuple[ResourceDescription, Any], None, None]:
            """
            Loads all the data files using the configured finders.

            This is only relevant when resource have been added to this
            pool using ``add()``.

            Returns:
                Generator of (meta, resource) tuples
            """
            for meta in self._resources:
                resource = self.load(meta)
                yield meta, resource

            self._resources = []

        def resolve_loader(self, meta: ResourceDescription) -> None:
            """
            Attempts to assign a loader class to a ResourceDescription.

            Args:
                meta (:py:class:`~moderngl_window.meta.base.ResourceDescription`):
                The resource description instance
            """
            # Get loader using kind if specified
            if meta.kind:
                for loader_cls in self.loaders:
                    if loader_cls.kind == meta.kind:
                        meta.loader_cls = loader_cls
                        return

                raise ImproperlyConfigured(
                    "Resource has invalid loader kind '{}': {}\nAvailable loaders: {}".format(
                        meta.kind, meta, [loader.kind for loader in self.loaders]
                    )
                )

            # Get loader based on file extension
            for loader_cls in self.loaders:
                if loader_cls.supports_file(meta):
                    meta.loader_cls = loader_cls
                    return

            raise ImproperlyConfigured("Could not find a loader for: {}".format(meta))

        def _check_meta(self, meta: Any) -> None:
            """Check is the instance is a resource description
            Raises:
                ImproperlyConfigured if not a ResourceDescription instance
            """
            if inspect.isclass(type(meta)):
                if issubclass(meta.__class__, ResourceDescription):
                    return

            raise ImproperlyConfigured(
                "Resource loader got type {}, not a resource description".format(type(meta))
            )