Skip to content

Repository files navigation

isinstance2

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.

tl;dr

fromisinstance2importisinstance2, issubclass2fromtypingimportIterableassertisinstance2((1, 2.0, 'three'), tuple[int, float, str])
assertissubclass2(dict[str, int], dict[str, int|float])
assertissubclass2(list[int] |set[int] |tuple[int, ...], Iterable[int])

Features

  • Perform runtime instance and subclass checks on generic classes
  • Supports built-in generic classes such as list, tuple, dict, set, and frozenset, as well as Optional and Literal.
  • Check if an object is an instance of a tuple with variadic arguments.
  • Register custom class or function with isinstance2's instance checker registry.

Installation

pip install isinstance2

Basic Usage

Instance Checks

fromtypingimportIterable, Literalfromisinstance2importisinstance2# Basic instance checksassertisinstance2([1, 2, 3], list[int])
assertisinstance2((1, 2.0, 'three'), tuple[int, float, str])
assertisinstance2({1, 2, 3}, set[int])
assertisinstance2({"foo": 1, "bar": 2}, dict[str, int])
assertisinstance2(frozenset([1, 2, 'Hi! 😊', 'literally amazing']), frozenset[int|Literal['Hi! 😊', 'literally amazing']])
# Ellipses in tuples workassertisinstance2((1, 'two', 3.0, 'four'), tuple[int|float|str, ...])
# You can also check against abstract generic classesassertisinstance2(range(10), Iterable[int])
assertnotisinstance2(range(10), Iterable[float])

Subclass Checks

fromtypingimportCollection, Iterablefromisinstance2importissubclass2# Basic subclass checksassertissubclass2(list[int], list[int|float])
assertissubclass2(tuple[int, float], tuple[int|float, ...])
# Classes without generic parameters are presumed to matchassertissubclass2(list, list[int])
assertissubclass2(list[int], list)
# Abstract generic classesassertissubclass2(list[int], Iterable[int])
assertissubclass2(Collection[bool], Iterable[int]) # Yes, bool is a subclass of int

Advanced Usage

To check if an object is an instance of a custom generic class, register it with isinstance2's instance checker

fromtypingimportGeneric, TypeVar, Anyfromisinstance2importisinstance2, register_instance_checkerT=TypeVar('T')
classMyClass(Generic[T]):
...
@register_instance_checkerdefis_instance_of_my_class(obj: Any) ->bool:
returnisinstance(obj, MyClass)
assertisinstance2(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).

fromtypingimportGeneric, TypeVarfromisinstance2importregister, instance_checker_registryfromfunctoolsimportpartial# Copy the default registrymy_registry=instance_checker_registry.copy()
# Make a custom registration functionmy_register=partial(register, registry=my_registry)

Now you can use my_register in place of register_instance_checker.

Limitations

  • 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 returns True when it clearly shouldn't
    • Instance checks are somewhat simpler and shouldn't suffer as much from this problem.
  • Requires Python 3.11 or later

License

isinstance2 is released under the MIT License.

About

Generic instance and subclass checks for Python

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages