From 3e810f3adc5337313cf549213ac1d56eca884a21 Mon Sep 17 00:00:00 2001 From: Niicck Date: Sat, 9 Nov 2024 16:53:35 -0600 Subject: [PATCH 1/2] create handles_type_annotations_on_3rd_party_models test case --- requirements.txt | 3 +++ tests/typecheck/models/test_3rd_party_models.yml | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/typecheck/models/test_3rd_party_models.yml diff --git a/requirements.txt b/requirements.txt index 800083d32..d5b5b0431 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,9 @@ Django==5.1.3; python_version >= '3.10' -e ./ext -e .[redis,compatible-mypy,oracle] +# 3rd Party Library for Testing +django-extensions==3.2.3 + # Overrides: mypy==1.13.0 pyright==1.1.388 diff --git a/tests/typecheck/models/test_3rd_party_models.yml b/tests/typecheck/models/test_3rd_party_models.yml new file mode 100644 index 000000000..9a6636575 --- /dev/null +++ b/tests/typecheck/models/test_3rd_party_models.yml @@ -0,0 +1,8 @@ +- case: handles_type_annotations_on_3rd_party_models + main: | + from django.db import models + from django_extensions.db.models import TimeStampedModel # type: ignore [import-untyped] + + class A(TimeStampedModel): + name = models.CharField() + count = models.IntegerField() From c200c4cebd994eed14774735aa49b49de40ff0f2 Mon Sep 17 00:00:00 2001 From: Niicck Date: Sun, 10 Nov 2024 01:44:11 -0600 Subject: [PATCH 2/2] pass handles_type_annotations_on_3rd_party_models --- mypy_django_plugin/lib/helpers.py | 4 ++++ mypy_django_plugin/transformers/fields.py | 5 ++++- .../typecheck/models/test_3rd_party_models.yml | 17 ++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/mypy_django_plugin/lib/helpers.py b/mypy_django_plugin/lib/helpers.py index cea674d4d..7758f5aba 100644 --- a/mypy_django_plugin/lib/helpers.py +++ b/mypy_django_plugin/lib/helpers.py @@ -526,6 +526,10 @@ def is_model_type(info: TypeInfo) -> bool: return info.metaclass_type is not None and info.metaclass_type.type.has_base(fullnames.MODEL_METACLASS_FULLNAME) +def is_registered_model_type(info: TypeInfo, django_context: "DjangoContext") -> bool: + return info.fullname in {get_class_fullname(cls) for cls in django_context.all_registered_model_classes} + + def get_model_from_expression( expr: Expression, *, diff --git a/mypy_django_plugin/transformers/fields.py b/mypy_django_plugin/transformers/fields.py index f2bd21009..ae5f2efeb 100644 --- a/mypy_django_plugin/transformers/fields.py +++ b/mypy_django_plugin/transformers/fields.py @@ -237,7 +237,10 @@ def transform_into_proper_return_type(ctx: FunctionContext, django_context: Djan assert isinstance(default_return_type, Instance) outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class() - if outer_model_info is None or not helpers.is_model_type(outer_model_info): + if outer_model_info is None or ( + not helpers.is_model_type(outer_model_info) + and not helpers.is_registered_model_type(outer_model_info, django_context) + ): return ctx.default_return_type assert isinstance(outer_model_info, TypeInfo) diff --git a/tests/typecheck/models/test_3rd_party_models.yml b/tests/typecheck/models/test_3rd_party_models.yml index 9a6636575..e7a23ef09 100644 --- a/tests/typecheck/models/test_3rd_party_models.yml +++ b/tests/typecheck/models/test_3rd_party_models.yml @@ -1,8 +1,15 @@ - case: handles_type_annotations_on_3rd_party_models + installed_apps: + - django_extensions + - myapp main: | - from django.db import models - from django_extensions.db.models import TimeStampedModel # type: ignore [import-untyped] + from myapp.models import A + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + from django_extensions.db.models import TimeStampedModel # type: ignore[import-untyped] - class A(TimeStampedModel): - name = models.CharField() - count = models.IntegerField() + class A(TimeStampedModel): + name = models.CharField()