diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index e31c713e..b1f13765 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -30,6 +30,7 @@ from jedi.evaluate import Evaluator from jedi.evaluate import representation as er from jedi.evaluate import compiled from jedi.evaluate import imports +from jedi.evaluate.param import try_iter_content from jedi.evaluate.cache import memoize_default from jedi.evaluate.helpers import FakeName, get_module_names from jedi.evaluate.finder import global_names_dict_generator, filter_definition_names @@ -524,32 +525,27 @@ class Script(object): for o in origins if hasattr(o, 'py__call__')] def _analysis(self): - def check_types(types): - for typ in types: - try: - f = typ.iter_content - except AttributeError: - pass + self._evaluator.is_analysis = True + try: + for node in self._parser.module().nodes_to_execute(): + if node.type in ('funcdef', 'classdef'): + if node.type == 'classdef': + continue + raise NotImplementedError + er.Function(self._evaluator, node).get_decorated_func() + elif isinstance(node, tree.Import): + import_names = set(node.get_defined_names()) + if node.is_nested(): + import_names |= set(path[-1] for path in node.paths()) + for n in import_names: + imports.ImportWrapper(self._evaluator, n).follow() else: - check_types(f()) + try_iter_content(self._evaluator.eval_element(node)) - for node in self._parser.module().nodes_to_execute(): - if node.type in ('funcdef', 'classdef'): - if node.type == 'classdef': - continue - raise NotImplementedError - er.Function(self._evaluator, node).get_decorated_func() - elif isinstance(node, tree.Import): - import_names = set(node.get_defined_names()) - if node.is_nested(): - import_names |= set(path[-1] for path in node.paths()) - for n in import_names: - imports.ImportWrapper(self._evaluator, n).follow() - else: - check_types(self._evaluator.eval_element(node)) - - ana = [a for a in self._evaluator.analysis if self.path == a.path] - return sorted(set(ana), key=lambda x: x.line) + ana = [a for a in self._evaluator.analysis if self.path == a.path] + return sorted(set(ana), key=lambda x: x.line) + finally: + self._evaluator.is_analysis = False class Interpreter(Script): diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 38d8de50..2a34cb85 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -89,6 +89,7 @@ class Evaluator(object): self.execution_recursion_detector = recursion.ExecutionRecursionDetector() self.analysis = [] self.predefined_if_name_dict_dict = {} + self.is_analysis = False def wrap(self, element): if isinstance(element, tree.Class): @@ -343,6 +344,9 @@ class Evaluator(object): if not isinstance(arguments, param.Arguments): arguments = param.Arguments(self, arguments, trailer) + if self.is_analysis: + arguments.eval_all() + if obj.isinstance(er.Function): obj = obj.get_decorated_func() diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index e6dc2956..c6535655 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -148,7 +148,7 @@ class Comprehension(IterableWrapper): return [self._evaluator.eval_element(self.eval_node())[index]] def __repr__(self): - return "" % (type(self).__name__, self._atom) + return "<%s of %s>" % (type(self).__name__, self._atom) class ArrayMixin(object): diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 8524bf95..2423704b 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -12,6 +12,17 @@ from jedi.evaluate.helpers import FakeName from jedi.cache import underscore_memoization +def try_iter_content(types): + """Helper method for static analysis.""" + for typ in types: + try: + f = typ.iter_content + except AttributeError: + pass + else: + try_iter_content(f()) + + class Arguments(tree.Base): def __init__(self, evaluator, argument_node, trailer=None): """ @@ -160,6 +171,16 @@ class Arguments(tree.Base): else: return None + def eval_all(self, func=None): + """ + Evaluates all arguments as a support for static analysis + (normally Jedi). + """ + for key, element_values in self.unpack(): + for element in element_values: + types = self._evaluator.eval_element(element) + try_iter_content(types) + class ExecutedParam(tree.Param): """Fake a param and give it values.""" diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 79c549bb..71aa0752 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -517,7 +517,7 @@ class Node(BaseNode): For static analysis. """ result = [] - if self.type not in Node._IGNORE_EXECUTE_NODES: + if self.type not in Node._IGNORE_EXECUTE_NODES and not last_added: result.append(self) last_added = True