1
0
forked from VimPlug/jedi

Merge branch 'dev' into unicode_tokenize_fix2

Conflicts:
	AUTHORS.txt
This commit is contained in:
farhad
2015-03-06 12:14:38 +04:00
105 changed files with 7728 additions and 6520 deletions

View File

@@ -338,6 +338,11 @@ tuple((1,))[0]
#? []
list().__iterable
# With a list comprehension.
for i in set(a for a in [1]):
#? int()
i
# -----------------
# Recursions
@@ -352,3 +357,11 @@ def recursion1(foo):
#? int()
recursion1([1,2])[0]
# -----------------
# Merged Arrays
# -----------------
for x in [1] + ['']:
#? int() str()
x

View File

@@ -105,6 +105,13 @@ for i in b:
#? float() str()
a[0]
for i in [1,2,3]:
#? int()
i
else:
i
# -----------------
# range()
# -----------------
@@ -112,115 +119,6 @@ for i in range(10):
#? int()
i
# -----------------
# list comprehensions
# -----------------
# basics:
a = ['' for a in [1]]
#? str()
a[0]
a = [a for a in [1]]
#? int()
a[0]
a = [a for a in 1,2]
#? int()
a[0]
a = [a for a,b in [(1,'')]]
#? int()
a[0]
arr = [1,'']
a = [a for a in arr]
#? int() str()
a[0]
a = [a if 1.0 else '' for a in [1] if [1.0]]
#? int() str()
a[0]
# name resolve should be correct
left, right = 'a', 'b'
left, right = [x for x in (left, right)]
#? str()
left
# with a dict literal
#? str()
[a for a in {1:'x'}][0]
##? str()
{a-1:b for a,b in {1:'a', 3:1.0}.items()}[0]
# list comprehensions should also work in combination with functions
def listen(arg):
for x in arg:
#? str()
x
listen(['' for x in [1]])
#? str
([str for x in []])[0]
# -----------------
# nested list comprehensions
# -----------------
b = [a for arr in [[1]] for a in arr]
#? int()
b[0]
b = [a for arr in [[1]] if '' for a in arr if '']
#? int()
b[0]
b = [b for arr in [[[1.0]]] for a in arr for b in a]
#? float()
b[0]
# jedi issue #26
#? list()
a = [[int(v) for v in line.strip().split() if v] for line in ["123", "123", "123"] if line]
#? list()
a[0]
#? int()
a[0][0]
# -----------------
# generator comprehensions
# -----------------
left, right = (i for i in (1, ''))
#? int()
left
gen = (i for i in (1,))
#? int()
next(gen)
#?
gen[0]
gen = (a for arr in [[1.0]] for a in arr)
#? float()
next(gen)
#? int()
(i for i in (1,)).send()
# issues with different formats
left, right = (i for i in
('1', '2'))
#? str()
left
# -----------------
# ternary operator
# -----------------
@@ -327,9 +225,10 @@ except ImportError as i_a:
try:
import math
except ImportError, i_b:
#? ['i_b']
# TODO check this only in Python2
##? ['i_b']
i_b
#? ImportError()
##? ImportError()
i_b

View File

@@ -388,6 +388,8 @@ class PrivateVar():
self.__var = 1
#? int()
self.__var
#? ['__var']
self.__var
#? []
PrivateVar().__var
#?
@@ -448,9 +450,26 @@ class TestX(object):
# -----------------
class A(object):
pass
a = 3
#? ['mro']
A.mro
#? []
A().mro
# -----------------
# mro resolution
# -----------------
class B(A()):
b = 3
#?
B.a
#?
B().a
#? int()
B.b
#? int()
B().b

View File

@@ -0,0 +1,125 @@
# -----------------
# list comprehensions
# -----------------
# basics:
a = ['' for a in [1]]
#? str()
a[0]
#? ['insert']
a.insert
a = [a for a in [1]]
#? int()
a[0]
y = 1.0
# Should not leak.
[y for y in [3]]
#? float()
y
a = [a for a in (1, 2)]
#? int()
a[0]
a = [a for a,b in [(1,'')]]
#? int()
a[0]
arr = [1,'']
a = [a for a in arr]
#? int() str()
a[0]
a = [a if 1.0 else '' for a in [1] if [1.0]]
#? int() str()
a[0]
# name resolve should be correct
left, right = 'a', 'b'
left, right = [x for x in (left, right)]
#? str()
left
# with a dict literal
#? str()
[a for a in {1:'x'}][0]
##? str()
{a-1:b for a,b in {1:'a', 3:1.0}.items()}[0]
# list comprehensions should also work in combination with functions
def listen(arg):
for x in arg:
#? str()
x
listen(['' for x in [1]])
#? str
([str for x in []])[0]
# -----------------
# nested list comprehensions
# -----------------
b = [a for arr in [[1]] for a in arr]
#? int()
b[0]
b = [a for arr in [[1]] if '' for a in arr if '']
#? int()
b[0]
b = [b for arr in [[[1.0]]] for a in arr for b in a]
#? float()
b[0]
# jedi issue #26
#? list()
a = [[int(v) for v in line.strip().split() if v] for line in ["123", "123", "123"] if line]
#? list()
a[0]
#? int()
a[0][0]
# -----------------
# generator comprehensions
# -----------------
left, right = (i for i in (1, ''))
#? int()
left
gen = (i for i in (1,))
#? int()
next(gen)
#?
gen[0]
gen = (a for arr in [[1.0]] for a in arr)
#? float()
next(gen)
#? int()
(i for i in (1,)).send()
# issues with different formats
left, right = (i for i in
('1', '2'))
#? str()
left
# -----------------
# name resolution in comprehensions.
# -----------------
def x():
"""Should not try to resolve to the if hio, which was a bug."""
#? 22
[a for a in h if hio]
if hio: pass

View File

@@ -121,7 +121,7 @@ class SelfVars():
@Decorator
def __init__(self):
"""
init decorators should be ignored when looking up variables in the
__init__ decorators should be ignored when looking up variables in the
class.
"""
self.c = list

View File

