various fixes

This commit is contained in:
Maxim Kurnikov
2019-07-20 20:28:43 +03:00
parent fee4aad3d0
commit 248504c25a
22 changed files with 204 additions and 299 deletions

View File

@@ -204,6 +204,12 @@ class NewSemanalDjangoPlugin(Plugin):
return partial(settings.get_type_of_settings_attribute,
django_context=self.django_context)
# def get_type_analyze_hook(self, fullname: str
# ( ):
# info = self._get_typeinfo_or_none(fullname)
# if info and info.has_base(fullnames.QUERYSET_CLASS_FULLNAME):
# return partial(querysets.set_first_generic_param_as_default_for_second, fullname=fullname)
def plugin(version):
return NewSemanalDjangoPlugin

View File

@@ -1,6 +1,6 @@
from typing import Optional, Tuple, cast
from mypy.nodes import MypyFile, TypeInfo
from mypy.nodes import MypyFile, TypeInfo, Var, AssignmentStmt, SymbolTableNode, MDEF
from mypy.plugin import FunctionContext
from mypy.types import AnyType, CallableType, Instance, Type as MypyType, TypeOfAny
@@ -15,9 +15,9 @@ def get_referred_to_model_fullname(ctx: FunctionContext, django_context: DjangoC
return to_arg_type.ret_type.type.fullname()
outer_model_info = ctx.api.scope.active_class()
if not outer_model_info or not outer_model_info.has_base(fullnames.MODEL_CLASS_FULLNAME):
# not inside models.Model class
return None
# if not outer_model_info or not outer_model_info.has_base(fullnames.MODEL_CLASS_FULLNAME):
# # not inside models.Model class
# return None
assert isinstance(outer_model_info, TypeInfo)
to_arg_expr = helpers.get_call_argument_by_name(ctx, 'to')
@@ -101,6 +101,12 @@ def transform_into_proper_return_type(ctx: FunctionContext, django_context: Djan
default_return_type = ctx.default_return_type
assert isinstance(default_return_type, Instance)
outer_model_info = ctx.api.scope.active_class()
if not outer_model_info or not outer_model_info.has_base(fullnames.MODEL_CLASS_FULLNAME):
# not inside models.Model class
return ctx.default_return_type
assert isinstance(outer_model_info, TypeInfo)
if helpers.has_any_of_bases(default_return_type.type, fullnames.RELATED_FIELDS_CLASSES):
return fill_descriptor_types_for_related_field(ctx, django_context)

View File

@@ -1,6 +1,5 @@
from abc import ABCMeta, abstractmethod
from abc import ABCMeta, abstractmethod
from typing import cast, Type
from typing import Type, cast
from django.db.models.base import Model
from django.db.models.fields.related import ForeignKey
@@ -18,7 +17,7 @@ from mypy_django_plugin.transformers import fields
from mypy_django_plugin.transformers.fields import get_field_descriptor_types
class ModelClassInitializer(metaclass=ABCMeta):
class ModelClassInitializer:
def __init__(self, ctx: ClassDefContext, django_context: DjangoContext):
self.api = cast(NewSemanticAnalyzer, ctx.api)
self.model_classdef = ctx.cls
@@ -52,7 +51,6 @@ class ModelClassInitializer(metaclass=ABCMeta):
return
self.run_with_model_cls(model_cls)
@abstractmethod
def run_with_model_cls(self, model_cls):
pass
@@ -70,7 +68,7 @@ class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
to get around incompatible Meta inner classes for different models.
"""
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run(self) -> None:
meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None:
return None
@@ -78,7 +76,7 @@ class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
class AddDefaultPrimaryKey(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
auto_field = model_cls._meta.auto_field
if auto_field and not self.model_classdef.info.has_readable_member(auto_field.attname):
# autogenerated field
@@ -91,7 +89,7 @@ class AddDefaultPrimaryKey(ModelClassInitializer):
class AddRelatedModelsId(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for field in model_cls._meta.get_fields():
if isinstance(field, ForeignKey):
rel_primary_key_field = self.django_context.get_primary_key_field(field.related_model)
@@ -103,7 +101,7 @@ class AddRelatedModelsId(ModelClassInitializer):
class AddManagers(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
for manager_name, manager in model_cls._meta.managers_map.items():
if manager_name not in self.model_classdef.info.names:
manager_fullname = helpers.get_class_fullname(manager.__class__)
@@ -140,7 +138,7 @@ class AddManagers(ModelClassInitializer):
class AddExtraFieldMethods(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
# get_FOO_display for choices
for field in self.django_context.get_model_fields(model_cls):
if field.choices:
@@ -172,7 +170,7 @@ class AddExtraFieldMethods(ModelClassInitializer):
class AddMetaOptionsAttribute(ModelClassInitializer):
def run_with_model_cls(self, model_cls: Type[Model])-> None:
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
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',

View File

@@ -1,9 +1,10 @@
from collections import OrderedDict
from typing import Optional, Tuple, Type, Sequence, List, Union
from typing import Optional, Tuple, Type, Sequence, List, Union, cast
from django.core.exceptions import FieldError
from django.db.models.base import Model
from django.db.models.fields.related import ForeignKey
from mypy.newsemanal.typeanal import TypeAnalyser
from mypy.nodes import NameExpr, Expression
from mypy.plugin import AnalyzeTypeContext, FunctionContext, MethodContext
from mypy.types import AnyType, Instance, Type as MypyType, TypeOfAny
@@ -13,24 +14,20 @@ from mypy_django_plugin.lib import fullnames, helpers
def set_first_generic_param_as_default_for_second(ctx: AnalyzeTypeContext, fullname: str) -> MypyType:
info = helpers.lookup_fully_qualified_typeinfo(ctx.api.api, fullname)
if info is None:
if not ctx.api.api.final_iteration:
ctx.api.api.defer()
if not ctx.type.args:
try:
return ctx.api.named_type(fullname, [AnyType(TypeOfAny.explicit),
AnyType(TypeOfAny.explicit)])
except KeyError:
# really should never happen
return AnyType(TypeOfAny.explicit)
return Instance(info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
args = ctx.type.args
if len(args) == 1:
args = [args[0], args[0]]
analyzed_args = [ctx.api.analyze_type(arg) for arg in args]
ctx.api.analyze_type(ctx.type)
try:
return ctx.api.named_type(fullname, analyzed_args)
except KeyError:
return AnyType(TypeOfAny.explicit)
return Instance(info, analyzed_args)
def determine_proper_manager_type(ctx: FunctionContext) -> MypyType:
@@ -69,7 +66,7 @@ def get_values_list_row_type(ctx: MethodContext, django_context: DjangoContext,
if flat:
primary_key_field = django_context.get_primary_key_field(model_cls)
_, column_type = get_lookup_field_get_type(ctx, django_context, model_cls,
primary_key_field.attname, 'values_list')
primary_key_field.attname, 'values_list')
return column_type
elif named:
column_types = OrderedDict()