mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-09 07:14:48 +08:00
Merge branch 'values'
This commit is contained in:
@@ -33,6 +33,7 @@ from jedi.evaluate.helpers import get_module_names, evaluate_call_of_leaf
|
|||||||
from jedi.evaluate.sys_path import get_venv_path, dotted_path_in_sys_path
|
from jedi.evaluate.sys_path import get_venv_path, dotted_path_in_sys_path
|
||||||
from jedi.evaluate.iterable import unpack_tuple_to_dict
|
from jedi.evaluate.iterable import unpack_tuple_to_dict
|
||||||
from jedi.evaluate.filters import TreeNameDefinition
|
from jedi.evaluate.filters import TreeNameDefinition
|
||||||
|
from jedi.evaluate.syntax_tree import tree_name_to_contexts
|
||||||
|
|
||||||
# Jedi uses lots and lots of recursion. By setting this a little bit higher, we
|
# Jedi uses lots and lots of recursion. By setting this a little bit higher, we
|
||||||
# can remove some "maximum recursion depth" errors.
|
# can remove some "maximum recursion depth" errors.
|
||||||
@@ -319,10 +320,8 @@ class Script(object):
|
|||||||
for node in get_executable_nodes(module_node):
|
for node in get_executable_nodes(module_node):
|
||||||
context = self._get_module().create_context(node)
|
context = self._get_module().create_context(node)
|
||||||
if node.type in ('funcdef', 'classdef'):
|
if node.type in ('funcdef', 'classdef'):
|
||||||
# TODO This is stupid, should be private
|
|
||||||
from jedi.evaluate.finder import _name_to_types
|
|
||||||
# Resolve the decorators.
|
# Resolve the decorators.
|
||||||
_name_to_types(self._evaluator, context, node.children[1])
|
tree_name_to_contexts(self._evaluator, context, node.children[1])
|
||||||
elif isinstance(node, tree.Import):
|
elif isinstance(node, tree.Import):
|
||||||
import_names = set(node.get_defined_names())
|
import_names = set(node.get_defined_names())
|
||||||
if node.is_nested():
|
if node.is_nested():
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from parso.python.tree import search_ancestor
|
|||||||
|
|
||||||
from jedi._compatibility import u
|
from jedi._compatibility import u
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import common
|
from jedi.evaluate.utils import ignored, unite
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
from jedi.evaluate import instance
|
from jedi.evaluate import instance
|
||||||
@@ -290,7 +290,7 @@ class BaseDefinition(object):
|
|||||||
if not path:
|
if not path:
|
||||||
return None # for keywords the path is empty
|
return None # for keywords the path is empty
|
||||||
|
|
||||||
with common.ignored(KeyError):
|
with ignored(KeyError):
|
||||||
path[0] = self._mapping[path[0]]
|
path[0] = self._mapping[path[0]]
|
||||||
for key, repl in self._tuple_mapping.items():
|
for key, repl in self._tuple_mapping.items():
|
||||||
if tuple(path[:len(key)]) == key:
|
if tuple(path[:len(key)]) == key:
|
||||||
@@ -567,7 +567,7 @@ class Definition(BaseDefinition):
|
|||||||
"""
|
"""
|
||||||
defs = self._name.infer()
|
defs = self._name.infer()
|
||||||
return sorted(
|
return sorted(
|
||||||
common.unite(defined_names(self._evaluator, d) for d in defs),
|
unite(defined_names(self._evaluator, d) for d in defs),
|
||||||
key=lambda s: s._name.start_pos or (0, 0)
|
key=lambda s: s._name.start_pos or (0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from parso.python import tree
|
|||||||
from parso import split_lines
|
from parso import split_lines
|
||||||
|
|
||||||
from jedi._compatibility import u
|
from jedi._compatibility import u
|
||||||
|
from jedi.evaluate.syntax_tree import eval_atom
|
||||||
from jedi.evaluate.helpers import evaluate_call_of_leaf
|
from jedi.evaluate.helpers import evaluate_call_of_leaf
|
||||||
from jedi.cache import time_cache
|
from jedi.cache import time_cache
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ def evaluate_goto_definition(evaluator, context, leaf):
|
|||||||
elif parent.type == 'trailer':
|
elif parent.type == 'trailer':
|
||||||
return evaluate_call_of_leaf(context, leaf)
|
return evaluate_call_of_leaf(context, leaf)
|
||||||
elif isinstance(leaf, tree.Literal):
|
elif isinstance(leaf, tree.Literal):
|
||||||
return context.evaluator.eval_atom(context, leaf)
|
return eval_atom(context, leaf)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import pydoc
|
|||||||
import keyword
|
import keyword
|
||||||
|
|
||||||
from jedi._compatibility import is_py3, is_py35
|
from jedi._compatibility import is_py3, is_py35
|
||||||
from jedi import common
|
from jedi.evaluate.utils import ignored
|
||||||
from jedi.evaluate.filters import AbstractNameDefinition
|
from jedi.evaluate.filters import AbstractNameDefinition
|
||||||
from parso.python.tree import Leaf
|
from parso.python.tree import Leaf
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ def imitate_pydoc(string):
|
|||||||
# with unicode strings)
|
# with unicode strings)
|
||||||
string = str(string)
|
string = str(string)
|
||||||
h = pydoc.help
|
h = pydoc.help
|
||||||
with common.ignored(KeyError):
|
with ignored(KeyError):
|
||||||
# try to access symbols
|
# try to access symbols
|
||||||
string = h.symbols[string]
|
string = h.symbols[string]
|
||||||
string, _, related = string.partition(' ')
|
string, _, related = string.partition(' ')
|
||||||
|
|||||||
1
jedi/common/__init__.py
Normal file
1
jedi/common/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from jedi.common.context import BaseContextSet
|
||||||
54
jedi/common/context.py
Normal file
54
jedi/common/context.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
class BaseContextSet(object):
|
||||||
|
def __init__(self, *args):
|
||||||
|
self._set = set(args)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_iterable(cls, iterable):
|
||||||
|
return cls.from_set(set(iterable))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_set(cls, set_):
|
||||||
|
self = cls()
|
||||||
|
self._set = set_
|
||||||
|
return self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_sets(cls, sets):
|
||||||
|
"""
|
||||||
|
Used to work with an iterable of set.
|
||||||
|
"""
|
||||||
|
aggregated = set()
|
||||||
|
sets = list(sets)
|
||||||
|
for set_ in sets:
|
||||||
|
if isinstance(set_, BaseContextSet):
|
||||||
|
aggregated |= set_._set
|
||||||
|
else:
|
||||||
|
aggregated |= set_
|
||||||
|
return cls.from_set(aggregated)
|
||||||
|
|
||||||
|
def __or__(self, other):
|
||||||
|
return type(self).from_set(self._set | other._set)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for element in self._set:
|
||||||
|
yield element
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return bool(self._set)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._set)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%s)' % (self.__class__.__name__, ', '.join(str(s) for s in self._set))
|
||||||
|
|
||||||
|
def filter(self, filter_func):
|
||||||
|
return type(self).from_iterable(filter(filter_func, self._set))
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
def mapper(*args, **kwargs):
|
||||||
|
return type(self).from_sets(
|
||||||
|
getattr(context, name)(*args, **kwargs)
|
||||||
|
for context in self._set
|
||||||
|
)
|
||||||
|
return mapper
|
||||||
@@ -12,29 +12,31 @@ Evaluation of Python code in |jedi| is based on three assumptions:
|
|||||||
* The programmer is not a total dick, e.g. like `this
|
* The programmer is not a total dick, e.g. like `this
|
||||||
<https://github.com/davidhalter/jedi/issues/24>`_ :-)
|
<https://github.com/davidhalter/jedi/issues/24>`_ :-)
|
||||||
|
|
||||||
The actual algorithm is based on a principle called lazy evaluation. If you
|
The actual algorithm is based on a principle called lazy evaluation. That
|
||||||
don't know about it, google it. That said, the typical entry point for static
|
said, the typical entry point for static analysis is calling
|
||||||
analysis is calling ``eval_statement``. There's separate logic for
|
``eval_expr_stmt``. There's separate logic for autocompletion in the API, the
|
||||||
autocompletion in the API, the evaluator is all about evaluating an expression.
|
evaluator is all about evaluating an expression.
|
||||||
|
|
||||||
Now you need to understand what follows after ``eval_statement``. Let's
|
TODO this paragraph is not what jedi does anymore.
|
||||||
|
|
||||||
|
Now you need to understand what follows after ``eval_expr_stmt``. Let's
|
||||||
make an example::
|
make an example::
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
datetime.date.toda# <-- cursor here
|
datetime.date.toda# <-- cursor here
|
||||||
|
|
||||||
First of all, this module doesn't care about completion. It really just cares
|
First of all, this module doesn't care about completion. It really just cares
|
||||||
about ``datetime.date``. At the end of the procedure ``eval_statement`` will
|
about ``datetime.date``. At the end of the procedure ``eval_expr_stmt`` will
|
||||||
return the ``date`` class.
|
return the ``date`` class.
|
||||||
|
|
||||||
To *visualize* this (simplified):
|
To *visualize* this (simplified):
|
||||||
|
|
||||||
- ``Evaluator.eval_statement`` doesn't do much, because there's no assignment.
|
- ``Evaluator.eval_expr_stmt`` doesn't do much, because there's no assignment.
|
||||||
- ``Evaluator.eval_element`` cares for resolving the dotted path
|
- ``Context.eval_node`` cares for resolving the dotted path
|
||||||
- ``Evaluator.find_types`` searches for global definitions of datetime, which
|
- ``Evaluator.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.
|
||||||
- Now ``find_types`` is called again by ``eval_element`` to find ``date``
|
- Now ``find_types`` is called again by ``eval_node`` to find ``date``
|
||||||
inside the datetime module.
|
inside the datetime module.
|
||||||
|
|
||||||
Now what would happen if we wanted ``datetime.date.foo.bar``? Two more
|
Now what would happen if we wanted ``datetime.date.foo.bar``? Two more
|
||||||
@@ -46,7 +48,7 @@ What if the import would contain another ``ExprStmt`` like this::
|
|||||||
from foo import bar
|
from foo import bar
|
||||||
Date = bar.baz
|
Date = bar.baz
|
||||||
|
|
||||||
Well... You get it. Just another ``eval_statement`` recursion. It's really
|
Well... You get it. Just another ``eval_expr_stmt`` recursion. It's really
|
||||||
easy. Python can obviously get way more complicated then this. To understand
|
easy. Python can obviously get way more complicated then this. To understand
|
||||||
tuple assignments, list comprehensions and everything else, a lot more code had
|
tuple assignments, list comprehensions and everything else, a lot more code had
|
||||||
to be written.
|
to be written.
|
||||||
@@ -67,48 +69,23 @@ from parso.python import tree
|
|||||||
import parso
|
import parso
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.common import unite
|
from jedi.evaluate.utils import unite
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi.evaluate.cache import evaluator_function_cache
|
from jedi.evaluate.cache import evaluator_function_cache
|
||||||
from jedi.evaluate import stdlib
|
|
||||||
from jedi.evaluate import finder
|
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import precedence
|
|
||||||
from jedi.evaluate import param
|
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import pep0484
|
|
||||||
from jedi.evaluate.filters import TreeNameDefinition, ParamName
|
from jedi.evaluate.filters import TreeNameDefinition, ParamName
|
||||||
from jedi.evaluate.instance import AnonymousInstance, BoundMethod
|
from jedi.evaluate.instance import AnonymousInstance, BoundMethod
|
||||||
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
|
from jedi.evaluate.context import ContextualizedName, ContextualizedNode, \
|
||||||
|
ContextSet, NO_CONTEXTS, iterate_contexts
|
||||||
|
from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \
|
||||||
|
eval_node, check_tuple_assignments
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
|
|
||||||
|
|
||||||
def _limit_context_infers(func):
|
|
||||||
"""
|
|
||||||
This is for now the way how we limit type inference going wild. There are
|
|
||||||
other ways to ensure recursion limits as well. This is mostly necessary
|
|
||||||
because of instance (self) access that can be quite tricky to limit.
|
|
||||||
|
|
||||||
I'm still not sure this is the way to go, but it looks okay for now and we
|
|
||||||
can still go anther way in the future. Tests are there. ~ dave
|
|
||||||
"""
|
|
||||||
def wrapper(evaluator, context, *args, **kwargs):
|
|
||||||
n = context.tree_node
|
|
||||||
try:
|
|
||||||
evaluator.inferred_element_counts[n] += 1
|
|
||||||
if evaluator.inferred_element_counts[n] > 300:
|
|
||||||
debug.warning('In context %s there were too many inferences.', n)
|
|
||||||
return set()
|
|
||||||
except KeyError:
|
|
||||||
evaluator.inferred_element_counts[n] = 1
|
|
||||||
return func(evaluator, context, *args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
class Evaluator(object):
|
class Evaluator(object):
|
||||||
def __init__(self, grammar, sys_path=None):
|
def __init__(self, grammar, sys_path=None):
|
||||||
self.grammar = grammar
|
self.grammar = grammar
|
||||||
@@ -141,82 +118,9 @@ class Evaluator(object):
|
|||||||
self.recursion_detector = recursion.RecursionDetector()
|
self.recursion_detector = recursion.RecursionDetector()
|
||||||
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
|
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
|
||||||
|
|
||||||
def find_types(self, context, name_or_str, name_context, position=None,
|
|
||||||
search_global=False, is_goto=False, analysis_errors=True):
|
|
||||||
"""
|
|
||||||
This is the search function. The most important part to debug.
|
|
||||||
`remove_statements` and `filter_statements` really are the core part of
|
|
||||||
this completion.
|
|
||||||
|
|
||||||
:param position: Position of the last statement -> tuple of line, column
|
|
||||||
:return: List of Names. Their parents are the types.
|
|
||||||
"""
|
|
||||||
f = finder.NameFinder(self, context, name_context, name_or_str,
|
|
||||||
position, analysis_errors=analysis_errors)
|
|
||||||
filters = f.get_filters(search_global)
|
|
||||||
if is_goto:
|
|
||||||
return f.filter_name(filters)
|
|
||||||
return f.find(filters, attribute_lookup=not search_global)
|
|
||||||
|
|
||||||
@_limit_context_infers
|
|
||||||
def eval_statement(self, context, stmt, seek_name=None):
|
|
||||||
with recursion.execution_allowed(self, stmt) as allowed:
|
|
||||||
if allowed or context.get_root_context() == self.BUILTINS:
|
|
||||||
return self._eval_stmt(context, stmt, seek_name)
|
|
||||||
return set()
|
|
||||||
|
|
||||||
#@evaluator_function_cache(default=[])
|
|
||||||
@debug.increase_indent
|
|
||||||
def _eval_stmt(self, context, stmt, seek_name=None):
|
|
||||||
"""
|
|
||||||
The starting point of the completion. A statement always owns a call
|
|
||||||
list, which are the calls, that a statement does. In case multiple
|
|
||||||
names are defined in the statement, `seek_name` returns the result for
|
|
||||||
this name.
|
|
||||||
|
|
||||||
:param stmt: A `tree.ExprStmt`.
|
|
||||||
"""
|
|
||||||
debug.dbg('eval_statement %s (%s)', stmt, seek_name)
|
|
||||||
rhs = stmt.get_rhs()
|
|
||||||
types = self.eval_element(context, rhs)
|
|
||||||
|
|
||||||
if seek_name:
|
|
||||||
c_node = ContextualizedName(context, seek_name)
|
|
||||||
types = finder.check_tuple_assignments(self, c_node, types)
|
|
||||||
|
|
||||||
first_operator = next(stmt.yield_operators(), None)
|
|
||||||
if first_operator not in ('=', None) and first_operator.type == 'operator':
|
|
||||||
# `=` is always the last character in aug assignments -> -1
|
|
||||||
operator = copy.copy(first_operator)
|
|
||||||
operator.value = operator.value[:-1]
|
|
||||||
name = stmt.get_defined_names()[0].value
|
|
||||||
left = context.py__getattribute__(
|
|
||||||
name, position=stmt.start_pos, search_global=True)
|
|
||||||
|
|
||||||
for_stmt = tree.search_ancestor(stmt, 'for_stmt')
|
|
||||||
if for_stmt is not None and for_stmt.type == 'for_stmt' and types \
|
|
||||||
and parser_utils.for_stmt_defines_one_name(for_stmt):
|
|
||||||
# Iterate through result and add the values, that's possible
|
|
||||||
# only in for loops without clutter, because they are
|
|
||||||
# predictable. Also only do it, if the variable is not a tuple.
|
|
||||||
node = for_stmt.get_testlist()
|
|
||||||
cn = ContextualizedNode(context, node)
|
|
||||||
ordered = list(iterable.py__iter__(self, cn.infer(), cn))
|
|
||||||
|
|
||||||
for lazy_context in ordered:
|
|
||||||
dct = {for_stmt.children[1].value: lazy_context.infer()}
|
|
||||||
with helpers.predefine_names(context, for_stmt, dct):
|
|
||||||
t = self.eval_element(context, rhs)
|
|
||||||
left = precedence.calculate(self, context, left, operator, t)
|
|
||||||
types = left
|
|
||||||
else:
|
|
||||||
types = precedence.calculate(self, context, left, operator, types)
|
|
||||||
debug.dbg('eval_statement result %s', types)
|
|
||||||
return types
|
|
||||||
|
|
||||||
def eval_element(self, context, element):
|
def eval_element(self, context, element):
|
||||||
if isinstance(context, iterable.CompForContext):
|
if isinstance(context, iterable.CompForContext):
|
||||||
return self._eval_element_not_cached(context, element)
|
return eval_node(context, element)
|
||||||
|
|
||||||
if_stmt = element
|
if_stmt = element
|
||||||
while if_stmt is not None:
|
while if_stmt is not None:
|
||||||
@@ -261,23 +165,23 @@ class Evaluator(object):
|
|||||||
new_name_dicts = list(original_name_dicts)
|
new_name_dicts = list(original_name_dicts)
|
||||||
for i, name_dict in enumerate(new_name_dicts):
|
for i, name_dict in enumerate(new_name_dicts):
|
||||||
new_name_dicts[i] = name_dict.copy()
|
new_name_dicts[i] = name_dict.copy()
|
||||||
new_name_dicts[i][if_name.value] = set([definition])
|
new_name_dicts[i][if_name.value] = ContextSet(definition)
|
||||||
|
|
||||||
name_dicts += new_name_dicts
|
name_dicts += new_name_dicts
|
||||||
else:
|
else:
|
||||||
for name_dict in name_dicts:
|
for name_dict in name_dicts:
|
||||||
name_dict[if_name.value] = definitions
|
name_dict[if_name.value] = definitions
|
||||||
if len(name_dicts) > 1:
|
if len(name_dicts) > 1:
|
||||||
result = set()
|
result = ContextSet()
|
||||||
for name_dict in name_dicts:
|
for name_dict in name_dicts:
|
||||||
with helpers.predefine_names(context, if_stmt, name_dict):
|
with helpers.predefine_names(context, if_stmt, name_dict):
|
||||||
result |= self._eval_element_not_cached(context, element)
|
result |= eval_node(context, element)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
return self._eval_element_if_evaluated(context, element)
|
return self._eval_element_if_evaluated(context, element)
|
||||||
else:
|
else:
|
||||||
if predefined_if_name_dict:
|
if predefined_if_name_dict:
|
||||||
return self._eval_element_not_cached(context, element)
|
return eval_node(context, element)
|
||||||
else:
|
else:
|
||||||
return self._eval_element_if_evaluated(context, element)
|
return self._eval_element_if_evaluated(context, element)
|
||||||
|
|
||||||
@@ -290,201 +194,32 @@ class Evaluator(object):
|
|||||||
parent = parent.parent
|
parent = parent.parent
|
||||||
predefined_if_name_dict = context.predefined_names.get(parent)
|
predefined_if_name_dict = context.predefined_names.get(parent)
|
||||||
if predefined_if_name_dict is not None:
|
if predefined_if_name_dict is not None:
|
||||||
return self._eval_element_not_cached(context, element)
|
return eval_node(context, element)
|
||||||
return self._eval_element_cached(context, element)
|
return self._eval_element_cached(context, element)
|
||||||
|
|
||||||
@evaluator_function_cache(default=set())
|
@evaluator_function_cache(default=NO_CONTEXTS)
|
||||||
def _eval_element_cached(self, context, element):
|
def _eval_element_cached(self, context, element):
|
||||||
return self._eval_element_not_cached(context, element)
|
return eval_node(context, element)
|
||||||
|
|
||||||
@debug.increase_indent
|
|
||||||
@_limit_context_infers
|
|
||||||
def _eval_element_not_cached(self, context, element):
|
|
||||||
debug.dbg('eval_element %s@%s', element, element.start_pos)
|
|
||||||
types = set()
|
|
||||||
typ = element.type
|
|
||||||
if typ in ('name', 'number', 'string', 'atom'):
|
|
||||||
types = self.eval_atom(context, element)
|
|
||||||
elif typ == 'keyword':
|
|
||||||
# For False/True/None
|
|
||||||
if element.value in ('False', 'True', 'None'):
|
|
||||||
types.add(compiled.builtin_from_name(self, element.value))
|
|
||||||
# else: print e.g. could be evaluated like this in Python 2.7
|
|
||||||
elif typ == 'lambdef':
|
|
||||||
types = set([er.FunctionContext(self, context, element)])
|
|
||||||
elif typ == 'expr_stmt':
|
|
||||||
types = self.eval_statement(context, element)
|
|
||||||
elif typ in ('power', 'atom_expr'):
|
|
||||||
first_child = element.children[0]
|
|
||||||
if not (first_child.type == 'keyword' and first_child.value == 'await'):
|
|
||||||
types = self.eval_atom(context, first_child)
|
|
||||||
for trailer in element.children[1:]:
|
|
||||||
if trailer == '**': # has a power operation.
|
|
||||||
right = self.eval_element(context, element.children[2])
|
|
||||||
types = set(precedence.calculate(self, context, types, trailer, right))
|
|
||||||
break
|
|
||||||
types = self.eval_trailer(context, types, trailer)
|
|
||||||
elif typ in ('testlist_star_expr', 'testlist',):
|
|
||||||
# The implicit tuple in statements.
|
|
||||||
types = set([iterable.SequenceLiteralContext(self, context, element)])
|
|
||||||
elif typ in ('not_test', 'factor'):
|
|
||||||
types = self.eval_element(context, element.children[-1])
|
|
||||||
for operator in element.children[:-1]:
|
|
||||||
types = set(precedence.factor_calculate(self, types, operator))
|
|
||||||
elif typ == 'test':
|
|
||||||
# `x if foo else y` case.
|
|
||||||
types = (self.eval_element(context, element.children[0]) |
|
|
||||||
self.eval_element(context, element.children[-1]))
|
|
||||||
elif typ == 'operator':
|
|
||||||
# Must be an ellipsis, other operators are not evaluated.
|
|
||||||
# In Python 2 ellipsis is coded as three single dot tokens, not
|
|
||||||
# as one token 3 dot token.
|
|
||||||
assert element.value in ('.', '...')
|
|
||||||
types = set([compiled.create(self, Ellipsis)])
|
|
||||||
elif typ == 'dotted_name':
|
|
||||||
types = self.eval_atom(context, element.children[0])
|
|
||||||
for next_name in element.children[2::2]:
|
|
||||||
# TODO add search_global=True?
|
|
||||||
types = unite(
|
|
||||||
typ.py__getattribute__(next_name, name_context=context)
|
|
||||||
for typ in types
|
|
||||||
)
|
|
||||||
types = types
|
|
||||||
elif typ == 'eval_input':
|
|
||||||
types = self._eval_element_not_cached(context, element.children[0])
|
|
||||||
elif typ == 'annassign':
|
|
||||||
types = pep0484._evaluate_for_annotation(context, element.children[1])
|
|
||||||
else:
|
|
||||||
types = precedence.calculate_children(self, context, element.children)
|
|
||||||
debug.dbg('eval_element result %s', types)
|
|
||||||
return types
|
|
||||||
|
|
||||||
def eval_atom(self, context, atom):
|
|
||||||
"""
|
|
||||||
Basically to process ``atom`` nodes. The parser sometimes doesn't
|
|
||||||
generate the node (because it has just one child). In that case an atom
|
|
||||||
might be a name or a literal as well.
|
|
||||||
"""
|
|
||||||
if atom.type == 'name':
|
|
||||||
# This is the first global lookup.
|
|
||||||
stmt = tree.search_ancestor(
|
|
||||||
atom, 'expr_stmt', 'lambdef'
|
|
||||||
) or atom
|
|
||||||
if stmt.type == 'lambdef':
|
|
||||||
stmt = atom
|
|
||||||
return context.py__getattribute__(
|
|
||||||
name_or_str=atom,
|
|
||||||
position=stmt.start_pos,
|
|
||||||
search_global=True
|
|
||||||
)
|
|
||||||
elif isinstance(atom, tree.Literal):
|
|
||||||
string = parser_utils.safe_literal_eval(atom.value)
|
|
||||||
return set([compiled.create(self, string)])
|
|
||||||
else:
|
|
||||||
c = atom.children
|
|
||||||
if c[0].type == 'string':
|
|
||||||
# Will be one string.
|
|
||||||
types = self.eval_atom(context, c[0])
|
|
||||||
for string in c[1:]:
|
|
||||||
right = self.eval_atom(context, string)
|
|
||||||
types = precedence.calculate(self, context, types, '+', right)
|
|
||||||
return types
|
|
||||||
# Parentheses without commas are not tuples.
|
|
||||||
elif c[0] == '(' and not len(c) == 2 \
|
|
||||||
and not(c[1].type == 'testlist_comp' and
|
|
||||||
len(c[1].children) > 1):
|
|
||||||
return self.eval_element(context, c[1])
|
|
||||||
|
|
||||||
try:
|
|
||||||
comp_for = c[1].children[1]
|
|
||||||
except (IndexError, AttributeError):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if comp_for == ':':
|
|
||||||
# Dict comprehensions have a colon at the 3rd index.
|
|
||||||
try:
|
|
||||||
comp_for = c[1].children[3]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if comp_for.type == 'comp_for':
|
|
||||||
return set([iterable.Comprehension.from_atom(self, context, atom)])
|
|
||||||
|
|
||||||
# It's a dict/list/tuple literal.
|
|
||||||
array_node = c[1]
|
|
||||||
try:
|
|
||||||
array_node_c = array_node.children
|
|
||||||
except AttributeError:
|
|
||||||
array_node_c = []
|
|
||||||
if c[0] == '{' and (array_node == '}' or ':' in array_node_c):
|
|
||||||
context = iterable.DictLiteralContext(self, context, atom)
|
|
||||||
else:
|
|
||||||
context = iterable.SequenceLiteralContext(self, context, atom)
|
|
||||||
return set([context])
|
|
||||||
|
|
||||||
def eval_trailer(self, context, types, trailer):
|
|
||||||
trailer_op, node = trailer.children[:2]
|
|
||||||
if node == ')': # `arglist` is optional.
|
|
||||||
node = ()
|
|
||||||
|
|
||||||
new_types = set()
|
|
||||||
if trailer_op == '[':
|
|
||||||
new_types |= iterable.py__getitem__(self, context, types, trailer)
|
|
||||||
else:
|
|
||||||
for typ in types:
|
|
||||||
debug.dbg('eval_trailer: %s in scope %s', trailer, typ)
|
|
||||||
if trailer_op == '.':
|
|
||||||
new_types |= typ.py__getattribute__(
|
|
||||||
name_context=context,
|
|
||||||
name_or_str=node
|
|
||||||
)
|
|
||||||
elif trailer_op == '(':
|
|
||||||
arguments = param.TreeArguments(self, context, node, trailer)
|
|
||||||
new_types |= self.execute(typ, arguments)
|
|
||||||
return new_types
|
|
||||||
|
|
||||||
@debug.increase_indent
|
|
||||||
def execute(self, obj, arguments):
|
|
||||||
if self.is_analysis:
|
|
||||||
arguments.eval_all()
|
|
||||||
|
|
||||||
debug.dbg('execute: %s %s', obj, arguments)
|
|
||||||
try:
|
|
||||||
# Some stdlib functions like super(), namedtuple(), etc. have been
|
|
||||||
# hard-coded in Jedi to support them.
|
|
||||||
return stdlib.execute(self, obj, arguments)
|
|
||||||
except stdlib.NotInStdLib:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
func = obj.py__call__
|
|
||||||
except AttributeError:
|
|
||||||
debug.warning("no execution possible %s", obj)
|
|
||||||
return set()
|
|
||||||
else:
|
|
||||||
types = func(arguments)
|
|
||||||
debug.dbg('execute result: %s in %s', types, obj)
|
|
||||||
return types
|
|
||||||
|
|
||||||
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:
|
||||||
type_ = def_.type
|
type_ = def_.type
|
||||||
if type_ == 'classdef':
|
if type_ == 'classdef':
|
||||||
return [er.ClassContext(self, name.parent, context)]
|
return [er.ClassContext(self, context, name.parent)]
|
||||||
elif type_ == 'funcdef':
|
elif type_ == 'funcdef':
|
||||||
return [er.FunctionContext(self, context, name.parent)]
|
return [er.FunctionContext(self, context, name.parent)]
|
||||||
|
|
||||||
if type_ == 'expr_stmt':
|
if type_ == 'expr_stmt':
|
||||||
is_simple_name = name.parent.type not in ('power', 'trailer')
|
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||||
if is_simple_name:
|
if is_simple_name:
|
||||||
return self.eval_statement(context, def_, name)
|
return eval_expr_stmt(context, def_, name)
|
||||||
if type_ == 'for_stmt':
|
if type_ == 'for_stmt':
|
||||||
container_types = self.eval_element(context, def_.children[3])
|
container_types = context.eval_node(def_.children[3])
|
||||||
cn = ContextualizedNode(context, def_.children[3])
|
cn = ContextualizedNode(context, def_.children[3])
|
||||||
for_types = iterable.py__iter__types(self, container_types, cn)
|
for_types = iterate_contexts(container_types, cn)
|
||||||
c_node = ContextualizedName(context, name)
|
c_node = ContextualizedName(context, name)
|
||||||
return finder.check_tuple_assignments(self, c_node, for_types)
|
return check_tuple_assignments(self, c_node, 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)
|
||||||
|
|
||||||
@@ -509,25 +244,25 @@ class Evaluator(object):
|
|||||||
return module_names
|
return module_names
|
||||||
|
|
||||||
par = name.parent
|
par = name.parent
|
||||||
typ = par.type
|
node_type = par.type
|
||||||
if typ == 'argument' and par.children[1] == '=' and par.children[0] == name:
|
if node_type == 'argument' and par.children[1] == '=' and par.children[0] == name:
|
||||||
# Named param goto.
|
# Named param goto.
|
||||||
trailer = par.parent
|
trailer = par.parent
|
||||||
if trailer.type == 'arglist':
|
if trailer.type == 'arglist':
|
||||||
trailer = trailer.parent
|
trailer = trailer.parent
|
||||||
if trailer.type != 'classdef':
|
if trailer.type != 'classdef':
|
||||||
if trailer.type == 'decorator':
|
if trailer.type == 'decorator':
|
||||||
types = self.eval_element(context, trailer.children[1])
|
context_set = context.eval_node(trailer.children[1])
|
||||||
else:
|
else:
|
||||||
i = trailer.parent.children.index(trailer)
|
i = trailer.parent.children.index(trailer)
|
||||||
to_evaluate = trailer.parent.children[:i]
|
to_evaluate = trailer.parent.children[:i]
|
||||||
types = self.eval_element(context, to_evaluate[0])
|
context_set = context.eval_node(to_evaluate[0])
|
||||||
for trailer in to_evaluate[1:]:
|
for trailer in to_evaluate[1:]:
|
||||||
types = self.eval_trailer(context, types, trailer)
|
context_set = eval_trailer(context, context_set, trailer)
|
||||||
param_names = []
|
param_names = []
|
||||||
for typ in types:
|
for context in context_set:
|
||||||
try:
|
try:
|
||||||
get_param_names = typ.get_param_names
|
get_param_names = context.get_param_names
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -535,18 +270,18 @@ class Evaluator(object):
|
|||||||
if param_name.string_name == name.value:
|
if param_name.string_name == name.value:
|
||||||
param_names.append(param_name)
|
param_names.append(param_name)
|
||||||
return param_names
|
return param_names
|
||||||
elif typ == 'dotted_name': # Is a decorator.
|
elif node_type == 'dotted_name': # Is a decorator.
|
||||||
index = par.children.index(name)
|
index = par.children.index(name)
|
||||||
if index > 0:
|
if index > 0:
|
||||||
new_dotted = helpers.deep_ast_copy(par)
|
new_dotted = helpers.deep_ast_copy(par)
|
||||||
new_dotted.children[index - 1:] = []
|
new_dotted.children[index - 1:] = []
|
||||||
values = self.eval_element(context, new_dotted)
|
values = context.eval_node(new_dotted)
|
||||||
return unite(
|
return unite(
|
||||||
value.py__getattribute__(name, name_context=context, is_goto=True)
|
value.py__getattribute__(name, name_context=context, is_goto=True)
|
||||||
for value in values
|
for value in values
|
||||||
)
|
)
|
||||||
|
|
||||||
if typ == 'trailer' and par.children[0] == '.':
|
if node_type == 'trailer' and par.children[0] == '.':
|
||||||
values = helpers.evaluate_call_of_leaf(context, name, cut_own_trailer=True)
|
values = helpers.evaluate_call_of_leaf(context, name, cut_own_trailer=True)
|
||||||
return unite(
|
return unite(
|
||||||
value.py__getattribute__(name, name_context=context, is_goto=True)
|
value.py__getattribute__(name, name_context=context, is_goto=True)
|
||||||
@@ -604,7 +339,7 @@ class Evaluator(object):
|
|||||||
return func.get_function_execution()
|
return func.get_function_execution()
|
||||||
return func
|
return func
|
||||||
elif scope_node.type == 'classdef':
|
elif scope_node.type == 'classdef':
|
||||||
class_context = er.ClassContext(self, scope_node, parent_context)
|
class_context = er.ClassContext(self, parent_context, scope_node)
|
||||||
if child_is_funcdef:
|
if child_is_funcdef:
|
||||||
# anonymous instance
|
# anonymous instance
|
||||||
return AnonymousInstance(self, parent_context, class_context)
|
return AnonymousInstance(self, parent_context, class_context)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
- ``CachedMetaClass`` uses ``_memoize_default`` to do the same with classes.
|
- ``CachedMetaClass`` uses ``_memoize_default`` to do the same with classes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
_NO_DEFAULT = object()
|
_NO_DEFAULT = object()
|
||||||
|
|
||||||
|
|
||||||
@@ -40,8 +38,6 @@ def _memoize_default(default=_NO_DEFAULT, evaluator_is_first_arg=False, second_a
|
|||||||
if default is not _NO_DEFAULT:
|
if default is not _NO_DEFAULT:
|
||||||
memo[key] = default
|
memo[key] = default
|
||||||
rv = function(obj, *args, **kwargs)
|
rv = function(obj, *args, **kwargs)
|
||||||
if inspect.isgenerator(rv):
|
|
||||||
rv = list(rv)
|
|
||||||
memo[key] = rv
|
memo[key] = rv
|
||||||
return rv
|
return rv
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from jedi import debug
|
|||||||
from jedi.cache import underscore_memoization, memoize_method
|
from jedi.cache import underscore_memoization, memoize_method
|
||||||
from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition, \
|
from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition, \
|
||||||
ContextNameMixin
|
ContextNameMixin
|
||||||
from jedi.evaluate.context import Context, LazyKnownContext
|
from jedi.evaluate.context import Context, LazyKnownContext, ContextSet
|
||||||
from jedi.evaluate.compiled.getattr_static import getattr_static
|
from jedi.evaluate.compiled.getattr_static import getattr_static
|
||||||
from . import fake
|
from . import fake
|
||||||
|
|
||||||
@@ -83,9 +83,9 @@ class CompiledObject(Context):
|
|||||||
def py__call__(self, params):
|
def py__call__(self, params):
|
||||||
if inspect.isclass(self.obj):
|
if inspect.isclass(self.obj):
|
||||||
from jedi.evaluate.instance import CompiledInstance
|
from jedi.evaluate.instance import CompiledInstance
|
||||||
return set([CompiledInstance(self.evaluator, self.parent_context, self, params)])
|
return ContextSet(CompiledInstance(self.evaluator, self.parent_context, self, params))
|
||||||
else:
|
else:
|
||||||
return set(self._execute_function(params))
|
return ContextSet.from_iterable(self._execute_function(params))
|
||||||
|
|
||||||
@CheckAttribute
|
@CheckAttribute
|
||||||
def py__class__(self):
|
def py__class__(self):
|
||||||
@@ -221,9 +221,9 @@ class CompiledObject(Context):
|
|||||||
def py__getitem__(self, index):
|
def py__getitem__(self, index):
|
||||||
if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict):
|
if type(self.obj) not in (str, list, tuple, unicode, bytes, bytearray, dict):
|
||||||
# Get rid of side effects, we won't call custom `__getitem__`s.
|
# Get rid of side effects, we won't call custom `__getitem__`s.
|
||||||
return set()
|
return ContextSet()
|
||||||
|
|
||||||
return set([create(self.evaluator, self.obj[index])])
|
return ContextSet(create(self.evaluator, self.obj[index]))
|
||||||
|
|
||||||
@CheckAttribute
|
@CheckAttribute
|
||||||
def py__iter__(self):
|
def py__iter__(self):
|
||||||
@@ -266,7 +266,7 @@ class CompiledObject(Context):
|
|||||||
# TODO do we?
|
# TODO do we?
|
||||||
continue
|
continue
|
||||||
bltn_obj = create(self.evaluator, bltn_obj)
|
bltn_obj = create(self.evaluator, bltn_obj)
|
||||||
for result in self.evaluator.execute(bltn_obj, params):
|
for result in bltn_obj.execute(params):
|
||||||
yield result
|
yield result
|
||||||
for type_ in docstrings.infer_return_types(self):
|
for type_ in docstrings.infer_return_types(self):
|
||||||
yield type_
|
yield type_
|
||||||
@@ -278,7 +278,9 @@ class CompiledObject(Context):
|
|||||||
return [] # Builtins don't have imports
|
return [] # Builtins don't have imports
|
||||||
|
|
||||||
def dict_values(self):
|
def dict_values(self):
|
||||||
return set(create(self.evaluator, v) for v in self.obj.values())
|
return ContextSet.from_iterable(
|
||||||
|
create(self.evaluator, v) for v in self.obj.values()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CompiledName(AbstractNameDefinition):
|
class CompiledName(AbstractNameDefinition):
|
||||||
@@ -301,7 +303,9 @@ class CompiledName(AbstractNameDefinition):
|
|||||||
@underscore_memoization
|
@underscore_memoization
|
||||||
def infer(self):
|
def infer(self):
|
||||||
module = self.parent_context.get_root_context()
|
module = self.parent_context.get_root_context()
|
||||||
return [_create_from_name(self._evaluator, module, self.parent_context, self.string_name)]
|
return ContextSet(_create_from_name(
|
||||||
|
self._evaluator, module, self.parent_context, self.string_name
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
class SignatureParamName(AbstractNameDefinition):
|
class SignatureParamName(AbstractNameDefinition):
|
||||||
@@ -318,13 +322,13 @@ class SignatureParamName(AbstractNameDefinition):
|
|||||||
def infer(self):
|
def infer(self):
|
||||||
p = self._signature_param
|
p = self._signature_param
|
||||||
evaluator = self.parent_context.evaluator
|
evaluator = self.parent_context.evaluator
|
||||||
types = set()
|
contexts = ContextSet()
|
||||||
if p.default is not p.empty:
|
if p.default is not p.empty:
|
||||||
types.add(create(evaluator, p.default))
|
contexts = ContextSet(create(evaluator, p.default))
|
||||||
if p.annotation is not p.empty:
|
if p.annotation is not p.empty:
|
||||||
annotation = create(evaluator, p.annotation)
|
annotation = create(evaluator, p.annotation)
|
||||||
types |= annotation.execute_evaluated()
|
contexts |= annotation.execute_evaluated()
|
||||||
return types
|
return contexts
|
||||||
|
|
||||||
|
|
||||||
class UnresolvableParamName(AbstractNameDefinition):
|
class UnresolvableParamName(AbstractNameDefinition):
|
||||||
@@ -335,7 +339,7 @@ class UnresolvableParamName(AbstractNameDefinition):
|
|||||||
self.string_name = name
|
self.string_name = name
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return set()
|
return ContextSet()
|
||||||
|
|
||||||
|
|
||||||
class CompiledContextName(ContextNameMixin, AbstractNameDefinition):
|
class CompiledContextName(ContextNameMixin, AbstractNameDefinition):
|
||||||
@@ -356,7 +360,7 @@ class EmptyCompiledName(AbstractNameDefinition):
|
|||||||
self.string_name = name
|
self.string_name = name
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return []
|
return ContextSet()
|
||||||
|
|
||||||
|
|
||||||
class CompiledObjectFilter(AbstractFilter):
|
class CompiledObjectFilter(AbstractFilter):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from jedi import settings
|
|||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.cache import underscore_memoization
|
from jedi.cache import underscore_memoization
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate.context import Context
|
from jedi.evaluate.context import Context, ContextSet
|
||||||
from jedi.evaluate.cache import evaluator_function_cache
|
from jedi.evaluate.cache import evaluator_function_cache
|
||||||
from jedi.evaluate.compiled.getattr_static import getattr_static
|
from jedi.evaluate.compiled.getattr_static import getattr_static
|
||||||
|
|
||||||
@@ -41,9 +41,6 @@ class MixedObject(object):
|
|||||||
# We have to overwrite everything that has to do with trailers, name
|
# We have to overwrite everything that has to do with trailers, name
|
||||||
# lookups and filters to make it possible to route name lookups towards
|
# lookups and filters to make it possible to route name lookups towards
|
||||||
# compiled objects and the rest towards tree node contexts.
|
# compiled objects and the rest towards tree node contexts.
|
||||||
def eval_trailer(*args, **kwags):
|
|
||||||
return Context.eval_trailer(*args, **kwags)
|
|
||||||
|
|
||||||
def py__getattribute__(*args, **kwargs):
|
def py__getattribute__(*args, **kwargs):
|
||||||
return Context.py__getattribute__(*args, **kwargs)
|
return Context.py__getattribute__(*args, **kwargs)
|
||||||
|
|
||||||
@@ -85,7 +82,9 @@ class MixedName(compiled.CompiledName):
|
|||||||
# PyQt4.QtGui.QStyleOptionComboBox.currentText
|
# PyQt4.QtGui.QStyleOptionComboBox.currentText
|
||||||
# -> just set it to None
|
# -> just set it to None
|
||||||
obj = None
|
obj = None
|
||||||
return [_create(self._evaluator, obj, parent_context=self.parent_context)]
|
return ContextSet(
|
||||||
|
_create(self._evaluator, obj, parent_context=self.parent_context)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
def api_type(self):
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from jedi._compatibility import Python3Method
|
|
||||||
from jedi.common import unite
|
|
||||||
from parso.python.tree import ExprStmt, CompFor
|
from parso.python.tree import ExprStmt, CompFor
|
||||||
|
|
||||||
|
from jedi import debug
|
||||||
|
from jedi._compatibility import Python3Method, zip_longest, unicode
|
||||||
from jedi.parser_utils import clean_scope_docstring, get_doc_with_call_signature
|
from jedi.parser_utils import clean_scope_docstring, get_doc_with_call_signature
|
||||||
|
from jedi.common import BaseContextSet
|
||||||
|
|
||||||
|
|
||||||
class Context(object):
|
class Context(object):
|
||||||
@@ -32,7 +34,36 @@ class Context(object):
|
|||||||
return context
|
return context
|
||||||
context = context.parent_context
|
context = context.parent_context
|
||||||
|
|
||||||
|
@debug.increase_indent
|
||||||
def execute(self, arguments):
|
def execute(self, arguments):
|
||||||
|
"""
|
||||||
|
In contrast to py__call__ this function is always available.
|
||||||
|
|
||||||
|
`hasattr(x, py__call__)` can also be checked to see if a context is
|
||||||
|
executable.
|
||||||
|
"""
|
||||||
|
if self.evaluator.is_analysis:
|
||||||
|
arguments.eval_all()
|
||||||
|
|
||||||
|
debug.dbg('execute: %s %s', self, arguments)
|
||||||
|
from jedi.evaluate import stdlib
|
||||||
|
try:
|
||||||
|
# Some stdlib functions like super(), namedtuple(), etc. have been
|
||||||
|
# hard-coded in Jedi to support them.
|
||||||
|
return stdlib.execute(self.evaluator, self, arguments)
|
||||||
|
except stdlib.NotInStdLib:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = self.py__call__
|
||||||
|
except AttributeError:
|
||||||
|
debug.warning("no execution possible %s", self)
|
||||||
|
return NO_CONTEXTS
|
||||||
|
else:
|
||||||
|
context_set = func(arguments)
|
||||||
|
debug.dbg('execute result: %s in %s', context_set, self)
|
||||||
|
return context_set
|
||||||
|
|
||||||
return self.evaluator.execute(self, arguments)
|
return self.evaluator.execute(self, arguments)
|
||||||
|
|
||||||
def execute_evaluated(self, *value_list):
|
def execute_evaluated(self, *value_list):
|
||||||
@@ -40,24 +71,87 @@ class Context(object):
|
|||||||
Execute a function with already executed arguments.
|
Execute a function with already executed arguments.
|
||||||
"""
|
"""
|
||||||
from jedi.evaluate.param import ValuesArguments
|
from jedi.evaluate.param import ValuesArguments
|
||||||
arguments = ValuesArguments([[value] for value in value_list])
|
arguments = ValuesArguments([ContextSet(value) for value in value_list])
|
||||||
return self.execute(arguments)
|
return self.execute(arguments)
|
||||||
|
|
||||||
|
def iterate(self, contextualized_node=None):
|
||||||
|
debug.dbg('iterate')
|
||||||
|
try:
|
||||||
|
iter_method = self.py__iter__
|
||||||
|
except AttributeError:
|
||||||
|
if contextualized_node is not None:
|
||||||
|
from jedi.evaluate import analysis
|
||||||
|
analysis.add(
|
||||||
|
contextualized_node.context,
|
||||||
|
'type-error-not-iterable',
|
||||||
|
contextualized_node.node,
|
||||||
|
message="TypeError: '%s' object is not iterable" % self)
|
||||||
|
return iter([])
|
||||||
|
else:
|
||||||
|
return iter_method()
|
||||||
|
|
||||||
|
def get_item(self, index_contexts, contextualized_node):
|
||||||
|
from jedi.evaluate.compiled import CompiledObject
|
||||||
|
from jedi.evaluate.iterable import Slice, AbstractSequence
|
||||||
|
result = ContextSet()
|
||||||
|
|
||||||
|
for index in index_contexts:
|
||||||
|
if isinstance(index, (CompiledObject, Slice)):
|
||||||
|
index = index.obj
|
||||||
|
|
||||||
|
if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)):
|
||||||
|
# If the index is not clearly defined, we have to get all the
|
||||||
|
# possiblities.
|
||||||
|
if isinstance(self, AbstractSequence) and self.array_type == 'dict':
|
||||||
|
result |= self.dict_values()
|
||||||
|
else:
|
||||||
|
result |= iterate_contexts(ContextSet(self))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# The actual getitem call.
|
||||||
|
try:
|
||||||
|
getitem = self.py__getitem__
|
||||||
|
except AttributeError:
|
||||||
|
from jedi.evaluate import analysis
|
||||||
|
# TODO this context is probably not right.
|
||||||
|
analysis.add(
|
||||||
|
contextualized_node.context,
|
||||||
|
'type-error-not-subscriptable',
|
||||||
|
contextualized_node.node,
|
||||||
|
message="TypeError: '%s' object is not subscriptable" % self
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
result |= getitem(index)
|
||||||
|
except IndexError:
|
||||||
|
result |= iterate_contexts(ContextSet(self))
|
||||||
|
except KeyError:
|
||||||
|
# Must be a dict. Lists don't raise KeyErrors.
|
||||||
|
result |= self.dict_values()
|
||||||
|
return result
|
||||||
|
|
||||||
def eval_node(self, node):
|
def eval_node(self, node):
|
||||||
return self.evaluator.eval_element(self, node)
|
return self.evaluator.eval_element(self, node)
|
||||||
|
|
||||||
def eval_stmt(self, stmt, seek_name=None):
|
|
||||||
return self.evaluator.eval_statement(self, stmt, seek_name)
|
|
||||||
|
|
||||||
def eval_trailer(self, types, trailer):
|
|
||||||
return self.evaluator.eval_trailer(self, types, trailer)
|
|
||||||
|
|
||||||
@Python3Method
|
@Python3Method
|
||||||
def py__getattribute__(self, name_or_str, name_context=None, position=None,
|
def py__getattribute__(self, name_or_str, name_context=None, position=None,
|
||||||
search_global=False, is_goto=False,
|
search_global=False, is_goto=False,
|
||||||
analysis_errors=True):
|
analysis_errors=True):
|
||||||
|
"""
|
||||||
|
This is the search function.
|
||||||
|
|
||||||
|
:param position: Position of the last statement -> tuple of line, column
|
||||||
|
"""
|
||||||
if name_context is None:
|
if name_context is None:
|
||||||
name_context = self
|
name_context = self
|
||||||
|
from jedi.evaluate import finder
|
||||||
|
f = finder.NameFinder(self.evaluator, self, name_context, name_or_str,
|
||||||
|
position, analysis_errors=analysis_errors)
|
||||||
|
filters = f.get_filters(search_global)
|
||||||
|
if is_goto:
|
||||||
|
return f.filter_name(filters)
|
||||||
|
return f.find(filters, attribute_lookup=not search_global)
|
||||||
|
|
||||||
return self.evaluator.find_types(
|
return self.evaluator.find_types(
|
||||||
self, name_or_str, name_context, position, search_global, is_goto,
|
self, name_or_str, name_context, position, search_global, is_goto,
|
||||||
analysis_errors)
|
analysis_errors)
|
||||||
@@ -88,6 +182,17 @@ class Context(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def iterate_contexts(contexts, contextualized_node=None):
|
||||||
|
"""
|
||||||
|
Calls `iterate`, on all contexts but ignores the ordering and just returns
|
||||||
|
all contexts that the iterate functions yield.
|
||||||
|
"""
|
||||||
|
return ContextSet.from_sets(
|
||||||
|
lazy_context.infer()
|
||||||
|
for lazy_context in contexts.iterate(contextualized_node)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TreeContext(Context):
|
class TreeContext(Context):
|
||||||
def __init__(self, evaluator, parent_context=None):
|
def __init__(self, evaluator, parent_context=None):
|
||||||
super(TreeContext, self).__init__(evaluator, parent_context)
|
super(TreeContext, self).__init__(evaluator, parent_context)
|
||||||
@@ -111,11 +216,11 @@ class AbstractLazyContext(object):
|
|||||||
class LazyKnownContext(AbstractLazyContext):
|
class LazyKnownContext(AbstractLazyContext):
|
||||||
"""data is a context."""
|
"""data is a context."""
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return set([self.data])
|
return ContextSet(self.data)
|
||||||
|
|
||||||
|
|
||||||
class LazyKnownContexts(AbstractLazyContext):
|
class LazyKnownContexts(AbstractLazyContext):
|
||||||
"""data is a set of contexts."""
|
"""data is a ContextSet."""
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
@@ -125,7 +230,7 @@ class LazyUnknownContext(AbstractLazyContext):
|
|||||||
super(LazyUnknownContext, self).__init__(None)
|
super(LazyUnknownContext, self).__init__(None)
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
class LazyTreeContext(AbstractLazyContext):
|
class LazyTreeContext(AbstractLazyContext):
|
||||||
@@ -155,26 +260,26 @@ def get_merged_lazy_context(lazy_contexts):
|
|||||||
class MergedLazyContexts(AbstractLazyContext):
|
class MergedLazyContexts(AbstractLazyContext):
|
||||||
"""data is a list of lazy contexts."""
|
"""data is a list of lazy contexts."""
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return unite(l.infer() for l in self.data)
|
return ContextSet.from_sets(l.infer() for l in self.data)
|
||||||
|
|
||||||
|
|
||||||
class ContextualizedNode(object):
|
class ContextualizedNode(object):
|
||||||
def __init__(self, context, node):
|
def __init__(self, context, node):
|
||||||
self.context = context
|
self.context = context
|
||||||
self._node = node
|
self.node = node
|
||||||
|
|
||||||
def get_root_context(self):
|
def get_root_context(self):
|
||||||
return self.context.get_root_context()
|
return self.context.get_root_context()
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return self.context.eval_node(self._node)
|
return self.context.eval_node(self.node)
|
||||||
|
|
||||||
|
|
||||||
class ContextualizedName(ContextualizedNode):
|
class ContextualizedName(ContextualizedNode):
|
||||||
# TODO merge with TreeNameDefinition?!
|
# TODO merge with TreeNameDefinition?!
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._node
|
return self.node
|
||||||
|
|
||||||
def assignment_indexes(self):
|
def assignment_indexes(self):
|
||||||
"""
|
"""
|
||||||
@@ -188,8 +293,8 @@ class ContextualizedName(ContextualizedNode):
|
|||||||
would result in ``[(1, xyz_node), (0, yz_node)]``.
|
would result in ``[(1, xyz_node), (0, yz_node)]``.
|
||||||
"""
|
"""
|
||||||
indexes = []
|
indexes = []
|
||||||
node = self._node.parent
|
node = self.node.parent
|
||||||
compare = self._node
|
compare = self.node
|
||||||
while node is not None:
|
while node is not None:
|
||||||
if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'):
|
if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'):
|
||||||
for i, child in enumerate(node.children):
|
for i, child in enumerate(node.children):
|
||||||
@@ -204,3 +309,25 @@ class ContextualizedName(ContextualizedNode):
|
|||||||
compare = node
|
compare = node
|
||||||
node = node.parent
|
node = node.parent
|
||||||
return indexes
|
return indexes
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSet(BaseContextSet):
|
||||||
|
def py__class__(self):
|
||||||
|
return ContextSet.from_iterable(c.py__class__() for c in self._set)
|
||||||
|
|
||||||
|
def iterate(self, contextualized_node=None):
|
||||||
|
type_iters = [c.iterate(contextualized_node) for c in self._set]
|
||||||
|
for lazy_contexts in zip_longest(*type_iters):
|
||||||
|
yield get_merged_lazy_context(
|
||||||
|
[l for l in lazy_contexts if l is not None]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
NO_CONTEXTS = ContextSet()
|
||||||
|
|
||||||
|
|
||||||
|
def iterator_to_context_set(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return ContextSet.from_iterable(func(*args, **kwargs))
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ from textwrap import dedent
|
|||||||
from parso import parse
|
from parso import parse
|
||||||
|
|
||||||
from jedi._compatibility import u
|
from jedi._compatibility import u
|
||||||
from jedi.common import unite
|
from jedi.evaluate.utils import indent_block
|
||||||
from jedi.evaluate import context
|
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.common import indent_block
|
|
||||||
from jedi.evaluate.iterable import SequenceLiteralContext, FakeSequence
|
from jedi.evaluate.iterable import SequenceLiteralContext, FakeSequence
|
||||||
|
from jedi.evaluate.context import iterator_to_context_set, ContextSet, \
|
||||||
|
NO_CONTEXTS, LazyKnownContexts
|
||||||
|
|
||||||
|
|
||||||
DOCSTRING_PARAM_PATTERNS = [
|
DOCSTRING_PARAM_PATTERNS = [
|
||||||
@@ -223,7 +223,10 @@ def _execute_types_in_stmt(module_context, stmt):
|
|||||||
contain is executed. (Used as type information).
|
contain is executed. (Used as type information).
|
||||||
"""
|
"""
|
||||||
definitions = module_context.eval_node(stmt)
|
definitions = module_context.eval_node(stmt)
|
||||||
return unite(_execute_array_values(module_context.evaluator, d) for d in definitions)
|
return ContextSet.from_sets(
|
||||||
|
_execute_array_values(module_context.evaluator, d)
|
||||||
|
for d in definitions
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _execute_array_values(evaluator, array):
|
def _execute_array_values(evaluator, array):
|
||||||
@@ -234,8 +237,11 @@ def _execute_array_values(evaluator, array):
|
|||||||
if isinstance(array, SequenceLiteralContext):
|
if isinstance(array, SequenceLiteralContext):
|
||||||
values = []
|
values = []
|
||||||
for lazy_context in array.py__iter__():
|
for lazy_context in array.py__iter__():
|
||||||
objects = unite(_execute_array_values(evaluator, typ) for typ in lazy_context.infer())
|
objects = ContextSet.from_sets(
|
||||||
values.append(context.LazyKnownContexts(objects))
|
_execute_array_values(evaluator, typ)
|
||||||
|
for typ in lazy_context.infer()
|
||||||
|
)
|
||||||
|
values.append(LazyKnownContexts(objects))
|
||||||
return set([FakeSequence(evaluator, array.array_type, values)])
|
return set([FakeSequence(evaluator, array.array_type, values)])
|
||||||
else:
|
else:
|
||||||
return array.execute_evaluated()
|
return array.execute_evaluated()
|
||||||
@@ -246,7 +252,7 @@ def infer_param(execution_context, param):
|
|||||||
from jedi.evaluate.instance import AnonymousInstanceFunctionExecution
|
from jedi.evaluate.instance import AnonymousInstanceFunctionExecution
|
||||||
|
|
||||||
def eval_docstring(docstring):
|
def eval_docstring(docstring):
|
||||||
return set(
|
return ContextSet.from_iterable(
|
||||||
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 _evaluate_for_statement_string(module_context, param_str)
|
for p in _evaluate_for_statement_string(module_context, param_str)
|
||||||
@@ -254,7 +260,7 @@ def infer_param(execution_context, param):
|
|||||||
module_context = execution_context.get_root_context()
|
module_context = execution_context.get_root_context()
|
||||||
func = param.get_parent_function()
|
func = param.get_parent_function()
|
||||||
if func.type == 'lambdef':
|
if func.type == 'lambdef':
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
types = eval_docstring(execution_context.py__doc__())
|
types = eval_docstring(execution_context.py__doc__())
|
||||||
if isinstance(execution_context, AnonymousInstanceFunctionExecution) and \
|
if isinstance(execution_context, AnonymousInstanceFunctionExecution) and \
|
||||||
@@ -266,6 +272,7 @@ def infer_param(execution_context, param):
|
|||||||
|
|
||||||
|
|
||||||
@evaluator_method_cache()
|
@evaluator_method_cache()
|
||||||
|
@iterator_to_context_set
|
||||||
def infer_return_types(function_context):
|
def infer_return_types(function_context):
|
||||||
def search_return_in_docstr(code):
|
def search_return_in_docstr(code):
|
||||||
for p in DOCSTRING_RETURN_PATTERNS:
|
for p in DOCSTRING_RETURN_PATTERNS:
|
||||||
@@ -279,4 +286,3 @@ def infer_return_types(function_context):
|
|||||||
for type_str in search_return_in_docstr(function_context.py__doc__()):
|
for type_str in search_return_in_docstr(function_context.py__doc__()):
|
||||||
for type_eval in _evaluate_for_statement_string(function_context.get_root_context(), type_str):
|
for type_eval in _evaluate_for_statement_string(function_context.get_root_context(), type_str):
|
||||||
yield type_eval
|
yield type_eval
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ It works as follows:
|
|||||||
|
|
||||||
- |Jedi| sees a param
|
- |Jedi| sees a param
|
||||||
- search for function calls named ``foo``
|
- search for function calls named ``foo``
|
||||||
- execute these calls and check the input. This work with a ``ParamListener``.
|
- execute these calls and check the input.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
@@ -24,24 +24,14 @@ from jedi.evaluate.cache import evaluator_function_cache
|
|||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate.param import TreeArguments, create_default_params
|
from jedi.evaluate.param import TreeArguments, create_default_params
|
||||||
from jedi.evaluate.helpers import is_stdlib_path
|
from jedi.evaluate.helpers import is_stdlib_path
|
||||||
from jedi.common import to_list, unite
|
from jedi.evaluate.utils import to_list
|
||||||
|
from jedi.evaluate.context import ContextSet
|
||||||
from jedi.parser_utils import get_parent_scope
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
MAX_PARAM_SEARCHES = 20
|
MAX_PARAM_SEARCHES = 20
|
||||||
|
|
||||||
|
|
||||||
class ParamListener(object):
|
|
||||||
"""
|
|
||||||
This listener is used to get the params for a function.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self.param_possibilities = []
|
|
||||||
|
|
||||||
def execute(self, params):
|
|
||||||
self.param_possibilities += params
|
|
||||||
|
|
||||||
|
|
||||||
class MergedExecutedParams(object):
|
class MergedExecutedParams(object):
|
||||||
"""
|
"""
|
||||||
Simulates being a parameter while actually just being multiple params.
|
Simulates being a parameter while actually just being multiple params.
|
||||||
@@ -50,7 +40,7 @@ class MergedExecutedParams(object):
|
|||||||
self._executed_params = executed_params
|
self._executed_params = executed_params
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return unite(p.infer() for p in self._executed_params)
|
return ContextSet.from_sets(p.infer() for p in self._executed_params)
|
||||||
|
|
||||||
|
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
@@ -103,7 +93,7 @@ def search_params(evaluator, execution_context, funcdef):
|
|||||||
evaluator.dynamic_params_depth -= 1
|
evaluator.dynamic_params_depth -= 1
|
||||||
|
|
||||||
|
|
||||||
@evaluator_function_cache(default=[])
|
@evaluator_function_cache(default=None)
|
||||||
@to_list
|
@to_list
|
||||||
def _search_function_executions(evaluator, module_context, funcdef):
|
def _search_function_executions(evaluator, module_context, funcdef):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ from abc import abstractmethod
|
|||||||
|
|
||||||
from parso.tree import search_ancestor
|
from parso.tree import search_ancestor
|
||||||
from jedi.evaluate import flow_analysis
|
from jedi.evaluate import flow_analysis
|
||||||
from jedi.common import to_list, unite
|
from jedi.evaluate.context import ContextSet
|
||||||
from jedi.parser_utils import get_parent_scope
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
from jedi.evaluate.utils import to_list
|
||||||
|
|
||||||
|
|
||||||
class AbstractNameDefinition(object):
|
class AbstractNameDefinition(object):
|
||||||
@@ -35,10 +36,10 @@ class AbstractNameDefinition(object):
|
|||||||
return '<%s: %s@%s>' % (self.__class__.__name__, self.string_name, self.start_pos)
|
return '<%s: %s@%s>' % (self.__class__.__name__, self.string_name, self.start_pos)
|
||||||
|
|
||||||
def execute(self, arguments):
|
def execute(self, arguments):
|
||||||
return unite(context.execute(arguments) for context in self.infer())
|
return self.infer().execute(arguments)
|
||||||
|
|
||||||
def execute_evaluated(self, *args, **kwargs):
|
def execute_evaluated(self, *args, **kwargs):
|
||||||
return unite(context.execute_evaluated(*args, **kwargs) for context in self.infer())
|
return self.infer().execute_evaluated(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
def api_type(self):
|
||||||
@@ -64,7 +65,7 @@ class AbstractTreeName(AbstractNameDefinition):
|
|||||||
|
|
||||||
class ContextNameMixin(object):
|
class ContextNameMixin(object):
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return set([self._context])
|
return ContextSet(self._context)
|
||||||
|
|
||||||
def get_root_context(self):
|
def get_root_context(self):
|
||||||
if self.parent_context is None:
|
if self.parent_context is None:
|
||||||
@@ -93,8 +94,8 @@ class TreeNameDefinition(AbstractTreeName):
|
|||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
# Refactor this, should probably be here.
|
# Refactor this, should probably be here.
|
||||||
from jedi.evaluate.finder import _name_to_types
|
from jedi.evaluate.syntax_tree import tree_name_to_contexts
|
||||||
return _name_to_types(self.parent_context.evaluator, self.parent_context, self.tree_name)
|
return tree_name_to_contexts(self.parent_context.evaluator, self.parent_context, self.tree_name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_type(self):
|
def api_type(self):
|
||||||
@@ -128,7 +129,7 @@ class AnonymousInstanceParamName(ParamName):
|
|||||||
if param_node.position_index == 0:
|
if param_node.position_index == 0:
|
||||||
# This is a speed optimization, to return the self param (because
|
# This is a speed optimization, to return the self param (because
|
||||||
# it's known). This only affects anonymous instances.
|
# it's known). This only affects anonymous instances.
|
||||||
return set([self.parent_context.instance])
|
return ContextSet(self.parent_context.instance)
|
||||||
else:
|
else:
|
||||||
return self.get_param().infer()
|
return self.get_param().infer()
|
||||||
|
|
||||||
|
|||||||
@@ -18,20 +18,16 @@ check for -> a is a string). There's big potential in these checks.
|
|||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
from parso.tree import search_ancestor
|
from parso.tree import search_ancestor
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.common import unite
|
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.evaluate import representation as er
|
|
||||||
from jedi.evaluate.instance import AbstractInstanceContext
|
from jedi.evaluate.instance import AbstractInstanceContext
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import pep0484
|
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi.evaluate import imports
|
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
from jedi.evaluate import flow_analysis
|
from jedi.evaluate import flow_analysis
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate.filters import get_global_filters, TreeNameDefinition
|
from jedi.evaluate.filters import get_global_filters, TreeNameDefinition
|
||||||
from jedi.evaluate.context import ContextualizedName, ContextualizedNode
|
from jedi.evaluate.context import ContextSet
|
||||||
from jedi.parser_utils import is_scope, get_parent_scope
|
from jedi.parser_utils import is_scope, get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +58,7 @@ class NameFinder(object):
|
|||||||
check = flow_analysis.reachability_check(
|
check = flow_analysis.reachability_check(
|
||||||
self._context, self._context.tree_node, self._name)
|
self._context, self._context.tree_node, self._name)
|
||||||
if check is flow_analysis.UNREACHABLE:
|
if check is flow_analysis.UNREACHABLE:
|
||||||
return set()
|
return ContextSet()
|
||||||
return self._found_predefined_types
|
return self._found_predefined_types
|
||||||
|
|
||||||
types = self._names_to_types(names, attribute_lookup)
|
types = self._names_to_types(names, attribute_lookup)
|
||||||
@@ -158,22 +154,20 @@ class NameFinder(object):
|
|||||||
return inst.execute_function_slots(names, name)
|
return inst.execute_function_slots(names, name)
|
||||||
|
|
||||||
def _names_to_types(self, names, attribute_lookup):
|
def _names_to_types(self, names, attribute_lookup):
|
||||||
types = set()
|
contexts = ContextSet.from_sets(name.infer() for name in names)
|
||||||
|
|
||||||
types = unite(name.infer() for name in names)
|
debug.dbg('finder._names_to_types: %s -> %s', names, contexts)
|
||||||
|
|
||||||
debug.dbg('finder._names_to_types: %s -> %s', names, types)
|
|
||||||
if not names and isinstance(self._context, AbstractInstanceContext):
|
if not names and isinstance(self._context, AbstractInstanceContext):
|
||||||
# handling __getattr__ / __getattribute__
|
# handling __getattr__ / __getattribute__
|
||||||
return self._check_getattr(self._context)
|
return self._check_getattr(self._context)
|
||||||
|
|
||||||
# Add isinstance and other if/assert knowledge.
|
# Add isinstance and other if/assert knowledge.
|
||||||
if not types and isinstance(self._name, tree.Name) and \
|
if not contexts and isinstance(self._name, tree.Name) and \
|
||||||
not isinstance(self._name_context, AbstractInstanceContext):
|
not isinstance(self._name_context, AbstractInstanceContext):
|
||||||
flow_scope = self._name
|
flow_scope = self._name
|
||||||
base_node = self._name_context.tree_node
|
base_node = self._name_context.tree_node
|
||||||
if base_node.type == 'comp_for':
|
if base_node.type == 'comp_for':
|
||||||
return types
|
return contexts
|
||||||
while True:
|
while True:
|
||||||
flow_scope = get_parent_scope(flow_scope, include_flows=True)
|
flow_scope = get_parent_scope(flow_scope, include_flows=True)
|
||||||
n = _check_flow_information(self._name_context, flow_scope,
|
n = _check_flow_information(self._name_context, flow_scope,
|
||||||
@@ -182,132 +176,7 @@ class NameFinder(object):
|
|||||||
return n
|
return n
|
||||||
if flow_scope == base_node:
|
if flow_scope == base_node:
|
||||||
break
|
break
|
||||||
return types
|
return contexts
|
||||||
|
|
||||||
|
|
||||||
def _name_to_types(evaluator, context, tree_name):
|
|
||||||
types = []
|
|
||||||
node = tree_name.get_definition(import_name_always=True)
|
|
||||||
if node is None:
|
|
||||||
node = tree_name.parent
|
|
||||||
if node.type == 'global_stmt':
|
|
||||||
context = evaluator.create_context(context, tree_name)
|
|
||||||
finder = NameFinder(evaluator, context, context, tree_name.value)
|
|
||||||
filters = finder.get_filters(search_global=True)
|
|
||||||
# For global_stmt lookups, we only need the first possible scope,
|
|
||||||
# which means the function itself.
|
|
||||||
filters = [next(filters)]
|
|
||||||
return finder.find(filters, attribute_lookup=False)
|
|
||||||
elif node.type not in ('import_from', 'import_name'):
|
|
||||||
raise ValueError("Should not happen.")
|
|
||||||
|
|
||||||
typ = node.type
|
|
||||||
if typ == 'for_stmt':
|
|
||||||
types = pep0484.find_type_from_comment_hint_for(context, node, tree_name)
|
|
||||||
if types:
|
|
||||||
return types
|
|
||||||
if typ == 'with_stmt':
|
|
||||||
types = pep0484.find_type_from_comment_hint_with(context, node, tree_name)
|
|
||||||
if types:
|
|
||||||
return types
|
|
||||||
if typ in ('for_stmt', 'comp_for'):
|
|
||||||
try:
|
|
||||||
types = context.predefined_names[node][tree_name.value]
|
|
||||||
except KeyError:
|
|
||||||
cn = ContextualizedNode(context, node.children[3])
|
|
||||||
for_types = iterable.py__iter__types(evaluator, cn.infer(), cn)
|
|
||||||
c_node = ContextualizedName(context, tree_name)
|
|
||||||
types = check_tuple_assignments(evaluator, c_node, for_types)
|
|
||||||
elif typ == 'expr_stmt':
|
|
||||||
types = _remove_statements(evaluator, context, node, tree_name)
|
|
||||||
elif typ == 'with_stmt':
|
|
||||||
context_managers = context.eval_node(node.get_test_node_from_name(tree_name))
|
|
||||||
enter_methods = unite(
|
|
||||||
context_manager.py__getattribute__('__enter__')
|
|
||||||
for context_manager in context_managers
|
|
||||||
)
|
|
||||||
types = unite(method.execute_evaluated() for method in enter_methods)
|
|
||||||
elif typ in ('import_from', 'import_name'):
|
|
||||||
types = imports.infer_import(context, tree_name)
|
|
||||||
elif typ in ('funcdef', 'classdef'):
|
|
||||||
types = _apply_decorators(evaluator, context, node)
|
|
||||||
elif typ == 'try_stmt':
|
|
||||||
# TODO an exception can also be a tuple. Check for those.
|
|
||||||
# TODO check for types that are not classes and add it to
|
|
||||||
# the static analysis report.
|
|
||||||
exceptions = context.eval_node(tree_name.get_previous_sibling().get_previous_sibling())
|
|
||||||
types = unite(
|
|
||||||
evaluator.execute(t, param.ValuesArguments([]))
|
|
||||||
for t in exceptions
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ValueError("Should not happen.")
|
|
||||||
return types
|
|
||||||
|
|
||||||
|
|
||||||
def _apply_decorators(evaluator, context, node):
|
|
||||||
"""
|
|
||||||
Returns the function, that should to be executed in the end.
|
|
||||||
This is also the places where the decorators are processed.
|
|
||||||
"""
|
|
||||||
if node.type == 'classdef':
|
|
||||||
decoratee_context = er.ClassContext(
|
|
||||||
evaluator,
|
|
||||||
parent_context=context,
|
|
||||||
classdef=node
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
decoratee_context = er.FunctionContext(
|
|
||||||
evaluator,
|
|
||||||
parent_context=context,
|
|
||||||
funcdef=node
|
|
||||||
)
|
|
||||||
initial = values = set([decoratee_context])
|
|
||||||
for dec in reversed(node.get_decorators()):
|
|
||||||
debug.dbg('decorator: %s %s', dec, values)
|
|
||||||
dec_values = context.eval_node(dec.children[1])
|
|
||||||
trailer_nodes = dec.children[2:-1]
|
|
||||||
if trailer_nodes:
|
|
||||||
# Create a trailer and evaluate it.
|
|
||||||
trailer = tree.PythonNode('trailer', trailer_nodes)
|
|
||||||
trailer.parent = dec
|
|
||||||
dec_values = evaluator.eval_trailer(context, dec_values, trailer)
|
|
||||||
|
|
||||||
if not len(dec_values):
|
|
||||||
debug.warning('decorator not found: %s on %s', dec, node)
|
|
||||||
return initial
|
|
||||||
|
|
||||||
values = unite(dec_value.execute(param.ValuesArguments([values]))
|
|
||||||
for dec_value in dec_values)
|
|
||||||
if not len(values):
|
|
||||||
debug.warning('not possible to resolve wrappers found %s', node)
|
|
||||||
return initial
|
|
||||||
|
|
||||||
debug.dbg('decorator end %s', values)
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
def _remove_statements(evaluator, context, stmt, name):
|
|
||||||
"""
|
|
||||||
This is the part where statements are being stripped.
|
|
||||||
|
|
||||||
Due to lazy evaluation, statements like a = func; b = a; b() have to be
|
|
||||||
evaluated.
|
|
||||||
"""
|
|
||||||
types = set()
|
|
||||||
check_instance = None
|
|
||||||
|
|
||||||
pep0484types = \
|
|
||||||
pep0484.find_type_from_comment_hint_assign(context, stmt, name)
|
|
||||||
if pep0484types:
|
|
||||||
return pep0484types
|
|
||||||
types |= context.eval_stmt(stmt, seek_name=name)
|
|
||||||
|
|
||||||
if check_instance is not None:
|
|
||||||
# class renames
|
|
||||||
types = set([er.get_instance_el(evaluator, check_instance, a, True)
|
|
||||||
if isinstance(a, er.Function) else a for a in types])
|
|
||||||
return types
|
|
||||||
|
|
||||||
|
|
||||||
def _check_flow_information(context, flow, search_name, pos):
|
def _check_flow_information(context, flow, search_name, pos):
|
||||||
@@ -377,34 +246,13 @@ def _check_isinstance_type(context, element, search_name):
|
|||||||
except AssertionError:
|
except AssertionError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
result = set()
|
context_set = ContextSet()
|
||||||
for cls_or_tup in lazy_context_cls.infer():
|
for cls_or_tup in lazy_context_cls.infer():
|
||||||
if isinstance(cls_or_tup, iterable.AbstractSequence) and \
|
if isinstance(cls_or_tup, iterable.AbstractSequence) and \
|
||||||
cls_or_tup.array_type == 'tuple':
|
cls_or_tup.array_type == 'tuple':
|
||||||
for lazy_context in cls_or_tup.py__iter__():
|
for lazy_context in cls_or_tup.py__iter__():
|
||||||
for context in lazy_context.infer():
|
for context in lazy_context.infer():
|
||||||
result |= context.execute_evaluated()
|
context_set |= context.execute_evaluated()
|
||||||
else:
|
else:
|
||||||
result |= cls_or_tup.execute_evaluated()
|
context_set |= cls_or_tup.execute_evaluated()
|
||||||
return result
|
return context_set
|
||||||
|
|
||||||
|
|
||||||
def check_tuple_assignments(evaluator, contextualized_name, types):
|
|
||||||
"""
|
|
||||||
Checks if tuples are assigned.
|
|
||||||
"""
|
|
||||||
lazy_context = None
|
|
||||||
for index, node in contextualized_name.assignment_indexes():
|
|
||||||
cn = ContextualizedNode(contextualized_name.context, node)
|
|
||||||
iterated = iterable.py__iter__(evaluator, types, cn)
|
|
||||||
for _ in range(index + 1):
|
|
||||||
try:
|
|
||||||
lazy_context = next(iterated)
|
|
||||||
except StopIteration:
|
|
||||||
# We could do this with the default param in next. But this
|
|
||||||
# would allow this loop to run for a very long time if the
|
|
||||||
# index number is high. Therefore break if the loop is
|
|
||||||
# finished.
|
|
||||||
return set()
|
|
||||||
types = lazy_context.infer()
|
|
||||||
return types
|
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ from itertools import chain
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
|
||||||
|
from jedi._compatibility import unicode
|
||||||
from jedi.parser_utils import get_parent_scope
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
from jedi.evaluate.compiled import CompiledObject
|
||||||
|
|
||||||
|
|
||||||
def is_stdlib_path(path):
|
def is_stdlib_path(path):
|
||||||
@@ -87,8 +90,9 @@ def evaluate_call_of_leaf(context, leaf, cut_own_trailer=False):
|
|||||||
trailers = power.children[1:cut]
|
trailers = power.children[1:cut]
|
||||||
|
|
||||||
values = context.eval_node(base)
|
values = context.eval_node(base)
|
||||||
|
from jedi.evaluate.syntax_tree import eval_trailer
|
||||||
for trailer in trailers:
|
for trailer in trailers:
|
||||||
values = context.eval_trailer(values, trailer)
|
values = eval_trailer(context, values, trailer)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
@@ -172,3 +176,19 @@ def predefine_names(context, flow_scope, dct):
|
|||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
del predefined[flow_scope]
|
del predefined[flow_scope]
|
||||||
|
|
||||||
|
|
||||||
|
def is_compiled(context):
|
||||||
|
return isinstance(context, CompiledObject)
|
||||||
|
|
||||||
|
|
||||||
|
def is_string(context):
|
||||||
|
return is_compiled(context) and isinstance(context.obj, (str, unicode))
|
||||||
|
|
||||||
|
|
||||||
|
def is_literal(context):
|
||||||
|
return is_number(context) or is_string(context)
|
||||||
|
|
||||||
|
|
||||||
|
def is_number(context):
|
||||||
|
return is_compiled(context) and isinstance(context.obj, (int, float))
|
||||||
|
|||||||
@@ -24,18 +24,19 @@ from parso import python_bytes_to_unicode
|
|||||||
from jedi._compatibility import find_module, unicode, ImplicitNSInfo
|
from jedi._compatibility import find_module, unicode, ImplicitNSInfo
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi.common import unite
|
|
||||||
from jedi.evaluate import sys_path
|
from jedi.evaluate import sys_path
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
|
from jedi.evaluate.utils import unite
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.evaluate.filters import AbstractNameDefinition
|
from jedi.evaluate.filters import AbstractNameDefinition
|
||||||
|
from jedi.evaluate.context import ContextSet, NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
# This memoization is needed, because otherwise we will infinitely loop on
|
# This memoization is needed, because otherwise we will infinitely loop on
|
||||||
# certain imports.
|
# certain imports.
|
||||||
@evaluator_method_cache(default=set())
|
@evaluator_method_cache(default=NO_CONTEXTS)
|
||||||
def infer_import(context, tree_name, is_goto=False):
|
def infer_import(context, tree_name, is_goto=False):
|
||||||
module_context = context.get_root_context()
|
module_context = context.get_root_context()
|
||||||
import_node = search_ancestor(tree_name, 'import_name', 'import_from')
|
import_node = search_ancestor(tree_name, 'import_name', 'import_from')
|
||||||
@@ -63,7 +64,7 @@ def infer_import(context, tree_name, is_goto=False):
|
|||||||
# scopes = [NestedImportModule(module, import_node)]
|
# scopes = [NestedImportModule(module, import_node)]
|
||||||
|
|
||||||
if not types:
|
if not types:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
if from_import_name is not None:
|
if from_import_name is not None:
|
||||||
types = unite(
|
types = unite(
|
||||||
@@ -72,8 +73,11 @@ def infer_import(context, tree_name, is_goto=False):
|
|||||||
name_context=context,
|
name_context=context,
|
||||||
is_goto=is_goto,
|
is_goto=is_goto,
|
||||||
analysis_errors=False
|
analysis_errors=False
|
||||||
) for t in types
|
)
|
||||||
|
for t in types
|
||||||
)
|
)
|
||||||
|
if not is_goto:
|
||||||
|
types = ContextSet.from_set(types)
|
||||||
|
|
||||||
if not types:
|
if not types:
|
||||||
path = import_path + [from_import_name]
|
path = import_path + [from_import_name]
|
||||||
@@ -270,7 +274,7 @@ class Importer(object):
|
|||||||
|
|
||||||
def follow(self):
|
def follow(self):
|
||||||
if not self.import_path:
|
if not self.import_path:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
return self._do_import(self.import_path, self.sys_path_with_modifications())
|
return self._do_import(self.import_path, self.sys_path_with_modifications())
|
||||||
|
|
||||||
def _do_import(self, import_path, sys_path):
|
def _do_import(self, import_path, sys_path):
|
||||||
@@ -296,7 +300,7 @@ class Importer(object):
|
|||||||
|
|
||||||
module_name = '.'.join(import_parts)
|
module_name = '.'.join(import_parts)
|
||||||
try:
|
try:
|
||||||
return set([self._evaluator.modules[module_name]])
|
return ContextSet(self._evaluator.modules[module_name])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -305,7 +309,7 @@ class Importer(object):
|
|||||||
# the module cache.
|
# the module cache.
|
||||||
bases = self._do_import(import_path[:-1], sys_path)
|
bases = self._do_import(import_path[:-1], sys_path)
|
||||||
if not bases:
|
if not bases:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
# We can take the first element, because only the os special
|
# We can take the first element, because only the os special
|
||||||
# case yields multiple modules, which is not important for
|
# case yields multiple modules, which is not important for
|
||||||
# further imports.
|
# further imports.
|
||||||
@@ -323,7 +327,7 @@ class Importer(object):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
# The module is not a package.
|
# The module is not a package.
|
||||||
_add_error(self.module_context, import_path[-1])
|
_add_error(self.module_context, import_path[-1])
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
paths = method()
|
paths = method()
|
||||||
debug.dbg('search_module %s in paths %s', module_name, paths)
|
debug.dbg('search_module %s in paths %s', module_name, paths)
|
||||||
@@ -340,7 +344,7 @@ class Importer(object):
|
|||||||
module_path = None
|
module_path = None
|
||||||
if module_path is None:
|
if module_path is None:
|
||||||
_add_error(self.module_context, import_path[-1])
|
_add_error(self.module_context, import_path[-1])
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
parent_module = None
|
parent_module = None
|
||||||
try:
|
try:
|
||||||
@@ -356,7 +360,7 @@ class Importer(object):
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
# The module is not a package.
|
# The module is not a package.
|
||||||
_add_error(self.module_context, import_path[-1])
|
_add_error(self.module_context, import_path[-1])
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
code = None
|
code = None
|
||||||
if is_pkg:
|
if is_pkg:
|
||||||
@@ -383,10 +387,10 @@ class Importer(object):
|
|||||||
if module is None:
|
if module is None:
|
||||||
# The file might raise an ImportError e.g. and therefore not be
|
# The file might raise an ImportError e.g. and therefore not be
|
||||||
# importable.
|
# importable.
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
self._evaluator.modules[module_name] = module
|
self._evaluator.modules[module_name] = module
|
||||||
return set([module])
|
return ContextSet(module)
|
||||||
|
|
||||||
def _generate_name(self, name, in_module=None):
|
def _generate_name(self, name, in_module=None):
|
||||||
# Create a pseudo import to be able to follow them.
|
# Create a pseudo import to be able to follow them.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from abc import abstractproperty
|
from abc import abstractproperty
|
||||||
|
|
||||||
from jedi._compatibility import is_py3
|
from jedi._compatibility import is_py3
|
||||||
from jedi.common import unite
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import filters
|
from jedi.evaluate import filters
|
||||||
from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts
|
from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts, \
|
||||||
|
ContextSet, iterator_to_context_set, NO_CONTEXTS
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.evaluate.param import AbstractArguments, AnonymousArguments
|
from jedi.evaluate.param import AbstractArguments, AnonymousArguments
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
@@ -58,7 +58,7 @@ class AbstractInstanceContext(Context):
|
|||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
def execute(arguments):
|
def execute(arguments):
|
||||||
return unite(name.execute(arguments) for name in names)
|
return ContextSet.from_sets(name.execute(arguments) for name in names)
|
||||||
|
|
||||||
return execute
|
return execute
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ class AbstractInstanceContext(Context):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def execute_function_slots(self, names, *evaluated_args):
|
def execute_function_slots(self, names, *evaluated_args):
|
||||||
return unite(
|
return ContextSet.from_sets(
|
||||||
name.execute_evaluated(*evaluated_args)
|
name.execute_evaluated(*evaluated_args)
|
||||||
for name in names
|
for name in names
|
||||||
)
|
)
|
||||||
@@ -96,7 +96,7 @@ class AbstractInstanceContext(Context):
|
|||||||
none_obj = compiled.create(self.evaluator, None)
|
none_obj = compiled.create(self.evaluator, None)
|
||||||
return self.execute_function_slots(names, none_obj, obj)
|
return self.execute_function_slots(names, none_obj, obj)
|
||||||
else:
|
else:
|
||||||
return set([self])
|
return ContextSet(self)
|
||||||
|
|
||||||
def get_filters(self, search_global=None, until_position=None,
|
def get_filters(self, search_global=None, until_position=None,
|
||||||
origin_scope=None, include_self_names=True):
|
origin_scope=None, include_self_names=True):
|
||||||
@@ -122,7 +122,7 @@ class AbstractInstanceContext(Context):
|
|||||||
names = self.get_function_slot_names('__getitem__')
|
names = self.get_function_slot_names('__getitem__')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
debug.warning('No __getitem__, cannot access the array.')
|
debug.warning('No __getitem__, cannot access the array.')
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
index_obj = compiled.create(self.evaluator, index)
|
index_obj = compiled.create(self.evaluator, index)
|
||||||
return self.execute_function_slots(names, index_obj)
|
return self.execute_function_slots(names, index_obj)
|
||||||
@@ -250,6 +250,7 @@ class CompiledInstanceName(compiled.CompiledName):
|
|||||||
super(CompiledInstanceName, self).__init__(evaluator, parent_context, name)
|
super(CompiledInstanceName, self).__init__(evaluator, parent_context, name)
|
||||||
self._instance = instance
|
self._instance = instance
|
||||||
|
|
||||||
|
@iterator_to_context_set
|
||||||
def infer(self):
|
def infer(self):
|
||||||
for result_context in super(CompiledInstanceName, self).infer():
|
for result_context in super(CompiledInstanceName, self).infer():
|
||||||
if isinstance(result_context, er.FunctionContext):
|
if isinstance(result_context, er.FunctionContext):
|
||||||
@@ -311,9 +312,7 @@ class CompiledBoundMethod(compiled.CompiledObject):
|
|||||||
|
|
||||||
class InstanceNameDefinition(filters.TreeNameDefinition):
|
class InstanceNameDefinition(filters.TreeNameDefinition):
|
||||||
def infer(self):
|
def infer(self):
|
||||||
contexts = super(InstanceNameDefinition, self).infer()
|
return super(InstanceNameDefinition, self).infer()
|
||||||
for context in contexts:
|
|
||||||
yield context
|
|
||||||
|
|
||||||
|
|
||||||
class LazyInstanceName(filters.TreeNameDefinition):
|
class LazyInstanceName(filters.TreeNameDefinition):
|
||||||
@@ -331,6 +330,7 @@ class LazyInstanceName(filters.TreeNameDefinition):
|
|||||||
|
|
||||||
|
|
||||||
class LazyInstanceClassName(LazyInstanceName):
|
class LazyInstanceClassName(LazyInstanceName):
|
||||||
|
@iterator_to_context_set
|
||||||
def infer(self):
|
def infer(self):
|
||||||
for result_context in super(LazyInstanceClassName, self).infer():
|
for result_context in super(LazyInstanceClassName, self).infer():
|
||||||
if isinstance(result_context, er.FunctionContext):
|
if isinstance(result_context, er.FunctionContext):
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ It is important to note that:
|
|||||||
"""
|
"""
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import common
|
from jedi.evaluate.utils import safe_property
|
||||||
from jedi.common import unite, safe_property
|
from jedi._compatibility import is_py3
|
||||||
from jedi._compatibility import unicode, zip_longest, is_py3
|
from jedi.evaluate.utils import to_list
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
from jedi.evaluate import pep0484
|
from jedi.evaluate.helpers import is_string
|
||||||
from jedi.evaluate import context
|
|
||||||
from jedi.evaluate import precedence
|
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
|
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
|
||||||
ParserTreeFilter
|
ParserTreeFilter
|
||||||
|
from jedi.evaluate import context
|
||||||
|
from jedi.evaluate.context import ContextSet, NO_CONTEXTS, Context
|
||||||
from jedi.parser_utils import get_comp_fors
|
from jedi.parser_utils import get_comp_fors
|
||||||
|
|
||||||
|
|
||||||
@@ -53,15 +53,20 @@ class AbstractSequence(context.Context):
|
|||||||
return compiled.CompiledContextName(self, self.array_type)
|
return compiled.CompiledContextName(self, self.array_type)
|
||||||
|
|
||||||
|
|
||||||
class BuiltinMethod(object):
|
class BuiltinMethod(Context):
|
||||||
"""``Generator.__next__`` ``dict.values`` methods and so on."""
|
"""``Generator.__next__`` ``dict.values`` methods and so on."""
|
||||||
|
api_type = 'function'
|
||||||
|
|
||||||
def __init__(self, builtin_context, method, builtin_func):
|
def __init__(self, builtin_context, method, builtin_func):
|
||||||
self._builtin_context = builtin_context
|
super(BuiltinMethod, self).__init__(
|
||||||
|
builtin_context.evaluator,
|
||||||
|
parent_context=builtin_context
|
||||||
|
)
|
||||||
self._method = method
|
self._method = method
|
||||||
self._builtin_func = builtin_func
|
self._builtin_func = builtin_func
|
||||||
|
|
||||||
def py__call__(self, params):
|
def py__call__(self, params):
|
||||||
return self._method(self._builtin_context)
|
return self._method(self.parent_context)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self._builtin_func, name)
|
return getattr(self._builtin_func, name)
|
||||||
@@ -87,7 +92,7 @@ class SpecialMethodFilter(DictFilter):
|
|||||||
# always only going to be one name. The same is true for the
|
# always only going to be one name. The same is true for the
|
||||||
# inferred values.
|
# inferred values.
|
||||||
builtin_func = next(iter(filter.get(self.string_name)[0].infer()))
|
builtin_func = next(iter(filter.get(self.string_name)[0].infer()))
|
||||||
return set([BuiltinMethod(self.parent_context, self._callable, builtin_func)])
|
return ContextSet(BuiltinMethod(self.parent_context, self._callable, builtin_func))
|
||||||
|
|
||||||
def __init__(self, context, dct, builtin_context):
|
def __init__(self, context, dct, builtin_context):
|
||||||
super(SpecialMethodFilter, self).__init__(dct)
|
super(SpecialMethodFilter, self).__init__(dct)
|
||||||
@@ -104,7 +109,7 @@ class SpecialMethodFilter(DictFilter):
|
|||||||
return self.SpecialMethodName(self.context, name, value, self._builtin_context)
|
return self.SpecialMethodName(self.context, name, value, self._builtin_context)
|
||||||
|
|
||||||
|
|
||||||
def has_builtin_methods(cls):
|
def _has_builtin_methods(cls):
|
||||||
base_dct = {}
|
base_dct = {}
|
||||||
# Need to care properly about inheritance. Builtin Methods should not get
|
# Need to care properly about inheritance. Builtin Methods should not get
|
||||||
# lost, just because they are not mentioned in a class.
|
# lost, just because they are not mentioned in a class.
|
||||||
@@ -123,7 +128,7 @@ def has_builtin_methods(cls):
|
|||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
def register_builtin_method(method_name, python_version_match=None):
|
def _register_builtin_method(method_name, python_version_match=None):
|
||||||
def wrapper(func):
|
def wrapper(func):
|
||||||
if python_version_match and python_version_match != 2 + int(is_py3):
|
if python_version_match and python_version_match != 2 + int(is_py3):
|
||||||
# Some functions do only apply to certain versions.
|
# Some functions do only apply to certain versions.
|
||||||
@@ -134,16 +139,16 @@ def register_builtin_method(method_name, python_version_match=None):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@has_builtin_methods
|
@_has_builtin_methods
|
||||||
class GeneratorMixin(object):
|
class GeneratorMixin(object):
|
||||||
array_type = None
|
array_type = None
|
||||||
|
|
||||||
@register_builtin_method('send')
|
@_register_builtin_method('send')
|
||||||
@register_builtin_method('next', python_version_match=2)
|
@_register_builtin_method('next', python_version_match=2)
|
||||||
@register_builtin_method('__next__', python_version_match=3)
|
@_register_builtin_method('__next__', python_version_match=3)
|
||||||
def py__next__(self):
|
def py__next__(self):
|
||||||
# TODO add TypeError if params are given.
|
# TODO add TypeError if params are given.
|
||||||
return unite(lazy_context.infer() for lazy_context in self.py__iter__())
|
return ContextSet.from_sets(lazy_context.infer() for lazy_context in self.py__iter__())
|
||||||
|
|
||||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||||
gen_obj = compiled.get_special_object(self.evaluator, 'GENERATOR_OBJECT')
|
gen_obj = compiled.get_special_object(self.evaluator, 'GENERATOR_OBJECT')
|
||||||
@@ -234,14 +239,13 @@ class Comprehension(AbstractSequence):
|
|||||||
return CompForContext.from_comp_for(parent_context, comp_for)
|
return CompForContext.from_comp_for(parent_context, comp_for)
|
||||||
|
|
||||||
def _nested(self, comp_fors, parent_context=None):
|
def _nested(self, comp_fors, parent_context=None):
|
||||||
evaluator = self.evaluator
|
|
||||||
comp_for = comp_fors[0]
|
comp_for = comp_fors[0]
|
||||||
input_node = comp_for.children[3]
|
input_node = comp_for.children[3]
|
||||||
parent_context = parent_context or self._defining_context
|
parent_context = parent_context or self._defining_context
|
||||||
input_types = parent_context.eval_node(input_node)
|
input_types = parent_context.eval_node(input_node)
|
||||||
|
|
||||||
cn = context.ContextualizedNode(parent_context, input_node)
|
cn = context.ContextualizedNode(parent_context, input_node)
|
||||||
iterated = py__iter__(evaluator, input_types, cn)
|
iterated = input_types.iterate(cn)
|
||||||
exprlist = comp_for.children[1]
|
exprlist = comp_for.children[1]
|
||||||
for i, lazy_context in enumerate(iterated):
|
for i, lazy_context in enumerate(iterated):
|
||||||
types = lazy_context.infer()
|
types = lazy_context.infer()
|
||||||
@@ -262,7 +266,7 @@ class Comprehension(AbstractSequence):
|
|||||||
yield iterated
|
yield iterated
|
||||||
|
|
||||||
@evaluator_method_cache(default=[])
|
@evaluator_method_cache(default=[])
|
||||||
@common.to_list
|
@to_list
|
||||||
def _iterate(self):
|
def _iterate(self):
|
||||||
comp_fors = tuple(get_comp_fors(self._get_comp_for()))
|
comp_fors = tuple(get_comp_fors(self._get_comp_for()))
|
||||||
for result in self._nested(comp_fors):
|
for result in self._nested(comp_fors):
|
||||||
@@ -296,7 +300,10 @@ class ArrayMixin(object):
|
|||||||
return self.evaluator.BUILTINS
|
return self.evaluator.BUILTINS
|
||||||
|
|
||||||
def dict_values(self):
|
def dict_values(self):
|
||||||
return unite(self._defining_context.eval_node(v) for k, v in self._items())
|
return ContextSet.from_sets(
|
||||||
|
self._defining_context.eval_node(v)
|
||||||
|
for k, v in self._items()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListComprehension(ArrayMixin, Comprehension):
|
class ListComprehension(ArrayMixin, Comprehension):
|
||||||
@@ -304,7 +311,7 @@ class ListComprehension(ArrayMixin, Comprehension):
|
|||||||
|
|
||||||
def py__getitem__(self, index):
|
def py__getitem__(self, index):
|
||||||
if isinstance(index, slice):
|
if isinstance(index, slice):
|
||||||
return set([self])
|
return ContextSet(self)
|
||||||
|
|
||||||
all_types = list(self.py__iter__())
|
all_types = list(self.py__iter__())
|
||||||
return all_types[index].infer()
|
return all_types[index].infer()
|
||||||
@@ -314,7 +321,7 @@ class SetComprehension(ArrayMixin, Comprehension):
|
|||||||
array_type = 'set'
|
array_type = 'set'
|
||||||
|
|
||||||
|
|
||||||
@has_builtin_methods
|
@_has_builtin_methods
|
||||||
class DictComprehension(ArrayMixin, Comprehension):
|
class DictComprehension(ArrayMixin, Comprehension):
|
||||||
array_type = 'dict'
|
array_type = 'dict'
|
||||||
|
|
||||||
@@ -334,16 +341,16 @@ class DictComprehension(ArrayMixin, Comprehension):
|
|||||||
return self.dict_values()
|
return self.dict_values()
|
||||||
|
|
||||||
def dict_values(self):
|
def dict_values(self):
|
||||||
return unite(values for keys, values in self._iterate())
|
return ContextSet.from_sets(values for keys, values in self._iterate())
|
||||||
|
|
||||||
@register_builtin_method('values')
|
@_register_builtin_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self):
|
||||||
lazy_context = context.LazyKnownContexts(self.dict_values())
|
lazy_context = context.LazyKnownContexts(self.dict_values())
|
||||||
return set([FakeSequence(self.evaluator, 'list', [lazy_context])])
|
return ContextSet(FakeSequence(self.evaluator, 'list', [lazy_context]))
|
||||||
|
|
||||||
@register_builtin_method('items')
|
@_register_builtin_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self):
|
||||||
items = set(
|
items = ContextSet.from_iterable(
|
||||||
FakeSequence(
|
FakeSequence(
|
||||||
self.evaluator, 'tuple'
|
self.evaluator, 'tuple'
|
||||||
(context.LazyKnownContexts(keys), context.LazyKnownContexts(values))
|
(context.LazyKnownContexts(keys), context.LazyKnownContexts(values))
|
||||||
@@ -385,7 +392,7 @@ class SequenceLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
|
|
||||||
# Can raise an IndexError
|
# Can raise an IndexError
|
||||||
if isinstance(index, slice):
|
if isinstance(index, slice):
|
||||||
return set([self])
|
return ContextSet(self)
|
||||||
else:
|
else:
|
||||||
return self._defining_context.eval_node(self._items()[index])
|
return self._defining_context.eval_node(self._items()[index])
|
||||||
|
|
||||||
@@ -396,7 +403,7 @@ class SequenceLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
"""
|
"""
|
||||||
if self.array_type == 'dict':
|
if self.array_type == 'dict':
|
||||||
# Get keys.
|
# Get keys.
|
||||||
types = set()
|
types = ContextSet()
|
||||||
for k, _ in self._items():
|
for k, _ in self._items():
|
||||||
types |= self._defining_context.eval_node(k)
|
types |= self._defining_context.eval_node(k)
|
||||||
# We don't know which dict index comes first, therefore always
|
# We don't know which dict index comes first, therefore always
|
||||||
@@ -413,7 +420,7 @@ class SequenceLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
def _values(self):
|
def _values(self):
|
||||||
"""Returns a list of a list of node."""
|
"""Returns a list of a list of node."""
|
||||||
if self.array_type == 'dict':
|
if self.array_type == 'dict':
|
||||||
return unite(v for k, v in self._items())
|
return ContextSet.from_sets(v for k, v in self._items())
|
||||||
else:
|
else:
|
||||||
return self._items()
|
return self._items()
|
||||||
|
|
||||||
@@ -451,14 +458,14 @@ class SequenceLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
"""
|
"""
|
||||||
for key_node, value in self._items():
|
for key_node, value in self._items():
|
||||||
for key in self._defining_context.eval_node(key_node):
|
for key in self._defining_context.eval_node(key_node):
|
||||||
if precedence.is_string(key):
|
if is_string(key):
|
||||||
yield key.obj, context.LazyTreeContext(self._defining_context, value)
|
yield key.obj, context.LazyTreeContext(self._defining_context, value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s of %s>" % (self.__class__.__name__, self.atom)
|
return "<%s of %s>" % (self.__class__.__name__, self.atom)
|
||||||
|
|
||||||
|
|
||||||
@has_builtin_methods
|
@_has_builtin_methods
|
||||||
class DictLiteralContext(SequenceLiteralContext):
|
class DictLiteralContext(SequenceLiteralContext):
|
||||||
array_type = 'dict'
|
array_type = 'dict'
|
||||||
|
|
||||||
@@ -467,12 +474,12 @@ class DictLiteralContext(SequenceLiteralContext):
|
|||||||
self._defining_context = defining_context
|
self._defining_context = defining_context
|
||||||
self.atom = atom
|
self.atom = atom
|
||||||
|
|
||||||
@register_builtin_method('values')
|
@_register_builtin_method('values')
|
||||||
def _imitate_values(self):
|
def _imitate_values(self):
|
||||||
lazy_context = context.LazyKnownContexts(self.dict_values())
|
lazy_context = context.LazyKnownContexts(self.dict_values())
|
||||||
return set([FakeSequence(self.evaluator, 'list', [lazy_context])])
|
return ContextSet(FakeSequence(self.evaluator, 'list', [lazy_context]))
|
||||||
|
|
||||||
@register_builtin_method('items')
|
@_register_builtin_method('items')
|
||||||
def _imitate_items(self):
|
def _imitate_items(self):
|
||||||
lazy_contexts = [
|
lazy_contexts = [
|
||||||
context.LazyKnownContext(FakeSequence(
|
context.LazyKnownContext(FakeSequence(
|
||||||
@@ -482,7 +489,7 @@ class DictLiteralContext(SequenceLiteralContext):
|
|||||||
)) for key_node, value_node in self._items()
|
)) for key_node, value_node in self._items()
|
||||||
]
|
]
|
||||||
|
|
||||||
return set([FakeSequence(self.evaluator, 'list', lazy_contexts)])
|
return ContextSet(FakeSequence(self.evaluator, 'list', lazy_contexts))
|
||||||
|
|
||||||
|
|
||||||
class _FakeArray(SequenceLiteralContext):
|
class _FakeArray(SequenceLiteralContext):
|
||||||
@@ -502,7 +509,7 @@ class FakeSequence(_FakeArray):
|
|||||||
self._lazy_context_list = lazy_context_list
|
self._lazy_context_list = lazy_context_list
|
||||||
|
|
||||||
def py__getitem__(self, index):
|
def py__getitem__(self, index):
|
||||||
return set(self._lazy_context_list[index].infer())
|
return self._lazy_context_list[index].infer()
|
||||||
|
|
||||||
def py__iter__(self):
|
def py__iter__(self):
|
||||||
return self._lazy_context_list
|
return self._lazy_context_list
|
||||||
@@ -527,7 +534,7 @@ class FakeDict(_FakeArray):
|
|||||||
return self._dct[index].infer()
|
return self._dct[index].infer()
|
||||||
|
|
||||||
def dict_values(self):
|
def dict_values(self):
|
||||||
return unite(lazy_context.infer() for lazy_context in self._dct.values())
|
return ContextSet.from_sets(lazy_context.infer() for lazy_context in self._dct.values())
|
||||||
|
|
||||||
def exact_key_items(self):
|
def exact_key_items(self):
|
||||||
return self._dct.items()
|
return self._dct.items()
|
||||||
@@ -544,7 +551,7 @@ class MergedArray(_FakeArray):
|
|||||||
yield lazy_context
|
yield lazy_context
|
||||||
|
|
||||||
def py__getitem__(self, index):
|
def py__getitem__(self, index):
|
||||||
return unite(lazy_context.infer() for lazy_context in self.py__iter__())
|
return ContextSet.from_sets(lazy_context.infer() for lazy_context in self.py__iter__())
|
||||||
|
|
||||||
def _items(self):
|
def _items(self):
|
||||||
for array in self._arrays:
|
for array in self._arrays:
|
||||||
@@ -568,7 +575,7 @@ def unpack_tuple_to_dict(context, types, exprlist):
|
|||||||
dct = {}
|
dct = {}
|
||||||
parts = iter(exprlist.children[::2])
|
parts = iter(exprlist.children[::2])
|
||||||
n = 0
|
n = 0
|
||||||
for lazy_context in py__iter__(context.evaluator, types, exprlist):
|
for lazy_context in types.iterate(exprlist):
|
||||||
n += 1
|
n += 1
|
||||||
try:
|
try:
|
||||||
part = next(parts)
|
part = next(parts)
|
||||||
@@ -595,103 +602,16 @@ def unpack_tuple_to_dict(context, types, exprlist):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def py__iter__(evaluator, types, contextualized_node=None):
|
|
||||||
debug.dbg('py__iter__')
|
|
||||||
type_iters = []
|
|
||||||
for typ in types:
|
|
||||||
try:
|
|
||||||
iter_method = typ.py__iter__
|
|
||||||
except AttributeError:
|
|
||||||
if contextualized_node is not None:
|
|
||||||
analysis.add(
|
|
||||||
contextualized_node.context,
|
|
||||||
'type-error-not-iterable',
|
|
||||||
contextualized_node._node,
|
|
||||||
message="TypeError: '%s' object is not iterable" % typ)
|
|
||||||
else:
|
|
||||||
type_iters.append(iter_method())
|
|
||||||
|
|
||||||
for lazy_contexts in zip_longest(*type_iters):
|
|
||||||
yield context.get_merged_lazy_context(
|
|
||||||
[l for l in lazy_contexts if l is not None]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def py__iter__types(evaluator, types, contextualized_node=None):
|
|
||||||
"""
|
|
||||||
Calls `py__iter__`, but ignores the ordering in the end and just returns
|
|
||||||
all types that it contains.
|
|
||||||
"""
|
|
||||||
return unite(
|
|
||||||
lazy_context.infer()
|
|
||||||
for lazy_context in py__iter__(evaluator, types, contextualized_node)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def py__getitem__(evaluator, context, types, trailer):
|
|
||||||
from jedi.evaluate.representation import ClassContext
|
|
||||||
from jedi.evaluate.instance import TreeInstance
|
|
||||||
result = set()
|
|
||||||
|
|
||||||
trailer_op, node, trailer_cl = trailer.children
|
|
||||||
assert trailer_op == "["
|
|
||||||
assert trailer_cl == "]"
|
|
||||||
|
|
||||||
# special case: PEP0484 typing module, see
|
|
||||||
# https://github.com/davidhalter/jedi/issues/663
|
|
||||||
for typ in list(types):
|
|
||||||
if isinstance(typ, (ClassContext, TreeInstance)):
|
|
||||||
typing_module_types = pep0484.py__getitem__(context, typ, node)
|
|
||||||
if typing_module_types is not None:
|
|
||||||
types.remove(typ)
|
|
||||||
result |= typing_module_types
|
|
||||||
|
|
||||||
if not types:
|
|
||||||
# all consumed by special cases
|
|
||||||
return result
|
|
||||||
|
|
||||||
for index in create_index_types(evaluator, context, node):
|
|
||||||
if isinstance(index, (compiled.CompiledObject, Slice)):
|
|
||||||
index = index.obj
|
|
||||||
|
|
||||||
if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)):
|
|
||||||
# If the index is not clearly defined, we have to get all the
|
|
||||||
# possiblities.
|
|
||||||
for typ in list(types):
|
|
||||||
if isinstance(typ, AbstractSequence) and typ.array_type == 'dict':
|
|
||||||
types.remove(typ)
|
|
||||||
result |= typ.dict_values()
|
|
||||||
return result | py__iter__types(evaluator, types)
|
|
||||||
|
|
||||||
for typ in types:
|
|
||||||
# The actual getitem call.
|
|
||||||
try:
|
|
||||||
getitem = typ.py__getitem__
|
|
||||||
except AttributeError:
|
|
||||||
# TODO this context is probably not right.
|
|
||||||
analysis.add(context, 'type-error-not-subscriptable', trailer_op,
|
|
||||||
message="TypeError: '%s' object is not subscriptable" % typ)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
result |= getitem(index)
|
|
||||||
except IndexError:
|
|
||||||
result |= py__iter__types(evaluator, set([typ]))
|
|
||||||
except KeyError:
|
|
||||||
# Must be a dict. Lists don't raise KeyErrors.
|
|
||||||
result |= typ.dict_values()
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def check_array_additions(context, sequence):
|
def check_array_additions(context, sequence):
|
||||||
""" Just a mapper function for the internal _check_array_additions """
|
""" Just a mapper function for the internal _check_array_additions """
|
||||||
if sequence.array_type not in ('list', 'set'):
|
if sequence.array_type not in ('list', 'set'):
|
||||||
# TODO also check for dict updates
|
# TODO also check for dict updates
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
return _check_array_additions(context, sequence)
|
return _check_array_additions(context, sequence)
|
||||||
|
|
||||||
|
|
||||||
@evaluator_method_cache(default=set())
|
@evaluator_method_cache(default=NO_CONTEXTS)
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
def _check_array_additions(context, sequence):
|
def _check_array_additions(context, sequence):
|
||||||
"""
|
"""
|
||||||
@@ -706,7 +626,7 @@ def _check_array_additions(context, sequence):
|
|||||||
module_context = context.get_root_context()
|
module_context = context.get_root_context()
|
||||||
if not settings.dynamic_array_additions or isinstance(module_context, compiled.CompiledObject):
|
if not settings.dynamic_array_additions or isinstance(module_context, compiled.CompiledObject):
|
||||||
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
debug.dbg('Dynamic array search aborted.', color='MAGENTA')
|
||||||
return set()
|
return ContextSet()
|
||||||
|
|
||||||
def find_additions(context, arglist, add_name):
|
def find_additions(context, arglist, add_name):
|
||||||
params = list(param.TreeArguments(context.evaluator, context, arglist).unpack())
|
params = list(param.TreeArguments(context.evaluator, context, arglist).unpack())
|
||||||
@@ -714,11 +634,11 @@ def _check_array_additions(context, sequence):
|
|||||||
if add_name in ['insert']:
|
if add_name in ['insert']:
|
||||||
params = params[1:]
|
params = params[1:]
|
||||||
if add_name in ['append', 'add', 'insert']:
|
if add_name in ['append', 'add', 'insert']:
|
||||||
for key, lazy_context in params:
|
for key, whatever in params:
|
||||||
result.add(lazy_context)
|
result.add(whatever)
|
||||||
elif add_name in ['extend', 'update']:
|
elif add_name in ['extend', 'update']:
|
||||||
for key, lazy_context in params:
|
for key, lazy_context in params:
|
||||||
result |= set(py__iter__(context.evaluator, lazy_context.infer()))
|
result |= set(lazy_context.infer().iterate())
|
||||||
return result
|
return result
|
||||||
|
|
||||||
temp_param_add, settings.dynamic_params_for_other_modules = \
|
temp_param_add, settings.dynamic_params_for_other_modules = \
|
||||||
@@ -781,7 +701,7 @@ def get_dynamic_array_instance(instance):
|
|||||||
|
|
||||||
ai = _ArrayInstance(instance)
|
ai = _ArrayInstance(instance)
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
return param.ValuesArguments([[ai]])
|
return param.ValuesArguments([ContextSet(ai)])
|
||||||
|
|
||||||
|
|
||||||
class _ArrayInstance(object):
|
class _ArrayInstance(object):
|
||||||
@@ -806,7 +726,7 @@ class _ArrayInstance(object):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
for lazy in py__iter__(self.instance.evaluator, lazy_context.infer()):
|
for lazy in lazy_context.infer().iterate():
|
||||||
yield lazy
|
yield lazy
|
||||||
|
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
@@ -815,6 +735,9 @@ class _ArrayInstance(object):
|
|||||||
for addition in additions:
|
for addition in additions:
|
||||||
yield addition
|
yield addition
|
||||||
|
|
||||||
|
def iterate(self, contextualized_node=None):
|
||||||
|
return self.py__iter__()
|
||||||
|
|
||||||
|
|
||||||
class Slice(context.Context):
|
class Slice(context.Context):
|
||||||
def __init__(self, context, start, stop, step):
|
def __init__(self, context, start, stop, step):
|
||||||
@@ -852,33 +775,3 @@ class Slice(context.Context):
|
|||||||
return slice(get(self._start), get(self._stop), get(self._step))
|
return slice(get(self._start), get(self._stop), get(self._step))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return slice(None, None, None)
|
return slice(None, None, None)
|
||||||
|
|
||||||
|
|
||||||
def create_index_types(evaluator, context, index):
|
|
||||||
"""
|
|
||||||
Handles slices in subscript nodes.
|
|
||||||
"""
|
|
||||||
if index == ':':
|
|
||||||
# Like array[:]
|
|
||||||
return set([Slice(context, None, None, None)])
|
|
||||||
|
|
||||||
elif index.type == 'subscript' and not index.children[0] == '.':
|
|
||||||
# subscript basically implies a slice operation, except for Python 2's
|
|
||||||
# Ellipsis.
|
|
||||||
# e.g. array[:3]
|
|
||||||
result = []
|
|
||||||
for el in index.children:
|
|
||||||
if el == ':':
|
|
||||||
if not result:
|
|
||||||
result.append(None)
|
|
||||||
elif el.type == 'sliceop':
|
|
||||||
if len(el.children) == 2:
|
|
||||||
result.append(el.children[1])
|
|
||||||
else:
|
|
||||||
result.append(el)
|
|
||||||
result += [None] * (3 - len(result))
|
|
||||||
|
|
||||||
return set([Slice(context, *result)])
|
|
||||||
|
|
||||||
# No slices
|
|
||||||
return context.eval_node(index)
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from collections import defaultdict
|
|||||||
|
|
||||||
from jedi._compatibility import zip_longest
|
from jedi._compatibility import zip_longest
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import common
|
from jedi.evaluate.utils import PushBackIterator
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
@@ -10,6 +10,7 @@ from jedi.evaluate import context
|
|||||||
from jedi.evaluate import docstrings
|
from jedi.evaluate import docstrings
|
||||||
from jedi.evaluate import pep0484
|
from jedi.evaluate import pep0484
|
||||||
from jedi.evaluate.filters import ParamName
|
from jedi.evaluate.filters import ParamName
|
||||||
|
from jedi.evaluate.context import NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
def add_argument_issue(parent_context, error_name, lazy_context, message):
|
def add_argument_issue(parent_context, error_name, lazy_context, message):
|
||||||
@@ -51,7 +52,7 @@ class AbstractArguments():
|
|||||||
debug.warning('TypeError: %s expected at least %s arguments, got %s',
|
debug.warning('TypeError: %s expected at least %s arguments, got %s',
|
||||||
name, len(parameters), i)
|
name, len(parameters), i)
|
||||||
raise ValueError
|
raise ValueError
|
||||||
values = set() if argument is None else argument.infer()
|
values = NO_CONTEXTS if argument is None else argument.infer()
|
||||||
|
|
||||||
if not values and not optional:
|
if not values and not optional:
|
||||||
# For the stdlib we always want values. If we don't get them,
|
# For the stdlib we always want values. If we don't get them,
|
||||||
@@ -237,7 +238,7 @@ class ExecutedParam(object):
|
|||||||
pep0484_hints = pep0484.infer_param(self._execution_context, self._param_node)
|
pep0484_hints = pep0484.infer_param(self._execution_context, self._param_node)
|
||||||
doc_params = docstrings.infer_param(self._execution_context, self._param_node)
|
doc_params = docstrings.infer_param(self._execution_context, self._param_node)
|
||||||
if pep0484_hints or doc_params:
|
if pep0484_hints or doc_params:
|
||||||
return list(set(pep0484_hints) | set(doc_params))
|
return pep0484_hints | doc_params
|
||||||
|
|
||||||
return self._lazy_context.infer()
|
return self._lazy_context.infer()
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@ def get_params(execution_context, var_args):
|
|||||||
for param in funcdef.get_params():
|
for param in funcdef.get_params():
|
||||||
param_dict[param.name.value] = param
|
param_dict[param.name.value] = param
|
||||||
unpacked_va = list(var_args.unpack(funcdef))
|
unpacked_va = list(var_args.unpack(funcdef))
|
||||||
var_arg_iterator = common.PushBackIterator(iter(unpacked_va))
|
var_arg_iterator = PushBackIterator(iter(unpacked_va))
|
||||||
|
|
||||||
non_matching_keys = defaultdict(lambda: [])
|
non_matching_keys = defaultdict(lambda: [])
|
||||||
keys_used = {}
|
keys_used = {}
|
||||||
|
|||||||
@@ -19,17 +19,15 @@ x support for type hint comments for functions, `# type: (int, str) -> int`.
|
|||||||
See comment from Guido https://github.com/davidhalter/jedi/issues/662
|
See comment from Guido https://github.com/davidhalter/jedi/issues/662
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import itertools
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from parso import ParserSyntaxError
|
from parso import ParserSyntaxError
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
|
||||||
from jedi.common import unite
|
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate.context import LazyTreeContext
|
from jedi.evaluate.context import LazyTreeContext, NO_CONTEXTS, ContextSet
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import _compatibility
|
from jedi import _compatibility
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
@@ -42,16 +40,15 @@ def _evaluate_for_annotation(context, annotation, index=None):
|
|||||||
and we're interested in that index
|
and we're interested in that index
|
||||||
"""
|
"""
|
||||||
if annotation is not None:
|
if annotation is not None:
|
||||||
definitions = context.eval_node(
|
context_set = context.eval_node(_fix_forward_reference(context, annotation))
|
||||||
_fix_forward_reference(context, annotation))
|
|
||||||
if index is not None:
|
if index is not None:
|
||||||
definitions = list(itertools.chain.from_iterable(
|
context_set = context_set.filter(
|
||||||
definition.py__getitem__(index) for definition in definitions
|
lambda context: context.array_type == 'tuple' \
|
||||||
if definition.array_type == 'tuple' and
|
and len(list(context.py__iter__())) >= index
|
||||||
len(list(definition.py__iter__())) >= index))
|
).py__getitem__(index)
|
||||||
return unite(d.execute_evaluated() for d in definitions)
|
return context_set.execute_evaluated()
|
||||||
else:
|
else:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
def _fix_forward_reference(context, node):
|
def _fix_forward_reference(context, node):
|
||||||
@@ -147,7 +144,7 @@ def py__getitem__(context, typ, node):
|
|||||||
if type_name in ("Union", '_Union'):
|
if type_name in ("Union", '_Union'):
|
||||||
# In Python 3.6 it's still called typing.Union but it's an instance
|
# In Python 3.6 it's still called typing.Union but it's an instance
|
||||||
# called _Union.
|
# called _Union.
|
||||||
return unite(context.eval_node(node) for node in nodes)
|
return ContextSet.from_sets(context.eval_node(node) for node in nodes)
|
||||||
if type_name in ("Optional", '_Optional'):
|
if type_name in ("Optional", '_Optional'):
|
||||||
# Here we have the same issue like in Union. Therefore we also need to
|
# Here we have the same issue like in Union. Therefore we also need to
|
||||||
# check for the instance typing._Optional (Python 3.6).
|
# check for the instance typing._Optional (Python 3.6).
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
"""
|
|
||||||
Handles operator precedence.
|
|
||||||
"""
|
|
||||||
import operator as op
|
|
||||||
|
|
||||||
from jedi._compatibility import unicode
|
|
||||||
from jedi import debug
|
|
||||||
from jedi.evaluate.compiled import CompiledObject, create, builtin_from_name
|
|
||||||
from jedi.evaluate import analysis
|
|
||||||
|
|
||||||
# Maps Python syntax to the operator module.
|
|
||||||
COMPARISON_OPERATORS = {
|
|
||||||
'==': op.eq,
|
|
||||||
'!=': op.ne,
|
|
||||||
'is': op.is_,
|
|
||||||
'is not': op.is_not,
|
|
||||||
'<': op.lt,
|
|
||||||
'<=': op.le,
|
|
||||||
'>': op.gt,
|
|
||||||
'>=': op.ge,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def literals_to_types(evaluator, result):
|
|
||||||
# Changes literals ('a', 1, 1.0, etc) to its type instances (str(),
|
|
||||||
# int(), float(), etc).
|
|
||||||
new_result = set()
|
|
||||||
for typ in result:
|
|
||||||
if is_literal(typ):
|
|
||||||
# Literals are only valid as long as the operations are
|
|
||||||
# correct. Otherwise add a value-free instance.
|
|
||||||
cls = builtin_from_name(evaluator, typ.name.string_name)
|
|
||||||
new_result |= cls.execute_evaluated()
|
|
||||||
else:
|
|
||||||
new_result.add(typ)
|
|
||||||
return new_result
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_children(evaluator, context, children):
|
|
||||||
"""
|
|
||||||
Calculate a list of children with operators.
|
|
||||||
"""
|
|
||||||
iterator = iter(children)
|
|
||||||
types = context.eval_node(next(iterator))
|
|
||||||
for operator in iterator:
|
|
||||||
right = next(iterator)
|
|
||||||
if operator.type == 'comp_op': # not in / is not
|
|
||||||
operator = ' '.join(c.value for c in operator.children)
|
|
||||||
|
|
||||||
# handle lazy evaluation of and/or here.
|
|
||||||
if operator in ('and', 'or'):
|
|
||||||
left_bools = set([left.py__bool__() for left in types])
|
|
||||||
if left_bools == set([True]):
|
|
||||||
if operator == 'and':
|
|
||||||
types = context.eval_node(right)
|
|
||||||
elif left_bools == set([False]):
|
|
||||||
if operator != 'and':
|
|
||||||
types = context.eval_node(right)
|
|
||||||
# Otherwise continue, because of uncertainty.
|
|
||||||
else:
|
|
||||||
types = calculate(evaluator, context, types, operator,
|
|
||||||
context.eval_node(right))
|
|
||||||
debug.dbg('calculate_children types %s', types)
|
|
||||||
return types
|
|
||||||
|
|
||||||
|
|
||||||
def calculate(evaluator, context, left_result, operator, right_result):
|
|
||||||
result = set()
|
|
||||||
if not left_result or not right_result:
|
|
||||||
# illegal slices e.g. cause left/right_result to be None
|
|
||||||
result = (left_result or set()) | (right_result or set())
|
|
||||||
result = literals_to_types(evaluator, result)
|
|
||||||
else:
|
|
||||||
# I don't think there's a reasonable chance that a string
|
|
||||||
# operation is still correct, once we pass something like six
|
|
||||||
# objects.
|
|
||||||
if len(left_result) * len(right_result) > 6:
|
|
||||||
result = literals_to_types(evaluator, left_result | right_result)
|
|
||||||
else:
|
|
||||||
for left in left_result:
|
|
||||||
for right in right_result:
|
|
||||||
result |= _element_calculate(evaluator, context, left, operator, right)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def factor_calculate(evaluator, types, operator):
|
|
||||||
"""
|
|
||||||
Calculates `+`, `-`, `~` and `not` prefixes.
|
|
||||||
"""
|
|
||||||
for typ in types:
|
|
||||||
if operator == '-':
|
|
||||||
if _is_number(typ):
|
|
||||||
yield create(evaluator, -typ.obj)
|
|
||||||
elif operator == 'not':
|
|
||||||
value = typ.py__bool__()
|
|
||||||
if value is None: # Uncertainty.
|
|
||||||
return
|
|
||||||
yield create(evaluator, not value)
|
|
||||||
else:
|
|
||||||
yield typ
|
|
||||||
|
|
||||||
|
|
||||||
def _is_number(obj):
|
|
||||||
return isinstance(obj, CompiledObject) \
|
|
||||||
and isinstance(obj.obj, (int, float))
|
|
||||||
|
|
||||||
|
|
||||||
def is_string(obj):
|
|
||||||
return isinstance(obj, CompiledObject) \
|
|
||||||
and isinstance(obj.obj, (str, unicode))
|
|
||||||
|
|
||||||
|
|
||||||
def is_literal(obj):
|
|
||||||
return _is_number(obj) or is_string(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_tuple(obj):
|
|
||||||
from jedi.evaluate import iterable
|
|
||||||
return isinstance(obj, iterable.AbstractSequence) and obj.array_type == 'tuple'
|
|
||||||
|
|
||||||
|
|
||||||
def _is_list(obj):
|
|
||||||
from jedi.evaluate import iterable
|
|
||||||
return isinstance(obj, iterable.AbstractSequence) and obj.array_type == 'list'
|
|
||||||
|
|
||||||
|
|
||||||
def _element_calculate(evaluator, context, left, operator, right):
|
|
||||||
from jedi.evaluate import iterable, instance
|
|
||||||
l_is_num = _is_number(left)
|
|
||||||
r_is_num = _is_number(right)
|
|
||||||
if operator == '*':
|
|
||||||
# for iterables, ignore * operations
|
|
||||||
if isinstance(left, iterable.AbstractSequence) or is_string(left):
|
|
||||||
return set([left])
|
|
||||||
elif isinstance(right, iterable.AbstractSequence) or is_string(right):
|
|
||||||
return set([right])
|
|
||||||
elif operator == '+':
|
|
||||||
if l_is_num and r_is_num or is_string(left) and is_string(right):
|
|
||||||
return set([create(evaluator, left.obj + right.obj)])
|
|
||||||
elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
|
|
||||||
return set([iterable.MergedArray(evaluator, (left, right))])
|
|
||||||
elif operator == '-':
|
|
||||||
if l_is_num and r_is_num:
|
|
||||||
return set([create(evaluator, left.obj - right.obj)])
|
|
||||||
elif operator == '%':
|
|
||||||
# With strings and numbers the left type typically remains. Except for
|
|
||||||
# `int() % float()`.
|
|
||||||
return set([left])
|
|
||||||
elif operator in COMPARISON_OPERATORS:
|
|
||||||
operation = COMPARISON_OPERATORS[operator]
|
|
||||||
if isinstance(left, CompiledObject) and isinstance(right, CompiledObject):
|
|
||||||
# Possible, because the return is not an option. Just compare.
|
|
||||||
left = left.obj
|
|
||||||
right = right.obj
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = operation(left, right)
|
|
||||||
except TypeError:
|
|
||||||
# Could be True or False.
|
|
||||||
return set([create(evaluator, True), create(evaluator, False)])
|
|
||||||
else:
|
|
||||||
return set([create(evaluator, result)])
|
|
||||||
elif operator == 'in':
|
|
||||||
return set()
|
|
||||||
|
|
||||||
def check(obj):
|
|
||||||
"""Checks if a Jedi object is either a float or an int."""
|
|
||||||
return isinstance(obj, instance.CompiledInstance) and \
|
|
||||||
obj.name.string_name in ('int', 'float')
|
|
||||||
|
|
||||||
# Static analysis, one is a number, the other one is not.
|
|
||||||
if operator in ('+', '-') and l_is_num != r_is_num \
|
|
||||||
and not (check(left) or check(right)):
|
|
||||||
message = "TypeError: unsupported operand type(s) for +: %s and %s"
|
|
||||||
analysis.add(context, 'type-error-operation', operator,
|
|
||||||
message % (left, right))
|
|
||||||
|
|
||||||
return set([left, right])
|
|
||||||
@@ -29,6 +29,7 @@ therefore the quality might not always be maximal.
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
|
from jedi.evaluate.context import NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
recursion_limit = 15
|
recursion_limit = 15
|
||||||
@@ -71,7 +72,7 @@ def execution_allowed(evaluator, node):
|
|||||||
pushed_nodes.pop()
|
pushed_nodes.pop()
|
||||||
|
|
||||||
|
|
||||||
def execution_recursion_decorator(default=set()):
|
def execution_recursion_decorator(default=NO_CONTEXTS):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
def wrapper(execution, **kwargs):
|
def wrapper(execution, **kwargs):
|
||||||
detector = execution.evaluator.execution_recursion_detector
|
detector = execution.evaluator.execution_recursion_detector
|
||||||
|
|||||||
@@ -51,19 +51,20 @@ from jedi import debug
|
|||||||
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
|
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate import iterable
|
|
||||||
from jedi.evaluate import docstrings
|
from jedi.evaluate import docstrings
|
||||||
from jedi.evaluate import pep0484
|
from jedi.evaluate import pep0484
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import flow_analysis
|
from jedi.evaluate import flow_analysis
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
|
from jedi.evaluate import iterable
|
||||||
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
||||||
GlobalNameFilter, DictFilter, ContextName, AbstractNameDefinition, \
|
GlobalNameFilter, DictFilter, ContextName, AbstractNameDefinition, \
|
||||||
ParamName, AnonymousInstanceParamName, TreeNameDefinition, \
|
ParamName, AnonymousInstanceParamName, TreeNameDefinition, \
|
||||||
ContextNameMixin
|
ContextNameMixin
|
||||||
from jedi.evaluate import context
|
from jedi.evaluate import context
|
||||||
from jedi.evaluate.context import ContextualizedNode
|
from jedi.evaluate.context import ContextualizedNode, NO_CONTEXTS, \
|
||||||
|
ContextSet, iterator_to_context_set
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
from jedi.evaluate.parser_cache import get_yield_exprs
|
from jedi.evaluate.parser_cache import get_yield_exprs
|
||||||
|
|
||||||
@@ -83,12 +84,13 @@ class ClassName(TreeNameDefinition):
|
|||||||
super(ClassName, self).__init__(parent_context, tree_name)
|
super(ClassName, self).__init__(parent_context, tree_name)
|
||||||
self._name_context = name_context
|
self._name_context = name_context
|
||||||
|
|
||||||
|
@iterator_to_context_set
|
||||||
def infer(self):
|
def infer(self):
|
||||||
# TODO this _name_to_types might get refactored and be a part of the
|
# TODO this _name_to_types might get refactored and be a part of the
|
||||||
# parent class. Once it is, we can probably just overwrite method to
|
# parent class. Once it is, we can probably just overwrite method to
|
||||||
# achieve this.
|
# achieve this.
|
||||||
from jedi.evaluate.finder import _name_to_types
|
from jedi.evaluate.syntax_tree import tree_name_to_contexts
|
||||||
inferred = _name_to_types(
|
inferred = tree_name_to_contexts(
|
||||||
self.parent_context.evaluator, self._name_context, self.tree_name)
|
self.parent_context.evaluator, self._name_context, self.tree_name)
|
||||||
|
|
||||||
for result_context in inferred:
|
for result_context in inferred:
|
||||||
@@ -111,7 +113,7 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
"""
|
"""
|
||||||
api_type = 'class'
|
api_type = 'class'
|
||||||
|
|
||||||
def __init__(self, evaluator, classdef, parent_context):
|
def __init__(self, evaluator, parent_context, classdef):
|
||||||
super(ClassContext, self).__init__(evaluator, parent_context=parent_context)
|
super(ClassContext, self).__init__(evaluator, parent_context=parent_context)
|
||||||
self.tree_node = classdef
|
self.tree_node = classdef
|
||||||
|
|
||||||
@@ -162,7 +164,7 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
|
|
||||||
def py__call__(self, params):
|
def py__call__(self, params):
|
||||||
from jedi.evaluate.instance import TreeInstance
|
from jedi.evaluate.instance import TreeInstance
|
||||||
return set([TreeInstance(self.evaluator, self.parent_context, self, params)])
|
return ContextSet(TreeInstance(self.evaluator, self.parent_context, self, params))
|
||||||
|
|
||||||
def py__class__(self):
|
def py__class__(self):
|
||||||
return compiled.create(self.evaluator, type)
|
return compiled.create(self.evaluator, type)
|
||||||
@@ -227,7 +229,7 @@ class LambdaName(AbstractNameDefinition):
|
|||||||
return self._lambda_context.tree_node.start_pos
|
return self._lambda_context.tree_node.start_pos
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return set([self._lambda_context])
|
return ContextSet(self._lambda_context)
|
||||||
|
|
||||||
|
|
||||||
class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
||||||
@@ -260,7 +262,7 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
"""
|
"""
|
||||||
yield_exprs = get_yield_exprs(self.evaluator, self.tree_node)
|
yield_exprs = get_yield_exprs(self.evaluator, self.tree_node)
|
||||||
if yield_exprs:
|
if yield_exprs:
|
||||||
return set([iterable.Generator(self.evaluator, function_execution)])
|
return ContextSet(iterable.Generator(self.evaluator, function_execution))
|
||||||
else:
|
else:
|
||||||
return function_execution.get_return_values()
|
return function_execution.get_return_values()
|
||||||
|
|
||||||
@@ -312,7 +314,7 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
self.tree_node = function_context.tree_node
|
self.tree_node = function_context.tree_node
|
||||||
self.var_args = var_args
|
self.var_args = var_args
|
||||||
|
|
||||||
@evaluator_method_cache(default=set())
|
@evaluator_method_cache(default=NO_CONTEXTS)
|
||||||
@recursion.execution_recursion_decorator()
|
@recursion.execution_recursion_decorator()
|
||||||
def get_return_values(self, check_yields=False):
|
def get_return_values(self, check_yields=False):
|
||||||
funcdef = self.tree_node
|
funcdef = self.tree_node
|
||||||
@@ -320,12 +322,12 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
return self.evaluator.eval_element(self, funcdef.children[-1])
|
return self.evaluator.eval_element(self, funcdef.children[-1])
|
||||||
|
|
||||||
if check_yields:
|
if check_yields:
|
||||||
types = set()
|
context_set = NO_CONTEXTS
|
||||||
returns = get_yield_exprs(self.evaluator, funcdef)
|
returns = get_yield_exprs(self.evaluator, funcdef)
|
||||||
else:
|
else:
|
||||||
returns = funcdef.iter_return_stmts()
|
returns = funcdef.iter_return_stmts()
|
||||||
types = set(docstrings.infer_return_types(self.function_context))
|
context_set = docstrings.infer_return_types(self.function_context)
|
||||||
types |= set(pep0484.infer_return_types(self.function_context))
|
context_set |= pep0484.infer_return_types(self.function_context)
|
||||||
|
|
||||||
for r in returns:
|
for r in returns:
|
||||||
check = flow_analysis.reachability_check(self, funcdef, r)
|
check = flow_analysis.reachability_check(self, funcdef, r)
|
||||||
@@ -333,18 +335,21 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
debug.dbg('Return unreachable: %s', r)
|
debug.dbg('Return unreachable: %s', r)
|
||||||
else:
|
else:
|
||||||
if check_yields:
|
if check_yields:
|
||||||
types |= set(self._eval_yield(r))
|
context_set |= ContextSet.from_sets(
|
||||||
|
lazy_context.infer()
|
||||||
|
for lazy_context in self._eval_yield(r)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
children = r.children
|
children = r.children
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
types.add(compiled.create(self.evaluator, None))
|
context_set |= ContextSet(compiled.create(self.evaluator, None))
|
||||||
else:
|
else:
|
||||||
types |= self.eval_node(children[1])
|
context_set |= self.eval_node(children[1])
|
||||||
if check is flow_analysis.REACHABLE:
|
if check is flow_analysis.REACHABLE:
|
||||||
debug.dbg('Return reachable: %s', r)
|
debug.dbg('Return reachable: %s', r)
|
||||||
break
|
break
|
||||||
return types
|
return context_set
|
||||||
|
|
||||||
def _eval_yield(self, yield_expr):
|
def _eval_yield(self, yield_expr):
|
||||||
if yield_expr.type == 'keyword':
|
if yield_expr.type == 'keyword':
|
||||||
@@ -355,7 +360,7 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
node = yield_expr.children[1]
|
node = yield_expr.children[1]
|
||||||
if node.type == 'yield_arg': # It must be a yield from.
|
if node.type == 'yield_arg': # It must be a yield from.
|
||||||
cn = ContextualizedNode(self, node.children[1])
|
cn = ContextualizedNode(self, node.children[1])
|
||||||
for lazy_context in iterable.py__iter__(self.evaluator, cn.infer(), cn):
|
for lazy_context in cn.infer().iterate(cn):
|
||||||
yield lazy_context
|
yield lazy_context
|
||||||
else:
|
else:
|
||||||
yield context.LazyTreeContext(self, node)
|
yield context.LazyTreeContext(self, node)
|
||||||
@@ -386,11 +391,10 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
else:
|
else:
|
||||||
types = self.get_return_values(check_yields=True)
|
types = self.get_return_values(check_yields=True)
|
||||||
if types:
|
if types:
|
||||||
yield context.get_merged_lazy_context(list(types))
|
yield context.LazyKnownContexts(types)
|
||||||
return
|
return
|
||||||
last_for_stmt = for_stmt
|
last_for_stmt = for_stmt
|
||||||
|
|
||||||
evaluator = self.evaluator
|
|
||||||
for for_stmt, yields in yields_order:
|
for for_stmt, yields in yields_order:
|
||||||
if for_stmt is None:
|
if for_stmt is None:
|
||||||
# No for_stmt, just normal yields.
|
# No for_stmt, just normal yields.
|
||||||
@@ -400,7 +404,7 @@ class FunctionExecutionContext(context.TreeContext):
|
|||||||
else:
|
else:
|
||||||
input_node = for_stmt.get_testlist()
|
input_node = for_stmt.get_testlist()
|
||||||
cn = ContextualizedNode(self, input_node)
|
cn = ContextualizedNode(self, input_node)
|
||||||
ordered = iterable.py__iter__(evaluator, cn.infer(), cn)
|
ordered = cn.infer().iterate(cn)
|
||||||
ordered = list(ordered)
|
ordered = list(ordered)
|
||||||
for lazy_context in ordered:
|
for lazy_context in ordered:
|
||||||
dct = {str(for_stmt.children[1].value): lazy_context.infer()}
|
dct = {str(for_stmt.children[1].value): lazy_context.infer()}
|
||||||
@@ -430,9 +434,7 @@ class ModuleAttributeName(AbstractNameDefinition):
|
|||||||
self.string_name = string_name
|
self.string_name = string_name
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return compiled.create(self.parent_context.evaluator, str).execute(
|
return compiled.create(self.parent_context.evaluator, str).execute_evaluated()
|
||||||
param.ValuesArguments([])
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleName(ContextNameMixin, AbstractNameDefinition):
|
class ModuleName(ContextNameMixin, AbstractNameDefinition):
|
||||||
@@ -628,7 +630,7 @@ class ImplicitNSName(AbstractNameDefinition):
|
|||||||
self.string_name = string_name
|
self.string_name = string_name
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return []
|
return NO_CONTEXTS
|
||||||
|
|
||||||
def get_root_context(self):
|
def get_root_context(self):
|
||||||
return self.implicit_ns_context
|
return self.implicit_ns_context
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ compiled module that returns the types for C-builtins.
|
|||||||
import collections
|
import collections
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from jedi.common import unite
|
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
from jedi.evaluate.instance import InstanceFunctionExecution, \
|
from jedi.evaluate.instance import InstanceFunctionExecution, \
|
||||||
@@ -20,10 +19,11 @@ from jedi.evaluate.instance import InstanceFunctionExecution, \
|
|||||||
AnonymousInstanceFunctionExecution
|
AnonymousInstanceFunctionExecution
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate import precedence
|
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
from jedi.evaluate.context import LazyTreeContext, ContextualizedNode
|
from jedi.evaluate.context import LazyTreeContext, ContextualizedNode, \
|
||||||
|
NO_CONTEXTS, ContextSet
|
||||||
|
from jedi.evaluate.syntax_tree import is_string
|
||||||
|
|
||||||
# Now this is all part of fake tuples in Jedi. However super doesn't work on
|
# Now this is all part of fake tuples in Jedi. However super doesn't work on
|
||||||
# __init__ and __new__ doesn't work at all. So adding this to nametuples is
|
# __init__ and __new__ doesn't work at all. So adding this to nametuples is
|
||||||
@@ -77,7 +77,7 @@ def _follow_param(evaluator, arguments, index):
|
|||||||
try:
|
try:
|
||||||
key, lazy_context = list(arguments.unpack())[index]
|
key, lazy_context = list(arguments.unpack())[index]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
return lazy_context.infer()
|
return lazy_context.infer()
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ def argument_clinic(string, want_obj=False, want_context=False, want_arguments=F
|
|||||||
try:
|
try:
|
||||||
lst = list(arguments.eval_argument_clinic(clinic_args))
|
lst = list(arguments.eval_argument_clinic(clinic_args))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if want_context:
|
if want_context:
|
||||||
@@ -137,15 +137,16 @@ def builtins_next(evaluator, iterators, defaults):
|
|||||||
else:
|
else:
|
||||||
name = '__next__'
|
name = '__next__'
|
||||||
|
|
||||||
types = set()
|
context_set = NO_CONTEXTS
|
||||||
for iterator in iterators:
|
for iterator in iterators:
|
||||||
if isinstance(iterator, AbstractInstanceContext):
|
if isinstance(iterator, AbstractInstanceContext):
|
||||||
for filter in iterator.get_filters(include_self_names=True):
|
context_set = ContextSet.from_sets(
|
||||||
for n in filter.get(name):
|
n.infer()
|
||||||
for context in n.infer():
|
for filter in iterator.get_filters(include_self_names=True)
|
||||||
types |= context.execute_evaluated()
|
for n in filter.get(name)
|
||||||
if types:
|
).execute_evaluated()
|
||||||
return types
|
if context_set:
|
||||||
|
return context_set
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
|
|
||||||
@@ -154,21 +155,21 @@ def builtins_getattr(evaluator, objects, names, defaults=None):
|
|||||||
# follow the first param
|
# follow the first param
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
for name in names:
|
for name in names:
|
||||||
if precedence.is_string(name):
|
if is_string(name):
|
||||||
return obj.py__getattribute__(name.obj)
|
return obj.py__getattribute__(name.obj)
|
||||||
else:
|
else:
|
||||||
debug.warning('getattr called without str')
|
debug.warning('getattr called without str')
|
||||||
continue
|
continue
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('object[, bases, dict], /')
|
@argument_clinic('object[, bases, dict], /')
|
||||||
def builtins_type(evaluator, objects, bases, dicts):
|
def builtins_type(evaluator, objects, bases, dicts):
|
||||||
if bases or dicts:
|
if bases or dicts:
|
||||||
# It's a type creation... maybe someday...
|
# It's a type creation... maybe someday...
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
else:
|
else:
|
||||||
return set([o.py__class__() for o in objects])
|
return objects.py__class__()
|
||||||
|
|
||||||
|
|
||||||
class SuperInstance(AbstractInstanceContext):
|
class SuperInstance(AbstractInstanceContext):
|
||||||
@@ -184,8 +185,8 @@ def builtins_super(evaluator, types, objects, context):
|
|||||||
if isinstance(context, (InstanceFunctionExecution,
|
if isinstance(context, (InstanceFunctionExecution,
|
||||||
AnonymousInstanceFunctionExecution)):
|
AnonymousInstanceFunctionExecution)):
|
||||||
su = context.instance.py__class__().py__bases__()
|
su = context.instance.py__class__().py__bases__()
|
||||||
return unite(context.execute_evaluated() for context in su[0].infer())
|
return su[0].infer().execute_evaluated()
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('sequence, /', want_obj=True, want_arguments=True)
|
@argument_clinic('sequence, /', want_obj=True, want_arguments=True)
|
||||||
@@ -198,7 +199,7 @@ def builtins_reversed(evaluator, sequences, obj, arguments):
|
|||||||
if isinstance(lazy_context, LazyTreeContext):
|
if isinstance(lazy_context, LazyTreeContext):
|
||||||
# TODO access private
|
# TODO access private
|
||||||
cn = ContextualizedNode(lazy_context._context, lazy_context.data)
|
cn = ContextualizedNode(lazy_context._context, lazy_context.data)
|
||||||
ordered = list(iterable.py__iter__(evaluator, sequences, cn))
|
ordered = list(sequences.iterate(cn))
|
||||||
|
|
||||||
rev = list(reversed(ordered))
|
rev = list(reversed(ordered))
|
||||||
# Repack iterator values and then run it the normal way. This is
|
# Repack iterator values and then run it the normal way. This is
|
||||||
@@ -206,13 +207,13 @@ def builtins_reversed(evaluator, sequences, obj, arguments):
|
|||||||
# 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 = iterable.FakeSequence(evaluator, 'list', rev)
|
seq = iterable.FakeSequence(evaluator, 'list', rev)
|
||||||
arguments = param.ValuesArguments([[seq]])
|
arguments = param.ValuesArguments([ContextSet(seq)])
|
||||||
return set([CompiledInstance(evaluator, evaluator.BUILTINS, obj, arguments)])
|
return ContextSet(CompiledInstance(evaluator, evaluator.BUILTINS, obj, arguments))
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('obj, type, /', want_arguments=True)
|
@argument_clinic('obj, type, /', want_arguments=True)
|
||||||
def builtins_isinstance(evaluator, objects, types, arguments):
|
def builtins_isinstance(evaluator, objects, types, arguments):
|
||||||
bool_results = set([])
|
bool_results = set()
|
||||||
for o in objects:
|
for o in objects:
|
||||||
try:
|
try:
|
||||||
mro_func = o.py__class__().py__mro__
|
mro_func = o.py__class__().py__mro__
|
||||||
@@ -220,7 +221,7 @@ def builtins_isinstance(evaluator, objects, types, arguments):
|
|||||||
# This is temporary. Everything should have a class attribute in
|
# This is temporary. Everything should have a class attribute in
|
||||||
# Python?! Maybe we'll leave it here, because some numpy objects or
|
# Python?! Maybe we'll leave it here, because some numpy objects or
|
||||||
# whatever might not.
|
# whatever might not.
|
||||||
return set([compiled.create(True), compiled.create(False)])
|
return ContextSet(compiled.create(True), compiled.create(False))
|
||||||
|
|
||||||
mro = mro_func()
|
mro = mro_func()
|
||||||
|
|
||||||
@@ -230,9 +231,9 @@ def builtins_isinstance(evaluator, objects, types, arguments):
|
|||||||
elif cls_or_tup.name.string_name == 'tuple' \
|
elif cls_or_tup.name.string_name == 'tuple' \
|
||||||
and cls_or_tup.get_root_context() == evaluator.BUILTINS:
|
and cls_or_tup.get_root_context() == evaluator.BUILTINS:
|
||||||
# Check for tuples.
|
# Check for tuples.
|
||||||
classes = unite(
|
classes = ContextSet.from_sets(
|
||||||
lazy_context.infer()
|
lazy_context.infer()
|
||||||
for lazy_context in cls_or_tup.py__iter__()
|
for lazy_context in cls_or_tup.iterate()
|
||||||
)
|
)
|
||||||
bool_results.add(any(cls in mro for cls in classes))
|
bool_results.add(any(cls in mro for cls in classes))
|
||||||
else:
|
else:
|
||||||
@@ -244,7 +245,7 @@ def builtins_isinstance(evaluator, objects, types, arguments):
|
|||||||
'not %s.' % cls_or_tup
|
'not %s.' % cls_or_tup
|
||||||
analysis.add(lazy_context._context, 'type-error-isinstance', node, message)
|
analysis.add(lazy_context._context, 'type-error-isinstance', node, message)
|
||||||
|
|
||||||
return set(compiled.create(evaluator, x) for x in bool_results)
|
return ContextSet.from_iterable(compiled.create(evaluator, x) for x in bool_results)
|
||||||
|
|
||||||
|
|
||||||
def collections_namedtuple(evaluator, obj, arguments):
|
def collections_namedtuple(evaluator, obj, arguments):
|
||||||
@@ -259,7 +260,7 @@ def collections_namedtuple(evaluator, obj, arguments):
|
|||||||
"""
|
"""
|
||||||
# Namedtuples are not supported on Python 2.6
|
# Namedtuples are not supported on Python 2.6
|
||||||
if not hasattr(collections, '_class_template'):
|
if not hasattr(collections, '_class_template'):
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
# Process arguments
|
# Process arguments
|
||||||
# TODO here we only use one of the types, we should use all.
|
# TODO here we only use one of the types, we should use all.
|
||||||
@@ -274,7 +275,7 @@ def collections_namedtuple(evaluator, obj, arguments):
|
|||||||
for v in lazy_context.infer() if hasattr(v, 'obj')
|
for v in lazy_context.infer() if hasattr(v, 'obj')
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
return set()
|
return NO_CONTEXTS
|
||||||
|
|
||||||
base = collections._class_template
|
base = collections._class_template
|
||||||
base += _NAMEDTUPLE_INIT
|
base += _NAMEDTUPLE_INIT
|
||||||
@@ -293,7 +294,7 @@ def collections_namedtuple(evaluator, obj, arguments):
|
|||||||
module = evaluator.grammar.parse(source)
|
module = evaluator.grammar.parse(source)
|
||||||
generated_class = next(module.iter_classdefs())
|
generated_class = next(module.iter_classdefs())
|
||||||
parent_context = er.ModuleContext(evaluator, module, '')
|
parent_context = er.ModuleContext(evaluator, module, '')
|
||||||
return set([er.ClassContext(evaluator, generated_class, parent_context)])
|
return ContextSet(er.ClassContext(evaluator, parent_context, generated_class))
|
||||||
|
|
||||||
|
|
||||||
@argument_clinic('first, /')
|
@argument_clinic('first, /')
|
||||||
@@ -314,8 +315,8 @@ _implemented = {
|
|||||||
'deepcopy': _return_first_param,
|
'deepcopy': _return_first_param,
|
||||||
},
|
},
|
||||||
'json': {
|
'json': {
|
||||||
'load': lambda *args: set(),
|
'load': lambda *args: NO_CONTEXTS,
|
||||||
'loads': lambda *args: set(),
|
'loads': lambda *args: NO_CONTEXTS,
|
||||||
},
|
},
|
||||||
'collections': {
|
'collections': {
|
||||||
'namedtuple': collections_namedtuple,
|
'namedtuple': collections_namedtuple,
|
||||||
|
|||||||
588
jedi/evaluate/syntax_tree.py
Normal file
588
jedi/evaluate/syntax_tree.py
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
"""
|
||||||
|
Functions evaluating the syntax tree.
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import operator as op
|
||||||
|
|
||||||
|
from parso.python import tree
|
||||||
|
|
||||||
|
from jedi import debug
|
||||||
|
from jedi import parser_utils
|
||||||
|
from jedi.evaluate.context import ContextSet, NO_CONTEXTS, ContextualizedNode, \
|
||||||
|
ContextualizedName, iterator_to_context_set, iterate_contexts
|
||||||
|
from jedi.evaluate import compiled
|
||||||
|
from jedi.evaluate import pep0484
|
||||||
|
from jedi.evaluate import recursion
|
||||||
|
from jedi.evaluate import helpers
|
||||||
|
from jedi.evaluate import analysis
|
||||||
|
from jedi.evaluate import iterable
|
||||||
|
from jedi.evaluate import imports
|
||||||
|
from jedi.evaluate import param
|
||||||
|
from jedi.evaluate import representation as er
|
||||||
|
from jedi.evaluate.instance import TreeInstance, CompiledInstance
|
||||||
|
from jedi.evaluate.finder import NameFinder
|
||||||
|
from jedi.evaluate.helpers import is_string, is_literal, is_number, is_compiled
|
||||||
|
|
||||||
|
|
||||||
|
def _limit_context_infers(func):
|
||||||
|
"""
|
||||||
|
This is for now the way how we limit type inference going wild. There are
|
||||||
|
other ways to ensure recursion limits as well. This is mostly necessary
|
||||||
|
because of instance (self) access that can be quite tricky to limit.
|
||||||
|
|
||||||
|
I'm still not sure this is the way to go, but it looks okay for now and we
|
||||||
|
can still go anther way in the future. Tests are there. ~ dave
|
||||||
|
"""
|
||||||
|
def wrapper(context, *args, **kwargs):
|
||||||
|
n = context.tree_node
|
||||||
|
evaluator = context.evaluator
|
||||||
|
try:
|
||||||
|
evaluator.inferred_element_counts[n] += 1
|
||||||
|
if evaluator.inferred_element_counts[n] > 300:
|
||||||
|
debug.warning('In context %s there were too many inferences.', n)
|
||||||
|
return NO_CONTEXTS
|
||||||
|
except KeyError:
|
||||||
|
evaluator.inferred_element_counts[n] = 1
|
||||||
|
return func(context, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@debug.increase_indent
|
||||||
|
@_limit_context_infers
|
||||||
|
def eval_node(context, element):
|
||||||
|
debug.dbg('eval_element %s@%s', element, element.start_pos)
|
||||||
|
evaluator = context.evaluator
|
||||||
|
typ = element.type
|
||||||
|
if typ in ('name', 'number', 'string', 'atom'):
|
||||||
|
return eval_atom(context, element)
|
||||||
|
elif typ == 'keyword':
|
||||||
|
# For False/True/None
|
||||||
|
if element.value in ('False', 'True', 'None'):
|
||||||
|
return ContextSet(compiled.builtin_from_name(evaluator, element.value))
|
||||||
|
# else: print e.g. could be evaluated like this in Python 2.7
|
||||||
|
return NO_CONTEXTS
|
||||||
|
elif typ == 'lambdef':
|
||||||
|
return ContextSet(er.FunctionContext(evaluator, context, element))
|
||||||
|
elif typ == 'expr_stmt':
|
||||||
|
return eval_expr_stmt(context, element)
|
||||||
|
elif typ in ('power', 'atom_expr'):
|
||||||
|
first_child = element.children[0]
|
||||||
|
if not (first_child.type == 'keyword' and first_child.value == 'await'):
|
||||||
|
context_set = eval_atom(context, first_child)
|
||||||
|
for trailer in element.children[1:]:
|
||||||
|
if trailer == '**': # has a power operation.
|
||||||
|
right = evaluator.eval_element(context, element.children[2])
|
||||||
|
context_set = _eval_comparison(
|
||||||
|
evaluator,
|
||||||
|
context,
|
||||||
|
context_set,
|
||||||
|
trailer,
|
||||||
|
right
|
||||||
|
)
|
||||||
|
break
|
||||||
|
context_set = eval_trailer(context, context_set, trailer)
|
||||||
|
return context_set
|
||||||
|
return NO_CONTEXTS
|
||||||
|
elif typ in ('testlist_star_expr', 'testlist',):
|
||||||
|
# The implicit tuple in statements.
|
||||||
|
return ContextSet(iterable.SequenceLiteralContext(evaluator, context, element))
|
||||||
|
elif typ in ('not_test', 'factor'):
|
||||||
|
context_set = context.eval_node(element.children[-1])
|
||||||
|
for operator in element.children[:-1]:
|
||||||
|
context_set = eval_factor(context_set, operator)
|
||||||
|
return context_set
|
||||||
|
elif typ == 'test':
|
||||||
|
# `x if foo else y` case.
|
||||||
|
return (context.eval_node(element.children[0]) |
|
||||||
|
context.eval_node(element.children[-1]))
|
||||||
|
elif typ == 'operator':
|
||||||
|
# Must be an ellipsis, other operators are not evaluated.
|
||||||
|
# In Python 2 ellipsis is coded as three single dot tokens, not
|
||||||
|
# as one token 3 dot token.
|
||||||
|
assert element.value in ('.', '...')
|
||||||
|
return ContextSet(compiled.create(evaluator, Ellipsis))
|
||||||
|
elif typ == 'dotted_name':
|
||||||
|
context_set = eval_atom(context, element.children[0])
|
||||||
|
for next_name in element.children[2::2]:
|
||||||
|
# TODO add search_global=True?
|
||||||
|
context_set = context_set.py__getattribute__(next_name, name_context=context)
|
||||||
|
return context_set
|
||||||
|
elif typ == 'eval_input':
|
||||||
|
return eval_node(context, element.children[0])
|
||||||
|
elif typ == 'annassign':
|
||||||
|
return pep0484._evaluate_for_annotation(context, element.children[1])
|
||||||
|
else:
|
||||||
|
return eval_or_test(context, element)
|
||||||
|
|
||||||
|
|
||||||
|
def eval_trailer(context, base_contexts, trailer):
|
||||||
|
trailer_op, node = trailer.children[:2]
|
||||||
|
if node == ')': # `arglist` is optional.
|
||||||
|
node = ()
|
||||||
|
|
||||||
|
if trailer_op == '[':
|
||||||
|
trailer_op, node, _ = trailer.children
|
||||||
|
|
||||||
|
# TODO It's kind of stupid to cast this from a context set to a set.
|
||||||
|
foo = set(base_contexts)
|
||||||
|
# special case: PEP0484 typing module, see
|
||||||
|
# https://github.com/davidhalter/jedi/issues/663
|
||||||
|
result = ContextSet()
|
||||||
|
for typ in list(foo):
|
||||||
|
if isinstance(typ, (er.ClassContext, TreeInstance)):
|
||||||
|
typing_module_types = pep0484.py__getitem__(context, typ, node)
|
||||||
|
if typing_module_types is not None:
|
||||||
|
foo.remove(typ)
|
||||||
|
result |= typing_module_types
|
||||||
|
|
||||||
|
return result | base_contexts.get_item(
|
||||||
|
eval_subscript_list(context.evaluator, context, node),
|
||||||
|
ContextualizedNode(context, trailer)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
debug.dbg('eval_trailer: %s in %s', trailer, base_contexts)
|
||||||
|
if trailer_op == '.':
|
||||||
|
return base_contexts.py__getattribute__(
|
||||||
|
name_context=context,
|
||||||
|
name_or_str=node
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert trailer_op == '('
|
||||||
|
arguments = param.TreeArguments(context.evaluator, context, node, trailer)
|
||||||
|
return base_contexts.execute(arguments)
|
||||||
|
|
||||||
|
|
||||||
|
def eval_atom(context, atom):
|
||||||
|
"""
|
||||||
|
Basically to process ``atom`` nodes. The parser sometimes doesn't
|
||||||
|
generate the node (because it has just one child). In that case an atom
|
||||||
|
might be a name or a literal as well.
|
||||||
|
"""
|
||||||
|
if atom.type == 'name':
|
||||||
|
# This is the first global lookup.
|
||||||
|
stmt = tree.search_ancestor(
|
||||||
|
atom, 'expr_stmt', 'lambdef'
|
||||||
|
) or atom
|
||||||
|
if stmt.type == 'lambdef':
|
||||||
|
stmt = atom
|
||||||
|
return context.py__getattribute__(
|
||||||
|
name_or_str=atom,
|
||||||
|
position=stmt.start_pos,
|
||||||
|
search_global=True
|
||||||
|
)
|
||||||
|
|
||||||
|
elif isinstance(atom, tree.Literal):
|
||||||
|
string = parser_utils.safe_literal_eval(atom.value)
|
||||||
|
return ContextSet(compiled.create(context.evaluator, string))
|
||||||
|
else:
|
||||||
|
c = atom.children
|
||||||
|
if c[0].type == 'string':
|
||||||
|
# Will be one string.
|
||||||
|
context_set = eval_atom(context, c[0])
|
||||||
|
for string in c[1:]:
|
||||||
|
right = eval_atom(context, string)
|
||||||
|
context_set = _eval_comparison(context.evaluator, context, context_set, '+', right)
|
||||||
|
return context_set
|
||||||
|
# Parentheses without commas are not tuples.
|
||||||
|
elif c[0] == '(' and not len(c) == 2 \
|
||||||
|
and not(c[1].type == 'testlist_comp' and
|
||||||
|
len(c[1].children) > 1):
|
||||||
|
return context.eval_node(c[1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
comp_for = c[1].children[1]
|
||||||
|
except (IndexError, AttributeError):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if comp_for == ':':
|
||||||
|
# Dict comprehensions have a colon at the 3rd index.
|
||||||
|
try:
|
||||||
|
comp_for = c[1].children[3]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if comp_for.type == 'comp_for':
|
||||||
|
return ContextSet(iterable.Comprehension.from_atom(context.evaluator, context, atom))
|
||||||
|
|
||||||
|
# It's a dict/list/tuple literal.
|
||||||
|
array_node = c[1]
|
||||||
|
try:
|
||||||
|
array_node_c = array_node.children
|
||||||
|
except AttributeError:
|
||||||
|
array_node_c = []
|
||||||
|
if c[0] == '{' and (array_node == '}' or ':' in array_node_c):
|
||||||
|
context = iterable.DictLiteralContext(context.evaluator, context, atom)
|
||||||
|
else:
|
||||||
|
context = iterable.SequenceLiteralContext(context.evaluator, context, atom)
|
||||||
|
return ContextSet(context)
|
||||||
|
|
||||||
|
|
||||||
|
@_limit_context_infers
|
||||||
|
def eval_expr_stmt(context, stmt, seek_name=None):
|
||||||
|
with recursion.execution_allowed(context.evaluator, stmt) as allowed:
|
||||||
|
if allowed or context.get_root_context() == context.evaluator.BUILTINS:
|
||||||
|
return _eval_expr_stmt(context, stmt, seek_name)
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|
||||||
|
@debug.increase_indent
|
||||||
|
def _eval_expr_stmt(context, stmt, seek_name=None):
|
||||||
|
"""
|
||||||
|
The starting point of the completion. A statement always owns a call
|
||||||
|
list, which are the calls, that a statement does. In case multiple
|
||||||
|
names are defined in the statement, `seek_name` returns the result for
|
||||||
|
this name.
|
||||||
|
|
||||||
|
:param stmt: A `tree.ExprStmt`.
|
||||||
|
"""
|
||||||
|
debug.dbg('eval_expr_stmt %s (%s)', stmt, seek_name)
|
||||||
|
rhs = stmt.get_rhs()
|
||||||
|
context_set = context.eval_node(rhs)
|
||||||
|
|
||||||
|
if seek_name:
|
||||||
|
c_node = ContextualizedName(context, seek_name)
|
||||||
|
context_set = check_tuple_assignments(context.evaluator, c_node, context_set)
|
||||||
|
|
||||||
|
first_operator = next(stmt.yield_operators(), None)
|
||||||
|
if first_operator not in ('=', None) and first_operator.type == 'operator':
|
||||||
|
# `=` is always the last character in aug assignments -> -1
|
||||||
|
operator = copy.copy(first_operator)
|
||||||
|
operator.value = operator.value[:-1]
|
||||||
|
name = stmt.get_defined_names()[0].value
|
||||||
|
left = context.py__getattribute__(
|
||||||
|
name, position=stmt.start_pos, search_global=True)
|
||||||
|
|
||||||
|
for_stmt = tree.search_ancestor(stmt, 'for_stmt')
|
||||||
|
if for_stmt is not None and for_stmt.type == 'for_stmt' and context_set \
|
||||||
|
and parser_utils.for_stmt_defines_one_name(for_stmt):
|
||||||
|
# Iterate through result and add the values, that's possible
|
||||||
|
# only in for loops without clutter, because they are
|
||||||
|
# predictable. Also only do it, if the variable is not a tuple.
|
||||||
|
node = for_stmt.get_testlist()
|
||||||
|
cn = ContextualizedNode(context, node)
|
||||||
|
ordered = list(cn.infer().iterate(cn))
|
||||||
|
|
||||||
|
for lazy_context in ordered:
|
||||||
|
dct = {for_stmt.children[1].value: lazy_context.infer()}
|
||||||
|
with helpers.predefine_names(context, for_stmt, dct):
|
||||||
|
t = context.eval_node(rhs)
|
||||||
|
left = _eval_comparison(context.evaluator, context, left, operator, t)
|
||||||
|
context_set = left
|
||||||
|
else:
|
||||||
|
context_set = _eval_comparison(context.evaluator, context, left, operator, context_set)
|
||||||
|
debug.dbg('eval_expr_stmt result %s', context_set)
|
||||||
|
return context_set
|
||||||
|
|
||||||
|
|
||||||
|
def eval_or_test(context, or_test):
|
||||||
|
iterator = iter(or_test.children)
|
||||||
|
types = context.eval_node(next(iterator))
|
||||||
|
for operator in iterator:
|
||||||
|
right = next(iterator)
|
||||||
|
if operator.type == 'comp_op': # not in / is not
|
||||||
|
operator = ' '.join(c.value for c in operator.children)
|
||||||
|
|
||||||
|
# handle lazy evaluation of and/or here.
|
||||||
|
if operator in ('and', 'or'):
|
||||||
|
left_bools = set(left.py__bool__() for left in types)
|
||||||
|
if left_bools == set([True]):
|
||||||
|
if operator == 'and':
|
||||||
|
types = context.eval_node(right)
|
||||||
|
elif left_bools == set([False]):
|
||||||
|
if operator != 'and':
|
||||||
|
types = context.eval_node(right)
|
||||||
|
# Otherwise continue, because of uncertainty.
|
||||||
|
else:
|
||||||
|
types = _eval_comparison(context.evaluator, context, types, operator,
|
||||||
|
context.eval_node(right))
|
||||||
|
debug.dbg('eval_or_test types %s', types)
|
||||||
|
return types
|
||||||
|
|
||||||
|
|
||||||
|
@iterator_to_context_set
|
||||||
|
def eval_factor(context_set, operator):
|
||||||
|
"""
|
||||||
|
Calculates `+`, `-`, `~` and `not` prefixes.
|
||||||
|
"""
|
||||||
|
for context in context_set:
|
||||||
|
if operator == '-':
|
||||||
|
if is_number(context):
|
||||||
|
yield compiled.create(context.evaluator, -context.obj)
|
||||||
|
elif operator == 'not':
|
||||||
|
value = context.py__bool__()
|
||||||
|
if value is None: # Uncertainty.
|
||||||
|
return
|
||||||
|
yield compiled.create(context.evaluator, not value)
|
||||||
|
else:
|
||||||
|
yield context
|
||||||
|
|
||||||
|
|
||||||
|
# Maps Python syntax to the operator module.
|
||||||
|
COMPARISON_OPERATORS = {
|
||||||
|
'==': op.eq,
|
||||||
|
'!=': op.ne,
|
||||||
|
'is': op.is_,
|
||||||
|
'is not': op.is_not,
|
||||||
|
'<': op.lt,
|
||||||
|
'<=': op.le,
|
||||||
|
'>': op.gt,
|
||||||
|
'>=': op.ge,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _literals_to_types(evaluator, result):
|
||||||
|
# Changes literals ('a', 1, 1.0, etc) to its type instances (str(),
|
||||||
|
# int(), float(), etc).
|
||||||
|
new_result = NO_CONTEXTS
|
||||||
|
for typ in result:
|
||||||
|
if is_literal(typ):
|
||||||
|
# Literals are only valid as long as the operations are
|
||||||
|
# correct. Otherwise add a value-free instance.
|
||||||
|
cls = compiled.builtin_from_name(evaluator, typ.name.string_name)
|
||||||
|
new_result |= cls.execute_evaluated()
|
||||||
|
else:
|
||||||
|
new_result |= ContextSet(typ)
|
||||||
|
return new_result
|
||||||
|
|
||||||
|
|
||||||
|
def _eval_comparison(evaluator, context, left_contexts, operator, right_contexts):
|
||||||
|
if not left_contexts or not right_contexts:
|
||||||
|
# illegal slices e.g. cause left/right_result to be None
|
||||||
|
result = (left_contexts or NO_CONTEXTS) | (right_contexts or NO_CONTEXTS)
|
||||||
|
return _literals_to_types(evaluator, result)
|
||||||
|
else:
|
||||||
|
# I don't think there's a reasonable chance that a string
|
||||||
|
# operation is still correct, once we pass something like six
|
||||||
|
# objects.
|
||||||
|
if len(left_contexts) * len(right_contexts) > 6:
|
||||||
|
return _literals_to_types(evaluator, left_contexts | right_contexts)
|
||||||
|
else:
|
||||||
|
return ContextSet.from_sets(
|
||||||
|
_eval_comparison_part(evaluator, context, left, operator, right)
|
||||||
|
for left in left_contexts
|
||||||
|
for right in right_contexts
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_tuple(context):
|
||||||
|
return isinstance(context, iterable.AbstractSequence) and context.array_type == 'tuple'
|
||||||
|
|
||||||
|
|
||||||
|
def _is_list(context):
|
||||||
|
return isinstance(context, iterable.AbstractSequence) and context.array_type == 'list'
|
||||||
|
|
||||||
|
|
||||||
|
def _eval_comparison_part(evaluator, context, left, operator, right):
|
||||||
|
l_is_num = is_number(left)
|
||||||
|
r_is_num = is_number(right)
|
||||||
|
if operator == '*':
|
||||||
|
# for iterables, ignore * operations
|
||||||
|
if isinstance(left, iterable.AbstractSequence) or is_string(left):
|
||||||
|
return ContextSet(left)
|
||||||
|
elif isinstance(right, iterable.AbstractSequence) or is_string(right):
|
||||||
|
return ContextSet(right)
|
||||||
|
elif operator == '+':
|
||||||
|
if l_is_num and r_is_num or is_string(left) and is_string(right):
|
||||||
|
return ContextSet(compiled.create(evaluator, left.obj + right.obj))
|
||||||
|
elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
|
||||||
|
return ContextSet(iterable.MergedArray(evaluator, (left, right)))
|
||||||
|
elif operator == '-':
|
||||||
|
if l_is_num and r_is_num:
|
||||||
|
return ContextSet(compiled.create(evaluator, left.obj - right.obj))
|
||||||
|
elif operator == '%':
|
||||||
|
# With strings and numbers the left type typically remains. Except for
|
||||||
|
# `int() % float()`.
|
||||||
|
return ContextSet(left)
|
||||||
|
elif operator in COMPARISON_OPERATORS:
|
||||||
|
operation = COMPARISON_OPERATORS[operator]
|
||||||
|
if is_compiled(left) and is_compiled(right):
|
||||||
|
# Possible, because the return is not an option. Just compare.
|
||||||
|
left = left.obj
|
||||||
|
right = right.obj
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = operation(left, right)
|
||||||
|
except TypeError:
|
||||||
|
# Could be True or False.
|
||||||
|
return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False))
|
||||||
|
else:
|
||||||
|
return ContextSet(compiled.create(evaluator, result))
|
||||||
|
elif operator == 'in':
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
def check(obj):
|
||||||
|
"""Checks if a Jedi object is either a float or an int."""
|
||||||
|
return isinstance(obj, CompiledInstance) and \
|
||||||
|
obj.name.string_name in ('int', 'float')
|
||||||
|
|
||||||
|
# Static analysis, one is a number, the other one is not.
|
||||||
|
if operator in ('+', '-') and l_is_num != r_is_num \
|
||||||
|
and not (check(left) or check(right)):
|
||||||
|
message = "TypeError: unsupported operand type(s) for +: %s and %s"
|
||||||
|
analysis.add(context, 'type-error-operation', operator,
|
||||||
|
message % (left, right))
|
||||||
|
|
||||||
|
return ContextSet(left, right)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_statements(evaluator, context, stmt, name):
|
||||||
|
"""
|
||||||
|
This is the part where statements are being stripped.
|
||||||
|
|
||||||
|
Due to lazy evaluation, statements like a = func; b = a; b() have to be
|
||||||
|
evaluated.
|
||||||
|
"""
|
||||||
|
pep0484_contexts = \
|
||||||
|
pep0484.find_type_from_comment_hint_assign(context, stmt, name)
|
||||||
|
if pep0484_contexts:
|
||||||
|
return pep0484_contexts
|
||||||
|
|
||||||
|
return eval_expr_stmt(context, stmt, seek_name=name)
|
||||||
|
|
||||||
|
|
||||||
|
def tree_name_to_contexts(evaluator, context, tree_name):
|
||||||
|
types = []
|
||||||
|
node = tree_name.get_definition(import_name_always=True)
|
||||||
|
if node is None:
|
||||||
|
node = tree_name.parent
|
||||||
|
if node.type == 'global_stmt':
|
||||||
|
context = evaluator.create_context(context, tree_name)
|
||||||
|
finder = NameFinder(evaluator, context, context, tree_name.value)
|
||||||
|
filters = finder.get_filters(search_global=True)
|
||||||
|
# For global_stmt lookups, we only need the first possible scope,
|
||||||
|
# which means the function itself.
|
||||||
|
filters = [next(filters)]
|
||||||
|
return finder.find(filters, attribute_lookup=False)
|
||||||
|
elif node.type not in ('import_from', 'import_name'):
|
||||||
|
raise ValueError("Should not happen.")
|
||||||
|
|
||||||
|
typ = node.type
|
||||||
|
if typ == 'for_stmt':
|
||||||
|
types = pep0484.find_type_from_comment_hint_for(context, node, tree_name)
|
||||||
|
if types:
|
||||||
|
return types
|
||||||
|
if typ == 'with_stmt':
|
||||||
|
types = pep0484.find_type_from_comment_hint_with(context, node, tree_name)
|
||||||
|
if types:
|
||||||
|
return types
|
||||||
|
|
||||||
|
if typ in ('for_stmt', 'comp_for'):
|
||||||
|
try:
|
||||||
|
types = context.predefined_names[node][tree_name.value]
|
||||||
|
except KeyError:
|
||||||
|
cn = ContextualizedNode(context, node.children[3])
|
||||||
|
for_types = iterate_contexts(cn.infer(), cn)
|
||||||
|
c_node = ContextualizedName(context, tree_name)
|
||||||
|
types = check_tuple_assignments(evaluator, c_node, for_types)
|
||||||
|
elif typ == 'expr_stmt':
|
||||||
|
types = _remove_statements(evaluator, context, node, tree_name)
|
||||||
|
elif typ == 'with_stmt':
|
||||||
|
context_managers = context.eval_node(node.get_test_node_from_name(tree_name))
|
||||||
|
enter_methods = context_managers.py__getattribute__('__enter__')
|
||||||
|
return enter_methods.execute_evaluated()
|
||||||
|
elif typ in ('import_from', 'import_name'):
|
||||||
|
types = imports.infer_import(context, tree_name)
|
||||||
|
elif typ in ('funcdef', 'classdef'):
|
||||||
|
types = _apply_decorators(context, node)
|
||||||
|
elif typ == 'try_stmt':
|
||||||
|
# TODO an exception can also be a tuple. Check for those.
|
||||||
|
# TODO check for types that are not classes and add it to
|
||||||
|
# the static analysis report.
|
||||||
|
exceptions = context.eval_node(tree_name.get_previous_sibling().get_previous_sibling())
|
||||||
|
types = exceptions.execute_evaluated()
|
||||||
|
else:
|
||||||
|
raise ValueError("Should not happen.")
|
||||||
|
return types
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_decorators(context, node):
|
||||||
|
"""
|
||||||
|
Returns the function, that should to be executed in the end.
|
||||||
|
This is also the places where the decorators are processed.
|
||||||
|
"""
|
||||||
|
if node.type == 'classdef':
|
||||||
|
decoratee_context = er.ClassContext(
|
||||||
|
context.evaluator,
|
||||||
|
parent_context=context,
|
||||||
|
classdef=node
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
decoratee_context = er.FunctionContext(
|
||||||
|
context.evaluator,
|
||||||
|
parent_context=context,
|
||||||
|
funcdef=node
|
||||||
|
)
|
||||||
|
initial = values = ContextSet(decoratee_context)
|
||||||
|
for dec in reversed(node.get_decorators()):
|
||||||
|
debug.dbg('decorator: %s %s', dec, values)
|
||||||
|
dec_values = context.eval_node(dec.children[1])
|
||||||
|
trailer_nodes = dec.children[2:-1]
|
||||||
|
if trailer_nodes:
|
||||||
|
# Create a trailer and evaluate it.
|
||||||
|
trailer = tree.PythonNode('trailer', trailer_nodes)
|
||||||
|
trailer.parent = dec
|
||||||
|
dec_values = eval_trailer(context, dec_values, trailer)
|
||||||
|
|
||||||
|
if not len(dec_values):
|
||||||
|
debug.warning('decorator not found: %s on %s', dec, node)
|
||||||
|
return initial
|
||||||
|
|
||||||
|
values = dec_values.execute(param.ValuesArguments([values]))
|
||||||
|
if not len(values):
|
||||||
|
debug.warning('not possible to resolve wrappers found %s', node)
|
||||||
|
return initial
|
||||||
|
|
||||||
|
debug.dbg('decorator end %s', values)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def check_tuple_assignments(evaluator, contextualized_name, context_set):
|
||||||
|
"""
|
||||||
|
Checks if tuples are assigned.
|
||||||
|
"""
|
||||||
|
lazy_context = None
|
||||||
|
for index, node in contextualized_name.assignment_indexes():
|
||||||
|
cn = ContextualizedNode(contextualized_name.context, node)
|
||||||
|
iterated = context_set.iterate(cn)
|
||||||
|
for _ in range(index + 1):
|
||||||
|
try:
|
||||||
|
lazy_context = next(iterated)
|
||||||
|
except StopIteration:
|
||||||
|
# We could do this with the default param in next. But this
|
||||||
|
# would allow this loop to run for a very long time if the
|
||||||
|
# index number is high. Therefore break if the loop is
|
||||||
|
# finished.
|
||||||
|
return ContextSet()
|
||||||
|
context_set = lazy_context.infer()
|
||||||
|
return context_set
|
||||||
|
|
||||||
|
|
||||||
|
def eval_subscript_list(evaluator, context, index):
|
||||||
|
"""
|
||||||
|
Handles slices in subscript nodes.
|
||||||
|
"""
|
||||||
|
if index == ':':
|
||||||
|
# Like array[:]
|
||||||
|
return ContextSet(iterable.Slice(context, None, None, None))
|
||||||
|
|
||||||
|
elif index.type == 'subscript' and not index.children[0] == '.':
|
||||||
|
# subscript basically implies a slice operation, except for Python 2's
|
||||||
|
# Ellipsis.
|
||||||
|
# e.g. array[:3]
|
||||||
|
result = []
|
||||||
|
for el in index.children:
|
||||||
|
if el == ':':
|
||||||
|
if not result:
|
||||||
|
result.append(None)
|
||||||
|
elif el.type == 'sliceop':
|
||||||
|
if len(el.children) == 2:
|
||||||
|
result.append(el.children[1])
|
||||||
|
else:
|
||||||
|
result.append(el)
|
||||||
|
result += [None] * (3 - len(result))
|
||||||
|
|
||||||
|
return ContextSet(iterable.Slice(context, *result))
|
||||||
|
|
||||||
|
# No slices
|
||||||
|
return context.eval_node(index)
|
||||||
@@ -10,13 +10,13 @@ from jedi.evaluate.compiled import CompiledObject
|
|||||||
from jedi.evaluate.context import ContextualizedNode
|
from jedi.evaluate.context import ContextualizedNode
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import common
|
from jedi.evaluate.utils import ignored
|
||||||
|
|
||||||
|
|
||||||
def get_venv_path(venv):
|
def get_venv_path(venv):
|
||||||
"""Get sys.path for specified virtual environment."""
|
"""Get sys.path for specified virtual environment."""
|
||||||
sys_path = _get_venv_path_dirs(venv)
|
sys_path = _get_venv_path_dirs(venv)
|
||||||
with common.ignored(ValueError):
|
with ignored(ValueError):
|
||||||
sys_path.remove('')
|
sys_path.remove('')
|
||||||
sys_path = _get_sys_path_with_egglinks(sys_path)
|
sys_path = _get_sys_path_with_egglinks(sys_path)
|
||||||
# As of now, get_venv_path_dirs does not scan built-in pythonpath and
|
# As of now, get_venv_path_dirs does not scan built-in pythonpath and
|
||||||
@@ -120,10 +120,9 @@ def _paths_from_assignment(module_context, expr_stmt):
|
|||||||
except AssertionError:
|
except AssertionError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
from jedi.evaluate.iterable import py__iter__
|
from jedi.evaluate.syntax_tree import is_string
|
||||||
from jedi.evaluate.precedence import is_string
|
|
||||||
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
||||||
for lazy_context in py__iter__(module_context.evaluator, cn.infer(), cn):
|
for lazy_context in cn.infer().iterate(cn):
|
||||||
for context in lazy_context.infer():
|
for context in lazy_context.infer():
|
||||||
if is_string(context):
|
if is_string(context):
|
||||||
yield context.obj
|
yield context.obj
|
||||||
@@ -194,7 +193,7 @@ def sys_path_with_modifications(evaluator, module_context):
|
|||||||
|
|
||||||
curdir = os.path.abspath(os.curdir)
|
curdir = os.path.abspath(os.curdir)
|
||||||
#TODO why do we need a chdir?
|
#TODO why do we need a chdir?
|
||||||
with common.ignored(OSError):
|
with ignored(OSError):
|
||||||
os.chdir(os.path.dirname(path))
|
os.chdir(os.path.dirname(path))
|
||||||
|
|
||||||
buildout_script_paths = set()
|
buildout_script_paths = set()
|
||||||
@@ -246,7 +245,7 @@ def _detect_django_path(module_path):
|
|||||||
result = []
|
result = []
|
||||||
|
|
||||||
for parent in traverse_parents(module_path):
|
for parent in traverse_parents(module_path):
|
||||||
with common.ignored(IOError):
|
with ignored(IOError):
|
||||||
with open(parent + os.path.sep + 'manage.py'):
|
with open(parent + os.path.sep + 'manage.py'):
|
||||||
debug.dbg('Found django path: %s', module_path)
|
debug.dbg('Found django path: %s', module_path)
|
||||||
result.append(parent)
|
result.append(parent)
|
||||||
|
|||||||
@@ -4,7 +4,17 @@ import contextlib
|
|||||||
import functools
|
import functools
|
||||||
|
|
||||||
from jedi._compatibility import reraise
|
from jedi._compatibility import reraise
|
||||||
from jedi import settings
|
|
||||||
|
|
||||||
|
def to_list(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return list(func(*args, **kwargs))
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def unite(iterable):
|
||||||
|
"""Turns a two dimensional array into a one dimensional."""
|
||||||
|
return set(typ for types in iterable for typ in types)
|
||||||
|
|
||||||
|
|
||||||
class UncaughtAttributeError(Exception):
|
class UncaughtAttributeError(Exception):
|
||||||
@@ -78,16 +88,6 @@ class PushBackIterator(object):
|
|||||||
return self.current
|
return self.current
|
||||||
|
|
||||||
|
|
||||||
def indent_block(text, indention=' '):
|
|
||||||
"""This function indents a text block with a default of four spaces."""
|
|
||||||
temp = ''
|
|
||||||
while text and text[-1] == '\n':
|
|
||||||
temp += text[-1]
|
|
||||||
text = text[:-1]
|
|
||||||
lines = text.split('\n')
|
|
||||||
return '\n'.join(map(lambda s: indention + s, lines)) + temp
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def ignored(*exceptions):
|
def ignored(*exceptions):
|
||||||
"""
|
"""
|
||||||
@@ -100,12 +100,11 @@ def ignored(*exceptions):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def unite(iterable):
|
def indent_block(text, indention=' '):
|
||||||
"""Turns a two dimensional array into a one dimensional."""
|
"""This function indents a text block with a default of four spaces."""
|
||||||
return set(typ for types in iterable for typ in types)
|
temp = ''
|
||||||
|
while text and text[-1] == '\n':
|
||||||
|
temp += text[-1]
|
||||||
def to_list(func):
|
text = text[:-1]
|
||||||
def wrapper(*args, **kwargs):
|
lines = text.split('\n')
|
||||||
return list(func(*args, **kwargs))
|
return '\n'.join(map(lambda s: indention + s, lines)) + temp
|
||||||
return wrapper
|
|
||||||
@@ -14,7 +14,6 @@ following functions (sometimes bug-prone):
|
|||||||
"""
|
"""
|
||||||
import difflib
|
import difflib
|
||||||
|
|
||||||
from jedi import common
|
|
||||||
from parso import python_bytes_to_unicode, split_lines
|
from parso import python_bytes_to_unicode, split_lines
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
|
|
||||||
@@ -165,38 +164,37 @@ def inline(script):
|
|||||||
dct = {}
|
dct = {}
|
||||||
|
|
||||||
definitions = script.goto_assignments()
|
definitions = script.goto_assignments()
|
||||||
with common.ignored(AssertionError):
|
assert len(definitions) == 1
|
||||||
assert len(definitions) == 1
|
stmt = definitions[0]._definition
|
||||||
stmt = definitions[0]._definition
|
usages = script.usages()
|
||||||
usages = script.usages()
|
inlines = [r for r in usages
|
||||||
inlines = [r for r in usages
|
if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos]
|
||||||
if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos]
|
inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column),
|
||||||
inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column),
|
reverse=True)
|
||||||
reverse=True)
|
expression_list = stmt.expression_list()
|
||||||
expression_list = stmt.expression_list()
|
# don't allow multiline refactorings for now.
|
||||||
# don't allow multiline refactorings for now.
|
assert stmt.start_pos[0] == stmt.end_pos[0]
|
||||||
assert stmt.start_pos[0] == stmt.end_pos[0]
|
index = stmt.start_pos[0] - 1
|
||||||
index = stmt.start_pos[0] - 1
|
|
||||||
|
|
||||||
line = new_lines[index]
|
line = new_lines[index]
|
||||||
replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1]
|
replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1]
|
||||||
replace_str = replace_str.strip()
|
replace_str = replace_str.strip()
|
||||||
# tuples need parentheses
|
# tuples need parentheses
|
||||||
if expression_list and isinstance(expression_list[0], pr.Array):
|
if expression_list and isinstance(expression_list[0], pr.Array):
|
||||||
arr = expression_list[0]
|
arr = expression_list[0]
|
||||||
if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1:
|
if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1:
|
||||||
replace_str = '(%s)' % replace_str
|
replace_str = '(%s)' % replace_str
|
||||||
|
|
||||||
# if it's the only assignment, remove the statement
|
# if it's the only assignment, remove the statement
|
||||||
if len(stmt.get_defined_names()) == 1:
|
if len(stmt.get_defined_names()) == 1:
|
||||||
line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:]
|
line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:]
|
||||||
|
|
||||||
dct = _rename(inlines, replace_str)
|
dct = _rename(inlines, replace_str)
|
||||||
# remove the empty line
|
# remove the empty line
|
||||||
new_lines = dct[script.path][2]
|
new_lines = dct[script.path][2]
|
||||||
if line.strip():
|
if line.strip():
|
||||||
new_lines[index] = line
|
new_lines[index] = line
|
||||||
else:
|
else:
|
||||||
new_lines.pop(index)
|
new_lines.pop(index)
|
||||||
|
|
||||||
return Refactoring(dct)
|
return Refactoring(dct)
|
||||||
|
|||||||
@@ -95,4 +95,4 @@ def test_time_docstring():
|
|||||||
|
|
||||||
|
|
||||||
def test_dict_values():
|
def test_dict_values():
|
||||||
assert Script('import sys/sys.modules["alshdb;lasdhf"]').goto_definitions()
|
assert Script('import sys\nsys.modules["alshdb;lasdhf"]').goto_definitions()
|
||||||
|
|||||||
Reference in New Issue
Block a user