Files
django-stubs/mypy_django_plugin/monkeypatch.py
Maxim Kurnikov f59cfe6371 latest changes
2018-11-26 23:58:34 +03:00

113 lines
5.1 KiB
Python

from typing import Optional, List, Sequence
from mypy.build import BuildManager, Graph, State
from mypy.modulefinder import BuildSource
from mypy.nodes import Expression, Context
from mypy.plugin import FunctionContext, MethodContext
from mypy.types import Type, CallableType, Instance
def state_compute_dependencies_to_parse_installed_apps_setting_in_settings_module(settings_module: str,
models_py_modules: List[str]):
from mypy.build import State
old_compute_dependencies = State.compute_dependencies
def patched_compute_dependencies(self: State):
old_compute_dependencies(self)
if self.id == settings_module:
self.dependencies.extend(models_py_modules)
State.compute_dependencies = patched_compute_dependencies
def load_graph_to_add_settings_file_as_a_source_seed(settings_module: str):
from mypy import build
old_load_graph = build.load_graph
def patched_load_graph(sources: List[BuildSource], manager: BuildManager,
old_graph: Optional[Graph] = None,
new_modules: Optional[List[State]] = None):
if all([source.module != settings_module for source in sources]):
sources.append(BuildSource(None, settings_module, None))
return old_load_graph(sources=sources, manager=manager,
old_graph=old_graph,
new_modules=new_modules)
build.load_graph = patched_load_graph
def replace_apply_function_plugin_method():
def apply_function_plugin(self,
arg_types: List[Type],
inferred_ret_type: Type,
arg_names: Optional[Sequence[Optional[str]]],
formal_to_actual: List[List[int]],
args: List[Expression],
num_formals: int,
fullname: str,
object_type: Optional[Type],
context: Context) -> Type:
"""Use special case logic to infer the return type of a specific named function/method.
Caller must ensure that a plugin hook exists. There are two different cases:
- If object_type is None, the caller must ensure that a function hook exists
for fullname.
- If object_type is not None, the caller must ensure that a method hook exists
for fullname.
Return the inferred return type.
"""
formal_arg_types = [[] for _ in range(num_formals)] # type: List[List[Type]]
formal_arg_exprs = [[] for _ in range(num_formals)] # type: List[List[Expression]]
formal_arg_names = [None for _ in range(num_formals)] # type: List[Optional[str]]
for formal, actuals in enumerate(formal_to_actual):
for actual in actuals:
formal_arg_types[formal].append(arg_types[actual])
formal_arg_exprs[formal].append(args[actual])
if arg_names:
formal_arg_names[formal] = arg_names[actual]
num_passed_positionals = sum([1 if name is None else 0
for name in formal_arg_names])
if arg_names and num_passed_positionals > 0:
object_type_info = None
if object_type is not None:
if isinstance(object_type, CallableType):
# class object, convert to corresponding Instance
object_type = object_type.ret_type
if isinstance(object_type, Instance):
# skip TypedDictType and others
object_type_info = object_type.type
defn_arg_names = self._get_defn_arg_names(fullname, object_type=object_type_info)
if defn_arg_names:
if num_formals < len(defn_arg_names):
# self/cls argument has been passed implicitly
defn_arg_names = defn_arg_names[1:]
formal_arg_names[:num_passed_positionals] = defn_arg_names[:num_passed_positionals]
if object_type is None:
# Apply function plugin
callback = self.plugin.get_function_hook(fullname)
assert callback is not None # Assume that caller ensures this
return callback(
FunctionContext(formal_arg_names, formal_arg_types,
inferred_ret_type, formal_arg_exprs,
context, self.chk))
else:
# Apply method plugin
method_callback = self.plugin.get_method_hook(fullname)
assert method_callback is not None # Assume that caller ensures this
return method_callback(
MethodContext(object_type, formal_arg_names, formal_arg_types,
inferred_ret_type, formal_arg_exprs,
context, self.chk))
from mypy.checkexpr import ExpressionChecker
ExpressionChecker.apply_function_plugin = apply_function_plugin