def is_registration_compatible_with_requested_key(
         self,
         requested_key: RegistrationKey,
         registered_key: RegistrationKey
 ):
     requested_converter: Converter = requested_key.bean_contract
     registered_converter: Converter = registered_key.bean_contract
     return can_substitute(requested_converter.generic_TFrom, registered_converter.generic_TFrom) \
            and can_substitute(registered_converter.generic_TTo, requested_converter.generic_TTo)
Beispiel #2
0
    def test_can_substitute_to_generic_class_bound(self):
        TB = typing.TypeVar("TB", bound=BaseClass1)

        class GenericClassBound(typing.Generic[TB]):
            pass

        assert can_substitute(GenericClassBound[DerivedClass1], GenericClassBound[TB])
        assert not can_substitute(GenericClassBound[TB], GenericClassBound[DerivedClass1])
        assert can_substitute(GenericClassBound[DerivedClass1], GenericClassBound)
        assert not can_substitute(GenericClassBound, GenericClassBound[DerivedClass1])
Beispiel #3
0
    def identity(cls) -> "Converter[TFrom, TTo]":
        """
        :return: An identity converter that maps ``x`` to ``x``.
        :raises ValueError: If ``type_from`` or ``type_to`` is a :py:class:`~typing.TypeVar`.
        :raises ValueError: If ``type_from`` can't substitute ``type_to`` according to the Liskov Substitution \
                            Principle. More specifically, a ``ValueError`` is raised if \
                            ``not can_substitute(type_from, type_to)``. See \
                            :py:func:`~grundzeug.reflection.types.can_substitute` for more details.
        """
        type_args = get_type_arguments(cls)
        type_from = type_args[TFrom]
        type_to = type_args[TTo]
        if isinstance(type_to, typing.TypeVar) or isinstance(
                type_from, typing.TypeVar):
            raise ValueError(
                f"Attempt to call 'identity' on a generic Converter class. Please provide concrete type "
                f"arguments to resolve this error.")
        if not can_substitute(type_from, type_to):
            raise ValueError(
                f"TFrom={type_from} must be a subclass of TTo={type_to} for an identity mapping to exist "
                f"from TFrom to TTo.")

        class _IdentityConverter(cls):
            def __call__(self, value: TFrom) -> TTo:
                return value

        return _IdentityConverter()
Beispiel #4
0
 def test_can_substitute_to_tuples(self):
     assert can_substitute(Tuple[DerivedClass1], Tuple[BaseClass1])
     assert can_substitute(Tuple[DerivedClass1], tuple)
     assert can_substitute(Tuple[DerivedClass1, DerivedClass2], Tuple[BaseClass1, BaseClass2])
     assert can_substitute(Tuple[DerivedClass1, BaseClass2], Tuple[BaseClass1, BaseClass2])
     assert not can_substitute(Tuple[BaseClass1, BaseClass2], Tuple[DerivedClass1, DerivedClass2])
     assert not can_substitute(Tuple[BaseClass1, BaseClass2], Tuple[DerivedClass1, BaseClass2])
