@@ -67,14 +67,17 @@ def not_ready_callback(self, name: str, context: Context) -> None:
67
67
self .chk .handle_cannot_determine_type (name , context )
68
68
69
69
def copy_modified (self , * , messages : Optional [MessageBuilder ] = None ,
70
- self_type : Optional [Type ] = None ) -> 'MemberContext' :
70
+ self_type : Optional [Type ] = None ,
71
+ is_lvalue : Optional [bool ] = None ) -> 'MemberContext' :
71
72
mx = MemberContext (self .is_lvalue , self .is_super , self .is_operator ,
72
73
self .original_type , self .context , self .msg , self .chk ,
73
74
self .self_type , self .module_symbol_table )
74
75
if messages is not None :
75
76
mx .msg = messages
76
77
if self_type is not None :
77
78
mx .self_type = self_type
79
+ if is_lvalue is not None :
80
+ mx .is_lvalue = is_lvalue
78
81
return mx
79
82
80
83
@@ -197,7 +200,7 @@ def analyze_instance_member_access(name: str,
197
200
198
201
# Look up the member. First look up the method dictionary.
199
202
method = info .get_method (name )
200
- if method :
203
+ if method and not isinstance ( method , Decorator ) :
201
204
if method .is_property :
202
205
assert isinstance (method , OverloadedFuncDef )
203
206
first_item = cast (Decorator , method .items [0 ])
@@ -390,29 +393,46 @@ def analyze_member_var_access(name: str,
390
393
if not mx .is_lvalue :
391
394
for method_name in ('__getattribute__' , '__getattr__' ):
392
395
method = info .get_method (method_name )
396
+
393
397
# __getattribute__ is defined on builtins.object and returns Any, so without
394
398
# the guard this search will always find object.__getattribute__ and conclude
395
399
# that the attribute exists
396
400
if method and method .info .fullname != 'builtins.object' :
397
- function = function_type (method , mx .named_type ('builtins.function' ))
398
- bound_method = bind_self (function , mx .self_type )
401
+ if isinstance (method , Decorator ):
402
+ # https://github.com/python/mypy/issues/10409
403
+ bound_method = analyze_var (method_name , method .var , itype , info , mx )
404
+ else :
405
+ bound_method = bind_self (
406
+ function_type (method , mx .named_type ('builtins.function' )),
407
+ mx .self_type ,
408
+ )
399
409
typ = map_instance_to_supertype (itype , method .info )
400
410
getattr_type = get_proper_type (expand_type_by_instance (bound_method , typ ))
401
411
if isinstance (getattr_type , CallableType ):
402
412
result = getattr_type .ret_type
403
-
404
- # Call the attribute hook before returning.
405
- fullname = '{}.{}' .format (method .info .fullname , name )
406
- hook = mx .chk .plugin .get_attribute_hook (fullname )
407
- if hook :
408
- result = hook (AttributeContext (get_proper_type (mx .original_type ),
409
- result , mx .context , mx .chk ))
410
- return result
413
+ else :
414
+ result = getattr_type
415
+
416
+ # Call the attribute hook before returning.
417
+ fullname = '{}.{}' .format (method .info .fullname , name )
418
+ hook = mx .chk .plugin .get_attribute_hook (fullname )
419
+ if hook :
420
+ result = hook (AttributeContext (get_proper_type (mx .original_type ),
421
+ result , mx .context , mx .chk ))
422
+ return result
411
423
else :
412
424
setattr_meth = info .get_method ('__setattr__' )
413
425
if setattr_meth and setattr_meth .info .fullname != 'builtins.object' :
414
- setattr_func = function_type (setattr_meth , mx .named_type ('builtins.function' ))
415
- bound_type = bind_self (setattr_func , mx .self_type )
426
+ if isinstance (setattr_meth , Decorator ):
427
+ bound_type = analyze_var (
428
+ name , setattr_meth .var , itype , info ,
429
+ mx .copy_modified (is_lvalue = False ),
430
+ )
431
+ else :
432
+ bound_type = bind_self (
433
+ function_type (setattr_meth , mx .named_type ('builtins.function' )),
434
+ mx .self_type ,
435
+ )
416
436
typ = map_instance_to_supertype (itype , setattr_meth .info )
417
437
setattr_type = get_proper_type (expand_type_by_instance (bound_type , typ ))
418
438
if isinstance (setattr_type , CallableType ) and len (setattr_type .arg_types ) > 0 :
@@ -441,32 +461,24 @@ def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Cont
441
461
msg .cant_assign_to_final (name , attr_assign = True , ctx = ctx )
442
462
443
463
444
- def analyze_descriptor_access (instance_type : Type ,
445
- descriptor_type : Type ,
446
- named_type : Callable [[str ], Instance ],
447
- msg : MessageBuilder ,
448
- context : Context , * ,
449
- chk : 'mypy.checker.TypeChecker' ) -> Type :
464
+ def analyze_descriptor_access (descriptor_type : Type ,
465
+ mx : MemberContext ) -> Type :
450
466
"""Type check descriptor access.
451
467
452
468
Arguments:
453
- instance_type: The type of the instance on which the descriptor
454
- attribute is being accessed (the type of ``a`` in ``a.f`` when
455
- ``f`` is a descriptor).
456
469
descriptor_type: The type of the descriptor attribute being accessed
457
470
(the type of ``f`` in ``a.f`` when ``f`` is a descriptor).
458
- context : The node defining the context of this inference .
471
+ mx : The current member access context.
459
472
Return:
460
473
The return type of the appropriate ``__get__`` overload for the descriptor.
461
474
"""
462
- instance_type = get_proper_type (instance_type )
475
+ instance_type = get_proper_type (mx . original_type )
463
476
descriptor_type = get_proper_type (descriptor_type )
464
477
465
478
if isinstance (descriptor_type , UnionType ):
466
479
# Map the access over union types
467
480
return make_simplified_union ([
468
- analyze_descriptor_access (instance_type , typ , named_type ,
469
- msg , context , chk = chk )
481
+ analyze_descriptor_access (typ , mx )
470
482
for typ in descriptor_type .items
471
483
])
472
484
elif not isinstance (descriptor_type , Instance ):
@@ -476,13 +488,21 @@ def analyze_descriptor_access(instance_type: Type,
476
488
return descriptor_type
477
489
478
490
dunder_get = descriptor_type .type .get_method ('__get__' )
479
-
480
491
if dunder_get is None :
481
- msg .fail (message_registry .DESCRIPTOR_GET_NOT_CALLABLE .format (descriptor_type ), context )
492
+ mx .msg .fail (message_registry .DESCRIPTOR_GET_NOT_CALLABLE .format (descriptor_type ),
493
+ mx .context )
482
494
return AnyType (TypeOfAny .from_error )
483
495
484
- function = function_type (dunder_get , named_type ('builtins.function' ))
485
- bound_method = bind_self (function , descriptor_type )
496
+ if isinstance (dunder_get , Decorator ):
497
+ bound_method = analyze_var (
498
+ '__set__' , dunder_get .var , descriptor_type , descriptor_type .type , mx ,
499
+ )
500
+ else :
501
+ bound_method = bind_self (
502
+ function_type (dunder_get , mx .named_type ('builtins.function' )),
503
+ descriptor_type ,
504
+ )
505
+
486
506
typ = map_instance_to_supertype (descriptor_type , dunder_get .info )
487
507
dunder_get_type = expand_type_by_instance (bound_method , typ )
488
508
@@ -495,19 +515,19 @@ def analyze_descriptor_access(instance_type: Type,
495
515
else :
496
516
owner_type = instance_type
497
517
498
- callable_name = chk .expr_checker .method_fullname (descriptor_type , "__get__" )
499
- dunder_get_type = chk .expr_checker .transform_callee_type (
518
+ callable_name = mx . chk .expr_checker .method_fullname (descriptor_type , "__get__" )
519
+ dunder_get_type = mx . chk .expr_checker .transform_callee_type (
500
520
callable_name , dunder_get_type ,
501
- [TempNode (instance_type , context = context ),
502
- TempNode (TypeType .make_normalized (owner_type ), context = context )],
503
- [ARG_POS , ARG_POS ], context , object_type = descriptor_type ,
521
+ [TempNode (instance_type , context = mx . context ),
522
+ TempNode (TypeType .make_normalized (owner_type ), context = mx . context )],
523
+ [ARG_POS , ARG_POS ], mx . context , object_type = descriptor_type ,
504
524
)
505
525
506
- _ , inferred_dunder_get_type = chk .expr_checker .check_call (
526
+ _ , inferred_dunder_get_type = mx . chk .expr_checker .check_call (
507
527
dunder_get_type ,
508
- [TempNode (instance_type , context = context ),
509
- TempNode (TypeType .make_normalized (owner_type ), context = context )],
510
- [ARG_POS , ARG_POS ], context , object_type = descriptor_type ,
528
+ [TempNode (instance_type , context = mx . context ),
529
+ TempNode (TypeType .make_normalized (owner_type ), context = mx . context )],
530
+ [ARG_POS , ARG_POS ], mx . context , object_type = descriptor_type ,
511
531
callable_name = callable_name )
512
532
513
533
inferred_dunder_get_type = get_proper_type (inferred_dunder_get_type )
@@ -516,7 +536,8 @@ def analyze_descriptor_access(instance_type: Type,
516
536
return inferred_dunder_get_type
517
537
518
538
if not isinstance (inferred_dunder_get_type , CallableType ):
519
- msg .fail (message_registry .DESCRIPTOR_GET_NOT_CALLABLE .format (descriptor_type ), context )
539
+ mx .msg .fail (message_registry .DESCRIPTOR_GET_NOT_CALLABLE .format (descriptor_type ),
540
+ mx .context )
520
541
return AnyType (TypeOfAny .from_error )
521
542
522
543
return inferred_dunder_get_type .ret_type
@@ -605,8 +626,7 @@ def analyze_var(name: str,
605
626
fullname = '{}.{}' .format (var .info .fullname , name )
606
627
hook = mx .chk .plugin .get_attribute_hook (fullname )
607
628
if result and not mx .is_lvalue and not implicit :
608
- result = analyze_descriptor_access (mx .original_type , result , mx .named_type ,
609
- mx .msg , mx .context , chk = mx .chk )
629
+ result = analyze_descriptor_access (result , mx )
610
630
if hook :
611
631
result = hook (AttributeContext (get_proper_type (mx .original_type ),
612
632
result , mx .context , mx .chk ))
@@ -785,8 +805,7 @@ def analyze_class_attribute_access(itype: Instance,
785
805
result = add_class_tvars (t , isuper , is_classmethod ,
786
806
mx .self_type , original_vars = original_vars )
787
807
if not mx .is_lvalue :
788
- result = analyze_descriptor_access (mx .original_type , result , mx .named_type ,
789
- mx .msg , mx .context , chk = mx .chk )
808
+ result = analyze_descriptor_access (result , mx )
790
809
return result
791
810
elif isinstance (node .node , Var ):
792
811
mx .not_ready_callback (name , mx .context )
0 commit comments