forked from VimPlug/jedi
Merge branch 'master' of https://github.com/davidhalter/jedi
This commit is contained in:
@@ -34,7 +34,6 @@ from jedi.inference import usages
|
|||||||
from jedi.inference.arguments import try_iter_content
|
from jedi.inference.arguments import try_iter_content
|
||||||
from jedi.inference.helpers import get_module_names, infer_call_of_leaf
|
from jedi.inference.helpers import get_module_names, infer_call_of_leaf
|
||||||
from jedi.inference.sys_path import transform_path_to_dotted
|
from jedi.inference.sys_path import transform_path_to_dotted
|
||||||
from jedi.inference.names import TreeNameDefinition, ParamName
|
|
||||||
from jedi.inference.syntax_tree import tree_name_to_values
|
from jedi.inference.syntax_tree import tree_name_to_values
|
||||||
from jedi.inference.value import ModuleValue
|
from jedi.inference.value import ModuleValue
|
||||||
from jedi.inference.base_value import ValueSet
|
from jedi.inference.base_value import ValueSet
|
||||||
@@ -302,8 +301,8 @@ class Script(object):
|
|||||||
# Without a name we really just want to jump to the result e.g.
|
# Without a name we really just want to jump to the result e.g.
|
||||||
# executed by `foo()`, if we the cursor is after `)`.
|
# executed by `foo()`, if we the cursor is after `)`.
|
||||||
return self.goto_definitions(only_stubs=only_stubs, prefer_stubs=prefer_stubs)
|
return self.goto_definitions(only_stubs=only_stubs, prefer_stubs=prefer_stubs)
|
||||||
context = self._get_module_context().create_context(tree_name)
|
name = self._get_module_context().create_name(tree_name)
|
||||||
names = list(self._inference_state.goto(context, tree_name))
|
names = list(name.goto())
|
||||||
|
|
||||||
if follow_imports:
|
if follow_imports:
|
||||||
names = filter_follow_imports(names)
|
names = filter_follow_imports(names)
|
||||||
@@ -503,23 +502,13 @@ def names(source=None, path=None, encoding='utf-8', all_scopes=False,
|
|||||||
is_def = _def._name.tree_name.is_definition()
|
is_def = _def._name.tree_name.is_definition()
|
||||||
return definitions and is_def or references and not is_def
|
return definitions and is_def or references and not is_def
|
||||||
|
|
||||||
def create_name(name):
|
|
||||||
if name.parent.type == 'param':
|
|
||||||
cls = ParamName
|
|
||||||
else:
|
|
||||||
cls = TreeNameDefinition
|
|
||||||
return cls(
|
|
||||||
module_context.create_context(name),
|
|
||||||
name
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set line/column to a random position, because they don't matter.
|
# Set line/column to a random position, because they don't matter.
|
||||||
script = Script(source, line=1, column=0, path=path, encoding=encoding, environment=environment)
|
script = Script(source, line=1, column=0, path=path, encoding=encoding, environment=environment)
|
||||||
module_context = script._get_module_context()
|
module_context = script._get_module_context()
|
||||||
defs = [
|
defs = [
|
||||||
classes.Definition(
|
classes.Definition(
|
||||||
script._inference_state,
|
script._inference_state,
|
||||||
create_name(name)
|
module_context.create_name(name)
|
||||||
) for name in get_module_names(script._module_node, all_scopes)
|
) for name in get_module_names(script._module_node, all_scopes)
|
||||||
]
|
]
|
||||||
return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))
|
return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))
|
||||||
|
|||||||
@@ -553,7 +553,7 @@ class Definition(BaseDefinition):
|
|||||||
typ = 'def'
|
typ = 'def'
|
||||||
return typ + ' ' + self._name.get_public_name()
|
return typ + ' ' + self._name.get_public_name()
|
||||||
|
|
||||||
definition = tree_name.get_definition() or tree_name
|
definition = tree_name.get_definition(include_setitem=True) or tree_name
|
||||||
# Remove the prefix, because that's not what we want for get_code
|
# Remove the prefix, because that's not what we want for get_code
|
||||||
# here.
|
# here.
|
||||||
txt = definition.get_code(include_prefix=False)
|
txt = definition.get_code(include_prefix=False)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ return the ``date`` class.
|
|||||||
To *visualize* this (simplified):
|
To *visualize* this (simplified):
|
||||||
|
|
||||||
- ``InferenceState.infer_expr_stmt`` doesn't do much, because there's no assignment.
|
- ``InferenceState.infer_expr_stmt`` doesn't do much, because there's no assignment.
|
||||||
- ``Value.infer_node`` cares for resolving the dotted path
|
- ``Context.infer_node`` cares for resolving the dotted path
|
||||||
- ``InferenceState.find_types`` searches for global definitions of datetime, which
|
- ``InferenceState.find_types`` searches for global definitions of datetime, which
|
||||||
it finds in the definition of an import, by scanning the syntax tree.
|
it finds in the definition of an import, by scanning the syntax tree.
|
||||||
- Using the import logic, the datetime module is found.
|
- Using the import logic, the datetime module is found.
|
||||||
@@ -62,25 +62,22 @@ I need to mention now that lazy type inference is really good because it
|
|||||||
only *inferes* what needs to be *inferred*. All the statements and modules
|
only *inferes* what needs to be *inferred*. All the statements and modules
|
||||||
that are not used are just being ignored.
|
that are not used are just being ignored.
|
||||||
"""
|
"""
|
||||||
from parso.python import tree
|
|
||||||
import parso
|
import parso
|
||||||
from parso import python_bytes_to_unicode
|
from parso import python_bytes_to_unicode
|
||||||
from jedi.file_io import FileIO
|
from jedi.file_io import FileIO
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import parser_utils
|
|
||||||
from jedi.inference.utils import unite
|
|
||||||
from jedi.inference import imports
|
from jedi.inference import imports
|
||||||
from jedi.inference import recursion
|
from jedi.inference import recursion
|
||||||
from jedi.inference.cache import inference_state_function_cache
|
from jedi.inference.cache import inference_state_function_cache
|
||||||
from jedi.inference import helpers
|
from jedi.inference import helpers
|
||||||
from jedi.inference.names import TreeNameDefinition, ParamName
|
from jedi.inference.names import TreeNameDefinition
|
||||||
from jedi.inference.base_value import ContextualizedName, ContextualizedNode, \
|
from jedi.inference.base_value import ContextualizedNode, \
|
||||||
ValueSet, NO_VALUES, iterate_values
|
ValueSet, iterate_values
|
||||||
from jedi.inference.value import ClassValue, FunctionValue
|
from jedi.inference.value import ClassValue, FunctionValue
|
||||||
from jedi.inference.context import CompForContext
|
from jedi.inference.syntax_tree import infer_expr_stmt, \
|
||||||
from jedi.inference.syntax_tree import infer_trailer, infer_expr_stmt, \
|
check_tuple_assignments
|
||||||
infer_node, check_tuple_assignments
|
from jedi.inference.imports import follow_error_node_imports_if_possible
|
||||||
from jedi.plugins import plugin_manager
|
from jedi.plugins import plugin_manager
|
||||||
|
|
||||||
|
|
||||||
@@ -149,93 +146,6 @@ class InferenceState(object):
|
|||||||
"""Convenience function"""
|
"""Convenience function"""
|
||||||
return self.project._get_sys_path(self, environment=self.environment, **kwargs)
|
return self.project._get_sys_path(self, environment=self.environment, **kwargs)
|
||||||
|
|
||||||
def infer_element(self, context, element):
|
|
||||||
if isinstance(context, CompForContext):
|
|
||||||
return infer_node(context, element)
|
|
||||||
|
|
||||||
if_stmt = element
|
|
||||||
while if_stmt is not None:
|
|
||||||
if_stmt = if_stmt.parent
|
|
||||||
if if_stmt.type in ('if_stmt', 'for_stmt'):
|
|
||||||
break
|
|
||||||
if parser_utils.is_scope(if_stmt):
|
|
||||||
if_stmt = None
|
|
||||||
break
|
|
||||||
predefined_if_name_dict = context.predefined_names.get(if_stmt)
|
|
||||||
# TODO there's a lot of issues with this one. We actually should do
|
|
||||||
# this in a different way. Caching should only be active in certain
|
|
||||||
# cases and this all sucks.
|
|
||||||
if predefined_if_name_dict is None and if_stmt \
|
|
||||||
and if_stmt.type == 'if_stmt' and self.is_analysis:
|
|
||||||
if_stmt_test = if_stmt.children[1]
|
|
||||||
name_dicts = [{}]
|
|
||||||
# If we already did a check, we don't want to do it again -> If
|
|
||||||
# value.predefined_names is filled, we stop.
|
|
||||||
# We don't want to check the if stmt itself, it's just about
|
|
||||||
# the content.
|
|
||||||
if element.start_pos > if_stmt_test.end_pos:
|
|
||||||
# Now we need to check if the names in the if_stmt match the
|
|
||||||
# names in the suite.
|
|
||||||
if_names = helpers.get_names_of_node(if_stmt_test)
|
|
||||||
element_names = helpers.get_names_of_node(element)
|
|
||||||
str_element_names = [e.value for e in element_names]
|
|
||||||
if any(i.value in str_element_names for i in if_names):
|
|
||||||
for if_name in if_names:
|
|
||||||
definitions = self.goto_definitions(context, if_name)
|
|
||||||
# Every name that has multiple different definitions
|
|
||||||
# causes the complexity to rise. The complexity should
|
|
||||||
# never fall below 1.
|
|
||||||
if len(definitions) > 1:
|
|
||||||
if len(name_dicts) * len(definitions) > 16:
|
|
||||||
debug.dbg('Too many options for if branch inference %s.', if_stmt)
|
|
||||||
# There's only a certain amount of branches
|
|
||||||
# Jedi can infer, otherwise it will take to
|
|
||||||
# long.
|
|
||||||
name_dicts = [{}]
|
|
||||||
break
|
|
||||||
|
|
||||||
original_name_dicts = list(name_dicts)
|
|
||||||
name_dicts = []
|
|
||||||
for definition in definitions:
|
|
||||||
new_name_dicts = list(original_name_dicts)
|
|
||||||
for i, name_dict in enumerate(new_name_dicts):
|
|
||||||
new_name_dicts[i] = name_dict.copy()
|
|
||||||
new_name_dicts[i][if_name.value] = ValueSet([definition])
|
|
||||||
|
|
||||||
name_dicts += new_name_dicts
|
|
||||||
else:
|
|
||||||
for name_dict in name_dicts:
|
|
||||||
name_dict[if_name.value] = definitions
|
|
||||||
if len(name_dicts) > 1:
|
|
||||||
result = NO_VALUES
|
|
||||||
for name_dict in name_dicts:
|
|
||||||
with context.predefine_names(if_stmt, name_dict):
|
|
||||||
result |= infer_node(context, element)
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return self._infer_element_if_inferred(context, element)
|
|
||||||
else:
|
|
||||||
if predefined_if_name_dict:
|
|
||||||
return infer_node(context, element)
|
|
||||||
else:
|
|
||||||
return self._infer_element_if_inferred(context, element)
|
|
||||||
|
|
||||||
def _infer_element_if_inferred(self, context, element):
|
|
||||||
"""
|
|
||||||
TODO This function is temporary: Merge with infer_element.
|
|
||||||
"""
|
|
||||||
parent = element
|
|
||||||
while parent is not None:
|
|
||||||
parent = parent.parent
|
|
||||||
predefined_if_name_dict = context.predefined_names.get(parent)
|
|
||||||
if predefined_if_name_dict is not None:
|
|
||||||
return infer_node(context, element)
|
|
||||||
return self._infer_element_cached(context, element)
|
|
||||||
|
|
||||||
@inference_state_function_cache(default=NO_VALUES)
|
|
||||||
def _infer_element_cached(self, context, element):
|
|
||||||
return infer_node(context, element)
|
|
||||||
|
|
||||||
def goto_definitions(self, context, name):
|
def goto_definitions(self, context, name):
|
||||||
def_ = name.get_definition(import_name_always=True)
|
def_ = name.get_definition(import_name_always=True)
|
||||||
if def_ is not None:
|
if def_ is not None:
|
||||||
@@ -256,113 +166,17 @@ class InferenceState(object):
|
|||||||
container_types = context.infer_node(def_.children[3])
|
container_types = context.infer_node(def_.children[3])
|
||||||
cn = ContextualizedNode(context, def_.children[3])
|
cn = ContextualizedNode(context, def_.children[3])
|
||||||
for_types = iterate_values(container_types, cn)
|
for_types = iterate_values(container_types, cn)
|
||||||
c_node = ContextualizedName(context, name)
|
n = TreeNameDefinition(context, name)
|
||||||
return check_tuple_assignments(c_node, for_types)
|
return check_tuple_assignments(n, for_types)
|
||||||
if type_ in ('import_from', 'import_name'):
|
if type_ in ('import_from', 'import_name'):
|
||||||
return imports.infer_import(context, name)
|
return imports.infer_import(context, name)
|
||||||
else:
|
else:
|
||||||
result = self._follow_error_node_imports_if_possible(context, name)
|
result = follow_error_node_imports_if_possible(context, name)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return helpers.infer_call_of_leaf(context, name)
|
return helpers.infer_call_of_leaf(context, name)
|
||||||
|
|
||||||
def _follow_error_node_imports_if_possible(self, context, name):
|
|
||||||
error_node = tree.search_ancestor(name, 'error_node')
|
|
||||||
if error_node is not None:
|
|
||||||
# Get the first command start of a started simple_stmt. The error
|
|
||||||
# node is sometimes a small_stmt and sometimes a simple_stmt. Check
|
|
||||||
# for ; leaves that start a new statements.
|
|
||||||
start_index = 0
|
|
||||||
for index, n in enumerate(error_node.children):
|
|
||||||
if n.start_pos > name.start_pos:
|
|
||||||
break
|
|
||||||
if n == ';':
|
|
||||||
start_index = index + 1
|
|
||||||
nodes = error_node.children[start_index:]
|
|
||||||
first_name = nodes[0].get_first_leaf().value
|
|
||||||
|
|
||||||
# Make it possible to infer stuff like `import foo.` or
|
|
||||||
# `from foo.bar`.
|
|
||||||
if first_name in ('from', 'import'):
|
|
||||||
is_import_from = first_name == 'from'
|
|
||||||
level, names = helpers.parse_dotted_names(
|
|
||||||
nodes,
|
|
||||||
is_import_from=is_import_from,
|
|
||||||
until_node=name,
|
|
||||||
)
|
|
||||||
return imports.Importer(self, names, context.get_root_context(), level).follow()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def goto(self, context, name):
|
|
||||||
definition = name.get_definition(import_name_always=True)
|
|
||||||
if definition is not None:
|
|
||||||
type_ = definition.type
|
|
||||||
if type_ == 'expr_stmt':
|
|
||||||
# Only take the parent, because if it's more complicated than just
|
|
||||||
# a name it's something you can "goto" again.
|
|
||||||
is_simple_name = name.parent.type not in ('power', 'trailer')
|
|
||||||
if is_simple_name:
|
|
||||||
return [TreeNameDefinition(context, name)]
|
|
||||||
elif type_ == 'param':
|
|
||||||
return [ParamName(context, name)]
|
|
||||||
elif type_ in ('import_from', 'import_name'):
|
|
||||||
module_names = imports.goto_import(context, name)
|
|
||||||
return module_names
|
|
||||||
else:
|
|
||||||
return [TreeNameDefinition(context, name)]
|
|
||||||
else:
|
|
||||||
values = self._follow_error_node_imports_if_possible(context, name)
|
|
||||||
if values is not None:
|
|
||||||
return [value.name for value in values]
|
|
||||||
|
|
||||||
par = name.parent
|
|
||||||
node_type = par.type
|
|
||||||
if node_type == 'argument' and par.children[1] == '=' and par.children[0] == name:
|
|
||||||
# Named param goto.
|
|
||||||
trailer = par.parent
|
|
||||||
if trailer.type == 'arglist':
|
|
||||||
trailer = trailer.parent
|
|
||||||
if trailer.type != 'classdef':
|
|
||||||
if trailer.type == 'decorator':
|
|
||||||
value_set = context.infer_node(trailer.children[1])
|
|
||||||
else:
|
|
||||||
i = trailer.parent.children.index(trailer)
|
|
||||||
to_infer = trailer.parent.children[:i]
|
|
||||||
if to_infer[0] == 'await':
|
|
||||||
to_infer.pop(0)
|
|
||||||
value_set = context.infer_node(to_infer[0])
|
|
||||||
for trailer in to_infer[1:]:
|
|
||||||
value_set = infer_trailer(context, value_set, trailer)
|
|
||||||
param_names = []
|
|
||||||
for value in value_set:
|
|
||||||
for signature in value.get_signatures():
|
|
||||||
for param_name in signature.get_param_names():
|
|
||||||
if param_name.string_name == name.value:
|
|
||||||
param_names.append(param_name)
|
|
||||||
return param_names
|
|
||||||
elif node_type == 'dotted_name': # Is a decorator.
|
|
||||||
index = par.children.index(name)
|
|
||||||
if index > 0:
|
|
||||||
new_dotted = helpers.deep_ast_copy(par)
|
|
||||||
new_dotted.children[index - 1:] = []
|
|
||||||
values = context.infer_node(new_dotted)
|
|
||||||
return unite(
|
|
||||||
value.goto(name, name_context=value.as_context())
|
|
||||||
for value in values
|
|
||||||
)
|
|
||||||
|
|
||||||
if node_type == 'trailer' and par.children[0] == '.':
|
|
||||||
values = helpers.infer_call_of_leaf(context, name, cut_own_trailer=True)
|
|
||||||
return values.goto(name, name_context=context)
|
|
||||||
else:
|
|
||||||
stmt = tree.search_ancestor(
|
|
||||||
name, 'expr_stmt', 'lambdef'
|
|
||||||
) or name
|
|
||||||
if stmt.type == 'lambdef':
|
|
||||||
stmt = name
|
|
||||||
return context.goto(name, position=stmt.start_pos)
|
|
||||||
|
|
||||||
def parse_and_get_code(self, code=None, path=None, encoding='utf-8',
|
def parse_and_get_code(self, code=None, path=None, encoding='utf-8',
|
||||||
use_latest_grammar=False, file_io=None, **kwargs):
|
use_latest_grammar=False, file_io=None, **kwargs):
|
||||||
if self.allow_different_encoding:
|
if self.allow_different_encoding:
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ from jedi.inference.utils import PushBackIterator
|
|||||||
from jedi.inference import analysis
|
from jedi.inference import analysis
|
||||||
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
|
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
|
||||||
LazyTreeValue, get_merged_lazy_value
|
LazyTreeValue, get_merged_lazy_value
|
||||||
from jedi.inference.names import ParamName, TreeNameDefinition
|
from jedi.inference.names import ParamName, TreeNameDefinition, AnonymousParamName
|
||||||
from jedi.inference.base_value import NO_VALUES, ValueSet, ContextualizedNode
|
from jedi.inference.base_value import NO_VALUES, ValueSet, ContextualizedNode
|
||||||
from jedi.inference.value import iterable
|
from jedi.inference.value import iterable
|
||||||
from jedi.inference.cache import inference_state_as_method_param_cache
|
from jedi.inference.cache import inference_state_as_method_param_cache
|
||||||
from jedi.inference.param import get_executed_param_names_and_issues
|
|
||||||
|
|
||||||
|
|
||||||
def try_iter_content(types, depth=0):
|
def try_iter_content(types, depth=0):
|
||||||
@@ -84,7 +83,7 @@ def _iterate_argument_clinic(inference_state, arguments, parameters):
|
|||||||
break
|
break
|
||||||
|
|
||||||
lazy_values.append(argument)
|
lazy_values.append(argument)
|
||||||
yield ValueSet([iterable.FakeSequence(inference_state, u'tuple', lazy_values)])
|
yield ValueSet([iterable.FakeTuple(inference_state, lazy_values)])
|
||||||
lazy_values
|
lazy_values
|
||||||
continue
|
continue
|
||||||
elif stars == 2:
|
elif stars == 2:
|
||||||
@@ -144,9 +143,6 @@ class _AbstractArgumentsMixin(object):
|
|||||||
def unpack(self, funcdef=None):
|
def unpack(self, funcdef=None):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_executed_param_names_and_issues(self, execution_context):
|
|
||||||
return get_executed_param_names_and_issues(execution_context, self)
|
|
||||||
|
|
||||||
def get_calling_nodes(self):
|
def get_calling_nodes(self):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -157,19 +153,6 @@ class AbstractArguments(_AbstractArgumentsMixin):
|
|||||||
trailer = None
|
trailer = None
|
||||||
|
|
||||||
|
|
||||||
class AnonymousArguments(AbstractArguments):
|
|
||||||
def get_executed_param_names_and_issues(self, execution_context):
|
|
||||||
from jedi.inference.dynamic import search_param_names
|
|
||||||
return search_param_names(
|
|
||||||
execution_context.inference_state,
|
|
||||||
execution_context,
|
|
||||||
execution_context.tree_node
|
|
||||||
), []
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '%s()' % self.__class__.__name__
|
|
||||||
|
|
||||||
|
|
||||||
def unpack_arglist(arglist):
|
def unpack_arglist(arglist):
|
||||||
if arglist is None:
|
if arglist is None:
|
||||||
return
|
return
|
||||||
@@ -275,7 +258,6 @@ class TreeArguments(AbstractArguments):
|
|||||||
return '<%s: %s>' % (self.__class__.__name__, self.argument_node)
|
return '<%s: %s>' % (self.__class__.__name__, self.argument_node)
|
||||||
|
|
||||||
def get_calling_nodes(self):
|
def get_calling_nodes(self):
|
||||||
from jedi.inference.dynamic import DynamicExecutedParamName
|
|
||||||
old_arguments_list = []
|
old_arguments_list = []
|
||||||
arguments = self
|
arguments = self
|
||||||
|
|
||||||
@@ -288,15 +270,14 @@ class TreeArguments(AbstractArguments):
|
|||||||
names = calling_name.goto()
|
names = calling_name.goto()
|
||||||
if len(names) != 1:
|
if len(names) != 1:
|
||||||
break
|
break
|
||||||
|
if isinstance(names[0], AnonymousParamName):
|
||||||
|
# Dynamic parameters should not have calling nodes, because
|
||||||
|
# they are dynamic and extremely random.
|
||||||
|
return []
|
||||||
if not isinstance(names[0], ParamName):
|
if not isinstance(names[0], ParamName):
|
||||||
break
|
break
|
||||||
param = names[0].get_executed_param_name()
|
executed_param_name = names[0].get_executed_param_name()
|
||||||
if isinstance(param, DynamicExecutedParamName):
|
arguments = executed_param_name.arguments
|
||||||
# For dynamic searches we don't even want to see errors.
|
|
||||||
return []
|
|
||||||
if param.var_args is None:
|
|
||||||
break
|
|
||||||
arguments = param.var_args
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if arguments.argument_node is not None:
|
if arguments.argument_node is not None:
|
||||||
|
|||||||
@@ -140,9 +140,12 @@ class HelperValueMixin(object):
|
|||||||
|
|
||||||
class Value(HelperValueMixin, BaseValue):
|
class Value(HelperValueMixin, BaseValue):
|
||||||
"""
|
"""
|
||||||
To be defined by subclasses.
|
To be implemented by subclasses.
|
||||||
"""
|
"""
|
||||||
tree_node = None
|
tree_node = None
|
||||||
|
# Possible values: None, tuple, list, dict and set. Here to deal with these
|
||||||
|
# very important containers.
|
||||||
|
array_type = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
def api_type(self):
|
||||||
@@ -161,6 +164,9 @@ class Value(HelperValueMixin, BaseValue):
|
|||||||
)
|
)
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
|
def py__simple_getitem__(self, index):
|
||||||
|
raise SimpleGetItemNotFound
|
||||||
|
|
||||||
def py__iter__(self, contextualized_node=None):
|
def py__iter__(self, contextualized_node=None):
|
||||||
if contextualized_node is not None:
|
if contextualized_node is not None:
|
||||||
from jedi.inference import analysis
|
from jedi.inference import analysis
|
||||||
@@ -323,67 +329,15 @@ class ContextualizedNode(object):
|
|||||||
return '<%s: %s in %s>' % (self.__class__.__name__, self.node, self.context)
|
return '<%s: %s in %s>' % (self.__class__.__name__, self.node, self.context)
|
||||||
|
|
||||||
|
|
||||||
class ContextualizedName(ContextualizedNode):
|
|
||||||
# TODO merge with TreeNameDefinition?!
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.node
|
|
||||||
|
|
||||||
def assignment_indexes(self):
|
|
||||||
"""
|
|
||||||
Returns an array of tuple(int, node) of the indexes that are used in
|
|
||||||
tuple assignments.
|
|
||||||
|
|
||||||
For example if the name is ``y`` in the following code::
|
|
||||||
|
|
||||||
x, (y, z) = 2, ''
|
|
||||||
|
|
||||||
would result in ``[(1, xyz_node), (0, yz_node)]``.
|
|
||||||
|
|
||||||
When searching for b in the case ``a, *b, c = [...]`` it will return::
|
|
||||||
|
|
||||||
[(slice(1, -1), abc_node)]
|
|
||||||
"""
|
|
||||||
indexes = []
|
|
||||||
is_star_expr = False
|
|
||||||
node = self.node.parent
|
|
||||||
compare = self.node
|
|
||||||
while node is not None:
|
|
||||||
if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'):
|
|
||||||
for i, child in enumerate(node.children):
|
|
||||||
if child == compare:
|
|
||||||
index = int(i / 2)
|
|
||||||
if is_star_expr:
|
|
||||||
from_end = int((len(node.children) - i) / 2)
|
|
||||||
index = slice(index, -from_end)
|
|
||||||
indexes.insert(0, (index, node))
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise LookupError("Couldn't find the assignment.")
|
|
||||||
is_star_expr = False
|
|
||||||
elif node.type == 'star_expr':
|
|
||||||
is_star_expr = True
|
|
||||||
elif isinstance(node, (ExprStmt, SyncCompFor)):
|
|
||||||
break
|
|
||||||
|
|
||||||
compare = node
|
|
||||||
node = node.parent
|
|
||||||
return indexes
|
|
||||||
|
|
||||||
|
|
||||||
def _getitem(value, index_values, contextualized_node):
|
def _getitem(value, index_values, contextualized_node):
|
||||||
# The actual getitem call.
|
# The actual getitem call.
|
||||||
simple_getitem = getattr(value, 'py__simple_getitem__', None)
|
|
||||||
|
|
||||||
result = NO_VALUES
|
result = NO_VALUES
|
||||||
unused_values = set()
|
unused_values = set()
|
||||||
for index_value in index_values:
|
for index_value in index_values:
|
||||||
if simple_getitem is not None:
|
|
||||||
|
|
||||||
index = index_value.get_safe_value(default=None)
|
index = index_value.get_safe_value(default=None)
|
||||||
if type(index) in (float, int, str, unicode, slice, bytes):
|
if type(index) in (float, int, str, unicode, slice, bytes):
|
||||||
try:
|
try:
|
||||||
result |= simple_getitem(index)
|
result |= value.py__simple_getitem__(index)
|
||||||
continue
|
continue
|
||||||
except SimpleGetItemNotFound:
|
except SimpleGetItemNotFound:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from parso.python.tree import Name
|
|||||||
|
|
||||||
from jedi.inference.filters import ParserTreeFilter, MergedFilter, \
|
from jedi.inference.filters import ParserTreeFilter, MergedFilter, \
|
||||||
GlobalNameFilter
|
GlobalNameFilter
|
||||||
|
from jedi.inference.names import AnonymousParamName, TreeNameDefinition
|
||||||
from jedi.inference.base_value import NO_VALUES, ValueSet
|
from jedi.inference.base_value import NO_VALUES, ValueSet
|
||||||
from jedi.parser_utils import get_parent_scope
|
from jedi.parser_utils import get_parent_scope
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
@@ -151,9 +152,6 @@ class AbstractContext(object):
|
|||||||
finally:
|
finally:
|
||||||
del predefined[flow_scope]
|
del predefined[flow_scope]
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '%s(%s)' % (self.__class__.__name__, self._value)
|
|
||||||
|
|
||||||
|
|
||||||
class ValueContext(AbstractContext):
|
class ValueContext(AbstractContext):
|
||||||
"""
|
"""
|
||||||
@@ -208,7 +206,8 @@ class ValueContext(AbstractContext):
|
|||||||
|
|
||||||
class TreeContextMixin(object):
|
class TreeContextMixin(object):
|
||||||
def infer_node(self, node):
|
def infer_node(self, node):
|
||||||
return self.inference_state.infer_element(self, node)
|
from jedi.inference.syntax_tree import infer_node
|
||||||
|
return infer_node(self, node)
|
||||||
|
|
||||||
def create_value(self, node):
|
def create_value(self, node):
|
||||||
from jedi.inference import value
|
from jedi.inference import value
|
||||||
@@ -274,6 +273,16 @@ class TreeContextMixin(object):
|
|||||||
scope_node = parent_scope(scope_node)
|
scope_node = parent_scope(scope_node)
|
||||||
return from_scope_node(scope_node, is_nested=True)
|
return from_scope_node(scope_node, is_nested=True)
|
||||||
|
|
||||||
|
def create_name(self, tree_name):
|
||||||
|
definition = tree_name.get_definition()
|
||||||
|
if definition and definition.type == 'param' and definition.name == tree_name:
|
||||||
|
funcdef = search_ancestor(definition, 'funcdef', 'lambdef')
|
||||||
|
func = self.create_value(funcdef)
|
||||||
|
return AnonymousParamName(func, tree_name)
|
||||||
|
else:
|
||||||
|
context = self.create_context(tree_name)
|
||||||
|
return TreeNameDefinition(context, tree_name)
|
||||||
|
|
||||||
|
|
||||||
class FunctionContext(TreeContextMixin, ValueContext):
|
class FunctionContext(TreeContextMixin, ValueContext):
|
||||||
def get_filters(self, until_position=None, origin_scope=None):
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
@@ -358,6 +367,12 @@ class CompForContext(TreeContextMixin, AbstractContext):
|
|||||||
def get_filters(self, until_position=None, origin_scope=None):
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
yield ParserTreeFilter(self)
|
yield ParserTreeFilter(self)
|
||||||
|
|
||||||
|
def py__name__(self):
|
||||||
|
return '<comprehension context>'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, self.tree_node)
|
||||||
|
|
||||||
|
|
||||||
class CompiledContext(ValueContext):
|
class CompiledContext(ValueContext):
|
||||||
def get_filters(self, until_position=None, origin_scope=None):
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
@@ -440,14 +455,14 @@ def get_global_filters(context, until_position, origin_scope):
|
|||||||
[...]
|
[...]
|
||||||
"""
|
"""
|
||||||
base_context = context
|
base_context = context
|
||||||
from jedi.inference.value.function import FunctionExecutionContext
|
from jedi.inference.value.function import BaseFunctionExecutionContext
|
||||||
while context is not None:
|
while context is not None:
|
||||||
# Names in methods cannot be resolved within the class.
|
# Names in methods cannot be resolved within the class.
|
||||||
for filter in context.get_filters(
|
for filter in context.get_filters(
|
||||||
until_position=until_position,
|
until_position=until_position,
|
||||||
origin_scope=origin_scope):
|
origin_scope=origin_scope):
|
||||||
yield filter
|
yield filter
|
||||||
if isinstance(context, FunctionExecutionContext):
|
if isinstance(context, BaseFunctionExecutionContext):
|
||||||
# The position should be reset if the current scope is a function.
|
# The position should be reset if the current scope is a function.
|
||||||
until_position = None
|
until_position = None
|
||||||
|
|
||||||
|
|||||||
@@ -253,8 +253,8 @@ def _execute_array_values(inference_state, array):
|
|||||||
Tuples indicate that there's not just one return value, but the listed
|
Tuples indicate that there's not just one return value, but the listed
|
||||||
ones. `(str, int)` means that it returns a tuple with both types.
|
ones. `(str, int)` means that it returns a tuple with both types.
|
||||||
"""
|
"""
|
||||||
from jedi.inference.value.iterable import SequenceLiteralValue, FakeSequence
|
from jedi.inference.value.iterable import SequenceLiteralValue, FakeTuple, FakeList
|
||||||
if isinstance(array, SequenceLiteralValue):
|
if isinstance(array, SequenceLiteralValue) and array.array_type in ('tuple', 'list'):
|
||||||
values = []
|
values = []
|
||||||
for lazy_value in array.py__iter__():
|
for lazy_value in array.py__iter__():
|
||||||
objects = ValueSet.from_sets(
|
objects = ValueSet.from_sets(
|
||||||
@@ -262,33 +262,29 @@ def _execute_array_values(inference_state, array):
|
|||||||
for typ in lazy_value.infer()
|
for typ in lazy_value.infer()
|
||||||
)
|
)
|
||||||
values.append(LazyKnownValues(objects))
|
values.append(LazyKnownValues(objects))
|
||||||
return {FakeSequence(inference_state, array.array_type, values)}
|
cls = FakeTuple if array.array_type == 'tuple' else FakeList
|
||||||
|
return {cls(inference_state, values)}
|
||||||
else:
|
else:
|
||||||
return array.execute_annotation()
|
return array.execute_annotation()
|
||||||
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
@inference_state_method_cache()
|
||||||
def infer_param(execution_context, param):
|
def infer_param(function_value, param):
|
||||||
from jedi.inference.value.instance import InstanceArguments
|
|
||||||
from jedi.inference.value import FunctionExecutionContext
|
|
||||||
|
|
||||||
def infer_docstring(docstring):
|
def infer_docstring(docstring):
|
||||||
return ValueSet(
|
return ValueSet(
|
||||||
p
|
p
|
||||||
for param_str in _search_param_in_docstr(docstring, param.name.value)
|
for param_str in _search_param_in_docstr(docstring, param.name.value)
|
||||||
for p in _infer_for_statement_string(module_context, param_str)
|
for p in _infer_for_statement_string(module_context, param_str)
|
||||||
)
|
)
|
||||||
module_context = execution_context.get_root_context()
|
module_context = function_value.get_root_context()
|
||||||
func = param.get_parent_function()
|
func = param.get_parent_function()
|
||||||
if func.type == 'lambdef':
|
if func.type == 'lambdef':
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
types = infer_docstring(execution_context.py__doc__())
|
types = infer_docstring(function_value.py__doc__())
|
||||||
if isinstance(execution_context, FunctionExecutionContext) \
|
if function_value.is_bound_method() \
|
||||||
and isinstance(execution_context.var_args, InstanceArguments) \
|
and function_value.py__name__() == '__init__':
|
||||||
and execution_context.function_value.py__name__() == '__init__':
|
types |= infer_docstring(function_value.class_context.py__doc__())
|
||||||
class_value = execution_context.var_args.instance.class_value
|
|
||||||
types |= infer_docstring(class_value.py__doc__())
|
|
||||||
|
|
||||||
debug.dbg('Found param types for docstring: %s', types, color='BLUE')
|
debug.dbg('Found param types for docstring: %s', types, color='BLUE')
|
||||||
return types
|
return types
|
||||||
|
|||||||
@@ -19,44 +19,42 @@ It works as follows:
|
|||||||
|
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.inference.cache import inference_state_function_cache
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
from jedi.inference import imports
|
from jedi.inference import imports
|
||||||
from jedi.inference.arguments import TreeArguments
|
from jedi.inference.arguments import TreeArguments
|
||||||
from jedi.inference.param import create_default_params
|
from jedi.inference.param import get_executed_param_names
|
||||||
from jedi.inference.helpers import is_stdlib_path
|
from jedi.inference.helpers import is_stdlib_path
|
||||||
from jedi.inference.utils import to_list
|
from jedi.inference.utils import to_list
|
||||||
from jedi.parser_utils import get_parent_scope
|
|
||||||
from jedi.inference.value import instance
|
from jedi.inference.value import instance
|
||||||
from jedi.inference.base_value import ValueSet, NO_VALUES
|
from jedi.inference.base_value import ValueSet, NO_VALUES
|
||||||
from jedi.inference import recursion
|
from jedi.inference import recursion
|
||||||
from jedi.inference.names import ParamNameWrapper
|
|
||||||
|
|
||||||
|
|
||||||
MAX_PARAM_SEARCHES = 20
|
MAX_PARAM_SEARCHES = 20
|
||||||
|
|
||||||
|
|
||||||
class DynamicExecutedParamName(ParamNameWrapper):
|
def _avoid_recursions(func):
|
||||||
"""
|
def wrapper(function_value, param_index):
|
||||||
Simulates being a parameter while actually just being multiple params.
|
inf = function_value.inference_state
|
||||||
"""
|
with recursion.execution_allowed(inf, function_value.tree_node) as allowed:
|
||||||
|
|
||||||
def __init__(self, executed_param_names):
|
|
||||||
super(DynamicExecutedParamName, self).__init__(executed_param_names[0])
|
|
||||||
self._executed_param_names = executed_param_names
|
|
||||||
|
|
||||||
def infer(self):
|
|
||||||
inf = self.parent_context.inference_state
|
|
||||||
with recursion.execution_allowed(inf, self) as allowed:
|
|
||||||
# We need to catch recursions that may occur, because an
|
# We need to catch recursions that may occur, because an
|
||||||
# anonymous functions can create an anonymous parameter that is
|
# anonymous functions can create an anonymous parameter that is
|
||||||
# more or less self referencing.
|
# more or less self referencing.
|
||||||
if allowed:
|
if allowed:
|
||||||
return ValueSet.from_sets(p.infer() for p in self._executed_param_names)
|
inf.dynamic_params_depth += 1
|
||||||
|
try:
|
||||||
|
return func(function_value, param_index)
|
||||||
|
finally:
|
||||||
|
inf.dynamic_params_depth -= 1
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
return
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
def search_param_names(inference_state, execution_context, funcdef):
|
@_avoid_recursions
|
||||||
|
def dynamic_param_lookup(function_value, param_index):
|
||||||
"""
|
"""
|
||||||
A dynamic search for param values. If you try to complete a type:
|
A dynamic search for param values. If you try to complete a type:
|
||||||
|
|
||||||
@@ -69,54 +67,42 @@ def search_param_names(inference_state, execution_context, funcdef):
|
|||||||
have to look for all calls to ``func`` to find out what ``foo`` possibly
|
have to look for all calls to ``func`` to find out what ``foo`` possibly
|
||||||
is.
|
is.
|
||||||
"""
|
"""
|
||||||
if not settings.dynamic_params:
|
funcdef = function_value.tree_node
|
||||||
return create_default_params(execution_context, funcdef)
|
|
||||||
|
|
||||||
inference_state.dynamic_params_depth += 1
|
if not settings.dynamic_params:
|
||||||
try:
|
return NO_VALUES
|
||||||
path = execution_context.get_root_context().py__file__()
|
|
||||||
|
path = function_value.get_root_context().py__file__()
|
||||||
if path is not None and is_stdlib_path(path):
|
if path is not None and is_stdlib_path(path):
|
||||||
# We don't want to search for usages in the stdlib. Usually people
|
# We don't want to search for usages in the stdlib. Usually people
|
||||||
# don't work with it (except if you are a core maintainer, sorry).
|
# don't work with it (except if you are a core maintainer, sorry).
|
||||||
# This makes everything slower. Just disable it and run the tests,
|
# This makes everything slower. Just disable it and run the tests,
|
||||||
# you will see the slowdown, especially in 3.6.
|
# you will see the slowdown, especially in 3.6.
|
||||||
return create_default_params(execution_context, funcdef)
|
return NO_VALUES
|
||||||
|
|
||||||
if funcdef.type == 'lambdef':
|
if funcdef.type == 'lambdef':
|
||||||
string_name = _get_lambda_name(funcdef)
|
string_name = _get_lambda_name(funcdef)
|
||||||
if string_name is None:
|
if string_name is None:
|
||||||
return create_default_params(execution_context, funcdef)
|
return NO_VALUES
|
||||||
else:
|
else:
|
||||||
string_name = funcdef.name.value
|
string_name = funcdef.name.value
|
||||||
debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA')
|
debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA')
|
||||||
|
|
||||||
try:
|
module_context = function_value.get_root_context()
|
||||||
module_context = execution_context.get_root_context()
|
arguments_list = _search_function_arguments(module_context, funcdef, string_name)
|
||||||
function_executions = _search_function_executions(
|
values = ValueSet.from_sets(
|
||||||
inference_state,
|
get_executed_param_names(
|
||||||
module_context,
|
function_value, arguments
|
||||||
funcdef,
|
)[param_index].infer()
|
||||||
string_name=string_name,
|
for arguments in arguments_list
|
||||||
)
|
)
|
||||||
if function_executions:
|
|
||||||
zipped_param_names = zip(*list(
|
|
||||||
function_execution.get_executed_param_names_and_issues()[0]
|
|
||||||
for function_execution in function_executions
|
|
||||||
))
|
|
||||||
params = [DynamicExecutedParamName(executed_param_names)
|
|
||||||
for executed_param_names in zipped_param_names]
|
|
||||||
else:
|
|
||||||
return create_default_params(execution_context, funcdef)
|
|
||||||
finally:
|
|
||||||
debug.dbg('Dynamic param result finished', color='MAGENTA')
|
debug.dbg('Dynamic param result finished', color='MAGENTA')
|
||||||
return params
|
return values
|
||||||
finally:
|
|
||||||
inference_state.dynamic_params_depth -= 1
|
|
||||||
|
|
||||||
|
|
||||||
@inference_state_function_cache(default=None)
|
@inference_state_method_cache(default=None)
|
||||||
@to_list
|
@to_list
|
||||||
def _search_function_executions(inference_state, module_context, funcdef, string_name):
|
def _search_function_arguments(module_context, funcdef, string_name):
|
||||||
"""
|
"""
|
||||||
Returns a list of param names.
|
Returns a list of param names.
|
||||||
"""
|
"""
|
||||||
@@ -127,8 +113,9 @@ def _search_function_executions(inference_state, module_context, funcdef, string
|
|||||||
string_name = cls.name.value
|
string_name = cls.name.value
|
||||||
compare_node = cls
|
compare_node = cls
|
||||||
|
|
||||||
found_executions = False
|
found_arguments = False
|
||||||
i = 0
|
i = 0
|
||||||
|
inference_state = module_context.inference_state
|
||||||
for for_mod_context in imports.get_module_contexts_containing_name(
|
for for_mod_context in imports.get_module_contexts_containing_name(
|
||||||
inference_state, [module_context], string_name):
|
inference_state, [module_context], string_name):
|
||||||
for name, trailer in _get_potential_nodes(for_mod_context, string_name):
|
for name, trailer in _get_potential_nodes(for_mod_context, string_name):
|
||||||
@@ -141,14 +128,14 @@ def _search_function_executions(inference_state, module_context, funcdef, string
|
|||||||
return
|
return
|
||||||
|
|
||||||
random_context = for_mod_context.create_context(name)
|
random_context = for_mod_context.create_context(name)
|
||||||
for function_execution in _check_name_for_execution(
|
for arguments in _check_name_for_execution(
|
||||||
inference_state, random_context, compare_node, name, trailer):
|
inference_state, random_context, compare_node, name, trailer):
|
||||||
found_executions = True
|
found_arguments = True
|
||||||
yield function_execution
|
yield arguments
|
||||||
|
|
||||||
# If there are results after processing a module, we're probably
|
# If there are results after processing a module, we're probably
|
||||||
# good to process. This is a speed optimization.
|
# good to process. This is a speed optimization.
|
||||||
if found_executions:
|
if found_arguments:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -178,13 +165,14 @@ def _get_potential_nodes(module_value, func_string_name):
|
|||||||
|
|
||||||
|
|
||||||
def _check_name_for_execution(inference_state, context, compare_node, name, trailer):
|
def _check_name_for_execution(inference_state, context, compare_node, name, trailer):
|
||||||
from jedi.inference.value.function import FunctionExecutionContext
|
from jedi.inference.value.function import BaseFunctionExecutionContext
|
||||||
|
|
||||||
def create_func_excs(value):
|
def create_args(value):
|
||||||
arglist = trailer.children[1]
|
arglist = trailer.children[1]
|
||||||
if arglist == ')':
|
if arglist == ')':
|
||||||
arglist = None
|
arglist = None
|
||||||
args = TreeArguments(inference_state, context, arglist, trailer)
|
args = TreeArguments(inference_state, context, arglist, trailer)
|
||||||
|
from jedi.inference.value.instance import InstanceArguments
|
||||||
if value.tree_node.type == 'classdef':
|
if value.tree_node.type == 'classdef':
|
||||||
created_instance = instance.TreeInstance(
|
created_instance = instance.TreeInstance(
|
||||||
inference_state,
|
inference_state,
|
||||||
@@ -192,30 +180,29 @@ def _check_name_for_execution(inference_state, context, compare_node, name, trai
|
|||||||
value,
|
value,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
for execution in created_instance.create_init_executions():
|
return InstanceArguments(created_instance, args)
|
||||||
yield execution
|
|
||||||
else:
|
else:
|
||||||
yield value.as_context(args)
|
if value.is_bound_method():
|
||||||
|
args = InstanceArguments(value.instance, args)
|
||||||
|
return args
|
||||||
|
|
||||||
for value in inference_state.goto_definitions(context, name):
|
for value in inference_state.goto_definitions(context, name):
|
||||||
value_node = value.tree_node
|
value_node = value.tree_node
|
||||||
if compare_node == value_node:
|
if compare_node == value_node:
|
||||||
for func_execution in create_func_excs(value):
|
yield create_args(value)
|
||||||
yield func_execution
|
elif isinstance(value.parent_context, BaseFunctionExecutionContext) \
|
||||||
elif isinstance(value.parent_context, FunctionExecutionContext) and \
|
and compare_node.type == 'funcdef':
|
||||||
compare_node.type == 'funcdef':
|
|
||||||
# Here we're trying to find decorators by checking the first
|
# Here we're trying to find decorators by checking the first
|
||||||
# parameter. It's not very generic though. Should find a better
|
# parameter. It's not very generic though. Should find a better
|
||||||
# solution that also applies to nested decorators.
|
# solution that also applies to nested decorators.
|
||||||
param_names, _ = value.parent_context.get_executed_param_names_and_issues()
|
param_names = value.parent_context.get_param_names()
|
||||||
if len(param_names) != 1:
|
if len(param_names) != 1:
|
||||||
continue
|
continue
|
||||||
values = param_names[0].infer()
|
values = param_names[0].infer()
|
||||||
nodes = [v.tree_node for v in values]
|
if [v.tree_node for v in values] == [compare_node]:
|
||||||
if nodes == [compare_node]:
|
|
||||||
# Found a decorator.
|
# Found a decorator.
|
||||||
module_context = context.get_root_context()
|
module_context = context.get_root_context()
|
||||||
execution_context = next(create_func_excs(value))
|
execution_context = value.as_context(create_args(value))
|
||||||
potential_nodes = _get_potential_nodes(module_context, param_names[0].string_name)
|
potential_nodes = _get_potential_nodes(module_context, param_names[0].string_name)
|
||||||
for name, trailer in potential_nodes:
|
for name, trailer in potential_nodes:
|
||||||
if value_node.start_pos < name.start_pos < value_node.end_pos:
|
if value_node.start_pos < name.start_pos < value_node.end_pos:
|
||||||
@@ -227,5 +214,5 @@ def _check_name_for_execution(inference_state, context, compare_node, name, trai
|
|||||||
name,
|
name,
|
||||||
trailer
|
trailer
|
||||||
)
|
)
|
||||||
for function_execution in iterator:
|
for arguments in iterator:
|
||||||
yield function_execution
|
yield arguments
|
||||||
@@ -13,7 +13,8 @@ from jedi.inference.base_value import ValueSet, Value, ValueWrapper, \
|
|||||||
LazyValueWrapper
|
LazyValueWrapper
|
||||||
from jedi.parser_utils import get_cached_parent_scope
|
from jedi.parser_utils import get_cached_parent_scope
|
||||||
from jedi.inference.utils import to_list
|
from jedi.inference.utils import to_list
|
||||||
from jedi.inference.names import TreeNameDefinition, ParamName, AbstractNameDefinition
|
from jedi.inference.names import TreeNameDefinition, ParamName, \
|
||||||
|
AnonymousParamName, AbstractNameDefinition
|
||||||
|
|
||||||
_definition_name_cache = weakref.WeakKeyDictionary()
|
_definition_name_cache = weakref.WeakKeyDictionary()
|
||||||
|
|
||||||
@@ -61,7 +62,9 @@ def _get_definition_names(used_names, name_key):
|
|||||||
return for_module[name_key]
|
return for_module[name_key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
names = used_names.get(name_key, ())
|
names = used_names.get(name_key, ())
|
||||||
result = for_module[name_key] = tuple(name for name in names if name.is_definition())
|
result = for_module[name_key] = tuple(
|
||||||
|
name for name in names if name.is_definition(include_setitem=True)
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -140,28 +143,45 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
class FunctionExecutionFilter(ParserTreeFilter):
|
class _FunctionExecutionFilter(ParserTreeFilter):
|
||||||
param_name = ParamName
|
def __init__(self, parent_context, function_value, until_position, origin_scope):
|
||||||
|
super(_FunctionExecutionFilter, self).__init__(
|
||||||
def __init__(self, parent_context, node_context=None,
|
|
||||||
until_position=None, origin_scope=None):
|
|
||||||
super(FunctionExecutionFilter, self).__init__(
|
|
||||||
parent_context,
|
parent_context,
|
||||||
node_context,
|
until_position=until_position,
|
||||||
until_position,
|
origin_scope=origin_scope,
|
||||||
origin_scope
|
|
||||||
)
|
)
|
||||||
|
self._function_value = function_value
|
||||||
|
|
||||||
|
def _convert_param(self, param, name):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
@to_list
|
@to_list
|
||||||
def _convert_names(self, names):
|
def _convert_names(self, names):
|
||||||
for name in names:
|
for name in names:
|
||||||
param = search_ancestor(name, 'param')
|
param = search_ancestor(name, 'param')
|
||||||
|
# Here we don't need to check if the param is a default/annotation,
|
||||||
|
# because those are not definitions and never make it to this
|
||||||
|
# point.
|
||||||
if param:
|
if param:
|
||||||
yield self.param_name(self.parent_context, name)
|
yield self._convert_param(param, name)
|
||||||
else:
|
else:
|
||||||
yield TreeNameDefinition(self.parent_context, name)
|
yield TreeNameDefinition(self.parent_context, name)
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionExecutionFilter(_FunctionExecutionFilter):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._arguments = kwargs.pop('arguments') # Python 2
|
||||||
|
super(FunctionExecutionFilter, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _convert_param(self, param, name):
|
||||||
|
return ParamName(self._function_value, name, self._arguments)
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousFunctionExecutionFilter(_FunctionExecutionFilter):
|
||||||
|
def _convert_param(self, param, name):
|
||||||
|
return AnonymousParamName(self._function_value, name)
|
||||||
|
|
||||||
|
|
||||||
class GlobalNameFilter(AbstractUsedNamesFilter):
|
class GlobalNameFilter(AbstractUsedNamesFilter):
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from jedi.inference.gradual.typing import TypeVar, LazyGenericClass, \
|
|||||||
from jedi.inference.gradual.typing import GenericClass
|
from jedi.inference.gradual.typing import GenericClass
|
||||||
from jedi.inference.helpers import is_string
|
from jedi.inference.helpers import is_string
|
||||||
from jedi.inference.compiled import builtin_from_name
|
from jedi.inference.compiled import builtin_from_name
|
||||||
|
from jedi.inference.param import get_executed_param_names
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
|
|
||||||
@@ -107,11 +108,11 @@ def _split_comment_param_declaration(decl_text):
|
|||||||
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
@inference_state_method_cache()
|
||||||
def infer_param(execution_context, param, ignore_stars=False):
|
def infer_param(function_value, param, ignore_stars=False):
|
||||||
values = _infer_param(execution_context, param)
|
values = _infer_param(function_value, param)
|
||||||
if ignore_stars:
|
if ignore_stars:
|
||||||
return values
|
return values
|
||||||
inference_state = execution_context.inference_state
|
inference_state = function_value.inference_state
|
||||||
if param.star_count == 1:
|
if param.star_count == 1:
|
||||||
tuple_ = builtin_from_name(inference_state, 'tuple')
|
tuple_ = builtin_from_name(inference_state, 'tuple')
|
||||||
return ValueSet([GenericClass(
|
return ValueSet([GenericClass(
|
||||||
@@ -128,7 +129,7 @@ def infer_param(execution_context, param, ignore_stars=False):
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
def _infer_param(execution_context, param):
|
def _infer_param(function_value, param):
|
||||||
"""
|
"""
|
||||||
Infers the type of a function parameter, using type annotations.
|
Infers the type of a function parameter, using type annotations.
|
||||||
"""
|
"""
|
||||||
@@ -161,7 +162,7 @@ def _infer_param(execution_context, param):
|
|||||||
params_comments, all_params
|
params_comments, all_params
|
||||||
)
|
)
|
||||||
from jedi.inference.value.instance import InstanceArguments
|
from jedi.inference.value.instance import InstanceArguments
|
||||||
if isinstance(execution_context.var_args, InstanceArguments):
|
if function_value.is_bound_method():
|
||||||
if index == 0:
|
if index == 0:
|
||||||
# Assume it's self, which is already handled
|
# Assume it's self, which is already handled
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
@@ -171,11 +172,11 @@ def _infer_param(execution_context, param):
|
|||||||
|
|
||||||
param_comment = params_comments[index]
|
param_comment = params_comments[index]
|
||||||
return _infer_annotation_string(
|
return _infer_annotation_string(
|
||||||
execution_context.function_value.get_default_param_context(),
|
function_value.get_default_param_context(),
|
||||||
param_comment
|
param_comment
|
||||||
)
|
)
|
||||||
# Annotations are like default params and resolve in the same way.
|
# Annotations are like default params and resolve in the same way.
|
||||||
context = execution_context.function_value.get_default_param_context()
|
context = function_value.get_default_param_context()
|
||||||
return infer_annotation(context, annotation)
|
return infer_annotation(context, annotation)
|
||||||
|
|
||||||
|
|
||||||
@@ -193,16 +194,16 @@ def py__annotations__(funcdef):
|
|||||||
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
@inference_state_method_cache()
|
||||||
def infer_return_types(function_execution_context):
|
def infer_return_types(function, arguments):
|
||||||
"""
|
"""
|
||||||
Infers the type of a function's return value,
|
Infers the type of a function's return value,
|
||||||
according to type annotations.
|
according to type annotations.
|
||||||
"""
|
"""
|
||||||
all_annotations = py__annotations__(function_execution_context.tree_node)
|
all_annotations = py__annotations__(function.tree_node)
|
||||||
annotation = all_annotations.get("return", None)
|
annotation = all_annotations.get("return", None)
|
||||||
if annotation is None:
|
if annotation is None:
|
||||||
# If there is no Python 3-type annotation, look for a Python 2-type annotation
|
# If there is no Python 3-type annotation, look for a Python 2-type annotation
|
||||||
node = function_execution_context.tree_node
|
node = function.tree_node
|
||||||
comment = parser_utils.get_following_comment_same_line(node)
|
comment = parser_utils.get_following_comment_same_line(node)
|
||||||
if comment is None:
|
if comment is None:
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
@@ -212,19 +213,19 @@ def infer_return_types(function_execution_context):
|
|||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
return _infer_annotation_string(
|
return _infer_annotation_string(
|
||||||
function_execution_context.function_value.get_default_param_context(),
|
function.get_default_param_context(),
|
||||||
match.group(1).strip()
|
match.group(1).strip()
|
||||||
).execute_annotation()
|
).execute_annotation()
|
||||||
if annotation is None:
|
if annotation is None:
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
context = function_execution_context.function_value.get_default_param_context()
|
context = function.get_default_param_context()
|
||||||
unknown_type_vars = list(find_unknown_type_vars(context, annotation))
|
unknown_type_vars = find_unknown_type_vars(context, annotation)
|
||||||
annotation_values = infer_annotation(context, annotation)
|
annotation_values = infer_annotation(context, annotation)
|
||||||
if not unknown_type_vars:
|
if not unknown_type_vars:
|
||||||
return annotation_values.execute_annotation()
|
return annotation_values.execute_annotation()
|
||||||
|
|
||||||
type_var_dict = infer_type_vars_for_execution(function_execution_context, all_annotations)
|
type_var_dict = infer_type_vars_for_execution(function, arguments, all_annotations)
|
||||||
|
|
||||||
return ValueSet.from_sets(
|
return ValueSet.from_sets(
|
||||||
ann.define_generics(type_var_dict)
|
ann.define_generics(type_var_dict)
|
||||||
@@ -233,7 +234,7 @@ def infer_return_types(function_execution_context):
|
|||||||
).execute_annotation()
|
).execute_annotation()
|
||||||
|
|
||||||
|
|
||||||
def infer_type_vars_for_execution(execution_context, annotation_dict):
|
def infer_type_vars_for_execution(function, arguments, annotation_dict):
|
||||||
"""
|
"""
|
||||||
Some functions use type vars that are not defined by the class, but rather
|
Some functions use type vars that are not defined by the class, but rather
|
||||||
only defined in the function. See for example `iter`. In those cases we
|
only defined in the function. See for example `iter`. In those cases we
|
||||||
@@ -243,10 +244,10 @@ def infer_type_vars_for_execution(execution_context, annotation_dict):
|
|||||||
2. Infer type vars with the execution state we have.
|
2. Infer type vars with the execution state we have.
|
||||||
3. Return the union of all type vars that have been found.
|
3. Return the union of all type vars that have been found.
|
||||||
"""
|
"""
|
||||||
context = execution_context.function_value.get_default_param_context()
|
context = function.get_default_param_context()
|
||||||
|
|
||||||
annotation_variable_results = {}
|
annotation_variable_results = {}
|
||||||
executed_param_names, _ = execution_context.get_executed_param_names_and_issues()
|
executed_param_names = get_executed_param_names(function, arguments)
|
||||||
for executed_param_name in executed_param_names:
|
for executed_param_name in executed_param_names:
|
||||||
try:
|
try:
|
||||||
annotation_node = annotation_dict[executed_param_name.string_name]
|
annotation_node = annotation_dict[executed_param_name.string_name]
|
||||||
@@ -275,6 +276,7 @@ def infer_type_vars_for_execution(execution_context, annotation_dict):
|
|||||||
|
|
||||||
def _merge_type_var_dicts(base_dict, new_dict):
|
def _merge_type_var_dicts(base_dict, new_dict):
|
||||||
for type_var_name, values in new_dict.items():
|
for type_var_name, values in new_dict.items():
|
||||||
|
if values:
|
||||||
try:
|
try:
|
||||||
base_dict[type_var_name] |= values
|
base_dict[type_var_name] |= values
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class _WithIndexBase(_BaseTypingValue):
|
|||||||
def __init__(self, inference_state, parent_context, name, index_value, value_of_index):
|
def __init__(self, inference_state, parent_context, name, index_value, value_of_index):
|
||||||
super(_WithIndexBase, self).__init__(inference_state, parent_context, name)
|
super(_WithIndexBase, self).__init__(inference_state, parent_context, name)
|
||||||
self._index_value = index_value
|
self._index_value = index_value
|
||||||
self._value_of_index = value_of_index
|
self._context_of_index = value_of_index
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s[%s]>' % (
|
return '<%s: %s[%s]>' % (
|
||||||
@@ -179,12 +179,12 @@ class TypingValueWithIndex(_WithIndexBase):
|
|||||||
self.parent_context,
|
self.parent_context,
|
||||||
self._tree_name,
|
self._tree_name,
|
||||||
self._index_value,
|
self._index_value,
|
||||||
self._value_of_index
|
self._context_of_index
|
||||||
)])
|
)])
|
||||||
|
|
||||||
def gather_annotation_classes(self):
|
def gather_annotation_classes(self):
|
||||||
return ValueSet.from_sets(
|
return ValueSet.from_sets(
|
||||||
_iter_over_arguments(self._index_value, self._value_of_index)
|
_iter_over_arguments(self._index_value, self._context_of_index)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ class TypeAlias(LazyValueWrapper):
|
|||||||
|
|
||||||
class _ContainerBase(_WithIndexBase):
|
class _ContainerBase(_WithIndexBase):
|
||||||
def _get_getitem_values(self, index):
|
def _get_getitem_values(self, index):
|
||||||
args = _iter_over_arguments(self._index_value, self._value_of_index)
|
args = _iter_over_arguments(self._index_value, self._context_of_index)
|
||||||
for i, values in enumerate(args):
|
for i, values in enumerate(args):
|
||||||
if i == index:
|
if i == index:
|
||||||
return values
|
return values
|
||||||
@@ -333,7 +333,7 @@ class Tuple(_ContainerBase):
|
|||||||
return self._get_getitem_values(0).execute_annotation()
|
return self._get_getitem_values(0).execute_annotation()
|
||||||
|
|
||||||
return ValueSet.from_sets(
|
return ValueSet.from_sets(
|
||||||
_iter_over_arguments(self._index_value, self._value_of_index)
|
_iter_over_arguments(self._index_value, self._context_of_index)
|
||||||
).execute_annotation()
|
).execute_annotation()
|
||||||
|
|
||||||
|
|
||||||
@@ -649,11 +649,11 @@ class LazyGenericClass(AbstractAnnotatedClass):
|
|||||||
def __init__(self, class_value, index_value, value_of_index):
|
def __init__(self, class_value, index_value, value_of_index):
|
||||||
super(LazyGenericClass, self).__init__(class_value)
|
super(LazyGenericClass, self).__init__(class_value)
|
||||||
self._index_value = index_value
|
self._index_value = index_value
|
||||||
self._value_of_index = value_of_index
|
self._context_of_index = value_of_index
|
||||||
|
|
||||||
@inference_state_method_cache()
|
@inference_state_method_cache()
|
||||||
def get_generics(self):
|
def get_generics(self):
|
||||||
return list(_iter_over_arguments(self._index_value, self._value_of_index))
|
return list(_iter_over_arguments(self._index_value, self._context_of_index))
|
||||||
|
|
||||||
|
|
||||||
class GenericClass(AbstractAnnotatedClass):
|
class GenericClass(AbstractAnnotatedClass):
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ def infer_import(context, tree_name):
|
|||||||
module_context = context.get_root_context()
|
module_context = context.get_root_context()
|
||||||
from_import_name, import_path, level, values = \
|
from_import_name, import_path, level, values = \
|
||||||
_prepare_infer_import(module_context, tree_name)
|
_prepare_infer_import(module_context, tree_name)
|
||||||
if not values:
|
if values:
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
if from_import_name is not None:
|
if from_import_name is not None:
|
||||||
values = values.py__getattribute__(
|
values = values.py__getattribute__(
|
||||||
@@ -578,3 +577,33 @@ def get_module_contexts_containing_name(inference_state, module_contexts, name):
|
|||||||
m = check_fs(file_io, base_names)
|
m = check_fs(file_io, base_names)
|
||||||
if m is not None:
|
if m is not None:
|
||||||
yield m
|
yield m
|
||||||
|
|
||||||
|
|
||||||
|
def follow_error_node_imports_if_possible(context, name):
|
||||||
|
error_node = tree.search_ancestor(name, 'error_node')
|
||||||
|
if error_node is not None:
|
||||||
|
# Get the first command start of a started simple_stmt. The error
|
||||||
|
# node is sometimes a small_stmt and sometimes a simple_stmt. Check
|
||||||
|
# for ; leaves that start a new statements.
|
||||||
|
start_index = 0
|
||||||
|
for index, n in enumerate(error_node.children):
|
||||||
|
if n.start_pos > name.start_pos:
|
||||||
|
break
|
||||||
|
if n == ';':
|
||||||
|
start_index = index + 1
|
||||||
|
nodes = error_node.children[start_index:]
|
||||||
|
first_name = nodes[0].get_first_leaf().value
|
||||||
|
|
||||||
|
# Make it possible to infer stuff like `import foo.` or
|
||||||
|
# `from foo.bar`.
|
||||||
|
if first_name in ('from', 'import'):
|
||||||
|
is_import_from = first_name == 'from'
|
||||||
|
level, names = helpers.parse_dotted_names(
|
||||||
|
nodes,
|
||||||
|
is_import_from=is_import_from,
|
||||||
|
until_node=name,
|
||||||
|
)
|
||||||
|
return Importer(
|
||||||
|
context.inference_state, names, context.get_root_context(), level).follow()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ from abc import abstractmethod
|
|||||||
from parso.tree import search_ancestor
|
from parso.tree import search_ancestor
|
||||||
|
|
||||||
from jedi._compatibility import Parameter
|
from jedi._compatibility import Parameter
|
||||||
|
from jedi.inference.utils import unite
|
||||||
from jedi.inference.base_value import ValueSet, NO_VALUES
|
from jedi.inference.base_value import ValueSet, NO_VALUES
|
||||||
from jedi.inference import docstrings
|
from jedi.inference import docstrings
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
|
from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf
|
||||||
|
|
||||||
|
|
||||||
class AbstractNameDefinition(object):
|
class AbstractNameDefinition(object):
|
||||||
@@ -106,11 +108,78 @@ class AbstractTreeName(AbstractNameDefinition):
|
|||||||
return None
|
return None
|
||||||
return parent_names + (self.tree_name.value,)
|
return parent_names + (self.tree_name.value,)
|
||||||
|
|
||||||
def goto(self, **kwargs):
|
def goto(self):
|
||||||
return self.parent_context.inference_state.goto(
|
context = self.parent_context
|
||||||
self.parent_context, self.tree_name, **kwargs
|
name = self.tree_name
|
||||||
|
definition = name.get_definition(import_name_always=True)
|
||||||
|
if definition is not None:
|
||||||
|
type_ = definition.type
|
||||||
|
if type_ == 'expr_stmt':
|
||||||
|
# Only take the parent, because if it's more complicated than just
|
||||||
|
# a name it's something you can "goto" again.
|
||||||
|
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||||
|
if is_simple_name:
|
||||||
|
return [self]
|
||||||
|
elif type_ in ('import_from', 'import_name'):
|
||||||
|
from jedi.inference.imports import goto_import
|
||||||
|
module_names = goto_import(context, name)
|
||||||
|
return module_names
|
||||||
|
else:
|
||||||
|
return [self]
|
||||||
|
else:
|
||||||
|
from jedi.inference.imports import follow_error_node_imports_if_possible
|
||||||
|
values = follow_error_node_imports_if_possible(context, name)
|
||||||
|
if values is not None:
|
||||||
|
return [value.name for value in values]
|
||||||
|
|
||||||
|
par = name.parent
|
||||||
|
node_type = par.type
|
||||||
|
if node_type == 'argument' and par.children[1] == '=' and par.children[0] == name:
|
||||||
|
# Named param goto.
|
||||||
|
trailer = par.parent
|
||||||
|
if trailer.type == 'arglist':
|
||||||
|
trailer = trailer.parent
|
||||||
|
if trailer.type != 'classdef':
|
||||||
|
if trailer.type == 'decorator':
|
||||||
|
value_set = context.infer_node(trailer.children[1])
|
||||||
|
else:
|
||||||
|
i = trailer.parent.children.index(trailer)
|
||||||
|
to_infer = trailer.parent.children[:i]
|
||||||
|
if to_infer[0] == 'await':
|
||||||
|
to_infer.pop(0)
|
||||||
|
value_set = context.infer_node(to_infer[0])
|
||||||
|
from jedi.inference.syntax_tree import infer_trailer
|
||||||
|
for trailer in to_infer[1:]:
|
||||||
|
value_set = infer_trailer(context, value_set, trailer)
|
||||||
|
param_names = []
|
||||||
|
for value in value_set:
|
||||||
|
for signature in value.get_signatures():
|
||||||
|
for param_name in signature.get_param_names():
|
||||||
|
if param_name.string_name == name.value:
|
||||||
|
param_names.append(param_name)
|
||||||
|
return param_names
|
||||||
|
elif node_type == 'dotted_name': # Is a decorator.
|
||||||
|
index = par.children.index(name)
|
||||||
|
if index > 0:
|
||||||
|
new_dotted = deep_ast_copy(par)
|
||||||
|
new_dotted.children[index - 1:] = []
|
||||||
|
values = context.infer_node(new_dotted)
|
||||||
|
return unite(
|
||||||
|
value.goto(name, name_context=value.as_context())
|
||||||
|
for value in values
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if node_type == 'trailer' and par.children[0] == '.':
|
||||||
|
values = infer_call_of_leaf(context, name, cut_own_trailer=True)
|
||||||
|
return values.goto(name, name_context=context)
|
||||||
|
else:
|
||||||
|
stmt = search_ancestor(
|
||||||
|
name, 'expr_stmt', 'lambdef'
|
||||||
|
) or name
|
||||||
|
if stmt.type == 'lambdef':
|
||||||
|
stmt = name
|
||||||
|
return context.goto(name, position=stmt.start_pos)
|
||||||
|
|
||||||
def is_import(self):
|
def is_import(self):
|
||||||
imp = search_ancestor(self.tree_name, 'import_from', 'import_name')
|
imp = search_ancestor(self.tree_name, 'import_from', 'import_name')
|
||||||
return imp is not None
|
return imp is not None
|
||||||
@@ -175,6 +244,47 @@ class TreeNameDefinition(AbstractTreeName):
|
|||||||
return 'statement'
|
return 'statement'
|
||||||
return self._API_TYPES.get(definition.type, 'statement')
|
return self._API_TYPES.get(definition.type, 'statement')
|
||||||
|
|
||||||
|
def assignment_indexes(self):
|
||||||
|
"""
|
||||||
|
Returns an array of tuple(int, node) of the indexes that are used in
|
||||||
|
tuple assignments.
|
||||||
|
|
||||||
|
For example if the name is ``y`` in the following code::
|
||||||
|
|
||||||
|
x, (y, z) = 2, ''
|
||||||
|
|
||||||
|
would result in ``[(1, xyz_node), (0, yz_node)]``.
|
||||||
|
|
||||||
|
When searching for b in the case ``a, *b, c = [...]`` it will return::
|
||||||
|
|
||||||
|
[(slice(1, -1), abc_node)]
|
||||||
|
"""
|
||||||
|
indexes = []
|
||||||
|
is_star_expr = False
|
||||||
|
node = self.tree_name.parent
|
||||||
|
compare = self.tree_name
|
||||||
|
while node is not None:
|
||||||
|
if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'):
|
||||||
|
for i, child in enumerate(node.children):
|
||||||
|
if child == compare:
|
||||||
|
index = int(i / 2)
|
||||||
|
if is_star_expr:
|
||||||
|
from_end = int((len(node.children) - i) / 2)
|
||||||
|
index = slice(index, -from_end)
|
||||||
|
indexes.insert(0, (index, node))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise LookupError("Couldn't find the assignment.")
|
||||||
|
is_star_expr = False
|
||||||
|
elif node.type == 'star_expr':
|
||||||
|
is_star_expr = True
|
||||||
|
elif node.type in ('expr_stmt', 'sync_comp_for'):
|
||||||
|
break
|
||||||
|
|
||||||
|
compare = node
|
||||||
|
node = node.parent
|
||||||
|
return indexes
|
||||||
|
|
||||||
|
|
||||||
class _ParamMixin(object):
|
class _ParamMixin(object):
|
||||||
def maybe_positional_argument(self, include_star=True):
|
def maybe_positional_argument(self, include_star=True):
|
||||||
@@ -242,8 +352,24 @@ class BaseTreeParamName(ParamNameInterface, AbstractTreeName):
|
|||||||
output += '=' + default.get_code(include_prefix=False)
|
output += '=' + default.get_code(include_prefix=False)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def get_public_name(self):
|
||||||
|
name = self.string_name
|
||||||
|
if name.startswith('__'):
|
||||||
|
# Params starting with __ are an equivalent to positional only
|
||||||
|
# variables in typeshed.
|
||||||
|
name = name[2:]
|
||||||
|
return name
|
||||||
|
|
||||||
|
def goto(self, **kwargs):
|
||||||
|
return [self]
|
||||||
|
|
||||||
|
|
||||||
|
class _ActualTreeParamName(BaseTreeParamName):
|
||||||
|
def __init__(self, function_value, tree_name):
|
||||||
|
super(_ActualTreeParamName, self).__init__(
|
||||||
|
function_value.get_default_param_context(), tree_name)
|
||||||
|
self.function_value = function_value
|
||||||
|
|
||||||
class ParamName(BaseTreeParamName):
|
|
||||||
def _get_param_node(self):
|
def _get_param_node(self):
|
||||||
return search_ancestor(self.tree_name, 'param')
|
return search_ancestor(self.tree_name, 'param')
|
||||||
|
|
||||||
@@ -254,7 +380,7 @@ class ParamName(BaseTreeParamName):
|
|||||||
def infer_annotation(self, execute_annotation=True, ignore_stars=False):
|
def infer_annotation(self, execute_annotation=True, ignore_stars=False):
|
||||||
from jedi.inference.gradual.annotation import infer_param
|
from jedi.inference.gradual.annotation import infer_param
|
||||||
values = infer_param(
|
values = infer_param(
|
||||||
self.parent_context, self._get_param_node(),
|
self.function_value, self._get_param_node(),
|
||||||
ignore_stars=ignore_stars)
|
ignore_stars=ignore_stars)
|
||||||
if execute_annotation:
|
if execute_annotation:
|
||||||
values = values.execute_annotation()
|
values = values.execute_annotation()
|
||||||
@@ -264,20 +390,12 @@ class ParamName(BaseTreeParamName):
|
|||||||
node = self.default_node
|
node = self.default_node
|
||||||
if node is None:
|
if node is None:
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
return self.parent_context.parent_context.infer_node(node)
|
return self.parent_context.infer_node(node)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_node(self):
|
def default_node(self):
|
||||||
return self._get_param_node().default
|
return self._get_param_node().default
|
||||||
|
|
||||||
def get_public_name(self):
|
|
||||||
name = self.string_name
|
|
||||||
if name.startswith('__'):
|
|
||||||
# Params starting with __ are an equivalent to positional only
|
|
||||||
# variables in typeshed.
|
|
||||||
name = name[2:]
|
|
||||||
return name
|
|
||||||
|
|
||||||
def get_kind(self):
|
def get_kind(self):
|
||||||
tree_param = self._get_param_node()
|
tree_param = self._get_param_node()
|
||||||
if tree_param.star_count == 1: # *args
|
if tree_param.star_count == 1: # *args
|
||||||
@@ -311,14 +429,52 @@ class ParamName(BaseTreeParamName):
|
|||||||
if values:
|
if values:
|
||||||
return values
|
return values
|
||||||
|
|
||||||
doc_params = docstrings.infer_param(self.parent_context, self._get_param_node())
|
doc_params = docstrings.infer_param(self.function_value, self._get_param_node())
|
||||||
if doc_params:
|
|
||||||
return doc_params
|
return doc_params
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousParamName(_ActualTreeParamName):
|
||||||
|
def __init__(self, function_value, tree_name):
|
||||||
|
super(AnonymousParamName, self).__init__(function_value, tree_name)
|
||||||
|
|
||||||
|
def infer(self):
|
||||||
|
values = super(AnonymousParamName, self).infer()
|
||||||
|
if values:
|
||||||
|
return values
|
||||||
|
from jedi.inference.dynamic_params import dynamic_param_lookup
|
||||||
|
param = self._get_param_node()
|
||||||
|
values = dynamic_param_lookup(self.function_value, param.position_index)
|
||||||
|
if values:
|
||||||
|
return values
|
||||||
|
|
||||||
|
if param.star_count == 1:
|
||||||
|
from jedi.inference.value.iterable import FakeTuple
|
||||||
|
value = FakeTuple(self.function_value.inference_state, [])
|
||||||
|
elif param.star_count == 2:
|
||||||
|
from jedi.inference.value.iterable import FakeDict
|
||||||
|
value = FakeDict(self.function_value.inference_state, {})
|
||||||
|
elif param.default is None:
|
||||||
|
return NO_VALUES
|
||||||
|
else:
|
||||||
|
return self.function_value.parent_context.infer_node(param.default)
|
||||||
|
return ValueSet({value})
|
||||||
|
|
||||||
|
|
||||||
|
class ParamName(_ActualTreeParamName):
|
||||||
|
def __init__(self, function_value, tree_name, arguments):
|
||||||
|
super(ParamName, self).__init__(function_value, tree_name)
|
||||||
|
self.arguments = arguments
|
||||||
|
|
||||||
|
def infer(self):
|
||||||
|
values = super(ParamName, self).infer()
|
||||||
|
if values:
|
||||||
|
return values
|
||||||
|
|
||||||
return self.get_executed_param_name().infer()
|
return self.get_executed_param_name().infer()
|
||||||
|
|
||||||
def get_executed_param_name(self):
|
def get_executed_param_name(self):
|
||||||
params_names, _ = self.parent_context.get_executed_param_names_and_issues()
|
from jedi.inference.param import get_executed_param_names
|
||||||
|
params_names = get_executed_param_names(self.function_value, self.arguments)
|
||||||
return params_names[self._get_param_node().position_index]
|
return params_names[self._get_param_node().position_index]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ def _add_argument_issue(error_name, lazy_value, message):
|
|||||||
|
|
||||||
|
|
||||||
class ExecutedParamName(ParamName):
|
class ExecutedParamName(ParamName):
|
||||||
"""Fake a param and give it values."""
|
def __init__(self, function_value, arguments, param_node, lazy_value, is_default=False):
|
||||||
def __init__(self, execution_context, param_node, lazy_value, is_default=False):
|
super(ExecutedParamName, self).__init__(
|
||||||
super(ExecutedParamName, self).__init__(execution_context, param_node.name)
|
function_value, param_node.name, arguments=arguments)
|
||||||
self._lazy_value = lazy_value
|
self._lazy_value = lazy_value
|
||||||
self._is_default = is_default
|
self._is_default = is_default
|
||||||
|
|
||||||
@@ -42,19 +42,15 @@ class ExecutedParamName(ParamName):
|
|||||||
matches = any(c1.is_sub_class_of(c2)
|
matches = any(c1.is_sub_class_of(c2)
|
||||||
for c1 in argument_values
|
for c1 in argument_values
|
||||||
for c2 in annotations.gather_annotation_classes())
|
for c2 in annotations.gather_annotation_classes())
|
||||||
debug.dbg("signature compare %s: %s <=> %s",
|
debug.dbg("param compare %s: %s <=> %s",
|
||||||
matches, argument_values, annotations, color='BLUE')
|
matches, argument_values, annotations, color='BLUE')
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
@property
|
|
||||||
def var_args(self):
|
|
||||||
return self.parent_context.var_args
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.string_name)
|
return '<%s: %s>' % (self.__class__.__name__, self.string_name)
|
||||||
|
|
||||||
|
|
||||||
def get_executed_param_names_and_issues(execution_context, arguments):
|
def get_executed_param_names_and_issues(function_value, arguments):
|
||||||
def too_many_args(argument):
|
def too_many_args(argument):
|
||||||
m = _error_argument_count(funcdef, len(unpacked_va))
|
m = _error_argument_count(funcdef, len(unpacked_va))
|
||||||
# Just report an error for the first param that is not needed (like
|
# Just report an error for the first param that is not needed (like
|
||||||
@@ -70,15 +66,16 @@ def get_executed_param_names_and_issues(execution_context, arguments):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
issues.append(None)
|
issues.append(None)
|
||||||
|
debug.warning('non-public warning: %s', m)
|
||||||
|
|
||||||
issues = [] # List[Optional[analysis issue]]
|
issues = [] # List[Optional[analysis issue]]
|
||||||
result_params = []
|
result_params = []
|
||||||
param_dict = {}
|
param_dict = {}
|
||||||
funcdef = execution_context.tree_node
|
funcdef = function_value.tree_node
|
||||||
# Default params are part of the value where the function was defined.
|
# Default params are part of the value where the function was defined.
|
||||||
# This means that they might have access on class variables that the
|
# This means that they might have access on class variables that the
|
||||||
# function itself doesn't have.
|
# function itself doesn't have.
|
||||||
default_param_context = execution_context.function_value.get_default_param_context()
|
default_param_context = function_value.get_default_param_context()
|
||||||
|
|
||||||
for param in funcdef.get_params():
|
for param in funcdef.get_params():
|
||||||
param_dict[param.name.value] = param
|
param_dict[param.name.value] = param
|
||||||
@@ -114,7 +111,8 @@ def get_executed_param_names_and_issues(execution_context, arguments):
|
|||||||
contextualized_node.node, message=m)
|
contextualized_node.node, message=m)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
keys_used[key] = ExecutedParamName(execution_context, key_param, argument)
|
keys_used[key] = ExecutedParamName(
|
||||||
|
function_value, arguments, key_param, argument)
|
||||||
key, argument = next(var_arg_iterator, (None, None))
|
key, argument = next(var_arg_iterator, (None, None))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -134,13 +132,13 @@ def get_executed_param_names_and_issues(execution_context, arguments):
|
|||||||
var_arg_iterator.push_back((key, argument))
|
var_arg_iterator.push_back((key, argument))
|
||||||
break
|
break
|
||||||
lazy_value_list.append(argument)
|
lazy_value_list.append(argument)
|
||||||
seq = iterable.FakeSequence(execution_context.inference_state, u'tuple', lazy_value_list)
|
seq = iterable.FakeTuple(function_value.inference_state, lazy_value_list)
|
||||||
result_arg = LazyKnownValue(seq)
|
result_arg = LazyKnownValue(seq)
|
||||||
elif param.star_count == 2:
|
elif param.star_count == 2:
|
||||||
if argument is not None:
|
if argument is not None:
|
||||||
too_many_args(argument)
|
too_many_args(argument)
|
||||||
# **kwargs param
|
# **kwargs param
|
||||||
dct = iterable.FakeDict(execution_context.inference_state, dict(non_matching_keys))
|
dct = iterable.FakeDict(function_value.inference_state, dict(non_matching_keys))
|
||||||
result_arg = LazyKnownValue(dct)
|
result_arg = LazyKnownValue(dct)
|
||||||
non_matching_keys = {}
|
non_matching_keys = {}
|
||||||
else:
|
else:
|
||||||
@@ -167,8 +165,7 @@ def get_executed_param_names_and_issues(execution_context, arguments):
|
|||||||
result_arg = argument
|
result_arg = argument
|
||||||
|
|
||||||
result_params.append(ExecutedParamName(
|
result_params.append(ExecutedParamName(
|
||||||
execution_context, param, result_arg,
|
function_value, arguments, param, result_arg, is_default=is_default
|
||||||
is_default=is_default
|
|
||||||
))
|
))
|
||||||
if not isinstance(result_arg, LazyUnknownValue):
|
if not isinstance(result_arg, LazyUnknownValue):
|
||||||
keys_used[param.name.value] = result_params[-1]
|
keys_used[param.name.value] = result_params[-1]
|
||||||
@@ -209,6 +206,10 @@ def get_executed_param_names_and_issues(execution_context, arguments):
|
|||||||
return result_params, issues
|
return result_params, issues
|
||||||
|
|
||||||
|
|
||||||
|
def get_executed_param_names(function_value, arguments):
|
||||||
|
return get_executed_param_names_and_issues(function_value, arguments)[0]
|
||||||
|
|
||||||
|
|
||||||
def _error_argument_count(funcdef, actual_count):
|
def _error_argument_count(funcdef, actual_count):
|
||||||
params = funcdef.get_params()
|
params = funcdef.get_params()
|
||||||
default_arguments = sum(1 for p in params if p.default or p.star_count)
|
default_arguments = sum(1 for p in params if p.default or p.star_count)
|
||||||
@@ -219,24 +220,3 @@ def _error_argument_count(funcdef, actual_count):
|
|||||||
before = 'from %s to ' % (len(params) - default_arguments)
|
before = 'from %s to ' % (len(params) - default_arguments)
|
||||||
return ('TypeError: %s() takes %s%s arguments (%s given).'
|
return ('TypeError: %s() takes %s%s arguments (%s given).'
|
||||||
% (funcdef.name, before, len(params), actual_count))
|
% (funcdef.name, before, len(params), actual_count))
|
||||||
|
|
||||||
|
|
||||||
def _create_default_param(execution_context, param):
|
|
||||||
if param.star_count == 1:
|
|
||||||
result_arg = LazyKnownValue(
|
|
||||||
iterable.FakeSequence(execution_context.inference_state, u'tuple', [])
|
|
||||||
)
|
|
||||||
elif param.star_count == 2:
|
|
||||||
result_arg = LazyKnownValue(
|
|
||||||
iterable.FakeDict(execution_context.inference_state, {})
|
|
||||||
)
|
|
||||||
elif param.default is None:
|
|
||||||
result_arg = LazyUnknownValue()
|
|
||||||
else:
|
|
||||||
result_arg = LazyTreeValue(execution_context.parent_context, param.default)
|
|
||||||
return ExecutedParamName(execution_context, param, result_arg)
|
|
||||||
|
|
||||||
|
|
||||||
def create_default_params(execution_context, funcdef):
|
|
||||||
return [_create_default_param(execution_context, p)
|
|
||||||
for p in funcdef.get_params()]
|
|
||||||
|
|||||||
@@ -133,8 +133,7 @@ class ExecutionRecursionDetector(object):
|
|||||||
self._execution_count += 1
|
self._execution_count += 1
|
||||||
|
|
||||||
if self._funcdef_execution_counts.setdefault(funcdef, 0) >= per_function_execution_limit:
|
if self._funcdef_execution_counts.setdefault(funcdef, 0) >= per_function_execution_limit:
|
||||||
# TODO why check for builtins here again?
|
if module_context.py__name__() == 'typing':
|
||||||
if module_context.py__name__() in ('builtins', 'typing'):
|
|
||||||
return False
|
return False
|
||||||
debug.warning(
|
debug.warning(
|
||||||
'Per function execution limit (%s) reached: %s',
|
'Per function execution limit (%s) reached: %s',
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from jedi._compatibility import Parameter
|
from jedi._compatibility import Parameter
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
|
from jedi import debug
|
||||||
|
from jedi import parser_utils
|
||||||
|
|
||||||
|
|
||||||
class _SignatureMixin(object):
|
class _SignatureMixin(object):
|
||||||
@@ -55,6 +57,8 @@ class AbstractSignature(_SignatureMixin):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
if self.value is self._function_value:
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self.value)
|
||||||
return '<%s: %s, %s>' % (self.__class__.__name__, self.value, self._function_value)
|
return '<%s: %s, %s>' % (self.__class__.__name__, self.value, self._function_value)
|
||||||
|
|
||||||
|
|
||||||
@@ -89,6 +93,26 @@ class TreeSignature(AbstractSignature):
|
|||||||
params = process_params(params)
|
params = process_params(params)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
def matches_signature(self, arguments):
|
||||||
|
from jedi.inference.param import get_executed_param_names_and_issues
|
||||||
|
executed_param_names, issues = \
|
||||||
|
get_executed_param_names_and_issues(self._function_value, arguments)
|
||||||
|
if issues:
|
||||||
|
return False
|
||||||
|
|
||||||
|
matches = all(executed_param_name.matches_signature()
|
||||||
|
for executed_param_name in executed_param_names)
|
||||||
|
if debug.enable_notice:
|
||||||
|
tree_node = self._function_value.tree_node
|
||||||
|
signature = parser_utils.get_call_signature(tree_node)
|
||||||
|
if matches:
|
||||||
|
debug.dbg("Overloading match: %s@%s (%s)",
|
||||||
|
signature, tree_node.start_pos[0], arguments, color='BLUE')
|
||||||
|
else:
|
||||||
|
debug.dbg("Overloading no match: %s@%s (%s)",
|
||||||
|
signature, tree_node.start_pos[0], arguments, color='BLUE')
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
class BuiltinSignature(AbstractSignature):
|
class BuiltinSignature(AbstractSignature):
|
||||||
def __init__(self, value, return_string, is_bound=False):
|
def __init__(self, value, return_string, is_bound=False):
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ def _iter_nodes_for_param(param_name):
|
|||||||
)
|
)
|
||||||
for c in values:
|
for c in values:
|
||||||
yield c, args
|
yield c, args
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
|
|
||||||
def _goes_to_param_name(param_name, context, potential_name):
|
def _goes_to_param_name(param_name, context, potential_name):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from jedi._compatibility import force_unicode, unicode
|
|||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
from jedi.inference.base_value import ValueSet, NO_VALUES, ContextualizedNode, \
|
from jedi.inference.base_value import ValueSet, NO_VALUES, ContextualizedNode, \
|
||||||
ContextualizedName, iterator_to_value_set, iterate_values
|
iterator_to_value_set, iterate_values
|
||||||
from jedi.inference.lazy_value import LazyTreeValue
|
from jedi.inference.lazy_value import LazyTreeValue
|
||||||
from jedi.inference import compiled
|
from jedi.inference import compiled
|
||||||
from jedi.inference import recursion
|
from jedi.inference import recursion
|
||||||
@@ -18,12 +18,15 @@ from jedi.inference import imports
|
|||||||
from jedi.inference import arguments
|
from jedi.inference import arguments
|
||||||
from jedi.inference.value import ClassValue, FunctionValue
|
from jedi.inference.value import ClassValue, FunctionValue
|
||||||
from jedi.inference.value import iterable
|
from jedi.inference.value import iterable
|
||||||
|
from jedi.inference.value.dynamic_arrays import ListModification, DictModification
|
||||||
from jedi.inference.value import TreeInstance
|
from jedi.inference.value import TreeInstance
|
||||||
from jedi.inference.helpers import is_string, is_literal, is_number
|
from jedi.inference.helpers import is_string, is_literal, is_number, get_names_of_node
|
||||||
from jedi.inference.compiled.access import COMPARISON_OPERATORS
|
from jedi.inference.compiled.access import COMPARISON_OPERATORS
|
||||||
from jedi.inference.cache import inference_state_method_cache
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
from jedi.inference.gradual.stub_value import VersionInfo
|
from jedi.inference.gradual.stub_value import VersionInfo
|
||||||
from jedi.inference.gradual import annotation
|
from jedi.inference.gradual import annotation
|
||||||
|
from jedi.inference.names import TreeNameDefinition
|
||||||
|
from jedi.inference.context import CompForContext
|
||||||
from jedi.inference.value.decorator import Decoratee
|
from jedi.inference.value.decorator import Decoratee
|
||||||
from jedi.plugins import plugin_manager
|
from jedi.plugins import plugin_manager
|
||||||
|
|
||||||
@@ -64,9 +67,99 @@ def _py__stop_iteration_returns(generators):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def infer_node(context, element):
|
||||||
|
if isinstance(context, CompForContext):
|
||||||
|
return _infer_node(context, element)
|
||||||
|
|
||||||
|
if_stmt = element
|
||||||
|
while if_stmt is not None:
|
||||||
|
if_stmt = if_stmt.parent
|
||||||
|
if if_stmt.type in ('if_stmt', 'for_stmt'):
|
||||||
|
break
|
||||||
|
if parser_utils.is_scope(if_stmt):
|
||||||
|
if_stmt = None
|
||||||
|
break
|
||||||
|
predefined_if_name_dict = context.predefined_names.get(if_stmt)
|
||||||
|
# TODO there's a lot of issues with this one. We actually should do
|
||||||
|
# this in a different way. Caching should only be active in certain
|
||||||
|
# cases and this all sucks.
|
||||||
|
if predefined_if_name_dict is None and if_stmt \
|
||||||
|
and if_stmt.type == 'if_stmt' and context.inference_state.is_analysis:
|
||||||
|
if_stmt_test = if_stmt.children[1]
|
||||||
|
name_dicts = [{}]
|
||||||
|
# If we already did a check, we don't want to do it again -> If
|
||||||
|
# value.predefined_names is filled, we stop.
|
||||||
|
# We don't want to check the if stmt itself, it's just about
|
||||||
|
# the content.
|
||||||
|
if element.start_pos > if_stmt_test.end_pos:
|
||||||
|
# Now we need to check if the names in the if_stmt match the
|
||||||
|
# names in the suite.
|
||||||
|
if_names = get_names_of_node(if_stmt_test)
|
||||||
|
element_names = get_names_of_node(element)
|
||||||
|
str_element_names = [e.value for e in element_names]
|
||||||
|
if any(i.value in str_element_names for i in if_names):
|
||||||
|
for if_name in if_names:
|
||||||
|
definitions = context.inference_state.goto_definitions(context, if_name)
|
||||||
|
# Every name that has multiple different definitions
|
||||||
|
# causes the complexity to rise. The complexity should
|
||||||
|
# never fall below 1.
|
||||||
|
if len(definitions) > 1:
|
||||||
|
if len(name_dicts) * len(definitions) > 16:
|
||||||
|
debug.dbg('Too many options for if branch inference %s.', if_stmt)
|
||||||
|
# There's only a certain amount of branches
|
||||||
|
# Jedi can infer, otherwise it will take to
|
||||||
|
# long.
|
||||||
|
name_dicts = [{}]
|
||||||
|
break
|
||||||
|
|
||||||
|
original_name_dicts = list(name_dicts)
|
||||||
|
name_dicts = []
|
||||||
|
for definition in definitions:
|
||||||
|
new_name_dicts = list(original_name_dicts)
|
||||||
|
for i, name_dict in enumerate(new_name_dicts):
|
||||||
|
new_name_dicts[i] = name_dict.copy()
|
||||||
|
new_name_dicts[i][if_name.value] = ValueSet([definition])
|
||||||
|
|
||||||
|
name_dicts += new_name_dicts
|
||||||
|
else:
|
||||||
|
for name_dict in name_dicts:
|
||||||
|
name_dict[if_name.value] = definitions
|
||||||
|
if len(name_dicts) > 1:
|
||||||
|
result = NO_VALUES
|
||||||
|
for name_dict in name_dicts:
|
||||||
|
with context.predefine_names(if_stmt, name_dict):
|
||||||
|
result |= _infer_node(context, element)
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return _infer_node_if_inferred(context, element)
|
||||||
|
else:
|
||||||
|
if predefined_if_name_dict:
|
||||||
|
return _infer_node(context, element)
|
||||||
|
else:
|
||||||
|
return _infer_node_if_inferred(context, element)
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_node_if_inferred(context, element):
|
||||||
|
"""
|
||||||
|
TODO This function is temporary: Merge with infer_node.
|
||||||
|
"""
|
||||||
|
parent = element
|
||||||
|
while parent is not None:
|
||||||
|
parent = parent.parent
|
||||||
|
predefined_if_name_dict = context.predefined_names.get(parent)
|
||||||
|
if predefined_if_name_dict is not None:
|
||||||
|
return _infer_node(context, element)
|
||||||
|
return _infer_node_cached(context, element)
|
||||||
|
|
||||||
|
|
||||||
|
@inference_state_method_cache(default=NO_VALUES)
|
||||||
|
def _infer_node_cached(context, element):
|
||||||
|
return _infer_node(context, element)
|
||||||
|
|
||||||
|
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
@_limit_value_infers
|
@_limit_value_infers
|
||||||
def infer_node(context, element):
|
def _infer_node(context, element):
|
||||||
debug.dbg('infer_node %s@%s in %s', element, element.start_pos, context)
|
debug.dbg('infer_node %s@%s in %s', element, element.start_pos, context)
|
||||||
inference_state = context.inference_state
|
inference_state = context.inference_state
|
||||||
typ = element.type
|
typ = element.type
|
||||||
@@ -126,7 +219,7 @@ def infer_node(context, element):
|
|||||||
value_set = value_set.py__getattribute__(next_name, name_context=context)
|
value_set = value_set.py__getattribute__(next_name, name_context=context)
|
||||||
return value_set
|
return value_set
|
||||||
elif typ == 'eval_input':
|
elif typ == 'eval_input':
|
||||||
return infer_node(context, element.children[0])
|
return context.infer_node(element.children[0])
|
||||||
elif typ == 'annassign':
|
elif typ == 'annassign':
|
||||||
return annotation.infer_annotation(context, element.children[1]) \
|
return annotation.infer_annotation(context, element.children[1]) \
|
||||||
.execute_annotation()
|
.execute_annotation()
|
||||||
@@ -141,7 +234,7 @@ def infer_node(context, element):
|
|||||||
# Generator.send() is not implemented.
|
# Generator.send() is not implemented.
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
elif typ == 'namedexpr_test':
|
elif typ == 'namedexpr_test':
|
||||||
return infer_node(context, element.children[2])
|
return context.infer_node(element.children[2])
|
||||||
else:
|
else:
|
||||||
return infer_or_test(context, element)
|
return infer_or_test(context, element)
|
||||||
|
|
||||||
@@ -264,20 +357,6 @@ def infer_atom(context, atom):
|
|||||||
@_limit_value_infers
|
@_limit_value_infers
|
||||||
def infer_expr_stmt(context, stmt, seek_name=None):
|
def infer_expr_stmt(context, stmt, seek_name=None):
|
||||||
with recursion.execution_allowed(context.inference_state, stmt) as allowed:
|
with recursion.execution_allowed(context.inference_state, stmt) as allowed:
|
||||||
# Here we allow list/set to recurse under certain conditions. To make
|
|
||||||
# it possible to resolve stuff like list(set(list(x))), this is
|
|
||||||
# necessary.
|
|
||||||
if not allowed and context.get_root_context().is_builtins_module():
|
|
||||||
try:
|
|
||||||
instance = context.var_args.instance
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if instance.name.string_name in ('list', 'set'):
|
|
||||||
c = instance.get_first_non_keyword_argument_values()
|
|
||||||
if instance not in c:
|
|
||||||
allowed = True
|
|
||||||
|
|
||||||
if allowed:
|
if allowed:
|
||||||
return _infer_expr_stmt(context, stmt, seek_name)
|
return _infer_expr_stmt(context, stmt, seek_name)
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
@@ -291,24 +370,53 @@ def _infer_expr_stmt(context, stmt, seek_name=None):
|
|||||||
names are defined in the statement, `seek_name` returns the result for
|
names are defined in the statement, `seek_name` returns the result for
|
||||||
this name.
|
this name.
|
||||||
|
|
||||||
|
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
|
||||||
|
('=' (yield_expr|testlist_star_expr))*)
|
||||||
|
annassign: ':' test ['=' test]
|
||||||
|
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
|
||||||
|
'<<=' | '>>=' | '**=' | '//=')
|
||||||
|
|
||||||
:param stmt: A `tree.ExprStmt`.
|
:param stmt: A `tree.ExprStmt`.
|
||||||
"""
|
"""
|
||||||
|
def check_setitem(stmt):
|
||||||
|
atom_expr = stmt.children[0]
|
||||||
|
if atom_expr.type not in ('atom_expr', 'power'):
|
||||||
|
return False, None
|
||||||
|
name = atom_expr.children[0]
|
||||||
|
if name.type != 'name' or len(atom_expr.children) != 2:
|
||||||
|
return False, None
|
||||||
|
trailer = atom_expr.children[-1]
|
||||||
|
return trailer.children[0] == '[', trailer.children[1]
|
||||||
|
|
||||||
debug.dbg('infer_expr_stmt %s (%s)', stmt, seek_name)
|
debug.dbg('infer_expr_stmt %s (%s)', stmt, seek_name)
|
||||||
rhs = stmt.get_rhs()
|
rhs = stmt.get_rhs()
|
||||||
value_set = context.infer_node(rhs)
|
value_set = context.infer_node(rhs)
|
||||||
|
|
||||||
if seek_name:
|
if seek_name:
|
||||||
c_node = ContextualizedName(context, seek_name)
|
n = TreeNameDefinition(context, seek_name)
|
||||||
value_set = check_tuple_assignments(c_node, value_set)
|
value_set = check_tuple_assignments(n, value_set)
|
||||||
|
|
||||||
first_operator = next(stmt.yield_operators(), None)
|
first_operator = next(stmt.yield_operators(), None)
|
||||||
if first_operator not in ('=', None) and first_operator.type == 'operator':
|
is_setitem, subscriptlist = check_setitem(stmt)
|
||||||
|
is_annassign = first_operator not in ('=', None) and first_operator.type == 'operator'
|
||||||
|
if is_annassign or is_setitem:
|
||||||
# `=` is always the last character in aug assignments -> -1
|
# `=` is always the last character in aug assignments -> -1
|
||||||
|
name = stmt.get_defined_names(include_setitem=True)[0].value
|
||||||
|
left_values = context.py__getattribute__(name, position=stmt.start_pos)
|
||||||
|
|
||||||
|
if is_setitem:
|
||||||
|
def to_mod(v):
|
||||||
|
c = ContextualizedNode(context, subscriptlist)
|
||||||
|
if v.array_type == 'dict':
|
||||||
|
return DictModification(v, value_set, c)
|
||||||
|
elif v.array_type == 'list':
|
||||||
|
return ListModification(v, value_set, c)
|
||||||
|
return v
|
||||||
|
|
||||||
|
value_set = ValueSet(to_mod(v) for v in left_values)
|
||||||
|
else:
|
||||||
operator = copy.copy(first_operator)
|
operator = copy.copy(first_operator)
|
||||||
operator.value = operator.value[:-1]
|
operator.value = operator.value[:-1]
|
||||||
name = stmt.get_defined_names()[0].value
|
|
||||||
left = context.py__getattribute__(name, position=stmt.start_pos)
|
|
||||||
|
|
||||||
for_stmt = tree.search_ancestor(stmt, 'for_stmt')
|
for_stmt = tree.search_ancestor(stmt, 'for_stmt')
|
||||||
if for_stmt is not None and for_stmt.type == 'for_stmt' and value_set \
|
if for_stmt is not None and for_stmt.type == 'for_stmt' and value_set \
|
||||||
and parser_utils.for_stmt_defines_one_name(for_stmt):
|
and parser_utils.for_stmt_defines_one_name(for_stmt):
|
||||||
@@ -323,10 +431,10 @@ def _infer_expr_stmt(context, stmt, seek_name=None):
|
|||||||
dct = {for_stmt.children[1].value: lazy_value.infer()}
|
dct = {for_stmt.children[1].value: lazy_value.infer()}
|
||||||
with context.predefine_names(for_stmt, dct):
|
with context.predefine_names(for_stmt, dct):
|
||||||
t = context.infer_node(rhs)
|
t = context.infer_node(rhs)
|
||||||
left = _infer_comparison(context, left, operator, t)
|
left_values = _infer_comparison(context, left_values, operator, t)
|
||||||
value_set = left
|
value_set = left_values
|
||||||
else:
|
else:
|
||||||
value_set = _infer_comparison(context, left, operator, value_set)
|
value_set = _infer_comparison(context, left_values, operator, value_set)
|
||||||
debug.dbg('infer_expr_stmt result %s', value_set)
|
debug.dbg('infer_expr_stmt result %s', value_set)
|
||||||
return value_set
|
return value_set
|
||||||
|
|
||||||
@@ -564,7 +672,7 @@ def tree_name_to_values(inference_state, context, tree_name):
|
|||||||
return value_set
|
return value_set
|
||||||
|
|
||||||
types = []
|
types = []
|
||||||
node = tree_name.get_definition(import_name_always=True)
|
node = tree_name.get_definition(import_name_always=True, include_setitem=True)
|
||||||
if node is None:
|
if node is None:
|
||||||
node = tree_name.parent
|
node = tree_name.parent
|
||||||
if node.type == 'global_stmt':
|
if node.type == 'global_stmt':
|
||||||
@@ -598,8 +706,8 @@ def tree_name_to_values(inference_state, context, tree_name):
|
|||||||
contextualized_node=cn,
|
contextualized_node=cn,
|
||||||
is_async=node.parent.type == 'async_stmt',
|
is_async=node.parent.type == 'async_stmt',
|
||||||
)
|
)
|
||||||
c_node = ContextualizedName(context, tree_name)
|
n = TreeNameDefinition(context, tree_name)
|
||||||
types = check_tuple_assignments(c_node, for_types)
|
types = check_tuple_assignments(n, for_types)
|
||||||
elif typ == 'expr_stmt':
|
elif typ == 'expr_stmt':
|
||||||
types = _remove_statements(context, node, tree_name)
|
types = _remove_statements(context, node, tree_name)
|
||||||
elif typ == 'with_stmt':
|
elif typ == 'with_stmt':
|
||||||
@@ -671,13 +779,13 @@ def _apply_decorators(context, node):
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
def check_tuple_assignments(contextualized_name, value_set):
|
def check_tuple_assignments(name, value_set):
|
||||||
"""
|
"""
|
||||||
Checks if tuples are assigned.
|
Checks if tuples are assigned.
|
||||||
"""
|
"""
|
||||||
lazy_value = None
|
lazy_value = None
|
||||||
for index, node in contextualized_name.assignment_indexes():
|
for index, node in name.assignment_indexes():
|
||||||
cn = ContextualizedNode(contextualized_name.context, node)
|
cn = ContextualizedNode(name.parent_context, node)
|
||||||
iterated = value_set.iterate(cn)
|
iterated = value_set.iterate(cn)
|
||||||
if isinstance(index, slice):
|
if isinstance(index, slice):
|
||||||
# For no star unpacking is not possible.
|
# For no star unpacking is not possible.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import os
|
|||||||
from jedi._compatibility import unicode, force_unicode, all_suffixes
|
from jedi._compatibility import unicode, force_unicode, all_suffixes
|
||||||
from jedi.inference.cache import inference_state_method_cache
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
from jedi.inference.base_value import ContextualizedNode
|
from jedi.inference.base_value import ContextualizedNode
|
||||||
from jedi.inference.helpers import is_string
|
from jedi.inference.helpers import is_string, get_str_or_none
|
||||||
from jedi.common.utils import traverse_parents
|
from jedi.common.utils import traverse_parents
|
||||||
from jedi.parser_utils import get_cached_code_lines
|
from jedi.parser_utils import get_cached_code_lines
|
||||||
from jedi.file_io import FileIO
|
from jedi.file_io import FileIO
|
||||||
@@ -86,8 +86,10 @@ def _paths_from_list_modifications(module_context, trailer1, trailer2):
|
|||||||
arg = arg.children[2]
|
arg = arg.children[2]
|
||||||
|
|
||||||
for value in module_context.create_context(arg).infer_node(arg):
|
for value in module_context.create_context(arg).infer_node(arg):
|
||||||
if is_string(value):
|
p = get_str_or_none(value)
|
||||||
abs_path = _abs_path(module_context, value.get_safe_value())
|
if p is None:
|
||||||
|
continue
|
||||||
|
abs_path = _abs_path(module_context, p)
|
||||||
if abs_path is not None:
|
if abs_path is not None:
|
||||||
yield abs_path
|
yield abs_path
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from jedi.inference import imports
|
from jedi.inference import imports
|
||||||
from jedi.inference.names import TreeNameDefinition
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_names(definition_names, avoid_names=()):
|
def _resolve_names(definition_names, avoid_names=()):
|
||||||
@@ -27,8 +26,7 @@ def _dictionarize(names):
|
|||||||
|
|
||||||
|
|
||||||
def _find_names(module_context, tree_name):
|
def _find_names(module_context, tree_name):
|
||||||
context = module_context.create_context(tree_name)
|
name = module_context.create_name(tree_name)
|
||||||
name = TreeNameDefinition(context, tree_name)
|
|
||||||
found_names = set(name.goto())
|
found_names = set(name.goto())
|
||||||
found_names.add(name)
|
found_names.add(name)
|
||||||
return _dictionarize(_resolve_names(found_names))
|
return _dictionarize(_resolve_names(found_names))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from jedi.inference.value.module import ModuleValue
|
from jedi.inference.value.module import ModuleValue
|
||||||
from jedi.inference.value.klass import ClassValue
|
from jedi.inference.value.klass import ClassValue
|
||||||
from jedi.inference.value.function import FunctionValue, \
|
from jedi.inference.value.function import FunctionValue, \
|
||||||
MethodValue, FunctionExecutionContext
|
MethodValue
|
||||||
from jedi.inference.value.instance import AnonymousInstance, BoundMethod, \
|
from jedi.inference.value.instance import AnonymousInstance, BoundMethod, \
|
||||||
CompiledInstance, AbstractInstanceValue, TreeInstance
|
CompiledInstance, AbstractInstanceValue, TreeInstance
|
||||||
|
|||||||
201
jedi/inference/value/dynamic_arrays.py
Normal file
201
jedi/inference/value/dynamic_arrays.py
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
"""
|
||||||
|
A module to deal with stuff like `list.append` and `set.add`.
|
||||||
|
|
||||||
|
Array modifications
|
||||||
|
*******************
|
||||||
|
|
||||||
|
If the content of an array (``set``/``list``) is requested somewhere, the
|
||||||
|
current module will be checked for appearances of ``arr.append``,
|
||||||
|
``arr.insert``, etc. If the ``arr`` name points to an actual array, the
|
||||||
|
content will be added
|
||||||
|
|
||||||
|
This can be really cpu intensive, as you can imagine. Because |jedi| has to
|
||||||
|
follow **every** ``append`` and check wheter it's the right array. However this
|
||||||
|
works pretty good, because in *slow* cases, the recursion detector and other
|
||||||
|
settings will stop this process.
|
||||||
|
|
||||||
|
It is important to note that:
|
||||||
|
|
||||||
|
1. Array modfications work only in the current module.
|
||||||
|
2. Jedi only checks Array additions; ``list.pop``, etc are ignored.
|
||||||
|
"""
|
||||||
|
from jedi import debug
|
||||||
|
from jedi import settings
|
||||||
|
from jedi.inference import recursion
|
||||||
|
from jedi.inference.base_value import ValueSet, NO_VALUES, HelperValueMixin, \
|
||||||
|
ValueWrapper
|
||||||
|
from jedi.inference.lazy_value import LazyKnownValues
|
||||||
|
from jedi.inference.helpers import infer_call_of_leaf
|
||||||
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
|
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
|
|
||||||
|
def check_array_additions(context, sequence):
|
||||||
|
""" Just a mapper function for the internal _internal_check_array_additions """
|
||||||
|
if sequence.array_type not in ('list', 'set'):
|
||||||
|
# TODO also check for dict updates
|
||||||
|
return NO_VALUES
|
||||||
|
|
||||||
|
return _internal_check_array_additions(context, sequence)
|
||||||
|
|
||||||
|
|
||||||
|
@inference_state_method_cache(default=NO_VALUES)
|
||||||
|
@debug.increase_indent
|
||||||
|
def _internal_check_array_additions(context, sequence):
|
||||||
|
"""
|
||||||
|
Checks if a `Array` has "add" (append, insert, extend) statements:
|
||||||
|
|
||||||
|
>>> a = [""]
|
||||||
|
>>> a.append(1)
|
||||||
|
"""
|
||||||
|
from jedi.inference import arguments
|
||||||
|
|
||||||
|
debug.dbg('Dynamic array search for %s' % sequence, color='MAGENTA')
|
||||||
|
module_context = context.get_root_context()
|
||||||
|
if not settings.dynamic_array_additions or module_context.is_compiled():
|
||||||
|
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
||||||
|
return NO_VALUES
|
||||||
|
|
||||||
|
def find_additions(context, arglist, add_name):
|
||||||
|
params = list(arguments.TreeArguments(context.inference_state, context, arglist).unpack())
|
||||||
|
result = set()
|
||||||
|
if add_name in ['insert']:
|
||||||
|
params = params[1:]
|
||||||
|
if add_name in ['append', 'add', 'insert']:
|
||||||
|
for key, lazy_value in params:
|
||||||
|
result.add(lazy_value)
|
||||||
|
elif add_name in ['extend', 'update']:
|
||||||
|
for key, lazy_value in params:
|
||||||
|
result |= set(lazy_value.infer().iterate())
|
||||||
|
return result
|
||||||
|
|
||||||
|
temp_param_add, settings.dynamic_params_for_other_modules = \
|
||||||
|
settings.dynamic_params_for_other_modules, False
|
||||||
|
|
||||||
|
is_list = sequence.name.string_name == 'list'
|
||||||
|
search_names = (['append', 'extend', 'insert'] if is_list else ['add', 'update'])
|
||||||
|
|
||||||
|
added_types = set()
|
||||||
|
for add_name in search_names:
|
||||||
|
try:
|
||||||
|
possible_names = module_context.tree_node.get_used_names()[add_name]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for name in possible_names:
|
||||||
|
value_node = context.tree_node
|
||||||
|
if not (value_node.start_pos < name.start_pos < value_node.end_pos):
|
||||||
|
continue
|
||||||
|
trailer = name.parent
|
||||||
|
power = trailer.parent
|
||||||
|
trailer_pos = power.children.index(trailer)
|
||||||
|
try:
|
||||||
|
execution_trailer = power.children[trailer_pos + 1]
|
||||||
|
except IndexError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if execution_trailer.type != 'trailer' \
|
||||||
|
or execution_trailer.children[0] != '(' \
|
||||||
|
or execution_trailer.children[1] == ')':
|
||||||
|
continue
|
||||||
|
|
||||||
|
random_context = context.create_context(name)
|
||||||
|
|
||||||
|
with recursion.execution_allowed(context.inference_state, power) as allowed:
|
||||||
|
if allowed:
|
||||||
|
found = infer_call_of_leaf(
|
||||||
|
random_context,
|
||||||
|
name,
|
||||||
|
cut_own_trailer=True
|
||||||
|
)
|
||||||
|
if sequence in found:
|
||||||
|
# The arrays match. Now add the results
|
||||||
|
added_types |= find_additions(
|
||||||
|
random_context,
|
||||||
|
execution_trailer.children[1],
|
||||||
|
add_name
|
||||||
|
)
|
||||||
|
|
||||||
|
# reset settings
|
||||||
|
settings.dynamic_params_for_other_modules = temp_param_add
|
||||||
|
debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA')
|
||||||
|
return added_types
|
||||||
|
|
||||||
|
|
||||||
|
def get_dynamic_array_instance(instance, arguments):
|
||||||
|
"""Used for set() and list() instances."""
|
||||||
|
ai = _DynamicArrayAdditions(instance, arguments)
|
||||||
|
from jedi.inference import arguments
|
||||||
|
return arguments.ValuesArguments([ValueSet([ai])])
|
||||||
|
|
||||||
|
|
||||||
|
class _DynamicArrayAdditions(HelperValueMixin):
|
||||||
|
"""
|
||||||
|
Used for the usage of set() and list().
|
||||||
|
This is definitely a hack, but a good one :-)
|
||||||
|
It makes it possible to use set/list conversions.
|
||||||
|
|
||||||
|
This is not a proper context, because it doesn't have to be. It's not used
|
||||||
|
in the wild, it's just used within typeshed as an argument to `__init__`
|
||||||
|
for set/list and never used in any other place.
|
||||||
|
"""
|
||||||
|
def __init__(self, instance, arguments):
|
||||||
|
self._instance = instance
|
||||||
|
self._arguments = arguments
|
||||||
|
|
||||||
|
def py__class__(self):
|
||||||
|
tuple_, = self._instance.inference_state.builtins_module.py__getattribute__('tuple')
|
||||||
|
return tuple_
|
||||||
|
|
||||||
|
def py__iter__(self, contextualized_node=None):
|
||||||
|
arguments = self._arguments
|
||||||
|
try:
|
||||||
|
_, lazy_value = next(arguments.unpack())
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for lazy in lazy_value.infer().iterate():
|
||||||
|
yield lazy
|
||||||
|
|
||||||
|
from jedi.inference.arguments import TreeArguments
|
||||||
|
if isinstance(arguments, TreeArguments):
|
||||||
|
additions = _internal_check_array_additions(arguments.context, self._instance)
|
||||||
|
for addition in additions:
|
||||||
|
yield addition
|
||||||
|
|
||||||
|
def iterate(self, contextualized_node=None, is_async=False):
|
||||||
|
return self.py__iter__(contextualized_node)
|
||||||
|
|
||||||
|
|
||||||
|
class _Modification(ValueWrapper):
|
||||||
|
def __init__(self, wrapped_value, assigned_values, contextualized_key):
|
||||||
|
super(_Modification, self).__init__(wrapped_value)
|
||||||
|
self._assigned_values = assigned_values
|
||||||
|
self._contextualized_key = contextualized_key
|
||||||
|
|
||||||
|
def py__getitem__(self, *args, **kwargs):
|
||||||
|
return self._wrapped_value.py__getitem__(*args, **kwargs) | self._assigned_values
|
||||||
|
|
||||||
|
def py__simple_getitem__(self, index):
|
||||||
|
actual = [
|
||||||
|
v.get_safe_value(_sentinel)
|
||||||
|
for v in self._contextualized_key.infer()
|
||||||
|
]
|
||||||
|
if index in actual:
|
||||||
|
return self._assigned_values
|
||||||
|
return self._wrapped_value.py__simple_getitem__(index)
|
||||||
|
|
||||||
|
|
||||||
|
class DictModification(_Modification):
|
||||||
|
def py__iter__(self):
|
||||||
|
for lazy_context in self._wrapped_value.py__iter__():
|
||||||
|
yield lazy_context
|
||||||
|
yield self._contextualized_key
|
||||||
|
|
||||||
|
|
||||||
|
class ListModification(_Modification):
|
||||||
|
def py__iter__(self):
|
||||||
|
for lazy_context in self._wrapped_value.py__iter__():
|
||||||
|
yield lazy_context
|
||||||
|
yield LazyKnownValues(self._assigned_values)
|
||||||
@@ -8,9 +8,10 @@ from jedi.inference import recursion
|
|||||||
from jedi.inference import docstrings
|
from jedi.inference import docstrings
|
||||||
from jedi.inference import flow_analysis
|
from jedi.inference import flow_analysis
|
||||||
from jedi.inference.signature import TreeSignature
|
from jedi.inference.signature import TreeSignature
|
||||||
from jedi.inference.arguments import AnonymousArguments
|
from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
||||||
from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter
|
AnonymousFunctionExecutionFilter
|
||||||
from jedi.inference.names import ValueName, AbstractNameDefinition, ParamName
|
from jedi.inference.names import ValueName, AbstractNameDefinition, \
|
||||||
|
AnonymousParamName, ParamName
|
||||||
from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \
|
from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \
|
||||||
ValueSet, TreeValue, ValueWrapper
|
ValueSet, TreeValue, ValueWrapper
|
||||||
from jedi.inference.lazy_value import LazyKnownValues, LazyKnownValue, \
|
from jedi.inference.lazy_value import LazyKnownValues, LazyKnownValue, \
|
||||||
@@ -69,8 +70,7 @@ class FunctionMixin(object):
|
|||||||
return ValueSet([BoundMethod(instance, self)])
|
return ValueSet([BoundMethod(instance, self)])
|
||||||
|
|
||||||
def get_param_names(self):
|
def get_param_names(self):
|
||||||
function_execution = self.as_context()
|
return [AnonymousParamName(self, param.name)
|
||||||
return [ParamName(function_execution, param.name)
|
|
||||||
for param in self.tree_node.get_params()]
|
for param in self.tree_node.get_params()]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -88,7 +88,7 @@ class FunctionMixin(object):
|
|||||||
|
|
||||||
def _as_context(self, arguments=None):
|
def _as_context(self, arguments=None):
|
||||||
if arguments is None:
|
if arguments is None:
|
||||||
arguments = AnonymousArguments()
|
return AnonymousFunctionExecution(self)
|
||||||
return FunctionExecutionContext(self, arguments)
|
return FunctionExecutionContext(self, arguments)
|
||||||
|
|
||||||
def get_signatures(self):
|
def get_signatures(self):
|
||||||
@@ -127,7 +127,8 @@ class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndCla
|
|||||||
if overloaded_funcs:
|
if overloaded_funcs:
|
||||||
return OverloadedFunctionValue(
|
return OverloadedFunctionValue(
|
||||||
function,
|
function,
|
||||||
[create(f) for f in overloaded_funcs]
|
# Get them into the correct order: lower line first.
|
||||||
|
list(reversed([create(f) for f in overloaded_funcs]))
|
||||||
)
|
)
|
||||||
return function
|
return function
|
||||||
|
|
||||||
@@ -159,13 +160,9 @@ class MethodValue(FunctionValue):
|
|||||||
return names + (self.py__name__(),)
|
return names + (self.py__name__(),)
|
||||||
|
|
||||||
|
|
||||||
class FunctionExecutionContext(ValueContext, TreeContextMixin):
|
class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
|
||||||
function_execution_filter = FunctionExecutionFilter
|
def _infer_annotations(self):
|
||||||
|
raise NotImplementedError
|
||||||
def __init__(self, function_value, var_args):
|
|
||||||
super(FunctionExecutionContext, self).__init__(function_value)
|
|
||||||
self.function_value = function_value
|
|
||||||
self.var_args = var_args
|
|
||||||
|
|
||||||
@inference_state_method_cache(default=NO_VALUES)
|
@inference_state_method_cache(default=NO_VALUES)
|
||||||
@recursion.execution_recursion_decorator()
|
@recursion.execution_recursion_decorator()
|
||||||
@@ -178,14 +175,13 @@ class FunctionExecutionContext(ValueContext, TreeContextMixin):
|
|||||||
value_set = NO_VALUES
|
value_set = NO_VALUES
|
||||||
returns = get_yield_exprs(self.inference_state, funcdef)
|
returns = get_yield_exprs(self.inference_state, funcdef)
|
||||||
else:
|
else:
|
||||||
returns = funcdef.iter_return_stmts()
|
value_set = self._infer_annotations()
|
||||||
from jedi.inference.gradual.annotation import infer_return_types
|
|
||||||
value_set = infer_return_types(self)
|
|
||||||
if value_set:
|
if value_set:
|
||||||
# If there are annotations, prefer them over anything else.
|
# If there are annotations, prefer them over anything else.
|
||||||
# This will make it faster.
|
# This will make it faster.
|
||||||
return value_set
|
return value_set
|
||||||
value_set |= docstrings.infer_return_types(self.function_value)
|
value_set |= docstrings.infer_return_types(self._value)
|
||||||
|
returns = funcdef.iter_return_stmts()
|
||||||
|
|
||||||
for r in returns:
|
for r in returns:
|
||||||
check = flow_analysis.reachability_check(self, funcdef, r)
|
check = flow_analysis.reachability_check(self, funcdef, r)
|
||||||
@@ -280,32 +276,6 @@ class FunctionExecutionContext(ValueContext, TreeContextMixin):
|
|||||||
for lazy_value in self.get_yield_lazy_values()
|
for lazy_value in self.get_yield_lazy_values()
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_filters(self, until_position=None, origin_scope=None):
|
|
||||||
yield self.function_execution_filter(self,
|
|
||||||
until_position=until_position,
|
|
||||||
origin_scope=origin_scope)
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
|
||||||
def get_executed_param_names_and_issues(self):
|
|
||||||
return self.var_args.get_executed_param_names_and_issues(self)
|
|
||||||
|
|
||||||
def matches_signature(self):
|
|
||||||
executed_param_names, issues = self.get_executed_param_names_and_issues()
|
|
||||||
if issues:
|
|
||||||
return False
|
|
||||||
|
|
||||||
matches = all(executed_param_name.matches_signature()
|
|
||||||
for executed_param_name in executed_param_names)
|
|
||||||
if debug.enable_notice:
|
|
||||||
signature = parser_utils.get_call_signature(self.tree_node)
|
|
||||||
if matches:
|
|
||||||
debug.dbg("Overloading match: %s@%s (%s)",
|
|
||||||
signature, self.tree_node.start_pos[0], self.var_args, color='BLUE')
|
|
||||||
else:
|
|
||||||
debug.dbg("Overloading no match: %s@%s (%s)",
|
|
||||||
signature, self.tree_node.start_pos[0], self.var_args, color='BLUE')
|
|
||||||
return matches
|
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
"""
|
"""
|
||||||
Created to be used by inheritance.
|
Created to be used by inheritance.
|
||||||
@@ -347,6 +317,47 @@ class FunctionExecutionContext(ValueContext, TreeContextMixin):
|
|||||||
return self.get_return_values()
|
return self.get_return_values()
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionExecutionContext(BaseFunctionExecutionContext):
|
||||||
|
def __init__(self, function_value, arguments):
|
||||||
|
super(FunctionExecutionContext, self).__init__(function_value)
|
||||||
|
self._arguments = arguments
|
||||||
|
|
||||||
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
|
yield FunctionExecutionFilter(
|
||||||
|
self, self._value,
|
||||||
|
until_position=until_position,
|
||||||
|
origin_scope=origin_scope,
|
||||||
|
arguments=self._arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
def _infer_annotations(self):
|
||||||
|
from jedi.inference.gradual.annotation import infer_return_types
|
||||||
|
return infer_return_types(self._value, self._arguments)
|
||||||
|
|
||||||
|
def get_param_names(self):
|
||||||
|
return [
|
||||||
|
ParamName(self._value, param.name, self._arguments)
|
||||||
|
for param in self._value.tree_node.get_params()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousFunctionExecution(BaseFunctionExecutionContext):
|
||||||
|
def _infer_annotations(self):
|
||||||
|
# I don't think inferring anonymous executions is a big thing.
|
||||||
|
# Anonymous contexts are mostly there for the user to work in. ~ dave
|
||||||
|
return NO_VALUES
|
||||||
|
|
||||||
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
|
yield AnonymousFunctionExecutionFilter(
|
||||||
|
self, self._value,
|
||||||
|
until_position=until_position,
|
||||||
|
origin_scope=origin_scope,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_param_names(self):
|
||||||
|
return self._value.get_param_names()
|
||||||
|
|
||||||
|
|
||||||
class OverloadedFunctionValue(FunctionMixin, ValueWrapper):
|
class OverloadedFunctionValue(FunctionMixin, ValueWrapper):
|
||||||
def __init__(self, function, overloaded_functions):
|
def __init__(self, function, overloaded_functions):
|
||||||
super(OverloadedFunctionValue, self).__init__(function)
|
super(OverloadedFunctionValue, self).__init__(function)
|
||||||
@@ -355,18 +366,12 @@ class OverloadedFunctionValue(FunctionMixin, ValueWrapper):
|
|||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
debug.dbg("Execute overloaded function %s", self._wrapped_value, color='BLUE')
|
debug.dbg("Execute overloaded function %s", self._wrapped_value, color='BLUE')
|
||||||
function_executions = []
|
function_executions = []
|
||||||
value_set = NO_VALUES
|
for signature in self.get_signatures():
|
||||||
matched = False
|
function_execution = signature.value.as_context(arguments)
|
||||||
for f in self._overloaded_functions:
|
|
||||||
function_execution = f.as_context(arguments)
|
|
||||||
function_executions.append(function_execution)
|
function_executions.append(function_execution)
|
||||||
if function_execution.matches_signature():
|
if signature.matches_signature(arguments):
|
||||||
matched = True
|
|
||||||
return function_execution.infer()
|
return function_execution.infer()
|
||||||
|
|
||||||
if matched:
|
|
||||||
return value_set
|
|
||||||
|
|
||||||
if self.inference_state.is_analysis:
|
if self.inference_state.is_analysis:
|
||||||
# In this case we want precision.
|
# In this case we want precision.
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|||||||
@@ -1,29 +1,30 @@
|
|||||||
from abc import abstractproperty
|
from abc import abstractproperty
|
||||||
|
|
||||||
|
from parso.python.tree import search_ancestor
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.inference import compiled
|
from jedi.inference import compiled
|
||||||
from jedi.inference.compiled.value import CompiledObjectFilter
|
from jedi.inference.compiled.value import CompiledObjectFilter
|
||||||
from jedi.inference.helpers import values_from_qualified_names
|
from jedi.inference.helpers import values_from_qualified_names
|
||||||
from jedi.inference.filters import AbstractFilter
|
from jedi.inference.filters import AbstractFilter, AnonymousFunctionExecutionFilter
|
||||||
from jedi.inference.names import ValueName, TreeNameDefinition, ParamName
|
from jedi.inference.names import ValueName, TreeNameDefinition, ParamName
|
||||||
from jedi.inference.base_value import Value, NO_VALUES, ValueSet, \
|
from jedi.inference.base_value import Value, NO_VALUES, ValueSet, \
|
||||||
iterator_to_value_set, ValueWrapper
|
iterator_to_value_set, ValueWrapper
|
||||||
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues
|
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues
|
||||||
from jedi.inference.cache import inference_state_method_cache
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
from jedi.inference.arguments import AnonymousArguments, \
|
from jedi.inference.arguments import ValuesArguments, TreeArgumentsWrapper
|
||||||
ValuesArguments, TreeArgumentsWrapper
|
|
||||||
from jedi.inference.value.function import \
|
from jedi.inference.value.function import \
|
||||||
FunctionValue, FunctionMixin, OverloadedFunctionValue
|
FunctionValue, FunctionMixin, OverloadedFunctionValue, \
|
||||||
from jedi.inference.value.klass import ClassValue, apply_py__get__, \
|
BaseFunctionExecutionContext, FunctionExecutionContext
|
||||||
ClassFilter
|
from jedi.inference.value.klass import apply_py__get__, ClassFilter
|
||||||
from jedi.inference.value import iterable
|
from jedi.inference.value.dynamic_arrays import get_dynamic_array_instance
|
||||||
from jedi.parser_utils import get_parent_scope
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceExecutedParamName(ParamName):
|
class InstanceExecutedParamName(ParamName):
|
||||||
def __init__(self, instance, execution_context, tree_param):
|
def __init__(self, instance, function_value, tree_name):
|
||||||
super(InstanceExecutedParamName, self).__init__(execution_context, tree_param.name)
|
super(InstanceExecutedParamName, self).__init__(
|
||||||
|
function_value, tree_name, arguments=None)
|
||||||
self._instance = instance
|
self._instance = instance
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
@@ -33,40 +34,54 @@ class InstanceExecutedParamName(ParamName):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AnonymousInstanceArguments(AnonymousArguments):
|
class AnonymousMethodExecutionFilter(AnonymousFunctionExecutionFilter):
|
||||||
def __init__(self, instance):
|
def __init__(self, instance, *args, **kwargs):
|
||||||
|
super(AnonymousMethodExecutionFilter, self).__init__(*args, **kwargs)
|
||||||
self._instance = instance
|
self._instance = instance
|
||||||
|
|
||||||
def get_executed_param_names_and_issues(self, execution_context):
|
def _convert_param(self, param, name):
|
||||||
from jedi.inference.dynamic import search_param_names
|
if param.position_index == 0:
|
||||||
tree_params = execution_context.tree_node.get_params()
|
return InstanceExecutedParamName(self._instance, self._function_value, name)
|
||||||
if not tree_params:
|
return super(AnonymousMethodExecutionFilter, self)._convert_param(param, name)
|
||||||
return [], []
|
|
||||||
|
|
||||||
self_param = InstanceExecutedParamName(
|
|
||||||
self._instance, execution_context, tree_params[0])
|
class AnonymousMethodExecutionContext(BaseFunctionExecutionContext):
|
||||||
if len(tree_params) == 1:
|
def __init__(self, instance, value):
|
||||||
# If the only param is self, we don't need to try to find
|
super(AnonymousMethodExecutionContext, self).__init__(value)
|
||||||
# executions of this function, we have all the params already.
|
self.instance = instance
|
||||||
return [self_param], []
|
|
||||||
executed_param_names = list(search_param_names(
|
def get_filters(self, until_position=None, origin_scope=None):
|
||||||
execution_context.inference_state,
|
yield AnonymousMethodExecutionFilter(
|
||||||
execution_context,
|
self.instance, self, self._value,
|
||||||
execution_context.tree_node
|
until_position=until_position,
|
||||||
))
|
origin_scope=origin_scope,
|
||||||
executed_param_names[0] = self_param
|
)
|
||||||
return executed_param_names, []
|
|
||||||
|
def get_param_names(self):
|
||||||
|
param_names = list(self._value.get_param_names())
|
||||||
|
# set the self name
|
||||||
|
param_names[0] = InstanceExecutedParamName(
|
||||||
|
self.instance,
|
||||||
|
self._function_value,
|
||||||
|
param_names[0].tree_name
|
||||||
|
)
|
||||||
|
return param_names
|
||||||
|
|
||||||
|
|
||||||
|
class MethodExecutionContext(FunctionExecutionContext):
|
||||||
|
def __init__(self, instance, *args, **kwargs):
|
||||||
|
super(MethodExecutionContext, self).__init__(*args, **kwargs)
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
|
||||||
class AbstractInstanceValue(Value):
|
class AbstractInstanceValue(Value):
|
||||||
api_type = u'instance'
|
api_type = u'instance'
|
||||||
|
|
||||||
def __init__(self, inference_state, parent_context, class_value, var_args):
|
def __init__(self, inference_state, parent_context, class_value):
|
||||||
super(AbstractInstanceValue, self).__init__(inference_state, parent_context)
|
super(AbstractInstanceValue, self).__init__(inference_state, parent_context)
|
||||||
# Generated instances are classes that are just generated by self
|
# Generated instances are classes that are just generated by self
|
||||||
# (No var_args) used.
|
# (No arguments) used.
|
||||||
self.class_value = class_value
|
self.class_value = class_value
|
||||||
self.var_args = var_args
|
|
||||||
|
|
||||||
def is_instance(self):
|
def is_instance(self):
|
||||||
return True
|
return True
|
||||||
@@ -77,14 +92,6 @@ class AbstractInstanceValue(Value):
|
|||||||
def get_annotated_class_object(self):
|
def get_annotated_class_object(self):
|
||||||
return self.class_value # This is the default.
|
return self.class_value # This is the default.
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
|
||||||
names = self.get_function_slot_names(u'__call__')
|
|
||||||
if not names:
|
|
||||||
# Means the Instance is not callable.
|
|
||||||
return super(AbstractInstanceValue, self).py__call__(arguments)
|
|
||||||
|
|
||||||
return ValueSet.from_sets(name.infer().execute(arguments) for name in names)
|
|
||||||
|
|
||||||
def py__class__(self):
|
def py__class__(self):
|
||||||
return self.class_value
|
return self.class_value
|
||||||
|
|
||||||
@@ -92,42 +99,62 @@ class AbstractInstanceValue(Value):
|
|||||||
# Signalize that we don't know about the bool type.
|
# Signalize that we don't know about the bool type.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_function_slot_names(self, name):
|
@abstractproperty
|
||||||
# Python classes don't look at the dictionary of the instance when
|
def name(self):
|
||||||
# looking up `__call__`. This is something that has to do with Python's
|
raise NotImplementedError
|
||||||
# internal slot system (note: not __slots__, but C slots).
|
|
||||||
for filter in self.get_filters(include_self_names=False):
|
|
||||||
names = filter.get(name)
|
|
||||||
if names:
|
|
||||||
return names
|
|
||||||
return []
|
|
||||||
|
|
||||||
def execute_function_slots(self, names, *inferred_args):
|
def get_signatures(self):
|
||||||
return ValueSet.from_sets(
|
call_funcs = self.py__getattribute__('__call__').py__get__(self, self.class_value)
|
||||||
name.infer().execute_with_values(*inferred_args)
|
return [s.bind(self) for s in call_funcs.get_signatures()]
|
||||||
for name in names
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s of %s>" % (self.__class__.__name__, self.class_value)
|
||||||
|
|
||||||
|
|
||||||
|
class CompiledInstance(AbstractInstanceValue):
|
||||||
|
def __init__(self, inference_state, parent_context, class_value, arguments):
|
||||||
|
super(CompiledInstance, self).__init__(inference_state, parent_context,
|
||||||
|
class_value)
|
||||||
|
self._arguments = arguments
|
||||||
|
|
||||||
|
def get_filters(self, origin_scope=None, include_self_names=True):
|
||||||
|
class_value = self.get_annotated_class_object()
|
||||||
|
class_filters = class_value.get_filters(
|
||||||
|
origin_scope=origin_scope,
|
||||||
|
is_instance=True,
|
||||||
)
|
)
|
||||||
|
for f in class_filters:
|
||||||
|
yield CompiledInstanceClassFilter(self, f)
|
||||||
|
|
||||||
def py__get__(self, obj, class_value):
|
@property
|
||||||
"""
|
def name(self):
|
||||||
obj may be None.
|
return compiled.CompiledValueName(self, self.class_value.name.string_name)
|
||||||
"""
|
|
||||||
# Arguments in __get__ descriptors are obj, class.
|
def is_compiled(self):
|
||||||
# `method` is the new parent of the array, don't know if that's good.
|
return True
|
||||||
names = self.get_function_slot_names(u'__get__')
|
|
||||||
if names:
|
def is_stub(self):
|
||||||
if obj is None:
|
return False
|
||||||
obj = compiled.builtin_from_name(self.inference_state, u'None')
|
|
||||||
return self.execute_function_slots(names, obj, class_value)
|
|
||||||
else:
|
class _BaseTreeInstance(AbstractInstanceValue):
|
||||||
return ValueSet([self])
|
@property
|
||||||
|
def array_type(self):
|
||||||
|
name = self.class_value.py__name__()
|
||||||
|
if name in ['list', 'set', 'dict'] \
|
||||||
|
and self.parent_context.get_root_context().is_builtins_module():
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return ValueName(self, self.class_value.name.tree_name)
|
||||||
|
|
||||||
def get_filters(self, origin_scope=None, include_self_names=True):
|
def get_filters(self, origin_scope=None, include_self_names=True):
|
||||||
class_value = self.get_annotated_class_object()
|
class_value = self.get_annotated_class_object()
|
||||||
if include_self_names:
|
if include_self_names:
|
||||||
for cls in class_value.py__mro__():
|
for cls in class_value.py__mro__():
|
||||||
if not isinstance(cls, compiled.CompiledObject) \
|
if not cls.is_compiled():
|
||||||
or cls.tree_node is not None:
|
|
||||||
# In this case we're excluding compiled objects that are
|
# In this case we're excluding compiled objects that are
|
||||||
# not fake objects. It doesn't make sense for normal
|
# not fake objects. It doesn't make sense for normal
|
||||||
# compiled objects to search for self variables.
|
# compiled objects to search for self variables.
|
||||||
@@ -146,6 +173,52 @@ class AbstractInstanceValue(Value):
|
|||||||
# Propably from the metaclass.
|
# Propably from the metaclass.
|
||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
def _get_annotation_init_functions(self):
|
||||||
|
filter = next(self.class_value.get_filters())
|
||||||
|
for init_name in filter.get('__init__'):
|
||||||
|
for init in init_name.infer():
|
||||||
|
if init.is_function():
|
||||||
|
for signature in init.get_signatures():
|
||||||
|
yield signature.value
|
||||||
|
|
||||||
|
@inference_state_method_cache()
|
||||||
|
def create_instance_context(self, class_context, node):
|
||||||
|
new = node
|
||||||
|
while True:
|
||||||
|
func_node = new
|
||||||
|
new = search_ancestor(new, 'funcdef', 'classdef')
|
||||||
|
if class_context.tree_node is new:
|
||||||
|
func = FunctionValue.from_context(class_context, func_node)
|
||||||
|
bound_method = BoundMethod(self, func)
|
||||||
|
if func_node.name.value == '__init__':
|
||||||
|
context = bound_method.as_context(self._arguments)
|
||||||
|
else:
|
||||||
|
context = bound_method.as_context()
|
||||||
|
break
|
||||||
|
return context.create_context(node)
|
||||||
|
|
||||||
|
def py__getattribute__alternatives(self, string_name):
|
||||||
|
'''
|
||||||
|
Since nothing was inferred, now check the __getattr__ and
|
||||||
|
__getattribute__ methods. Stubs don't need to be checked, because
|
||||||
|
they don't contain any logic.
|
||||||
|
'''
|
||||||
|
if self.is_stub():
|
||||||
|
return NO_VALUES
|
||||||
|
|
||||||
|
name = compiled.create_simple_object(self.inference_state, string_name)
|
||||||
|
|
||||||
|
# This is a little bit special. `__getattribute__` is in Python
|
||||||
|
# executed before `__getattr__`. But: I know no use case, where
|
||||||
|
# this could be practical and where Jedi would return wrong types.
|
||||||
|
# If you ever find something, let me know!
|
||||||
|
# We are inversing this, because a hand-crafted `__getattribute__`
|
||||||
|
# could still call another hand-crafted `__getattr__`, but not the
|
||||||
|
# other way around.
|
||||||
|
names = (self.get_function_slot_names(u'__getattr__') or
|
||||||
|
self.get_function_slot_names(u'__getattribute__'))
|
||||||
|
return self.execute_function_slots(names, name)
|
||||||
|
|
||||||
def py__getitem__(self, index_value_set, contextualized_node):
|
def py__getitem__(self, index_value_set, contextualized_node):
|
||||||
names = self.get_function_slot_names(u'__getitem__')
|
names = self.get_function_slot_names(u'__getitem__')
|
||||||
if not names:
|
if not names:
|
||||||
@@ -182,97 +255,60 @@ class AbstractInstanceValue(Value):
|
|||||||
yield lazy_value
|
yield lazy_value
|
||||||
return iterate()
|
return iterate()
|
||||||
|
|
||||||
@abstractproperty
|
def py__call__(self, arguments):
|
||||||
def name(self):
|
names = self.get_function_slot_names(u'__call__')
|
||||||
pass
|
if not names:
|
||||||
|
# Means the Instance is not callable.
|
||||||
|
return super(AbstractInstanceValue, self).py__call__(arguments)
|
||||||
|
|
||||||
def create_init_executions(self):
|
return ValueSet.from_sets(name.infer().execute(arguments) for name in names)
|
||||||
for name in self.get_function_slot_names(u'__init__'):
|
|
||||||
# TODO is this correct? I think we need to check for functions.
|
def py__get__(self, obj, class_value):
|
||||||
if isinstance(name, LazyInstanceClassName):
|
"""
|
||||||
function = FunctionValue.from_context(
|
obj may be None.
|
||||||
self.parent_context,
|
"""
|
||||||
name.tree_name.parent
|
# Arguments in __get__ descriptors are obj, class.
|
||||||
|
# `method` is the new parent of the array, don't know if that's good.
|
||||||
|
names = self.get_function_slot_names(u'__get__')
|
||||||
|
if names:
|
||||||
|
if obj is None:
|
||||||
|
obj = compiled.builtin_from_name(self.inference_state, u'None')
|
||||||
|
return self.execute_function_slots(names, obj, class_value)
|
||||||
|
else:
|
||||||
|
return ValueSet([self])
|
||||||
|
|
||||||
|
def get_function_slot_names(self, name):
|
||||||
|
# Python classes don't look at the dictionary of the instance when
|
||||||
|
# looking up `__call__`. This is something that has to do with Python's
|
||||||
|
# internal slot system (note: not __slots__, but C slots).
|
||||||
|
for filter in self.get_filters(include_self_names=False):
|
||||||
|
names = filter.get(name)
|
||||||
|
if names:
|
||||||
|
return names
|
||||||
|
return []
|
||||||
|
|
||||||
|
def execute_function_slots(self, names, *inferred_args):
|
||||||
|
return ValueSet.from_sets(
|
||||||
|
name.infer().execute_with_values(*inferred_args)
|
||||||
|
for name in names
|
||||||
)
|
)
|
||||||
bound_method = BoundMethod(self, function)
|
|
||||||
yield bound_method.as_context(self.var_args)
|
|
||||||
|
|
||||||
@inference_state_method_cache()
|
|
||||||
def create_instance_context(self, class_context, node):
|
|
||||||
if node.parent.type in ('funcdef', 'classdef'):
|
|
||||||
node = node.parent
|
|
||||||
scope = get_parent_scope(node)
|
|
||||||
if scope == class_context.tree_node:
|
|
||||||
return class_context
|
|
||||||
else:
|
|
||||||
parent_context = self.create_instance_context(class_context, scope)
|
|
||||||
if scope.type == 'funcdef':
|
|
||||||
func = FunctionValue.from_context(
|
|
||||||
parent_context,
|
|
||||||
scope,
|
|
||||||
)
|
|
||||||
bound_method = BoundMethod(self, func)
|
|
||||||
if scope.name.value == '__init__' and parent_context == class_context:
|
|
||||||
return bound_method.as_context(self.var_args)
|
|
||||||
else:
|
|
||||||
return bound_method.as_context()
|
|
||||||
elif scope.type == 'classdef':
|
|
||||||
class_context = ClassValue(self.inference_state, parent_context, scope)
|
|
||||||
return class_context.as_context()
|
|
||||||
elif scope.type in ('comp_for', 'sync_comp_for'):
|
|
||||||
# Comprehensions currently don't have a special scope in Jedi.
|
|
||||||
return self.create_instance_context(class_context, scope)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
return class_context
|
|
||||||
|
|
||||||
def get_signatures(self):
|
|
||||||
call_funcs = self.py__getattribute__('__call__').py__get__(self, self.class_value)
|
|
||||||
return [s.bind(self) for s in call_funcs.get_signatures()]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value,
|
|
||||||
self.var_args)
|
|
||||||
|
|
||||||
|
|
||||||
class CompiledInstance(AbstractInstanceValue):
|
class TreeInstance(_BaseTreeInstance):
|
||||||
def __init__(self, inference_state, parent_context, class_value, var_args):
|
def __init__(self, inference_state, parent_context, class_value, arguments):
|
||||||
self._original_var_args = var_args
|
|
||||||
super(CompiledInstance, self).__init__(inference_state, parent_context, class_value, var_args)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return compiled.CompiledValueName(self, self.class_value.name.string_name)
|
|
||||||
|
|
||||||
def get_first_non_keyword_argument_values(self):
|
|
||||||
key, lazy_value = next(self._original_var_args.unpack(), ('', None))
|
|
||||||
if key is not None:
|
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
return lazy_value.infer()
|
|
||||||
|
|
||||||
def is_stub(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class TreeInstance(AbstractInstanceValue):
|
|
||||||
def __init__(self, inference_state, parent_context, class_value, var_args):
|
|
||||||
# I don't think that dynamic append lookups should happen here. That
|
# I don't think that dynamic append lookups should happen here. That
|
||||||
# sounds more like something that should go to py__iter__.
|
# sounds more like something that should go to py__iter__.
|
||||||
if class_value.py__name__() in ['list', 'set'] \
|
if class_value.py__name__() in ['list', 'set'] \
|
||||||
and parent_context.get_root_context().is_builtins_module():
|
and parent_context.get_root_context().is_builtins_module():
|
||||||
# compare the module path with the builtin name.
|
# compare the module path with the builtin name.
|
||||||
if settings.dynamic_array_additions:
|
if settings.dynamic_array_additions:
|
||||||
var_args = iterable.get_dynamic_array_instance(self, var_args)
|
arguments = get_dynamic_array_instance(self, arguments)
|
||||||
|
|
||||||
super(TreeInstance, self).__init__(inference_state, parent_context,
|
super(_BaseTreeInstance, self).__init__(inference_state, parent_context,
|
||||||
class_value, var_args)
|
class_value)
|
||||||
|
self._arguments = arguments
|
||||||
self.tree_node = class_value.tree_node
|
self.tree_node = class_value.tree_node
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return ValueName(self, self.class_value.name.tree_name)
|
|
||||||
|
|
||||||
# This can recurse, if the initialization of the class includes a reference
|
# This can recurse, if the initialization of the class includes a reference
|
||||||
# to itself.
|
# to itself.
|
||||||
@inference_state_method_cache(default=None)
|
@inference_state_method_cache(default=None)
|
||||||
@@ -280,19 +316,20 @@ class TreeInstance(AbstractInstanceValue):
|
|||||||
from jedi.inference.gradual.annotation import py__annotations__, \
|
from jedi.inference.gradual.annotation import py__annotations__, \
|
||||||
infer_type_vars_for_execution
|
infer_type_vars_for_execution
|
||||||
|
|
||||||
for func in self._get_annotation_init_functions():
|
args = InstanceArguments(self, self._arguments)
|
||||||
|
for signature in self.class_value.py__getattribute__('__init__').get_signatures():
|
||||||
# Just take the first result, it should always be one, because we
|
# Just take the first result, it should always be one, because we
|
||||||
# control the typeshed code.
|
# control the typeshed code.
|
||||||
bound = BoundMethod(self, func)
|
if not signature.matches_signature(args):
|
||||||
execution = bound.as_context(self.var_args)
|
|
||||||
if not execution.matches_signature():
|
|
||||||
# First check if the signature even matches, if not we don't
|
# First check if the signature even matches, if not we don't
|
||||||
# need to infer anything.
|
# need to infer anything.
|
||||||
continue
|
continue
|
||||||
|
bound_method = BoundMethod(self, signature.value)
|
||||||
all_annotations = py__annotations__(execution.tree_node)
|
all_annotations = py__annotations__(signature.value.tree_node)
|
||||||
|
type_var_dict = infer_type_vars_for_execution(bound_method, args, all_annotations)
|
||||||
|
if type_var_dict:
|
||||||
defined, = self.class_value.define_generics(
|
defined, = self.class_value.define_generics(
|
||||||
infer_type_vars_for_execution(execution, all_annotations),
|
infer_type_vars_for_execution(signature.value, args, all_annotations),
|
||||||
)
|
)
|
||||||
debug.dbg('Inferred instance value as %s', defined, color='BLUE')
|
debug.dbg('Inferred instance value as %s', defined, color='BLUE')
|
||||||
return defined
|
return defined
|
||||||
@@ -301,52 +338,39 @@ class TreeInstance(AbstractInstanceValue):
|
|||||||
def get_annotated_class_object(self):
|
def get_annotated_class_object(self):
|
||||||
return self._get_annotated_class_object() or self.class_value
|
return self._get_annotated_class_object() or self.class_value
|
||||||
|
|
||||||
def _get_annotation_init_functions(self):
|
def py__simple_getitem__(self, index):
|
||||||
filter = next(self.class_value.get_filters())
|
if self.array_type == 'dict':
|
||||||
for init_name in filter.get('__init__'):
|
# Logic for dict({'foo': bar}) and dict(foo=bar)
|
||||||
for init in init_name.infer():
|
# reversed, because:
|
||||||
if init.is_function():
|
# >>> dict({'a': 1}, a=3)
|
||||||
for signature in init.get_signatures():
|
# {'a': 3}
|
||||||
yield signature.value
|
# TODO tuple initializations
|
||||||
|
# >>> dict([('a', 4)])
|
||||||
def py__getattribute__alternatives(self, string_name):
|
# {'a': 4}
|
||||||
'''
|
for key, lazy_context in reversed(list(self._arguments.unpack())):
|
||||||
Since nothing was inferred, now check the __getattr__ and
|
if key is None:
|
||||||
__getattribute__ methods. Stubs don't need to be checked, because
|
values = ValueSet.from_sets(
|
||||||
they don't contain any logic.
|
dct_value.py__simple_getitem__(index)
|
||||||
'''
|
for dct_value in lazy_context.infer()
|
||||||
if self.is_stub():
|
if dct_value.array_type == 'dict'
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
name = compiled.create_simple_object(self.inference_state, string_name)
|
|
||||||
|
|
||||||
# This is a little bit special. `__getattribute__` is in Python
|
|
||||||
# executed before `__getattr__`. But: I know no use case, where
|
|
||||||
# this could be practical and where Jedi would return wrong types.
|
|
||||||
# If you ever find something, let me know!
|
|
||||||
# We are inversing this, because a hand-crafted `__getattribute__`
|
|
||||||
# could still call another hand-crafted `__getattr__`, but not the
|
|
||||||
# other way around.
|
|
||||||
names = (self.get_function_slot_names(u'__getattr__') or
|
|
||||||
self.get_function_slot_names(u'__getattribute__'))
|
|
||||||
return self.execute_function_slots(names, name)
|
|
||||||
|
|
||||||
|
|
||||||
class AnonymousInstance(TreeInstance):
|
|
||||||
def __init__(self, inference_state, parent_context, class_value):
|
|
||||||
super(AnonymousInstance, self).__init__(
|
|
||||||
inference_state,
|
|
||||||
parent_context,
|
|
||||||
class_value,
|
|
||||||
var_args=AnonymousInstanceArguments(self),
|
|
||||||
)
|
)
|
||||||
|
if values:
|
||||||
|
return values
|
||||||
|
else:
|
||||||
|
if key == index:
|
||||||
|
return lazy_context.infer()
|
||||||
|
return super(TreeInstance, self).py__simple_getitem__(index)
|
||||||
|
|
||||||
def get_annotated_class_object(self):
|
def __repr__(self):
|
||||||
return self.class_value # This is the default.
|
return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value,
|
||||||
|
self._arguments)
|
||||||
|
|
||||||
|
|
||||||
|
class AnonymousInstance(_BaseTreeInstance):
|
||||||
|
_arguments = None
|
||||||
|
|
||||||
|
|
||||||
class CompiledInstanceName(compiled.CompiledName):
|
class CompiledInstanceName(compiled.CompiledName):
|
||||||
|
|
||||||
def __init__(self, inference_state, instance, klass, name):
|
def __init__(self, inference_state, instance, klass, name):
|
||||||
super(CompiledInstanceName, self).__init__(
|
super(CompiledInstanceName, self).__init__(
|
||||||
inference_state,
|
inference_state,
|
||||||
@@ -397,14 +421,15 @@ class BoundMethod(FunctionMixin, ValueWrapper):
|
|||||||
return c
|
return c
|
||||||
|
|
||||||
def _get_arguments(self, arguments):
|
def _get_arguments(self, arguments):
|
||||||
if arguments is None:
|
assert arguments is not None
|
||||||
arguments = AnonymousInstanceArguments(self.instance)
|
|
||||||
|
|
||||||
return InstanceArguments(self.instance, arguments)
|
return InstanceArguments(self.instance, arguments)
|
||||||
|
|
||||||
def as_context(self, arguments=None):
|
def _as_context(self, arguments=None):
|
||||||
|
if arguments is None:
|
||||||
|
return AnonymousMethodExecutionContext(self.instance, self)
|
||||||
|
|
||||||
arguments = self._get_arguments(arguments)
|
arguments = self._get_arguments(arguments)
|
||||||
return super(BoundMethod, self).as_context(arguments)
|
return MethodExecutionContext(self.instance, self, arguments)
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
if isinstance(self._wrapped_value, OverloadedFunctionValue):
|
if isinstance(self._wrapped_value, OverloadedFunctionValue):
|
||||||
@@ -445,10 +470,7 @@ class SelfName(TreeNameDefinition):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def parent_context(self):
|
def parent_context(self):
|
||||||
return self._instance.create_instance_context(
|
return self._instance.create_instance_context(self.class_context, self.tree_name)
|
||||||
self.class_context,
|
|
||||||
self.tree_name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LazyInstanceClassName(object):
|
class LazyInstanceClassName(object):
|
||||||
@@ -509,9 +531,9 @@ class SelfAttributeFilter(ClassFilter):
|
|||||||
self._instance = instance
|
self._instance = instance
|
||||||
|
|
||||||
def _filter(self, names):
|
def _filter(self, names):
|
||||||
names = self._filter_self_names(names)
|
|
||||||
start, end = self._parser_scope.start_pos, self._parser_scope.end_pos
|
start, end = self._parser_scope.start_pos, self._parser_scope.end_pos
|
||||||
return [n for n in names if start < n.start_pos < end]
|
names = [n for n in names if start < n.start_pos < end]
|
||||||
|
return self._filter_self_names(names)
|
||||||
|
|
||||||
def _filter_self_names(self, names):
|
def _filter_self_names(self, names):
|
||||||
for name in names:
|
for name in names:
|
||||||
@@ -520,9 +542,20 @@ class SelfAttributeFilter(ClassFilter):
|
|||||||
and len(trailer.parent.children) == 2 \
|
and len(trailer.parent.children) == 2 \
|
||||||
and trailer.children[0] == '.':
|
and trailer.children[0] == '.':
|
||||||
if name.is_definition() and self._access_possible(name, from_instance=True):
|
if name.is_definition() and self._access_possible(name, from_instance=True):
|
||||||
# TODO filter non-self assignments.
|
# TODO filter non-self assignments instead of this bad
|
||||||
|
# filter.
|
||||||
|
if self._is_in_right_scope(name):
|
||||||
yield name
|
yield name
|
||||||
|
|
||||||
|
def _is_in_right_scope(self, name):
|
||||||
|
base = name
|
||||||
|
hit_funcdef = False
|
||||||
|
while True:
|
||||||
|
base = search_ancestor(base, 'funcdef', 'classdef', 'lambdef')
|
||||||
|
if base is self._parser_scope:
|
||||||
|
return hit_funcdef
|
||||||
|
hit_funcdef = True
|
||||||
|
|
||||||
def _convert_names(self, names):
|
def _convert_names(self, names):
|
||||||
return [SelfName(self._instance, self._node_context, name) for name in names]
|
return [SelfName(self._instance, self._node_context, name) for name in names]
|
||||||
|
|
||||||
@@ -539,9 +572,3 @@ class InstanceArguments(TreeArgumentsWrapper):
|
|||||||
yield None, LazyKnownValue(self.instance)
|
yield None, LazyKnownValue(self.instance)
|
||||||
for values in self._wrapped_arguments.unpack(func):
|
for values in self._wrapped_arguments.unpack(func):
|
||||||
yield values
|
yield values
|
||||||
|
|
||||||
def get_executed_param_names_and_issues(self, execution_context):
|
|
||||||
if isinstance(self._wrapped_arguments, AnonymousInstanceArguments):
|
|
||||||
return self._wrapped_arguments.get_executed_param_names_and_issues(execution_context)
|
|
||||||
|
|
||||||
return super(InstanceArguments, self).get_executed_param_names_and_issues(execution_context)
|
|
||||||
|
|||||||
@@ -1,45 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
Contains all classes and functions to deal with lists, dicts, generators and
|
Contains all classes and functions to deal with lists, dicts, generators and
|
||||||
iterators in general.
|
iterators in general.
|
||||||
|
|
||||||
Array modifications
|
|
||||||
*******************
|
|
||||||
|
|
||||||
If the content of an array (``set``/``list``) is requested somewhere, the
|
|
||||||
current module will be checked for appearances of ``arr.append``,
|
|
||||||
``arr.insert``, etc. If the ``arr`` name points to an actual array, the
|
|
||||||
content will be added
|
|
||||||
|
|
||||||
This can be really cpu intensive, as you can imagine. Because |jedi| has to
|
|
||||||
follow **every** ``append`` and check wheter it's the right array. However this
|
|
||||||
works pretty good, because in *slow* cases, the recursion detector and other
|
|
||||||
settings will stop this process.
|
|
||||||
|
|
||||||
It is important to note that:
|
|
||||||
|
|
||||||
1. Array modfications work only in the current module.
|
|
||||||
2. Jedi only checks Array additions; ``list.pop``, etc are ignored.
|
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from jedi import debug
|
|
||||||
from jedi import settings
|
|
||||||
from jedi._compatibility import force_unicode, is_py3
|
from jedi._compatibility import force_unicode, is_py3
|
||||||
from jedi.inference import compiled
|
from jedi.inference import compiled
|
||||||
from jedi.inference import analysis
|
from jedi.inference import analysis
|
||||||
from jedi.inference import recursion
|
|
||||||
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
|
from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
|
||||||
LazyTreeValue
|
LazyTreeValue
|
||||||
from jedi.inference.helpers import get_int_or_none, is_string, \
|
from jedi.inference.helpers import get_int_or_none, is_string, \
|
||||||
infer_call_of_leaf, reraise_getitem_errors, SimpleGetItemNotFound
|
reraise_getitem_errors, SimpleGetItemNotFound
|
||||||
from jedi.inference.utils import safe_property, to_list
|
from jedi.inference.utils import safe_property, to_list
|
||||||
from jedi.inference.cache import inference_state_method_cache
|
from jedi.inference.cache import inference_state_method_cache
|
||||||
from jedi.inference.filters import LazyAttributeOverwrite, publish_method
|
from jedi.inference.filters import LazyAttributeOverwrite, publish_method
|
||||||
from jedi.inference.base_value import ValueSet, Value, NO_VALUES, \
|
from jedi.inference.base_value import ValueSet, Value, NO_VALUES, \
|
||||||
ContextualizedNode, iterate_values, HelperValueMixin, sentinel, \
|
ContextualizedNode, iterate_values, sentinel, \
|
||||||
LazyValueWrapper
|
LazyValueWrapper
|
||||||
from jedi.parser_utils import get_sync_comp_fors
|
from jedi.parser_utils import get_sync_comp_fors
|
||||||
from jedi.inference.context import CompForContext
|
from jedi.inference.context import CompForContext
|
||||||
|
from jedi.inference.value.dynamic_arrays import check_array_additions
|
||||||
|
|
||||||
|
|
||||||
class IterableMixin(object):
|
class IterableMixin(object):
|
||||||
@@ -298,15 +278,14 @@ class DictComprehension(ComprehensionMixin, Sequence):
|
|||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self):
|
||||||
lazy_value = LazyKnownValues(self._dict_values())
|
lazy_value = LazyKnownValues(self._dict_values())
|
||||||
return ValueSet([FakeSequence(self.inference_state, u'list', [lazy_value])])
|
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
||||||
|
|
||||||
@publish_method('items')
|
@publish_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self):
|
||||||
lazy_values = [
|
lazy_values = [
|
||||||
LazyKnownValue(
|
LazyKnownValue(
|
||||||
FakeSequence(
|
FakeTuple(
|
||||||
self.inference_state,
|
self.inference_state,
|
||||||
u'tuple',
|
|
||||||
[LazyKnownValues(key),
|
[LazyKnownValues(key),
|
||||||
LazyKnownValues(value)]
|
LazyKnownValues(value)]
|
||||||
)
|
)
|
||||||
@@ -314,7 +293,7 @@ class DictComprehension(ComprehensionMixin, Sequence):
|
|||||||
for key, value in self._iterate()
|
for key, value in self._iterate()
|
||||||
]
|
]
|
||||||
|
|
||||||
return ValueSet([FakeSequence(self.inference_state, u'list', lazy_values)])
|
return ValueSet([FakeList(self.inference_state, lazy_values)])
|
||||||
|
|
||||||
def get_mapping_item_values(self):
|
def get_mapping_item_values(self):
|
||||||
return self._dict_keys(), self._dict_values()
|
return self._dict_keys(), self._dict_values()
|
||||||
@@ -344,19 +323,6 @@ class SequenceLiteralValue(Sequence):
|
|||||||
|
|
||||||
def py__simple_getitem__(self, index):
|
def py__simple_getitem__(self, index):
|
||||||
"""Here the index is an int/str. Raises IndexError/KeyError."""
|
"""Here the index is an int/str. Raises IndexError/KeyError."""
|
||||||
if self.array_type == u'dict':
|
|
||||||
compiled_obj_index = compiled.create_simple_object(self.inference_state, index)
|
|
||||||
for key, value in self.get_tree_entries():
|
|
||||||
for k in self._defining_context.infer_node(key):
|
|
||||||
try:
|
|
||||||
method = k.execute_operation
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if method(compiled_obj_index, u'==').get_safe_value():
|
|
||||||
return self._defining_context.infer_node(value)
|
|
||||||
raise SimpleGetItemNotFound('No key found in dictionary %s.' % self)
|
|
||||||
|
|
||||||
if isinstance(index, slice):
|
if isinstance(index, slice):
|
||||||
return ValueSet([self])
|
return ValueSet([self])
|
||||||
else:
|
else:
|
||||||
@@ -369,16 +335,6 @@ class SequenceLiteralValue(Sequence):
|
|||||||
While values returns the possible values for any array field, this
|
While values returns the possible values for any array field, this
|
||||||
function returns the value for a certain index.
|
function returns the value for a certain index.
|
||||||
"""
|
"""
|
||||||
if self.array_type == u'dict':
|
|
||||||
# Get keys.
|
|
||||||
types = NO_VALUES
|
|
||||||
for k, _ in self.get_tree_entries():
|
|
||||||
types |= self._defining_context.infer_node(k)
|
|
||||||
# We don't know which dict index comes first, therefore always
|
|
||||||
# yield all the types.
|
|
||||||
for _ in types:
|
|
||||||
yield LazyKnownValues(types)
|
|
||||||
else:
|
|
||||||
for node in self.get_tree_entries():
|
for node in self.get_tree_entries():
|
||||||
if node == ':' or node.type == 'subscript':
|
if node == ':' or node.type == 'subscript':
|
||||||
# TODO this should probably use at least part of the code
|
# TODO this should probably use at least part of the code
|
||||||
@@ -393,12 +349,6 @@ class SequenceLiteralValue(Sequence):
|
|||||||
# This function is not really used often. It's more of a try.
|
# This function is not really used often. It's more of a try.
|
||||||
return len(self.get_tree_entries())
|
return len(self.get_tree_entries())
|
||||||
|
|
||||||
def _dict_values(self):
|
|
||||||
return ValueSet.from_sets(
|
|
||||||
self._defining_context.infer_node(v)
|
|
||||||
for k, v in self.get_tree_entries()
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tree_entries(self):
|
def get_tree_entries(self):
|
||||||
c = self.atom.children
|
c = self.atom.children
|
||||||
|
|
||||||
@@ -466,22 +416,56 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue):
|
|||||||
self._defining_context = defining_context
|
self._defining_context = defining_context
|
||||||
self.atom = atom
|
self.atom = atom
|
||||||
|
|
||||||
|
def py__simple_getitem__(self, index):
|
||||||
|
"""Here the index is an int/str. Raises IndexError/KeyError."""
|
||||||
|
compiled_obj_index = compiled.create_simple_object(self.inference_state, index)
|
||||||
|
for key, value in self.get_tree_entries():
|
||||||
|
for k in self._defining_context.infer_node(key):
|
||||||
|
try:
|
||||||
|
method = k.execute_operation
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if method(compiled_obj_index, u'==').get_safe_value():
|
||||||
|
return self._defining_context.infer_node(value)
|
||||||
|
raise SimpleGetItemNotFound('No key found in dictionary %s.' % self)
|
||||||
|
|
||||||
|
def py__iter__(self, contextualized_node=None):
|
||||||
|
"""
|
||||||
|
While values returns the possible values for any array field, this
|
||||||
|
function returns the value for a certain index.
|
||||||
|
"""
|
||||||
|
# Get keys.
|
||||||
|
types = NO_VALUES
|
||||||
|
for k, _ in self.get_tree_entries():
|
||||||
|
types |= self._defining_context.infer_node(k)
|
||||||
|
# We don't know which dict index comes first, therefore always
|
||||||
|
# yield all the types.
|
||||||
|
for _ in types:
|
||||||
|
yield LazyKnownValues(types)
|
||||||
|
|
||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self):
|
||||||
lazy_value = LazyKnownValues(self._dict_values())
|
lazy_value = LazyKnownValues(self._dict_values())
|
||||||
return ValueSet([FakeSequence(self.inference_state, u'list', [lazy_value])])
|
return ValueSet([FakeList(self.inference_state, [lazy_value])])
|
||||||
|
|
||||||
@publish_method('items')
|
@publish_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self):
|
||||||
lazy_values = [
|
lazy_values = [
|
||||||
LazyKnownValue(FakeSequence(
|
LazyKnownValue(FakeTuple(
|
||||||
self.inference_state, u'tuple',
|
self.inference_state,
|
||||||
(LazyTreeValue(self._defining_context, key_node),
|
(LazyTreeValue(self._defining_context, key_node),
|
||||||
LazyTreeValue(self._defining_context, value_node))
|
LazyTreeValue(self._defining_context, value_node))
|
||||||
)) for key_node, value_node in self.get_tree_entries()
|
)) for key_node, value_node in self.get_tree_entries()
|
||||||
]
|
]
|
||||||
|
|
||||||
return ValueSet([FakeSequence(self.inference_state, u'list', lazy_values)])
|
return ValueSet([FakeList(self.inference_state, lazy_values)])
|
||||||
|
|
||||||
|
def _dict_values(self):
|
||||||
|
return ValueSet.from_sets(
|
||||||
|
self._defining_context.infer_node(v)
|
||||||
|
for k, v in self.get_tree_entries()
|
||||||
|
)
|
||||||
|
|
||||||
def _dict_keys(self):
|
def _dict_keys(self):
|
||||||
return ValueSet.from_sets(
|
return ValueSet.from_sets(
|
||||||
@@ -493,20 +477,12 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue):
|
|||||||
return self._dict_keys(), self._dict_values()
|
return self._dict_keys(), self._dict_values()
|
||||||
|
|
||||||
|
|
||||||
class _FakeArray(SequenceLiteralValue):
|
class _FakeSequence(Sequence):
|
||||||
def __init__(self, inference_state, container, type):
|
def __init__(self, inference_state, lazy_value_list):
|
||||||
super(SequenceLiteralValue, self).__init__(inference_state)
|
|
||||||
self.array_type = type
|
|
||||||
self.atom = container
|
|
||||||
# TODO is this class really needed?
|
|
||||||
|
|
||||||
|
|
||||||
class FakeSequence(_FakeArray):
|
|
||||||
def __init__(self, inference_state, array_type, lazy_value_list):
|
|
||||||
"""
|
"""
|
||||||
type should be one of "tuple", "list"
|
type should be one of "tuple", "list"
|
||||||
"""
|
"""
|
||||||
super(FakeSequence, self).__init__(inference_state, None, array_type)
|
super(_FakeSequence, self).__init__(inference_state)
|
||||||
self._lazy_value_list = lazy_value_list
|
self._lazy_value_list = lazy_value_list
|
||||||
|
|
||||||
def py__simple_getitem__(self, index):
|
def py__simple_getitem__(self, index):
|
||||||
@@ -527,9 +503,19 @@ class FakeSequence(_FakeArray):
|
|||||||
return "<%s of %s>" % (type(self).__name__, self._lazy_value_list)
|
return "<%s of %s>" % (type(self).__name__, self._lazy_value_list)
|
||||||
|
|
||||||
|
|
||||||
class FakeDict(_DictMixin, _FakeArray):
|
class FakeTuple(_FakeSequence):
|
||||||
|
array_type = u'tuple'
|
||||||
|
|
||||||
|
|
||||||
|
class FakeList(_FakeSequence):
|
||||||
|
array_type = u'tuple'
|
||||||
|
|
||||||
|
|
||||||
|
class FakeDict(_DictMixin, Sequence):
|
||||||
|
array_type = u'dict'
|
||||||
|
|
||||||
def __init__(self, inference_state, dct):
|
def __init__(self, inference_state, dct):
|
||||||
super(FakeDict, self).__init__(inference_state, dct, u'dict')
|
super(FakeDict, self).__init__(inference_state)
|
||||||
self._dct = dct
|
self._dct = dct
|
||||||
|
|
||||||
def py__iter__(self, contextualized_node=None):
|
def py__iter__(self, contextualized_node=None):
|
||||||
@@ -558,8 +544,8 @@ class FakeDict(_DictMixin, _FakeArray):
|
|||||||
|
|
||||||
@publish_method('values')
|
@publish_method('values')
|
||||||
def _values(self):
|
def _values(self):
|
||||||
return ValueSet([FakeSequence(
|
return ValueSet([FakeTuple(
|
||||||
self.inference_state, u'tuple',
|
self.inference_state,
|
||||||
[LazyKnownValues(self._dict_values())]
|
[LazyKnownValues(self._dict_values())]
|
||||||
)])
|
)])
|
||||||
|
|
||||||
@@ -575,10 +561,14 @@ class FakeDict(_DictMixin, _FakeArray):
|
|||||||
def exact_key_items(self):
|
def exact_key_items(self):
|
||||||
return self._dct.items()
|
return self._dct.items()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s: %s>' % (self.__class__.__name__, self._dct)
|
||||||
|
|
||||||
class MergedArray(_FakeArray):
|
|
||||||
|
class MergedArray(Sequence):
|
||||||
def __init__(self, inference_state, arrays):
|
def __init__(self, inference_state, arrays):
|
||||||
super(MergedArray, self).__init__(inference_state, arrays, arrays[-1].array_type)
|
super(MergedArray, self).__init__(inference_state)
|
||||||
|
self.array_type = arrays[-1].array_type
|
||||||
self._arrays = arrays
|
self._arrays = arrays
|
||||||
|
|
||||||
def py__iter__(self, contextualized_node=None):
|
def py__iter__(self, contextualized_node=None):
|
||||||
@@ -589,14 +579,6 @@ class MergedArray(_FakeArray):
|
|||||||
def py__simple_getitem__(self, index):
|
def py__simple_getitem__(self, index):
|
||||||
return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
|
return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
|
||||||
|
|
||||||
def get_tree_entries(self):
|
|
||||||
for array in self._arrays:
|
|
||||||
for a in array.get_tree_entries():
|
|
||||||
yield a
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return sum(len(a) for a in self._arrays)
|
|
||||||
|
|
||||||
|
|
||||||
def unpack_tuple_to_dict(value, types, exprlist):
|
def unpack_tuple_to_dict(value, types, exprlist):
|
||||||
"""
|
"""
|
||||||
@@ -638,139 +620,6 @@ def unpack_tuple_to_dict(value, types, exprlist):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def check_array_additions(context, sequence):
|
|
||||||
""" Just a mapper function for the internal _check_array_additions """
|
|
||||||
if sequence.array_type not in ('list', 'set'):
|
|
||||||
# TODO also check for dict updates
|
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
return _check_array_additions(context, sequence)
|
|
||||||
|
|
||||||
|
|
||||||
@inference_state_method_cache(default=NO_VALUES)
|
|
||||||
@debug.increase_indent
|
|
||||||
def _check_array_additions(context, sequence):
|
|
||||||
"""
|
|
||||||
Checks if a `Array` has "add" (append, insert, extend) statements:
|
|
||||||
|
|
||||||
>>> a = [""]
|
|
||||||
>>> a.append(1)
|
|
||||||
"""
|
|
||||||
from jedi.inference import arguments
|
|
||||||
|
|
||||||
debug.dbg('Dynamic array search for %s' % sequence, color='MAGENTA')
|
|
||||||
module_context = context.get_root_context()
|
|
||||||
if not settings.dynamic_array_additions or module_context.is_compiled():
|
|
||||||
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
def find_additions(context, arglist, add_name):
|
|
||||||
params = list(arguments.TreeArguments(context.inference_state, context, arglist).unpack())
|
|
||||||
result = set()
|
|
||||||
if add_name in ['insert']:
|
|
||||||
params = params[1:]
|
|
||||||
if add_name in ['append', 'add', 'insert']:
|
|
||||||
for key, lazy_value in params:
|
|
||||||
result.add(lazy_value)
|
|
||||||
elif add_name in ['extend', 'update']:
|
|
||||||
for key, lazy_value in params:
|
|
||||||
result |= set(lazy_value.infer().iterate())
|
|
||||||
return result
|
|
||||||
|
|
||||||
temp_param_add, settings.dynamic_params_for_other_modules = \
|
|
||||||
settings.dynamic_params_for_other_modules, False
|
|
||||||
|
|
||||||
is_list = sequence.name.string_name == 'list'
|
|
||||||
search_names = (['append', 'extend', 'insert'] if is_list else ['add', 'update'])
|
|
||||||
|
|
||||||
added_types = set()
|
|
||||||
for add_name in search_names:
|
|
||||||
try:
|
|
||||||
possible_names = module_context.tree_node.get_used_names()[add_name]
|
|
||||||
except KeyError:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
for name in possible_names:
|
|
||||||
value_node = context.tree_node
|
|
||||||
if not (value_node.start_pos < name.start_pos < value_node.end_pos):
|
|
||||||
continue
|
|
||||||
trailer = name.parent
|
|
||||||
power = trailer.parent
|
|
||||||
trailer_pos = power.children.index(trailer)
|
|
||||||
try:
|
|
||||||
execution_trailer = power.children[trailer_pos + 1]
|
|
||||||
except IndexError:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if execution_trailer.type != 'trailer' \
|
|
||||||
or execution_trailer.children[0] != '(' \
|
|
||||||
or execution_trailer.children[1] == ')':
|
|
||||||
continue
|
|
||||||
|
|
||||||
random_context = context.create_context(name)
|
|
||||||
|
|
||||||
with recursion.execution_allowed(context.inference_state, power) as allowed:
|
|
||||||
if allowed:
|
|
||||||
found = infer_call_of_leaf(
|
|
||||||
random_context,
|
|
||||||
name,
|
|
||||||
cut_own_trailer=True
|
|
||||||
)
|
|
||||||
if sequence in found:
|
|
||||||
# The arrays match. Now add the results
|
|
||||||
added_types |= find_additions(
|
|
||||||
random_context,
|
|
||||||
execution_trailer.children[1],
|
|
||||||
add_name
|
|
||||||
)
|
|
||||||
|
|
||||||
# reset settings
|
|
||||||
settings.dynamic_params_for_other_modules = temp_param_add
|
|
||||||
debug.dbg('Dynamic array result %s' % added_types, color='MAGENTA')
|
|
||||||
return added_types
|
|
||||||
|
|
||||||
|
|
||||||
def get_dynamic_array_instance(instance, arguments):
|
|
||||||
"""Used for set() and list() instances."""
|
|
||||||
ai = _ArrayInstance(instance, arguments)
|
|
||||||
from jedi.inference import arguments
|
|
||||||
return arguments.ValuesArguments([ValueSet([ai])])
|
|
||||||
|
|
||||||
|
|
||||||
class _ArrayInstance(HelperValueMixin):
|
|
||||||
"""
|
|
||||||
Used for the usage of set() and list().
|
|
||||||
This is definitely a hack, but a good one :-)
|
|
||||||
It makes it possible to use set/list conversions.
|
|
||||||
"""
|
|
||||||
def __init__(self, instance, var_args):
|
|
||||||
self.instance = instance
|
|
||||||
self.var_args = var_args
|
|
||||||
|
|
||||||
def py__class__(self):
|
|
||||||
tuple_, = self.instance.inference_state.builtins_module.py__getattribute__('tuple')
|
|
||||||
return tuple_
|
|
||||||
|
|
||||||
def py__iter__(self, contextualized_node=None):
|
|
||||||
var_args = self.var_args
|
|
||||||
try:
|
|
||||||
_, lazy_value = next(var_args.unpack())
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
for lazy in lazy_value.infer().iterate():
|
|
||||||
yield lazy
|
|
||||||
|
|
||||||
from jedi.inference import arguments
|
|
||||||
if isinstance(var_args, arguments.TreeArguments):
|
|
||||||
additions = _check_array_additions(var_args.context, self.instance)
|
|
||||||
for addition in additions:
|
|
||||||
yield addition
|
|
||||||
|
|
||||||
def iterate(self, contextualized_node=None, is_async=False):
|
|
||||||
return self.py__iter__(contextualized_node)
|
|
||||||
|
|
||||||
|
|
||||||
class Slice(LazyValueWrapper):
|
class Slice(LazyValueWrapper):
|
||||||
def __init__(self, python_context, start, stop, step):
|
def __init__(self, python_context, start, stop, step):
|
||||||
self.inference_state = python_context.inference_state
|
self.inference_state = python_context.inference_state
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class ClassFilter(ParserTreeFilter):
|
|||||||
# Filter for ClassVar variables
|
# Filter for ClassVar variables
|
||||||
# TODO this is not properly done, yet. It just checks for the string
|
# TODO this is not properly done, yet. It just checks for the string
|
||||||
# ClassVar in the annotation, which can be quite imprecise. If we
|
# ClassVar in the annotation, which can be quite imprecise. If we
|
||||||
# wanted to do this correct, we would have to resolve the ClassVar.
|
# wanted to do this correct, we would have to infer the ClassVar.
|
||||||
if not from_instance:
|
if not from_instance:
|
||||||
expr_stmt = name.get_definition()
|
expr_stmt = name.get_definition()
|
||||||
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ from jedi._compatibility import force_unicode, Parameter
|
|||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.inference.utils import safe_property
|
from jedi.inference.utils import safe_property
|
||||||
from jedi.inference.helpers import get_str_or_none
|
from jedi.inference.helpers import get_str_or_none
|
||||||
from jedi.inference.arguments import ValuesArguments, \
|
from jedi.inference.arguments import \
|
||||||
repack_with_argument_clinic, AbstractArguments, TreeArgumentsWrapper
|
repack_with_argument_clinic, AbstractArguments, TreeArgumentsWrapper
|
||||||
from jedi.inference import analysis
|
from jedi.inference import analysis
|
||||||
from jedi.inference import compiled
|
from jedi.inference import compiled
|
||||||
from jedi.inference.value.instance import BoundMethod, InstanceArguments
|
from jedi.inference.value.instance import \
|
||||||
|
AnonymousMethodExecutionContext, MethodExecutionContext
|
||||||
from jedi.inference.base_value import ContextualizedNode, \
|
from jedi.inference.base_value import ContextualizedNode, \
|
||||||
NO_VALUES, ValueSet, ValueWrapper, LazyValueWrapper
|
NO_VALUES, ValueSet, ValueWrapper, LazyValueWrapper
|
||||||
from jedi.inference.value import ClassValue, ModuleValue, \
|
from jedi.inference.value import ClassValue, ModuleValue
|
||||||
FunctionExecutionContext
|
|
||||||
from jedi.inference.value.klass import ClassMixin
|
from jedi.inference.value.klass import ClassMixin
|
||||||
from jedi.inference.value.function import FunctionMixin
|
from jedi.inference.value.function import FunctionMixin
|
||||||
from jedi.inference.value import iterable
|
from jedi.inference.value import iterable
|
||||||
@@ -121,19 +121,7 @@ def execute(callback):
|
|||||||
else:
|
else:
|
||||||
return call()
|
return call()
|
||||||
|
|
||||||
if isinstance(value, BoundMethod):
|
if value.is_bound_method():
|
||||||
if module_name == 'builtins':
|
|
||||||
if value.py__name__() == '__get__':
|
|
||||||
if value.class_context.py__name__() == 'property':
|
|
||||||
return builtins_property(
|
|
||||||
value,
|
|
||||||
arguments=arguments,
|
|
||||||
callback=call,
|
|
||||||
)
|
|
||||||
elif value.py__name__() in ('deleter', 'getter', 'setter'):
|
|
||||||
if value.class_context.py__name__() == 'property':
|
|
||||||
return ValueSet([value.instance])
|
|
||||||
|
|
||||||
return call()
|
return call()
|
||||||
|
|
||||||
# for now we just support builtin functions.
|
# for now we just support builtin functions.
|
||||||
@@ -157,7 +145,7 @@ def _follow_param(inference_state, arguments, index):
|
|||||||
return lazy_value.infer()
|
return lazy_value.infer()
|
||||||
|
|
||||||
|
|
||||||
def argument_clinic(string, want_obj=False, want_context=False,
|
def argument_clinic(string, want_value=False, want_context=False,
|
||||||
want_arguments=False, want_inference_state=False,
|
want_arguments=False, want_inference_state=False,
|
||||||
want_callback=False):
|
want_callback=False):
|
||||||
"""
|
"""
|
||||||
@@ -167,18 +155,18 @@ def argument_clinic(string, want_obj=False, want_context=False,
|
|||||||
def f(func):
|
def f(func):
|
||||||
@repack_with_argument_clinic(string, keep_arguments_param=True,
|
@repack_with_argument_clinic(string, keep_arguments_param=True,
|
||||||
keep_callback_param=True)
|
keep_callback_param=True)
|
||||||
def wrapper(obj, *args, **kwargs):
|
def wrapper(value, *args, **kwargs):
|
||||||
arguments = kwargs.pop('arguments')
|
arguments = kwargs.pop('arguments')
|
||||||
callback = kwargs.pop('callback')
|
callback = kwargs.pop('callback')
|
||||||
assert not kwargs # Python 2...
|
assert not kwargs # Python 2...
|
||||||
debug.dbg('builtin start %s' % obj, color='MAGENTA')
|
debug.dbg('builtin start %s' % value, color='MAGENTA')
|
||||||
result = NO_VALUES
|
result = NO_VALUES
|
||||||
if want_context:
|
if want_context:
|
||||||
kwargs['context'] = arguments.context
|
kwargs['context'] = arguments.context
|
||||||
if want_obj:
|
if want_value:
|
||||||
kwargs['obj'] = obj
|
kwargs['value'] = value
|
||||||
if want_inference_state:
|
if want_inference_state:
|
||||||
kwargs['inference_state'] = obj.inference_state
|
kwargs['inference_state'] = value.inference_state
|
||||||
if want_arguments:
|
if want_arguments:
|
||||||
kwargs['arguments'] = arguments
|
kwargs['arguments'] = arguments
|
||||||
if want_callback:
|
if want_callback:
|
||||||
@@ -191,17 +179,6 @@ def argument_clinic(string, want_obj=False, want_context=False,
|
|||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('obj, type, /', want_obj=True, want_arguments=True)
|
|
||||||
def builtins_property(objects, types, obj, arguments):
|
|
||||||
property_args = obj.instance.var_args.unpack()
|
|
||||||
key, lazy_value = next(property_args, (None, None))
|
|
||||||
if key is not None or lazy_value is None:
|
|
||||||
debug.warning('property expected a first param, not %s', arguments)
|
|
||||||
return NO_VALUES
|
|
||||||
|
|
||||||
return lazy_value.infer().py__call__(arguments=ValuesArguments([objects]))
|
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('iterator[, default], /', want_inference_state=True)
|
@argument_clinic('iterator[, default], /', want_inference_state=True)
|
||||||
def builtins_next(iterators, defaults, inference_state):
|
def builtins_next(iterators, defaults, inference_state):
|
||||||
if inference_state.environment.version_info.major == 2:
|
if inference_state.environment.version_info.major == 2:
|
||||||
@@ -223,14 +200,14 @@ def builtins_iter(iterators_or_callables, defaults):
|
|||||||
@argument_clinic('object, name[, default], /')
|
@argument_clinic('object, name[, default], /')
|
||||||
def builtins_getattr(objects, names, defaults=None):
|
def builtins_getattr(objects, names, defaults=None):
|
||||||
# follow the first param
|
# follow the first param
|
||||||
for obj in objects:
|
for value in objects:
|
||||||
for name in names:
|
for name in names:
|
||||||
string = get_str_or_none(name)
|
string = get_str_or_none(name)
|
||||||
if string is None:
|
if string is None:
|
||||||
debug.warning('getattr called without str')
|
debug.warning('getattr called without str')
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
return obj.py__getattribute__(force_unicode(string))
|
return value.py__getattribute__(force_unicode(string))
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
|
|
||||||
@@ -262,21 +239,21 @@ class SuperInstance(LazyValueWrapper):
|
|||||||
|
|
||||||
def get_filters(self, origin_scope=None):
|
def get_filters(self, origin_scope=None):
|
||||||
for b in self._get_bases():
|
for b in self._get_bases():
|
||||||
for obj in b.infer().execute_with_values():
|
for value in b.infer().execute_with_values():
|
||||||
for f in obj.get_filters():
|
for f in value.get_filters():
|
||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('[type[, obj]], /', want_context=True)
|
@argument_clinic('[type[, value]], /', want_context=True)
|
||||||
def builtins_super(types, objects, context):
|
def builtins_super(types, objects, context):
|
||||||
if isinstance(context, FunctionExecutionContext):
|
instance = None
|
||||||
if isinstance(context.var_args, InstanceArguments):
|
if isinstance(context, AnonymousMethodExecutionContext):
|
||||||
instance = context.var_args.instance
|
instance = context.instance
|
||||||
# TODO if a class is given it doesn't have to be the direct super
|
elif isinstance(context, MethodExecutionContext):
|
||||||
# class, it can be an anecestor from long ago.
|
instance = context.instance
|
||||||
return ValueSet({SuperInstance(instance.inference_state, instance)})
|
if instance is None:
|
||||||
|
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
return ValueSet({SuperInstance(instance.inference_state, instance)})
|
||||||
|
|
||||||
|
|
||||||
class ReversedObject(AttributeOverwrite):
|
class ReversedObject(AttributeOverwrite):
|
||||||
@@ -296,8 +273,8 @@ class ReversedObject(AttributeOverwrite):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('sequence, /', want_obj=True, want_arguments=True)
|
@argument_clinic('sequence, /', want_value=True, want_arguments=True)
|
||||||
def builtins_reversed(sequences, obj, arguments):
|
def builtins_reversed(sequences, value, arguments):
|
||||||
# While we could do without this variable (just by using sequences), we
|
# While we could do without this variable (just by using sequences), we
|
||||||
# want static analysis to work well. Therefore we need to generated the
|
# want static analysis to work well. Therefore we need to generated the
|
||||||
# values again.
|
# values again.
|
||||||
@@ -311,11 +288,11 @@ def builtins_reversed(sequences, obj, arguments):
|
|||||||
# necessary, because `reversed` is a function and autocompletion
|
# necessary, because `reversed` is a function and autocompletion
|
||||||
# would fail in certain cases like `reversed(x).__iter__` if we
|
# would fail in certain cases like `reversed(x).__iter__` if we
|
||||||
# just returned the result directly.
|
# just returned the result directly.
|
||||||
seq, = obj.inference_state.typing_module.py__getattribute__('Iterator').execute_with_values()
|
seq, = value.inference_state.typing_module.py__getattribute__('Iterator').execute_with_values()
|
||||||
return ValueSet([ReversedObject(seq, list(reversed(ordered)))])
|
return ValueSet([ReversedObject(seq, list(reversed(ordered)))])
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('obj, type, /', want_arguments=True, want_inference_state=True)
|
@argument_clinic('value, type, /', want_arguments=True, want_inference_state=True)
|
||||||
def builtins_isinstance(objects, types, arguments, inference_state):
|
def builtins_isinstance(objects, types, arguments, inference_state):
|
||||||
bool_results = set()
|
bool_results = set()
|
||||||
for o in objects:
|
for o in objects:
|
||||||
@@ -357,10 +334,7 @@ def builtins_isinstance(objects, types, arguments, inference_state):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class StaticMethodObject(AttributeOverwrite, ValueWrapper):
|
class StaticMethodObject(ValueWrapper):
|
||||||
def get_object(self):
|
|
||||||
return self._wrapped_value
|
|
||||||
|
|
||||||
def py__get__(self, instance, klass):
|
def py__get__(self, instance, klass):
|
||||||
return ValueSet([self._wrapped_value])
|
return ValueSet([self._wrapped_value])
|
||||||
|
|
||||||
@@ -370,22 +344,19 @@ def builtins_staticmethod(functions):
|
|||||||
return ValueSet(StaticMethodObject(f) for f in functions)
|
return ValueSet(StaticMethodObject(f) for f in functions)
|
||||||
|
|
||||||
|
|
||||||
class ClassMethodObject(AttributeOverwrite, ValueWrapper):
|
class ClassMethodObject(ValueWrapper):
|
||||||
def __init__(self, class_method_obj, function):
|
def __init__(self, class_method_obj, function):
|
||||||
super(ClassMethodObject, self).__init__(class_method_obj)
|
super(ClassMethodObject, self).__init__(class_method_obj)
|
||||||
self._function = function
|
self._function = function
|
||||||
|
|
||||||
def get_object(self):
|
def py__get__(self, instance, class_value):
|
||||||
return self._wrapped_value
|
|
||||||
|
|
||||||
def py__get__(self, obj, class_value):
|
|
||||||
return ValueSet([
|
return ValueSet([
|
||||||
ClassMethodGet(__get__, class_value, self._function)
|
ClassMethodGet(__get__, class_value, self._function)
|
||||||
for __get__ in self._wrapped_value.py__getattribute__('__get__')
|
for __get__ in self._wrapped_value.py__getattribute__('__get__')
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class ClassMethodGet(AttributeOverwrite, ValueWrapper):
|
class ClassMethodGet(ValueWrapper):
|
||||||
def __init__(self, get_method, klass, function):
|
def __init__(self, get_method, klass, function):
|
||||||
super(ClassMethodGet, self).__init__(get_method)
|
super(ClassMethodGet, self).__init__(get_method)
|
||||||
self._class = klass
|
self._class = klass
|
||||||
@@ -394,9 +365,6 @@ class ClassMethodGet(AttributeOverwrite, ValueWrapper):
|
|||||||
def get_signatures(self):
|
def get_signatures(self):
|
||||||
return self._function.get_signatures()
|
return self._function.get_signatures()
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return self._wrapped_value
|
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
return self._function.execute(ClassMethodArguments(self._class, arguments))
|
return self._function.execute(ClassMethodArguments(self._class, arguments))
|
||||||
|
|
||||||
@@ -412,16 +380,42 @@ class ClassMethodArguments(TreeArgumentsWrapper):
|
|||||||
yield values
|
yield values
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('sequence, /', want_obj=True, want_arguments=True)
|
@argument_clinic('sequence, /', want_value=True, want_arguments=True)
|
||||||
def builtins_classmethod(functions, obj, arguments):
|
def builtins_classmethod(functions, value, arguments):
|
||||||
return ValueSet(
|
return ValueSet(
|
||||||
ClassMethodObject(class_method_object, function)
|
ClassMethodObject(class_method_object, function)
|
||||||
for class_method_object in obj.py__call__(arguments=arguments)
|
for class_method_object in value.py__call__(arguments=arguments)
|
||||||
for function in functions
|
for function in functions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def collections_namedtuple(obj, arguments, callback):
|
class PropertyObject(AttributeOverwrite, ValueWrapper):
|
||||||
|
def __init__(self, property_obj, function):
|
||||||
|
super(PropertyObject, self).__init__(property_obj)
|
||||||
|
self._function = function
|
||||||
|
|
||||||
|
def py__get__(self, instance, class_value):
|
||||||
|
if instance is None:
|
||||||
|
return NO_VALUES
|
||||||
|
return self._function.execute_with_values(instance)
|
||||||
|
|
||||||
|
@publish_method('deleter')
|
||||||
|
@publish_method('getter')
|
||||||
|
@publish_method('setter')
|
||||||
|
def _return_self(self):
|
||||||
|
return ValueSet({self})
|
||||||
|
|
||||||
|
|
||||||
|
@argument_clinic('func, /', want_callback=True)
|
||||||
|
def builtins_property(functions, callback):
|
||||||
|
return ValueSet(
|
||||||
|
PropertyObject(property_value, function)
|
||||||
|
for property_value in callback()
|
||||||
|
for function in functions
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def collections_namedtuple(value, arguments, callback):
|
||||||
"""
|
"""
|
||||||
Implementation of the namedtuple function.
|
Implementation of the namedtuple function.
|
||||||
|
|
||||||
@@ -429,7 +423,7 @@ def collections_namedtuple(obj, arguments, callback):
|
|||||||
inferring the result.
|
inferring the result.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
inference_state = obj.inference_state
|
inference_state = value.inference_state
|
||||||
|
|
||||||
# Process arguments
|
# Process arguments
|
||||||
name = u'jedi_unknown_namedtuple'
|
name = u'jedi_unknown_namedtuple'
|
||||||
@@ -548,10 +542,10 @@ class MergedPartialArguments(AbstractArguments):
|
|||||||
yield key_lazy_value
|
yield key_lazy_value
|
||||||
|
|
||||||
|
|
||||||
def functools_partial(obj, arguments, callback):
|
def functools_partial(value, arguments, callback):
|
||||||
return ValueSet(
|
return ValueSet(
|
||||||
PartialObject(instance, arguments)
|
PartialObject(instance, arguments)
|
||||||
for instance in obj.py__call__(arguments)
|
for instance in value.py__call__(arguments)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -569,12 +563,12 @@ def _random_choice(sequences):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _dataclass(obj, arguments, callback):
|
def _dataclass(value, arguments, callback):
|
||||||
for c in _follow_param(obj.inference_state, arguments, 0):
|
for c in _follow_param(value.inference_state, arguments, 0):
|
||||||
if c.is_class():
|
if c.is_class():
|
||||||
return ValueSet([DataclassWrapper(c)])
|
return ValueSet([DataclassWrapper(c)])
|
||||||
else:
|
else:
|
||||||
return ValueSet([obj])
|
return ValueSet([value])
|
||||||
return NO_VALUES
|
return NO_VALUES
|
||||||
|
|
||||||
|
|
||||||
@@ -643,9 +637,8 @@ class ItemGetterCallable(ValueWrapper):
|
|||||||
# TODO we need to add the contextualized value.
|
# TODO we need to add the contextualized value.
|
||||||
value_set |= item_value_set.get_item(lazy_values[0].infer(), None)
|
value_set |= item_value_set.get_item(lazy_values[0].infer(), None)
|
||||||
else:
|
else:
|
||||||
value_set |= ValueSet([iterable.FakeSequence(
|
value_set |= ValueSet([iterable.FakeList(
|
||||||
self._wrapped_value.inference_state,
|
self._wrapped_value.inference_state,
|
||||||
'list',
|
|
||||||
[
|
[
|
||||||
LazyKnownValues(item_value_set.get_item(lazy_value.infer(), None))
|
LazyKnownValues(item_value_set.get_item(lazy_value.infer(), None))
|
||||||
for lazy_value in lazy_values
|
for lazy_value in lazy_values
|
||||||
@@ -681,17 +674,17 @@ class Wrapped(ValueWrapper, FunctionMixin):
|
|||||||
return [self]
|
return [self]
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('*args, /', want_obj=True, want_arguments=True)
|
@argument_clinic('*args, /', want_value=True, want_arguments=True)
|
||||||
def _operator_itemgetter(args_value_set, obj, arguments):
|
def _operator_itemgetter(args_value_set, value, arguments):
|
||||||
return ValueSet([
|
return ValueSet([
|
||||||
ItemGetterCallable(instance, args_value_set)
|
ItemGetterCallable(instance, args_value_set)
|
||||||
for instance in obj.py__call__(arguments)
|
for instance in value.py__call__(arguments)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def _create_string_input_function(func):
|
def _create_string_input_function(func):
|
||||||
@argument_clinic('string, /', want_obj=True, want_arguments=True)
|
@argument_clinic('string, /', want_value=True, want_arguments=True)
|
||||||
def wrapper(strings, obj, arguments):
|
def wrapper(strings, value, arguments):
|
||||||
def iterate():
|
def iterate():
|
||||||
for value in strings:
|
for value in strings:
|
||||||
s = get_str_or_none(value)
|
s = get_str_or_none(value)
|
||||||
@@ -701,7 +694,7 @@ def _create_string_input_function(func):
|
|||||||
values = ValueSet(iterate())
|
values = ValueSet(iterate())
|
||||||
if values:
|
if values:
|
||||||
return values
|
return values
|
||||||
return obj.py__call__(arguments)
|
return value.py__call__(arguments)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@@ -738,14 +731,15 @@ _implemented = {
|
|||||||
'iter': builtins_iter,
|
'iter': builtins_iter,
|
||||||
'staticmethod': builtins_staticmethod,
|
'staticmethod': builtins_staticmethod,
|
||||||
'classmethod': builtins_classmethod,
|
'classmethod': builtins_classmethod,
|
||||||
|
'property': builtins_property,
|
||||||
},
|
},
|
||||||
'copy': {
|
'copy': {
|
||||||
'copy': _return_first_param,
|
'copy': _return_first_param,
|
||||||
'deepcopy': _return_first_param,
|
'deepcopy': _return_first_param,
|
||||||
},
|
},
|
||||||
'json': {
|
'json': {
|
||||||
'load': lambda obj, arguments, callback: NO_VALUES,
|
'load': lambda value, arguments, callback: NO_VALUES,
|
||||||
'loads': lambda obj, arguments, callback: NO_VALUES,
|
'loads': lambda value, arguments, callback: NO_VALUES,
|
||||||
},
|
},
|
||||||
'collections': {
|
'collections': {
|
||||||
'namedtuple': collections_namedtuple,
|
'namedtuple': collections_namedtuple,
|
||||||
@@ -772,7 +766,7 @@ _implemented = {
|
|||||||
# The _alias function just leads to some annoying type inference.
|
# The _alias function just leads to some annoying type inference.
|
||||||
# Therefore, just make it return nothing, which leads to the stubs
|
# Therefore, just make it return nothing, which leads to the stubs
|
||||||
# being used instead. This only matters for 3.7+.
|
# being used instead. This only matters for 3.7+.
|
||||||
'_alias': lambda obj, arguments, callback: NO_VALUES,
|
'_alias': lambda value, arguments, callback: NO_VALUES,
|
||||||
},
|
},
|
||||||
'dataclasses': {
|
'dataclasses': {
|
||||||
# For now this works at least better than Jedi trying to understand it.
|
# For now this works at least better than Jedi trying to understand it.
|
||||||
@@ -902,8 +896,8 @@ class EnumInstance(LazyValueWrapper):
|
|||||||
return ValueName(self, self._name.tree_name)
|
return ValueName(self, self._name.tree_name)
|
||||||
|
|
||||||
def _get_wrapped_value(self):
|
def _get_wrapped_value(self):
|
||||||
obj, = self._cls.execute_with_values()
|
value, = self._cls.execute_with_values()
|
||||||
return obj
|
return value
|
||||||
|
|
||||||
def get_filters(self, origin_scope=None):
|
def get_filters(self, origin_scope=None):
|
||||||
yield DictFilter(dict(
|
yield DictFilter(dict(
|
||||||
|
|||||||
@@ -261,11 +261,17 @@ def y(a):
|
|||||||
#?
|
#?
|
||||||
y(**d)
|
y(**d)
|
||||||
|
|
||||||
|
#? str()
|
||||||
|
d['a']
|
||||||
|
|
||||||
# problem with more complicated casts
|
# problem with more complicated casts
|
||||||
dic = {str(key): ''}
|
dic = {str(key): ''}
|
||||||
#? str()
|
#? str()
|
||||||
dic['']
|
dic['']
|
||||||
|
|
||||||
|
# Just skip Python 2 tests from here. EoL soon, I'm too lazy for it.
|
||||||
|
# python > 2.7
|
||||||
|
|
||||||
|
|
||||||
for x in {1: 3.0, '': 1j}:
|
for x in {1: 3.0, '': 1j}:
|
||||||
#? int() str()
|
#? int() str()
|
||||||
@@ -278,11 +284,19 @@ d = dict(a=3, b='')
|
|||||||
x, = d.values()
|
x, = d.values()
|
||||||
#? int() str()
|
#? int() str()
|
||||||
x
|
x
|
||||||
#? int() str()
|
#? int()
|
||||||
d['a']
|
d['a']
|
||||||
#? int() str() None
|
#? int() str() None
|
||||||
d.get('a')
|
d.get('a')
|
||||||
|
|
||||||
|
some_dct = dict({'a': 1, 'b': ''}, a=1.0)
|
||||||
|
#? float()
|
||||||
|
some_dct['a']
|
||||||
|
#? str()
|
||||||
|
some_dct['b']
|
||||||
|
#? int() float() str()
|
||||||
|
some_dct['c']
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# with variable as index
|
# with variable as index
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ second = 1
|
|||||||
second = ""
|
second = ""
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
var_class = TestClass(1)
|
var_class = TestClass(1)
|
||||||
|
self.pseudo_var = 3
|
||||||
|
|
||||||
def __init__(self2, first_param, second_param, third=1.0):
|
def __init__(self2, first_param, second_param, third=1.0):
|
||||||
self2.var_inst = first_param
|
self2.var_inst = first_param
|
||||||
@@ -85,6 +86,10 @@ TestClass.var
|
|||||||
inst.var_local
|
inst.var_local
|
||||||
#? []
|
#? []
|
||||||
TestClass.var_local.
|
TestClass.var_local.
|
||||||
|
#?
|
||||||
|
TestClass.pseudo_var
|
||||||
|
#?
|
||||||
|
TestClass().pseudo_var
|
||||||
|
|
||||||
#? int()
|
#? int()
|
||||||
TestClass().ret(1)
|
TestClass().ret(1)
|
||||||
|
|||||||
@@ -118,15 +118,15 @@ class D(): pass
|
|||||||
class E(): pass
|
class E(): pass
|
||||||
lst = [1]
|
lst = [1]
|
||||||
lst.append(1.0)
|
lst.append(1.0)
|
||||||
lst += [C]
|
lst += [C()]
|
||||||
s = set(lst)
|
s = set(lst)
|
||||||
s.add("")
|
s.add("")
|
||||||
s += [D]
|
s += [D()]
|
||||||
lst = list(s)
|
lst = list(s)
|
||||||
lst.append({})
|
lst.append({})
|
||||||
lst += [E]
|
lst += [E()]
|
||||||
|
|
||||||
##? dict() int() float() str() C D E
|
#? dict() int() float() str() C() D() E()
|
||||||
lst[0]
|
lst[0]
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
@@ -307,3 +307,77 @@ lst.append('')
|
|||||||
#? float() int() str()
|
#? float() int() str()
|
||||||
lst[0]
|
lst[0]
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# list setitem
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
some_lst = [int]
|
||||||
|
some_lst[3] = str
|
||||||
|
#? int
|
||||||
|
some_lst[0]
|
||||||
|
#? str
|
||||||
|
some_lst[3]
|
||||||
|
#? int str
|
||||||
|
some_lst[2]
|
||||||
|
|
||||||
|
some_lst[0] = tuple
|
||||||
|
#? tuple
|
||||||
|
some_lst[0]
|
||||||
|
#? int str tuple
|
||||||
|
some_lst[1]
|
||||||
|
|
||||||
|
some_lst2 = list([1])
|
||||||
|
some_lst2[3] = ''
|
||||||
|
#? int() str()
|
||||||
|
some_lst2[0]
|
||||||
|
#? str()
|
||||||
|
some_lst2[3]
|
||||||
|
#? int() str()
|
||||||
|
some_lst2[2]
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# set setitem/other modifications (should not work)
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
some_set = {int}
|
||||||
|
some_set[3] = str
|
||||||
|
#? int
|
||||||
|
some_set[0]
|
||||||
|
#? int
|
||||||
|
some_set[3]
|
||||||
|
|
||||||
|
something = object()
|
||||||
|
something[3] = str
|
||||||
|
#?
|
||||||
|
something[0]
|
||||||
|
#?
|
||||||
|
something[3]
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# dict setitem
|
||||||
|
# -----------------
|
||||||
|
|
||||||
|
some_dct = {'a': float, 1: int}
|
||||||
|
some_dct['x'] = list
|
||||||
|
some_dct['y'] = tuple
|
||||||
|
#? list
|
||||||
|
some_dct['x']
|
||||||
|
#? int float list tuple
|
||||||
|
some_dct['unknown']
|
||||||
|
#? float
|
||||||
|
some_dct['a']
|
||||||
|
|
||||||
|
some_dct = dict({'a': 1, 1: ''})
|
||||||
|
#? int() str()
|
||||||
|
some_dct['la']
|
||||||
|
#? int()
|
||||||
|
some_dct['a']
|
||||||
|
|
||||||
|
some_dct['x'] = list
|
||||||
|
some_dct['y'] = tuple
|
||||||
|
#? list
|
||||||
|
some_dct['x']
|
||||||
|
#? int() str() list tuple
|
||||||
|
some_dct['unknown']
|
||||||
|
#? int()
|
||||||
|
some_dct['a']
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ there should never be any errors.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# wait until keywords are out of definitions (pydoc function).
|
# wait until keywords are out of definitions (pydoc function).
|
||||||
##? 5
|
#? 5
|
||||||
's'()
|
's'()
|
||||||
|
|
||||||
#? []
|
#? []
|
||||||
@@ -32,7 +32,7 @@ def wrong_indents():
|
|||||||
asdf
|
asdf
|
||||||
asdf(
|
asdf(
|
||||||
# TODO this seems to be wrong now?
|
# TODO this seems to be wrong now?
|
||||||
##? int()
|
#? int()
|
||||||
asdf
|
asdf
|
||||||
def openbrace():
|
def openbrace():
|
||||||
asdf = 3
|
asdf = 3
|
||||||
|
|||||||
@@ -422,3 +422,15 @@ def cast_tests():
|
|||||||
|
|
||||||
#? str()
|
#? str()
|
||||||
cast_tests()
|
cast_tests()
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# dynamic
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
def dynamic_annotation(x: int):
|
||||||
|
#? int()
|
||||||
|
return x
|
||||||
|
|
||||||
|
#? int()
|
||||||
|
dynamic_annotation('')
|
||||||
|
|||||||
@@ -240,8 +240,7 @@ cls().s
|
|||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
z = zipfile.ZipFile("foo")
|
z = zipfile.ZipFile("foo")
|
||||||
# It's too slow. So we don't run it at the moment.
|
#? ['upper']
|
||||||
##? ['upper']
|
|
||||||
z.read('name').upper
|
z.read('name').upper
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from os import dirname
|
from os.path import dirname
|
||||||
|
|
||||||
sys.path.insert(0, '../../jedi')
|
sys.path.insert(0, '../../jedi')
|
||||||
sys.path.append(dirname(os.path.abspath('thirdparty' + os.path.sep + 'asdf')))
|
sys.path.append(os.path.join(dirname(__file__), 'thirdparty'))
|
||||||
|
|
||||||
# modifications, that should fail:
|
# modifications, that should fail:
|
||||||
# syntax err
|
# syntax err
|
||||||
@@ -17,8 +17,8 @@ import inference
|
|||||||
inference.inference_state_fu
|
inference.inference_state_fu
|
||||||
|
|
||||||
# Those don't work because dirname and abspath are not properly understood.
|
# Those don't work because dirname and abspath are not properly understood.
|
||||||
##? ['jedi_']
|
#? ['jedi_']
|
||||||
import jedi_
|
import jedi_
|
||||||
|
|
||||||
##? ['el']
|
#? ['el']
|
||||||
jedi_.el
|
jedi_.el
|
||||||
|
|||||||
@@ -150,8 +150,7 @@ class NestedClass():
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
# Shouldn't find a definition, because there's other `instance`.
|
# Shouldn't find a definition, because there's other `instance`.
|
||||||
# TODO reenable that test
|
#< (0, 14),
|
||||||
##< (0, 14),
|
|
||||||
NestedClass().instance
|
NestedClass().instance
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ Test all things related to the ``jedi.api`` module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
import pytest
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
from parso import cache
|
from parso import cache
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ from jedi import preload_module
|
|||||||
from jedi.inference.gradual import typeshed
|
from jedi.inference.gradual import typeshed
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info[0] == 2, reason="Ignore Python 2, EoL")
|
||||||
def test_preload_modules():
|
def test_preload_modules():
|
||||||
def check_loaded(*modules):
|
def check_loaded(*modules):
|
||||||
for grammar_cache in cache.parser_cache.values():
|
for grammar_cache in cache.parser_cache.values():
|
||||||
@@ -101,7 +104,7 @@ def test_completion_on_hex_literals(Script):
|
|||||||
_check_number(Script, '0xE7.', 'int')
|
_check_number(Script, '0xE7.', 'int')
|
||||||
_check_number(Script, '0xEa.', 'int')
|
_check_number(Script, '0xEa.', 'int')
|
||||||
# theoretically, but people can just check for syntax errors:
|
# theoretically, but people can just check for syntax errors:
|
||||||
#assert Script('0x.').completions() == []
|
assert Script('0x.').completions() == []
|
||||||
|
|
||||||
|
|
||||||
def test_completion_on_complex_literals(Script):
|
def test_completion_on_complex_literals(Script):
|
||||||
|
|||||||
@@ -266,11 +266,11 @@ def _params(Script, source, line=None, column=None):
|
|||||||
def test_int_params(Script):
|
def test_int_params(Script):
|
||||||
sig1, sig2 = Script('int(').call_signatures()
|
sig1, sig2 = Script('int(').call_signatures()
|
||||||
# int is defined as: `int(x[, base])`
|
# int is defined as: `int(x[, base])`
|
||||||
assert len(sig1.params) == 2
|
assert len(sig1.params) == 1
|
||||||
assert sig1.params[0].name == 'x'
|
assert sig1.params[0].name == 'x'
|
||||||
assert sig1.params[1].name == 'base'
|
assert len(sig2.params) == 2
|
||||||
assert len(sig2.params) == 1
|
|
||||||
assert sig2.params[0].name == 'x'
|
assert sig2.params[0].name == 'x'
|
||||||
|
assert sig2.params[1].name == 'base'
|
||||||
|
|
||||||
|
|
||||||
def test_pow_params(Script):
|
def test_pow_params(Script):
|
||||||
|
|||||||
@@ -266,3 +266,25 @@ def test_dataclass_signature(Script, skip_pre_python37, start, start_params):
|
|||||||
assert quantity.name == 'int'
|
assert quantity.name == 'int'
|
||||||
price, = sig.params[-2].infer()
|
price, = sig.params[-2].infer()
|
||||||
assert price.name == 'float'
|
assert price.name == 'float'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'stmt, expected', [
|
||||||
|
('args = 1', 'wrapped(*args, b, c)'),
|
||||||
|
('args = (1,)', 'wrapped(*args, c)'),
|
||||||
|
('kwargs = 1', 'wrapped(b, /, **kwargs)'),
|
||||||
|
('kwargs = dict(b=3)', 'wrapped(b, /, **kwargs)'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_param_resolving_to_static(Script, stmt, expected, skip_pre_python35):
|
||||||
|
code = dedent('''\
|
||||||
|
def full_redirect(func):
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
{stmt}
|
||||||
|
return func(1, *args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
def simple(a, b, *, c): ...
|
||||||
|
full_redirect(simple)('''.format(stmt=stmt))
|
||||||
|
|
||||||
|
sig, = Script(code).call_signatures()
|
||||||
|
assert sig.to_string() == expected
|
||||||
|
|||||||
Reference in New Issue
Block a user