fix star import parsing for settings

This commit is contained in:
Maxim Kurnikov
2019-02-14 03:16:07 +03:00
parent f30cd092f1
commit a08ad80a0d
6 changed files with 106 additions and 43 deletions

View File

@@ -127,16 +127,17 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo) -> Dict[str, T
if name in {'_meta', 'pk'}:
continue
if isinstance(sym.node, Var):
if isinstance(sym.node.type, Instance):
if sym.node.type is None or isinstance(sym.node.type, AnyType):
# types are not ready, fallback to Any
expected_types[name] = AnyType(TypeOfAny.from_unimported_type)
expected_types[name + '_id'] = AnyType(TypeOfAny.from_unimported_type)
elif isinstance(sym.node.type, Instance):
tp = sym.node.type
field_type = extract_field_setter_type(tp)
if field_type is None:
continue
choices_type_fullname = extract_choices_type(model, name)
if choices_type_fullname:
field_type = UnionType([field_type, ctx.api.named_generic_type(choices_type_fullname, [])])
if tp.type.fullname() in {helpers.FOREIGN_KEY_FULLNAME, helpers.ONETOONE_FIELD_FULLNAME}:
ref_to_model = tp.args[0]
primary_key_type = AnyType(TypeOfAny.special_form)
@@ -145,9 +146,7 @@ def extract_expected_types(ctx: FunctionContext, model: TypeInfo) -> Dict[str, T
if typ:
primary_key_type = typ
expected_types[name + '_id'] = primary_key_type
if field_type:
expected_types[name] = field_type
elif isinstance(sym.node.type, AnyType):
expected_types[name] = sym.node.type
return expected_types

View File

@@ -1,9 +1,10 @@
from typing import List, Optional, cast
from typing import Iterable, List, Optional, cast
from mypy.nodes import ClassDef, Context, MypyFile, SymbolNode, SymbolTableNode, Var
from mypy.nodes import ClassDef, Context, ImportAll, MypyFile, SymbolNode, SymbolTableNode, TypeInfo, Var
from mypy.plugin import ClassDefContext
from mypy.semanal import SemanticAnalyzerPass2
from mypy.types import Instance, NoneTyp, Type, UnionType
from mypy.types import AnyType, Instance, NoneTyp, Type, TypeOfAny, UnionType
from mypy.util import correct_relative_import
def get_error_context(node: SymbolNode) -> Context:
@@ -36,14 +37,44 @@ def make_sym_copy_of_setting(sym: SymbolTableNode) -> Optional[SymbolTableNode]:
return None
def load_settings_from_module(settings_classdef: ClassDef, module: MypyFile) -> None:
for name, sym in module.names.items():
if name.isupper() and isinstance(sym.node, Var):
if sym.type is not None:
copied = make_sym_copy_of_setting(sym)
if copied is None:
continue
settings_classdef.info.names[name] = copied
def get_settings_metadata(lazy_settings_info: TypeInfo):
return lazy_settings_info.metadata.setdefault('django', {}).setdefault('settings', {})
def load_settings_from_names(settings_classdef: ClassDef,
modules: Iterable[MypyFile],
api: SemanticAnalyzerPass2) -> None:
settings_metadata = get_settings_metadata(settings_classdef.info)
for module in modules:
for name, sym in module.names.items():
if name.isupper() and isinstance(sym.node, Var):
if sym.type is not None:
copied = make_sym_copy_of_setting(sym)
if copied is None:
continue
settings_classdef.info.names[name] = copied
else:
var = Var(name, AnyType(TypeOfAny.unannotated))
var.info = api.named_type('__builtins__.object').type
settings_classdef.info.names[name] = SymbolTableNode(sym.kind, var)
settings_metadata[name] = module.fullname()
def get_import_star_modules(api: SemanticAnalyzerPass2, module: MypyFile) -> List[str]:
import_star_modules = []
for module_import in module.imports:
# relative import * are not resolved by mypy
if isinstance(module_import, ImportAll) and module_import.relative:
absolute_import_path, correct = correct_relative_import(module.fullname(), module_import.relative, module_import.id,
is_cur_package_init_file=False)
if not correct:
return []
for path in [absolute_import_path] + get_import_star_modules(api, module=api.modules.get(absolute_import_path)):
if path not in import_star_modules:
import_star_modules.append(path)
return import_star_modules
class AddSettingValuesToDjangoConfObject:
@@ -55,7 +86,9 @@ class AddSettingValuesToDjangoConfObject:
api = cast(SemanticAnalyzerPass2, ctx.api)
for module_name in self.settings_modules:
module = api.modules[module_name]
load_settings_from_module(ctx.cls, module=module)
star_deps = [api.modules[star_dep]
for star_dep in reversed(get_import_star_modules(api, module))]
load_settings_from_names(ctx.cls, modules=star_deps + [module], api=api)
if self.ignore_missing_settings:
ctx.cls.info.fallback_to_any = True