Skip to content

Commit 87e006d

Browse files
author
Nanshu Chen
committed
backport python#6655 and python#7446 to v0.660
1 parent c06aa9c commit 87e006d

File tree

8 files changed

+68
-22
lines changed

8 files changed

+68
-22
lines changed

mypy/checker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3465,12 +3465,12 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context,
34653465
isinstance(subtype, (Instance, TupleType, TypedDictType))):
34663466
self.msg.report_protocol_problems(subtype, supertype, context)
34673467
if isinstance(supertype, CallableType) and isinstance(subtype, Instance):
3468-
call = find_member('__call__', subtype, subtype)
3468+
call = find_member('__call__', subtype, subtype, is_operator=True)
34693469
if call:
34703470
self.msg.note_call(subtype, call, context)
34713471
if isinstance(subtype, (CallableType, Overloaded)) and isinstance(supertype, Instance):
34723472
if supertype.type.is_protocol and supertype.type.protocol_members == ['__call__']:
3473-
call = find_member('__call__', supertype, subtype)
3473+
call = find_member('__call__', supertype, subtype, is_operator=True)
34743474
assert call is not None
34753475
self.msg.note_call(supertype, call, context)
34763476
return False

mypy/checkexpr.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,8 @@ def check_call(self,
701701
elif isinstance(callee, UnionType):
702702
return self.check_union_call(callee, args, arg_kinds, arg_names, context, arg_messages)
703703
elif isinstance(callee, Instance):
704-
call_function = analyze_member_access('__call__', callee, context,
705-
False, False, False, self.msg,
704+
call_function = analyze_member_access('__call__', callee, context, is_lvalue=False,
705+
is_super=False, is_operator=True, msg=self.msg,
706706
original_type=callee, chk=self.chk,
707707
in_literal_context=self.is_literal_context())
708708
return self.check_call(call_function, args, arg_kinds, context, arg_names,
@@ -1264,7 +1264,8 @@ def check_arg(self, caller_type: Type, original_caller_type: Type,
12641264
self.msg.report_protocol_problems(original_caller_type, callee_type, context)
12651265
if (isinstance(callee_type, CallableType) and
12661266
isinstance(original_caller_type, Instance)):
1267-
call = find_member('__call__', original_caller_type, original_caller_type)
1267+
call = find_member('__call__', original_caller_type, original_caller_type,
1268+
is_operator=True)
12681269
if call:
12691270
self.msg.note_call(original_caller_type, call, context)
12701271

mypy/checkmember.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,8 @@ def analyze_member_var_access(name: str,
326326
return analyze_var(name, v, itype, info, mx, implicit=implicit)
327327
elif isinstance(v, FuncDef):
328328
assert False, "Did not expect a function"
329-
elif not v and name not in ['__getattr__', '__setattr__', '__getattribute__']:
329+
elif (not v and name not in ['__getattr__', '__setattr__', '__getattribute__'] and
330+
not mx.is_operator):
330331
if not mx.is_lvalue:
331332
for method_name in ('__getattribute__', '__getattr__'):
332333
method = info.get_method(method_name)

mypy/constraints.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
284284
# Special case: a generic callback protocol
285285
if not any(is_same_type(template, t) for t in template.type.inferring):
286286
template.type.inferring.append(template)
287-
call = mypy.subtypes.find_member('__call__', template, actual)
287+
call = mypy.subtypes.find_member('__call__', template, actual,
288+
is_operator=True)
288289
assert call is not None
289290
if mypy.subtypes.is_subtype(actual, erase_typevars(call)):
290291
subres = infer_constraints(call, actual, self.direction)
@@ -425,7 +426,8 @@ def visit_callable_type(self, template: CallableType) -> List[Constraint]:
425426
elif isinstance(self.actual, Instance):
426427
# Instances with __call__ method defined are considered structural
427428
# subtypes of Callable with a compatible signature.
428-
call = mypy.subtypes.find_member('__call__', self.actual, self.actual)
429+
call = mypy.subtypes.find_member('__call__', self.actual, self.actual,
430+
is_operator=True)
429431
if call:
430432
return infer_constraints(template, call, self.direction)
431433
else:

mypy/join.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,5 +462,5 @@ def join_type_list(types: List[Type]) -> Type:
462462
def unpack_callback_protocol(t: Instance) -> Optional[Type]:
463463
assert t.type.is_protocol
464464
if t.type.protocol_members == ['__call__']:
465-
return find_member('__call__', t, t)
465+
return find_member('__call__', t, t, is_operator=True)
466466
return None

mypy/subtypes.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def visit_instance(self, left: Instance) -> bool:
230230
return is_named_instance(item, 'builtins.object')
231231
if isinstance(right, CallableType):
232232
# Special case: Instance can be a subtype of Callable.
233-
call = find_member('__call__', left, left)
233+
call = find_member('__call__', left, left, is_operator=True)
234234
if call:
235235
return self._is_subtype(call, right)
236236
return False
@@ -258,7 +258,7 @@ def visit_callable_type(self, left: CallableType) -> bool:
258258
if right.type.is_protocol and right.type.protocol_members == ['__call__']:
259259
# OK, a callable can implement a protocol with a single `__call__` member.
260260
# TODO: we should probably explicitly exclude self-types in this case.
261-
call = find_member('__call__', right, left)
261+
call = find_member('__call__', right, left, is_operator=True)
262262
assert call is not None
263263
if self._is_subtype(left, call):
264264
return True
@@ -337,7 +337,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
337337
if isinstance(right, Instance):
338338
if right.type.is_protocol and right.type.protocol_members == ['__call__']:
339339
# same as for CallableType
340-
call = find_member('__call__', right, left)
340+
call = find_member('__call__', right, left, is_operator=True)
341341
assert call is not None
342342
if self._is_subtype(left, call):
343343
return True
@@ -512,7 +512,10 @@ def f(self) -> A: ...
512512
return True
513513

514514

515-
def find_member(name: str, itype: Instance, subtype: Type) -> Optional[Type]:
515+
def find_member(name: str,
516+
itype: Instance,
517+
subtype: Type,
518+
is_operator: bool = False) -> Optional[Type]:
516519
"""Find the type of member by 'name' in 'itype's TypeInfo.
517520
518521
Fin the member type after applying type arguments from 'itype', and binding
@@ -540,7 +543,8 @@ def find_member(name: str, itype: Instance, subtype: Type) -> Optional[Type]:
540543
v = v.var
541544
if isinstance(v, Var):
542545
return find_node_type(v, itype, subtype)
543-
if not v and name not in ['__getattr__', '__setattr__', '__getattribute__']:
546+
if (not v and name not in ['__getattr__', '__setattr__', '__getattribute__'] and
547+
not is_operator):
544548
for method_name in ('__getattribute__', '__getattr__'):
545549
# Normally, mypy assumes that instances that define __getattr__ have all
546550
# attributes with the corresponding return type. If this will produce
@@ -1109,7 +1113,7 @@ def check_argument(leftarg: Type, rightarg: Type, variance: int) -> bool:
11091113
return True
11101114
return False
11111115
if isinstance(right, CallableType):
1112-
call = find_member('__call__', left, left)
1116+
call = find_member('__call__', left, left, is_operator=True)
11131117
if call:
11141118
return self._is_proper_subtype(call, right)
11151119
return False

test-data/unit/check-classes.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,44 @@ b = a.bar
24932493
[out]
24942494
main:9: error: Incompatible types in assignment (expression has type "A", variable has type "B")
24952495

2496+
[case testGetattrWithCallable]
2497+
from typing import Callable, Any
2498+
2499+
class C:
2500+
def __getattr__(self, attr: str) -> C: ...
2501+
2502+
def do(cd: Callable[..., Any]) -> None: ...
2503+
2504+
do(C()) # E: Argument 1 to "do" has incompatible type "C"; expected "Callable[..., Any]"
2505+
2506+
[case testGetattrWithCallableTypeVar]
2507+
from typing import Callable, Any, TypeVar
2508+
2509+
class C:
2510+
def __getattr__(self, attr: str) -> C: ...
2511+
2512+
T = TypeVar('T', bound=Callable[..., Any])
2513+
2514+
def do(cd: T) -> T: ...
2515+
2516+
do(C()) # E: Value of type variable "T" of "do" cannot be "C"
2517+
2518+
[case testGetattrWithGetitem]
2519+
class A:
2520+
def __getattr__(self, x: str) -> 'A':
2521+
return A()
2522+
2523+
a = A()
2524+
a[0] # E: Value of type "A" is not indexable
2525+
2526+
[case testGetattrWithCall]
2527+
class A:
2528+
def __getattr__(self, x: str) -> 'A':
2529+
return A()
2530+
2531+
a = A()
2532+
a.y() # E: "A" not callable
2533+
24962534
[case testNestedGetattr]
24972535
def foo() -> object:
24982536
def __getattr__() -> None: # no error because not in a class

test-requirements.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
attrs>=18.0
2-
flake8
3-
flake8-bugbear; python_version >= '3.5'
4-
flake8-pyi; python_version >= '3.6'
1+
attrs>=18.0,<=18.2.0
2+
flake8<=3.6.0
3+
flake8-bugbear<=18.8.0; python_version >= '3.5'
4+
flake8-pyi<=18.3.1; python_version >= '3.6'
55
lxml==4.2.4
66
mypy_extensions==0.4.0
77
psutil==5.4.0
8-
pytest>=3.4
9-
pytest-xdist>=1.22
10-
pytest-cov>=2.4.0
8+
pytest>=3.4,<=4.1.1
9+
pytest-xdist>=1.22,<=1.26.0
10+
pytest-cov>=2.4.0,<=2.6.1
1111
typed-ast>=1.2.0,<1.3.0
1212
typing>=3.5.2; python_version < '3.5'
1313
py>=1.5.2

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy