mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Unexpected error behaviour: Parameterized generics cannot be used with class or instance checks

Open JohanSchott opened this issue 3 years ago • 6 comments

mypy reports success for

import numpy as np
x = "a"
assert isinstance(x, np.float64)

but fails for

from numpy import float64
x = "a"
assert isinstance(x, float64)

with the error fails.py:3: error: Parameterized generics cannot be used with class or instance checks

I'm using mypy (0.931) and numpy (1.22.2).

Why do I get different results?

JohanSchott avatar Feb 09 '22 11:02 JohanSchott

Interesting. float64 is defined here: https://github.com/numpy/numpy/blob/51abd9693a78907f2ca7dfacee2017ef44fb9f49/numpy/init.pyi#L2973. It's obviously meant as a type alias, but apparently mypy sometimes interprets it as a variable assignment.

JelleZijlstra avatar Feb 09 '22 15:02 JelleZijlstra

I have the same issue with Unions

version:

$  mypy --version
mypy 1.4.1 (compiled: yes)
$ python --version
Python 3.10.9

Config:

[tool.mypy]
namespace_packages=true
explicit_package_bases=true
strict=true
show_error_codes=true

Code

from dataclasses import dataclass


@dataclass
class A:
    val_a: str

@dataclass
class B:
    val_b: str

ABUnion = A|B

obj = A(val_a="hey")

assert isinstance(obj, ABUnion)

Result:

$ mypy check.py 
check.py:16: error: Parameterized generics cannot be used with class or instance checks  [misc]
check.py:16: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

Code works fine

Thank you!

Edit: One solution is to use get_args on the union

from typing import get_args

assert isinstance(obj, get_args(ABUnion))

This passes both code and check, but does not work as a type guard

Edit 2: This work as type guard with a TypeGuard, obj is correctly scoped to a shorter set of the union

from dataclasses import dataclass
from typing import get_args, TypeGuard


@dataclass
class A:
    val_a: str
    common: str

@dataclass
class B:
    val_b: str
    common: str

@dataclass
class C:  # no common
    val_c: str

ABUnion = A|B  # has common
ABCUnion = A|B|C

obj: ABCUnion  = A(val_a="hey", common="common")

def is_ab(val: ABCUnion) -> TypeGuard[ABUnion]:
    return type(val) in get_args(ABUnion)

if is_ab(obj):
    print(obj.common)

lindycoder avatar Aug 01 '23 16:08 lindycoder

Any status update?

Note that isinstance is defined as working on unions: "If classinfo is a tuple of type objects (or recursively, other such tuples) or a Union Type of multiple types, return True if object is an instance of any of the types."

tibbe avatar Aug 31 '23 13:08 tibbe

I'm also wondering about the status on this.

brandon-neth avatar Jan 25 '24 18:01 brandon-neth

Should this have the label topic-pep-604? I'm not 100% sure but it seems like it when I look at others open issues that have the label.

hunterC90 avatar Feb 22 '24 01:02 hunterC90

Any new development on mitigating this. I am using cryptography and their aliases are not disected properly and even get_args does not help to mark object as suitable. Also a Union of child classes is not properly inspected when variable of object is of parent class but the object is of child class.

msetina avatar Apr 24 '24 07:04 msetina