diff --git a/jedi/evaluate/context/__init__.py b/jedi/evaluate/context/__init__.py index 8db0ff1c..199aaf1d 100644 --- a/jedi/evaluate/context/__init__.py +++ b/jedi/evaluate/context/__init__.py @@ -1,320 +1 @@ -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.common import BaseContextSet, BaseContext - - -class Context(BaseContext): - """ - Should be defined, otherwise the API returns empty types. - """ - - predefined_names = {} - tree_node = None - """ - To be defined by subclasses. - """ - - @property - def api_type(self): - # By default just lower name of the class. Can and should be - # overwritten. - return self.__class__.__name__.lower() - - @debug.increase_indent - 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) - - def execute_evaluated(self, *value_list): - """ - Execute a function with already executed arguments. - """ - from jedi.evaluate.param import ValuesArguments - arguments = ValuesArguments([ContextSet(value) for value in value_list]) - 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): - return self.evaluator.eval_element(self, node) - - @Python3Method - def py__getattribute__(self, name_or_str, name_context=None, position=None, - search_global=False, is_goto=False, - analysis_errors=True): - """ - :param position: Position of the last statement -> tuple of line, column - """ - if name_context is None: - 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( - self, name_or_str, name_context, position, search_global, is_goto, - analysis_errors) - - def create_context(self, node, node_is_context=False, node_is_object=False): - return self.evaluator.create_context(self, node, node_is_context, node_is_object) - - def is_class(self): - return False - - def py__bool__(self): - """ - Since Wrapper is a super class for classes, functions and modules, - the return value will always be true. - """ - return True - - def py__doc__(self, include_call_signature=False): - try: - self.tree_node.get_doc_node - except AttributeError: - return '' - else: - if include_call_signature: - return get_doc_with_call_signature(self.tree_node) - else: - return clean_scope_docstring(self.tree_node) - 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): - def __init__(self, evaluator, parent_context=None): - super(TreeContext, self).__init__(evaluator, parent_context) - self.predefined_names = {} - - def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self.tree_node) - - -class AbstractLazyContext(object): - def __init__(self, data): - self.data = data - - def __repr__(self): - return '<%s: %s>' % (self.__class__.__name__, self.data) - - def infer(self): - raise NotImplementedError - - -class LazyKnownContext(AbstractLazyContext): - """data is a context.""" - def infer(self): - return ContextSet(self.data) - - -class LazyKnownContexts(AbstractLazyContext): - """data is a ContextSet.""" - def infer(self): - return self.data - - -class LazyUnknownContext(AbstractLazyContext): - def __init__(self): - super(LazyUnknownContext, self).__init__(None) - - def infer(self): - return NO_CONTEXTS - - -class LazyTreeContext(AbstractLazyContext): - def __init__(self, context, node): - super(LazyTreeContext, self).__init__(node) - self._context = context - # We need to save the predefined names. It's an unfortunate side effect - # that needs to be tracked otherwise results will be wrong. - self._predefined_names = dict(context.predefined_names) - - def infer(self): - old, self._context.predefined_names = \ - self._context.predefined_names, self._predefined_names - try: - return self._context.eval_node(self.data) - finally: - self._context.predefined_names = old - - -def get_merged_lazy_context(lazy_contexts): - if len(lazy_contexts) > 1: - return MergedLazyContexts(lazy_contexts) - else: - return lazy_contexts[0] - - -class MergedLazyContexts(AbstractLazyContext): - """data is a list of lazy contexts.""" - def infer(self): - return ContextSet.from_sets(l.infer() for l in self.data) - - -class ContextualizedNode(object): - def __init__(self, context, node): - self.context = context - self.node = node - - def get_root_context(self): - return self.context.get_root_context() - - def infer(self): - return self.context.eval_node(self.node) - - -class ContextualizedName(ContextualizedNode): - # TODO merge with TreeNameDefinition?! - @property - def name(self): - return self.node - - def assignment_indexes(self): - """ - Returns an array of tuple(int, node) of the indexes that are used in - tuple assignments. - - For example if the name is ``y`` in the following code:: - - x, (y, z) = 2, '' - - would result in ``[(1, xyz_node), (0, yz_node)]``. - """ - indexes = [] - node = self.node.parent - compare = self.node - while node is not None: - if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'): - for i, child in enumerate(node.children): - if child == compare: - indexes.insert(0, (int(i / 2), node)) - break - else: - raise LookupError("Couldn't find the assignment.") - elif isinstance(node, (ExprStmt, CompFor)): - break - - compare = node - node = node.parent - 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 +from jedi.evaluate.context.base import * diff --git a/jedi/evaluate/context/base.py b/jedi/evaluate/context/base.py new file mode 100644 index 00000000..8db0ff1c --- /dev/null +++ b/jedi/evaluate/context/base.py @@ -0,0 +1,320 @@ +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.common import BaseContextSet, BaseContext + + +class Context(BaseContext): + """ + Should be defined, otherwise the API returns empty types. + """ + + predefined_names = {} + tree_node = None + """ + To be defined by subclasses. + """ + + @property + def api_type(self): + # By default just lower name of the class. Can and should be + # overwritten. + return self.__class__.__name__.lower() + + @debug.increase_indent + 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) + + def execute_evaluated(self, *value_list): + """ + Execute a function with already executed arguments. + """ + from jedi.evaluate.param import ValuesArguments + arguments = ValuesArguments([ContextSet(value) for value in value_list]) + 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): + return self.evaluator.eval_element(self, node) + + @Python3Method + def py__getattribute__(self, name_or_str, name_context=None, position=None, + search_global=False, is_goto=False, + analysis_errors=True): + """ + :param position: Position of the last statement -> tuple of line, column + """ + if name_context is None: + 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( + self, name_or_str, name_context, position, search_global, is_goto, + analysis_errors) + + def create_context(self, node, node_is_context=False, node_is_object=False): + return self.evaluator.create_context(self, node, node_is_context, node_is_object) + + def is_class(self): + return False + + def py__bool__(self): + """ + Since Wrapper is a super class for classes, functions and modules, + the return value will always be true. + """ + return True + + def py__doc__(self, include_call_signature=False): + try: + self.tree_node.get_doc_node + except AttributeError: + return '' + else: + if include_call_signature: + return get_doc_with_call_signature(self.tree_node) + else: + return clean_scope_docstring(self.tree_node) + 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): + def __init__(self, evaluator, parent_context=None): + super(TreeContext, self).__init__(evaluator, parent_context) + self.predefined_names = {} + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.tree_node) + + +class AbstractLazyContext(object): + def __init__(self, data): + self.data = data + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.data) + + def infer(self): + raise NotImplementedError + + +class LazyKnownContext(AbstractLazyContext): + """data is a context.""" + def infer(self): + return ContextSet(self.data) + + +class LazyKnownContexts(AbstractLazyContext): + """data is a ContextSet.""" + def infer(self): + return self.data + + +class LazyUnknownContext(AbstractLazyContext): + def __init__(self): + super(LazyUnknownContext, self).__init__(None) + + def infer(self): + return NO_CONTEXTS + + +class LazyTreeContext(AbstractLazyContext): + def __init__(self, context, node): + super(LazyTreeContext, self).__init__(node) + self._context = context + # We need to save the predefined names. It's an unfortunate side effect + # that needs to be tracked otherwise results will be wrong. + self._predefined_names = dict(context.predefined_names) + + def infer(self): + old, self._context.predefined_names = \ + self._context.predefined_names, self._predefined_names + try: + return self._context.eval_node(self.data) + finally: + self._context.predefined_names = old + + +def get_merged_lazy_context(lazy_contexts): + if len(lazy_contexts) > 1: + return MergedLazyContexts(lazy_contexts) + else: + return lazy_contexts[0] + + +class MergedLazyContexts(AbstractLazyContext): + """data is a list of lazy contexts.""" + def infer(self): + return ContextSet.from_sets(l.infer() for l in self.data) + + +class ContextualizedNode(object): + def __init__(self, context, node): + self.context = context + self.node = node + + def get_root_context(self): + return self.context.get_root_context() + + def infer(self): + return self.context.eval_node(self.node) + + +class ContextualizedName(ContextualizedNode): + # TODO merge with TreeNameDefinition?! + @property + def name(self): + return self.node + + def assignment_indexes(self): + """ + Returns an array of tuple(int, node) of the indexes that are used in + tuple assignments. + + For example if the name is ``y`` in the following code:: + + x, (y, z) = 2, '' + + would result in ``[(1, xyz_node), (0, yz_node)]``. + """ + indexes = [] + node = self.node.parent + compare = self.node + while node is not None: + if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'): + for i, child in enumerate(node.children): + if child == compare: + indexes.insert(0, (int(i / 2), node)) + break + else: + raise LookupError("Couldn't find the assignment.") + elif isinstance(node, (ExprStmt, CompFor)): + break + + compare = node + node = node.parent + 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