mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-06 20:24:31 +08:00
add support for _meta.get_field() typechecking
This commit is contained in:
38
mypy_django_plugin/transformers/meta.py
Normal file
38
mypy_django_plugin/transformers/meta.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from mypy.plugin import MethodContext
|
||||
from mypy.types import AnyType, Type as MypyType, TypeOfAny, Instance
|
||||
|
||||
from mypy_django_plugin.django.context import DjangoContext
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
|
||||
|
||||
def _get_field_instance(ctx: MethodContext, field_fullname: str) -> MypyType:
|
||||
field_info = helpers.lookup_fully_qualified_typeinfo(ctx.api, field_fullname)
|
||||
return Instance(field_info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
|
||||
|
||||
|
||||
def return_proper_field_type_from_get_field(ctx: MethodContext, django_context: DjangoContext) -> MypyType:
|
||||
model_type = ctx.type.args[0]
|
||||
if not isinstance(model_type, Instance):
|
||||
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
|
||||
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
|
||||
if model_cls is None:
|
||||
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
|
||||
|
||||
field_name_expr = helpers.get_call_argument_by_name(ctx, 'field_name')
|
||||
if field_name_expr is None:
|
||||
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
|
||||
|
||||
field_name = helpers.resolve_string_attribute_value(field_name_expr, ctx, django_context)
|
||||
if field_name is None:
|
||||
return _get_field_instance(ctx, fullnames.FIELD_FULLNAME)
|
||||
|
||||
try:
|
||||
field = model_cls._meta.get_field(field_name)
|
||||
except FieldDoesNotExist as exc:
|
||||
ctx.api.fail(exc.args[0], ctx.context)
|
||||
return AnyType(TypeOfAny.from_error)
|
||||
|
||||
field_fullname = helpers.get_class_fullname(field.__class__)
|
||||
return _get_field_instance(ctx, field_fullname)
|
||||
@@ -2,15 +2,15 @@ from abc import ABCMeta, abstractmethod
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import cast
|
||||
|
||||
from django.db.models.fields import DateTimeField, DateField
|
||||
from django.db.models.fields.related import ForeignKey
|
||||
from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel, OneToOneRel
|
||||
from mypy.newsemanal.semanal import NewSemanticAnalyzer
|
||||
from mypy.nodes import MDEF, SymbolTableNode, TypeInfo, Var, Argument, ARG_NAMED_OPT, ARG_STAR2
|
||||
from mypy.nodes import ARG_STAR2, Argument, MDEF, SymbolTableNode, TypeInfo, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.plugins import common
|
||||
from mypy.types import Instance, TypeOfAny, AnyType
|
||||
from mypy.types import AnyType, Instance, TypeOfAny
|
||||
|
||||
from django.db.models.fields import DateField, DateTimeField
|
||||
from mypy_django_plugin.django.context import DjangoContext
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
from mypy_django_plugin.transformers import fields
|
||||
@@ -177,6 +177,15 @@ class AddExtraFieldMethods(ModelClassInitializer):
|
||||
return_type=return_type)
|
||||
|
||||
|
||||
class AddMetaOptionsAttribute(ModelClassInitializer):
|
||||
def run(self):
|
||||
if '_meta' not in self.model_classdef.info.names:
|
||||
options_info = self.lookup_typeinfo_or_incomplete_defn_error(fullnames.OPTIONS_CLASS_FULLNAME)
|
||||
self.add_new_node_to_model_class('_meta',
|
||||
Instance(options_info, [
|
||||
Instance(self.model_classdef.info, [])
|
||||
]))
|
||||
|
||||
|
||||
def process_model_class(ctx: ClassDefContext,
|
||||
django_context: DjangoContext) -> None:
|
||||
@@ -186,6 +195,7 @@ def process_model_class(ctx: ClassDefContext,
|
||||
AddRelatedModelsId,
|
||||
AddManagers,
|
||||
AddExtraFieldMethods,
|
||||
AddMetaOptionsAttribute
|
||||
]
|
||||
for initializer_cls in initializers:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user