Skip to content

Commit

Permalink
A lot more fixes - fix all evaluate integration tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhalter committed Dec 3, 2016
1 parent ee1f077 commit 6940900
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 53 deletions.
22 changes: 14 additions & 8 deletions jedi/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,9 @@ def goto_assignments(self, follow_imports=False):
"""
def filter_follow_imports(names):
for name in names:
definition = name.get_definition()
if definition.type in ('import_name', 'import_from'):
imp = imports.ImportWrapper(context, name)
for name in filter_follow_imports(imp.follow(is_goto=True)):
yield name
if isinstance(name, (imports.ImportName, TreeNameDefinition)):
for context in name.infer():
yield context.name
else:
yield name

Expand Down Expand Up @@ -436,13 +434,21 @@ def names(source=None, path=None, encoding='utf-8', all_scopes=False,
``definitions=True``. E.g. ``a = b`` returns ``b``.
"""
def def_ref_filter(_def):
is_def = _def.is_definition()
is_def = _def._name.tree_name.is_definition()
return definitions and is_def or references and not is_def

# Set line/column to a random position, because they don't matter.
script = Script(source, line=1, column=0, path=path, encoding=encoding)
defs = [classes.Definition(script._evaluator, name_part)
for name_part in get_module_names(script._get_module().module_node, all_scopes)]
module_context = script._get_module()
defs = [
classes.Definition(
script._evaluator,
TreeNameDefinition(
module_context.create_context(name),
name
)
) for name in get_module_names(script._get_module_node(), all_scopes)
]
return sorted(filter(def_ref_filter, defs), key=lambda x: (x.line, x.column))


Expand Down
57 changes: 32 additions & 25 deletions jedi/api/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
from jedi.evaluate import iterable
from jedi.evaluate import imports
from jedi.evaluate import compiled
from jedi.evaluate.filters import ParamName
from jedi.evaluate.finder import filter_definition_names
from jedi.api.keywords import KeywordName


def defined_names(evaluator, scope):
Expand Down Expand Up @@ -63,7 +65,7 @@ def __init__(self, evaluator, name):
An instance of :class:`jedi.parser.reprsentation.Name` subclass.
"""
#self._definition = list(self._name.infer())[0]
#self.is_keyword = isinstance(self._definition, keywords.Keyword)
self.is_keyword = isinstance(self._name, KeywordName)
self._definition = None

# generate a path to the definition
Expand Down Expand Up @@ -232,9 +234,9 @@ def docstring(self, raw=False):
"""
if raw:
return _Help(self._definition).raw()
return _Help(self._name.parent_context).raw()
else:
return _Help(self._definition).full()
return _Help(self._name.parent_context).full()

@property
def doc(self):
Expand All @@ -259,7 +261,7 @@ def raw_doc(self):
@property
def description(self):
"""A textual description of the object."""
return unicode(self._name)
return unicode(self._name.string_name)

@property
def full_name(self):
Expand Down Expand Up @@ -298,14 +300,20 @@ def full_name(self):
return '.'.join(path if path[0] else path[1:])

def goto_assignments(self):
defs = self._evaluator.goto(self._name)
try:
tree_name = self._name.tree_name
except AttributeError:
return self

defs = self._evaluator.goto(self._name.parent_context, tree_name)
return [Definition(self._evaluator, d) for d in defs]

@memoize_method
def _follow_statements_imports(self):
"""
Follow both statements and imports, as far as possible.
"""
return self._name.infer()
if self._name.api_type == 'expr_stmt':
return self._evaluator.eval_statement(self._definition)
elif self._name.api_type == 'import':
Expand Down Expand Up @@ -351,9 +359,7 @@ def get_param_names(context):
return [_Param(self._evaluator, n) for n in get_param_names(context)]

def parent(self):
scope = self._definition.get_parent_scope()
scope = self._evaluator.wrap(scope)
return Definition(self._evaluator, scope.name)
return Definition(self._evaluator, self._name.parent_context.name)

