diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 648c12eb..fd2695ca 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -137,7 +137,7 @@ class Script(object): parser = FastParser(self._grammar, self._source, self.path) save_parser(self.path, parser, pickling=False) - module = self._evaluator.wrap(parser.module) + module = self._evaluator.wrap(parser.module, parent_context=None) imports.add_module(self._evaluator, unicode(module.name), module) return parser.module @@ -188,7 +188,9 @@ class Script(object): leaf = self._get_module().get_leaf_for_position(self._pos) if leaf is None: return [] - definitions = helpers.evaluate_goto_definition(self._evaluator, leaf) + + context = self._evaluator.create_context(leaf) + definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] @@ -299,9 +301,11 @@ class Script(object): if call_signature_details is None: return [] + context = self._evaluator.create_context(call_signature_details.bracket_leaf) with common.scale_speed_settings(settings.scale_call_signatures): definitions = helpers.cache_call_signatures( self._evaluator, + context, call_signature_details.bracket_leaf, self._code_lines, self._pos diff --git a/jedi/api/classes.py b/jedi/api/classes.py index f0dc1e22..e21736c5 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -65,6 +65,7 @@ class BaseDefinition(object): """ #self._definition = list(self._name.infer())[0] #self.is_keyword = isinstance(self._definition, keywords.Keyword) + self._definition = None # generate a path to the definition self._module = name.parent_context.get_root_context() @@ -83,7 +84,7 @@ class BaseDefinition(object): :rtype: str or None """ - return unicode(self._name) + return self._name.string_name @property def start_pos(self): @@ -198,7 +199,7 @@ class BaseDefinition(object): >>> print(d.module_name) # doctest: +ELLIPSIS json """ - return str(self._module.name) + return self._module.name.string_name def in_builtin_module(self): """Whether this is a builtin module.""" @@ -417,7 +418,7 @@ class Completion(BaseDefinition): if 'trailer' in node_names and 'argument' not in node_names: append += '=' - name = str(self._name) + name = self._name.string_name if like_name: name = name[self._like_name_length:] return name + append @@ -450,7 +451,8 @@ class Completion(BaseDefinition): def description(self): """Provide a description of the completion object.""" if self._definition is None: - return '' + return self._name.string_name + t = self.type if t == 'statement' or t == 'import': desc = self._definition.get_code() @@ -577,7 +579,7 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)): d = typ + ' ' + d.name.get_code() elif isinstance(d, iterable.Array): d = 'class ' + d.type - elif isinstance(d, (tree.Class, er.Class, er.Instance)): + elif isinstance(d, (tree.Class, er.ClassContext, er.Instance)): d = 'class ' + unicode(d.name) elif isinstance(d, (er.Function, tree.Function)): d = 'def ' + unicode(d.name) @@ -589,6 +591,7 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)): if d.endswith(','): d = d[:-1] # Remove the comma. else: # ExprStmt + return self._name.string_name try: first_leaf = d.first_leaf() except AttributeError: diff --git a/jedi/api/completion.py b/jedi/api/completion.py index adf77ae8..e05e59df 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -33,7 +33,6 @@ def filter_names(evaluator, completion_names, stack, like_name): and name.string_name.lower().startswith(like_name.lower()) \ or name.string_name.startswith(like_name): - print(name, name.infer()) new = classes.Completion( evaluator, name, @@ -71,7 +70,7 @@ def get_user_scope(module, position): class Completion: def __init__(self, evaluator, module, code_lines, position, call_signatures_method): self._evaluator = evaluator - self._module = evaluator.wrap(module) + self._module = evaluator.wrap(module, parent_context=None) self._code_lines = code_lines # The first step of completions is to get the name @@ -175,7 +174,7 @@ class Completion: scope = get_user_scope(self._module, self._position) if not scope.is_scope(): # Might be a flow (if/while/etc). scope = scope.get_parent_scope() - scope = self._evaluator.wrap(scope) + scope = self._evaluator.create_context(scope) debug.dbg('global completion scope: %s', scope) filters = get_global_filters( self._evaluator, @@ -222,6 +221,7 @@ class Completion: """ Autocomplete inherited methods when overriding in child class. """ + return leaf = self._module.get_leaf_for_position(self._position, include_prefixes=True) cls = leaf.get_parent_until(tree.Class) if isinstance(cls, (tree.Class, tree.Function)): diff --git a/jedi/api/helpers.py b/jedi/api/helpers.py index 20b6ad9c..b30208b3 100644 --- a/jedi/api/helpers.py +++ b/jedi/api/helpers.py @@ -192,7 +192,7 @@ def get_possible_completion_types(grammar, stack): return keywords, grammar_labels -def evaluate_goto_definition(evaluator, leaf): +def evaluate_goto_definition(evaluator, context, leaf): if leaf.type == 'name': # In case of a name we can just use goto_definition which does all the # magic itself. @@ -207,7 +207,7 @@ def evaluate_goto_definition(evaluator, leaf): if node is None: return [] - return evaluator.eval_element(node) + return evaluator.eval_element(context, node) CallSignatureDetails = namedtuple( @@ -288,7 +288,7 @@ def get_call_signature_details(module, position): @time_cache("call_signatures_validity") -def cache_call_signatures(evaluator, bracket_leaf, code_lines, user_pos): +def cache_call_signatures(evaluator, context, bracket_leaf, code_lines, user_pos): """This function calculates the cache key.""" index = user_pos[0] - 1 @@ -304,5 +304,6 @@ def cache_call_signatures(evaluator, bracket_leaf, code_lines, user_pos): yield (module_path, before_bracket, bracket_leaf.start_pos) yield evaluate_goto_definition( evaluator, + context, bracket_leaf.get_previous_leaf() ) diff --git a/jedi/api/keywords.py b/jedi/api/keywords.py index 365cb20c..85db5e6e 100644 --- a/jedi/api/keywords.py +++ b/jedi/api/keywords.py @@ -3,7 +3,7 @@ import keyword from jedi._compatibility import is_py3, is_py35 from jedi import common -from jedi.evaluate.helpers import FakeName +from jedi.evaluate.filters import AbstractNameDefinition from jedi.parser.tree import Leaf try: from pydoc_data import topics as pydoc_topics @@ -66,11 +66,17 @@ keywords_only_valid_as_leaf = ( ) +class KeywordName(AbstractNameDefinition): + def __init__(self, evaluator, name): + self.string_name = name + self.parent_context = evaluator.BUILTINS + + class Keyword(object): type = 'completion_keyword' def __init__(self, evaluator, name, pos): - self.name = FakeName(name, self, pos) + self.name = KeywordName(evaluator, name) self.start_pos = pos self.parent = evaluator.BUILTINS diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 31e1a5e4..3ba4bda8 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -109,7 +109,7 @@ class Evaluator(object): self.recursion_detector = recursion.RecursionDetector(self) self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self) - def wrap(self, element, parent_context=None): + def wrap(self, element, parent_context): if isinstance(element, (er.Wrapper, er.InstanceElement, er.ModuleContext, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None: # TODO this is so ugly, please refactor. @@ -118,7 +118,7 @@ class Evaluator(object): if element.type == 'classdef': return er.ClassContext(self, element, parent_context) elif element.type == 'funcdef': - return er.Function(self, element) + return er.Function(self, parent_context, element) elif element.type == 'lambda': return er.LambdaWrapper(self, element) elif element.type == 'file_input': @@ -126,7 +126,7 @@ class Evaluator(object): else: return element - def find_types(self, scope, name_str, position=None, search_global=False, + def find_types(self, context, name_str, position=None, search_global=False, is_goto=False): """ This is the search function. The most important part to debug. @@ -136,7 +136,7 @@ class Evaluator(object): :param position: Position of the last statement -> tuple of line, column :return: List of Names. Their parents are the types. """ - f = finder.NameFinder(self, scope, name_str, position) + f = finder.NameFinder(self, context, name_str, position) filters = f.get_filters(search_global) if is_goto: return f.filter_name(filters) @@ -341,8 +341,7 @@ class Evaluator(object): if isinstance(atom, tree.Name): # This is the first global lookup. stmt = atom.get_definition() - scope = stmt.get_parent_until(tree.IsScope, include_current=True) - if isinstance(scope, (tree.Function, er.FunctionExecution)): + if isinstance(context, er.FunctionExecution): # Adjust scope: If the name is not in the suite, it's a param # default or annotation and will be resolved as part of the # parent scope. @@ -355,7 +354,7 @@ class Evaluator(object): # We only need to adjust the start_pos for statements, because # there the name cannot be used. stmt = atom - return self.find_types(scope, atom, stmt.start_pos, search_global=True) + return self.find_types(context, atom, stmt.start_pos, search_global=True) elif isinstance(atom, tree.Literal): return set([compiled.create(self, atom.eval())]) else: @@ -421,7 +420,7 @@ class Evaluator(object): if self.is_analysis: arguments.eval_all() - if obj.isinstance(er.Function): + if isinstance(obj, er.Function): obj = obj.get_decorated_func() debug.dbg('execute: %s %s', obj, arguments) @@ -532,7 +531,15 @@ class Evaluator(object): search_global=True, is_goto=True) def create_context(self, node): - scope = node.get_parent_scope() - if scope.get_parent_scope() is not None: - raise NotImplementedError - return self.wrap(scope) + def from_scope(scope): + parent_context = None + parent_scope = scope.get_parent_scope() + if parent_scope is not None: + parent_context = from_scope(parent_scope) + return self.wrap(scope, parent_context=parent_context) + + if node.is_scope(): + scope = node + else: + scope = node.get_parent_scope() + return from_scope(scope) diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index ec5377bb..4c0f2c0c 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -10,9 +10,10 @@ from functools import partial from jedi._compatibility import builtins as _builtins, unicode from jedi import debug from jedi.cache import underscore_memoization, memoize_method -from jedi.parser.tree import Param, Base, Operator +from jedi.parser.tree import Param, Operator from jedi.evaluate.helpers import FakeName from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition +from jedi.evaluate.context import Context from . import fake @@ -36,22 +37,27 @@ class CheckAttribute(object): return partial(self.func, instance) -class CompiledObject(Base): +class CompiledObject(Context): # comply with the parser start_pos = 0, 0 path = None # modules have this attribute - set it to None. used_names = {} # To be consistent with modules. - def __init__(self, evaluator, obj, parent=None): + def __init__(self, evaluator, obj, parent_context=None): self._evaluator = evaluator self.obj = obj - self.parent = parent + self.parent_context = parent_context + + def get_root_node(self): + # To make things a bit easier with filters we add this method here. + return self.get_root_context() @CheckAttribute def py__call__(self, params): if inspect.isclass(self.obj): from jedi.evaluate.representation import Instance - return set([Instance(self._evaluator, self, params)]) + return set([self]) + return set([Instance(self._evaluator, self.parent_context, self, params)]) else: return set(self._execute_function(params)) @@ -206,7 +212,7 @@ class CompiledObject(Base): name = self._get_class().__name__ except AttributeError: name = repr(self.obj) - return FakeName(name, self) + return CompiledContextName(self, name) def _execute_function(self, params): if self.type != 'funcdef': @@ -267,15 +273,21 @@ class CompiledName(AbstractNameDefinition): name = None return '<%s: (%s).%s>' % (type(self).__name__, name, self.string_name) - def is_definition(self): - return True - @underscore_memoization def infer(self): - module = self._compiled_obj.get_parent_until() + module = self._compiled_obj.get_root_context() return [_create_from_name(self._evaluator, module, self._compiled_obj, self.string_name)] +class CompiledContextName(AbstractNameDefinition): + def __init__(self, parent_context, name): + self.string_name = name + self.parent_context = parent_context + + def infer(self): + return [self.parent_context] + + class LazyNamesDict(object): """ A names_dict instance for compiled objects, resembles the parser.tree. diff --git a/jedi/evaluate/compiled/fake.py b/jedi/evaluate/compiled/fake.py index 2fa605b8..a29002d9 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -11,7 +11,6 @@ import types from jedi._compatibility import is_py3, builtins, unicode, is_py34 from jedi.parser import ParserWithRecovery, load_grammar from jedi.parser import tree as pt -from jedi.evaluate.helpers import FakeName modules = {} @@ -69,14 +68,14 @@ def _load_faked_module(module): if module_name == 'builtins' and not is_py3: # There are two implementations of `open` for either python 2/3. # -> Rename the python2 version (`look at fake/builtins.pym`). - open_func = search_scope(module, 'open') - open_func.children[1] = FakeName('open_python3') - open_func = search_scope(module, 'open_python2') - open_func.children[1] = FakeName('open') + open_func = _search_scope(module, 'open') + open_func.children[1].value = 'open_python3' + open_func = _search_scope(module, 'open_python2') + open_func.children[1].value = 'open' return module -def search_scope(scope, obj_name): +def _search_scope(scope, obj_name): for s in scope.subscopes: if str(s.name) == obj_name: return s @@ -120,7 +119,7 @@ def _faked(module, obj, name): # for methods. if name is None: if inspect.isbuiltin(obj): - return search_scope(faked_mod, obj.__name__), faked_mod + return _search_scope(faked_mod, obj.__name__), faked_mod elif not inspect.isclass(obj): # object is a method or descriptor try: @@ -128,22 +127,22 @@ def _faked(module, obj, name): except AttributeError: return None, None else: - cls = search_scope(faked_mod, objclass.__name__) + cls = _search_scope(faked_mod, objclass.__name__) if cls is None: return None, None - return search_scope(cls, obj.__name__), faked_mod + return _search_scope(cls, obj.__name__), faked_mod else: if obj == module: - return search_scope(faked_mod, name), faked_mod + return _search_scope(faked_mod, name), faked_mod else: try: cls_name = obj.__name__ except AttributeError: return None, None - cls = search_scope(faked_mod, cls_name) + cls = _search_scope(faked_mod, cls_name) if cls is None: return None, None - return search_scope(cls, name), faked_mod + return _search_scope(cls, name), faked_mod return None, None diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index b7146f5e..466d4a21 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -12,35 +12,48 @@ from jedi.common import to_list class AbstractNameDefinition(object): start_pos = None string_name = None + parent_context = None @abstractmethod def infer(self): raise NotImplementedError + def get_root_context(self): + if self.parent_context is None: + return self + return self.parent_context.get_root_context() -class TreeNameDefinition(AbstractNameDefinition): + def __repr__(self): + if self.start_pos is None: + return '<%s: %s>' % (type(self).__name__, self.string_name) + return '<%s: %s@%s>' % (type(self).__name__, self.string_name, self.start_pos) + + +class ContextName(AbstractNameDefinition): def __init__(self, parent_context, name): self.parent_context = parent_context - self._name = name - - def get_parent_flow_context(self): - return self.parent_context + self.name = name @property def string_name(self): - return self._name.value + return self.name.value @property def start_pos(self): - return self._name.start_pos + return self.name.start_pos + + def infer(self): + return [self.parent_context] + + +class TreeNameDefinition(ContextName): + def get_parent_flow_context(self): + return self.parent_context 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._name, None) - - def __repr__(self): - return '<%s: %s@%s>' % (type(self).__name__, self.string_name, self.start_pos) + return _name_to_types(self.parent_context._evaluator, self.parent_context, self.name, None) class AbstractFilter(object): @@ -102,8 +115,8 @@ class ParserTreeFilter(AbstractUsedNamesFilter): def _check_flows(self, names): for name in sorted(names, key=lambda name: name.start_pos, reverse=True): stmt = name.get_definition() - name_scope = self._evaluator.wrap(stmt.get_parent_scope()) check = flow_analysis.UNSURE + #name_scope = self._evaluator.wrap(stmt.get_parent_scope()) #check = flow_analysis.break_check(self._evaluator, name_scope, # stmt, self._origin_scope) if check is not flow_analysis.UNREACHABLE: @@ -181,8 +194,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope): until_position = None in_func = True - node = context.parent_context - context = evaluator.wrap(node) + context = context.parent_context # Add builtins to the global scope. for filter in evaluator.BUILTINS.get_filters(search_global=True): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 1e6553aa..9ef8e359 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -95,10 +95,10 @@ def filter_definition_names(names, origin, position=None): class NameFinder(object): - def __init__(self, evaluator, scope, name_str, position=None): + def __init__(self, evaluator, context, name_str, position=None): self._evaluator = evaluator # Make sure that it's not just a syntax tree node. - self.scope = evaluator.wrap(scope) + self.context = context self.name_str = name_str self.position = position self._found_predefined_if_name = None @@ -123,7 +123,7 @@ class NameFinder(object): if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if attribute_lookup: analysis.add_attribute_error(self._evaluator, - self.scope, self.name_str) + self.context, self.name_str) else: message = ("NameError: name '%s' is not defined." % self.name_str) @@ -140,9 +140,9 @@ class NameFinder(object): origin_scope = None if search_global: - return get_global_filters(self._evaluator, self.scope, self.position, origin_scope) + return get_global_filters(self._evaluator, self.context, self.position, origin_scope) else: - return self.scope.get_filters(search_global, self.position, origin_scope=origin_scope) + return self.context.get_filters(search_global, self.position, origin_scope=origin_scope) def names_dict_lookup(self, names_dict, position): def get_param(scope, el): @@ -167,7 +167,7 @@ class NameFinder(object): stmt = name.get_definition() name_scope = self._evaluator.wrap(stmt.get_parent_scope()) - if isinstance(self.scope, er.Instance) and not isinstance(name_scope, er.Instance): + if isinstance(self.context, er.Instance) and not isinstance(name_scope, er.Instance): # Instances should not be checked for positioning, because we # don't know in which order the functions are called. last_names.append(name) @@ -210,7 +210,7 @@ class NameFinder(object): # deliver types. self._found_predefined_if_name = types else: - check = flow_analysis.break_check(self._evaluator, self.scope, + check = flow_analysis.break_check(self._evaluator, self.context, origin_scope) if check is flow_analysis.UNREACHABLE: self._found_predefined_if_name = set() @@ -249,7 +249,7 @@ class NameFinder(object): if names: break debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str, - self.scope, names, self.position) + self.context, names, self.position) return list(self._clean_names(names)) def _clean_names(self, names): @@ -311,13 +311,13 @@ class NameFinder(object): for name in names: new_types = name.infer() - if isinstance(self.scope, (er.ClassContext, er.Instance)) and attribute_lookup: + if isinstance(self.context, (er.ClassContext, er.Instance)) and attribute_lookup: types |= set(self._resolve_descriptors(name, new_types)) else: types |= set(new_types) - if not names and isinstance(self.scope, er.Instance): + if not names and isinstance(self.context, er.Instance): # handling __getattr__ / __getattribute__ - return self._check_getattr(self.scope) + return self._check_getattr(self.context) return types @@ -336,7 +336,7 @@ class NameFinder(object): except AttributeError: result.add(r) else: - result |= desc_return(self.scope) + result |= desc_return(self.context) return result @@ -365,7 +365,7 @@ def _name_to_types(evaluator, context, name, scope): for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3]) types = check_tuple_assignments(evaluator, for_types, name) elif isinstance(typ, tree.Param): - types = _eval_param(evaluator, typ, scope) + types = _eval_param(evaluator, context, typ, scope) elif typ.isinstance(tree.ExprStmt): types = _remove_statements(evaluator, context, typ, name) elif typ.isinstance(tree.WithStmt): @@ -373,7 +373,7 @@ def _name_to_types(evaluator, context, name, scope): elif isinstance(typ, tree.Import): types = imports.ImportWrapper(evaluator, name).follow() elif typ.isinstance(tree.Function, tree.Class): - types = [evaluator.wrap(typ)] + types = [evaluator.wrap(typ, parent_context=context)] elif typ.type == 'global_stmt': for s in _get_global_stmt_scopes(evaluator, typ, name): finder = NameFinder(evaluator, s, str(name)) @@ -427,7 +427,7 @@ def _remove_statements(evaluator, context, stmt, name): return types -def _eval_param(evaluator, param, scope): +def _eval_param(evaluator, context, param, scope): res_new = set() func = param.get_parent_scope() @@ -441,8 +441,9 @@ def _eval_param(evaluator, param, scope): if isinstance(scope, er.InstanceElement): res_new.add(scope.instance) else: - inst = er.Instance(evaluator, evaluator.wrap(cls), - Arguments(evaluator, ()), is_generated=True) + inst = er.Instance(evaluator, context.parent_context.parent_context, context.parent_context, + Arguments(evaluator, context, ()), + is_generated=True) res_new.add(inst) return res_new diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 7c61ac29..01e708b6 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -1,6 +1,7 @@ import copy from itertools import chain +from jedi.evaluate.filters import AbstractNameDefinition from jedi.parser import tree @@ -172,6 +173,7 @@ class FakeName(tree.Name): In case is_definition is defined (not None), that bool value will be returned. """ + raise NotImplementedError super(FakeName, self).__init__(name_str, start_pos) self.parent = parent self._is_definition = is_definition @@ -186,15 +188,11 @@ class FakeName(tree.Name): return self._is_definition -class LazyName(FakeName): +class LazyName(AbstractNameDefinition): def __init__(self, name, parent_callback, is_definition=None): - super(LazyName, self).__init__(name, is_definition=is_definition) + # TODO remove is_definition + self.string_name = name self._parent_callback = parent_callback - @property - def parent(self): + def infer(self): return self._parent_callback() - - @parent.setter - def parent(self, value): - pass # Do nothing, super classes can try to set the parent. diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 412a8e4f..fbbe8066 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -777,7 +777,7 @@ def check_array_instances(evaluator, instance): ai = _ArrayInstance(evaluator, instance) from jedi.evaluate import param - return param.Arguments(evaluator, [AlreadyEvaluated([ai])]) + return param.Arguments(evaluator, instance, [AlreadyEvaluated([ai])]) class _ArrayInstance(IterableWrapper): diff --git a/jedi/evaluate/recursion.py b/jedi/evaluate/recursion.py index 9b95fad5..bc8fb3f5 100644 --- a/jedi/evaluate/recursion.py +++ b/jedi/evaluate/recursion.py @@ -130,6 +130,9 @@ class ExecutionRecursionDetector(object): self.recursion_level -= 1 def push_execution(self, execution): + self.execution_funcs.add(execution.base) + self.parent_execution_funcs.append(execution.base) + return True # Remove in_par_execution_funcs = execution.base in self.parent_execution_funcs in_execution_funcs = execution.base in self.execution_funcs self.recursion_level += 1 diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index b9e4ba38..3442ffff 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -56,38 +56,17 @@ from jedi.evaluate import param from jedi.evaluate import flow_analysis from jedi.evaluate import imports from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \ - GlobalNameFilter, DictFilter + GlobalNameFilter, DictFilter, ContextName +from jedi.evaluate.context import Context -class Context(object): - def __init__(self, evaluator, parent_context=None): - self._evaluator = evaluator - self.parent_context = parent_context - - def get_parent_flow_context(self): - return self.parent_context - - def get_root_context(self): - context = self - while True: - if context.parent_context is None: - return context - context = context.parent_context - - -class FlowContext(Context): - def get_parent_flow_context(self): - if 1: - return self.parent_context - - -class Executed(Context, tree.Base): +class Executed(Context): """ An instance is also an executable - because __init__ is called :param var_args: The param input array, consist of a parser node or a list. """ - def __init__(self, evaluator, base, var_args=()): - self._evaluator = evaluator + def __init__(self, evaluator, parent_context, base, var_args): + super(Executed, self).__init__(evaluator, parent_context=parent_context) self.base = base self.var_args = var_args @@ -97,25 +76,21 @@ class Executed(Context, tree.Base): def get_parent_until(self, *args, **kwargs): return tree.Base.get_parent_until(self, *args, **kwargs) - @common.safe_property - def parent(self): - return self.base.parent - class Instance(use_metaclass(CachedMetaClass, Executed)): """ This class is used to evaluate instances. """ - def __init__(self, evaluator, base, var_args, is_generated=False): - super(Instance, self).__init__(evaluator, base, var_args) + def __init__(self, evaluator, parent_context, base, var_args, is_generated=False): + super(Instance, self).__init__(evaluator, parent_context, base, var_args) self.decorates = None # Generated instances are classes that are just generated by self # (No var_args) used. self.is_generated = is_generated self._init_execution = None - if base.name.get_code() in ['list', 'set'] \ - and evaluator.BUILTINS == base.get_parent_until(): + if base.name.string_name in ['list', 'set'] \ + and evaluator.BUILTINS == parent_context.get_root_context(): # compare the module path with the builtin name. self.var_args = iterable.check_array_instances(evaluator, self) elif not is_generated: @@ -157,7 +132,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): func = self.get_subscope_by_name('__init__') except KeyError: return None - return FunctionExecution(self._evaluator, func, self.var_args) + return FunctionExecution(self._evaluator, self, func, self.var_args) def _get_func_self_name(self, func): """ @@ -280,8 +255,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): @property @underscore_memoization def name(self): - name = self.base.name - return helpers.FakeName(unicode(name), self, name.start_pos) + return ContextName(self, self.base.name) def __getattr__(self, name): if name not in ['start_pos', 'end_pos', 'get_imports', 'type', @@ -400,6 +374,7 @@ def get_instance_el(evaluator, instance, var, is_class_var=False): in quite a lot of cases, which includes Nodes like ``power``, that need to know where a self name comes from for example. """ + return var if isinstance(var, tree.Name): parent = get_instance_el(evaluator, instance, var.parent, is_class_var) return InstanceName(var, parent) @@ -465,8 +440,7 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)): @property @memoize_default() def name(self): - name = self.var.name - return helpers.FakeName(unicode(name), self, name.start_pos) + return ContextName(self.var.name, self) def __iter__(self): for el in self.var.__iter__(): @@ -519,7 +493,7 @@ class Wrapper(tree.Base): @underscore_memoization def name(self): name = self.base.name - return helpers.FakeName(unicode(name), self, name.start_pos) + return ContextName(self, name) class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): @@ -574,7 +548,7 @@ class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): return [compiled.create(self._evaluator, object)] def py__call__(self, params): - return set([Instance(self._evaluator, self, params)]) + return set([Instance(self._evaluator, self.parent_context, self, params)]) def py__class__(self): return compiled.create(self._evaluator, type) @@ -628,13 +602,13 @@ class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): return "" % (type(self).__name__, self.base) -class Function(use_metaclass(CachedMetaClass, Wrapper)): +class Function(use_metaclass(CachedMetaClass, Context, Wrapper)): """ Needed because of decorators. Decorators are evaluated here. """ - def __init__(self, evaluator, func, is_decorated=False): + def __init__(self, evaluator, parent_context, func, is_decorated=False): """ This should not be called directly """ - self._evaluator = evaluator + super(Function, self).__init__(evaluator, parent_context) self.base = self.base_func = func self.is_decorated = is_decorated # A property that is set by the decorator resolution. @@ -716,7 +690,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): if self.base.is_generator(): return set([iterable.Generator(self._evaluator, self, params)]) else: - return FunctionExecution(self._evaluator, self, params).get_return_types() + return FunctionExecution(self._evaluator, self.parent_context, self, params).get_return_types() @memoize_default() def py__annotations__(self): @@ -767,24 +741,24 @@ class FunctionExecution(Executed): """ type = 'funcdef' - def __init__(self, evaluator, base, *args, **kwargs): - super(FunctionExecution, self).__init__(evaluator, base, *args, **kwargs) + def __init__(self, evaluator, parent_context, base, var_args): + super(FunctionExecution, self).__init__(evaluator, parent_context, base, var_args) self._copy_dict = {} - self._original_function = funcdef = base.base_func + self._original_function = funcdef = base if isinstance(funcdef, mixed.MixedObject): # The extra information in mixed is not needed anymore. We can just # unpack it and give it the tree object. funcdef = funcdef.definition # Just overwrite the old version. We don't need it anymore. - funcdef = helpers.deep_ast_copy(funcdef, new_elements=self._copy_dict) - for child in funcdef.children: - if child.type not in ('operator', 'keyword'): + #funcdef = helpers.deep_ast_copy(funcdef, new_elements=self._copy_dict) + #for child in funcdef.children: + #if child.type not in ('operator', 'keyword'): # Not all nodes are properly copied by deep_ast_copy. - child.parent = self - self.children = funcdef.children - self.names_dict = funcdef.names_dict - self._copied_funcdef = funcdef + #child.parent = self + #self.children = funcdef.children + #self.names_dict = funcdef.names_dict + #self._copied_funcdef = funcdef @memoize_default(default=set()) @recursion.execution_recursion_decorator @@ -940,6 +914,7 @@ class GlobalName(helpers.FakeName): We need to mark global names somehow. Otherwise they are just normal names that are not definitions. """ + raise NotImplementedError super(GlobalName, self).__init__(name.value, name.parent, name.start_pos, is_definition=True) @@ -1010,7 +985,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)): @property @memoize_default() def name(self): - return helpers.FakeName(unicode(self.base.name), self, (1, 0)) + return ContextName(self, self.base.name) def _get_init_directory(self): """ @@ -1101,6 +1076,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)): if path is not None and path.endswith(os.path.sep + '__init__.py'): mods = pkgutil.iter_modules([os.path.dirname(path)]) for module_loader, name, is_pkg in mods: + raise NotImplementedError fake_n = helpers.FakeName(name) # It's obviously a relative import to the current module. imp = helpers.FakeImport(fake_n, self, level=1) diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index f0c4346d..92cff5dd 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -35,7 +35,7 @@ def execute(evaluator, obj, arguments): except AttributeError: pass else: - if obj.parent == evaluator.BUILTINS: + if obj.parent_context == evaluator.BUILTINS: module_name = 'builtins' elif isinstance(obj.parent, tree.Module): module_name = str(obj.parent.name) diff --git a/test/run.py b/test/run.py index f27b1371..b41bf757 100755 --- a/test/run.py +++ b/test/run.py @@ -189,16 +189,19 @@ class IntegrationTestCase(object): parser = Parser(load_grammar(), string, start_symbol='eval_input') parser.get_root_node().move(self.line_nr) element = parser.get_parsed_node() + module = script._get_module() + # TODO remove element.parent = jedi.api.completion.get_user_scope( - script._get_module(), + module, (self.line_nr, self.column) ) - results = evaluator.eval_element(element) + module_context = evaluator.wrap(module, parent_context=None) + results = evaluator.eval_element(module_context, element) if not results: raise Exception('Could not resolve %s on line %s' % (match.string, self.line_nr - 1)) - should_be |= set(Definition(evaluator, r) for r in results) + should_be |= set(Definition(evaluator, r.name) for r in results) debug.dbg('Finished getting types', color='YELLOW') # Because the objects have different ids, `repr`, then compare.