smarie / python-pep-ideas

4 stars 0 forks source link

Provide `typing.isinstance` and `typing.issubclass` methods #8

Open smarie opened 4 years ago

smarie commented 4 years ago

Unfortunately, quoting the python 3.8 doc:

in general, isinstance() and issubclass() should not be used with types.

So PEP484 type hints are great to express many subtelties, but... are not supported by isinstance and issubclass. Implementing a PEP484-aware type checker is much more complicated than using isinstance/issubclass when PEP484 type hints need to be supported.

That is why dedicated libraries have emerged to provide static (mypy) or runtime (typeguard, pytypes, enforce) type-checking capabilities.

The main issue with all of these is that they have trouble supporting all legacy python versions, all of typing capabilities, as well as providing speed, readable output and error details when inner checks have raised interesting messages (such as a validating type vtypes).

Having an "official" implementation in the stdlib of PEP484-aware isinstance and issubclass would certainly help newcomers leverage the optional but powerful PEP484 typing system for runtime checks.

plammens commented 4 years ago

Just to clarify, what do you mean by typing.isinstance and typing.issubclass?

I'm guessing something like the current isinstance and issubclass but extended to all typing components, like subscripted generics, type variables, etc.?

import numbers
import typing as t

t.issubclass(t.Dict, t.Sequence) # False
t.issubclass(t.Dict[str, int], t.Collection[str])  # True
t.isinstance({'foo': 42}, t.Dict[str, int])  # True
t.isinstance(123, t.TypeVar('T', bound=numbers.Number))  # True

Currently, isinstance and issubclass already support most of the basic, well-defined checks:

issubclass(t.List, t.Sequence)  # True
issubclass(t.Collection, t.Container)  # True
isinstance({}, t.Mapping)  # True
isinstance((1, 2), t.Sequence)  # True
smarie commented 4 years ago

Thanks @plammens !

I'm guessing something like the current isinstance and issubclass but extended to all typing components, like subscripted generics, type variables, etc.?

Yes, either this out-of-the-box, or with a parameter allowing users to enable specific extra checks (such as generic parameters checks, see below), or - but I like this option less - a distinct API.

Currently, isinstance and issubclass already support most of the basic, well-defined checks:

This is unfortunately not guaranteed by the documentation,

image

and I believe, not exhaustive. For example parametrized types:

issubclass(dict(a=1), Mapping[str, int]) --> TypeError: Subscripted generics cannot be used with class and instance checks

I did not perform all checks but the typing module changed at every minor/patch version since 3.5, and is the reason why libs such as typeguard, pytypes and enforce had a hard time trying to cover all versions (and we also had a hard time getting retrocompatibility in typing-inspect)

Note that in addition to isinstance and issubclass, __subclasses__ is also something that could be improved to transparently work the same way for typing classes and parameterized generic types. We discussed various examples in the past in pytypes: https://github.com/Stewori/pytypes/issues/31

The idea behind this whole topic is really to hide the complexity in the typing module and reach practical use again.