Skip to content

Commit 3291eba

Browse files
committed
backport python#6655 and python#7446 to v0.660
1. limit the version of the test deps in test-requirements.txt by release data (i.e. Jan 16 2019, when v0.660 was released) 2. ./runtests.py and all tests pass 3. add tests from python#6655 and python#7446 and ./runtests.py and new test cases fail 4. apply changes from python#6655 and python#7446 manually 5. run tests and all pass
1 parent c06aa9c commit 3291eba

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)