1414 StrExpr ,
1515 SymbolTableNode ,
1616 TypeInfo ,
17- Var ,
1817)
19- from mypy .plugin import AttributeContext , ClassDefContext , DynamicClassDefContext
18+ from mypy .plugin import AttributeContext , ClassDefContext , DynamicClassDefContext , MethodContext
2019from mypy .semanal import SemanticAnalyzer
2120from mypy .semanal_shared import has_placeholder
21+ from mypy .subtypes import find_member
2222from mypy .types import (
2323 AnyType ,
2424 CallableType ,
2727 Overloaded ,
2828 ProperType ,
2929 TypeOfAny ,
30+ TypeType ,
3031 TypeVarType ,
3132 UnionType ,
3233 get_proper_type ,
@@ -120,15 +121,11 @@ def _process_dynamic_method(
120121 variables = method_type .variables
121122 ret_type = method_type .ret_type
122123
123- if not is_fallback_queryset :
124- queryset_instance = Instance (queryset_info , manager_instance .args )
125- else :
126- # The fallback queryset inherits _QuerySet, which has two generics
127- # instead of the one exposed on QuerySet. That means that we need
128- # to add the model twice. In real code it's not possible to inherit
129- # from _QuerySet, as it doesn't exist at runtime, so this fix is
130- # only needed for plugin-generated querysets.
131- queryset_instance = Instance (queryset_info , [manager_instance .args [0 ], manager_instance .args [0 ]])
124+ manager_model = find_member ("model" , manager_instance , manager_instance )
125+ assert isinstance (manager_model , TypeType ), manager_model
126+ manager_model_type = manager_model .item
127+
128+ queryset_instance = Instance (queryset_info , (manager_model_type ,) * len (queryset_info .type_vars ))
132129
133130 # For methods on the manager that return a queryset we need to override the
134131 # return type to be the actual queryset class, not the base QuerySet that's
@@ -553,26 +550,9 @@ def create_new_manager_class_from_as_manager_method(ctx: DynamicClassDefContext)
553550 manager_name = manager_class_name ,
554551 manager_base = manager_base ,
555552 )
553+ queryset_info .metadata .setdefault ("django_as_manager_names" , {})
554+ queryset_info .metadata ["django_as_manager_names" ][semanal_api .cur_mod_id ] = new_manager_info .name
556555
557- # So that the plugin will reparameterize the manager when it is constructed inside of a Model definition
558- helpers .add_new_manager_base (semanal_api , new_manager_info .fullname )
559-
560- # Whenever `<QuerySet>.as_manager()` isn't called at class level, we want to ensure
561- # that the variable is an instance of our generated manager. Instead of the return
562- # value of `.as_manager()`. Though model argument is populated as `Any`.
563- # `transformers.models.AddManagers` will populate a model's manager(s), when it
564- # finds it on class level.
565- var = Var (name = ctx .name , type = Instance (new_manager_info , [AnyType (TypeOfAny .from_omitted_generics )]))
566- var .info = new_manager_info
567- var ._fullname = f"{ current_module .fullname } .{ ctx .name } "
568- var .is_inferred = True
569- # Note: Order of `add_symbol_table_node` calls matters. Depending on what level
570- # we've found the `.as_manager()` call. Point here being that we want to replace the
571- # `.as_manager` return value with our newly created manager.
572- added = semanal_api .add_symbol_table_node (
573- ctx .name , SymbolTableNode (semanal_api .current_symbol_kind (), var , plugin_generated = True )
574- )
575- assert added
576556 # Add the new manager to the current module
577557 added = semanal_api .add_symbol_table_node (
578558 # We'll use `new_manager_info.name` instead of `manager_class_name` here
@@ -584,6 +564,26 @@ def create_new_manager_class_from_as_manager_method(ctx: DynamicClassDefContext)
584564 assert added
585565
586566
567+ def construct_as_manager_instance (ctx : MethodContext , * , info : TypeInfo ) -> MypyType :
568+ api = helpers .get_typechecker_api (ctx )
569+ module = helpers .get_current_module (api )
570+ try :
571+ manager_name = info .metadata ["django_as_manager_names" ][module .fullname ]
572+ except KeyError :
573+ return ctx .default_return_type
574+
575+ manager_node = api .lookup (manager_name )
576+ if not isinstance (manager_node .node , TypeInfo ):
577+ return ctx .default_return_type
578+
579+ # Whenever `<QuerySet>.as_manager()` isn't called at class level, we want to ensure
580+ # that the variable is an instance of our generated manager. Instead of the return
581+ # value of `.as_manager()`. Though model argument is populated as `Any`.
582+ # `transformers.models.AddManagers` will populate a model's manager(s), when it
583+ # finds it on class level.
584+ return Instance (manager_node .node , [AnyType (TypeOfAny .from_omitted_generics )])
585+
586+
587587def reparametrize_any_manager_hook (ctx : ClassDefContext ) -> None :
588588 """
589589 Add implicit generics to manager classes that are defined without generic.
0 commit comments