@@ -3,7 +3,7 @@
# -----------------
# sphinx style
# -----------------
def f(a, b, c, d, x):
def sphinxy(a, b, c, d, x):
""" asdfasdf
:param a: blablabla
:type a: str
@@ -27,10 +27,10 @@ def f(a, b, c, d, x):
x.lower
#? dict()
f()
sphinxy()
# wrong declarations
def f(a, b, x):
def sphinxy2(a, b, x):
"""
:param a: Forgot type declaration
:type a:
@@ -47,7 +47,7 @@ def f(a, b, x):
x
#?
f()
sphinxy2()
# local classes -> github #370
class ProgramNode():
@@ -104,7 +104,7 @@ return_module_object().join
# -----------------
# epydoc style
# -----------------
def e(a, b):
def epydoc(a, b):
""" asdfasdf
@type a: str
@param a: blablabla
@@ -121,7 +121,7 @@ def e(a, b):
b[1]
#? list()
e()
epydoc()
# Returns with param type only

View File

@@ -1,108 +1,6 @@
"""
This is used for dynamic object completion.
Jedi tries to guess the types with a backtracking approach.
Checking for ``list.append`` and all the other possible array modifications.
"""
def func(a):
#? int() str()
return a
#? int()
func(1)
func
int(1) + (int(2))+ func('')
# Again the same function, but with another call.
def func(a):
#? float()
return a
func(1.0)
# Again the same function, but with no call.
def func(a):
#?
return a
def func(a):
#? float()
return a
str(func(1.0))
# -----------------
# *args, **args
# -----------------
def arg(*args):
#? tuple()
args
#? int()
args[0]
arg(1,"")
# -----------------
# decorators
# -----------------
def def_func(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
@def_func
def func(c):
#? str()
return c
#? str()
func("str")
@def_func
def func(c=1):
#? int() float()
return c
func(1.0)
# Needs to be here, because in this case func is an import -> shouldn't lead to
# exceptions.
import sys as func
func.sys
# -----------------
# classes
# -----------------
class A():
def __init__(self, a):
#? str()
a
A("s")
class A():
def __init__(self, a):
#? int()
a
self.a = a
def test(self, a):
#? float()
a
self.c = self.test2()
def test2(self):
#? int()
return self.a
def test3(self):
#? int()
self.test2()
#? int()
self.c
A(3).test(2.0)
A(3).test2()
# -----------------
# list.append
# -----------------
@@ -344,13 +242,20 @@ class C():
a[0]
return a
def class_arr(self, el):
def literal_arr(self, el):
self.a = []
self.a.append(el)
#? int()
self.a[0]
return self.a
def list_arr(self, el):
self.b = list([])
self.b.append(el)
#? float()
self.b[0]
return self.b
#? int()
C().blub(1)[0]
#? float()
@@ -359,7 +264,12 @@ C().blub2(1)[0]
#? int()
C().a[0]
#? int()
C().class_arr(1)[0]
C().literal_arr(1)[0]
#? float()
C().b[0]
#? float()
C().list_arr(1.0)[0]
# -----------------
# array recursions
@@ -394,14 +304,3 @@ def third():
return list(b)
#?
third()[0]
# -----------------
# list comprehensions
# -----------------
def from_comprehension(foo):
#? int() float()
return foo
[from_comprehension(1.0) for n in (1,)]
[from_comprehension(n) for n in (1,)]

View File

@@ -0,0 +1,134 @@
"""
This is used for dynamic object completion.
Jedi tries to guess param types with a backtracking approach.
"""
def func(a, default_arg=2):
#? int()
default_arg
#? int() str()
return a
#? int()
func(1)
func
int(1) + (int(2))+ func('')
# Again the same function, but with another call.
def func(a):
#? float()
return a
func(1.0)
# Again the same function, but with no call.
def func(a):
#?
return a
def func(a):
#? float()
return a
str(func(1.0))
# -----------------
# *args, **args
# -----------------
def arg(*args):
#? tuple()
args
#? int()
args[0]
arg(1,"")
# -----------------
# decorators
# -----------------
def def_func(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
@def_func
def func(c):
#? str()
return c
#? str()
func("str")
@def_func
def func(c=1):
#? int() float()
return c
func(1.0)
def tricky_decorator(func):
def wrapper(*args):
return func(1, *args)
return wrapper
@tricky_decorator
def func(a, b):
#? int()
a
#? float()
b
func(1.0)
# Needs to be here, because in this case func is an import -> shouldn't lead to
# exceptions.
import sys as func
func.sys
# -----------------
# classes
# -----------------
class A():
def __init__(self, a):
#? str()
a
A("s")
class A():
def __init__(self, a):
#? int()
a
self.a = a
def test(self, a):
#? float()
a
self.c = self.test2()
def test2(self):
#? int()
return self.a
def test3(self):
#? int()
self.test2()
#? int()
self.c
A(3).test(2.0)
A(3).test2()
# -----------------
# list comprehensions
# -----------------
def from_comprehension(foo):
#? int() float()
return foo
[from_comprehension(1.0) for n in (1,)]
[from_comprehension(n) for n in (1,)]

View File

@@ -266,6 +266,7 @@ class Something():
def x(self, a, b=1):
return a
#? int()
Something().x(1)
@@ -288,10 +289,15 @@ exe['b']
#? int() float()
exe['c']
a = 'a'
exe2 = kwargs_func(**{a:3,
b:4.0})
'b':4.0})
#? int()
exe2['a']
#? float()
exe2['b']
#? int() float()
exe2['c']
# -----------------
# *args / ** kwargs
@@ -352,20 +358,20 @@ def nested_kw(**kwargs1):
def nested_kw2(**kwargs2):
return nested_kw(**kwargs2)
#? int()
# invalid command, doesn't need to return anything
#?
nested_kw(b=1, c=1.0, list)
#? int()
nested_kw(b=1)
#? int()
# invalid command, doesn't need to return anything
#?
nested_kw(d=1.0, b=1, list)
#? int()
nested_kw(b=1)
#? int()
nested_kw(a=3.0, b=1)
#? int()
nested_kw(b=1, a=r"")
#? []
nested_kw('')
nested_kw(1, '')
#? []
nested_kw(a='')
@@ -391,10 +397,12 @@ def nested_both(*args, **kwargs):
def nested_both2(*args, **kwargs):
return nested_both(*args, **kwargs)
#? int()
# invalid commands, may return whatever.
#? list
nested_both('', b=1, c=1.0, list)
#? int()
#? list
nested_both('', c=1.0, b=1, list)
#? []
nested_both('')
@@ -432,23 +440,6 @@ nested_def2('', c=1.0, b=1)[1]
#? []
nested_def2('')[1]
# -----------------
# function annotations (should be ignored at the moment)
# -----------------
def annot(a:3, *args:3):
return a, args[0]
#? str()
annot('', 1.0)[0]
#? float()
annot('', 1.0)[1]
def annot_ret(a:3) -> 3:
return a
#? str()
annot_ret('')
# -----------------
# magic methods
# -----------------

View File

@@ -24,6 +24,13 @@ next(gen_ret(1))
#? []
next(gen_ret())
# generators evaluate to true if cast by bool.
a = ''
if gen_ret():
a = 3
#? int()
a
# -----------------
# generators should not be indexable

View File

@@ -93,7 +93,7 @@ ClassVar().x = ''
# Recurring use of the same var name, github #315
def f(t=None):
#! 9 ['t = None']
#! 9 ['t=None']
t = t or 1
# -----------------
@@ -181,13 +181,38 @@ ab1(ClassDef);ab2(ClassDef);ab3(ClassDef)
# -----------------
for i in range(1):
#! ['for i in range(1): i']
#! ['for i in range(1): i']
i
for key, value in [(1,2)]:
#! ['for key,value in [(1, 2)]: key']
#! ['for key, value in [(1,2)]: key']
key
for i in []:
#! ['for i in []: i']
#! ['for i in []: i']
i
# -----------------
# decorator
# -----------------
def dec(dec_param=3):
pass
#! 8 ['dec_param=3']
@dec(dec_param=5)
def y():
pass
class ClassDec():
def class_func(func):
return func
#! 14 ['def class_func']
@ClassDec.class_func
def x():
pass
#! 2 ['class ClassDec']
@ClassDec.class_func
def z():
pass

View File

@@ -1,2 +1,4 @@
a = 1
from import_tree.random import a as c
foobarbaz = 3.0

View File

@@ -69,16 +69,6 @@ def scope_nested2():
#? ['rename1']
import_tree.rename1
def from_names():
#? ['mod1']
from import_tree.pkg.
#? ['path']
from os.
def builtin_test():
#? ['math']
import math
def scope_from_import_variable():
"""
All of them shouldn't work, because "fake" imports don't work in python
@@ -97,13 +87,28 @@ def scope_from_import_variable():
def scope_from_import_variable_with_parenthesis():
from import_tree.mod2.fake import (
a, c
a, foobarbaz
)
#?
a
#?
c
foobarbaz
# shouldn't complete, should still list the name though.
#? ['foobarbaz']
foobarbaz
def as_imports():
from import_tree.mod1 import a as xyz
#? int()
xyz
import not_existant, import_tree.mod1 as foo
#? int()
foo.a
import import_tree.mod1 as bar
#? int()
bar.a
# -----------------
# std lib modules
@@ -121,9 +126,6 @@ import os
#? ['dirname']
os.path.dirname
#? os.path.join
from os.path import join
from os.path import (
expanduser
)
@@ -173,28 +175,6 @@ def func_with_import():
#? ['sleep']
func_with_import().sleep
# -----------------
# completions within imports
# -----------------
#? ['sqlite3']
import sqlite3
#? ['classes']
import classes
#? ['timedelta']
from datetime import timedel
# should not be possible, because names can only be looked up 1 level deep.
#? []
from datetime.timedelta import resolution
#? []
from datetime.timedelta import
#? ['Cursor']
from sqlite3 import Cursor
# -----------------
# relative imports
# -----------------
@@ -231,65 +211,10 @@ from . import datetime as mod1
#? []
mod1.
#? str()
imp_tree.a
#? ['some_variable']
from . import some_variable
#? ['arrays']
from . import arrays
#? []
from . import import_tree as ren
# -----------------
# special positions -> edge cases
# -----------------
import datetime
#? 6 datetime
from datetime.time import time
#? []
import datetime.
#? []
import datetime.date
#? 18 ['import']
from import_tree. import pkg
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'recurse_class1', 'recurse_class2']
from import_tree. import pkg
#? 18 ['pkg']
from import_tree.p import pkg
#? 17 ['import_tree']
from .import_tree import
#? 10 ['run']
from ..run import
#? ['run']
from .. import run
#? []
from not_a_module import
# self import
# this can cause recursions
from imports import *
#137
import json
#? 23 json.dump
from json import load, dump
#? 17 json.load
from json import load, dump
# without the from clause:
import json, datetime
#? 7 json
import json, datetime
#? 13 datetime
import json, datetime
# -----------------
# packages
# -----------------

View File

