From d9d3aeb5bc2e104b7284fe7e03d8c8072e73aa9c Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 28 Sep 2017 09:16:43 +0200 Subject: [PATCH] Move more functions to the syntax tree module. --- jedi/api/__init__.py | 5 +- jedi/evaluate/__init__.py | 6 +- jedi/evaluate/filters.py | 4 +- jedi/evaluate/finder.py | 138 +------------------------------ jedi/evaluate/representation.py | 4 +- jedi/evaluate/syntax_tree.py | 141 +++++++++++++++++++++++++++++++- 6 files changed, 149 insertions(+), 149 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index ac1132be..0c21c1d9 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -34,6 +34,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.iterable import unpack_tuple_to_dict 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 # can remove some "maximum recursion depth" errors. @@ -348,10 +349,8 @@ class Script(object): for node in get_executable_nodes(module_node): context = self._get_module().create_context(node) if node.type in ('funcdef', 'classdef'): - # TODO This is stupid, should be private - from jedi.evaluate.finder import _name_to_types # 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): import_names = set(node.get_defined_names()) if node.is_nested(): diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index d9d42c01..39c2fa61 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -76,14 +76,14 @@ from jedi.evaluate import recursion from jedi.evaluate import iterable 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 helpers from jedi.evaluate.filters import TreeNameDefinition, ParamName from jedi.evaluate.instance import AnonymousInstance, BoundMethod from jedi.evaluate.context import ContextualizedName, ContextualizedNode, \ ContextSet, NO_CONTEXTS -from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, eval_node +from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \ + eval_node, check_tuple_assignments from jedi import parser_utils @@ -243,7 +243,7 @@ class Evaluator(object): cn = ContextualizedNode(context, def_.children[3]) for_types = iterable.py__iter__types(self, container_types, cn) 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'): return imports.infer_import(context, name) diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index f4c6463b..735b27b7 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -94,8 +94,8 @@ class TreeNameDefinition(AbstractTreeName): def infer(self): # Refactor this, should probably be here. - from jedi.evaluate.finder import _name_to_types - return _name_to_types(self.parent_context.evaluator, self.parent_context, self.tree_name) + from jedi.evaluate.syntax_tree import tree_name_to_contexts + return tree_name_to_contexts(self.parent_context.evaluator, self.parent_context, self.tree_name) @property def api_type(self): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 19ac6f25..9f59c22a 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -19,20 +19,16 @@ from parso.python import tree from parso.tree import search_ancestor from jedi import debug from jedi import settings -from jedi.evaluate import representation as er from jedi.evaluate.instance import AbstractInstanceContext from jedi.evaluate import compiled -from jedi.evaluate import pep0484 from jedi.evaluate import iterable -from jedi.evaluate import imports from jedi.evaluate import analysis from jedi.evaluate import flow_analysis from jedi.evaluate import param from jedi.evaluate import helpers from jedi.evaluate.filters import get_global_filters, TreeNameDefinition -from jedi.evaluate.context import ContextualizedName, ContextualizedNode, ContextSet +from jedi.evaluate.context import ContextSet from jedi.parser_utils import is_scope, get_parent_scope -from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt class NameFinder(object): @@ -183,117 +179,6 @@ class NameFinder(object): 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 = 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(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 = exceptions.execute_evaluated() - 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 = 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 _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 _check_flow_information(context, flow, search_name, pos): """ Try to find out the type of a variable just with the information that is given by the flows: e.g. It is also responsible for assert checks.:: @@ -371,24 +256,3 @@ def _check_isinstance_type(context, element, search_name): else: context_set |= cls_or_tup.execute_evaluated() return context_set - - -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 = iterable.py__iter__(evaluator, context_set, 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 diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index cb4525b1..a7f32855 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -89,8 +89,8 @@ class ClassName(TreeNameDefinition): # 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 # achieve this. - from jedi.evaluate.finder import _name_to_types - inferred = _name_to_types( + from jedi.evaluate.syntax_tree import tree_name_to_contexts + inferred = tree_name_to_contexts( self.parent_context.evaluator, self._name_context, self.tree_name) for result_context in inferred: diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index f9ba1847..4f310a14 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -223,8 +223,7 @@ def _eval_expr_stmt(context, stmt, seek_name=None): if seek_name: c_node = ContextualizedName(context, seek_name) - from jedi.evaluate import finder - context_set = finder.check_tuple_assignments(context.evaluator, c_node, context_set) + 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': @@ -426,3 +425,141 @@ def _eval_comparison_part(evaluator, context, left, operator, right): 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) + from jedi.evaluate.finder import NameFinder + 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'): + from jedi.evaluate import iterable + 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 = context_managers.py__getattribute__('__enter__') + return enter_methods.execute_evaluated() + elif typ in ('import_from', 'import_name'): + from jedi.evaluate import imports + 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 = exceptions.execute_evaluated() + 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. + """ + from jedi.evaluate import representation as er + 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 = 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 + + from jedi.evaluate import param + 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) + from jedi.evaluate import iterable + iterated = iterable.py__iter__(evaluator, context_set, 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