Skip to content

Commit 52af459

Browse files
committed
Revert "mypy: remove has_member (#8438)"
It turns out that the has_member check is an important (accidental?) performance optimization. Removing this caused a major (30+%?) slowdown at dropbox. There might be a better way to optimize this but I'm just going to revert it for now at least. This reverts commit 09cdab4.
1 parent a6a53e5 commit 52af459

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

mypy/checkexpr.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,7 +2414,16 @@ def make_local_errors() -> MessageBuilder:
24142414
def lookup_operator(op_name: str, base_type: Type) -> Optional[Type]:
24152415
"""Looks up the given operator and returns the corresponding type,
24162416
if it exists."""
2417+
2418+
# This check is an important performance optimization,
2419+
# even though it is mostly a subset of
2420+
# analyze_member_access.
2421+
# TODO: Find a way to remove this call without performance implications.
2422+
if not self.has_member(base_type, op_name):
2423+
return None
2424+
24172425
local_errors = make_local_errors()
2426+
24182427
member = analyze_member_access(
24192428
name=op_name,
24202429
typ=base_type,
@@ -3795,6 +3804,45 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool:
37953804
[self.named_type('builtins.unicode'),
37963805
AnyType(TypeOfAny.special_form)])))
37973806

3807+
def has_member(self, typ: Type, member: str) -> bool:
3808+
"""Does type have member with the given name?"""
3809+
# TODO: refactor this to use checkmember.analyze_member_access, otherwise
3810+
# these two should be carefully kept in sync.
3811+
# This is much faster than analyze_member_access, though, and so using
3812+
# it first as a filter is important for performance.
3813+
typ = get_proper_type(typ)
3814+
3815+
if isinstance(typ, TypeVarType):
3816+
typ = get_proper_type(typ.upper_bound)
3817+
if isinstance(typ, TupleType):
3818+
typ = tuple_fallback(typ)
3819+
if isinstance(typ, LiteralType):
3820+
typ = typ.fallback
3821+
if isinstance(typ, Instance):
3822+
return typ.type.has_readable_member(member)
3823+
if isinstance(typ, CallableType) and typ.is_type_obj():
3824+
return typ.fallback.type.has_readable_member(member)
3825+
elif isinstance(typ, AnyType):
3826+
return True
3827+
elif isinstance(typ, UnionType):
3828+
result = all(self.has_member(x, member) for x in typ.relevant_items())
3829+
return result
3830+
elif isinstance(typ, TypeType):
3831+
# Type[Union[X, ...]] is always normalized to Union[Type[X], ...],
3832+
# so we don't need to care about unions here.
3833+
item = typ.item
3834+
if isinstance(item, TypeVarType):
3835+
item = get_proper_type(item.upper_bound)
3836+
if isinstance(item, TupleType):
3837+
item = tuple_fallback(item)
3838+
if isinstance(item, Instance) and item.type.metaclass_type is not None:
3839+
return self.has_member(item.type.metaclass_type, member)
3840+
if isinstance(item, AnyType):
3841+
return True
3842+
return False
3843+
else:
3844+
return False
3845+
37983846
def not_ready_callback(self, name: str, context: Context) -> None:
37993847
"""Called when we can't infer the type of a variable because it's not ready yet.
38003848

mypy/typeops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,5 +719,5 @@ def custom_special_method(typ: Type, name: str, check_all: bool = False) -> bool
719719
if isinstance(typ, AnyType):
720720
# Avoid false positives in uncertain cases.
721721
return True
722-
# TODO: support other types (see analyze_member_access)?
722+
# TODO: support other types (see ExpressionChecker.has_member())?
723723
return False

test-data/unit/check-classes.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6650,7 +6650,7 @@ reveal_type(0.5 + C) # N: Revealed type is 'Any'
66506650

66516651
reveal_type(0.5 + D()) # N: Revealed type is 'Any'
66526652
reveal_type(D() + 0.5) # N: Revealed type is 'Any'
6653-
reveal_type("str" + D()) # N: Revealed type is 'Any'
6653+
reveal_type("str" + D()) # N: Revealed type is 'builtins.str'
66546654
reveal_type(D() + "str") # N: Revealed type is 'Any'
66556655

66566656

0 commit comments

Comments
 (0)