@@ -10,7 +10,7 @@ there should never be any errors.
##? 5
's'()
#? ['upper']
#? []
str()).upper
# -----------------
@@ -19,18 +19,25 @@ str()).upper
def asdf(a or b): # multiple param names
return a
#? int()
#?
asdf(2)
asdf = ''
from a import (b
def blub():
return 0
def openbrace():
def wrong_indents():
asdf = 3
asdf
asdf(
#? int()
asdf
def openbrace():
asdf = 3
asdf(
#? int()
asdf
return 1
#? int()
@@ -58,7 +65,7 @@ normalfunc()
# dots in param
def f(seq1...=None):
return seq1
#? int()
#?
f(1)
@
@@ -96,14 +103,17 @@ try:
#? str()
""
def break(): pass
# wrong ternary expression
a = ''
a = 1 if
#? int()
#? str()
a
# No completions for for loops without the right syntax
for for_local in :
for_local
#? ['for_local']
#? []
for_local
#?
for_local
@@ -122,7 +132,7 @@ a3 = [for xyz in]
a3[0]
a3 = [a4 for in 'b']
#? str()
#?
a3[0]
a3 = [a4 for a in for x in y]
@@ -137,10 +147,10 @@ a[0]
a = [a for a in [1,2]
def break(): pass
#? int()
#?
a[0]
#? ['real']
#? []
int()).real
# -----------------
@@ -165,14 +175,39 @@ import datetime as
call = ''
invalid = .call
#? str()
#?
invalid
invalid = call?.call
#? str()
#?
invalid
# comma
invalid = ,call
#? str()
#?
invalid
# -----------------
# classes
# -----------------
class BrokenPartsOfClass():
def foo(self):
# This construct contains two places where Jedi with Python 3 can fail.
# It should just ignore those constructs and still execute `bar`.
pass
if 2:
try:
pass
except ValueError, e:
raise TypeError, e
else:
pass
def bar(self):
self.x = 3
return ''
#? str()
BrokenPartsOfClass().bar()

View File

@@ -55,11 +55,26 @@ a = lambda: 3
a.__closure__
class C():
def __init__(self):
def __init__(self, foo=1.0):
self.a = lambda: 1
self.foo = foo
def ret(self):
return lambda: self.foo
def with_param(self):
return lambda x: x + self.a()
#? int()
C().a()
#? str()
C('foo').ret()()
index = C().with_param()(1)
#? float()
['', 1, 1.0][index]
def xy(param):
def ret(a, b):
@@ -80,3 +95,12 @@ class Test(object):
self.a
#? float()
pred(1.0, 2)
# -----------------
# test_nocond in grammar (happens in list comprehensions with `if`)
# -----------------
# Doesn't need to do anything yet. It should just not raise an error. These
# nocond lambdas make no sense at all.
#? int()
[a for a in [1,2] if lambda: 3][0]

View File

@@ -0,0 +1,104 @@
def from_names():
#? ['mod1']
from import_tree.pkg.
#? ['path']
from os.
def from_names_goto():
from import_tree import pkg
#? pkg
from import_tree.pkg
def builtin_test():
#? ['math']
import math
# -----------------
# completions within imports
# -----------------
#? ['sqlite3']
import sqlite3
#? ['classes']
import classes
#? ['timedelta']
from datetime import timedel
#? 21 []
from datetime.timedel import timedel
# should not be possible, because names can only be looked up 1 level deep.
#? []
from datetime.timedelta import resolution
#? []
from datetime.timedelta import
#? ['Cursor']
from sqlite3 import Cursor
#? ['some_variable']
from . import some_variable
#? ['arrays']
from . import arrays
#? []
from . import import_tree as ren
import os
#? os.path.join
from os.path import join
# -----------------
# special positions -> edge cases
# -----------------
import datetime
#? 6 datetime
from datetime.time import time
#? []
import datetime.
#? []
import datetime.date
#? 21 ['import']
from import_tree.pkg import pkg
#? 22 ['mod1']
from import_tree.pkg. import mod1
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'recurse_class1', 'recurse_class2']
from import_tree. import pkg
#? 18 ['pkg']
from import_tree.p import pkg
#? 17 ['import_tree']
from .import_tree import
#? 10 ['run']
from ..run import
#? ['run']
from ..run
#? 10 ['run']
from ..run.
#? []
from ..run.
#? ['run']
from .. import run
#? []
from not_a_module import
#137
import json
#? 23 json.dump
from json import load, dump
#? 17 json.load
from json import load, dump
# without the from clause:
import json, datetime
#? 7 json
import json, datetime
#? 13 datetime
import json, datetime

View File

@@ -89,6 +89,22 @@ def f(b, a): return a
#? []
f(b=3)
# -----------------
# closure
# -----------------
def x():
a = 0
def x():
return a
a = 3.0
return x()
#? float()
x()
# -----------------
# class
# -----------------

View File

@@ -68,6 +68,19 @@ i += 1
#? int()
x[i]
# -----------------
# in
# -----------------
if 'X' in 'Y':
a = 3
else:
a = ''
# For now don't really check for truth values. So in should return both
# results.
#? str() int()
a
# -----------------
# for flow assignments
# -----------------
@@ -108,16 +121,6 @@ for x in [l(0), l(1), l(2), l(3), l(4), l(5), l(6), l(7), l(8), l(9), l(10),
b[1]
# -----------------
# syntax errors
# -----------------
# strange slice
z = sorted([1], key = lambda x : x):
#? int()
z[0]
# -----------------
# undefined names
# -----------------

View File

@@ -47,6 +47,17 @@ arr2.app
#? int()
arr.count(1)
x = []
#?
x.pop()
x = [3]
#? int()
x.pop()
x = []
x.append(1.0)
#? float()
x.pop()
# -----------------
# dicts
# -----------------

View File

@@ -3,26 +3,28 @@ Renaming tests. This means search for usages.
I always leave a little bit of space to add room for additions, because the
results always contain position informations.
"""
#< 4 (0,4), (3,0), (5,0)
#< 4 (0,4), (3,0), (5,0), (17,0)
def abc(): pass
#< 0 (-3,4), (0,0), (2,0)
#< 0 (-3,4), (0,0), (2,0), (14,0)
abc.d.a.bsaasd.abc.d
abc
# unicode chars shouldn't be a problem.
x['smörbröd'].abc
# With the new parser these statements are not recognized as stateents, because
# they are not valid Python.
if 1:
abc =
else:
(abc) =
abc =
#< (-3,0), (0,0)
#< (-17,4), (-14,0), (-12,0), (0,0)
abc
abc = 5
Abc = 3
@@ -47,12 +49,13 @@ class Abc():
Abc.d.Abc
#< 4 (0,4), (4,1)
def blub():
#< 4 (0,4), (5,1)
def blubi():
pass
#< (-4,4), (0,1)
@blub
#< (-5,4), (0,1)
@blubi
def a(): pass
@@ -96,7 +99,7 @@ from import_tree.rename1 import abc
#< (0, 32),
from import_tree.rename1 import not_existing
# Shouldn't work (would raise a NotFoundError, because there's no name.)
# Shouldn't raise an error or do anything weird.
from not_existing import *
# -----------------
@@ -136,6 +139,9 @@ class TestInstanceVar():
def b(self):
#< (-4,13), (0,13)
self._instance_var
# A call to self used to trigger an error, because it's also a trailer
# with two children.
self()
class NestedClass():
@@ -161,7 +167,7 @@ class Super(object):
def base_method(self):
#< 13 (0,13), (20,13)
self.base_var = 1
#< 13 (0,13), (24,13), (29,13)
#< 13 (0,13),
self.instance_var = 1
#< 8 (0,8),
@@ -190,7 +196,7 @@ class TestClass(Super):
#< 9 (0,8),
def just_a_method(self):
#< (-5,13), (0,13), (-29,13)
#< (-5,13), (0,13)
self.instance_var

View File

@@ -276,7 +276,7 @@ def collect_file_tests(lines, lines_to_execute):
def collect_dir_tests(base_dir, test_files, check_thirdparty=False):
for f_name in os.listdir(base_dir):
files_to_execute = [a for a in test_files.items() if a[0] in f_name]
files_to_execute = [a for a in test_files.items() if f_name.startswith(a[0])]
lines_to_execute = reduce(lambda x, y: x + y[1], files_to_execute, [])
if f_name.endswith(".py") and (not test_files or files_to_execute):
skip = None

View File

@@ -34,7 +34,7 @@ def two_params(x, y):
two_params(y=2, x=1)
two_params(1, y=2)
#! 10 type-error-multiple-values
#! 11 type-error-multiple-values
two_params(1, x=2)
#! 17 type-error-too-many-arguments
two_params(1, 2, y=3)

View File

@@ -111,3 +111,9 @@ import import_tree
import_tree.a
import_tree.b
# This is something that raised an error, because it was using a complex
# mixture of Jedi fakes and compiled objects.
import _sre
#! 15 attribute-error
_sre.compile().not_existing

View File

@@ -1,7 +1,7 @@
def generator():
yield 1
#! 11 type-error-generator
#! 12 type-error-generator
generator()[0]
list(generator())[0]

View File

@@ -27,7 +27,7 @@ def nested_twice(*args1):
return nested(*args1)
nested_twice(2, 3)
#! 12 type-error-too-few-arguments
#! 13 type-error-too-few-arguments
nested_twice(2)
#! 19 type-error-too-many-arguments
nested_twice(2, 3, 4)
@@ -47,13 +47,13 @@ def kwargs_test(**kwargs):
return simple2(1, **kwargs)
kwargs_test(c=3, b=2)
#! 11 type-error-too-few-arguments
#! 12 type-error-too-few-arguments
kwargs_test(c=3)
#! 11 type-error-too-few-arguments
#! 12 type-error-too-few-arguments
kwargs_test(b=2)
#! 22 type-error-keyword-argument
kwargs_test(b=2, c=3, d=4)
##! 11 type-error-multiple-values
#! 12 type-error-multiple-values
kwargs_test(b=2, c=3, a=4)
@@ -65,10 +65,11 @@ kwargs_nested(c=3)
kwargs_nested()
#! 19 type-error-keyword-argument
kwargs_nested(c=2, d=4)
##! 13 type-error-multiple-values
#! 14 type-error-multiple-values
kwargs_nested(c=2, a=4)
#! 13 type-error-multiple-values
kwargs_nested(b=3, c=2)
# TODO reenable
##! 14 type-error-multiple-values
#kwargs_nested(b=3, c=2)
# -----------------
# mixed *args/**kwargs
@@ -77,7 +78,6 @@ kwargs_nested(b=3, c=2)
def simple_mixed(a, b, c):
return b
def mixed(*args, **kwargs):
return simple_mixed(1, *args, **kwargs)
@@ -91,15 +91,16 @@ def mixed2(*args, **kwargs):
return simple_mixed(1, *args, **kwargs)
#! 6 type-error-too-few-arguments
#! 7 type-error-too-few-arguments
mixed2(c=2)
#! 6 type-error-too-few-arguments
#! 7 type-error-too-few-arguments
mixed2(3)
#! 13 type-error-too-many-arguments
mixed2(3, 4, 5)
#! 13 type-error-too-many-arguments
mixed2(3, 4, c=5)
#! 6 type-error-multiple-values
# TODO reenable
##! 13 type-error-too-many-arguments
#mixed2(3, 4, c=5)
#! 7 type-error-multiple-values
mixed2(3, b=5)
# -----------------
@@ -108,6 +109,11 @@ mixed2(3, b=5)
#! 12 type-error-star-star
simple(1, **[])
#! 12 type-error-star-star
simple(1, **1)
class A(): pass
#! 12 type-error-star-star
simple(1, **A())
#! 11 type-error-star
simple(1, *1)

View File

@@ -5,6 +5,7 @@ Test all things related to the ``jedi.api`` module.
from textwrap import dedent
from jedi import api
from jedi._compatibility import is_py3
from pytest import raises
@@ -79,7 +80,10 @@ def test_completion_on_number_literals():
def test_completion_on_hex_literals():
assert api.Script('0x1..').completions() == []
_check_number('0x1.', 'int') # hexdecimal
_check_number('0b3.', 'int') # binary
# Completing binary literals doesn't work if they are not actually binary
# (invalid statements).
assert api.Script('0b2.').completions() == []
_check_number('0b1.', 'int') # binary
_check_number('0o7.', 'int') # octal
_check_number('0x2e.', 'int')
@@ -98,12 +102,19 @@ def test_completion_on_complex_literals():
assert api.Script('4j').completions() == []
def test_goto_assignments_on_non_statement():
with raises(api.NotFoundError):
api.Script('for').goto_assignments()
def test_goto_assignments_on_non_name():
assert api.Script('for').goto_assignments() == []
with raises(api.NotFoundError):
api.Script('assert').goto_assignments()
assert api.Script('assert').goto_assignments() == []
if is_py3:
assert api.Script('True').goto_assignments() == []
else:
# In Python 2.7 True is still a name.
assert api.Script('True').goto_assignments()[0].description == 'class bool'
def test_goto_definitions_on_non_name():
assert api.Script('import x', column=0).goto_definitions() == []
def test_goto_definition_not_multiple():

View File

@@ -95,7 +95,7 @@ def test_function_call_signature_in_doc():
pass
f""").goto_definitions()
doc = defs[0].doc
assert "f(x, y = 1, z = 'a')" in str(doc)
assert "f(x, y=1, z='a')" in str(doc)
def test_class_call_signature():
@@ -105,7 +105,7 @@ def test_class_call_signature():
pass
Foo""").goto_definitions()
doc = defs[0].doc
assert "Foo(self, x, y = 1, z = 'a')" in str(doc)
assert "Foo(self, x, y=1, z='a')" in str(doc)
def test_position_none_if_builtin():
@@ -119,11 +119,21 @@ def test_completion_docstring():
"""
Jedi should follow imports in certain conditions
"""
def docstr(src, result):
c = Script(src).completions()[0]
assert c.docstring(raw=True, fast=False) == cleandoc(result)
c = Script('import jedi\njed').completions()[0]
assert c.docstring(fast=False) == cleandoc(jedi_doc)
c = Script('import jedi\njedi.Scr').completions()[0]
assert c.docstring(raw=True, fast=False) == cleandoc(Script.__doc__)
docstr('import jedi\njedi.Scr', cleandoc(Script.__doc__))
docstr('abcd=3;abcd', '')
docstr('"hello"\nabcd=3\nabcd', 'hello')
# It works with a ; as well.
docstr('"hello"\nabcd=3;abcd', 'hello')
# Shouldn't work with a tuple.
docstr('"hello",0\nabcd=3\nabcd', '')
def test_completion_params():
@@ -148,6 +158,39 @@ def test_signature_params():
check(Script(s + '\nbar=foo\nbar').goto_assignments())
class TestIsDefinition(TestCase):
def _def(self, source, index=-1):
return names(dedent(source), references=True, all_scopes=True)[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):
def _parent(self, source, line=None, column=None):
defs = Script(dedent(source), line, column).goto_assignments()
@@ -179,7 +222,7 @@ class TestParent(TestCase):
def bar(): pass
Foo().bar''')).completions()[0].parent()
assert parent.name == 'Foo'
assert parent.type == 'class'
assert parent.type == 'instance'
parent = Script('str.join').completions()[0].parent()
assert parent.name == 'str'
@@ -219,3 +262,63 @@ class TestGotoAssignments(TestCase):
param = bar.goto_assignments()[0]
assert param.start_pos == (1, 13)
assert param.type == 'param'
def test_class_call(self):
src = 'from threading import Thread; Thread(group=1)'
n = names(src, references=True)[-1]
assert n.name == 'group'
param_def = n.goto_assignments()[0]
assert param_def.name == 'group'
assert param_def.type == 'param'
def test_parentheses(self):
n = names('("").upper', references=True)[-1]
assert n.goto_assignments()[0].name == 'upper'
def test_import(self):
nms = names('from json import load', references=True)
assert nms[0].name == 'json'
assert nms[0].type == 'import'
n = nms[0].goto_assignments()[0]
assert n.name == 'json'
assert n.type == 'module'
assert nms[1].name == 'load'
assert nms[1].type == 'import'
n = nms[1].goto_assignments()[0]
assert n.name == 'load'
assert n.type == 'function'
nms = names('import os; os.path', references=True)
assert nms[0].name == 'os'
assert nms[0].type == 'import'
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 == 'import'
nms = names('import os.path', references=True)
n = nms[0].goto_assignments()[0]
assert n.name == 'os'
assert n.type == 'module'
n = nms[1].goto_assignments()[0]
assert n.name == 'path'
assert n.type == 'import'
def test_import_alias(self):
nms = names('import json as foo', references=True)
assert nms[0].name == 'json'
assert nms[0].type == 'import'
n = nms[0].goto_assignments()[0]
assert n.name == 'json'
assert n.type == 'module'
assert nms[1].name == 'foo'
assert nms[1].type == 'import'
ass = nms[1].goto_assignments()
assert len(ass) == 1
assert ass[0].name == 'json'
assert ass[0].type == 'module'

View File

@@ -22,6 +22,9 @@ class TestCallSignatures(TestCase):
def _run_simple(self, source, name, index=0, column=None, line=1):
self._run(source, name, index, line, column)
def test_valid_call(self):
self._run('str()', 'str', column=4)
def test_simple(self):
run = self._run_simple
s7 = "str().upper().center("
@@ -171,6 +174,27 @@ class TestCallSignatures(TestCase):
def test_unterminated_strings(self):
self._run('str(";', 'str', 0)
def test_whitespace_before_bracket(self):
self._run('str (', 'str', 0)
self._run('str (";', 'str', 0)
# TODO this is not actually valid Python, the newline token should be
# ignored.
self._run('str\n(', 'str', 0)
def test_brackets_in_string_literals(self):
self._run('str (" (', 'str', 0)
self._run('str (" )', 'str', 0)
def test_function_definitions_should_break(self):
"""
Function definitions (and other tokens that cannot exist within call
signatures) should break and not be able to return a call signature.
"""
assert not Script('str(\ndef x').call_signatures()
def test_flow_call(self):
assert not Script('if (1').call_signatures()
class TestParams(TestCase):
def params(self, source, line=None, column=None):
@@ -277,3 +301,12 @@ def test_signature_index():
assert get(both + 'foo(a=2').index == 1
assert get(both + 'foo(a=2, b=2').index == 1
assert get(both + 'foo(a, b, c').index == 0
def test_bracket_start():
def bracket_start(src):
signatures = Script(src).call_signatures()
assert len(signatures) == 1
return signatures[0].bracket_start
assert bracket_start('str(') == (1, 3)

View File

@@ -10,10 +10,10 @@ from ..helpers import TestCase
class TestDefinedNames(TestCase):
def assert_definition_names(self, definitions, names):
self.assertEqual([d.name for d in definitions], names)
assert [d.name for d in definitions] == names
def check_defined_names(self, source, names):
definitions = api.defined_names(textwrap.dedent(source))
definitions = api.names(textwrap.dedent(source))
self.assert_definition_names(definitions, names)
return definitions
@@ -31,7 +31,7 @@ class TestDefinedNames(TestCase):
self.check_defined_names("""
x = Class()
x.y.z = None
""", ['x'])
""", ['x', 'z']) # TODO is this behavior what we want?
def test_multiple_assignment(self):
self.check_defined_names("""

View File

@@ -27,16 +27,14 @@ class MixinTestFullName(object):
def check(self, source, desired):
script = jedi.Script(textwrap.dedent(source))
definitions = getattr(script, type(self).operation)()
self.assertEqual(definitions[0].full_name, desired)
for d in definitions:
self.assertEqual(d.full_name, desired)
def test_os_path_join(self):
self.check('import os; os.path.join', 'os.path.join')
def test_builtin(self):
self.check('type', 'type')
def test_from_import(self):
self.check('from os import path', 'os.path')
self.check('TypeError', 'TypeError')
class TestFullNameWithGotoDefinitions(MixinTestFullName, TestCase):
@@ -47,7 +45,10 @@ class TestFullNameWithGotoDefinitions(MixinTestFullName, TestCase):
self.check("""
import re
any_re = re.compile('.*')
any_re""", 're.RegexObject')
any_re""", '_sre.compile.SRE_Pattern')
def test_from_import(self):
self.check('from os import path', 'os.path')
class TestFullNameWithCompletions(MixinTestFullName, TestCase):

View File

@@ -67,7 +67,9 @@ def test_star_import_cache_duration():
old, jedi.settings.star_import_cache_validity = \
jedi.settings.star_import_cache_validity, new
cache._star_import_cache = {} # first empty...
dct = cache._time_caches['star_import_cache_validity']
old_dct = dict(dct)
dct.clear() # first empty...
# path needs to be not-None (otherwise caching effects are not visible)
jedi.Script('', 1, 0, '').completions()
time.sleep(2 * new)
@@ -75,7 +77,8 @@ def test_star_import_cache_duration():
# reset values
jedi.settings.star_import_cache_validity = old
assert len(cache._star_import_cache) == 1
assert len(dct) == 1
dct = old_dct
cache._star_import_cache = {}

View File

@@ -0,0 +1 @@
/path/from/egg-link

View File

@@ -4,7 +4,7 @@ Python 2.X)
"""
import jedi
from jedi._compatibility import u
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
from .. import helpers
@@ -12,7 +12,7 @@ def test_explicit_absolute_imports():
"""
Detect modules with ``from __future__ import absolute_import``.
"""
parser = Parser(u("from __future__ import absolute_import"), "test.py")
parser = Parser(load_grammar(), u("from __future__ import absolute_import"), "test.py")
assert parser.module.has_explicit_absolute_import
@@ -20,7 +20,7 @@ def test_no_explicit_absolute_imports():
"""
Detect modules without ``from __future__ import absolute_import``.
"""
parser = Parser(u("1"), "test.py")
parser = Parser(load_grammar(), u("1"), "test.py")
assert not parser.module.has_explicit_absolute_import
@@ -30,7 +30,7 @@ def test_dont_break_imports_without_namespaces():
assume that all imports have non-``None`` namespaces.
"""
src = u("from __future__ import absolute_import\nimport xyzzy")
parser = Parser(src, "test.py")
parser = Parser(load_grammar(), src, "test.py")
assert parser.module.has_explicit_absolute_import

