mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-07 12:44:29 +08:00
fix star import parsing for settings
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user