mirror of
https://github.com/davidhalter/django-stubs.git
synced 2026-02-05 09:27:39 +08:00
nested class Meta support
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from .dependencies import (load_graph_to_add_settings_file_as_a_source_seed,
|
||||
inject_dependencies,
|
||||
restore_original_load_graph,
|
||||
restore_original_dependencies_handling)
|
||||
from .contexts import replace_apply_function_plugin_method
|
||||
restore_original_dependencies_handling,
|
||||
process_settings_before_dependants)
|
||||
from .contexts import replace_apply_function_plugin_method
|
||||
from .multiple_inheritance import make_inner_classes_with_inherit_from_any_compatible_with_each_other
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, AbstractSet, MutableSet, Set
|
||||
|
||||
from mypy.build import BuildManager, Graph, State
|
||||
from mypy.build import BuildManager, Graph, State, PRI_ALL
|
||||
from mypy.modulefinder import BuildSource
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from mypy import build
|
||||
|
||||
old_load_graph = build.load_graph
|
||||
OldState = build.State
|
||||
old_sorted_components = build.sorted_components
|
||||
|
||||
|
||||
def load_graph_to_add_settings_file_as_a_source_seed(settings_module: str):
|
||||
@@ -50,3 +51,40 @@ def restore_original_dependencies_handling():
|
||||
from mypy import build
|
||||
|
||||
build.State = OldState
|
||||
|
||||
|
||||
def _extract_dependencies(graph: Graph, state_id: str, visited_modules: Set[str]) -> Set[str]:
|
||||
visited_modules.add(state_id)
|
||||
dependencies = set(graph[state_id].dependencies)
|
||||
for new_dep_id in dependencies.copy():
|
||||
if new_dep_id not in visited_modules:
|
||||
dependencies.update(_extract_dependencies(graph, new_dep_id, visited_modules))
|
||||
return dependencies
|
||||
|
||||
|
||||
def extract_module_dependencies(graph: Graph, state_id: str) -> Set[str]:
|
||||
visited_modules = set()
|
||||
return _extract_dependencies(graph, state_id, visited_modules=visited_modules)
|
||||
|
||||
|
||||
def process_settings_before_dependants(settings_module: str):
|
||||
def patched_sorted_components(graph: Graph,
|
||||
vertices: Optional[AbstractSet[str]] = None,
|
||||
pri_max: int = PRI_ALL) -> List[AbstractSet[str]]:
|
||||
sccs = old_sorted_components(graph,
|
||||
vertices=vertices,
|
||||
pri_max=pri_max)
|
||||
for i, scc in enumerate(sccs.copy()):
|
||||
if 'django.conf' in scc:
|
||||
django_conf_deps = set(extract_module_dependencies(graph, 'django.conf')).union({'django.conf'})
|
||||
old_scc_modified = scc.difference(django_conf_deps)
|
||||
new_scc = scc.difference(old_scc_modified)
|
||||
if not old_scc_modified:
|
||||
# already processed
|
||||
break
|
||||
sccs[i] = frozenset(old_scc_modified)
|
||||
sccs.insert(i, frozenset(new_scc))
|
||||
break
|
||||
return sccs
|
||||
|
||||
build.sorted_components = patched_sorted_components
|
||||
|
||||
74
mypy_django_plugin/monkeypatch/multiple_inheritance.py
Normal file
74
mypy_django_plugin/monkeypatch/multiple_inheritance.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from typing import Optional
|
||||
|
||||
from mypy.checkmember import bind_self, is_final_node, type_object_type
|
||||
from mypy.nodes import TypeInfo, Context, SymbolTableNode, FuncBase
|
||||
from mypy.subtypes import is_subtype, is_equivalent
|
||||
from mypy.types import FunctionLike, CallableType, Type
|
||||
|
||||
|
||||
def make_inner_classes_with_inherit_from_any_compatible_with_each_other():
|
||||
from mypy.checker import TypeChecker
|
||||
|
||||
def determine_type_of_class_member(self, sym: SymbolTableNode) -> Optional[Type]:
|
||||
if sym.type is not None:
|
||||
return sym.type
|
||||
if isinstance(sym.node, FuncBase):
|
||||
return self.function_type(sym.node)
|
||||
if isinstance(sym.node, TypeInfo):
|
||||
# nested class
|
||||
return type_object_type(sym.node, self.named_type)
|
||||
return None
|
||||
|
||||
TypeChecker.determine_type_of_class_member = determine_type_of_class_member
|
||||
|
||||
def check_compatibility(self, name: str, base1: TypeInfo,
|
||||
base2: TypeInfo, ctx: Context) -> None:
|
||||
"""Check if attribute name in base1 is compatible with base2 in multiple inheritance.
|
||||
Assume base1 comes before base2 in the MRO, and that base1 and base2 don't have
|
||||
a direct subclass relationship (i.e., the compatibility requirement only derives from
|
||||
multiple inheritance).
|
||||
"""
|
||||
if name in ('__init__', '__new__', '__init_subclass__'):
|
||||
# __init__ and friends can be incompatible -- it's a special case.
|
||||
return
|
||||
first = base1[name]
|
||||
second = base2[name]
|
||||
first_type = self.determine_type_of_class_member(first)
|
||||
second_type = self.determine_type_of_class_member(second)
|
||||
|
||||
# TODO: What if some classes are generic?
|
||||
if (isinstance(first_type, FunctionLike) and
|
||||
isinstance(second_type, FunctionLike)):
|
||||
if ((isinstance(first_type, CallableType)
|
||||
and first_type.fallback.type.fullname() == 'builtins.type')
|
||||
and (isinstance(second_type, CallableType)
|
||||
and second_type.fallback.type.fullname() == 'builtins.type')):
|
||||
# Both members are classes (not necessary nested), check if compatible
|
||||
ok = is_subtype(first_type.ret_type, second_type.ret_type)
|
||||
else:
|
||||
# Method override
|
||||
first_sig = bind_self(first_type)
|
||||
second_sig = bind_self(second_type)
|
||||
ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True)
|
||||
elif first_type and second_type:
|
||||
ok = is_equivalent(first_type, second_type)
|
||||
else:
|
||||
if first_type is None:
|
||||
self.msg.cannot_determine_type_in_base(name, base1.name(), ctx)
|
||||
if second_type is None:
|
||||
self.msg.cannot_determine_type_in_base(name, base2.name(), ctx)
|
||||
ok = True
|
||||
# Final attributes can never be overridden, but can override
|
||||
# non-final read-only attributes.
|
||||
if is_final_node(second.node):
|
||||
self.msg.cant_override_final(name, base2.name(), ctx)
|
||||
if is_final_node(first.node):
|
||||
self.check_no_writable(name, second.node, ctx)
|
||||
# __slots__ is special and the type can vary across class hierarchy.
|
||||
if name == '__slots__':
|
||||
ok = True
|
||||
if not ok:
|
||||
self.msg.base_class_definitions_incompatible(name, base1, base2,
|
||||
ctx)
|
||||
|
||||
TypeChecker.check_compatibility = check_compatibility
|
||||
Reference in New Issue
Block a user