Skip to content

Commit 8a8ff89

Browse files
committed
Fix mypy cache of WithAnnotation types
1 parent 1672b54 commit 8a8ff89

File tree

3 files changed

+27
-26
lines changed

3 files changed

+27
-26
lines changed

mypy_django_plugin/django/context.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ def get_model_class_by_fullname(self, fullname: str) -> Optional[Type[Model]]:
121121
if "," in fullname:
122122
# Remove second type arg, which might be present
123123
fullname = fullname[: fullname.index(",")]
124+
fullname = fullname.replace("__", ".")
124125

125126
module, _, model_cls_name = fullname.rpartition(".")
126127
for model_cls in self.model_modules.get(module, set()):

mypy_django_plugin/transformers/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,9 @@ def get_or_create_annotated_type(
512512
model_type = model_type.type.bases[0]
513513

514514
if fields_dict is not None:
515-
type_name = f"WithAnnotations[{model_type.type.fullname}, {fields_dict}]"
515+
type_name = f"WithAnnotations[{model_type.type.fullname.replace('.', '__')}, {fields_dict}]"
516516
else:
517-
type_name = f"WithAnnotations[{model_type.type.fullname}]"
517+
type_name = f"WithAnnotations[{model_type.type.fullname.replace('.', '__')}]"
518518

519519
annotated_typeinfo = helpers.lookup_fully_qualified_typeinfo(
520520
cast(TypeChecker, api), model_module_name + "." + type_name

tests/typecheck/managers/querysets/test_annotate.yml

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
99
unannotated_user = User.objects.get(id=1)
1010
11-
print(annotated_user.asdf) # E: "WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]" has no attribute "asdf"
11+
print(annotated_user.asdf) # E: "WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]" has no attribute "asdf"
1212
print(unannotated_user.asdf) # E: "User" has no attribute "asdf"
1313
1414
def func(user: Annotated[User, Annotations]) -> str:
1515
return user.asdf
1616
17-
func(unannotated_user) # E: Argument 1 to "func" has incompatible type "User"; expected "WithAnnotations[myapp.models.User]"
18-
func(annotated_user) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]"; expected "WithAnnotations[myapp.models.User]"
17+
func(unannotated_user) # E: Argument 1 to "func" has incompatible type "User"; expected "WithAnnotations[myapp__models__User]"
18+
func(annotated_user) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]"; expected "WithAnnotations[myapp__models__User]"
1919
2020
def func2(user: WithAnnotations[User]) -> str:
2121
return user.asdf
2222
23-
func2(unannotated_user) # E: Argument 1 to "func2" has incompatible type "User"; expected "WithAnnotations[myapp.models.User]"
24-
func2(annotated_user) # E: Argument 1 to "func2" has incompatible type "WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]"; expected "WithAnnotations[myapp.models.User]"
23+
func2(unannotated_user) # E: Argument 1 to "func2" has incompatible type "User"; expected "WithAnnotations[myapp__models__User]"
24+
func2(annotated_user) # E: Argument 1 to "func2" has incompatible type "WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]"; expected "WithAnnotations[myapp__models__User]"
2525
installed_apps:
2626
- myapp
2727
files:
@@ -44,26 +44,26 @@
4444
foo: str
4545
4646
def func(user: Annotated[User, Annotations[MyDict]]) -> str:
47-
print(user.asdf) # E: "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]" has no attribute "asdf"
47+
print(user.asdf) # E: "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]" has no attribute "asdf"
4848
return user.foo
4949
5050
unannotated_user = User.objects.get(id=1)
5151
annotated_user = User.objects.annotate(foo=Value("")).get()
5252
other_annotated_user = User.objects.annotate(other=Value("")).get()
5353
54-
func(unannotated_user) # E: Argument 1 to "func" has incompatible type "User"; expected "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]"
54+
func(unannotated_user) # E: Argument 1 to "func" has incompatible type "User"; expected "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]"
5555
x: WithAnnotations[User]
5656
func(x)
5757
func(annotated_user)
58-
func(other_annotated_user) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp.models.User, TypedDict({'other': Any})]"; expected "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]"
58+
func(other_annotated_user) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp__models__User, TypedDict({'other': Any})]"; expected "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]"
5959
6060
def func2(user: WithAnnotations[User, MyDict]) -> str:
61-
print(user.asdf) # E: "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]" has no attribute "asdf"
61+
print(user.asdf) # E: "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]" has no attribute "asdf"
6262
return user.foo
6363
64-
func2(unannotated_user) # E: Argument 1 to "func2" has incompatible type "User"; expected "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]"
64+
func2(unannotated_user) # E: Argument 1 to "func2" has incompatible type "User"; expected "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]"
6565
func2(annotated_user)
66-
func2(other_annotated_user) # E: Argument 1 to "func2" has incompatible type "WithAnnotations[myapp.models.User, TypedDict({'other': Any})]"; expected "WithAnnotations[myapp.models.User, TypedDict('main.MyDict', {'foo': builtins.str})]"
66+
func2(other_annotated_user) # E: Argument 1 to "func2" has incompatible type "WithAnnotations[myapp__models__User, TypedDict({'other': Any})]"; expected "WithAnnotations[myapp__models__User, TypedDict('main.MyDict', {'foo': builtins.str})]"
6767
installed_apps:
6868
- myapp
6969
files:
@@ -100,7 +100,7 @@
100100
func(y)
101101
102102
z: WithAnnotations[User, OtherDict]
103-
func(z) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp.models.User, TypedDict('main.OtherDict', {'other': builtins.str})]"; expected "WithAnnotations[myapp.models.User, TypedDict('main.NarrowDict', {'foo': builtins.str})]"
103+
func(z) # E: Argument 1 to "func" has incompatible type "WithAnnotations[myapp__models__User, TypedDict('main.OtherDict', {'other': builtins.str})]"; expected "WithAnnotations[myapp__models__User, TypedDict('main.NarrowDict', {'foo': builtins.str})]"
104104
105105
installed_apps:
106106
- myapp
@@ -119,12 +119,12 @@
119119
from django.db.models.expressions import F
120120
121121
qs = User.objects.annotate(foo=F('id'))
122-
reveal_type(qs) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.User, TypedDict({'foo': Any})], django_stubs_ext.WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]]"
122+
reveal_type(qs) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__User, TypedDict({'foo': Any})], django_stubs_ext.WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]]"
123123
124124
annotated = qs.get()
125-
reveal_type(annotated) # N: Revealed type is "django_stubs_ext.WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]*"
125+
reveal_type(annotated) # N: Revealed type is "django_stubs_ext.WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]*"
126126
reveal_type(annotated.foo) # N: Revealed type is "Any"
127-
print(annotated.bar) # E: "WithAnnotations[myapp.models.User, TypedDict({'foo': Any})]" has no attribute "bar"
127+
print(annotated.bar) # E: "WithAnnotations[myapp__models__User, TypedDict({'foo': Any})]" has no attribute "bar"
128128
reveal_type(annotated.username) # N: Revealed type is "builtins.str*"
129129
130130
installed_apps:
@@ -144,7 +144,7 @@
144144
from django.db.models import Count
145145
146146
qs = User.objects.annotate(Count('id'))
147-
reveal_type(qs) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.User], django_stubs_ext.WithAnnotations[myapp.models.User]]"
147+
reveal_type(qs) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__User], django_stubs_ext.WithAnnotations[myapp__models__User]]"
148148
149149
installed_apps:
150150
- myapp
@@ -167,7 +167,7 @@
167167
def animals_only(param: Animal):
168168
pass
169169
# Make sure that even though attr access falls back to Any, the type is still checked
170-
animals_only(annotated_user) # E: Argument 1 to "animals_only" has incompatible type "WithAnnotations[myapp.models.User]"; expected "Animal"
170+
animals_only(annotated_user) # E: Argument 1 to "animals_only" has incompatible type "WithAnnotations[myapp__models__User]"; expected "Animal"
171171
172172
def users_allowed(param: User):
173173
# But this function accepts only the original User type, so any attr access is not allowed within this function
@@ -196,7 +196,7 @@
196196
qs = User.objects.annotate(foo=F('id'))
197197
qs = qs.annotate(bar=F('id'))
198198
annotated = qs.get()
199-
reveal_type(annotated) # N: Revealed type is "django_stubs_ext.WithAnnotations[myapp.models.User, TypedDict({'foo': Any, 'bar': Any})]*"
199+
reveal_type(annotated) # N: Revealed type is "django_stubs_ext.WithAnnotations[myapp__models__User, TypedDict({'foo': Any, 'bar': Any})]*"
200200
reveal_type(annotated.foo) # N: Revealed type is "Any"
201201
reveal_type(annotated.bar) # N: Revealed type is "Any"
202202
reveal_type(annotated.username) # N: Revealed type is "builtins.str*"
@@ -227,11 +227,11 @@
227227
return qs.annotate(foo=F('id'))
228228
229229
def add_wrong_annotation(qs: QuerySet[User]) -> QuerySet[WithAnnotations[User, FooDict]]:
230-
return qs.annotate(bar=F('id')) # E: Incompatible return value type (got "_QuerySet[WithAnnotations[myapp.models.User, TypedDict({'bar': Any})], WithAnnotations[myapp.models.User, TypedDict({'bar': Any})]]", expected "_QuerySet[WithAnnotations[myapp.models.User, TypedDict('main.FooDict', {'foo': builtins.str})], WithAnnotations[myapp.models.User, TypedDict('main.FooDict', {'foo': builtins.str})]]")
230+
return qs.annotate(bar=F('id')) # E: Incompatible return value type (got "_QuerySet[WithAnnotations[myapp__models__User, TypedDict({'bar': Any})], WithAnnotations[myapp__models__User, TypedDict({'bar': Any})]]", expected "_QuerySet[WithAnnotations[myapp__models__User, TypedDict('main.FooDict', {'foo': builtins.str})], WithAnnotations[myapp__models__User, TypedDict('main.FooDict', {'foo': builtins.str})]]")
231231
232232
qs = add_annotation(qs)
233233
qs.get().foo
234-
qs.get().bar # E: "WithAnnotations[myapp.models.User, TypedDict('main.FooDict', {'foo': builtins.str})]" has no attribute "bar"
234+
qs.get().bar # E: "WithAnnotations[myapp__models__User, TypedDict('main.FooDict', {'foo': builtins.str})]" has no attribute "bar"
235235
236236
installed_apps:
237237
- myapp
@@ -308,19 +308,19 @@
308308
# It's possible to provide more precise types than than this, but without inspecting the
309309
# arguments to .annotate, these are the best types we can infer.
310310
qs1 = Blog.objects.values('text').annotate(foo=F('id'))
311-
reveal_type(qs1) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.Blog, TypedDict({'foo': Any})], builtins.dict[builtins.str, Any]]"
311+
reveal_type(qs1) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__Blog, TypedDict({'foo': Any})], builtins.dict[builtins.str, Any]]"
312312
qs2 = Blog.objects.values_list('text').annotate(foo=F('id'))
313-
reveal_type(qs2) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.Blog, TypedDict({'foo': Any})], builtins.tuple[Any]]"
313+
reveal_type(qs2) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__Blog, TypedDict({'foo': Any})], builtins.tuple[Any]]"
314314
qs3 = Blog.objects.values_list('text', named=True).annotate(foo=F('id'))
315315
# TODO: Would be nice to infer a NamedTuple which contains the field 'text' (str) + any number of other fields.
316316
# The reason it would have to appear to have any other fields is that annotate could potentially be called with
317317
# arbitrary parameters such that we wouldn't know how many extra fields there might be.
318318
# But it's not trivial to make such a NamedTuple, partly because since it is also an ordinary tuple, it would
319319
# have to have an arbitrary length, but still have certain fields at certain indices with specific types.
320320
# For now, Any :)
321-
reveal_type(qs3) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.Blog, TypedDict({'foo': Any})], Any]"
321+
reveal_type(qs3) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__Blog, TypedDict({'foo': Any})], Any]"
322322
qs4 = Blog.objects.values_list('text', flat=True).annotate(foo=F('id'))
323-
reveal_type(qs4) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp.models.Blog, TypedDict({'foo': Any})], builtins.str]"
323+
reveal_type(qs4) # N: Revealed type is "django.db.models.query._QuerySet[django_stubs_ext.WithAnnotations[myapp__models__Blog, TypedDict({'foo': Any})], builtins.str]"
324324
325325
326326
before_values_no_params = Blog.objects.values().annotate(foo=F('id')).get()

0 commit comments

Comments
 (0)