diff --git a/pytypes/type_util.py b/pytypes/type_util.py index 976f96f..eb0c0b0 100644 --- a/pytypes/type_util.py +++ b/pytypes/type_util.py @@ -1535,7 +1535,7 @@ def _issubclass(subclass, superclass, bound_Generic=None, bound_typevars=None, bound_typevars : Optional[Dict[typing.TypeVar, type]] A dictionary holding values for unbound typevars occurring in ``subclass`` or ``superclass``. - Default: None + Default: {} Depending on ``bound_typevars_readonly`` pytypes can also bind values to typevars as needed. This is done by inserting according mappings into this dictionary. This can e.g. be useful to infer values for ``TypeVar``s or to consistently check a set of ``TypeVar``s across multiple @@ -1543,7 +1543,7 @@ def _issubclass(subclass, superclass, bound_Generic=None, bound_typevars=None, In collision case with ``bound_Generic`` the value from ``bound_Generic`` if preferred. bound_typevars_readonly : bool - Defines if pytypes is allowed to write into the ``bound_typevars`` dictionary if not ``None``. + Defines if pytypes is allowed to write into the ``bound_typevars`` dictionary. Default: True If set to False, pytypes cannot assign values to ``TypeVar``s, but only checks regarding values already present in ``bound_typevars`` or ``bound_Generic``. @@ -1561,6 +1561,8 @@ def _issubclass(subclass, superclass, bound_Generic=None, bound_typevars=None, a ``_ForwardRef`` is encountered, pytypes automatically creates this dictionary and continues in recursion-proof manner. """ + if bound_typevars is None: + bound_typevars = {} if superclass is Any: return True if subclass == superclass: @@ -1795,7 +1797,7 @@ def _isinstance(obj, cls, bound_Generic=None, bound_typevars=None, bound_typevars : Optional[Dict[typing.TypeVar, type]] A dictionary holding values for unbound typevars occurring in ``cls``. - Default: None + Default: {} Depending on ``bound_typevars_readonly`` pytypes can also bind values to typevars as needed. This is done by inserting according mappings into this dictionary. This can e.g. be useful to infer values for ``TypeVar``s or to consistently check a set of ``TypeVar``s across multiple @@ -1803,7 +1805,7 @@ def _isinstance(obj, cls, bound_Generic=None, bound_typevars=None, In collision case with ``bound_Generic`` the value from ``bound_Generic`` if preferred. bound_typevars_readonly : bool - Defines if pytypes is allowed to write into the ``bound_typevars`` dictionary if not ``None``. + Defines if pytypes is allowed to write into the ``bound_typevars`` dictionary. Default: True If set to False, pytypes cannot assign values to ``TypeVar``s, but only checks regarding values already present in ``bound_typevars`` or ``bound_Generic``. @@ -1821,6 +1823,8 @@ def _isinstance(obj, cls, bound_Generic=None, bound_typevars=None, a ``ForwardRef`` is encountered, pytypes automatically creates this dictionary and continues in recursion-proof manner. """ + if bound_typevars is None: + bound_typevars = {} # Special treatment if cls is Iterable[...] if is_Generic(cls) and cls.__origin__ is typing.Iterable: if not is_iterable(obj): diff --git a/tests/test_typechecker.py b/tests/test_typechecker.py index 9106d6b..7fb1565 100644 --- a/tests/test_typechecker.py +++ b/tests/test_typechecker.py @@ -4668,6 +4668,71 @@ class Foo(typing.Generic[T]): def test_frozenset(self): self.assertTrue(pytypes.is_of_type(frozenset({1, 2, 'a', None, 'b'}), typing.AbstractSet[typing.Union[str, int, None]])) + # See: https://github.com/Stewori/pytypes/issues/32 + # See: https://github.com/Stewori/pytypes/issues/33 + def test_empty_values(self): + self.assertTrue(pytypes.is_of_type([], typing.Sequence)) + self.assertTrue(pytypes.is_of_type([], typing.Sequence[int])) + + for interface in (typing.Iterable, typing.Sized, typing.Container): + self.assertTrue(isinstance(set(), interface), interface) + self.assertTrue(pytypes.is_of_type(set(), interface), interface) + self.assertTrue(isinstance([], interface), interface) + self.assertTrue(pytypes.is_of_type([], interface), interface) + + # See: https://github.com/Stewori/pytypes/issues/21 + def test_tuple_ellipsis(self): + class Foo: + pass + + self.assertTrue(pytypes.is_subtype(typing.Tuple[Foo], typing.Tuple[object, ...])) + self.assertTrue(pytypes.is_subtype(typing.Tuple[Foo], typing.Tuple[typing.Any, ...])) + + # See: https://github.com/Stewori/pytypes/issues/24 + def test_bound_typevars_readonly(self): + T = typing.TypeVar('T', covariant=True) + + class L(typing.List[T]): + pass + + C = typing.TypeVar('T', bound=L) + + self.assertTrue(pytypes.is_subtype(L[float], C)) + self.assertTrue(pytypes.is_subtype(L[float], C, bound_typevars={})) + self.assertFalse(pytypes.is_subtype(L[float], C, bound_typevars_readonly=True, bound_typevars={})) + self.assertTrue(pytypes.is_subtype(L[float], C, bound_typevars_readonly=False, bound_typevars={})) + + # See: https://github.com/Stewori/pytypes/issues/22 + def test_forward_declaration(self): + Wrapper = typing.Union[ + typing.Sequence['Data'], + ] + + Data = typing.Union[ + Wrapper, + str, bytes, bool, float, int, dict, + ] + + with self.assertRaises(pytypes.ForwardRefError): + pytypes.is_subtype(typing.Sequence[float], Wrapper) + + pytypes.resolve_fw_decl(Wrapper) + + self.assertTrue(pytypes.is_subtype(typing.Sequence[float], Wrapper)) + self.assertTrue(pytypes.is_subtype(int, Data)) + self.assertTrue(pytypes.is_subtype(float, Data)) + self.assertFalse(pytypes.is_subtype(Data, Wrapper)) + self.assertTrue(pytypes.is_subtype(Wrapper, Data)) + + # See: https://github.com/Stewori/pytypes/issues/22 + def test_forward_declaration_infinite_recursion(self): + Data = typing.Union['Wrapper', float] + Wrapper = typing.Union[Data, int] + + pytypes.resolve_fw_decl(Data) + + self.assertFalse(pytypes.is_subtype(list, Wrapper)) + if __name__ == '__main__': unittest.main()