1
0
forked from VimPlug/jedi

A lot of small improvements.

This commit is contained in:
Dave Halter
2016-10-22 17:40:42 +02:00
parent 4ccfbb4962
commit 2e6603cc2e
16 changed files with 174 additions and 149 deletions

View File

@@ -137,7 +137,7 @@ class Script(object):
parser = FastParser(self._grammar, self._source, self.path) parser = FastParser(self._grammar, self._source, self.path)
save_parser(self.path, parser, pickling=False) 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) imports.add_module(self._evaluator, unicode(module.name), module)
return parser.module return parser.module
@@ -188,7 +188,9 @@ class Script(object):
leaf = self._get_module().get_leaf_for_position(self._pos) leaf = self._get_module().get_leaf_for_position(self._pos)
if leaf is None: if leaf is None:
return [] 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] names = [s.name for s in definitions]
defs = [classes.Definition(self._evaluator, name) for name in names] defs = [classes.Definition(self._evaluator, name) for name in names]
@@ -299,9 +301,11 @@ class Script(object):
if call_signature_details is None: if call_signature_details is None:
return [] return []
context = self._evaluator.create_context(call_signature_details.bracket_leaf)
with common.scale_speed_settings(settings.scale_call_signatures): with common.scale_speed_settings(settings.scale_call_signatures):
definitions = helpers.cache_call_signatures( definitions = helpers.cache_call_signatures(
self._evaluator, self._evaluator,
context,
call_signature_details.bracket_leaf, call_signature_details.bracket_leaf,
self._code_lines, self._code_lines,
self._pos self._pos

View File

@@ -65,6 +65,7 @@ class BaseDefinition(object):
""" """
#self._definition = list(self._name.infer())[0] #self._definition = list(self._name.infer())[0]
#self.is_keyword = isinstance(self._definition, keywords.Keyword) #self.is_keyword = isinstance(self._definition, keywords.Keyword)
self._definition = None
# generate a path to the definition # generate a path to the definition
self._module = name.parent_context.get_root_context() self._module = name.parent_context.get_root_context()
@@ -83,7 +84,7 @@ class BaseDefinition(object):
:rtype: str or None :rtype: str or None
""" """
return unicode(self._name) return self._name.string_name
@property @property
def start_pos(self): def start_pos(self):
@@ -198,7 +199,7 @@ class BaseDefinition(object):
>>> print(d.module_name) # doctest: +ELLIPSIS >>> print(d.module_name) # doctest: +ELLIPSIS
json json
""" """
return str(self._module.name) return self._module.name.string_name
def in_builtin_module(self): def in_builtin_module(self):
"""Whether this is a builtin module.""" """Whether this is a builtin module."""
@@ -417,7 +418,7 @@ class Completion(BaseDefinition):
if 'trailer' in node_names and 'argument' not in node_names: if 'trailer' in node_names and 'argument' not in node_names:
append += '=' append += '='
name = str(self._name) name = self._name.string_name
if like_name: if like_name:
name = name[self._like_name_length:] name = name[self._like_name_length:]
return name + append return name + append
@@ -450,7 +451,8 @@ class Completion(BaseDefinition):
def description(self): def description(self):
"""Provide a description of the completion object.""" """Provide a description of the completion object."""
if self._definition is None: if self._definition is None:
return '' return self._name.string_name
t = self.type t = self.type
if t == 'statement' or t == 'import': if t == 'statement' or t == 'import':
desc = self._definition.get_code() desc = self._definition.get_code()
@@ -577,7 +579,7 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
d = typ + ' ' + d.name.get_code() d = typ + ' ' + d.name.get_code()
elif isinstance(d, iterable.Array): elif isinstance(d, iterable.Array):
d = 'class ' + d.type 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) d = 'class ' + unicode(d.name)
elif isinstance(d, (er.Function, tree.Function)): elif isinstance(d, (er.Function, tree.Function)):
d = 'def ' + unicode(d.name) d = 'def ' + unicode(d.name)
@@ -589,6 +591,7 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
if d.endswith(','): if d.endswith(','):
d = d[:-1] # Remove the comma. d = d[:-1] # Remove the comma.
else: # ExprStmt else: # ExprStmt
return self._name.string_name
try: try:
first_leaf = d.first_leaf() first_leaf = d.first_leaf()
except AttributeError: except AttributeError:

View File

@@ -33,7 +33,6 @@ def filter_names(evaluator, completion_names, stack, like_name):
and name.string_name.lower().startswith(like_name.lower()) \ and name.string_name.lower().startswith(like_name.lower()) \
or name.string_name.startswith(like_name): or name.string_name.startswith(like_name):
print(name, name.infer())
new = classes.Completion( new = classes.Completion(
evaluator, evaluator,
name, name,
@@ -71,7 +70,7 @@ def get_user_scope(module, position):
class Completion: class Completion:
def __init__(self, evaluator, module, code_lines, position, call_signatures_method): def __init__(self, evaluator, module, code_lines, position, call_signatures_method):
self._evaluator = evaluator self._evaluator = evaluator
self._module = evaluator.wrap(module) self._module = evaluator.wrap(module, parent_context=None)
self._code_lines = code_lines self._code_lines = code_lines
# The first step of completions is to get the name # The first step of completions is to get the name
@@ -175,7 +174,7 @@ class Completion:
scope = get_user_scope(self._module, self._position) scope = get_user_scope(self._module, self._position)
if not scope.is_scope(): # Might be a flow (if/while/etc). if not scope.is_scope(): # Might be a flow (if/while/etc).
scope = scope.get_parent_scope() scope = scope.get_parent_scope()
scope = self._evaluator.wrap(scope) scope = self._evaluator.create_context(scope)
debug.dbg('global completion scope: %s', scope) debug.dbg('global completion scope: %s', scope)
filters = get_global_filters( filters = get_global_filters(
self._evaluator, self._evaluator,
@@ -222,6 +221,7 @@ class Completion:
""" """
Autocomplete inherited methods when overriding in child class. Autocomplete inherited methods when overriding in child class.
""" """
return
leaf = self._module.get_leaf_for_position(self._position, include_prefixes=True) leaf = self._module.get_leaf_for_position(self._position, include_prefixes=True)
cls = leaf.get_parent_until(tree.Class) cls = leaf.get_parent_until(tree.Class)
if isinstance(cls, (tree.Class, tree.Function)): if isinstance(cls, (tree.Class, tree.Function)):

View File

@@ -192,7 +192,7 @@ def get_possible_completion_types(grammar, stack):
return keywords, grammar_labels return keywords, grammar_labels
def evaluate_goto_definition(evaluator, leaf): def evaluate_goto_definition(evaluator, context, leaf):
if leaf.type == 'name': if leaf.type == 'name':
# In case of a name we can just use goto_definition which does all the # In case of a name we can just use goto_definition which does all the
# magic itself. # magic itself.
@@ -207,7 +207,7 @@ def evaluate_goto_definition(evaluator, leaf):
if node is None: if node is None:
return [] return []
return evaluator.eval_element(node) return evaluator.eval_element(context, node)
CallSignatureDetails = namedtuple( CallSignatureDetails = namedtuple(
@@ -288,7 +288,7 @@ def get_call_signature_details(module, position):
@time_cache("call_signatures_validity") @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.""" """This function calculates the cache key."""
index = user_pos[0] - 1 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 (module_path, before_bracket, bracket_leaf.start_pos)
yield evaluate_goto_definition( yield evaluate_goto_definition(
evaluator, evaluator,
context,
bracket_leaf.get_previous_leaf() bracket_leaf.get_previous_leaf()
) )

View File

@@ -3,7 +3,7 @@ import keyword
from jedi._compatibility import is_py3, is_py35 from jedi._compatibility import is_py3, is_py35
from jedi import common from jedi import common
from jedi.evaluate.helpers import FakeName from jedi.evaluate.filters import AbstractNameDefinition
from jedi.parser.tree import Leaf from jedi.parser.tree import Leaf
try: try:
from pydoc_data import topics as pydoc_topics 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): class Keyword(object):
type = 'completion_keyword' type = 'completion_keyword'
def __init__(self, evaluator, name, pos): def __init__(self, evaluator, name, pos):
self.name = FakeName(name, self, pos) self.name = KeywordName(evaluator, name)
self.start_pos = pos self.start_pos = pos
self.parent = evaluator.BUILTINS self.parent = evaluator.BUILTINS

View File

@@ -109,7 +109,7 @@ class Evaluator(object):
self.recursion_detector = recursion.RecursionDetector(self) self.recursion_detector = recursion.RecursionDetector(self)
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(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, if isinstance(element, (er.Wrapper, er.InstanceElement,
er.ModuleContext, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None: er.ModuleContext, er.FunctionExecution, er.Instance, compiled.CompiledObject)) or element is None:
# TODO this is so ugly, please refactor. # TODO this is so ugly, please refactor.
@@ -118,7 +118,7 @@ class Evaluator(object):
if element.type == 'classdef': if element.type == 'classdef':
return er.ClassContext(self, element, parent_context) return er.ClassContext(self, element, parent_context)
elif element.type == 'funcdef': elif element.type == 'funcdef':
return er.Function(self, element) return er.Function(self, parent_context, element)
elif element.type == 'lambda': elif element.type == 'lambda':
return er.LambdaWrapper(self, element) return er.LambdaWrapper(self, element)
elif element.type == 'file_input': elif element.type == 'file_input':
@@ -126,7 +126,7 @@ class Evaluator(object):
else: else:
return element 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): is_goto=False):
""" """
This is the search function. The most important part to debug. 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 :param position: Position of the last statement -> tuple of line, column
:return: List of Names. Their parents are the types. :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) filters = f.get_filters(search_global)
if is_goto: if is_goto:
return f.filter_name(filters) return f.filter_name(filters)
@@ -341,8 +341,7 @@ class Evaluator(object):
if isinstance(atom, tree.Name): if isinstance(atom, tree.Name):
# This is the first global lookup. # This is the first global lookup.
stmt = atom.get_definition() stmt = atom.get_definition()
scope = stmt.get_parent_until(tree.IsScope, include_current=True) if isinstance(context, er.FunctionExecution):
if isinstance(scope, (tree.Function, er.FunctionExecution)):
# Adjust scope: If the name is not in the suite, it's a param # 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 # default or annotation and will be resolved as part of the
# parent scope. # parent scope.
@@ -355,7 +354,7 @@ class Evaluator(object):
# We only need to adjust the start_pos for statements, because # We only need to adjust the start_pos for statements, because
# there the name cannot be used. # there the name cannot be used.
stmt = atom 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): elif isinstance(atom, tree.Literal):
return set([compiled.create(self, atom.eval())]) return set([compiled.create(self, atom.eval())])
else: else:
@@ -421,7 +420,7 @@ class Evaluator(object):
if self.is_analysis: if self.is_analysis:
arguments.eval_all() arguments.eval_all()
if obj.isinstance(er.Function): if isinstance(obj, er.Function):
obj = obj.get_decorated_func() obj = obj.get_decorated_func()
debug.dbg('execute: %s %s', obj, arguments) debug.dbg('execute: %s %s', obj, arguments)
@@ -532,7 +531,15 @@ class Evaluator(object):
search_global=True, is_goto=True) search_global=True, is_goto=True)
def create_context(self, node): def create_context(self, node):
scope = node.get_parent_scope() def from_scope(scope):
if scope.get_parent_scope() is not None: parent_context = None
raise NotImplementedError parent_scope = scope.get_parent_scope()
return self.wrap(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)

View File

@@ -10,9 +10,10 @@ from functools import partial
from jedi._compatibility import builtins as _builtins, unicode from jedi._compatibility import builtins as _builtins, unicode
from jedi import debug from jedi import debug
from jedi.cache import underscore_memoization, memoize_method 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.helpers import FakeName
from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition from jedi.evaluate.filters import AbstractFilter, AbstractNameDefinition
from jedi.evaluate.context import Context
from . import fake from . import fake
@@ -36,22 +37,27 @@ class CheckAttribute(object):
return partial(self.func, instance) return partial(self.func, instance)
class CompiledObject(Base): class CompiledObject(Context):
# comply with the parser # comply with the parser
start_pos = 0, 0 start_pos = 0, 0
path = None # modules have this attribute - set it to None. path = None # modules have this attribute - set it to None.
used_names = {} # To be consistent with modules. 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._evaluator = evaluator
self.obj = obj 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 @CheckAttribute
def py__call__(self, params): def py__call__(self, params):
if inspect.isclass(self.obj): if inspect.isclass(self.obj):
from jedi.evaluate.representation import Instance 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: else:
return set(self._execute_function(params)) return set(self._execute_function(params))
@@ -206,7 +212,7 @@ class CompiledObject(Base):
name = self._get_class().__name__ name = self._get_class().__name__
except AttributeError: except AttributeError:
name = repr(self.obj) name = repr(self.obj)
return FakeName(name, self) return CompiledContextName(self, name)
def _execute_function(self, params): def _execute_function(self, params):
if self.type != 'funcdef': if self.type != 'funcdef':
@@ -267,15 +273,21 @@ class CompiledName(AbstractNameDefinition):
name = None name = None
return '<%s: (%s).%s>' % (type(self).__name__, name, self.string_name) return '<%s: (%s).%s>' % (type(self).__name__, name, self.string_name)
def is_definition(self):
return True
@underscore_memoization @underscore_memoization
def infer(self): 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)] 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): class LazyNamesDict(object):
""" """
A names_dict instance for compiled objects, resembles the parser.tree. A names_dict instance for compiled objects, resembles the parser.tree.

View File

@@ -11,7 +11,6 @@ import types
from jedi._compatibility import is_py3, builtins, unicode, is_py34 from jedi._compatibility import is_py3, builtins, unicode, is_py34
from jedi.parser import ParserWithRecovery, load_grammar from jedi.parser import ParserWithRecovery, load_grammar
from jedi.parser import tree as pt from jedi.parser import tree as pt
from jedi.evaluate.helpers import FakeName
modules = {} modules = {}
@@ -69,14 +68,14 @@ def _load_faked_module(module):
if module_name == 'builtins' and not is_py3: if module_name == 'builtins' and not is_py3:
# There are two implementations of `open` for either python 2/3. # There are two implementations of `open` for either python 2/3.
# -> Rename the python2 version (`look at fake/builtins.pym`). # -> Rename the python2 version (`look at fake/builtins.pym`).
open_func = search_scope(module, 'open') open_func = _search_scope(module, 'open')
open_func.children[1] = FakeName('open_python3') open_func.children[1].value = 'open_python3'
open_func = search_scope(module, 'open_python2') open_func = _search_scope(module, 'open_python2')
open_func.children[1] = FakeName('open') open_func.children[1].value = 'open'
return module return module
def search_scope(scope, obj_name): def _search_scope(scope, obj_name):
for s in scope.subscopes: for s in scope.subscopes:
if str(s.name) == obj_name: if str(s.name) == obj_name:
return s return s
@@ -120,7 +119,7 @@ def _faked(module, obj, name):
# for methods. # for methods.
if name is None: if name is None:
if inspect.isbuiltin(obj): 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): elif not inspect.isclass(obj):
# object is a method or descriptor # object is a method or descriptor
try: try:
@@ -128,22 +127,22 @@ def _faked(module, obj, name):
except AttributeError: except AttributeError:
return None, None return None, None
else: else:
cls = search_scope(faked_mod, objclass.__name__) cls = _search_scope(faked_mod, objclass.__name__)
if cls is None: if cls is None:
return None, None return None, None
return search_scope(cls, obj.__name__), faked_mod return _search_scope(cls, obj.__name__), faked_mod
else: else:
if obj == module: if obj == module:
return search_scope(faked_mod, name), faked_mod return _search_scope(faked_mod, name), faked_mod
else: else:
try: try:
cls_name = obj.__name__ cls_name = obj.__name__
except AttributeError: except AttributeError:
return None, None return None, None
cls = search_scope(faked_mod, cls_name) cls = _search_scope(faked_mod, cls_name)
if cls is None: if cls is None:
return None, None return None, None
return search_scope(cls, name), faked_mod return _search_scope(cls, name), faked_mod
return None, None return None, None

View File

@@ -12,35 +12,48 @@ from jedi.common import to_list
class AbstractNameDefinition(object): class AbstractNameDefinition(object):
start_pos = None start_pos = None
string_name = None string_name = None
parent_context = None
@abstractmethod @abstractmethod
def infer(self): def infer(self):
raise NotImplementedError 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): def __init__(self, parent_context, name):
self.parent_context = parent_context self.parent_context = parent_context
self._name = name self.name = name
def get_parent_flow_context(self):
return self.parent_context
@property @property
def string_name(self): def string_name(self):
return self._name.value return self.name.value
@property @property
def start_pos(self): 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): 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.finder import _name_to_types
return _name_to_types(self.parent_context._evaluator, self.parent_context, self._name, None) 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)
class AbstractFilter(object): class AbstractFilter(object):
@@ -102,8 +115,8 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
def _check_flows(self, names): def _check_flows(self, names):
for name in sorted(names, key=lambda name: name.start_pos, reverse=True): for name in sorted(names, key=lambda name: name.start_pos, reverse=True):
stmt = name.get_definition() stmt = name.get_definition()
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
check = flow_analysis.UNSURE check = flow_analysis.UNSURE
#name_scope = self._evaluator.wrap(stmt.get_parent_scope())
#check = flow_analysis.break_check(self._evaluator, name_scope, #check = flow_analysis.break_check(self._evaluator, name_scope,
# stmt, self._origin_scope) # stmt, self._origin_scope)
if check is not flow_analysis.UNREACHABLE: if check is not flow_analysis.UNREACHABLE:
@@ -181,8 +194,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope):
until_position = None until_position = None
in_func = True in_func = True
node = context.parent_context context = context.parent_context
context = evaluator.wrap(node)
# Add builtins to the global scope. # Add builtins to the global scope.
for filter in evaluator.BUILTINS.get_filters(search_global=True): for filter in evaluator.BUILTINS.get_filters(search_global=True):

View File

@@ -95,10 +95,10 @@ def filter_definition_names(names, origin, position=None):
class NameFinder(object): class NameFinder(object):
def __init__(self, evaluator, scope, name_str, position=None): def __init__(self, evaluator, context, name_str, position=None):
self._evaluator = evaluator self._evaluator = evaluator
# Make sure that it's not just a syntax tree node. # 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.name_str = name_str
self.position = position self.position = position
self._found_predefined_if_name = None self._found_predefined_if_name = None
@@ -123,7 +123,7 @@ class NameFinder(object):
if not isinstance(self.name_str, (str, unicode)): # TODO Remove? if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
if attribute_lookup: if attribute_lookup:
analysis.add_attribute_error(self._evaluator, analysis.add_attribute_error(self._evaluator,
self.scope, self.name_str) self.context, self.name_str)
else: else:
message = ("NameError: name '%s' is not defined." message = ("NameError: name '%s' is not defined."
% self.name_str) % self.name_str)
@@ -140,9 +140,9 @@ class NameFinder(object):
origin_scope = None origin_scope = None
if search_global: 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: 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 names_dict_lookup(self, names_dict, position):
def get_param(scope, el): def get_param(scope, el):
@@ -167,7 +167,7 @@ class NameFinder(object):
stmt = name.get_definition() stmt = name.get_definition()
name_scope = self._evaluator.wrap(stmt.get_parent_scope()) 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 # Instances should not be checked for positioning, because we
# don't know in which order the functions are called. # don't know in which order the functions are called.
last_names.append(name) last_names.append(name)
@@ -210,7 +210,7 @@ class NameFinder(object):
# deliver types. # deliver types.
self._found_predefined_if_name = types self._found_predefined_if_name = types
else: else:
check = flow_analysis.break_check(self._evaluator, self.scope, check = flow_analysis.break_check(self._evaluator, self.context,
origin_scope) origin_scope)
if check is flow_analysis.UNREACHABLE: if check is flow_analysis.UNREACHABLE:
self._found_predefined_if_name = set() self._found_predefined_if_name = set()
@@ -249,7 +249,7 @@ class NameFinder(object):
if names: if names:
break break
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str, 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)) return list(self._clean_names(names))
def _clean_names(self, names): def _clean_names(self, names):
@@ -311,13 +311,13 @@ class NameFinder(object):
for name in names: for name in names:
new_types = name.infer() 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)) types |= set(self._resolve_descriptors(name, new_types))
else: else:
types |= set(new_types) 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__ # handling __getattr__ / __getattribute__
return self._check_getattr(self.scope) return self._check_getattr(self.context)
return types return types
@@ -336,7 +336,7 @@ class NameFinder(object):
except AttributeError: except AttributeError:
result.add(r) result.add(r)
else: else:
result |= desc_return(self.scope) result |= desc_return(self.context)
return result 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]) for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3])
types = check_tuple_assignments(evaluator, for_types, name) types = check_tuple_assignments(evaluator, for_types, name)
elif isinstance(typ, tree.Param): elif isinstance(typ, tree.Param):
types = _eval_param(evaluator, typ, scope) types = _eval_param(evaluator, context, typ, scope)
elif typ.isinstance(tree.ExprStmt): elif typ.isinstance(tree.ExprStmt):
types = _remove_statements(evaluator, context, typ, name) types = _remove_statements(evaluator, context, typ, name)
elif typ.isinstance(tree.WithStmt): elif typ.isinstance(tree.WithStmt):
@@ -373,7 +373,7 @@ def _name_to_types(evaluator, context, name, scope):
elif isinstance(typ, tree.Import): elif isinstance(typ, tree.Import):
types = imports.ImportWrapper(evaluator, name).follow() types = imports.ImportWrapper(evaluator, name).follow()
elif typ.isinstance(tree.Function, tree.Class): elif typ.isinstance(tree.Function, tree.Class):
types = [evaluator.wrap(typ)] types = [evaluator.wrap(typ, parent_context=context)]
elif typ.type == 'global_stmt': elif typ.type == 'global_stmt':
for s in _get_global_stmt_scopes(evaluator, typ, name): for s in _get_global_stmt_scopes(evaluator, typ, name):
finder = NameFinder(evaluator, s, str(name)) finder = NameFinder(evaluator, s, str(name))
@@ -427,7 +427,7 @@ def _remove_statements(evaluator, context, stmt, name):
return types return types
def _eval_param(evaluator, param, scope): def _eval_param(evaluator, context, param, scope):
res_new = set() res_new = set()
func = param.get_parent_scope() func = param.get_parent_scope()
@@ -441,8 +441,9 @@ def _eval_param(evaluator, param, scope):
if isinstance(scope, er.InstanceElement): if isinstance(scope, er.InstanceElement):
res_new.add(scope.instance) res_new.add(scope.instance)
else: else:
inst = er.Instance(evaluator, evaluator.wrap(cls), inst = er.Instance(evaluator, context.parent_context.parent_context, context.parent_context,
Arguments(evaluator, ()), is_generated=True) Arguments(evaluator, context, ()),
is_generated=True)
res_new.add(inst) res_new.add(inst)
return res_new return res_new

View File

@@ -1,6 +1,7 @@
import copy import copy
from itertools import chain from itertools import chain
from jedi.evaluate.filters import AbstractNameDefinition
from jedi.parser import tree 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 In case is_definition is defined (not None), that bool value will be
returned. returned.
""" """
raise NotImplementedError
super(FakeName, self).__init__(name_str, start_pos) super(FakeName, self).__init__(name_str, start_pos)
self.parent = parent self.parent = parent
self._is_definition = is_definition self._is_definition = is_definition
@@ -186,15 +188,11 @@ class FakeName(tree.Name):
return self._is_definition return self._is_definition
class LazyName(FakeName): class LazyName(AbstractNameDefinition):
def __init__(self, name, parent_callback, is_definition=None): 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 self._parent_callback = parent_callback
@property def infer(self):
def parent(self):
return self._parent_callback() return self._parent_callback()
@parent.setter
def parent(self, value):
pass # Do nothing, super classes can try to set the parent.

View File

@@ -777,7 +777,7 @@ def check_array_instances(evaluator, instance):
ai = _ArrayInstance(evaluator, instance) ai = _ArrayInstance(evaluator, instance)
from jedi.evaluate import param from jedi.evaluate import param
return param.Arguments(evaluator, [AlreadyEvaluated([ai])]) return param.Arguments(evaluator, instance, [AlreadyEvaluated([ai])])
class _ArrayInstance(IterableWrapper): class _ArrayInstance(IterableWrapper):

View File

@@ -130,6 +130,9 @@ class ExecutionRecursionDetector(object):
self.recursion_level -= 1 self.recursion_level -= 1
def push_execution(self, execution): 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_par_execution_funcs = execution.base in self.parent_execution_funcs
in_execution_funcs = execution.base in self.execution_funcs in_execution_funcs = execution.base in self.execution_funcs
self.recursion_level += 1 self.recursion_level += 1

View File

@@ -56,38 +56,17 @@ 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.filters import ParserTreeFilter, FunctionExecutionFilter, \ from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
GlobalNameFilter, DictFilter GlobalNameFilter, DictFilter, ContextName
from jedi.evaluate.context import Context
class Context(object): class Executed(Context):
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):
""" """
An instance is also an executable - because __init__ is called 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. :param var_args: The param input array, consist of a parser node or a list.
""" """
def __init__(self, evaluator, base, var_args=()): def __init__(self, evaluator, parent_context, base, var_args):
self._evaluator = evaluator super(Executed, self).__init__(evaluator, parent_context=parent_context)
self.base = base self.base = base
self.var_args = var_args self.var_args = var_args
@@ -97,25 +76,21 @@ class Executed(Context, tree.Base):
def get_parent_until(self, *args, **kwargs): def get_parent_until(self, *args, **kwargs):
return tree.Base.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)): class Instance(use_metaclass(CachedMetaClass, Executed)):
""" """
This class is used to evaluate instances. This class is used to evaluate instances.
""" """
def __init__(self, evaluator, base, var_args, is_generated=False): def __init__(self, evaluator, parent_context, base, var_args, is_generated=False):
super(Instance, self).__init__(evaluator, base, var_args) super(Instance, self).__init__(evaluator, parent_context, base, var_args)
self.decorates = None self.decorates = None
# Generated instances are classes that are just generated by self # Generated instances are classes that are just generated by self
# (No var_args) used. # (No var_args) used.
self.is_generated = is_generated self.is_generated = is_generated
self._init_execution = None self._init_execution = None
if base.name.get_code() in ['list', 'set'] \ if base.name.string_name in ['list', 'set'] \
and evaluator.BUILTINS == base.get_parent_until(): and evaluator.BUILTINS == parent_context.get_root_context():
# compare the module path with the builtin name. # compare the module path with the builtin name.
self.var_args = iterable.check_array_instances(evaluator, self) self.var_args = iterable.check_array_instances(evaluator, self)
elif not is_generated: elif not is_generated:
@@ -157,7 +132,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
func = self.get_subscope_by_name('__init__') func = self.get_subscope_by_name('__init__')
except KeyError: except KeyError:
return None 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): def _get_func_self_name(self, func):
""" """
@@ -280,8 +255,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
@property @property
@underscore_memoization @underscore_memoization
def name(self): def name(self):
name = self.base.name return ContextName(self, self.base.name)
return helpers.FakeName(unicode(name), self, name.start_pos)
def __getattr__(self, name): def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'get_imports', 'type', 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 in quite a lot of cases, which includes Nodes like ``power``, that need to
know where a self name comes from for example. know where a self name comes from for example.
""" """
return var
if isinstance(var, tree.Name): if isinstance(var, tree.Name):
parent = get_instance_el(evaluator, instance, var.parent, is_class_var) parent = get_instance_el(evaluator, instance, var.parent, is_class_var)
return InstanceName(var, parent) return InstanceName(var, parent)
@@ -465,8 +440,7 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)):
@property @property
@memoize_default() @memoize_default()
def name(self): def name(self):
name = self.var.name return ContextName(self.var.name, self)
return helpers.FakeName(unicode(name), self, name.start_pos)
def __iter__(self): def __iter__(self):
for el in self.var.__iter__(): for el in self.var.__iter__():
@@ -519,7 +493,7 @@ class Wrapper(tree.Base):
@underscore_memoization @underscore_memoization
def name(self): def name(self):
name = self.base.name name = self.base.name
return helpers.FakeName(unicode(name), self, name.start_pos) return ContextName(self, name)
class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)): class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)):
@@ -574,7 +548,7 @@ class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)):
return [compiled.create(self._evaluator, object)] return [compiled.create(self._evaluator, object)]
def py__call__(self, params): 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): def py__class__(self):
return compiled.create(self._evaluator, type) return compiled.create(self._evaluator, type)
@@ -628,13 +602,13 @@ class ClassContext(use_metaclass(CachedMetaClass, Context, Wrapper)):
return "<e%s of %s>" % (type(self).__name__, self.base) return "<e%s of %s>" % (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. 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 """ """ This should not be called directly """
self._evaluator = evaluator super(Function, self).__init__(evaluator, parent_context)
self.base = self.base_func = func self.base = self.base_func = func
self.is_decorated = is_decorated self.is_decorated = is_decorated
# A property that is set by the decorator resolution. # A property that is set by the decorator resolution.
@@ -716,7 +690,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
if self.base.is_generator(): if self.base.is_generator():
return set([iterable.Generator(self._evaluator, self, params)]) return set([iterable.Generator(self._evaluator, self, params)])
else: else:
return FunctionExecution(self._evaluator, self, params).get_return_types() return FunctionExecution(self._evaluator, self.parent_context, self, params).get_return_types()
@memoize_default() @memoize_default()
def py__annotations__(self): def py__annotations__(self):
@@ -767,24 +741,24 @@ class FunctionExecution(Executed):
""" """
type = 'funcdef' type = 'funcdef'
def __init__(self, evaluator, base, *args, **kwargs): def __init__(self, evaluator, parent_context, base, var_args):
super(FunctionExecution, self).__init__(evaluator, base, *args, **kwargs) super(FunctionExecution, self).__init__(evaluator, parent_context, base, var_args)
self._copy_dict = {} self._copy_dict = {}
self._original_function = funcdef = base.base_func self._original_function = funcdef = base
if isinstance(funcdef, mixed.MixedObject): if isinstance(funcdef, mixed.MixedObject):
# The extra information in mixed is not needed anymore. We can just # The extra information in mixed is not needed anymore. We can just
# unpack it and give it the tree object. # unpack it and give it the tree object.
funcdef = funcdef.definition funcdef = funcdef.definition
# Just overwrite the old version. We don't need it anymore. # Just overwrite the old version. We don't need it anymore.
funcdef = helpers.deep_ast_copy(funcdef, new_elements=self._copy_dict) #funcdef = helpers.deep_ast_copy(funcdef, new_elements=self._copy_dict)
for child in funcdef.children: #for child in funcdef.children:
if child.type not in ('operator', 'keyword'): #if child.type not in ('operator', 'keyword'):
# Not all nodes are properly copied by deep_ast_copy. # Not all nodes are properly copied by deep_ast_copy.
child.parent = self #child.parent = self
self.children = funcdef.children #self.children = funcdef.children
self.names_dict = funcdef.names_dict #self.names_dict = funcdef.names_dict
self._copied_funcdef = funcdef #self._copied_funcdef = funcdef
@memoize_default(default=set()) @memoize_default(default=set())
@recursion.execution_recursion_decorator @recursion.execution_recursion_decorator
@@ -940,6 +914,7 @@ class GlobalName(helpers.FakeName):
We need to mark global names somehow. Otherwise they are just normal We need to mark global names somehow. Otherwise they are just normal
names that are not definitions. names that are not definitions.
""" """
raise NotImplementedError
super(GlobalName, self).__init__(name.value, name.parent, super(GlobalName, self).__init__(name.value, name.parent,
name.start_pos, is_definition=True) name.start_pos, is_definition=True)
@@ -1010,7 +985,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
@property @property
@memoize_default() @memoize_default()
def name(self): def name(self):
return helpers.FakeName(unicode(self.base.name), self, (1, 0)) return ContextName(self, self.base.name)
def _get_init_directory(self): 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'): if path is not None and path.endswith(os.path.sep + '__init__.py'):
mods = pkgutil.iter_modules([os.path.dirname(path)]) mods = pkgutil.iter_modules([os.path.dirname(path)])
for module_loader, name, is_pkg in mods: for module_loader, name, is_pkg in mods:
raise NotImplementedError
fake_n = helpers.FakeName(name) fake_n = helpers.FakeName(name)
# It's obviously a relative import to the current module. # It's obviously a relative import to the current module.
imp = helpers.FakeImport(fake_n, self, level=1) imp = helpers.FakeImport(fake_n, self, level=1)

View File

@@ -35,7 +35,7 @@ def execute(evaluator, obj, arguments):
except AttributeError: except AttributeError:
pass pass
else: else:
if obj.parent == evaluator.BUILTINS: if obj.parent_context == evaluator.BUILTINS:
module_name = 'builtins' module_name = 'builtins'
elif isinstance(obj.parent, tree.Module): elif isinstance(obj.parent, tree.Module):
module_name = str(obj.parent.name) module_name = str(obj.parent.name)

View File

@@ -189,16 +189,19 @@ class IntegrationTestCase(object):
parser = Parser(load_grammar(), string, start_symbol='eval_input') parser = Parser(load_grammar(), string, start_symbol='eval_input')
parser.get_root_node().move(self.line_nr) parser.get_root_node().move(self.line_nr)
element = parser.get_parsed_node() element = parser.get_parsed_node()
module = script._get_module()
# TODO remove
element.parent = jedi.api.completion.get_user_scope( element.parent = jedi.api.completion.get_user_scope(
script._get_module(), module,
(self.line_nr, self.column) (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: if not results:
raise Exception('Could not resolve %s on line %s' raise Exception('Could not resolve %s on line %s'
% (match.string, self.line_nr - 1)) % (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') debug.dbg('Finished getting types', color='YELLOW')
# Because the objects have different ids, `repr`, then compare. # Because the objects have different ids, `repr`, then compare.