mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
The deprecation of Python2.6 and the insertion of environments made it quite difficult to merge.
414 lines
12 KiB
Python
414 lines
12 KiB
Python
""" Test all things related to the ``jedi.api_classes`` module.
|
|
"""
|
|
|
|
from textwrap import dedent
|
|
from inspect import cleandoc
|
|
|
|
import pytest
|
|
|
|
import jedi
|
|
from jedi import __doc__ as jedi_doc, names
|
|
from ..helpers import cwd_at
|
|
from ..helpers import TestCase
|
|
|
|
|
|
def test_is_keyword(Script):
|
|
#results = Script('import ', 1, 1, None).goto_definitions()
|
|
#assert len(results) == 1 and results[0].is_keyword is True
|
|
results = Script('str', 1, 1, None).goto_definitions()
|
|
assert len(results) == 1 and results[0].is_keyword is False
|
|
|
|
|
|
def test_basedefinition_type(Script, environment):
|
|
def make_definitions():
|
|
"""
|
|
Return a list of definitions for parametrized tests.
|
|
|
|
:rtype: [jedi.api_classes.BaseDefinition]
|
|
"""
|
|
source = dedent("""
|
|
import sys
|
|
|
|
class C:
|
|
pass
|
|
|
|
x = C()
|
|
|
|
def f():
|
|
pass
|
|
|
|
def g():
|
|
yield
|
|
|
|
h = lambda: None
|
|
""")
|
|
|
|
definitions = []
|
|
definitions += names(source, environment=environment)
|
|
|
|
source += dedent("""
|
|
variable = sys or C or x or f or g or g() or h""")
|
|
lines = source.splitlines()
|
|
script = Script(source, len(lines), len('variable'), None)
|
|
definitions += script.goto_definitions()
|
|
|
|
script2 = Script(source, 4, len('class C'), None)
|
|
definitions += script2.usages()
|
|
|
|
source_param = "def f(a): return a"
|
|
script_param = Script(source_param, 1, len(source_param), None)
|
|
definitions += script_param.goto_assignments()
|
|
|
|
return definitions
|
|
|
|
for definition in make_definitions():
|
|
assert definition.type in ('module', 'class', 'instance', 'function',
|
|
'generator', 'statement', 'import', 'param')
|
|
|
|
|
|
def test_basedefinition_type_import(Script):
|
|
def get_types(source, **kwargs):
|
|
return {t.type for t in Script(source, **kwargs).completions()}
|
|
|
|
# import one level
|
|
assert get_types('import t') == {'module'}
|
|
assert get_types('import ') == {'module'}
|
|
assert get_types('import datetime; datetime') == {'module'}
|
|
|
|
# from
|
|
assert get_types('from datetime import timedelta') == {'class'}
|
|
assert get_types('from datetime import timedelta; timedelta') == {'class'}
|
|
assert get_types('from json import tool') == {'module'}
|
|
assert get_types('from json import tool; tool') == {'module'}
|
|
|
|
# import two levels
|
|
assert get_types('import json.tool; json') == {'module'}
|
|
assert get_types('import json.tool; json.tool') == {'module'}
|
|
assert get_types('import json.tool; json.tool.main') == {'function'}
|
|
assert get_types('import json.tool') == {'module'}
|
|
assert get_types('import json.tool', column=9) == {'module'}
|
|
|
|
|
|
def test_function_call_signature_in_doc(Script):
|
|
defs = Script("""
|
|
def f(x, y=1, z='a'):
|
|
pass
|
|
f""").goto_definitions()
|
|
doc = defs[0].docstring()
|
|
assert "f(x, y=1, z='a')" in str(doc)
|
|
|
|
|
|
def test_class_call_signature(Script):
|
|
defs = Script("""
|
|
class Foo:
|
|
def __init__(self, x, y=1, z='a'):
|
|
pass
|
|
Foo""").goto_definitions()
|
|
doc = defs[0].docstring()
|
|
assert "Foo(self, x, y=1, z='a')" in str(doc)
|
|
|
|
|
|
def test_position_none_if_builtin(Script):
|
|
gotos = Script('import sys; sys.path').goto_assignments()
|
|
assert gotos[0].line is None
|
|
assert gotos[0].column is None
|
|
|
|
|
|
def test_completion_docstring(Script, jedi_path):
|
|
"""
|
|
Jedi should follow imports in certain conditions
|
|
"""
|
|
def docstr(src, result):
|
|
c = Script(src, sys_path=[jedi_path]).completions()[0]
|
|
assert c.docstring(raw=True, fast=False) == cleandoc(result)
|
|
|
|
c = Script('import jedi\njed', sys_path=[jedi_path]).completions()[0]
|
|
assert c.docstring(fast=False) == cleandoc(jedi_doc)
|
|
|
|
docstr('import jedi\njedi.Scr', cleandoc(jedi.Script.__doc__))
|
|
|
|
docstr('abcd=3;abcd', '')
|
|
docstr('"hello"\nabcd=3\nabcd', '')
|
|
docstr(dedent('''
|
|
def x():
|
|
"hello"
|
|
0
|
|
x'''),
|
|
'hello'
|
|
)
|
|
docstr(dedent('''
|
|
def x():
|
|
"hello";0
|
|
x'''),
|
|
'hello'
|
|
)
|
|
# Shouldn't work with a tuple.
|
|
docstr(dedent('''
|
|
def x():
|
|
"hello",0
|
|
x'''),
|
|
''
|
|
)
|
|
# Should also not work if we rename something.
|
|
docstr(dedent('''
|
|
def x():
|
|
"hello"
|
|
y = x
|
|
y'''),
|
|
''
|
|
)
|
|
|
|
|
|
def test_completion_params(Script):
|
|
c = Script('import string; string.capwords').completions()[0]
|
|
assert [p.name for p in c.params] == ['s', 'sep']
|
|
|
|
|
|
def test_signature_params(Script):
|
|
def check(defs):
|
|
params = defs[0].params
|
|
assert len(params) == 1
|
|
assert params[0].name == 'bar'
|
|
|
|
s = dedent('''
|
|
def foo(bar):
|
|
pass
|
|
foo''')
|
|
|
|
check(Script(s).goto_definitions())
|
|
|
|
check(Script(s).goto_assignments())
|
|
check(Script(s + '\nbar=foo\nbar').goto_assignments())
|
|
|
|
|
|
def test_param_endings(Script):
|
|
"""
|
|
Params should be represented without the comma and whitespace they have
|
|
around them.
|
|
"""
|
|
sig = Script('def x(a, b=5, c=""): pass\n x(').call_signatures()[0]
|
|
assert [p.description for p in sig.params] == ['param a', 'param b=5', 'param c=""']
|
|
|
|
|
|
class TestIsDefinition(TestCase):
|
|
@pytest.fixture(autouse=True)
|
|
def init(self, environment):
|
|
self.environment = environment
|
|
|
|
def _def(self, source, index=-1):
|
|
return names(
|
|
dedent(source),
|
|
references=True,
|
|
all_scopes=True,
|
|
environment=self.environment
|
|
)[index]
|
|
|
|
def _bool_is_definitions(self, source):
|
|
ns = names(dedent(source), references=True, all_scopes=True)
|
|
# Assure that names are definitely sorted.
|
|
ns = sorted(ns, key=lambda name: (name.line, name.column))
|
|
return [name.is_definition() for name in ns]
|
|
|
|
def test_name(self):
|
|
d = self._def('name')
|
|
assert d.name == 'name'
|
|
assert not d.is_definition()
|
|
|
|
def test_stmt(self):
|
|
src = 'a = f(x)'
|
|
d = self._def(src, 0)
|
|
assert d.name == 'a'
|
|
assert d.is_definition()
|
|
d = self._def(src, 1)
|
|
assert d.name == 'f'
|
|
assert not d.is_definition()
|
|
d = self._def(src)
|
|
assert d.name == 'x'
|
|
assert not d.is_definition()
|
|
|
|
def test_import(self):
|
|
assert self._bool_is_definitions('import x as a') == [False, True]
|
|
assert self._bool_is_definitions('from x import y') == [False, True]
|
|
assert self._bool_is_definitions('from x.z import y') == [False, False, True]
|
|
|
|
|
|
class TestParent(TestCase):
|
|
@pytest.fixture(autouse=True)
|
|
def init(self, Script):
|
|
self.Script = Script
|
|
|
|
def _parent(self, source, line=None, column=None):
|
|
def_, = self.Script(dedent(source), line, column).goto_assignments()
|
|
return def_.parent()
|
|
|
|
def test_parent(self):
|
|
parent = self._parent('foo=1\nfoo')
|
|
assert parent.type == 'module'
|
|
|
|
parent = self._parent('''
|
|
def spam():
|
|
if 1:
|
|
y=1
|
|
y''')
|
|
assert parent.name == 'spam'
|
|
assert parent.parent().type == 'module'
|
|
|
|
def test_on_function(self):
|
|
parent = self._parent('''\
|
|
def spam():
|
|
pass''', 1, len('def spam'))
|
|
assert parent.name == ''
|
|
assert parent.type == 'module'
|
|
|
|
|
|
def test_parent_on_completion(Script):
|
|
parent = Script(dedent('''\
|
|
class Foo():
|
|
def bar(): pass
|
|
Foo().bar''')).completions()[0].parent()
|
|
assert parent.name == 'Foo'
|
|
assert parent.type == 'class'
|
|
|
|
parent = Script('str.join').completions()[0].parent()
|
|
assert parent.name == 'str'
|
|
assert parent.type == 'class'
|
|
|
|
|
|
def test_type(Script):
|
|
for c in Script('a = [str()]; a[0].').completions():
|
|
if c.name == '__class__':
|
|
assert c.type == 'class'
|
|
else:
|
|
assert c.type in ('function', 'instance')
|
|
|
|
# Github issue #397, type should never raise an error.
|
|
for c in Script('import os; os.path.').completions():
|
|
assert c.type
|
|
|
|
|
|
def test_type_II(Script):
|
|
"""
|
|
GitHub Issue #833, `keyword`s are seen as `module`s
|
|
"""
|
|
for c in Script('f').completions():
|
|
if c.name == 'for':
|
|
assert c.type == 'keyword'
|
|
|
|
|
|
"""
|
|
This tests the BaseDefinition.goto_assignments function, not the jedi
|
|
function. They are not really different in functionality, but really
|
|
different as an implementation.
|
|
"""
|
|
|
|
|
|
def test_goto_assignment_repetition(environment):
|
|
defs = names('a = 1; a', references=True, definitions=False, environment=environment)
|
|
# Repeat on the same variable. Shouldn't change once we're on a
|
|
# definition.
|
|
for _ in range(3):
|
|
assert len(defs) == 1
|
|
ass = defs[0].goto_assignments()
|
|
assert ass[0].description == 'a = 1'
|
|
|
|
|
|
def test_goto_assignments_named_params(environment):
|
|
src = """\
|
|
def foo(a=1, bar=2):
|
|
pass
|
|
foo(bar=1)
|
|
"""
|
|
bar = names(dedent(src), references=True, environment=environment)[-1]
|
|
param = bar.goto_assignments()[0]
|
|
assert (param.line, param.column) == (1, 13)
|
|
assert param.type == 'param'
|
|
|
|
|
|
def test_class_call(environment):
|
|
src = 'from threading import Thread; Thread(group=1)'
|
|
n = names(src, references=True, environment=environment)[-1]
|
|
assert n.name == 'group'
|
|
param_def = n.goto_assignments()[0]
|
|
assert param_def.name == 'group'
|
|
assert param_def.type == 'param'
|
|
|
|
|
|
def test_parentheses(environment):
|
|
n = names('("").upper', references=True, environment=environment)[-1]
|
|
assert n.goto_assignments()[0].name == 'upper'
|
|
|
|
|
|
def test_import(environment):
|
|
nms = names('from json import load', references=True, environment=environment)
|
|
assert nms[0].name == 'json'
|
|
assert nms[0].type == 'module'
|
|
n = nms[0].goto_assignments()[0]
|
|
assert n.name == 'json'
|
|
assert n.type == 'module'
|
|
|
|
assert nms[1].name == 'load'
|
|
assert nms[1].type == 'function'
|
|
n = nms[1].goto_assignments()[0]
|
|
assert n.name == 'load'
|
|
assert n.type == 'function'
|
|
|
|
nms = names('import os; os.path', references=True, environment=environment)
|
|
assert nms[0].name == 'os'
|
|
assert nms[0].type == 'module'
|
|
n = nms[0].goto_assignments()[0]
|
|
assert n.name == 'os'
|
|
assert n.type == 'module'
|
|
|
|
n = nms[2].goto_assignments()[0]
|
|
assert n.name == 'path'
|
|
assert n.type == 'module'
|
|
|
|
nms = names('import os.path', references=True, environment=environment)
|
|
n = nms[0].goto_assignments()[0]
|
|
assert n.name == 'os'
|
|
assert n.type == 'module'
|
|
n = nms[1].goto_assignments()[0]
|
|
# This is very special, normally the name doesn't chance, but since
|
|
# os.path is a sys.modules hack, it does.
|
|
assert n.name in ('ntpath', 'posixpath', 'os2emxpath')
|
|
assert n.type == 'module'
|
|
|
|
|
|
def test_import_alias(environment):
|
|
nms = names('import json as foo', references=True, environment=environment)
|
|
assert nms[0].name == 'json'
|
|
assert nms[0].type == 'module'
|
|
assert nms[0]._name.tree_name.parent.type == 'dotted_as_name'
|
|
n = nms[0].goto_assignments()[0]
|
|
assert n.name == 'json'
|
|
assert n.type == 'module'
|
|
assert n._name._context.tree_node.type == 'file_input'
|
|
|
|
assert nms[1].name == 'foo'
|
|
assert nms[1].type == 'module'
|
|
assert nms[1]._name.tree_name.parent.type == 'dotted_as_name'
|
|
ass = nms[1].goto_assignments()
|
|
assert len(ass) == 1
|
|
assert ass[0].name == 'json'
|
|
assert ass[0].type == 'module'
|
|
assert ass[0]._name._context.tree_node.type == 'file_input'
|
|
|
|
|
|
def test_added_equals_to_params(Script):
|
|
def run(rest_source):
|
|
source = dedent("""
|
|
def foo(bar, baz):
|
|
pass
|
|
""")
|
|
results = Script(source + rest_source).completions()
|
|
assert len(results) == 1
|
|
return results[0]
|
|
|
|
assert run('foo(bar').name_with_symbols == 'bar='
|
|
assert run('foo(bar').complete == '='
|
|
assert run('foo(bar, baz').complete == '='
|
|
assert run(' bar').name_with_symbols == 'bar'
|
|
assert run(' bar').complete == ''
|
|
x = run('foo(bar=isins').name_with_symbols
|
|
assert x == 'isinstance'
|