0
# MyPy Plugin
1
2
Django-stubs includes a comprehensive MyPy plugin that enhances type checking for Django-specific patterns, providing better inference for QuerySets, model fields, forms, and request handling.
3
4
## Capabilities
5
6
### Plugin Configuration
7
8
Main plugin class and configuration for enhanced Django type checking.
9
10
```python { .api }
11
class DjangoPlugin:
12
"""
13
Main MyPy plugin class for Django type checking enhancement.
14
15
Provides Django-specific type inference and validation.
16
"""
17
def __init__(self, options: Options): ...
18
19
def get_method_context_hook(self, fullname: str): ...
20
def get_method_signature_hook(self, fullname: str): ...
21
def get_attribute_hook(self, fullname: str): ...
22
def get_class_decorator_hook(self, fullname: str): ...
23
def get_metaclass_hook(self, fullname: str): ...
24
def get_base_class_hook(self, fullname: str): ...
25
def get_customize_class_mro_hook(self, fullname: str): ...
26
def get_dynamic_class_hook(self, fullname: str): ...
27
28
class DjangoPluginConfig:
29
"""
30
Configuration settings for Django MyPy plugin.
31
32
Controls plugin behavior and feature flags.
33
"""
34
django_settings_module: str = None
35
ignore_missing_model_attributes: bool = False
36
ignore_missing_settings: bool = False
37
strict_settings: bool = False
38
39
def __init__(self, config_file: str = None): ...
40
```
41
42
### Model Field Transformations
43
44
Enhanced type inference for Django model fields and attributes.
45
46
```python { .api }
47
class ModelFieldTransformer:
48
"""
49
Transformer for model field type resolution.
50
51
Provides accurate type information for model fields and relationships.
52
"""
53
def transform_model_field(self, field_node, field_type: Type, ctx) -> Type: ...
54
def get_field_descriptor_type(self, field: Field, is_set: bool = False) -> Type: ...
55
def resolve_field_access_type(self, field: Field, access_type: str) -> Type: ...
56
57
# Field type mappings
58
def get_char_field_type(self, max_length: int = None) -> Type: ...
59
def get_integer_field_type(self) -> Type: ...
60
def get_boolean_field_type(self) -> Type: ...
61
def get_datetime_field_type(self) -> Type: ...
62
def get_foreign_key_type(self, related_model: Type) -> Type: ...
63
def get_many_to_many_type(self, related_model: Type) -> Type: ...
64
65
class FieldDescriptorTypes:
66
"""
67
Type definitions for model field descriptors.
68
69
Provides proper typing for field access patterns.
70
"""
71
# Descriptor return types
72
FIELD_GET_TYPES: dict = {
73
'CharField': 'str',
74
'TextField': 'str',
75
'IntegerField': 'int',
76
'BooleanField': 'bool',
77
'DateTimeField': 'datetime.datetime',
78
'DateField': 'datetime.date',
79
'TimeField': 'datetime.time',
80
'DecimalField': 'decimal.Decimal',
81
'FloatField': 'float',
82
'EmailField': 'str',
83
'URLField': 'str',
84
'UUIDField': 'uuid.UUID',
85
'JSONField': 'Any',
86
}
87
88
# Descriptor set types (for assignment)
89
FIELD_SET_TYPES: dict = {
90
'CharField': 'Union[str, Combinable]',
91
'TextField': 'Union[str, Combinable]',
92
'IntegerField': 'Union[int, Combinable]',
93
'BooleanField': 'Union[bool, Combinable]',
94
'DateTimeField': 'Union[datetime.datetime, str, Combinable]',
95
'DateField': 'Union[datetime.date, str, Combinable]',
96
'TimeField': 'Union[datetime.time, str, Combinable]',
97
'DecimalField': 'Union[Decimal, int, float, str, Combinable]',
98
'FloatField': 'Union[float, int, Combinable]',
99
}
100
```
101
102
### QuerySet Method Resolution
103
104
Enhanced type checking for QuerySet operations and method chaining.
105
106
```python { .api }
107
class QuerySetMethodResolver:
108
"""
109
Resolver for QuerySet method types and return values.
110
111
Provides accurate type inference for QuerySet operations.
112
"""
113
def resolve_queryset_method(self, method_name: str, base_type: Type, args: list) -> Type: ...
114
def get_queryset_filter_type(self, base_type: Type, filter_args: dict) -> Type: ...
115
def get_values_return_type(self, base_type: Type, field_names: list) -> Type: ...
116
def get_values_list_return_type(self, base_type: Type, field_names: list, flat: bool = False, named: bool = False) -> Type: ...
117
def get_annotate_return_type(self, base_type: Type, annotations: dict) -> Type: ...
118
def get_aggregate_return_type(self, aggregations: dict) -> Type: ...
119
120
# QuerySet method return types
121
QUERYSET_METHOD_TYPES: dict = {
122
'all': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
123
'filter': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
124
'exclude': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
125
'order_by': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
126
'distinct': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
127
'select_related': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
128
'prefetch_related': lambda base_type: f'QuerySet[{base_type}, {base_type}]',
129
'get': lambda base_type: base_type,
130
'first': lambda base_type: f'Optional[{base_type}]',
131
'last': lambda base_type: f'Optional[{base_type}]',
132
'exists': lambda base_type: 'bool',
133
'count': lambda base_type: 'int',
134
'delete': lambda base_type: 'Tuple[int, Dict[str, int]]',
135
'update': lambda base_type: 'int',
136
}
137
138
class ValuesQuerySetResolver:
139
"""
140
Special resolver for ValuesQuerySet type inference.
141
142
Handles values() and values_list() return type resolution.
143
"""
144
def resolve_values_type(self, model_type: Type, field_names: list) -> Type: ...
145
def resolve_values_list_type(self, model_type: Type, field_names: list, flat: bool = False, named: bool = False) -> Type: ...
146
def get_field_value_type(self, model_type: Type, field_name: str) -> Type: ...
147
148
# Generate proper TypedDict for values() calls
149
def create_values_typed_dict(self, model_type: Type, field_names: list) -> Type: ...
150
```
151
152
### Manager Class Processing
153
154
Type inference enhancement for model managers and custom manager methods.
155
156
```python { .api }
157
class ManagerClassProcessor:
158
"""
159
Processor for Manager class creation and method resolution.
160
161
Provides proper typing for custom manager methods.
162
"""
163
def process_manager_class(self, manager_class: Type, model_type: Type) -> Type: ...
164
def add_manager_methods(self, manager_class: Type, model_type: Type) -> None: ...
165
def resolve_manager_method_type(self, method_name: str, manager_class: Type, model_type: Type) -> Type: ...
166
167
# Default manager method types
168
MANAGER_METHOD_TYPES: dict = {
169
'get_queryset': lambda model_type: f'QuerySet[{model_type}, {model_type}]',
170
'all': lambda model_type: f'QuerySet[{model_type}, {model_type}]',
171
'filter': lambda model_type: f'QuerySet[{model_type}, {model_type}]',
172
'exclude': lambda model_type: f'QuerySet[{model_type}, {model_type}]',
173
'get': lambda model_type: model_type,
174
'create': lambda model_type: model_type,
175
'get_or_create': lambda model_type: f'Tuple[{model_type}, bool]',
176
'update_or_create': lambda model_type: f'Tuple[{model_type}, bool]',
177
'bulk_create': lambda model_type: f'List[{model_type}]',
178
'bulk_update': lambda model_type: 'int',
179
}
180
181
class RelatedManagerProcessor:
182
"""
183
Processor for related object managers (ForeignKey, ManyToMany).
184
185
Provides typing for relationship manager methods.
186
"""
187
def process_related_manager(self, field: Field, related_type: Type) -> Type: ...
188
def get_foreign_key_manager_type(self, related_type: Type) -> Type: ...
189
def get_many_to_many_manager_type(self, related_type: Type) -> Type: ...
190
def get_reverse_foreign_key_manager_type(self, related_type: Type) -> Type: ...
191
```
192
193
### Form Processing
194
195
Enhanced type checking for Django forms and form fields.
196
197
```python { .api }
198
class FormFieldProcessor:
199
"""
200
Processor for form field types and validation.
201
202
Provides accurate typing for form field operations.
203
"""
204
def process_form_field(self, field_type: Type, field_kwargs: dict) -> Type: ...
205
def get_field_clean_return_type(self, field_type: Type) -> Type: ...
206
def get_widget_type(self, field_type: Type, widget_class: Type = None) -> Type: ...
207
208
# Form field clean method return types
209
FIELD_CLEAN_TYPES: dict = {
210
'CharField': 'str',
211
'IntegerField': 'int',
212
'BooleanField': 'bool',
213
'EmailField': 'str',
214
'URLField': 'str',
215
'DateField': 'datetime.date',
216
'DateTimeField': 'datetime.datetime',
217
'TimeField': 'datetime.time',
218
'DecimalField': 'decimal.Decimal',
219
'FloatField': 'float',
220
'ChoiceField': 'str',
221
'MultipleChoiceField': 'List[str]',
222
'FileField': 'UploadedFile',
223
'ImageField': 'UploadedFile',
224
'ModelChoiceField': 'Model',
225
'ModelMultipleChoiceField': 'QuerySet',
226
}
227
228
class ModelFormProcessor:
229
"""
230
Processor for ModelForm type inference and validation.
231
232
Connects model fields to form fields with proper typing.
233
"""
234
def process_model_form(self, form_class: Type, model_class: Type) -> Type: ...
235
def get_model_form_fields(self, model_class: Type, fields: list = None, exclude: list = None) -> dict: ...
236
def convert_model_field_to_form_field(self, model_field: Field) -> Type: ...
237
def get_form_save_type(self, model_class: Type, commit: bool = True) -> Type: ...
238
239
class FormSetProcessor:
240
"""
241
Processor for FormSet and ModelFormSet types.
242
243
Provides typing for formset operations and validation.
244
"""
245
def process_formset(self, form_class: Type, formset_class: Type) -> Type: ...
246
def process_model_formset(self, model_class: Type, form_class: Type) -> Type: ...
247
def get_formset_forms_type(self, form_class: Type) -> Type: ...
248
def get_formset_save_type(self, model_class: Type) -> Type: ...
249
```
250
251
### Request Type Resolution
252
253
Enhanced type inference for HttpRequest and user attributes.
254
255
```python { .api }
256
class RequestTypeResolver:
257
"""
258
Resolver for HttpRequest type inference and user types.
259
260
Provides accurate typing for request attributes and methods.
261
"""
262
def resolve_request_type(self, request_type: Type) -> Type: ...
263
def get_request_user_type(self, request_type: Type) -> Type: ...
264
def get_request_session_type(self) -> Type: ...
265
def resolve_request_method_type(self, method_name: str) -> Type: ...
266
267
# Request attribute types
268
REQUEST_ATTRIBUTE_TYPES: dict = {
269
'method': 'str',
270
'path': 'str',
271
'path_info': 'str',
272
'GET': 'QueryDict',
273
'POST': 'QueryDict',
274
'COOKIES': 'Dict[str, str]',
275
'FILES': 'MultiValueDict',
276
'META': 'Dict[str, str]',
277
'content_type': 'str',
278
'content_params': 'Dict[str, str]',
279
'resolver_match': 'ResolverMatch',
280
}
281
282
class UserTypeResolver:
283
"""
284
Resolver for User model types in requests and authentication.
285
286
Handles custom user models and AnonymousUser types.
287
"""
288
def resolve_user_type(self, user_model: Type = None) -> Type: ...
289
def get_authenticated_user_type(self) -> Type: ...
290
def get_anonymous_user_type(self) -> Type: ...
291
def resolve_user_attribute_type(self, attr_name: str, user_type: Type) -> Type: ...
292
```
293
294
### Settings Type Resolution
295
296
Type checking for Django settings access and configuration.
297
298
```python { .api }
299
class SettingsTypeResolver:
300
"""
301
Resolver for Django settings attribute types.
302
303
Provides type information for settings access patterns.
304
"""
305
def resolve_settings_attribute(self, attr_name: str) -> Type: ...
306
def get_settings_type_from_default(self, setting_name: str, default_value) -> Type: ...
307
def validate_settings_access(self, attr_name: str) -> bool: ...
308
309
# Common Django settings types
310
SETTINGS_TYPES: dict = {
311
'DEBUG': 'bool',
312
'SECRET_KEY': 'str',
313
'ALLOWED_HOSTS': 'List[str]',
314
'INSTALLED_APPS': 'List[str]',
315
'MIDDLEWARE': 'List[str]',
316
'ROOT_URLCONF': 'str',
317
'TEMPLATES': 'List[Dict[str, Any]]',
318
'DATABASES': 'Dict[str, Dict[str, Any]]',
319
'STATIC_URL': 'str',
320
'STATIC_ROOT': 'str',
321
'MEDIA_URL': 'str',
322
'MEDIA_ROOT': 'str',
323
'DEFAULT_AUTO_FIELD': 'str',
324
'USE_TZ': 'bool',
325
'USE_I18N': 'bool',
326
'TIME_ZONE': 'str',
327
'LANGUAGE_CODE': 'str',
328
}
329
330
class LazySettingsProcessor:
331
"""
332
Processor for lazy settings object type resolution.
333
334
Handles django.conf.settings lazy loading patterns.
335
"""
336
def process_lazy_settings(self) -> Type: ...
337
def resolve_lazy_attribute_access(self, attr_name: str) -> Type: ...
338
def validate_settings_configured(self) -> bool: ...
339
```
340
341
### ORM Lookup Processing
342
343
Enhanced type checking for ORM field lookups and query expressions.
344
345
```python { .api }
346
class ORMLookupProcessor:
347
"""
348
Processor for ORM lookup type validation and inference.
349
350
Validates field lookups in filter/exclude/get operations.
351
"""
352
def process_lookup_expression(self, field_name: str, lookup_type: str, model_type: Type) -> Type: ...
353
def validate_field_lookup(self, field: Field, lookup_type: str) -> bool: ...
354
def get_lookup_value_type(self, field: Field, lookup_type: str) -> Type: ...
355
356
# Field lookup compatibility matrix
357
FIELD_LOOKUP_TYPES: dict = {
358
'CharField': ['exact', 'iexact', 'contains', 'icontains', 'startswith', 'istartswith',
359
'endswith', 'iendswith', 'regex', 'iregex', 'in', 'isnull'],
360
'IntegerField': ['exact', 'lt', 'lte', 'gt', 'gte', 'in', 'range', 'isnull'],
361
'DateTimeField': ['exact', 'lt', 'lte', 'gt', 'gte', 'in', 'range', 'year', 'month',
362
'day', 'hour', 'minute', 'second', 'date', 'time', 'isnull'],
363
'BooleanField': ['exact', 'isnull'],
364
'ForeignKey': ['exact', 'in', 'isnull', 'pk', 'related_field_lookups'],
365
}
366
367
class QueryExpressionProcessor:
368
"""
369
Processor for query expression types (F, Q, etc.).
370
371
Provides typing for complex query expressions and aggregations.
372
"""
373
def process_f_expression(self, field_name: str, model_type: Type) -> Type: ...
374
def process_q_expression(self, q_expr: dict, model_type: Type) -> Type: ...
375
def process_case_expression(self, when_clauses: list, default_value: Type) -> Type: ...
376
def process_subquery_expression(self, subquery_type: Type) -> Type: ...
377
378
class AggregationProcessor:
379
"""
380
Processor for aggregation function types and return values.
381
382
Provides accurate typing for aggregate operations.
383
"""
384
def process_aggregation(self, func_name: str, field_type: Type) -> Type: ...
385
def get_aggregate_return_type(self, func_name: str, field_type: Type) -> Type: ...
386
387
# Aggregation function return types
388
AGGREGATION_TYPES: dict = {
389
'Count': 'int',
390
'Sum': lambda field_type: field_type,
391
'Avg': lambda field_type: 'Optional[Decimal]' if field_type == 'Decimal' else 'Optional[float]',
392
'Min': lambda field_type: f'Optional[{field_type}]',
393
'Max': lambda field_type: f'Optional[{field_type}]',
394
'StdDev': 'Optional[float]',
395
'Variance': 'Optional[float]',
396
}
397
```
398
399
### Error Codes
400
401
Custom error codes for Django-specific type checking issues.
402
403
```python { .api }
404
class DjangoErrorCodes:
405
"""
406
Error codes for Django-specific MyPy errors.
407
408
Provides descriptive error messages for Django patterns.
409
"""
410
# Model-related errors
411
INVALID_FIELD_ACCESS: str = 'django-field-access'
412
MISSING_MODEL_ATTRIBUTE: str = 'django-missing-model-attr'
413
INVALID_MANAGER_METHOD: str = 'django-manager-method'
414
415
# QuerySet-related errors
416
INVALID_QUERYSET_METHOD: str = 'django-queryset-method'
417
INVALID_LOOKUP: str = 'django-invalid-lookup'
418
INCOMPATIBLE_LOOKUP_TYPE: str = 'django-lookup-type'
419
420
# Form-related errors
421
INVALID_FORM_FIELD: str = 'django-form-field'
422
MISSING_FORM_ATTRIBUTE: str = 'django-missing-form-attr'
423
424
# Settings-related errors
425
MISSING_SETTING: str = 'django-missing-setting'
426
INVALID_SETTING_TYPE: str = 'django-setting-type'
427
428
# Request-related errors
429
INVALID_REQUEST_ATTRIBUTE: str = 'django-request-attr'
430
MISSING_USER_ATTRIBUTE: str = 'django-user-attr'
431
432
def register_error_code(code: str, description: str, category: str = 'django') -> None:
433
"""
434
Register custom Django error code with MyPy.
435
436
Args:
437
code: Error code identifier
438
description: Human-readable error description
439
category: Error category for grouping
440
"""
441
442
def emit_django_error(ctx, code: str, message: str, node) -> None:
443
"""
444
Emit Django-specific error through MyPy.
445
446
Args:
447
ctx: MyPy context object
448
code: Django error code
449
message: Error message
450
node: AST node where error occurred
451
"""
452
```
453
454
### Plugin Hooks
455
456
Integration points for extending MyPy's Django type checking.
457
458
```python { .api }
459
class DjangoMethodSignatureHook:
460
"""
461
Hook for customizing method signature inference.
462
463
Allows custom typing for Django method patterns.
464
"""
465
def __call__(self, ctx) -> CallableType: ...
466
467
class DjangoAttributeHook:
468
"""
469
Hook for customizing attribute type inference.
470
471
Provides custom typing for Django attribute access.
472
"""
473
def __call__(self, ctx) -> Type: ...
474
475
class DjangoClassDecoratorHook:
476
"""
477
Hook for processing Django class decorators.
478
479
Handles typing for Django decorator patterns.
480
"""
481
def __call__(self, ctx) -> None: ...
482
483
class DjangoMetaclassHook:
484
"""
485
Hook for processing Django metaclasses.
486
487
Handles Model metaclass and other Django metaclass patterns.
488
"""
489
def __call__(self, ctx) -> None: ...
490
491
def register_django_hook(hook_name: str, hook_function) -> None:
492
"""
493
Register custom Django plugin hook.
494
495
Args:
496
hook_name: Name of the hook to register
497
hook_function: Hook implementation function
498
"""
499
500
def get_django_hook(hook_name: str):
501
"""
502
Get registered Django plugin hook by name.
503
504
Args:
505
hook_name: Name of hook to retrieve
506
507
Returns:
508
Hook function or None if not registered
509
"""
510
```
511
512
### Plugin Utilities
513
514
Utility functions for Django plugin development and debugging.
515
516
```python { .api }
517
def is_django_model(cls: Type) -> bool:
518
"""
519
Check if class is a Django model.
520
521
Args:
522
cls: Class type to check
523
524
Returns:
525
True if class inherits from django.db.models.Model
526
"""
527
528
def is_django_form(cls: Type) -> bool:
529
"""
530
Check if class is a Django form.
531
532
Args:
533
cls: Class type to check
534
535
Returns:
536
True if class inherits from django.forms.Form
537
"""
538
539
def is_django_manager(cls: Type) -> bool:
540
"""
541
Check if class is a Django manager.
542
543
Args:
544
cls: Class type to check
545
546
Returns:
547
True if class inherits from django.db.models.Manager
548
"""
549
550
def get_model_from_manager(manager_type: Type) -> Type:
551
"""
552
Extract model type from manager class.
553
554
Args:
555
manager_type: Manager class type
556
557
Returns:
558
Associated model type
559
"""
560
561
def resolve_lazy_reference(reference: str, module_name: str) -> Type:
562
"""
563
Resolve lazy string reference to actual type.
564
565
Args:
566
reference: String reference to resolve
567
module_name: Module context for resolution
568
569
Returns:
570
Resolved type object
571
"""
572
573
def debug_django_plugin(message: str, level: str = 'info') -> None:
574
"""
575
Debug logging for Django plugin development.
576
577
Args:
578
message: Debug message to log
579
level: Log level (debug, info, warning, error)
580
"""
581
```