View File

@@ -0,0 +1,29 @@
from textwrap import dedent
import jedi
import pytest
@pytest.mark.skipif('sys.version_info[0] < 3')
def test_simple_annotations():
"""
Annotations only exist in Python 3.
At the moment we ignore them. So they should be parsed and not interfere
with anything.
"""
source = dedent("""\
def annot(a:3):
return a
annot('')""")
assert [d.name for d in jedi.Script(source, ).goto_definitions()] == ['str']
source = dedent("""\
def annot_ret(a:3) -> 3:
return a
annot_ret('')""")
assert [d.name for d in jedi.Script(source, ).goto_definitions()] == ['str']

View File

@@ -6,7 +6,7 @@ from jedi.evaluate.sys_path import (_get_parent_dir_with_file,
_get_buildout_scripts,
_check_module)
from jedi.evaluate import Evaluator
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
from ..helpers import cwd_at
@@ -35,8 +35,9 @@ def test_append_on_non_sys_path():
d = Dummy()
d.path.append('foo')"""))
p = Parser(SRC)
paths = _check_module(Evaluator(), p.module)
grammar = load_grammar()
p = Parser(grammar, SRC)
paths = _check_module(Evaluator(grammar), p.module)
assert len(paths) > 0
assert 'foo' not in paths
@@ -45,8 +46,9 @@ def test_path_from_invalid_sys_path_assignment():
SRC = dedent(u("""
import sys
sys.path = 'invalid'"""))
p = Parser(SRC)
paths = _check_module(Evaluator(), p.module)
grammar = load_grammar()
p = Parser(grammar, SRC)
paths = _check_module(Evaluator(grammar), p.module)
assert len(paths) > 0
assert 'invalid' not in paths
@@ -67,7 +69,8 @@ def test_path_from_sys_path_assignment():
if __name__ == '__main__':
sys.exit(important_package.main())"""))
p = Parser(SRC)
paths = _check_module(Evaluator(), p.module)
grammar = load_grammar()
p = Parser(grammar, SRC)
paths = _check_module(Evaluator(grammar), p.module)
assert 1 not in paths
assert '/home/test/.buildout/eggs/important_package.egg' in paths