def __repr__(self):
return "<%s %s>" % (type(self).__name__, self.description)
Expand All @@ -371,7 +377,7 @@ def get_line_code(self, before=0, after=0):
if self.in_builtin_module():
return ''

path = self._definition.get_parent_until().path
path = self._name.get_root_context().py__file__()
parser = load_parser(path)
lines = common.splitlines(parser.source)

Expand Down Expand Up @@ -401,7 +407,7 @@ def _complete(self, like_name):
and self.type == 'Function':
append = '('

if isinstance(self._definition, tree.Param) and self._stack is not None:
if isinstance(self._name, ParamName) and self._stack is not None:
node_names = list(self._stack.get_node_names(self._evaluator.grammar))
if 'trailer' in node_names and 'argument' not in node_names:
append += '='
Expand Down Expand Up @@ -438,9 +444,10 @@ def name_with_symbols(self):
@property
def description(self):
"""Provide a description of the completion object."""
if self._definition is None:
return self._name.string_name
# TODO improve the class structure.
return Definition.description.__get__(self)

# TODO remove
t = self.type
if t == 'statement' or t == 'import':
desc = self._definition.get_code()
Expand All @@ -461,20 +468,20 @@ def docstring(self, raw=False, fast=True):
the ``foo.docstring(fast=False)`` on every object, because it
parses all libraries starting with ``a``.
"""
definition = self._definition
if isinstance(definition, tree.Import):
raise DeprecationWarning
i = imports.ImportWrapper(self._evaluator, self._name)
if len(i.import_path) > 1 or not fast:
followed = self._follow_statements_imports()
context = self._name.parent_context
if isinstance(self._name, imports.ImportName):
if fast:
return ''
else:
followed = self._name.infer()
if followed:
# TODO: Use all of the followed objects as input to Documentation.
definition = list(followed)[0]
context = next(iter(followed))

if raw:
return _Help(definition).raw()
return _Help(context).raw()
else:
return _Help(definition).full()
return _Help(context).full()

@property
def type(self):
Expand Down Expand Up @@ -772,7 +779,7 @@ def __init__(self, definition):

def full(self):
try:
return self._name.doc
return self._name.get_node().doc
except AttributeError:
return self.raw()

Expand All @@ -782,7 +789,7 @@ def raw(self):
See :attr:`doc` for example.
"""
try:
return self._name.raw_doc
except AttributeError:
node = self._name.get_node()
if node is None:
return ''
return node.raw_doc
22 changes: 22 additions & 0 deletions jedi/evaluate/compiled/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ def get_params(self):
params.append(Param(parts, self))
return params

def get_param_names(self):
params_str, ret = self._parse_function_doc()
tokens = params_str.split(',')
if inspect.ismethoddescriptor(self.obj):
tokens.insert(0, 'self')
for p in tokens:
parts = p.strip().split('=')
if len(parts) > 1:
parts.insert(1, Operator('=', (0, 0)))
yield UnresolvableParamName(self, p[0])

def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, repr(self.obj))

Expand Down Expand Up @@ -278,6 +289,17 @@ def infer(self):
return [_create_from_name(self._evaluator, module, self.parent_context, self.string_name)]


class UnresolvableParamName(AbstractNameDefinition):
api_type = 'param'

def __init__(self, compiled_obj, name):
self.parent_context = compiled_obj.parent_context
self.string_name = name

def infer(self):
return set()


class CompiledContextName(AbstractNameDefinition):
def __init__(self, parent_context, name):
self.string_name = name
Expand Down
2 changes: 1 addition & 1 deletion jedi/evaluate/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ def completion_names(self, evaluator, only_modules=False):
if self.str_import_path == ('flask', 'ext'):
# List Flask extensions like ``flask_foo``
for mod in self._get_module_names():
modname = str(mod)
modname = mod.string_name
if modname.startswith('flask_'):
extname = modname[len('flask_'):]
names.append(self._generate_name(extname))
Expand Down
8 changes: 6 additions & 2 deletions jedi/evaluate/representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,10 +564,14 @@ def _py__path__(self):
# modules on sys_path or whatever the search_path is.
paths = set()
for s in search_path:
other = os.path.join(s, unicode(self.name))
other = os.path.join(s, self.name.string_name)
if os.path.isdir(other):
paths.add(other)
return list(paths)
if paths:
return list(paths)
# TODO I'm not sure if this is how nested namespace
# packages work. The tests are not really good enough to
# show that.
# Default to this.
return [self._get_init_directory()]