Beispiel #5
0
 def test_can_substitute_to_callable_callable(self):
     assert can_substitute(
         typing.Callable[[BaseClass1], BaseClass2],
         typing.Callable[[DerivedClass1], BaseClass2]
     )
     assert not can_substitute(
         typing.Callable[[DerivedClass1], BaseClass2],
         typing.Callable[[BaseClass1], BaseClass2]
     )
     assert can_substitute(
         typing.Callable[[BaseClass1], DerivedClass2],
         typing.Callable[[BaseClass1], BaseClass2]
     )
     assert not can_substitute(
         typing.Callable[[BaseClass1], BaseClass2],
         typing.Callable[[BaseClass1], DerivedClass2]
     )
     assert can_substitute(
         typing.Callable[[BaseClass1], DerivedClass2],
         typing.Callable[[DerivedClass1], BaseClass2]
     )
     assert not can_substitute(
         typing.Callable[[DerivedClass1], BaseClass2],
         typing.Callable[[BaseClass1], DerivedClass2]
     )
    def choose_best_candidate(
            self,
            requested_key: RegistrationKey,
            candidates: typing.OrderedDict[RegistrationKey, typing.Tuple[ContainerRegistration, IContainer]]
    ) -> typing.Optional[typing.Tuple[ContainerRegistration, IContainer]]:
        # Eliminate candidates which have more specific overrides.
        # For example, if the candidate list consists of Converter[Any, str] and Converter[int, str], then
        # Converter[Any, str] will be considered to be dominated by Converter[int, str], because it is less specific.
        # However, if we request a Converter[int, BaseClass] and the candidate list consists of
        # Converter[int, BaseClass] and Converter[int, DerivedClass], Converter[int, BaseClass] will be returned
        # because BaseClass is "closer" to the requested return type.
        is_dominated = [False for _ in candidates]
        for i, registration_key in enumerate(candidates):
            if is_dominated[i]:
                continue
            for j, other_registration_key in enumerate(candidates):
                if i == j or is_dominated[j]:
                    continue
                arg1 = registration_key.bean_contract.generic_TFrom
                arg2 = other_registration_key.bean_contract.generic_TFrom
                ret1 = registration_key.bean_contract.generic_TTo
                ret2 = other_registration_key.bean_contract.generic_TTo
                if can_substitute(arg1, arg2) \
                        and can_substitute(ret2, ret1):
                    is_dominated[j] = True

        result = [
            resolved
            for resolved, is_dominated
            in zip(candidates, is_dominated)
            if not is_dominated
        ]
        if len(result) < 1:
            return None
        elif len(result) > 1:
            raise Exception("Ambiguous resolution!")
        else:
            return candidates[result[0]]
Beispiel #7
0
    def test_can_substitute_to_generic_class_constraint(self):
        TC = typing.TypeVar("TC", BaseClass1, int)

        class GenericClassConstraint(typing.Generic[TC]):
            pass

        assert can_substitute(GenericClassConstraint[BaseClass1], GenericClassConstraint[TC])
        assert not can_substitute(GenericClassConstraint[DerivedClass1], GenericClassConstraint[TC])
        assert not can_substitute(GenericClassConstraint[TC], GenericClassConstraint[DerivedClass1])
        assert can_substitute(GenericClassConstraint[BaseClass1], GenericClassConstraint)
        assert not can_substitute(GenericClassConstraint[DerivedClass1], GenericClassConstraint)
        assert not can_substitute(GenericClassConstraint, GenericClassConstraint[DerivedClass1])
Beispiel #8
0
 def test_can_substitute_to_generic_subclass(self):
     assert can_substitute(DerivedGenericClass[str, int], GenericClass[int])
     assert not can_substitute(DerivedGenericClass[int, str], GenericClass[int])
Beispiel #9
0
 def test_can_substitute_to_generic_class(self):
     assert can_substitute(GenericClass[int], GenericClass[T1])
     assert not can_substitute(GenericClass[T1], GenericClass[int])
     assert can_substitute(GenericClass[int], GenericClass)
     assert not can_substitute(GenericClass, GenericClass[int])
Beispiel #10
0
 def test_can_substitute_to_none_optional(self):
     assert can_substitute(None, Optional[str])
Beispiel #11
0
 def test_can_substitute_to_any(self):
     assert can_substitute(str, Any)
     assert can_substitute(type, Any)
     assert can_substitute(None, Any)
     assert can_substitute(Tuple[DerivedClass1, DerivedClass2], Any)
Beispiel #12
0
 def test_can_substitute_to_callable_class(self):
     assert can_substitute(CallableClass, typing.Callable[[str, float], int])
Beispiel #13
0
 def test_can_substitute_to_optional(self):
     assert can_substitute(DerivedClass1, Optional[DerivedClass1])
     assert can_substitute(DerivedClass1, Optional[BaseClass1])
     assert not can_substitute(Optional[DerivedClass1], DerivedClass1)
     assert not can_substitute(Optional[BaseClass1], DerivedClass1)
 def is_registration_key_supported(self, registration_key: RegistrationKey):
     if registration_key.bean_name is not None:
         return False
     return can_substitute(registration_key.bean_contract, Converter, assume_cant_substitute=True)