diff --git a/.idea/basedtyping.iml b/.idea/basedtyping.iml
index 8e64af1..3b53d89 100644
--- a/.idea/basedtyping.iml
+++ b/.idea/basedtyping.iml
@@ -8,8 +8,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
index 20d228e..bd80a55 100644
--- a/.idea/watcherTasks.xml
+++ b/.idea/watcherTasks.xml
@@ -15,7 +15,7 @@
-
+
@@ -35,7 +35,7 @@
-
+
diff --git a/basedtyping/__init__.py b/basedtyping/__init__.py
index 1a06b72..f66d0a1 100644
--- a/basedtyping/__init__.py
+++ b/basedtyping/__init__.py
@@ -2,9 +2,8 @@
from __future__ import annotations
-import contextlib
import sys
-from typing import (
+from typing import ( # type: ignore[attr-defined]
TYPE_CHECKING,
Any,
Callable,
@@ -17,32 +16,20 @@
Type,
TypeVar,
Union,
+ _GenericAlias,
_SpecialForm,
+ _tp_cache,
cast,
)
import typing_extensions
-from typing_extensions import Never, ParamSpec, TypeAlias, TypeGuard, TypeVarTuple
+from typing_extensions import Never, ParamSpec, Self, TypeAlias, TypeGuard, TypeVarTuple
from basedtyping.runtime_only import OldUnionType
-if TYPE_CHECKING:
- from typing_extensions import override
-else:
-
- def override(arg, /):
- # TODO: Remove when typing_extensions is >= 4.4
- with contextlib.suppress(AttributeError, TypeError):
- # Skip the attribute silently if it is not writable.
- # AttributeError happens if the object has __slots__ or a
- # read-only property, TypeError if it's a builtin class.
- arg.__override__ = True
- return arg
-
-
if not TYPE_CHECKING:
# TODO: remove the TYPE_CHECKING block once these are typed in basedtypeshed
- from typing import _GenericAlias, _remove_dups_flatten, _tp_cache, _type_check
+ from typing import _remove_dups_flatten, _type_check
if sys.version_info >= (3, 11):
from typing import _collect_parameters
@@ -64,20 +51,47 @@ def override(arg, /):
"issubform",
"Untyped",
"Intersection",
+ "TypeForm",
)
-if not TYPE_CHECKING:
+if TYPE_CHECKING:
+ _tp_cache_typed: Callable[[T], T]
+else:
+ _tp_cache_typed = _tp_cache
+
+
+class _BasedSpecialForm(_SpecialForm, _root=True): # type: ignore[misc]
+ _name: str
+
+ def __init_subclass__(cls, _root=False):
+ super().__init_subclass__(_root=_root) # type: ignore[call-arg]
+
+ def __init__(self, *args: object, **kwargs: object):
+ self.alias = kwargs.pop("alias", _BasedGenericAlias)
+ super().__init__(*args, **kwargs)
+
+ def __repr__(self) -> str:
+ return "basedtyping." + self._name
+
+ def __and__(self, other: object) -> object:
+ return Intersection[self, other]
- class _BasedSpecialForm(_SpecialForm, _root=True):
- def __repr__(self):
- return "basedtyping." + self._name
+ def __rand__(self, other: object) -> object:
+ return Intersection[other, self]
- if sys.version_info < (3, 9):
+ if sys.version_info < (3, 9):
- def __getitem__(self, item):
- if self._name == "Intersection":
- return _IntersectionGenericAlias(self, item)
- return None
+ @_tp_cache_typed
+ def __getitem__(self, item: object) -> object:
+ return self.alias(self, item) # type: ignore[operator]
+
+
+class _BasedGenericAlias(_GenericAlias, _root=True):
+ def __and__(self, other: object) -> object:
+ return Intersection[self, other]
+
+ def __rand__(self, other: object) -> object:
+ return Intersection[other, self]
if TYPE_CHECKING:
@@ -202,7 +216,7 @@ def _raise_generics_not_reified(cls) -> NoReturn:
f" to instantiate a reified class: {cls._orig_type_vars}"
)
- def _check_generics_reified(cls) -> None:
+ def _check_generics_reified(cls):
if not cls._generics_are_reified() or cls._has_non_reified_type_vars():
cls._raise_generics_not_reified()
@@ -221,7 +235,6 @@ def _is_subclass(cls, subclass: object) -> TypeGuard[_ReifiedGenericMetaclass]:
cast(_ReifiedGenericMetaclass, subclass)._orig_class(),
)
- @override
def __subclasscheck__(cls, subclass: object) -> bool:
if not cls._is_subclass(subclass):
return False
@@ -241,7 +254,6 @@ def __subclasscheck__(cls, subclass: object) -> bool:
subclass._check_generics_reified()
return cls._type_var_check(subclass.__reified_generics__)
- @override
def __instancecheck__(cls, instance: object) -> bool:
if not cls._is_subclass(type(instance)):
return False
@@ -252,7 +264,6 @@ def __instancecheck__(cls, instance: object) -> bool:
)
# need the generic here for pyright. see https://github.com/microsoft/pyright/issues/5488
- @override
def __call__(cls: type[T], *args: object, **kwargs: object) -> T:
"""A placeholder ``__call__`` method that gets called when the class is
instantiated directly, instead of first supplying the type parameters.
@@ -317,7 +328,7 @@ class ReifiedGeneric(Generic[T], metaclass=_ReifiedGenericMetaclass):
__type_vars__: tuple[TypeVar, ...]
"""``TypeVar``\\s that have not yet been reified. so this Tuple should always be empty by the time the ``ReifiedGeneric`` is instantiated"""
- @_tp_cache # type: ignore[name-defined, misc]
+ @_tp_cache # type: ignore[no-any-expr, misc]
def __class_getitem__( # type: ignore[no-any-decorated]
cls, item: GenericItems
) -> type[ReifiedGeneric[T]]:
@@ -375,8 +386,7 @@ def __class_getitem__( # type: ignore[no-any-decorated]
ReifiedGenericCopy._can_do_instance_and_subclass_checks_without_generics = False
return ReifiedGenericCopy
- @override
- def __init_subclass__(cls) -> None:
+ def __init_subclass__(cls):
cls._can_do_instance_and_subclass_checks_without_generics = True
super().__init_subclass__()
@@ -435,8 +445,8 @@ def issubform(form: _Forms, forminfo: _Forms) -> bool:
Untyped: TypeAlias = Any # type: ignore[no-any-explicit]
elif sys.version_info >= (3, 9):
- @_SpecialForm # `_SpecialForm`s init isn't typed
- def Untyped(self: _SpecialForm, parameters: object) -> NoReturn: # noqa: ARG001
+ @_BasedSpecialForm
+ def Untyped(self: _BasedSpecialForm, parameters: object) -> NoReturn:
"""Special type indicating that something isn't typed.
This is more specialized than ``Any`` and can help with gradually typing modules.
@@ -444,7 +454,6 @@ def Untyped(self: _SpecialForm, parameters: object) -> NoReturn: # noqa: ARG001
raise TypeError(f"{self} is not subscriptable")
else:
- # old version had the doc argument
Untyped: Final = _BasedSpecialForm(
"Untyped",
doc=(
@@ -453,75 +462,96 @@ def Untyped(self: _SpecialForm, parameters: object) -> NoReturn: # noqa: ARG001
),
)
-if not TYPE_CHECKING:
- class _IntersectionGenericAlias(_GenericAlias, _root=True):
- def copy_with(self, args):
- return Intersection[args]
+class _IntersectionGenericAlias(_BasedGenericAlias, _root=True):
+ def copy_with(self, args: object) -> Self: # type: ignore[override] # TODO: put in the overloads
+ return cast(Self, Intersection[args])
- def __eq__(self, other):
- if not isinstance(other, _IntersectionGenericAlias):
- return NotImplemented
- return set(self.__args__) == set(other.__args__)
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, _IntersectionGenericAlias):
+ return NotImplemented
+ return set(self.__args__) == set(other.__args__)
- def __hash__(self):
- return hash(frozenset(self.__args__))
+ def __hash__(self) -> int:
+ return hash(frozenset(self.__args__))
- def __instancecheck__(self, obj):
- return self.__subclasscheck__(type(obj))
+ def __instancecheck__(self, obj: object) -> bool:
+ return self.__subclasscheck__(type(obj))
- def __subclasscheck__(self, cls):
- return any(issubclass(cls, arg) for arg in self.__args__)
+ def __subclasscheck__(self, cls: type[object]) -> bool:
+ return all(issubclass(cls, arg) for arg in self.__args__)
- def __reduce__(self):
- func, (_, args) = super().__reduce__()
- return func, (Intersection, args)
+ def __reduce__(self) -> (object, object):
+ func, (_, args) = super().__reduce__() # type: ignore[no-any-expr, misc]
+ return func, (Intersection, args)
- if sys.version_info > (3, 9):
- @_BasedSpecialForm
- def Intersection(self, parameters):
- """Intersection type; Intersection[X, Y] means both X and Y.
+if sys.version_info > (3, 9):
- To define an intersection:
- - If using __future__.annotations, shortform can be used e.g. A & B
- - otherwise the fullform must be used e.g. Intersection[A, B].
+ @_BasedSpecialForm
+ def Intersection(self, parameters):
+ """Intersection type; Intersection[X, Y] means both X and Y.
- Details:
- - The arguments must be types and there must be at least one.
- - None as an argument is a special case and is replaced by
- type(None).
- - Intersections of intersections are flattened, e.g.::
+ To define an intersection:
+ - If using __future__.annotations, shortform can be used e.g. A & B
+ - otherwise the fullform must be used e.g. Intersection[A, B].
- Intersection[Intersection[int, str], float] == Intersection[int, str, float]
+ Details:
+ - The arguments must be types and there must be at least one.
+ - None as an argument is a special case and is replaced by
+ type(None).
+ - Intersections of intersections are flattened, e.g.::
- - Intersections of a single argument vanish, e.g.::
+ Intersection[Intersection[int, str], float] == Intersection[int, str, float]
- Intersection[int] == int # The constructor actually returns int
+ - Intersections of a single argument vanish, e.g.::
- - Redundant arguments are skipped, e.g.::
+ Intersection[int] == int # The constructor actually returns int
- Intersection[int, str, int] == Intersection[int, str]
+ - Redundant arguments are skipped, e.g.::
- - When comparing intersections, the argument order is ignored, e.g.::
+ Intersection[int, str, int] == Intersection[int, str]
- Intersection[int, str] == Intersection[str, int]
+ - When comparing intersections, the argument order is ignored, e.g.::
- - You cannot subclass or instantiate an intersection.
- """
- if parameters == ():
- raise TypeError("Cannot take an Intersection of no types.")
- if not isinstance(parameters, tuple):
- parameters = (parameters,)
- msg = "Intersection[arg, ...]: each arg must be a type."
- parameters = tuple(_type_check(p, msg) for p in parameters)
- parameters = _remove_dups_flatten(parameters)
- if len(parameters) == 1:
- return parameters[0]
- return _IntersectionGenericAlias(self, parameters)
+ Intersection[int, str] == Intersection[str, int]
+
+ - You cannot subclass or instantiate an intersection.
+ """
+ if parameters == ():
+ raise TypeError("Cannot take an Intersection of no types.")
+ if not isinstance(parameters, tuple):
+ parameters = (parameters,)
+ msg = "Intersection[arg, ...]: each arg must be a type."
+ parameters = tuple(_type_check(p, msg) for p in parameters)
+ parameters = _remove_dups_flatten(parameters)
+ if len(parameters) == 1:
+ return parameters[0]
+ return _IntersectionGenericAlias(self, parameters)
- else:
- # old version had the doc argument
- Intersection = _BasedSpecialForm("Intersection", doc="")
else:
- Intersection: _SpecialForm
+ Intersection = _BasedSpecialForm(
+ "Intersection", doc="", alias=_IntersectionGenericAlias
+ )
+
+
+class _TypeFormForm(_BasedSpecialForm, _root=True): # type: ignore[misc]
+ def __init__(self, doc: str):
+ self._name = "TypeForm"
+ self._doc = self.__doc__ = doc
+
+ def __getitem__(self, parameters: object | tuple[object]) -> _BasedGenericAlias:
+ if not isinstance(parameters, tuple):
+ parameters = (parameters,)
+
+ return _BasedGenericAlias(self, parameters) # type: ignore[arg-type]
+
+
+TypeForm = _TypeFormForm(doc="""\
+ A type that can be used to represent a ``builtins.type`` or a ``SpecialForm``.
+ For example:
+
+ def f[T](t: TypeForm[T]) -> T: ...
+
+ reveal_type(f(int | str)) # int | str
+ """)
diff --git a/basedtyping/typetime_only.py b/basedtyping/typetime_only.py
index 38af0c0..4df1832 100644
--- a/basedtyping/typetime_only.py
+++ b/basedtyping/typetime_only.py
@@ -25,5 +25,5 @@ class assert_type(Generic[T]):
# TODO: make this use ReifiedGeneric so that it can check at runtime
# None return type on __new__ is supported in pyright but not mypy
- def __new__(cls, _value: T) -> None: # type: ignore[misc]
+ def __new__(cls, _value: T): # type: ignore[empty-body]
pass
diff --git a/pyproject.toml b/pyproject.toml
index c06d391..16f7896 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,6 +23,8 @@ requires = ["poetry-core>=1.0.8"]
[tool.mypy]
python_version = 3.8
+# we can't use override until we bump the minimum typing_extensions or something
+disable_error_code = ["explicit-override"]
[tool.black]
target-version = ["py38"]
diff --git a/tests/test_basedspecialform.py b/tests/test_basedspecialform.py
new file mode 100644
index 0000000..8865e5b
--- /dev/null
+++ b/tests/test_basedspecialform.py
@@ -0,0 +1,26 @@
+from __future__ import annotations
+
+import sys
+
+import pytest
+
+from basedtyping import Intersection, TypeForm, Untyped
+
+
+def test_basedgenericalias_intersection():
+ assert TypeForm[int] & None == Intersection[TypeForm[int], None]
+ assert None & TypeForm[int] == Intersection[None, TypeForm[int]]
+
+
+@pytest.mark.xfail(
+ sys.version_info >= (3, 9),
+ reason="""
+`typing._type_check` says:
+
+ if arg in (Any, LiteralString, NoReturn, Never, Self, TypeAlias):
+ return arg
+""",
+)
+def test_basedspecialform_intersection():
+ assert Untyped & None == Intersection[Untyped, None] # type: ignore[no-any-expr]
+ assert None & Untyped == Intersection[Untyped, None] # type: ignore[no-any-expr]
diff --git a/tests/test_function_type.py b/tests/test_function_type.py
index 13f2b74..dd07b2b 100644
--- a/tests/test_function_type.py
+++ b/tests/test_function_type.py
@@ -10,30 +10,30 @@
assert_function = assert_type[Function]
- def test_lambda_type() -> None:
+ def test_lambda_type():
assert_function(lambda: ...)
- def test_function_type() -> None:
- def func() -> None:
+ def test_function_type():
+ def func():
pass
assert_function(func)
- def test_builtin_function() -> None:
+ def test_builtin_function():
assert_function(len)
- def test_builtin_method() -> None:
+ def test_builtin_method():
assert_function([].append)
- def test_wrapper_descriptor() -> None:
+ def test_wrapper_descriptor():
assert_function(object.__init__)
- def test_method_wrapper() -> None:
+ def test_method_wrapper():
assert_function(object().__str__)
- def test_method_descriptor() -> None:
+ def test_method_descriptor():
assert_function(str.join)
- def test_class_method_descriptor() -> None:
+ def test_class_method_descriptor():
# method signature contains `Any`
assert_function(dict.fromkeys) # type: ignore[no-any-expr]
diff --git a/tests/test_intersection.py b/tests/test_intersection.py
index bd1029a..2c4fa95 100644
--- a/tests/test_intersection.py
+++ b/tests/test_intersection.py
@@ -1,5 +1,7 @@
from __future__ import annotations
+import pickle
+
from basedtyping import Intersection
@@ -11,9 +13,45 @@ class B:
y: int
-def test_intersection() -> None:
+class C(A, B): ...
+
+
+value = Intersection[A, B]
+other = Intersection[A, int]
+
+
+def test_intersection():
assert (
- str(Intersection[A, B])
+ str(value)
== f"basedtyping.Intersection[{A.__module__}.{A.__qualname__},"
f" {B.__module__}.{B.__qualname__}]"
)
+
+
+def test_intersection_eq():
+ assert value == value # noqa: PLR0124 we are testing __eq__
+ assert value != other
+
+
+def test_intersection_eq_hash():
+ assert hash(value) == hash(value)
+ assert hash(value) != other
+
+
+def test_intersection_instancecheck():
+ assert isinstance(C(), value) # type: ignore[arg-type]
+ assert not isinstance(A(), value) # type: ignore[arg-type]
+ assert not isinstance(B(), value) # type: ignore[arg-type]
+
+
+def test_intersection_subclasscheck():
+ assert issubclass(C, value) # type: ignore[arg-type]
+ assert not issubclass(A, value) # type: ignore[arg-type]
+ assert not issubclass(B, value) # type: ignore[arg-type]
+
+
+def test_intersection_reduce():
+ pickled = pickle.dumps(value)
+ loaded = pickle.loads(pickled) # type: ignore[no-any-expr]
+ assert loaded is value # type: ignore[no-any-expr]
+ assert loaded is not other # type: ignore[no-any-expr]
diff --git a/tests/test_is_subform.py b/tests/test_is_subform.py
index 5c372b7..9d40161 100644
--- a/tests/test_is_subform.py
+++ b/tests/test_is_subform.py
@@ -6,12 +6,12 @@
from basedtyping import issubform
-def test_normal() -> None:
+def test_normal():
if sys.version_info >= (3, 10):
assert issubform(int, int | str)
-def test_union_first_arg() -> None:
+def test_union_first_arg():
if sys.version_info >= (3, 10):
assert not issubform(int | str, int)
assert issubform(int | str, object)
@@ -19,7 +19,7 @@ def test_union_first_arg() -> None:
assert issubform(int | str, Union[int, str]) # type: ignore[unused-ignore, arg-type]
-def test_old_union() -> None:
+def test_old_union():
# TODO: fix the mypy error
assert not issubform(Union[int, str], int) # type: ignore[arg-type]
assert issubform(Union[int, str], object) # type: ignore[arg-type]
diff --git a/tests/test_never_type/test_runtime.py b/tests/test_never_type/test_runtime.py
index 5e6ae1c..c2b1cb5 100644
--- a/tests/test_never_type/test_runtime.py
+++ b/tests/test_never_type/test_runtime.py
@@ -9,33 +9,33 @@
@mark.xfail # https://github.com/KotlinIsland/basedtyping/issues/22
-def test_isinstance() -> None:
+def test_isinstance():
assert not isinstance(1, Never) # type: ignore[arg-type]
-def test_issubform_true() -> None:
+def test_issubform_true():
assert issubform(Never, int)
-def test_issubform_false() -> None:
+def test_issubform_false():
assert not issubform(str, Never)
-def test_issubform_never_is_never() -> None:
+def test_issubform_never_is_never():
assert issubform(Never, Never)
-def test_issubclass() -> None:
+def test_issubclass():
with raises(TypeError):
assert issubclass(int, Never) # type: ignore[arg-type]
-def test_cant_instantiate() -> None:
+def test_cant_instantiate():
with raises(TypeError):
Never() # type: ignore[operator]
-def test_cant_subtype() -> None:
+def test_cant_subtype():
with raises(TypeError):
class _SubNever(Never): # type: ignore[misc]
diff --git a/tests/test_never_type/test_typetime.py b/tests/test_never_type/test_typetime.py
index 2bcb876..8da4f10 100644
--- a/tests/test_never_type/test_typetime.py
+++ b/tests/test_never_type/test_typetime.py
@@ -9,22 +9,22 @@
from basedtyping.typetime_only import assert_type
- def test_never_equals_noreturn() -> None:
+ def test_never_equals_noreturn():
# TODO: better way to check if types are equal
assert_type[NoReturn](cast(Never, 1))
assert_type[Never](cast(NoReturn, 1))
- def test_valid_type_hint() -> None:
+ def test_valid_type_hint():
_never: Never
- def test_cant_assign_to_never() -> None:
+ def test_cant_assign_to_never():
_never: Never = 1 # type: ignore[assignment]
- def test_cant_subtype() -> None:
+ def test_cant_subtype():
class _A(Never): # type: ignore[misc]
...
- def test_type_never() -> None:
+ def test_type_never():
"""``type[Never]`` is invalid as ``Never`` is not a ``type``.
Should actually be:
diff --git a/tests/test_reified_generics/test_issubclass.py b/tests/test_reified_generics/test_issubclass.py
index a8c3fc1..b3ae3cf 100644
--- a/tests/test_reified_generics/test_issubclass.py
+++ b/tests/test_reified_generics/test_issubclass.py
@@ -16,12 +16,12 @@ class Reified(ReifiedGeneric[Tuple[T, T2]]):
# https://github.com/KotlinIsland/basedmypy/issues/5
-def test_issubclass() -> None:
+def test_issubclass():
assert issubclass(Reified[int, str], Reified[int, str]) # type: ignore[misc]
assert not issubclass(Reified[int, str], Reified[int, int]) # type: ignore[misc]
-def test_wrong_class_same_generics() -> None:
+def test_wrong_class_same_generics():
class Reified2(ReifiedGeneric[Tuple[T, T2]]):
pass
@@ -29,12 +29,12 @@ class Reified2(ReifiedGeneric[Tuple[T, T2]]):
@mark.xfail(reason="not implemented")
-def test_without_generics_first_arg_false() -> None:
+def test_without_generics_first_arg_false():
assert not issubclass(Reified, Reified[int, str]) # type: ignore[misc]
@mark.xfail(reason="not implemented")
-def test_without_generics_first_arg_true() -> None:
+def test_without_generics_first_arg_true():
# https://github.com/KotlinIsland/basedtyping/issues/70
class Foo(ReifiedGeneric[out_T]): # type:ignore[type-var]
pass
@@ -42,11 +42,11 @@ class Foo(ReifiedGeneric[out_T]): # type:ignore[type-var]
assert not issubclass(Foo, Foo[object]) # type: ignore[misc]
-def test_without_generics_second_arg() -> None:
+def test_without_generics_second_arg():
assert issubclass(Reified[int, str], Reified)
-def test_without_generics_both() -> None:
+def test_without_generics_both():
class SubReified(Reified[T, T2]):
pass
@@ -55,7 +55,7 @@ class SubReified(Reified[T, T2]):
@mark.xfail(reason="not implemented")
-def test_without_generics_same_as_bound() -> None:
+def test_without_generics_same_as_bound():
_T = TypeVar("_T", bound=Union[int, str]) # noqa: PYI018
class Foo(ReifiedGeneric[T]):
@@ -65,7 +65,7 @@ class Foo(ReifiedGeneric[T]):
assert issubclass(Foo[Union[int, str]], Foo)
-def test_without_generics_one_specified() -> None:
+def test_without_generics_one_specified():
class SubReified(Reified[int, T2]):
pass
diff --git a/tests/test_reified_generics/test_not_enough_type_params.py b/tests/test_reified_generics/test_not_enough_type_params.py
index c4ac439..07b1ad3 100644
--- a/tests/test_reified_generics/test_not_enough_type_params.py
+++ b/tests/test_reified_generics/test_not_enough_type_params.py
@@ -13,6 +13,6 @@ class Reified2(ReifiedGeneric[Tuple[T, U]]):
pass
-def test_not_enough_type_params() -> None:
+def test_not_enough_type_params():
with raises(NotEnoughTypeParametersError):
Reified2[int]() # type: ignore[misc]
diff --git a/tests/test_reified_generics/test_reified_generic.py b/tests/test_reified_generics/test_reified_generic.py
index 4befbe0..b496497 100644
--- a/tests/test_reified_generics/test_reified_generic.py
+++ b/tests/test_reified_generics/test_reified_generic.py
@@ -27,7 +27,7 @@ class Normal(Generic[T, T2]):
not_reified_parameter_error_match = "TypeVars cannot be used"
-def test_class_args_and_params_class() -> None:
+def test_class_args_and_params_class():
assert (
Normal[int, str].__args__ # type: ignore[attr-defined]
== Reified[int, str].__reified_generics__
@@ -38,31 +38,31 @@ def test_class_args_and_params_class() -> None:
)
-def test_class_args_and_params_instance() -> None:
+def test_class_args_and_params_instance():
assert Reified[int, str]().__reified_generics__ == (int, str)
assert not Reified[int, str]().__type_vars__
-def test_reified_list() -> None:
+def test_reified_list():
it = ReifiedList[int]([1, 2, 3])
assert it.__reified_generics__ == (int,)
assert not it.__type_vars__
-def test_reified_generic_without_generic_alias() -> None:
+def test_reified_generic_without_generic_alias():
with raises(NotReifiedError, match="Cannot instantiate ReifiedGeneric "):
Reified()
-def test_reified_in_init() -> None:
+def test_reified_in_init():
class Foo(ReifiedGeneric[T]):
- def __init__(self) -> None:
+ def __init__(self):
assert self.__reified_generics__ == (int,)
Foo[int]()
-def test_concrete_subclass() -> None:
+def test_concrete_subclass():
class A(ReifiedGeneric[T]):
pass
@@ -77,7 +77,7 @@ class SubASpecified(A[int]):
assert not isinstance(s, A[str]) # type: ignore[misc]
-def test_none_type() -> None:
+def test_none_type():
# TODO: is this mypy error correct?
assert Reified[None, None].__reified_generics__ == (
NoneType,
diff --git a/tests/test_reified_generics/test_type_of_types.py b/tests/test_reified_generics/test_type_of_types.py
index 35b9539..78c84ff 100644
--- a/tests/test_reified_generics/test_type_of_types.py
+++ b/tests/test_reified_generics/test_type_of_types.py
@@ -17,13 +17,13 @@ class Reified(ReifiedGeneric[Tuple[T, U]]):
from basedtyping.typetime_only import assert_type
- def test_instance() -> None:
+ def test_instance():
"""may be possible once https://github.com/KotlinIsland/basedmypy/issues/24 is resolved"""
assert_type[Tuple[Type[int], Type[str]]](
Reified[int, str]().__reified_generics__ # type: ignore[arg-type]
)
- def from_class() -> None:
+ def from_class():
"""may be possible once https://github.com/python/mypy/issues/11672 is resolved"""
assert_type[Tuple[Type[int], Type[str]]](
Reified[int, str].__reified_generics__ # type: ignore[arg-type]
diff --git a/tests/test_reified_generics/test_typevars.py b/tests/test_reified_generics/test_typevars.py
index 8cb984e..1ac4856 100644
--- a/tests/test_reified_generics/test_typevars.py
+++ b/tests/test_reified_generics/test_typevars.py
@@ -16,31 +16,31 @@ def _value() -> int:
return 1
-def test_instantiate(_value: T) -> None:
+def test_instantiate(_value: T):
with raises(NotReifiedError):
Reified[T]()
-def test_isinstance(_value: T) -> None:
+def test_isinstance(_value: T):
with raises(NotReifiedError):
isinstance(Reified[str](), Reified[T]) # type: ignore[misc]
-def test_unbound_instantiate(_value: T) -> None:
+def test_unbound_instantiate(_value: T):
with raises(NotReifiedError):
Reified[T]()
-def test_unbound_isinstance(_value: T) -> None:
+def test_unbound_isinstance(_value: T):
with raises(NotReifiedError):
isinstance(Reified[str](), Reified[T]) # type: ignore[misc]
-def test_issubclass_left(_value: T) -> None:
+def test_issubclass_left(_value: T):
with raises(NotReifiedError):
issubclass(Reified[T], Reified[int]) # type: ignore[misc]
-def test_issubclass_right(_value: T) -> None:
+def test_issubclass_right(_value: T):
with raises(NotReifiedError):
issubclass(Reified[int], Reified[T]) # type: ignore[misc]
diff --git a/tests/test_reified_generics/test_variance.py b/tests/test_reified_generics/test_variance.py
index 11d93ac..fd523dc 100644
--- a/tests/test_reified_generics/test_variance.py
+++ b/tests/test_reified_generics/test_variance.py
@@ -7,7 +7,7 @@
# type ignores are due to https://github.com/KotlinIsland/basedmypy/issues/5
-def test_covariant() -> None:
+def test_covariant():
# https://github.com/KotlinIsland/basedtyping/issues/70
class Foo(ReifiedGeneric[out_T]): # type:ignore[type-var]
pass
@@ -16,7 +16,7 @@ class Foo(ReifiedGeneric[out_T]): # type:ignore[type-var]
assert not isinstance(Foo[Union[int, str]](), Foo[int]) # type: ignore[misc]
-def test_contravariant() -> None:
+def test_contravariant():
# https://github.com/KotlinIsland/basedtyping/issues/70
class Foo(ReifiedGeneric[in_T]): # type:ignore[type-var]
pass
@@ -25,7 +25,7 @@ class Foo(ReifiedGeneric[in_T]): # type:ignore[type-var]
assert not isinstance(Foo[int](), Foo[Union[int, str]]) # type: ignore[misc]
-def test_invariant() -> None:
+def test_invariant():
class Foo(ReifiedGeneric[T]):
pass
diff --git a/tests/test_runtime_only/test_literal_type.py b/tests/test_runtime_only/test_literal_type.py
index c0ccbef..7d8effd 100644
--- a/tests/test_runtime_only/test_literal_type.py
+++ b/tests/test_runtime_only/test_literal_type.py
@@ -8,7 +8,7 @@
if sys.version_info >= (3, 9): # prevent mypy errors
@pytest.mark.skipif(sys.version_info < (3, 9), reason="need 3.9 for LiteralType")
- def test_literal_type_positive() -> None:
+ def test_literal_type_positive():
from typing import Literal
from basedtyping.runtime_only import LiteralType
@@ -16,7 +16,7 @@ def test_literal_type_positive() -> None:
assert isinstance(Literal[1, 2], LiteralType)
@pytest.mark.skipif(sys.version_info < (3, 9), reason="need 3.9 for LiteralType")
- def test_literal_type_negative() -> None:
+ def test_literal_type_negative():
from basedtyping.runtime_only import LiteralType
assert not isinstance(Union[int, str], LiteralType)
diff --git a/tests/test_runtime_only/test_old_union_type.py b/tests/test_runtime_only/test_old_union_type.py
index 95128c7..22bb01e 100644
--- a/tests/test_runtime_only/test_old_union_type.py
+++ b/tests/test_runtime_only/test_old_union_type.py
@@ -6,10 +6,10 @@
from basedtyping.runtime_only import OldUnionType
-def test_old_union() -> None:
+def test_old_union():
assert isinstance(Union[int, str], OldUnionType)
-def test_new_union() -> None:
+def test_new_union():
if sys.version_info >= (3, 10):
assert not isinstance(int | str, OldUnionType)
diff --git a/tests/test_typeform.py b/tests/test_typeform.py
new file mode 100644
index 0000000..5432207
--- /dev/null
+++ b/tests/test_typeform.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+
+from basedtyping import TypeForm
+
+
+class A:
+ x: int
+
+
+class B:
+ y: int
+
+
+def test_typeform():
+ assert str(TypeForm[A]) == f"basedtyping.TypeForm[{A.__module__}.{A.__qualname__}]"