Skip to content

Fix access to operators on metaclass through typevar #5009

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

Merged
merged 4 commits into from
May 7, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix handling of Type[M] + 0 where M is TypeVar
  • Loading branch information
elazarg committed May 5, 2018
commit 1f8e16a6c92897b0879247ddf79963a4af63fdaa
11 changes: 8 additions & 3 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2445,9 +2445,14 @@ def has_member(self, typ: Type, member: str) -> bool:
elif isinstance(typ, TypeType):
# Type[Union[X, ...]] is always normalized to Union[Type[X], ...],
# so we don't need to care about unions here.
if isinstance(typ.item, Instance) and typ.item.type.metaclass_type is not None:
return self.has_member(typ.item.type.metaclass_type, member)
if isinstance(typ.item, AnyType):
item = typ.item
if isinstance(item, TypeVarType):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder shouldn't we add the same fallback to upper bound at the very top of this function? For example if I have just

def f(x: T) -> T:
    x + 0

item = item.upper_bound
if isinstance(item, TupleType):
item = item.fallback
if isinstance(item, Instance) and item.type.metaclass_type is not None:
return self.has_member(item.type.metaclass_type, member)
if isinstance(item, AnyType):
return True
return False
else:
Expand Down
17 changes: 17 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -3459,6 +3459,23 @@ class Concrete(metaclass=Meta):
reveal_type(Concrete + X()) # E: Revealed type is 'builtins.str'
Concrete + "hello" # E: Unsupported operand types for + ("Type[Concrete]" and "str")

[case testMetaclassOperatorTypeVar]
from typing import Type, TypeVar

class MetaClass(type):
def __mul__(cls, other: int) -> str:
return ""

class Test(metaclass=MetaClass):
pass

S = TypeVar("S", bound=Test)

def f(x: Type[Test]) -> str:
return x * 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will still pass if the inferred type will become Any. I would either add an explicit reveal_type or add --warn-return-any flag in this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've avoided reveal_type since it passes with the broken code too. But I agree it should be added just to know it's not Any.

def g(x: Type[S]) -> str:
return x * 0

[case testMetaclassGetitem]
class M(type):
def __getitem__(self, key) -> int: return 1
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/lib-stub/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class type:
# These are provided here for convenience.
class int:
def __add__(self, other: 'int') -> 'int': pass
def __rmul__(self, other: 'int') -> 'int': pass
class float: pass

class str:
Expand Down