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