9
9
)
10
10
from mypy .nodes import (
11
11
TypeInfo , FuncBase , Var , FuncDef , SymbolNode , Context , MypyFile , TypeVarExpr ,
12
- ARG_POS , ARG_STAR , ARG_STAR2 , Decorator , OverloadedFuncDef , TypeAlias
12
+ ARG_POS , ARG_STAR , ARG_STAR2 , Decorator , OverloadedFuncDef , TypeAlias , TempNode
13
13
)
14
14
from mypy .messages import MessageBuilder
15
15
from mypy .maptype import map_instance_to_supertype
@@ -82,7 +82,7 @@ def analyze_member_access(name: str,
82
82
assert isinstance (method , OverloadedFuncDef )
83
83
first_item = cast (Decorator , method .items [0 ])
84
84
return analyze_var (name , first_item .var , typ , info , node , is_lvalue , msg ,
85
- original_type , not_ready_callback , chk = chk )
85
+ original_type , builtin_type , not_ready_callback , chk = chk )
86
86
if is_lvalue :
87
87
msg .cant_assign_to_method (node )
88
88
signature = function_type (method , builtin_type ('builtins.function' ))
@@ -155,7 +155,7 @@ def analyze_member_access(name: str,
155
155
# See https://github.com/python/mypy/pull/1787 for more info.
156
156
result = analyze_class_attribute_access (ret_type , name , node , is_lvalue ,
157
157
builtin_type , not_ready_callback , msg ,
158
- original_type = original_type )
158
+ original_type = original_type , chk = chk )
159
159
if result :
160
160
return result
161
161
# Look up from the 'type' type.
@@ -203,7 +203,7 @@ def analyze_member_access(name: str,
203
203
# See comment above for why operators are skipped
204
204
result = analyze_class_attribute_access (item , name , node , is_lvalue ,
205
205
builtin_type , not_ready_callback , msg ,
206
- original_type = original_type )
206
+ original_type = original_type , chk = chk )
207
207
if result :
208
208
if not (isinstance (result , AnyType ) and item .type .fallback_to_any ):
209
209
return result
@@ -261,8 +261,10 @@ def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo,
261
261
v .info = info
262
262
263
263
if isinstance (v , Var ):
264
+ implicit = info [name ].implicit
264
265
return analyze_var (name , v , itype , info , node , is_lvalue , msg ,
265
- original_type , not_ready_callback , chk = chk )
266
+ original_type , builtin_type , not_ready_callback ,
267
+ chk = chk , implicit = implicit )
266
268
elif isinstance (v , FuncDef ):
267
269
assert False , "Did not expect a function"
268
270
elif not v and name not in ['__getattr__' , '__setattr__' , '__getattribute__' ]:
@@ -302,6 +304,72 @@ def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo,
302
304
return msg .has_no_attr (original_type , itype , name , node )
303
305
304
306
307
+ def analyze_descriptor_access (instance_type : Type , descriptor_type : Type ,
308
+ builtin_type : Callable [[str ], Instance ],
309
+ msg : MessageBuilder ,
310
+ context : Context , * ,
311
+ chk : 'mypy.checker.TypeChecker' ) -> Type :
312
+ """Type check descriptor access.
313
+
314
+ Arguments:
315
+ instance_type: The type of the instance on which the descriptor
316
+ attribute is being accessed (the type of ``a`` in ``a.f`` when
317
+ ``f`` is a descriptor).
318
+ descriptor_type: The type of the descriptor attribute being accessed
319
+ (the type of ``f`` in ``a.f`` when ``f`` is a descriptor).
320
+ context: The node defining the context of this inference.
321
+ Return:
322
+ The return type of the appropriate ``__get__`` overload for the descriptor.
323
+ """
324
+ if isinstance (descriptor_type , UnionType ):
325
+ # Map the access over union types
326
+ return UnionType .make_simplified_union ([
327
+ analyze_descriptor_access (instance_type , typ , builtin_type ,
328
+ msg , context , chk = chk )
329
+ for typ in descriptor_type .items
330
+ ])
331
+ elif not isinstance (descriptor_type , Instance ):
332
+ return descriptor_type
333
+
334
+ if not descriptor_type .type .has_readable_member ('__get__' ):
335
+ return descriptor_type
336
+
337
+ dunder_get = descriptor_type .type .get_method ('__get__' )
338
+
339
+ if dunder_get is None :
340
+ msg .fail ("{}.__get__ is not callable" .format (descriptor_type ), context )
341
+ return AnyType (TypeOfAny .from_error )
342
+
343
+ function = function_type (dunder_get , builtin_type ('builtins.function' ))
344
+ bound_method = bind_self (function , descriptor_type )
345
+ typ = map_instance_to_supertype (descriptor_type , dunder_get .info )
346
+ dunder_get_type = expand_type_by_instance (bound_method , typ )
347
+
348
+ if isinstance (instance_type , FunctionLike ) and instance_type .is_type_obj ():
349
+ owner_type = instance_type .items ()[0 ].ret_type
350
+ instance_type = NoneTyp ()
351
+ elif isinstance (instance_type , TypeType ):
352
+ owner_type = instance_type .item
353
+ instance_type = NoneTyp ()
354
+ else :
355
+ owner_type = instance_type
356
+
357
+ _ , inferred_dunder_get_type = chk .expr_checker .check_call (
358
+ dunder_get_type ,
359
+ [TempNode (instance_type ), TempNode (TypeType .make_normalized (owner_type ))],
360
+ [ARG_POS , ARG_POS ], context )
361
+
362
+ if isinstance (inferred_dunder_get_type , AnyType ):
363
+ # check_call failed, and will have reported an error
364
+ return inferred_dunder_get_type
365
+
366
+ if not isinstance (inferred_dunder_get_type , CallableType ):
367
+ msg .fail ("{}.__get__ is not callable" .format (descriptor_type ), context )
368
+ return AnyType (TypeOfAny .from_error )
369
+
370
+ return inferred_dunder_get_type .ret_type
371
+
372
+
305
373
def instance_alias_type (alias : TypeAlias ,
306
374
builtin_type : Callable [[str ], Instance ]) -> Type :
307
375
"""Type of a type alias node targeting an instance, when appears in runtime context.
@@ -317,14 +385,16 @@ def instance_alias_type(alias: TypeAlias,
317
385
318
386
def analyze_var (name : str , var : Var , itype : Instance , info : TypeInfo , node : Context ,
319
387
is_lvalue : bool , msg : MessageBuilder , original_type : Type ,
388
+ builtin_type : Callable [[str ], Instance ],
320
389
not_ready_callback : Callable [[str , Context ], None ], * ,
321
- chk : 'mypy.checker.TypeChecker' ) -> Type :
390
+ chk : 'mypy.checker.TypeChecker' , implicit : bool = False ) -> Type :
322
391
"""Analyze access to an attribute via a Var node.
323
392
324
393
This is conceptually part of analyze_member_access and the arguments are similar.
325
394
326
395
itype is the class object in which var is defined
327
396
original_type is the type of E in the expression E.var
397
+ if implicit is True, the original Var was created as an assignment to self
328
398
"""
329
399
# Found a member variable.
330
400
itype = map_instance_to_supertype (itype , var .info )
@@ -374,6 +444,9 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont
374
444
result = AnyType (TypeOfAny .special_form )
375
445
fullname = '{}.{}' .format (var .info .fullname (), name )
376
446
hook = chk .plugin .get_attribute_hook (fullname )
447
+ if result and not is_lvalue and not implicit :
448
+ result = analyze_descriptor_access (original_type , result , builtin_type ,
449
+ msg , node , chk = chk )
377
450
if hook :
378
451
result = hook (AttributeContext (original_type , result , node , chk ))
379
452
return result
@@ -445,7 +518,8 @@ def analyze_class_attribute_access(itype: Instance,
445
518
builtin_type : Callable [[str ], Instance ],
446
519
not_ready_callback : Callable [[str , Context ], None ],
447
520
msg : MessageBuilder ,
448
- original_type : Type ) -> Optional [Type ]:
521
+ original_type : Type ,
522
+ chk : 'mypy.checker.TypeChecker' ) -> Optional [Type ]:
449
523
"""original_type is the type of E in the expression E.var"""
450
524
node = itype .type .get (name )
451
525
if not node :
@@ -474,7 +548,11 @@ def analyze_class_attribute_access(itype: Instance,
474
548
msg .fail (messages .GENERIC_INSTANCE_VAR_CLASS_ACCESS , context )
475
549
is_classmethod = ((is_decorated and cast (Decorator , node .node ).func .is_class )
476
550
or (isinstance (node .node , FuncBase ) and node .node .is_class ))
477
- return add_class_tvars (t , itype , is_classmethod , builtin_type , original_type )
551
+ result = add_class_tvars (t , itype , is_classmethod , builtin_type , original_type )
552
+ if not (is_lvalue or is_method ):
553
+ result = analyze_descriptor_access (original_type , result , builtin_type ,
554
+ msg , context , chk = chk )
555
+ return result
478
556
elif isinstance (node .node , Var ):
479
557
not_ready_callback (name , context )
480
558
return AnyType (TypeOfAny .special_form )
0 commit comments