View File

@@ -1,12 +1,13 @@
from jedi._compatibility import builtins, is_py3
from jedi.parser.representation import Function
from jedi.parser import load_grammar
from jedi.parser.tree import Function
from jedi.evaluate import compiled, representation
from jedi.evaluate import Evaluator
from jedi import Script
def test_simple():
e = Evaluator()
e = Evaluator(load_grammar())
bltn = compiled.CompiledObject(builtins)
obj = compiled.CompiledObject('_str_', bltn)
upper = e.find_types(obj, 'upper')
@@ -17,7 +18,7 @@ def test_simple():
def test_fake_loading():
assert isinstance(compiled.create(Evaluator(), next), Function)
assert isinstance(compiled.create(Evaluator(load_grammar()), next), Function)
string = compiled.builtin.get_subscope_by_name('str')
from_name = compiled._create_from_name(
@@ -29,7 +30,7 @@ def test_fake_loading():
def test_fake_docstr():
assert compiled.create(Evaluator(), next).raw_doc == next.__doc__
assert compiled.create(Evaluator(load_grammar()), next).raw_doc == next.__doc__
def test_parse_function_doc_illegal_docstr():

View File

@@ -1,29 +0,0 @@
from jedi._compatibility import unicode
from jedi.evaluate import helpers
from jedi.parser import representation as pr
from jedi.parser import Parser
def test_deep_ast_copy():
name = pr.Name(object, [('hallo', (0, 0))], (0, 0), (0, 0))
# fast parent copy should switch parent
new_name = helpers.deep_ast_copy(name)
assert new_name.names[0].parent == new_name
def test_statement_elements_in_statement():
def get_stmt_els(string):
p = Parser(unicode(string))
return helpers.statement_elements_in_statement(p.module.statements[0])
# list comprehension
stmt_els = get_stmt_els('foo = [(bar(f), f) for f in baz]')
# stmt_els: count all names: 6; + count all arrays: 2 = 8
assert len(stmt_els) == 8
# lambda
stmt_els = get_stmt_els('foo = [lambda x: y]')
# stmt_els: count all names: 3; + count all arrays: 1 = 4
assert len(stmt_els) == 4

View File

@@ -28,21 +28,11 @@ def test_import_not_in_sys_path():
assert a[0].name == 'str'
def setup_function(function):
sys.path.append(os.path.join(
os.path.dirname(__file__), 'flask-site-packages'))
def teardown_function(function):
path = os.path.join(os.path.dirname(__file__), 'flask-site-packages')
sys.path.remove(path)
@pytest.mark.parametrize("script,name", [
("from flask.ext import foo; foo.", "Foo"), # flask_foo.py
("from flask.ext import bar; bar.", "Bar"), # flaskext/bar.py
("from flask.ext import baz; baz.", "Baz"), # flask_baz/__init__.py
("from flask.ext import moo; moo.", "Moo"), # flaskext/moo/__init__.py
("from flask.ext import foo; foo.", "Foo"), # flask_foo.py
("from flask.ext import bar; bar.", "Bar"), # flaskext/bar.py
("from flask.ext import baz; baz.", "Baz"), # flask_baz/__init__.py
("from flask.ext import moo; moo.", "Moo"), # flaskext/moo/__init__.py
("from flask.ext.", "foo"),
("from flask.ext.", "bar"),
("from flask.ext.", "baz"),
@@ -55,5 +45,9 @@ def teardown_function(function):
def test_flask_ext(script, name):
"""flask.ext.foo is really imported from flaskext.foo or flask_foo.
"""
assert name in [c.name for c in jedi.Script(script).completions()]
path = os.path.join(os.path.dirname(__file__), 'flask-site-packages')
sys.path.append(path)
try:
assert name in [c.name for c in jedi.Script(script).completions()]
finally:
sys.path.remove(path)

View File

@@ -29,9 +29,11 @@ def test_namespace_package():
# completion
completions = jedi.Script('from pkg import ').completions()
names = [str(c.name) for c in completions] # str because of unicode
compare = ['foo', 'ns1_file', 'ns1_folder', 'ns2_folder', 'ns2_file']
compare = ['foo', 'ns1_file', 'ns1_folder', 'ns2_folder', 'ns2_file',
'pkg_resources', 'pkgutil', '__name__', '__path__',
'__package__', '__file__', '__doc__']
# must at least contain these items, other items are not important
assert not (set(compare) - set(names))
assert set(compare) == set(names)
tests = {
'from pkg import ns2_folder as x': 'ns2_folder!',

View File

@@ -1,71 +0,0 @@
from jedi._compatibility import u
from jedi.parser import Parser
from jedi.evaluate import precedence
def parse_tree(statement_string, is_slice=False):
p = Parser(u(statement_string), no_docstr=True)
stmt = p.module.statements[0]
if is_slice:
# get the part of the execution that is the slice
stmt = stmt.expression_list()[0].next[0]
iterable = stmt.expression_list()
pr = precedence.create_precedence(iterable)
if isinstance(pr, precedence.Precedence):
return pr.parse_tree(strip_literals=True)
else:
try:
return pr.value # Literal
except AttributeError:
return pr
def test_simple():
assert parse_tree('1+2') == (1, '+', 2)
assert parse_tree('+2') == (None, '+', 2)
assert parse_tree('1+2-3') == ((1, '+', 2), '-', 3)
def test_prefixed():
assert parse_tree('--2') == (None, '-', (None, '-', 2))
assert parse_tree('1 and not - 2') == (1, 'and', (None, 'not', (None, '-', 2)))
def test_invalid():
"""Should just return a simple operation."""
assert parse_tree('1 +') == 1
assert parse_tree('+') is None
assert parse_tree('* 1') == 1
assert parse_tree('1 * * 1') == (1, '*', 1)
# invalid operator
assert parse_tree('1 not - 1') == (1, '-', 1)
assert parse_tree('1 - not ~1') == (1, '-', (None, '~', 1))
# not not allowed
assert parse_tree('1 is not not 1') == (1, 'is not', 1)
def test_multi_part():
assert parse_tree('1 not in 2') == (1, 'not in', 2)
assert parse_tree('1 is not -1') == (1, 'is not', (None, '-', 1))
assert parse_tree('1 is 1') == (1, 'is', 1)
def test_power():
assert parse_tree('2 ** 3 ** 4') == (2, '**', (3, '**', 4))
def test_slice():
"""
Should be parsed as normal operators. This is not proper Python syntax,
but the warning shouldn't be given in the precedence generation.
"""
assert parse_tree('[0][2+1:3]', is_slice=True) == ((2, '+', 1), ':', 3)
assert parse_tree('[0][:]', is_slice=True) == (None, ':', None)
assert parse_tree('[0][1:]', is_slice=True) == (1, ':', None)
assert parse_tree('[0][:2]', is_slice=True) == (None, ':', 2)
# 3 part slice
assert parse_tree('[0][:2:1]', is_slice=True) == ((None, ':', 2), ':', 1)

View File

@@ -5,7 +5,7 @@ from jedi import Script
def get_definition_and_evaluator(source):
d = Script(dedent(source)).goto_definitions()[0]
return d._name.parent.parent, d._evaluator
return d._name.parent, d._evaluator
def test_function_execution():

View File

@@ -1,12 +1,15 @@
import os
from jedi._compatibility import unicode
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
from jedi.evaluate import sys_path, Evaluator
def test_paths_from_assignment():
def paths(src):
stmt = Parser(unicode(src)).module.statements[0]
return list(sys_path._paths_from_assignment(Evaluator(), stmt))
grammar = load_grammar()
stmt = Parser(grammar, unicode(src)).module.statements[0]
return list(sys_path._paths_from_assignment(Evaluator(grammar), stmt))
assert paths('sys.path[0:0] = ["a"]') == ['a']
assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == ['b', 'c']
@@ -14,3 +17,15 @@ def test_paths_from_assignment():
# Fail for complicated examples.
assert paths('sys.path, other = ["a"], 2') == []
def test_get_sys_path(monkeypatch):
monkeypatch.setenv('VIRTUAL_ENV', os.path.join(os.path.dirname(__file__),
'egg-link', 'venv'))
def sitepackages_dir(venv):
return os.path.join(venv, 'lib', 'python3.4', 'site-packages')
monkeypatch.setattr('jedi.evaluate.sys_path._get_venv_sitepackages',
sitepackages_dir)
assert '/path/from/egg-link' in sys_path.get_sys_path()

View File

@@ -16,11 +16,22 @@ def test_goto_definition_on_import():
@cwd_at('jedi')
def test_complete_on_empty_import():
assert Script("from datetime import").completions()[0].name == 'import'
# should just list the files in the directory
assert 10 < len(Script("from .", path='').completions()) < 30
assert 10 < len(Script("from . import", 1, 5, '').completions()) < 30
assert 10 < len(Script("from . import classes", 1, 5, '').completions()) < 30
assert len(Script("import").completions()) == 0
# Global import
assert len(Script("from . import", 1, 5, '').completions()) > 30
# relative import
assert 10 < len(Script("from . import", 1, 6, '').completions()) < 30
# Global import
assert len(Script("from . import classes", 1, 5, '').completions()) > 30
# relative import
assert 10 < len(Script("from . import classes", 1, 6, '').completions()) < 30
wanted = set(['ImportError', 'import', 'ImportWarning'])
assert set([c.name for c in Script("import").completions()]) == wanted
if not is_py26: # python 2.6 doesn't always come with a library `import*`.
assert len(Script("import import", path='').completions()) > 0
@@ -63,6 +74,7 @@ def test_after_from():
completions = Script(source, column=column).completions()
assert [c.name for c in completions] == result
check('\nfrom os. ', ['path'])
check('\nfrom os ', ['import'])
check('from os ', ['import'])
check('\nfrom os import whatever', ['import'], len('from os im'))

View File

@@ -1,9 +1,8 @@
"""
Test of keywords and ``jedi.keywords``
"""
import jedi
from jedi import Script, common
import pytest
from jedi._compatibility import is_py3
from jedi import Script
def test_goto_assignments_keyword():
@@ -18,13 +17,13 @@ def test_goto_assignments_keyword():
def test_keyword():
""" github jedi-vim issue #44 """
defs = Script("print").goto_definitions()
assert [d.doc for d in defs]
if is_py3:
assert [d.doc for d in defs]
else:
assert defs == []
with pytest.raises(jedi.NotFoundError):
Script("import").goto_assignments()
assert Script("import").goto_assignments() == []
completions = Script("import", 1, 1).completions()
assert len(completions) == 0
with common.ignored(jedi.NotFoundError): # TODO shouldn't throw that.
defs = Script("assert").goto_definitions()
assert len(defs) == 1
assert len(completions) > 10 and 'if' in [c.name for c in completions]
assert Script("assert").goto_definitions() == []

13
test/test_new_parser.py Normal file
View File

@@ -0,0 +1,13 @@
from jedi._compatibility import u
from jedi.parser import Parser, load_grammar
def test_basic_parsing():
def compare(string):
"""Generates the AST object and then regenerates the code."""
assert Parser(load_grammar(), string).module.get_code() == string
compare(u('\na #pass\n'))
compare(u('wblabla* 1\t\n'))
compare(u('def x(a, b:3): pass\n'))
compare(u('assert foo\n'))

View File

@@ -3,6 +3,7 @@ from textwrap import dedent
import jedi
from jedi._compatibility import u
from jedi import cache
from jedi.parser import load_grammar
from jedi.parser.fast import FastParser
@@ -12,15 +13,15 @@ def test_add_to_end():
help of caches, this is an example that didn't work.
"""
a = """
class Abc():
def abc(self):
self.x = 3
a = dedent("""
class Abc():
def abc(self):
self.x = 3
class Two(Abc):
def h(self):
self
""" # ^ here is the first completion
class Two(Abc):
def h(self):
self
""") # ^ here is the first completion
b = " def g(self):\n" \
" self."
@@ -54,30 +55,369 @@ def test_carriage_return_splitting():
pass
'''))
source = source.replace('\n', '\r\n')
p = FastParser(source)
assert [str(n) for n in p.module.get_defined_names()] == ['Foo']
p = FastParser(load_grammar(), source)
assert [n.value for lst in p.module.names_dict.values() for n in lst] == ['Foo']
def test_split_parts():
cache.parser_cache.pop(None, None)
def splits(source):
class Mock(FastParser):
def __init__(self, *args):
self.number_of_splits = 0
return tuple(FastParser._split_parts(Mock(None, None), source))
def test(*parts):
assert splits(''.join(parts)) == parts
test('a\n\n', 'def b(): pass\n', 'c\n')
test('a\n', 'def b():\n pass\n', 'c\n')
def check_fp(src, number_parsers_used, number_of_splits=None, number_of_misses=0):
if number_of_splits is None:
number_of_splits = number_parsers_used
p = FastParser(load_grammar(), u(src))
cache.save_parser(None, None, p, pickling=False)
# TODO Don't change get_code, the whole thing should be the same.
# -> Need to refactor the parser first, though.
assert src == p.module.get_code()
assert p.number_of_splits == number_of_splits
assert p.number_parsers_used == number_parsers_used
assert p.number_of_misses == number_of_misses
return p.module
def test_change_and_undo():
def fp(src):
p = FastParser(u(src))
cache.save_parser(None, None, p, pickling=False)
# TODO Don't change get_code, the whole thing should be the same.
# -> Need to refactor the parser first, though.
assert src == p.module.get_code()[:-1]
# Empty the parser cache for the path None.
cache.parser_cache.pop(None, None)
func_before = 'def func():\n pass\n'
fp(func_before + 'a')
fp(func_before + 'b')
fp(func_before + 'a')
# Parse the function and a.
check_fp(func_before + 'a', 2)
# Parse just b.
check_fp(func_before + 'b', 1, 2)
# b has changed to a again, so parse that.
check_fp(func_before + 'a', 1, 2)
# Same as before no parsers should be used.
check_fp(func_before + 'a', 0, 2)
# Getting rid of an old parser: Still no parsers used.
check_fp('a', 0, 1)
# Now the file has completely change and we need to parse.
check_fp('b', 1, 1)
# And again.
check_fp('a', 1, 1)
def test_positions():
# Empty the parser cache for the path None.
cache.parser_cache.pop(None, None)
fp('a')
fp('b')
fp('a')
func_before = 'class A:\n pass\n'
m = check_fp(func_before + 'a', 2)
assert m.start_pos == (1, 0)
assert m.end_pos == (3, 1)
m = check_fp('a', 0, 1)
assert m.start_pos == (1, 0)
assert m.end_pos == (1, 1)
def test_if():
src = dedent('''\
def func():
x = 3
if x:
def y():
return x
return y()
func()
''')
# Two parsers needed, one for pass and one for the function.
check_fp(src, 2)
assert [d.name for d in jedi.Script(src, 8, 6).goto_definitions()] == ['int']
def test_if_simple():
src = dedent('''\
if 1:
a = 3
''')
check_fp(src + 'a', 1)
check_fp(src + "else:\n a = ''\na", 1)
def test_for():
src = dedent("""\
for a in [1,2]:
a
for a1 in 1,"":
a1
""")
check_fp(src, 1)
def test_class_with_class_var():
src = dedent("""\
class SuperClass:
class_super = 3
def __init__(self):
self.foo = 4
pass
""")
check_fp(src, 3)
def test_func_with_if():
src = dedent("""\
def recursion(a):
if foo:
return recursion(a)
else:
if bar:
return inexistent
else:
return a
""")
check_fp(src, 1)
def test_decorator():
src = dedent("""\
class Decorator():
@memoize
def dec(self, a):
return a
""")
check_fp(src, 2)
def test_nested_funcs():
src = dedent("""\
def memoize(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
""")
check_fp(src, 3)
def test_class_and_if():
src = dedent("""\
class V:
def __init__(self):
pass
if 1:
c = 3
def a_func():
return 1
# COMMENT
a_func()""")
check_fp(src, 5, 5)
assert [d.name for d in jedi.Script(src).goto_definitions()] == ['int']
def test_func_with_for_and_comment():
# The first newline is important, leave it. It should not trigger another
# parser split.
src = dedent("""\
def func():
pass
for a in [1]:
# COMMENT
a""")
check_fp(src, 2)
# We don't need to parse the for loop, but we need to parse the other two,
# because the split is in a different place.
check_fp('a\n' + src, 2, 3)
def test_multi_line_params():
src = dedent("""\
def x(a,
b):
pass
foo = 1
""")
check_fp(src, 2)
def test_one_statement_func():
src = dedent("""\
first
def func(): a
""")
check_fp(src + 'second', 3)
# Empty the parser cache, because we're not interested in modifications
# here.
cache.parser_cache.pop(None, None)
check_fp(src + 'def second():\n a', 3)
def test_class_func_if():
src = dedent("""\
class Class:
def func(self):
if 1:
a
else:
b
pass
""")
check_fp(src, 3)
def test_for_on_one_line():
src = dedent("""\
foo = 1
for x in foo: pass
def hi():
pass
""")
check_fp(src, 2)
src = dedent("""\
def hi():
for x in foo: pass
pass
pass
""")
check_fp(src, 2)
src = dedent("""\
def hi():
for x in foo: pass
def nested():
pass
""")
check_fp(src, 2)
def test_multi_line_for():
src = dedent("""\
for x in [1,
2]:
pass
pass
""")
check_fp(src, 1)
def test_wrong_indentation():
src = dedent("""\
def func():
a
b
a
""")
check_fp(src, 1)
src = dedent("""\
def complex():
def nested():
a
b
a
def other():
pass
""")
check_fp(src, 3)
def test_open_parentheses():
func = 'def func():\n a'
p = FastParser(load_grammar(), u('isinstance(\n\n' + func))
# As you can see, the isinstance call cannot be seen anymore after
# get_code, because it isn't valid code.
assert p.module.get_code() == '\n\n' + func
assert p.number_of_splits == 2
assert p.number_parsers_used == 2
cache.save_parser(None, None, p, pickling=False)
# Now with a correct parser it should work perfectly well.
check_fp('isinstance()\n' + func, 1, 2)
def test_strange_parentheses():
src = dedent("""
class X():
a = (1
if 1 else 2)
def x():
pass
""")
check_fp(src, 2)
def test_backslash():
src = dedent(r"""
a = 1\
if 1 else 2
def x():
pass
""")
check_fp(src, 2)
src = dedent(r"""
def x():
a = 1\
if 1 else 2
def y():
pass
""")
# The dangling if leads to not splitting where we theoretically could
# split.
check_fp(src, 2)
src = dedent(r"""
def first():
if foo \
and bar \
or baz:
pass
def second():
pass
""")
check_fp(src, 2)
def test_fake_parentheses():
"""
The fast parser splitting counts parentheses, but not as correct tokens.
Therefore parentheses in string tokens are included as well. This needs to
be accounted for.
"""
src = dedent(r"""
def x():
a = (')'
if 1 else 2)
def y():
pass
def z():
pass
""")
check_fp(src, 3, 2, 1)
def test_incomplete_function():

View File

@@ -3,7 +3,7 @@ import difflib
import pytest
from jedi._compatibility import u
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
code_basic_features = u('''
"""A mod docstring"""
@@ -44,21 +44,19 @@ def diff_code_assert(a, b, n=4):
def test_basic_parsing():
"""Validate the parsing features"""
prs = Parser(code_basic_features)
prs = Parser(load_grammar(), code_basic_features)
diff_code_assert(
code_basic_features,
prs.module.get_code2()
prs.module.get_code()
)
@pytest.mark.skipif('True', reason='Not yet working.')
def test_operators():
src = u('5 * 3')
prs = Parser(src)
prs = Parser(load_grammar(), src)
diff_code_assert(src, prs.module.get_code())
@pytest.mark.skipif('True', reason='Broke get_code support for yield/return statements.')
def test_get_code():
"""Use the same code that the parser also generates, to compare"""
s = u('''"""a docstring"""
@@ -84,4 +82,24 @@ def method_with_docstring():
"""class docstr"""
pass
''')
assert Parser(s).module.get_code() == s
assert Parser(load_grammar(), s).module.get_code() == s
def test_end_newlines():
"""
The Python grammar explicitly needs a newline at the end. Jedi though still
wants to be able, to return the exact same code without the additional new
line the parser needs.
"""
def test(source, end_pos):
module = Parser(load_grammar(), u(source)).module
assert module.get_code() == source
assert module.end_pos == end_pos
test('a', (1, 1))
test('a\n', (2, 0))
test('a\nb', (2, 1))
test('a\n#comment\n', (3, 0))
test('a\n#comment', (2, 8))
test('a#comment', (1, 9))
test('def a():\n pass', (2, 5))

View File

@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
import jedi
from jedi._compatibility import u, is_py3
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
from jedi.parser.user_context import UserContextParser
from jedi.parser import representation as pr
from jedi.parser import tree as pt
from textwrap import dedent
@@ -13,50 +14,50 @@ def test_user_statement_on_import():
" time)")
for pos in [(2, 1), (2, 4)]:
p = UserContextParser(s, None, pos, None).user_stmt()
assert isinstance(p, pr.Import)
assert p.defunct is False
p = UserContextParser(load_grammar(), s, None, pos, None).user_stmt()
assert isinstance(p, pt.Import)
assert [str(n) for n in p.get_defined_names()] == ['time']
class TestCallAndName():
def get_call(self, source):
stmt = Parser(u(source), no_docstr=True).module.statements[0]
return stmt.expression_list()[0]
# Get the simple_stmt and then the first one.
simple_stmt = Parser(load_grammar(), u(source)).module.children[0]
return simple_stmt.children[0]
def test_name_and_call_positions(self):
call = self.get_call('name\nsomething_else')
assert str(call.name) == 'name'
assert call.name.start_pos == call.start_pos == (1, 0)
assert call.name.end_pos == call.end_pos == (1, 4)
name = self.get_call('name\nsomething_else')
assert str(name) == 'name'
assert name.start_pos == (1, 0)
assert name.end_pos == (1, 4)
call = self.get_call('1.0\n')
assert call.value == 1.0
assert call.start_pos == (1, 0)
assert call.end_pos == (1, 3)
leaf = self.get_call('1.0\n')
assert leaf.value == '1.0'
assert leaf.eval() == 1.0
assert leaf.start_pos == (1, 0)
assert leaf.end_pos == (1, 3)
def test_call_type(self):
call = self.get_call('hello')
assert isinstance(call, pr.Call)
assert type(call.name) == pr.Name
assert isinstance(call, pt.Name)
def test_literal_type(self):
literal = self.get_call('1.0')
assert isinstance(literal, pr.Literal)
assert type(literal.value) == float
assert isinstance(literal, pt.Literal)
assert type(literal.eval()) == float
literal = self.get_call('1')
assert isinstance(literal, pr.Literal)
assert type(literal.value) == int
assert isinstance(literal, pt.Literal)
assert type(literal.eval()) == int
literal = self.get_call('"hello"')
assert isinstance(literal, pr.Literal)
assert literal.value == 'hello'
assert isinstance(literal, pt.Literal)
assert literal.eval() == 'hello'
class TestSubscopes():
def get_sub(self, source):
return Parser(u(source)).module.subscopes[0]
return Parser(load_grammar(), u(source)).module.subscopes[0]
def test_subscope_names(self):
name = self.get_sub('class Foo: pass').name
@@ -72,7 +73,7 @@ class TestSubscopes():
class TestImports():
def get_import(self, source):
return Parser(source).module.imports[0]
return Parser(load_grammar(), source).module.imports[0]
def test_import_names(self):
imp = self.get_import(u('import math\n'))
@@ -87,13 +88,13 @@ class TestImports():
def test_module():
module = Parser(u('asdf'), 'example.py', no_docstr=True).module
module = Parser(load_grammar(), u('asdf'), 'example.py').module
name = module.name
assert str(name) == 'example'
assert name.start_pos == (1, 0)
assert name.end_pos == (1, 7)
module = Parser(u('asdf'), no_docstr=True).module
module = Parser(load_grammar(), u('asdf')).module
name = module.name
assert str(name) == ''
assert name.start_pos == (1, 0)
@@ -106,7 +107,7 @@ def test_end_pos():
def func():
y = None
'''))
parser = Parser(s)
parser = Parser(load_grammar(), s)
scope = parser.module.subscopes[0]
assert scope.start_pos == (3, 0)
assert scope.end_pos == (5, 0)
@@ -119,14 +120,15 @@ def test_carriage_return_statements():
# this is a namespace package
'''))
source = source.replace('\n', '\r\n')
stmt = Parser(source).module.statements[0]
stmt = Parser(load_grammar(), source).module.statements[0]
assert '#' not in stmt.get_code()
def test_incomplete_list_comprehension():
""" Shouldn't raise an error, same bug as #418. """
s = Parser(u('(1 for def')).module.statements[0]
assert s.expression_list()
# With the old parser this actually returned a statement. With the new
# parser only valid statements generate one.
assert Parser(load_grammar(), u('(1 for def')).module.statements == []
def test_hex_values_in_docstring():
@@ -138,8 +140,43 @@ def test_hex_values_in_docstring():
return 1
'''
doc = Parser(dedent(u(source))).module.subscopes[0].raw_doc
doc = Parser(load_grammar(), dedent(u(source))).module.subscopes[0].raw_doc
if is_py3:
assert doc == '\xff'
else:
assert doc == u('<EFBFBD>')
def test_error_correction_with():
source = """
with open() as f:
try:
f."""
comps = jedi.Script(source).completions()
assert len(comps) > 30
# `open` completions have a closed attribute.
assert [1 for c in comps if c.name == 'closed']
def test_newline_positions():
endmarker = Parser(load_grammar(), u('a\n')).module.children[-1]
assert endmarker.end_pos == (2, 0)
new_line = endmarker.get_previous()
assert new_line.start_pos == (1, 1)
assert new_line.end_pos == (2, 0)
def test_end_pos_error_correction():
"""
Source code without ending newline are given one, because the Python
grammar needs it. However, they are removed again. We still want the right
end_pos, even if something breaks in the parser (error correction).
"""
s = u('def x():\n .')
m = Parser(load_grammar(), s).module
func = m.children[0]
assert func.type == 'funcdef'
# This is not exactly correct, but ok, because it doesn't make a difference
# at all. We just want to make sure that the module end_pos is correct!
assert func.end_pos == (3, 0)
assert m.end_pos == (2, 2)

View File

@@ -1,16 +0,0 @@
from jedi.parser import Parser
from jedi.parser import representation as pr
from jedi._compatibility import u
import pytest
def test_import_is_nested():
imp = Parser(u('import ')).module.imports[0]
# should not raise an error, even if it's not a complete import
assert not imp.is_nested()
@pytest.mark.skipif('True', 'Reenable this later, module should also have a scope_names_generator.')
def test_module_scope_name_generator():
assert pr.Module().scope_names_generator()

View File

@@ -1,27 +1,101 @@
from jedi._compatibility import u
# -*- coding: utf-8 # This file contains Unicode characters.
from io import StringIO
from token import NEWLINE, STRING, INDENT
from jedi._compatibility import u, is_py3
from jedi.parser.token import NAME
from jedi import parser
from token import STRING
from ..helpers import unittest
class TokenTest(unittest.TestCase):
def test_end_pos_one_line(self):
parsed = parser.Parser(u('''
parsed = parser.Parser(parser.load_grammar(), u('''
def testit():
a = "huhu"
'''))
tok = parsed.module.subscopes[0].statements[0]._token_list[2]
self.assertEqual(tok.end_pos, (3, 14))
tok = parsed.module.subscopes[0].statements[0].children[2]
assert tok.end_pos == (3, 14)
def test_end_pos_multi_line(self):
parsed = parser.Parser(u('''
parsed = parser.Parser(parser.load_grammar(), u('''
def testit():
a = """huhu
asdfasdf""" + "h"
'''))
tok = parsed.module.subscopes[0].statements[0]._token_list[2]
self.assertEqual(tok.end_pos, (4, 11))
tok = parsed.module.subscopes[0].statements[0].children[2].children[0]
assert tok.end_pos == (4, 11)
def test_simple_no_whitespace(self):
# Test a simple one line string, no preceding whitespace
simple_docstring = u('"""simple one line docstring"""')
simple_docstring_io = StringIO(simple_docstring)
tokens = parser.tokenize.generate_tokens(simple_docstring_io.readline)
token_list = list(tokens)
_, value, _, prefix = token_list[0]
assert prefix == ''
assert value == '"""simple one line docstring"""'
def test_simple_with_whitespace(self):
# Test a simple one line string with preceding whitespace and newline
simple_docstring = u(' """simple one line docstring""" \r\n')
simple_docstring_io = StringIO(simple_docstring)
tokens = parser.tokenize.generate_tokens(simple_docstring_io.readline)
token_list = list(tokens)
assert token_list[0][0] == INDENT
typ, value, start_pos, prefix = token_list[1]
assert prefix == ' '
assert value == '"""simple one line docstring"""'
assert typ == STRING
typ, value, start_pos, prefix = token_list[2]
assert prefix == ' '
assert typ == NEWLINE
def test_function_whitespace(self):
# Test function definition whitespace identification
fundef = u('''def test_whitespace(*args, **kwargs):
x = 1
if x > 0:
print(True)
''')
fundef_io = StringIO(fundef)
tokens = parser.tokenize.generate_tokens(fundef_io.readline)
token_list = list(tokens)
for _, value, _, prefix in token_list:
if value == 'test_whitespace':
assert prefix == ' '
if value == '(':
assert prefix == ''
if value == '*':
assert prefix == ''
if value == '**':
assert prefix == ' '
if value == 'print':
assert prefix == ' '
if value == 'if':
assert prefix == ' '
def test_identifier_contains_unicode(self):
fundef = u('''
def 我あφ():
pass
''')
fundef_io = StringIO(fundef)
if is_py3:
tokens = parser.tokenize.generate_tokens(fundef_io.readline)
token_list = list(tokens)
identifier_token = next(
(token for token in token_list if token[1] == '我あφ'),
None
)
self.assertIsNotNone(identifier_token)
assert identifier_token[0] == NAME
else:
pass
def test_quoted_strings(self):
@@ -45,4 +119,4 @@ asdfasdf""" + "h"
def test_tokenizer_with_string_literal_backslash():
import jedi
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
assert c[0]._name.parent.parent.obj == 'foo'
assert c[0]._name.parent.obj == 'foo'

View File

@@ -15,7 +15,7 @@ from jedi._compatibility import u
from jedi import Script
from jedi import api
from jedi.evaluate import imports
from jedi.parser import Parser
from jedi.parser import Parser, load_grammar
#jedi.set_debug_function()
@@ -55,7 +55,7 @@ class TestRegression(TestCase):
assert should2 == diff_line
self.assertRaises(jedi.NotFoundError, get_def, cls)
assert get_def(cls) == []
@pytest.mark.skipif('True', reason='Skip for now, test case is not really supported.')
@cwd_at('jedi')
@@ -98,13 +98,12 @@ class TestRegression(TestCase):
self.assertEqual([d.description for d in defs],
['def f', 'class C'])
def test_end_pos(self):
def test_end_pos_line(self):
# jedi issue #150
s = u("x()\nx( )\nx( )\nx ( )")
parser = Parser(s)
for i, s in enumerate(parser.module.statements, 3):
for c in s.expression_list():
self.assertEqual(c.next.end_pos[1], i)
parser = Parser(load_grammar(), s)
for i, s in enumerate(parser.module.statements):
assert s.end_pos == (i + 1, i + 3)
def check_definition_by_marker(self, source, after_cursor, names):
r"""
@@ -170,4 +169,4 @@ def test_loading_unicode_files_with_bad_global_charset(monkeypatch, tmpdir):
f.write(data)
s = Script("from test1 import foo\nfoo.",
line=2, column=4, path=filename2)
s.complete()
s.completions()