Skip to content

(🐞) isinstance works with typing.Unions on 3.10+ #7505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
KotlinIsland opened this issue Mar 17, 2022 · 8 comments
Closed

(🐞) isinstance works with typing.Unions on 3.10+ #7505

KotlinIsland opened this issue Mar 17, 2022 · 8 comments

Comments

@KotlinIsland
Copy link
Contributor

KotlinIsland commented Mar 17, 2022

from typing import Union

print(isinstance(1, Union[str, int]))  # error: Argument 2 to "isinstance" has incompatible type "object"; expected "Union[type, UnionType, Tuple[Union[type, UnionType, Tuple[Any, ...]], ...]]"

But this outputs True

Union[X, Y] is stubbed to return object when it really returns a typing._UnionGenericAlias to typing.Union so that would need to be fixed first.

playground

@srittau
Copy link
Collaborator

srittau commented Mar 17, 2022

The stubs look correct to me:

typeshed/stdlib/builtins.pyi

Lines 1161 to 1172 in e8286e7

# We need recursive types to express the type of the second argument to `isinstance` properly, hence the use of `Any`
if sys.version_info >= (3, 10):
def isinstance(
__obj: object, __class_or_tuple: type | types.UnionType | tuple[type | types.UnionType | tuple[Any, ...], ...]
) -> bool: ...
def issubclass(
__cls: type, __class_or_tuple: type | types.UnionType | tuple[type | types.UnionType | tuple[Any, ...], ...]
) -> bool: ...
else:
def isinstance(__obj: object, __class_or_tuple: type | tuple[type | tuple[Any, ...], ...]) -> bool: ...
def issubclass(__cls: type, __class_or_tuple: type | tuple[type | tuple[Any, ...], ...]) -> bool: ...

I believe that his is purely a mypy bug, but someone could correct me here.

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Mar 17, 2022

@srittau Thanks for the response!

That stub doesn't contain anything about typing.Union.

Could you please elaborate?

@AlexWaygood
Copy link
Member

AlexWaygood commented Mar 17, 2022

Strictly speaking the Union[str, int] object being passed to the union is of type typing._SpecialForm (in the stubs at least — not at runtime) and it's true that the stub doesn't currently allow for the possibility of passing in _SpecialForm objects.

I'm not sure this is really a problem that should be solved in the stubs, though. If we add _SpecialForm to the union of types that's allowed for the second argument, that might mean that type checkers might start allowing things like isinstance(1, Literal[1]), which fails at runtime. Given that it's really only this specific special form that works with isinstance, I think it's probably better for a type checker to special-case it.

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Mar 17, 2022

@AlexWaygood Actually it's more specifically a typing._UnionGenericAlias to typing.Union which is an instance of _SpecialForm . We could just stub a fake subtype of _SpecialForm only for Unions, and use that to annotate.

I could make a pr when I get time.

@AlexWaygood
Copy link
Member

I could make a pr when I get time.

Cool, I'll happily take a look at a PR!

In general I'm slightly leery of overcomplicating typing.pyi by reproducing the exact runtime implementation details, but if there's a way to do it simply, then sure!

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Mar 17, 2022

Something roughly like this:

class _SpecialForm:
    def __getitem__(self, typeargs: Any) -> object: ...

class _UnionSpecialForm(_SpecialForm):
    def __getitem__(self, typeargs: Any) -> _UnionSpecialForm: ...

Union: _UnionSpecialForm = ...
 def isinstance(__obj: object, __class_or_tuple: type | types.UnionType | _UnionSpecialForm) -> bool: ...

@KotlinIsland
Copy link
Contributor Author

@AlexWaygood Here you go 😁 #7508

@AlexWaygood AlexWaygood changed the title (🐞) isinstance works with typing.Unions (🐞) isinstance works with typing.Unions on 3.10+ Mar 18, 2022
@AlexWaygood
Copy link
Member

The consensus in #7508 was that this was an unusual and niche use case, so it's probably best to close this as a "wontfix".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants