Skip to content

Commit 034060c

Browse files
committed
Fix missing methods on copied manager
1 parent a0004b3 commit 034060c

File tree

3 files changed

+15
-14
lines changed

3 files changed

+15
-14
lines changed

mypy_django_plugin/lib/helpers.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -382,29 +382,25 @@ def copy_method_to_another_class(
382382
method_node: FuncDef,
383383
return_type: Optional[MypyType] = None,
384384
original_module_name: Optional[str] = None,
385-
) -> None:
385+
) -> bool:
386386
semanal_api = get_semanal_api(ctx)
387387
if method_node.type is None:
388-
if not semanal_api.final_iteration:
389-
semanal_api.defer()
390-
return
391-
392388
arguments, return_type = build_unannotated_method_args(method_node)
393389
add_method_to_class(
394390
semanal_api, ctx.cls, new_method_name, args=arguments, return_type=return_type, self_type=self_type
395391
)
396-
return
392+
return True
397393

398394
method_type = method_node.type
399395
if not isinstance(method_type, CallableType):
400396
if not semanal_api.final_iteration:
401397
semanal_api.defer()
402-
return
398+
return False
403399

404400
if return_type is None:
405401
return_type = bind_or_analyze_type(method_type.ret_type, semanal_api, original_module_name)
406402
if return_type is None:
407-
return
403+
return False
408404

409405
# We build the arguments from the method signature (`CallableType`), because if we were to
410406
# use the arguments from the method node (`FuncDef.arguments`) we're not compatible with
@@ -417,7 +413,7 @@ def copy_method_to_another_class(
417413
):
418414
bound_arg_type = bind_or_analyze_type(arg_type, semanal_api, original_module_name)
419415
if bound_arg_type is None:
420-
return
416+
return False
421417
if arg_name is None and hasattr(method_node, "arguments"):
422418
arg_name = method_node.arguments[pos].variable.name
423419
arguments.append(
@@ -435,6 +431,8 @@ def copy_method_to_another_class(
435431
semanal_api, ctx.cls, new_method_name, args=arguments, return_type=return_type, self_type=self_type
436432
)
437433

434+
return True
435+
438436

439437
def add_new_manager_base(api: SemanticAnalyzerPluginInterface, fullname: str) -> None:
440438
sym = api.lookup_fully_qualified_or_none(fullnames.MANAGER_CLASS_FULLNAME)

mypy_django_plugin/transformers/models.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from mypy.types import AnyType, Instance
2525
from mypy.types import Type as MypyType
2626
from mypy.types import TypedDictType, TypeOfAny
27+
from typing_extensions import reveal_type
2728

2829
from mypy_django_plugin.django.context import DjangoContext
2930
from mypy_django_plugin.errorcodes import MANAGER_MISSING
@@ -211,12 +212,12 @@ def create_new_model_parametrized_manager(self, name: str, base_manager_info: Ty
211212
bases = []
212213
for original_base in base_manager_info.bases:
213214
if self.is_any_parametrized_manager(original_base):
214-
if original_base.type is None:
215-
raise helpers.IncompleteDefnException()
216-
217215
original_base = helpers.reparametrize_instance(original_base, [Instance(self.model_classdef.info, [])])
218216
bases.append(original_base)
219217

218+
# TODO: This adds the manager to the module, even if we end up
219+
# deferring. That can be avoided by not adding it to the module first,
220+
# but rather waiting until we know we won't defer
220221
new_manager_info = self.add_new_class_for_current_module(name, bases)
221222
# copy fields to a new manager
222223
new_cls_def_context = ClassDefContext(cls=new_manager_info.defn, reason=self.ctx.reason, api=self.api)
@@ -225,13 +226,15 @@ def create_new_model_parametrized_manager(self, name: str, base_manager_info: Ty
225226
for name, sym in base_manager_info.names.items():
226227
# replace self type with new class, if copying method
227228
if isinstance(sym.node, FuncDef):
228-
helpers.copy_method_to_another_class(
229+
copied_method = helpers.copy_method_to_another_class(
229230
new_cls_def_context,
230231
self_type=custom_manager_type,
231232
new_method_name=name,
232233
method_node=sym.node,
233234
original_module_name=base_manager_info.module_name,
234235
)
236+
if not copied_method and not self.api.final_iteration:
237+
raise helpers.IncompleteDefnException()
235238
continue
236239

237240
new_sym = sym.copy()

tests/typecheck/managers/test_managers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@
364364
- case: custom_manager_annotate_method_before_type_declaration
365365
main: |
366366
from myapp.models import ModelA, ModelB, ManagerA
367-
reveal_type(ModelA.objects) # N: Revealed type is "myapp.models.ModelA_ManagerA[myapp.models.ModelA]"
367+
reveal_type(ModelA.objects) # N: Revealed type is "myapp.models.ModelA_ManagerA1[myapp.models.ModelA]"
368368
reveal_type(ModelA.objects.do_something) # N: Revealed type is "def (other_obj: myapp.models.ModelB) -> builtins.str"
369369
installed_apps:
370370
- myapp

0 commit comments

Comments
 (0)