@@ -67,14 +67,17 @@ def not_ready_callback(self, name: str, context: Context) -> None:
6767 self .chk .handle_cannot_determine_type (name , context )
6868
6969 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' :
7172 mx = MemberContext (self .is_lvalue , self .is_super , self .is_operator ,
7273 self .original_type , self .context , self .msg , self .chk ,
7374 self .self_type , self .module_symbol_table )
7475 if messages is not None :
7576 mx .msg = messages
7677 if self_type is not None :
7778 mx .self_type = self_type
79+ if is_lvalue is not None :
80+ mx .is_lvalue = is_lvalue
7881 return mx
7982
8083
@@ -197,7 +200,7 @@ def analyze_instance_member_access(name: str,
197200
198201 # Look up the member. First look up the method dictionary.
199202 method = info .get_method (name )
200- if method :
203+ if method and not isinstance ( method , Decorator ) :
201204 if method .is_property :
202205 assert isinstance (method , OverloadedFuncDef )
203206 first_item = cast (Decorator , method .items [0 ])
@@ -390,29 +393,46 @@ def analyze_member_var_access(name: str,
390393 if not mx .is_lvalue :
391394 for method_name in ('__getattribute__' , '__getattr__' ):
392395 method = info .get_method (method_name )
396+
393397 # __getattribute__ is defined on builtins.object and returns Any, so without
394398 # the guard this search will always find object.__getattribute__ and conclude
395399 # that the attribute exists
396400 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+ )
399409 typ = map_instance_to_supertype (itype , method .info )
400410 getattr_type = get_proper_type (expand_type_by_instance (bound_method , typ ))
401411 if isinstance (getattr_type , CallableType ):
402412 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
411423 else :
412424 setattr_meth = info .get_method ('__setattr__' )
413425 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+ )
416436 typ = map_instance_to_supertype (itype , setattr_meth .info )
417437 setattr_type = get_proper_type (expand_type_by_instance (bound_type , typ ))
418438 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
441461 msg .cant_assign_to_final (name , attr_assign = True , ctx = ctx )
442462
443463
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 :
450466 """Type check descriptor access.
451467
452468 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).
456469 descriptor_type: The type of the descriptor attribute being accessed
457470 (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.
459472 Return:
460473 The return type of the appropriate ``__get__`` overload for the descriptor.
461474 """
462- instance_type = get_proper_type (instance_type )
475+ instance_type = get_proper_type (mx . original_type )
463476 descriptor_type = get_proper_type (descriptor_type )
464477
465478 if isinstance (descriptor_type , UnionType ):
466479 # Map the access over union types
467480 return make_simplified_union ([
468- analyze_descriptor_access (instance_type , typ , named_type ,
469- msg , context , chk = chk )
481+ analyze_descriptor_access (typ , mx )
470482 for typ in descriptor_type .items
471483 ])
472484 elif not isinstance (descriptor_type , Instance ):
@@ -476,13 +488,21 @@ def analyze_descriptor_access(instance_type: Type,
476488 return descriptor_type
477489
478490 dunder_get = descriptor_type .type .get_method ('__get__' )
479-
480491 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 )
482494 return AnyType (TypeOfAny .from_error )
483495
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+
486506 typ = map_instance_to_supertype (descriptor_type , dunder_get .info )
487507 dunder_get_type = expand_type_by_instance (bound_method , typ )
488508
@@ -495,19 +515,19 @@ def analyze_descriptor_access(instance_type: Type,
495515 else :
496516 owner_type = instance_type
497517
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 (
500520 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 ,
504524 )
505525
506- _ , inferred_dunder_get_type = chk .expr_checker .check_call (
526+ _ , inferred_dunder_get_type = mx . chk .expr_checker .check_call (
507527 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 ,
511531 callable_name = callable_name )
512532
513533 inferred_dunder_get_type = get_proper_type (inferred_dunder_get_type )
@@ -516,7 +536,8 @@ def analyze_descriptor_access(instance_type: Type,
516536 return inferred_dunder_get_type
517537
518538 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 )
520541 return AnyType (TypeOfAny .from_error )
521542
522543 return inferred_dunder_get_type .ret_type
@@ -605,8 +626,7 @@ def analyze_var(name: str,
605626 fullname = '{}.{}' .format (var .info .fullname , name )
606627 hook = mx .chk .plugin .get_attribute_hook (fullname )
607628 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 )
610630 if hook :
611631 result = hook (AttributeContext (get_proper_type (mx .original_type ),
612632 result , mx .context , mx .chk ))
@@ -785,8 +805,7 @@ def analyze_class_attribute_access(itype: Instance,
785805 result = add_class_tvars (t , isuper , is_classmethod ,
786806 mx .self_type , original_vars = original_vars )
787807 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 )
790809 return result
791810 elif isinstance (node .node , Var ):
792811 mx .not_ready_callback (name , mx .context )
0 commit comments