Source code for betty.factory

"""
Functionality for creating new class instances.
"""

from __future__ import annotations

from abc import abstractmethod, ABC
from typing import TypeVar, Self, cast, Protocol

from typing_extensions import override


[docs] class FactoryError(RuntimeError): """ Raised when a class could not be instantiated by a factory API. """
[docs] @classmethod def new(cls, new_cls: type) -> Self: """ Create a new instance. """ return cls(f"Could not instantiate {new_cls}")
[docs] class InitFactoryError(FactoryError): """ Raised when a class could not be instantiated by calling it directly. """
[docs] @override @classmethod def new(cls, new_cls: type) -> Self: return cls(f"Could not instantiate {new_cls} by calling {new_cls.__name__}()")
[docs] class IndependentFactory(ABC): """ Provide a factory for classes that can instantiate themselves asynchronously. """
[docs] @classmethod @abstractmethod async def new(cls) -> Self: """ Create a new instance. """ pass
_T = TypeVar("_T")
[docs] async def new(cls: type[_T]) -> _T: """ Create a new instance. :return: #. If ``cls`` extends :py:class:`betty.factory.IndependentFactory`, this will call return ``cls``'s ``new()``'s return value. #. Otherwise ``cls()`` will be called without arguments, and the resulting instance will be returned. :raises FactoryError: raised when ``cls`` could not be instantiated. """ if issubclass(cls, IndependentFactory): return cast(_T, await cls.new()) try: return cls() except Exception as error: raise InitFactoryError.new(cls) from error
[docs] class TargetFactory(ABC): """ Provide a factory for classes that depend on ``self``. """
[docs] @abstractmethod async def new_target(self, cls: type[_T]) -> _T: """ Create a new instance. :raises FactoryError: raised when ``cls`` could not be instantiated. """ pass
[docs] class Factory(Protocol): """ A callable to create a new instance. """ async def __call__(self, cls: type[_T]) -> _T: """ Create a new instance. :raises FactoryError: raised when ``cls`` could not be instantiated. """ pass