isinstance2
is an experimental module that provides a powerful runtime type checker for Python's built-in generic classes and
generic type hints. It allows you to perform runtime instance type checks on objects that are instances of a generic
class, as well as subclass checks on generic classes, even if you don't know the exact type of the generic parameters.
from isinstance2 import isinstance2, issubclass2
from typing import Iterable
assert isinstance2((1, 2.0, 'three'), tuple[int, float, str])
assert issubclass2(dict[str, int], dict[str, int | float])
assert issubclass2(list[int] | set[int] | tuple[int, ...], Iterable[int])
- Perform runtime instance and subclass checks on generic classes
- Supports built-in generic classes such as
list
,tuple
,dict
,set
, andfrozenset
, as well asOptional
andLiteral
. - Check if an object is an instance of a
tuple
with variadic arguments. - Register custom class or function with
isinstance2
's instance checker registry.
pip install isinstance2
from typing import Iterable, Literal
from isinstance2 import isinstance2
# Basic instance checks
assert isinstance2([1, 2, 3], list[int])
assert isinstance2((1, 2.0, 'three'), tuple[int, float, str])
assert isinstance2({1, 2, 3}, set[int])
assert isinstance2({"foo": 1, "bar": 2}, dict[str, int])
assert isinstance2(frozenset([1, 2, 'Hi! 😊', 'literally amazing']), frozenset[int | Literal['Hi! 😊', 'literally amazing']])
# Ellipses in tuples work
assert isinstance2((1, 'two', 3.0, 'four'), tuple[int | float | str, ...])
# You can also check against abstract generic classes
assert isinstance2(range(10), Iterable[int])
assert not isinstance2(range(10), Iterable[float])
from typing import Collection, Iterable
from isinstance2 import issubclass2
# Basic subclass checks
assert issubclass2(list[int], list[int | float])
assert issubclass2(tuple[int, float], tuple[int | float, ...])
# Classes without generic parameters are presumed to match
assert issubclass2(list, list[int])
assert issubclass2(list[int], list)
# Abstract generic classes
assert issubclass2(list[int], Iterable[int])
assert issubclass2(Collection[bool], Iterable[int]) # Yes, bool is a subclass of int
To check if an object is an instance of a custom generic class, register it with isinstance2
's instance checker
from typing import Generic, TypeVar, Any
from isinstance2 import isinstance2, register_instance_checker
T = TypeVar('T')
class MyClass(Generic[T]):
...
@register_instance_checker
def is_instance_of_my_class(obj: Any) -> bool:
return isinstance(obj, MyClass)
assert isinstance2(MyClass(), MyClass)
If you'd prefer not to add your checkers globally, you can use isinstance2
's register
instead and pass a custom registry (which is just a dict
).
from typing import Generic, TypeVar
from isinstance2 import register, instance_checker_registry
from functools import partial
# Copy the default registry
my_registry = instance_checker_registry.copy()
# Make a custom registration function
my_register = partial(register, registry=my_registry)
Now you can use my_register
in place of register_instance_checker
.
- Does not yet support
TypeVar
Container
- And likely quite a few other generic classes that I've missed. Please open an issue if you find one.
- Subclass checks for custom classes (instance checks are supported)
- Subclass checks are, in general, unreliable.
- I haven't yet figured out how to deal with things like structural subtyping. For instance,
issubclass2(str, Iterator[int])
currently returnsTrue
when it clearly shouldn't - Instance checks are somewhat simpler and shouldn't suffer as much from this problem.
- I haven't yet figured out how to deal with things like structural subtyping. For instance,
- Requires Python 3.11 or later
isinstance2
is released under the MIT License.