Source code for warg.gdkc

#!/usr/bin/env python3

__author__ = "Christian Heider Lindbjerg"
__doc__ = """
Generalised wrapper for delayed construction of class objects. Encapsulates kwargs and callable constructor with the option of modifying construction arguments before construction is finally performed.

"""

__all__ = ["GeneralisedDelayedKwargConstruction", "GDKC"]


import logging
from typing import Any, Callable, Mapping, MutableMapping, Sequence

_logger = logging.getLogger(__name__)


[docs] class GeneralisedDelayedKwargConstruction: """ A generalised class for setting up kwargs for later construction of an instance of an object [constructor, args, kwargs] """
[docs] def __init__(self, constructor: Callable, *args: Any, **kwargs: Any): """ [constructor, args, kwargs] :param constructor: The delayed callable, to be evaluated at __call__ or context __enter__ :param args: arguments to use for evaluation, If only one is provided and is of typing mapping, it is assumed to be directly kwargs :param kwargs: arguments to use for evaluation """ self.constructor: Callable = constructor assert len(args) < 2, f"Does not support multiple args, only a single mapping type" if len(args) == 1: assert isinstance( args[0], Mapping ), f"Arg[0] type is not a mapping type, was {type(args[0])} which is not supported" assert not ( len(kwargs) > 0 and len(args) > 0 ), f"Does not support both args and kwargs, both supplied args, {args} and {kwargs}" if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], Mapping): self.kwargs: MutableMapping = args[0] elif len(kwargs) == 1 and next(iter(kwargs.keys())) == "kwargs": self.kwargs = kwargs["kwargs"] else: self.kwargs: MutableMapping = kwargs
def __enter__(self) -> Any: self.instance = self.__call__() return self.instance.__enter__() def __exit__(self, exc_type, exc_val, exc_tb) -> Any: return self.instance.__exit__(exc_type, exc_val, exc_tb) def __call__(self, *args, **kwargs) -> Any: """ Allows last minute override of kwargs :param args: :param kwargs: :return: """ war = "".join( [ f"Overwriting {k} with the value {v} in construction of {self.constructor}" for (k, v), b in zip(kwargs.items(), self.kwargs.keys()) if k == b ] ) if war != "": _logger.warning(war) self.kwargs.update(kwargs) try: return self.constructor(*args, **self.kwargs) except TypeError as e: e.args += (f"in construction of {self.constructor}",) raise e
GDKC = GeneralisedDelayedKwargConstruction if __name__ == "__main__": class UnreachableError(Exception): pass class A: """description""" def __init__(self, *args: Sequence[Any], **kwargs: MutableMapping[str, Any]): pass def stest_not_both() -> None: """ :rtype: None """ GeneralisedDelayedKwargConstruction(A, [1], a=2) def stest_kw() -> None: """ :rtype: None """ GeneralisedDelayedKwargConstruction(A, a=2) def stest_mapping() -> None: """ :rtype: None """ GeneralisedDelayedKwargConstruction(A, {"a": 2}) def stest_mapping_and_args_fail() -> None: """ :rtype: None """ GeneralisedDelayedKwargConstruction(A, {"a": 2}, 1) def stest_mapping_and_args_fail_inv() -> None: """ :rtype: None """ GeneralisedDelayedKwargConstruction(A, 1, {"a": 2}) stest_kw() stest_mapping() try: stest_mapping() # Successful raise UnreachableError except UnreachableError: # Is expected pass try: stest_mapping_and_args_fail() raise UnreachableError except AssertionError: pass try: stest_mapping_and_args_fail_inv() raise UnreachableError except AssertionError: pass try: stest_not_both() raise UnreachableError except AssertionError: pass