Expand Down
4 changes: 2 additions & 2 deletions test/test_api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def check_loaded(*modules):

api.preload_module('sys')
check_loaded() # compiled (c_builtin) modules shouldn't be in the cache.
api.preload_module('json', 'token')
check_loaded('json', 'token')
api.preload_module('types', 'token')
check_loaded('types', 'token')

utils.parser_cache = temp_cache

Expand Down
19 changes: 9 additions & 10 deletions test/test_evaluate/test_compiled.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from jedi._compatibility import builtins, is_py3
from jedi.parser import load_grammar
from jedi.parser.tree import Function
from jedi.evaluate import compiled, representation
from jedi.evaluate import compiled, instance
from jedi.evaluate.representation import FunctionContext
from jedi.evaluate import Evaluator
from jedi import Script

Expand All @@ -16,25 +16,24 @@ def test_simple():
e = _evaluator()
bltn = compiled.CompiledObject(e, builtins)
obj = compiled.CompiledObject(e, '_str_', bltn)
upper = e.find_types(obj, 'upper')
assert len(upper) == 1
objs = list(e.execute(list(upper)[0]))
upper, = obj.py__getattribute__('upper')
objs = list(upper.execute_evaluated())
assert len(objs) == 1
assert isinstance(objs[0], representation.Instance)
assert isinstance(objs[0], instance.CompiledInstance)


def test_fake_loading():
e = _evaluator()
assert isinstance(compiled.create(e, next), Function)
assert isinstance(compiled.create(e, next), FunctionContext)

builtin = compiled.get_special_object(e, 'BUILTINS')
string = builtin.get_subscope_by_name('str')
string, = builtin.py__getattribute__('str')
from_name = compiled._create_from_name(e, builtin, string, '__init__')
assert isinstance(from_name, Function)
assert isinstance(from_name, FunctionContext)


def test_fake_docstr():
assert compiled.create(_evaluator(), next).raw_doc == next.__doc__
assert compiled.create(_evaluator(), next).get_node().raw_doc == next.__doc__


def test_parse_function_doc_illegal_docstr():
Expand Down
2 changes: 1 addition & 1 deletion test/test_evaluate/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_call_of_leaf_in_brackets():
type(x)
""")
last_x = names(s, references=True, definitions=False)[-1]
name = last_x._name
name = last_x._name.tree_name

call = helpers.call_of_leaf(name)
assert call == name
2 changes: 1 addition & 1 deletion test/test_evaluate/test_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_not_importable_file():
def test_import_unique():
src = "import os; os.path"
defs = jedi.Script(src, path='example.py').goto_definitions()
defs = [d._definition for d in defs]
defs = [d._name.parent_context for d in defs]
assert len(defs) == len(set(defs))


Expand Down
6 changes: 3 additions & 3 deletions test/test_evaluate/test_namespace_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ def script_with_path(*args, **kwargs):
for c in script_with_path(source + '; x.').completions():
if c.name == 'foo':
completion = c
solution = "statement: foo = '%s'" % solution
solution = "foo = '%s'" % solution
assert completion.description == solution


def test_nested_namespace_package():
CODE = 'from nested_namespaces.namespace.pkg import CONST'
code = 'from nested_namespaces.namespace.pkg import CONST'

sys_path = [dirname(__file__)]

script = jedi.Script(sys_path=sys_path, source=CODE, line=1, column=45)
script = jedi.Script(sys_path=sys_path, source=code, line=1, column=45)

result = script.goto_definitions()

Expand Down

0 comments on commit 6940900

Please sign in to comment.