mirror of
https://github.com/davidhalter/jedi.git
synced 2026-02-07 09:50:55 +08:00
Merge remote-tracking branch 'upstream/dev' into bugfix/performances_degradation
# Conflicts: # jedi/evaluate/compiled/fake.py
This commit is contained in:
@@ -39,13 +39,16 @@ b[8:]
|
||||
#? list()
|
||||
b[int():]
|
||||
|
||||
#? list()
|
||||
b[:]
|
||||
|
||||
|
||||
class _StrangeSlice():
|
||||
def __getitem__(self, slice):
|
||||
return slice
|
||||
def __getitem__(self, sliced):
|
||||
return sliced
|
||||
|
||||
# Should not result in an error, just because the slice itself is returned.
|
||||
#? []
|
||||
#? slice()
|
||||
_StrangeSlice()[1:2]
|
||||
|
||||
|
||||
@@ -125,6 +128,9 @@ f
|
||||
# -----------------
|
||||
# unnessecary braces
|
||||
# -----------------
|
||||
a = (1)
|
||||
#? int()
|
||||
a
|
||||
#? int()
|
||||
(1)
|
||||
#? int()
|
||||
@@ -204,9 +210,22 @@ dic2['asdf']
|
||||
dic2[r'asdf']
|
||||
#? int()
|
||||
dic2[r'asdf']
|
||||
#? int()
|
||||
dic2[r'as' 'd' u'f']
|
||||
#? int() str()
|
||||
dic2['just_something']
|
||||
|
||||
# unpacking
|
||||
a, b = dic2
|
||||
#? str()
|
||||
a
|
||||
a, b = {1: 'x', 2.0: 1j}
|
||||
#? int() float()
|
||||
a
|
||||
#? int() float()
|
||||
b
|
||||
|
||||
|
||||
def f():
|
||||
""" github #83 """
|
||||
r = {}
|
||||
@@ -232,6 +251,11 @@ dic = {str(key): ''}
|
||||
#? str()
|
||||
dic['']
|
||||
|
||||
|
||||
for x in {1: 3.0, '': 1j}:
|
||||
#? int() str()
|
||||
x
|
||||
|
||||
# -----------------
|
||||
# with variable as index
|
||||
# -----------------
|
||||
@@ -365,3 +389,45 @@ recursion1([1,2])[0]
|
||||
for x in [1] + ['']:
|
||||
#? int() str()
|
||||
x
|
||||
|
||||
# -----------------
|
||||
# For loops with attribute assignment.
|
||||
# -----------------
|
||||
def test_func():
|
||||
x = 'asdf'
|
||||
for x.something in [6,7,8]:
|
||||
pass
|
||||
#? str()
|
||||
x
|
||||
|
||||
for x.something, b in [[6, 6.0]]:
|
||||
pass
|
||||
#? str()
|
||||
x
|
||||
|
||||
|
||||
# -----------------
|
||||
# PEP 3132 Extended Iterable Unpacking (star unpacking)
|
||||
# -----------------
|
||||
|
||||
a, *b, c = [1, 'b', list, dict]
|
||||
#? int()
|
||||
a
|
||||
#? str()
|
||||
b
|
||||
#? list
|
||||
c
|
||||
|
||||
# Not valid syntax
|
||||
a, *b, *c = [1, 'd', list]
|
||||
#? int()
|
||||
a
|
||||
#? str()
|
||||
b
|
||||
#? list
|
||||
c
|
||||
|
||||
lc = [x for a, *x in [(1, '', 1.0)]]
|
||||
|
||||
#?
|
||||
lc[0][0]
|
||||
|
||||
@@ -165,6 +165,17 @@ def global_define():
|
||||
#? int()
|
||||
global_var_in_func
|
||||
|
||||
|
||||
def funct1():
|
||||
# From issue #610
|
||||
global global_dict_var
|
||||
global_dict_var = dict()
|
||||
def funct2():
|
||||
global global_dict_var
|
||||
#? dict()
|
||||
global_dict_var
|
||||
|
||||
|
||||
# -----------------
|
||||
# within docstrs
|
||||
# -----------------
|
||||
@@ -215,6 +226,9 @@ if 1:
|
||||
#? str()
|
||||
xyz
|
||||
|
||||
#?
|
||||
¹.
|
||||
|
||||
# -----------------
|
||||
# exceptions
|
||||
# -----------------
|
||||
|
||||
@@ -83,6 +83,9 @@ TestClass.var_local.
|
||||
|
||||
#? int()
|
||||
TestClass().ret(1)
|
||||
# Should not return int(), because we want the type before `.ret(1)`.
|
||||
#? 11 TestClass()
|
||||
TestClass().ret(1)
|
||||
#? int()
|
||||
inst.ret(1)
|
||||
|
||||
@@ -131,6 +134,8 @@ A().addition
|
||||
A().addition = None
|
||||
#? 8 int()
|
||||
A(1).addition = None
|
||||
#? 1 A
|
||||
A(1).addition = None
|
||||
a = A()
|
||||
#? 8 int()
|
||||
a.addition = None
|
||||
@@ -238,7 +243,10 @@ class V:
|
||||
V(1).b()
|
||||
#? int()
|
||||
V(1).c()
|
||||
#? []
|
||||
#?
|
||||
V(1).d()
|
||||
# Only keywords should be possible to complete.
|
||||
#? ['is', 'in', 'not', 'and', 'or', 'if']
|
||||
V(1).d()
|
||||
|
||||
|
||||
|
||||
@@ -30,8 +30,12 @@ a[0]
|
||||
|
||||
arr = [1,'']
|
||||
a = [a for a in arr]
|
||||
#? int() str()
|
||||
#? int()
|
||||
a[0]
|
||||
#? str()
|
||||
a[1]
|
||||
#? int() str()
|
||||
a[2]
|
||||
|
||||
a = [a if 1.0 else '' for a in [1] if [1.0]]
|
||||
#? int() str()
|
||||
@@ -44,12 +48,9 @@ left, right = [x for x in (left, right)]
|
||||
left
|
||||
|
||||
# with a dict literal
|
||||
#? str()
|
||||
#? int()
|
||||
[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:
|
||||
@@ -57,9 +58,12 @@ def listen(arg):
|
||||
x
|
||||
|
||||
listen(['' for x in [1]])
|
||||
#? str
|
||||
#?
|
||||
([str for x in []])[0]
|
||||
|
||||
# with a set literal
|
||||
#? int()
|
||||
[a for a in {1, 2, 3}][0]
|
||||
|
||||
# -----------------
|
||||
# nested list comprehensions
|
||||
@@ -93,6 +97,8 @@ left, right = (i for i in (1, ''))
|
||||
|
||||
#? int()
|
||||
left
|
||||
#? str()
|
||||
right
|
||||
|
||||
gen = (i for i in (1,))
|
||||
|
||||
@@ -110,9 +116,52 @@ next(gen)
|
||||
|
||||
# issues with different formats
|
||||
left, right = (i for i in
|
||||
('1', '2'))
|
||||
('1', 2))
|
||||
#? str()
|
||||
left
|
||||
#? int()
|
||||
right
|
||||
|
||||
# -----------------
|
||||
# dict comprehensions
|
||||
# -----------------
|
||||
|
||||
#? int()
|
||||
list({a - 1: 3 for a in [1]})[0]
|
||||
|
||||
d = {a - 1: b for a, b in {1: 'a', 3: 1.0}.items()}
|
||||
#? int()
|
||||
list(d)[0]
|
||||
#? str() float()
|
||||
d.values()[0]
|
||||
#? str()
|
||||
d[0]
|
||||
#? float() str()
|
||||
d[1]
|
||||
#? float()
|
||||
d[2]
|
||||
|
||||
# -----------------
|
||||
# set comprehensions
|
||||
# -----------------
|
||||
|
||||
#? set()
|
||||
{a - 1 for a in [1]}
|
||||
|
||||
#? set()
|
||||
{a for a in range(10)}
|
||||
|
||||
#? int()
|
||||
[x for x in {a for a in range(10)}][0]
|
||||
|
||||
#? int()
|
||||
{a for a in range(10)}.pop()
|
||||
#? float() str()
|
||||
{b for a in [[3.0], ['']] for b in a}.pop()
|
||||
|
||||
#? int()
|
||||
next(iter({a for a in range(10)}))
|
||||
|
||||
|
||||
# -----------------
|
||||
# name resolution in comprehensions.
|
||||
@@ -123,3 +172,5 @@ def x():
|
||||
#? 22
|
||||
[a for a in h if hio]
|
||||
if hio: pass
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ exe[1]
|
||||
#? set
|
||||
exe[2]
|
||||
#? []
|
||||
exe[3][0]
|
||||
exe[3][0].
|
||||
#? str()
|
||||
exe[4]['d']
|
||||
|
||||
@@ -70,7 +70,7 @@ exe[1]
|
||||
#? set
|
||||
exe[2]
|
||||
#? []
|
||||
exe[3][0]
|
||||
exe[3][0].
|
||||
#? str()
|
||||
exe[4]['d']
|
||||
|
||||
@@ -78,6 +78,9 @@ exe[4]['d']
|
||||
# -----------------
|
||||
# Decorator is a class
|
||||
# -----------------
|
||||
def same_func(func):
|
||||
return func
|
||||
|
||||
class Decorator(object):
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
@@ -94,10 +97,15 @@ nothing("")[0]
|
||||
#? str()
|
||||
nothing("")[1]
|
||||
|
||||
|
||||
@same_func
|
||||
@Decorator
|
||||
def nothing(a,b,c):
|
||||
return a,b,c
|
||||
|
||||
#? int()
|
||||
nothing("")[0]
|
||||
|
||||
class MethodDecoratorAsClass():
|
||||
class_var = 3
|
||||
@Decorator
|
||||
|
||||
@@ -6,6 +6,9 @@ Fallback to callee definition when definition not found.
|
||||
|
||||
"""Parenthesis closed at next line."""
|
||||
|
||||
# Ignore these definitions for a little while, not sure if we really want them.
|
||||
# python <= 2.5
|
||||
|
||||
#? isinstance
|
||||
isinstance(
|
||||
)
|
||||
|
||||
@@ -62,14 +62,14 @@ class B():
|
||||
p = property(t)
|
||||
|
||||
#? []
|
||||
B().r()
|
||||
B().r().
|
||||
#? int()
|
||||
B().r
|
||||
|
||||
#? str()
|
||||
B().p
|
||||
#? []
|
||||
B().p()
|
||||
B().p().
|
||||
|
||||
class PropClass():
|
||||
def __init__(self, a):
|
||||
|
||||
@@ -218,3 +218,40 @@ else:
|
||||
a = ''
|
||||
#? int()
|
||||
a
|
||||
|
||||
|
||||
# -----------------
|
||||
# Recursion issues
|
||||
# -----------------
|
||||
|
||||
def possible_recursion_error(filename):
|
||||
if filename == 'a':
|
||||
return filename
|
||||
# It seems like without the brackets there wouldn't be a RecursionError.
|
||||
elif type(filename) == str:
|
||||
return filename
|
||||
|
||||
|
||||
if NOT_DEFINED:
|
||||
s = str()
|
||||
else:
|
||||
s = str()
|
||||
#? str()
|
||||
possible_recursion_error(s)
|
||||
|
||||
|
||||
# -----------------
|
||||
# In combination with imports
|
||||
# -----------------
|
||||
|
||||
from import_tree import flow_import
|
||||
|
||||
if 1 == flow_import.env:
|
||||
a = 1
|
||||
elif 2 == flow_import.env:
|
||||
a = ''
|
||||
elif 3 == flow_import.env:
|
||||
a = 1.0
|
||||
|
||||
#? int() str()
|
||||
a
|
||||
|
||||
@@ -178,6 +178,23 @@ nested_default(a=1.0)[1]
|
||||
#? str()
|
||||
nested_default(a=1.0, b='')[1]
|
||||
|
||||
# Defaults should only work if they are defined before - not after.
|
||||
def default_function(a=default):
|
||||
#?
|
||||
return a
|
||||
|
||||
#?
|
||||
default_function()
|
||||
|
||||
default = int()
|
||||
|
||||
def default_function(a=default):
|
||||
#? int()
|
||||
return a
|
||||
|
||||
#? int()
|
||||
default_function()
|
||||
|
||||
|
||||
# -----------------
|
||||
# closures
|
||||
@@ -185,8 +202,9 @@ nested_default(a=1.0, b='')[1]
|
||||
def a():
|
||||
l = 3
|
||||
def func_b():
|
||||
#? str()
|
||||
l = ''
|
||||
#? str()
|
||||
l
|
||||
#? ['func_b']
|
||||
func_b
|
||||
#? int()
|
||||
@@ -327,6 +345,15 @@ exe[3]
|
||||
#? set
|
||||
exe[3]['c']
|
||||
|
||||
|
||||
def kwargs_iteration(**kwargs):
|
||||
return kwargs
|
||||
|
||||
for x in kwargs_iteration(d=3):
|
||||
#? float()
|
||||
{'d': 1.0, 'c': '1'}[x]
|
||||
|
||||
|
||||
# -----------------
|
||||
# nested *args
|
||||
# -----------------
|
||||
@@ -342,12 +369,12 @@ def nested_args2(*args, **kwargs):
|
||||
#? int()
|
||||
nested_args('', 1, 1.0, list)
|
||||
#? []
|
||||
nested_args('')
|
||||
nested_args('').
|
||||
|
||||
#? int()
|
||||
nested_args2('', 1, 1.0)
|
||||
#? []
|
||||
nested_args2('')
|
||||
nested_args2('').
|
||||
|
||||
# -----------------
|
||||
# nested **kwargs
|
||||
@@ -371,9 +398,9 @@ nested_kw(a=3.0, b=1)
|
||||
#? int()
|
||||
nested_kw(b=1, a=r"")
|
||||
#? []
|
||||
nested_kw(1, '')
|
||||
nested_kw(1, '').
|
||||
#? []
|
||||
nested_kw(a='')
|
||||
nested_kw(a='').
|
||||
|
||||
#? int()
|
||||
nested_kw2(b=1)
|
||||
@@ -382,9 +409,9 @@ nested_kw2(b=1, c=1.0)
|
||||
#? int()
|
||||
nested_kw2(c=1.0, b=1)
|
||||
#? []
|
||||
nested_kw2('')
|
||||
nested_kw2('').
|
||||
#? []
|
||||
nested_kw2(a='')
|
||||
nested_kw2(a='').
|
||||
#? []
|
||||
nested_kw2('', b=1).
|
||||
|
||||
@@ -404,14 +431,14 @@ nested_both('', b=1, c=1.0, list)
|
||||
nested_both('', c=1.0, b=1, list)
|
||||
|
||||
#? []
|
||||
nested_both('')
|
||||
nested_both('').
|
||||
|
||||
#? int()
|
||||
nested_both2('', b=1, c=1.0)
|
||||
#? int()
|
||||
nested_both2('', c=1.0, b=1)
|
||||
#? []
|
||||
nested_both2('')
|
||||
nested_both2('').
|
||||
|
||||
# -----------------
|
||||
# nested *args/**kwargs with a default arg
|
||||
@@ -438,7 +465,7 @@ nested_def2('', b=1, c=1.0)[1]
|
||||
#? int()
|
||||
nested_def2('', c=1.0, b=1)[1]
|
||||
#? []
|
||||
nested_def2('')[1]
|
||||
nested_def2('')[1].
|
||||
|
||||
# -----------------
|
||||
# magic methods
|
||||
|
||||
@@ -22,7 +22,7 @@ def gen_ret(value):
|
||||
next(gen_ret(1))
|
||||
|
||||
#? []
|
||||
next(gen_ret())
|
||||
next(gen_ret()).
|
||||
|
||||
# generators evaluate to true if cast by bool.
|
||||
a = ''
|
||||
@@ -42,7 +42,7 @@ def get(param):
|
||||
yield ""
|
||||
|
||||
#? []
|
||||
get()[0]
|
||||
get()[0].
|
||||
|
||||
# -----------------
|
||||
# __iter__
|
||||
@@ -131,6 +131,18 @@ def simple():
|
||||
yield ""
|
||||
|
||||
a, b = simple()
|
||||
#? int() str()
|
||||
a
|
||||
# For now this is ok.
|
||||
#?
|
||||
b
|
||||
|
||||
|
||||
def simple2():
|
||||
yield 1
|
||||
yield ""
|
||||
|
||||
a, b = simple2()
|
||||
#? int()
|
||||
a
|
||||
#? str()
|
||||
@@ -163,3 +175,25 @@ gen().send()
|
||||
|
||||
#?
|
||||
gen()()
|
||||
|
||||
# -----------------
|
||||
# yield from
|
||||
# -----------------
|
||||
|
||||
# python >= 3.3
|
||||
|
||||
def yield_from():
|
||||
yield from iter([1])
|
||||
|
||||
#? int()
|
||||
next(yield_from())
|
||||
|
||||
def yield_from_multiple():
|
||||
yield from iter([1])
|
||||
yield str()
|
||||
|
||||
x, y = yield_from_multiple()
|
||||
#? int()
|
||||
x
|
||||
#? str()
|
||||
y
|
||||
|
||||
@@ -30,6 +30,12 @@ b = math
|
||||
#! ['b = math']
|
||||
b
|
||||
|
||||
#! 18 ['foo = 10']
|
||||
foo = 10;print(foo)
|
||||
|
||||
# -----------------
|
||||
# classes
|
||||
# -----------------
|
||||
class C(object):
|
||||
def b(self):
|
||||
#! ['b = math']
|
||||
|
||||
4
test/completion/import_tree/flow_import.py
Normal file
4
test/completion/import_tree/flow_import.py
Normal file
@@ -0,0 +1,4 @@
|
||||
if name:
|
||||
env = 1
|
||||
else:
|
||||
env = 2
|
||||
@@ -49,6 +49,8 @@ def scope_nested():
|
||||
|
||||
#? float()
|
||||
import_tree.pkg.mod1.a
|
||||
#? ['a', '__name__', '__package__', '__file__', '__doc__']
|
||||
a = import_tree.pkg.mod1.
|
||||
|
||||
import import_tree.random
|
||||
#? set
|
||||
@@ -75,6 +77,7 @@ def scope_from_import_variable():
|
||||
without the use of ``sys.modules`` modifications (e.g. ``os.path`` see also
|
||||
github issue #213 for clarification.
|
||||
"""
|
||||
a = 3
|
||||
#?
|
||||
from import_tree.mod2.fake import a
|
||||
#?
|
||||
|
||||
@@ -31,7 +31,8 @@ def wrong_indents():
|
||||
asdf = 3
|
||||
asdf
|
||||
asdf(
|
||||
#? int()
|
||||
# TODO this seems to be wrong now?
|
||||
##? int()
|
||||
asdf
|
||||
def openbrace():
|
||||
asdf = 3
|
||||
@@ -101,7 +102,7 @@ if isi
|
||||
try:
|
||||
except TypeError:
|
||||
#? str()
|
||||
""
|
||||
str()
|
||||
|
||||
def break(): pass
|
||||
# wrong ternary expression
|
||||
@@ -175,16 +176,16 @@ import datetime as
|
||||
|
||||
call = ''
|
||||
invalid = .call
|
||||
#?
|
||||
#?
|
||||
invalid
|
||||
|
||||
invalid = call?.call
|
||||
#?
|
||||
#? str()
|
||||
invalid
|
||||
|
||||
# comma
|
||||
invalid = ,call
|
||||
#?
|
||||
#? str()
|
||||
invalid
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,14 @@ if 2:
|
||||
#? str()
|
||||
ass
|
||||
|
||||
# -----------------
|
||||
# invalid arguments
|
||||
# -----------------
|
||||
|
||||
if isinstance(wrong, str()):
|
||||
#?
|
||||
wrong
|
||||
|
||||
# -----------------
|
||||
# in functions
|
||||
# -----------------
|
||||
@@ -55,6 +63,17 @@ a
|
||||
fooooo2('')
|
||||
|
||||
|
||||
def isinstance_func(arr):
|
||||
for value in arr:
|
||||
if isinstance(value, dict):
|
||||
# Shouldn't fail, even with the dot.
|
||||
#? 17 dict()
|
||||
value.
|
||||
elif isinstance(value, int):
|
||||
x = value
|
||||
#? int()
|
||||
x
|
||||
|
||||
# -----------------
|
||||
# Names with multiple indices.
|
||||
# -----------------
|
||||
@@ -72,3 +91,10 @@ class Test():
|
||||
self.testing
|
||||
#? Test()
|
||||
self
|
||||
|
||||
# -----------------
|
||||
# Syntax
|
||||
# -----------------
|
||||
|
||||
#?
|
||||
isinstance(1, int())
|
||||
|
||||
@@ -2,5 +2,58 @@
|
||||
#? ['raise']
|
||||
raise
|
||||
|
||||
#? ['except', 'Exception']
|
||||
#? ['Exception']
|
||||
except
|
||||
|
||||
#? []
|
||||
b + continu
|
||||
|
||||
#? []
|
||||
b + continue
|
||||
|
||||
#? ['continue']
|
||||
b; continue
|
||||
|
||||
#? ['continue']
|
||||
b; continu
|
||||
|
||||
#? []
|
||||
c + brea
|
||||
|
||||
#? []
|
||||
a + break
|
||||
|
||||
#? ['break']
|
||||
b; break
|
||||
|
||||
# -----------------
|
||||
# Keywords should not appear everywhere.
|
||||
# -----------------
|
||||
|
||||
#? []
|
||||
with open() as f
|
||||
#? []
|
||||
def i
|
||||
#? []
|
||||
class i
|
||||
|
||||
#? []
|
||||
continue i
|
||||
|
||||
# More syntax details, e.g. while only after newline, but not after semicolon,
|
||||
# continue also after semicolon
|
||||
#? ['while']
|
||||
while
|
||||
#? []
|
||||
x while
|
||||
#? []
|
||||
x; while
|
||||
#? ['continue']
|
||||
x; continue
|
||||
|
||||
#? []
|
||||
and
|
||||
#? ['and']
|
||||
x and
|
||||
#? []
|
||||
x * and
|
||||
|
||||
@@ -20,3 +20,12 @@ a(some_args)
|
||||
|
||||
#? 13 []
|
||||
a(some_kwargs)
|
||||
|
||||
def multiple(foo, bar):
|
||||
pass
|
||||
|
||||
#? 17 ['bar']
|
||||
multiple(foo, bar)
|
||||
|
||||
#? ['bar']
|
||||
multiple(foo, bar
|
||||
|
||||
@@ -63,9 +63,13 @@ import datetime.date
|
||||
|
||||
#? 21 ['import']
|
||||
from import_tree.pkg import pkg
|
||||
#? 49 ['a', '__name__', '__doc__', '__file__', '__package__']
|
||||
from import_tree.pkg.mod1 import not_existant, # whitespace before
|
||||
#? ['a', '__name__', '__doc__', '__file__', '__package__']
|
||||
from import_tree.pkg.mod1 import not_existant,
|
||||
#? 22 ['mod1']
|
||||
from import_tree.pkg. import mod1
|
||||
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'recurse_class1', 'recurse_class2', 'invisible_pkg']
|
||||
#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'recurse_class1', 'recurse_class2', 'invisible_pkg', 'flow_import']
|
||||
from import_tree. import pkg
|
||||
|
||||
#? 18 ['pkg']
|
||||
|
||||
@@ -87,7 +87,7 @@ from os import path
|
||||
# should not return a function, because `a` is a function above
|
||||
def f(b, a): return a
|
||||
#? []
|
||||
f(b=3)
|
||||
f(b=3).
|
||||
|
||||
# -----------------
|
||||
# closure
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Issues with the parser not the completion engine should be here.
|
||||
Issues with the parser and not the type inference should be part of this file.
|
||||
"""
|
||||
|
||||
class IndentIssues():
|
||||
@@ -32,5 +32,12 @@ Just because there's a def keyword, doesn't mean it should not be able to
|
||||
complete to definition.
|
||||
"""
|
||||
definition = 0
|
||||
#? ['definition', 'def']
|
||||
#? ['definition']
|
||||
str(def
|
||||
|
||||
|
||||
# It might be hard to determine the context
|
||||
class Foo(object):
|
||||
@property
|
||||
#? ['str']
|
||||
def bar(str
|
||||
|
||||
160
test/completion/pep0484.py
Normal file
160
test/completion/pep0484.py
Normal file
@@ -0,0 +1,160 @@
|
||||
""" Pep-0484 type hinting """
|
||||
|
||||
# python >= 3.2
|
||||
|
||||
|
||||
class A():
|
||||
pass
|
||||
|
||||
|
||||
def function_parameters(a: A, b, c: str, d: int, e: str, f: str, g: int=4):
|
||||
"""
|
||||
:param e: if docstring and annotation agree, only one should be returned
|
||||
:type e: str
|
||||
:param f: if docstring and annotation disagree, both should be returned
|
||||
:type f: int
|
||||
"""
|
||||
#? A()
|
||||
a
|
||||
#?
|
||||
b
|
||||
#? str()
|
||||
c
|
||||
#? int()
|
||||
d
|
||||
#? str()
|
||||
e
|
||||
#? int() str()
|
||||
f
|
||||
# int()
|
||||
g
|
||||
|
||||
|
||||
def return_unspecified():
|
||||
pass
|
||||
|
||||
#?
|
||||
return_unspecified()
|
||||
|
||||
|
||||
def return_none() -> None:
|
||||
"""
|
||||
Return type None means the same as no return type as far as jedi
|
||||
is concerned
|
||||
"""
|
||||
pass
|
||||
|
||||
#?
|
||||
return_none()
|
||||
|
||||
|
||||
def return_str() -> str:
|
||||
pass
|
||||
|
||||
#? str()
|
||||
return_str()
|
||||
|
||||
|
||||
def return_custom_class() -> A:
|
||||
pass
|
||||
|
||||
#? A()
|
||||
return_custom_class()
|
||||
|
||||
|
||||
def return_annotation_and_docstring() -> str:
|
||||
"""
|
||||
:rtype: int
|
||||
"""
|
||||
pass
|
||||
|
||||
#? str() int()
|
||||
return_annotation_and_docstring()
|
||||
|
||||
|
||||
def return_annotation_and_docstring_different() -> str:
|
||||
"""
|
||||
:rtype: str
|
||||
"""
|
||||
pass
|
||||
|
||||
#? str()
|
||||
return_annotation_and_docstring_different()
|
||||
|
||||
|
||||
def annotation_forward_reference(b: "B") -> "B":
|
||||
#? B()
|
||||
b
|
||||
|
||||
#? ["test_element"]
|
||||
annotation_forward_reference(1).t
|
||||
|
||||
class B:
|
||||
test_element = 1
|
||||
pass
|
||||
|
||||
#? B()
|
||||
annotation_forward_reference(1)
|
||||
|
||||
|
||||
class SelfReference:
|
||||
test_element = 1
|
||||
def test_method(self, x: "SelfReference") -> "SelfReference":
|
||||
#? SelfReference()
|
||||
x
|
||||
#? ["test_element", "test_method"]
|
||||
self.t
|
||||
#? ["test_element", "test_method"]
|
||||
x.t
|
||||
#? ["test_element", "test_method"]
|
||||
self.test_method(1).t
|
||||
|
||||
#? SelfReference()
|
||||
SelfReference().test_method()
|
||||
|
||||
def function_with_non_pep_0484_annotation(
|
||||
x: "I can put anything here",
|
||||
xx: "",
|
||||
yy: "\r\n\0;+*&^564835(---^&*34",
|
||||
y: 3 + 3,
|
||||
zz: float) -> int("42"):
|
||||
# infers int from function call
|
||||
#? int()
|
||||
x
|
||||
# infers int from function call
|
||||
#? int()
|
||||
xx
|
||||
# infers int from function call
|
||||
#? int()
|
||||
yy
|
||||
# infers str from function call
|
||||
#? str()
|
||||
y
|
||||
#? float()
|
||||
zz
|
||||
#?
|
||||
function_with_non_pep_0484_annotation(1, 2, 3, "force string")
|
||||
|
||||
def function_forward_reference_dynamic(
|
||||
x: return_str_type(),
|
||||
y: "return_str_type()") -> None:
|
||||
#?
|
||||
x
|
||||
#? str()
|
||||
y
|
||||
|
||||
def return_str_type():
|
||||
return str
|
||||
|
||||
|
||||
X = str
|
||||
def function_with_assined_class_in_reference(x: X, y: "Y"):
|
||||
#? str()
|
||||
x
|
||||
#? int()
|
||||
y
|
||||
Y = int
|
||||
|
||||
def just_because_we_can(x: "flo" + "at"):
|
||||
#? float()
|
||||
x
|
||||
109
test/completion/pep0484_comments.py
Normal file
109
test/completion/pep0484_comments.py
Normal file
@@ -0,0 +1,109 @@
|
||||
a = 3 # type: str
|
||||
#? str()
|
||||
a
|
||||
|
||||
b = 3 # type: str but I write more
|
||||
#? int()
|
||||
b
|
||||
|
||||
c = 3 # type: str # I comment more
|
||||
#? str()
|
||||
c
|
||||
|
||||
d = "It should not read comments from the next line"
|
||||
# type: int
|
||||
#? str()
|
||||
d
|
||||
|
||||
# type: int
|
||||
e = "It should not read comments from the previous line"
|
||||
#? str()
|
||||
e
|
||||
|
||||
class BB: pass
|
||||
|
||||
def test(a, b):
|
||||
a = a # type: BB
|
||||
c = a # type: str
|
||||
d = a
|
||||
# type: str
|
||||
e = a # type: str # Should ignore long whitespace
|
||||
|
||||
#? BB()
|
||||
a
|
||||
#? str()
|
||||
c
|
||||
#? BB()
|
||||
d
|
||||
#? str()
|
||||
e
|
||||
|
||||
a,b = 1, 2 # type: str, float
|
||||
#? str()
|
||||
a
|
||||
#? float()
|
||||
b
|
||||
|
||||
class Employee:
|
||||
pass
|
||||
|
||||
# The typing library is not installable for Python 2.6, therefore ignore the
|
||||
# following tests.
|
||||
# python >= 2.7
|
||||
|
||||
from typing import List
|
||||
x = [] # type: List[Employee]
|
||||
#? Employee()
|
||||
x[1]
|
||||
x, y, z = [], [], [] # type: List[int], List[int], List[str]
|
||||
#? int()
|
||||
y[2]
|
||||
x, y, z = [], [], [] # type: (List[float], List[float], List[BB])
|
||||
for zi in z:
|
||||
#? BB()
|
||||
zi
|
||||
|
||||
x = [
|
||||
1,
|
||||
2,
|
||||
] # type: List[str]
|
||||
|
||||
#? str()
|
||||
x[1]
|
||||
|
||||
|
||||
for bar in foo(): # type: str
|
||||
#? str()
|
||||
bar
|
||||
|
||||
for bar, baz in foo(): # type: int, float
|
||||
#? int()
|
||||
bar
|
||||
#? float()
|
||||
baz
|
||||
|
||||
for bar, baz in foo():
|
||||
# type: str, str
|
||||
""" type hinting on next line should not work """
|
||||
#?
|
||||
bar
|
||||
#?
|
||||
baz
|
||||
|
||||
with foo(): # type: int
|
||||
...
|
||||
|
||||
with foo() as f: # type: str
|
||||
#? str()
|
||||
f
|
||||
|
||||
with foo() as f:
|
||||
# type: str
|
||||
""" type hinting on next line should not work """
|
||||
#?
|
||||
f
|
||||
|
||||
aaa = some_extremely_long_function_name_that_doesnt_leave_room_for_hints() \
|
||||
# type: float # We should be able to put hints on the next line with a \
|
||||
#? float()
|
||||
aaa
|
||||
263
test/completion/pep0484_typing.py
Normal file
263
test/completion/pep0484_typing.py
Normal file
@@ -0,0 +1,263 @@
|
||||
"""
|
||||
Test the typing library, with docstrings. This is needed since annotations
|
||||
are not supported in python 2.7 else then annotating by comment (and this is
|
||||
still TODO at 2016-01-23)
|
||||
"""
|
||||
# There's no Python 2.6 typing module.
|
||||
# python >= 2.7
|
||||
import typing
|
||||
class B:
|
||||
pass
|
||||
|
||||
def we_can_has_sequence(p, q, r, s, t, u):
|
||||
"""
|
||||
:type p: typing.Sequence[int]
|
||||
:type q: typing.Sequence[B]
|
||||
:type r: typing.Sequence[int]
|
||||
:type s: typing.Sequence["int"]
|
||||
:type t: typing.MutableSequence[dict]
|
||||
:type u: typing.List[float]
|
||||
"""
|
||||
#? ["count"]
|
||||
p.c
|
||||
#? int()
|
||||
p[1]
|
||||
#? ["count"]
|
||||
q.c
|
||||
#? B()
|
||||
q[1]
|
||||
#? ["count"]
|
||||
r.c
|
||||
#? int()
|
||||
r[1]
|
||||
#? ["count"]
|
||||
s.c
|
||||
#? int()
|
||||
s[1]
|
||||
#? []
|
||||
s.a
|
||||
#? ["append"]
|
||||
t.a
|
||||
#? dict()
|
||||
t[1]
|
||||
#? ["append"]
|
||||
u.a
|
||||
#? float()
|
||||
u[1]
|
||||
|
||||
def iterators(ps, qs, rs, ts):
|
||||
"""
|
||||
:type ps: typing.Iterable[int]
|
||||
:type qs: typing.Iterator[str]
|
||||
:type rs: typing.Sequence["ForwardReference"]
|
||||
:type ts: typing.AbstractSet["float"]
|
||||
"""
|
||||
for p in ps:
|
||||
#? int()
|
||||
p
|
||||
#?
|
||||
next(ps)
|
||||
a, b = ps
|
||||
#? int()
|
||||
a
|
||||
##? int() --- TODO fix support for tuple assignment
|
||||
# https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854
|
||||
# test below is just to make sure that in case it gets fixed by accident
|
||||
# these tests will be fixed as well the way they should be
|
||||
#?
|
||||
b
|
||||
|
||||
for q in qs:
|
||||
#? str()
|
||||
q
|
||||
#? str()
|
||||
next(qs)
|
||||
for r in rs:
|
||||
#? ForwardReference()
|
||||
r
|
||||
#?
|
||||
next(rs)
|
||||
for t in ts:
|
||||
#? float()
|
||||
t
|
||||
|
||||
def sets(p, q):
|
||||
"""
|
||||
:type p: typing.AbstractSet[int]
|
||||
:type q: typing.MutableSet[float]
|
||||
"""
|
||||
#? []
|
||||
p.a
|
||||
#? ["add"]
|
||||
q.a
|
||||
|
||||
def tuple(p, q, r):
|
||||
"""
|
||||
:type p: typing.Tuple[int]
|
||||
:type q: typing.Tuple[int, str, float]
|
||||
:type r: typing.Tuple[B, ...]
|
||||
"""
|
||||
#? int()
|
||||
p[0]
|
||||
#? int()
|
||||
q[0]
|
||||
#? str()
|
||||
q[1]
|
||||
#? float()
|
||||
q[2]
|
||||
#? B()
|
||||
r[0]
|
||||
#? B()
|
||||
r[1]
|
||||
#? B()
|
||||
r[2]
|
||||
#? B()
|
||||
r[10000]
|
||||
i, s, f = q
|
||||
#? int()
|
||||
i
|
||||
##? str() --- TODO fix support for tuple assignment
|
||||
# https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854
|
||||
#?
|
||||
s
|
||||
##? float() --- TODO fix support for tuple assignment
|
||||
# https://github.com/davidhalter/jedi/pull/663#issuecomment-172317854
|
||||
#?
|
||||
f
|
||||
|
||||
class Key:
|
||||
pass
|
||||
|
||||
class Value:
|
||||
pass
|
||||
|
||||
def mapping(p, q, d, r, s, t):
|
||||
"""
|
||||
:type p: typing.Mapping[Key, Value]
|
||||
:type q: typing.MutableMapping[Key, Value]
|
||||
:type d: typing.Dict[Key, Value]
|
||||
:type r: typing.KeysView[Key]
|
||||
:type s: typing.ValuesView[Value]
|
||||
:type t: typing.ItemsView[Key, Value]
|
||||
"""
|
||||
#? []
|
||||
p.setd
|
||||
#? ["setdefault"]
|
||||
q.setd
|
||||
#? ["setdefault"]
|
||||
d.setd
|
||||
#? Value()
|
||||
p[1]
|
||||
for key in p:
|
||||
#? Key()
|
||||
key
|
||||
for key in p.keys():
|
||||
#? Key()
|
||||
key
|
||||
for value in p.values():
|
||||
#? Value()
|
||||
value
|
||||
for item in p.items():
|
||||
#? Key()
|
||||
item[0]
|
||||
#? Value()
|
||||
item[1]
|
||||
(key, value) = item
|
||||
#? Key()
|
||||
key
|
||||
#? Value()
|
||||
value
|
||||
for key, value in p.items():
|
||||
#? Key()
|
||||
key
|
||||
#? Value()
|
||||
value
|
||||
for key in r:
|
||||
#? Key()
|
||||
key
|
||||
for value in s:
|
||||
#? Value()
|
||||
value
|
||||
for key, value in t:
|
||||
#? Key()
|
||||
key
|
||||
#? Value()
|
||||
value
|
||||
|
||||
def union(p, q, r, s, t):
|
||||
"""
|
||||
:type p: typing.Union[int]
|
||||
:type q: typing.Union[int, int]
|
||||
:type r: typing.Union[int, str, "int"]
|
||||
:type s: typing.Union[int, typing.Union[str, "typing.Union['float', 'dict']"]]
|
||||
:type t: typing.Union[int, None]
|
||||
"""
|
||||
#? int()
|
||||
p
|
||||
#? int()
|
||||
q
|
||||
#? int() str()
|
||||
r
|
||||
#? int() str() float() dict()
|
||||
s
|
||||
#? int()
|
||||
t
|
||||
|
||||
def optional(p):
|
||||
"""
|
||||
:type p: typing.Optional[int]
|
||||
Optional does not do anything special. However it should be recognised
|
||||
as being of that type. Jedi doesn't do anything with the extra into that
|
||||
it can be None as well
|
||||
"""
|
||||
#? int()
|
||||
p
|
||||
|
||||
class ForwardReference:
|
||||
pass
|
||||
|
||||
class TestDict(typing.Dict[str, int]):
|
||||
def setdud(self):
|
||||
pass
|
||||
|
||||
def testdict(x):
|
||||
"""
|
||||
:type x: TestDict
|
||||
"""
|
||||
#? ["setdud", "setdefault"]
|
||||
x.setd
|
||||
for key in x.keys():
|
||||
#? str()
|
||||
key
|
||||
for value in x.values():
|
||||
#? int()
|
||||
value
|
||||
|
||||
x = TestDict()
|
||||
#? ["setdud", "setdefault"]
|
||||
x.setd
|
||||
for key in x.keys():
|
||||
#? str()
|
||||
key
|
||||
for value in x.values():
|
||||
#? int()
|
||||
value
|
||||
# python >= 3.2
|
||||
"""
|
||||
docstrings have some auto-import, annotations can use all of Python's
|
||||
import logic
|
||||
"""
|
||||
import typing as t
|
||||
def union2(x: t.Union[int, str]):
|
||||
#? int() str()
|
||||
x
|
||||
|
||||
from typing import Union
|
||||
def union3(x: Union[int, str]):
|
||||
#? int() str()
|
||||
x
|
||||
|
||||
from typing import Union as U
|
||||
def union4(x: U[int, str]):
|
||||
#? int() str()
|
||||
x
|
||||
@@ -48,6 +48,14 @@ a = 3 * "a"
|
||||
#? str()
|
||||
a
|
||||
|
||||
a = 3 * "a"
|
||||
#? str()
|
||||
a
|
||||
|
||||
#? int()
|
||||
(3 ** 3)
|
||||
#? int() str()
|
||||
(3 ** 'a')
|
||||
|
||||
# -----------------
|
||||
# assignments
|
||||
|
||||
@@ -35,6 +35,45 @@ next(open(''))
|
||||
#? ['__itemsize__']
|
||||
tuple.__itemsize__
|
||||
|
||||
# -----------------
|
||||
# type() calls with one parameter
|
||||
# -----------------
|
||||
#? int
|
||||
type(1)
|
||||
#? int
|
||||
type(int())
|
||||
#? type
|
||||
type(int)
|
||||
#? type
|
||||
type(type)
|
||||
#? list
|
||||
type([])
|
||||
|
||||
def x():
|
||||
yield 1
|
||||
generator = type(x())
|
||||
#? generator
|
||||
type(x for x in [])
|
||||
#? type(x)
|
||||
type(lambda: x)
|
||||
|
||||
import math
|
||||
import os
|
||||
#? type(os)
|
||||
type(math)
|
||||
class X(): pass
|
||||
#? type
|
||||
type(X)
|
||||
|
||||
# -----------------
|
||||
# enumerate
|
||||
# -----------------
|
||||
for i, j in enumerate(["as", "ad"]):
|
||||
#? int()
|
||||
i
|
||||
#? str()
|
||||
j
|
||||
|
||||
# -----------------
|
||||
# re
|
||||
# -----------------
|
||||
@@ -173,3 +212,13 @@ class B(object):
|
||||
cls = random.choice([A, B])
|
||||
#? ['say', 'shout']
|
||||
cls().s
|
||||
|
||||
# -----------------
|
||||
# random
|
||||
# -----------------
|
||||
|
||||
import zipfile
|
||||
z = zipfile.ZipFile("foo")
|
||||
# It's too slow. So we don't run it at the moment.
|
||||
##? ['upper']
|
||||
z.read('name').upper
|
||||
|
||||
@@ -79,6 +79,18 @@ dic2.popitem
|
||||
#? int()
|
||||
dic2['asdf']
|
||||
|
||||
d = {'a': 3, 1.0: list}
|
||||
|
||||
#? int() list
|
||||
d.values()[0]
|
||||
##? int() list
|
||||
dict(d).values()[0]
|
||||
|
||||
#? str()
|
||||
d.items()[0][0]
|
||||
#? int()
|
||||
d.items()[0][1]
|
||||
|
||||
# -----------------
|
||||
# set
|
||||
# -----------------
|
||||
|
||||
@@ -3,10 +3,10 @@ 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), (17,0)
|
||||
#< 4 (0,4), (3,0), (5,0), (17,0), (12,4), (14,5), (15,0)
|
||||
def abc(): pass
|
||||
|
||||
#< 0 (-3,4), (0,0), (2,0), (14,0)
|
||||
#< 0 (-3,4), (0,0), (2,0), (14,0), (9,4), (11,5), (12,0)
|
||||
abc.d.a.bsaasd.abc.d
|
||||
|
||||
abc
|
||||
@@ -20,7 +20,7 @@ if 1:
|
||||
else:
|
||||
(abc) =
|
||||
abc =
|
||||
#< (-17,4), (-14,0), (-12,0), (0,0)
|
||||
#< (-17,4), (-14,0), (-12,0), (0,0), (-2,0), (-3,5), (-5,4)
|
||||
abc
|
||||
|
||||
abc = 5
|
||||
@@ -83,7 +83,7 @@ import module_not_exists
|
||||
module_not_exists
|
||||
|
||||
|
||||
#< ('rename1', 1,0), (0,24), (3,0), (6,17), ('rename2', 4,5), (10,17), (13,17), ('imports', 70, 16)
|
||||
#< ('rename1', 1,0), (0,24), (3,0), (6,17), ('rename2', 4,5), (10,17), (13,17), ('imports', 72, 16)
|
||||
from import_tree import rename1
|
||||
|
||||
#< (0,8), ('rename1',3,0), ('rename2',4,20), ('rename2',6,0), (3,32), (7,32), (4,0)
|
||||
@@ -93,7 +93,7 @@ rename1.abc
|
||||
from import_tree.rename1 import abc
|
||||
abc
|
||||
|
||||
#< 20 ('rename1', 1,0), ('rename2', 4,5), (-10,24), (-7,0), (-4,17), (0,17), (3,17), ('imports', 70, 16)
|
||||
#< 20 ('rename1', 1,0), ('rename2', 4,5), (-10,24), (-7,0), (-4,17), (0,17), (3,17), ('imports', 72, 16)
|
||||
from import_tree.rename1 import abc
|
||||
|
||||
#< (0, 32),
|
||||
|
||||
@@ -6,8 +6,10 @@ import pytest
|
||||
from . import helpers
|
||||
from . import run
|
||||
from . import refactor
|
||||
|
||||
import jedi
|
||||
from jedi.evaluate.analysis import Warning
|
||||
from jedi import settings
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
@@ -88,11 +90,15 @@ class StaticAnalysisCase(object):
|
||||
The tests also start with `#!`, like the goto_definition tests.
|
||||
"""
|
||||
def __init__(self, path):
|
||||
self.skip = False
|
||||
self._path = path
|
||||
with open(path) as f:
|
||||
self._source = f.read()
|
||||
|
||||
self.skip = False
|
||||
for line in self._source.splitlines():
|
||||
self.skip = self.skip or run.skip_python_version(line)
|
||||
|
||||
|
||||
def collect_comparison(self):
|
||||
cases = []
|
||||
for line_nr, line in enumerate(self._source.splitlines(), 1):
|
||||
@@ -121,5 +127,4 @@ def isolated_jedi_cache(monkeypatch, tmpdir):
|
||||
Same as `clean_jedi_cache`, but create the temporary directory for
|
||||
each test case (scope='function').
|
||||
"""
|
||||
from jedi import settings
|
||||
monkeypatch.setattr(settings, 'cache_directory', str(tmpdir))
|
||||
|
||||
116
test/run.py
116
test/run.py
@@ -54,7 +54,7 @@ Alternate Test Runner
|
||||
If you don't like the output of ``py.test``, there's an alternate test runner
|
||||
that you can start by running ``./run.py``. The above example could be run by::
|
||||
|
||||
./run.py basic 4 6 8
|
||||
./run.py basic 4 6 8 50-80
|
||||
|
||||
The advantage of this runner is simplicity and more customized error reports.
|
||||
Using both runners will help you to have a quicker overview of what's
|
||||
@@ -111,12 +111,16 @@ Tests look like this::
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import operator
|
||||
from ast import literal_eval
|
||||
from io import StringIO
|
||||
from functools import reduce
|
||||
|
||||
import jedi
|
||||
from jedi._compatibility import unicode, is_py3
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.api.classes import Definition
|
||||
|
||||
|
||||
TEST_COMPLETIONS = 0
|
||||
@@ -127,7 +131,7 @@ TEST_USAGES = 3
|
||||
|
||||
class IntegrationTestCase(object):
|
||||
def __init__(self, test_type, correct, line_nr, column, start, line,
|
||||
path=None):
|
||||
path=None, skip=None):
|
||||
self.test_type = test_type
|
||||
self.correct = correct
|
||||
self.line_nr = line_nr
|
||||
@@ -135,7 +139,7 @@ class IntegrationTestCase(object):
|
||||
self.start = start
|
||||
self.line = line
|
||||
self.path = path
|
||||
self.skip = None
|
||||
self.skip = skip
|
||||
|
||||
@property
|
||||
def module_name(self):
|
||||
@@ -170,36 +174,35 @@ class IntegrationTestCase(object):
|
||||
return compare_cb(self, comp_str, set(literal_eval(self.correct)))
|
||||
|
||||
def run_goto_definitions(self, compare_cb):
|
||||
script = self.script()
|
||||
evaluator = script._evaluator
|
||||
|
||||
def comparison(definition):
|
||||
suffix = '()' if definition.type == 'instance' else ''
|
||||
return definition.desc_with_module + suffix
|
||||
|
||||
def definition(correct, correct_start, path):
|
||||
def defs(line_nr, indent):
|
||||
s = jedi.Script(self.source, line_nr, indent, path)
|
||||
return set(s.goto_definitions())
|
||||
|
||||
should_be = set()
|
||||
number = 0
|
||||
for index in re.finditer('(?:[^ ]+)', correct):
|
||||
end = index.end()
|
||||
# +3 because of the comment start `#? `
|
||||
end += 3
|
||||
number += 1
|
||||
try:
|
||||
should_be |= defs(self.line_nr - 1, end + correct_start)
|
||||
except Exception:
|
||||
print('could not resolve %s indent %s'
|
||||
% (self.line_nr - 1, end))
|
||||
raise
|
||||
# because the objects have different ids, `repr`, then compare.
|
||||
for match in re.finditer('(?:[^ ]+)', correct):
|
||||
string = match.group(0)
|
||||
parser = Parser(load_grammar(), string, start_symbol='eval_input')
|
||||
parser.position_modifier.line = self.line_nr
|
||||
element = parser.get_parsed_node()
|
||||
element.parent = jedi.api.completion.get_user_scope(
|
||||
script._get_module(),
|
||||
(self.line_nr, self.column)
|
||||
)
|
||||
results = evaluator.eval_element(element)
|
||||
if not results:
|
||||
raise Exception('Could not resolve %s on line %s'
|
||||
% (match.string, self.line_nr - 1))
|
||||
|
||||
should_be |= set(Definition(evaluator, r) for r in results)
|
||||
|
||||
# Because the objects have different ids, `repr`, then compare.
|
||||
should = set(comparison(r) for r in should_be)
|
||||
if len(should) < number:
|
||||
raise Exception('Solution @%s not right, too few test results: %s'
|
||||
% (self.line_nr - 1, should))
|
||||
return should
|
||||
|
||||
script = self.script()
|
||||
should = definition(self.correct, self.start, script.path)
|
||||
result = script.goto_definitions()
|
||||
is_str = set(comparison(r) for r in result)
|
||||
@@ -232,12 +235,35 @@ class IntegrationTestCase(object):
|
||||
return compare_cb(self, compare, sorted(wanted))
|
||||
|
||||
|
||||
def collect_file_tests(lines, lines_to_execute):
|
||||
makecase = lambda t: IntegrationTestCase(t, correct, line_nr, column,
|
||||
start, line)
|
||||
def skip_python_version(line):
|
||||
comp_map = {
|
||||
'==': 'eq',
|
||||
'<=': 'le',
|
||||
'>=': 'ge',
|
||||
'<': 'lt',
|
||||
'>': 'gt',
|
||||
}
|
||||
# check for python minimal version number
|
||||
match = re.match(r" *# *python *([<>]=?|==) *(\d+(?:\.\d+)?)$", line)
|
||||
if match:
|
||||
minimal_python_version = tuple(
|
||||
map(int, match.group(2).split(".")))
|
||||
operation = getattr(operator, comp_map[match.group(1)])
|
||||
if not operation(sys.version_info, minimal_python_version):
|
||||
return "Minimal python version %s %s" % (match.group(1), match.group(2))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def collect_file_tests(path, lines, lines_to_execute):
|
||||
def makecase(t):
|
||||
return IntegrationTestCase(t, correct, line_nr, column,
|
||||
start, line, path=path, skip=skip)
|
||||
|
||||
start = None
|
||||
correct = None
|
||||
test_type = None
|
||||
skip = None
|
||||
for line_nr, line in enumerate(lines, 1):
|
||||
if correct is not None:
|
||||
r = re.match('^(\d+)\s*(.*)$', correct)
|
||||
@@ -257,6 +283,7 @@ def collect_file_tests(lines, lines_to_execute):
|
||||
yield makecase(TEST_DEFINITIONS)
|
||||
correct = None
|
||||
else:
|
||||
skip = skip or skip_python_version(line)
|
||||
try:
|
||||
r = re.search(r'(?:^|(?<=\s))#([?!<])\s*([^\n]*)', line)
|
||||
# test_type is ? for completion and ! for goto_assignments
|
||||
@@ -269,9 +296,14 @@ def collect_file_tests(lines, lines_to_execute):
|
||||
except AttributeError:
|
||||
correct = None
|
||||
else:
|
||||
# skip the test, if this is not specified test
|
||||
if lines_to_execute and line_nr not in lines_to_execute:
|
||||
correct = None
|
||||
# Skip the test, if this is not specified test.
|
||||
for l in lines_to_execute:
|
||||
if isinstance(l, tuple) and l[0] <= line_nr <= l[1] \
|
||||
or line_nr == l:
|
||||
break
|
||||
else:
|
||||
if lines_to_execute:
|
||||
correct = None
|
||||
|
||||
|
||||
def collect_dir_tests(base_dir, test_files, check_thirdparty=False):
|
||||
@@ -290,12 +322,14 @@ def collect_dir_tests(base_dir, test_files, check_thirdparty=False):
|
||||
skip = 'Thirdparty-Library %s not found.' % lib
|
||||
|
||||
path = os.path.join(base_dir, f_name)
|
||||
source = open(path).read()
|
||||
if not is_py3:
|
||||
source = unicode(source, 'UTF-8')
|
||||
for case in collect_file_tests(StringIO(source),
|
||||
|
||||
if is_py3:
|
||||
source = open(path, encoding='utf-8').read()
|
||||
else:
|
||||
source = unicode(open(path).read(), 'UTF-8')
|
||||
|
||||
for case in collect_file_tests(path, StringIO(source),
|
||||
lines_to_execute):
|
||||
case.path = path
|
||||
case.source = source
|
||||
if skip:
|
||||
case.skip = skip
|
||||
@@ -335,7 +369,11 @@ if __name__ == '__main__':
|
||||
test_files = {}
|
||||
last = None
|
||||
for arg in arguments['<rest>']:
|
||||
if arg.isdigit():
|
||||
match = re.match('(\d+)-(\d+)', arg)
|
||||
if match:
|
||||
start, end = match.groups()
|
||||
test_files[last].append((int(start), int(end)))
|
||||
elif arg.isdigit():
|
||||
if last is None:
|
||||
continue
|
||||
test_files[last].append(int(arg))
|
||||
@@ -344,7 +382,9 @@ if __name__ == '__main__':
|
||||
last = arg
|
||||
|
||||
# completion tests:
|
||||
completion_test_dir = '../test/completion'
|
||||
dir_ = os.path.dirname(os.path.realpath(__file__))
|
||||
completion_test_dir = os.path.join(dir_, '../test/completion')
|
||||
completion_test_dir = os.path.abspath(completion_test_dir)
|
||||
summary = []
|
||||
tests_fail = 0
|
||||
|
||||
@@ -371,6 +411,8 @@ if __name__ == '__main__':
|
||||
current = cases[0].path if cases else None
|
||||
count = fails = 0
|
||||
for c in cases:
|
||||
if c.skip:
|
||||
continue
|
||||
if current != c.path:
|
||||
file_change(current, count, fails)
|
||||
current = c.path
|
||||
|
||||
@@ -35,7 +35,7 @@ Inherited().undefined
|
||||
class SetattrCls():
|
||||
def __init__(self, dct):
|
||||
# Jedi doesn't even try to understand such code
|
||||
for k, v in dct:
|
||||
for k, v in dct.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
self.defined = 3
|
||||
|
||||
46
test/static_analysis/branches.py
Normal file
46
test/static_analysis/branches.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# -----------------
|
||||
# Simple tests
|
||||
# -----------------
|
||||
|
||||
import random
|
||||
|
||||
if random.choice([0, 1]):
|
||||
x = ''
|
||||
else:
|
||||
x = 1
|
||||
if random.choice([0, 1]):
|
||||
y = ''
|
||||
else:
|
||||
y = 1
|
||||
|
||||
# A simple test
|
||||
if x != 1:
|
||||
x.upper()
|
||||
else:
|
||||
#! 2 attribute-error
|
||||
x.upper()
|
||||
pass
|
||||
|
||||
# This operation is wrong, because the types could be different.
|
||||
#! 6 type-error-operation
|
||||
z = x + y
|
||||
# However, here we have correct types.
|
||||
if x == y:
|
||||
z = x + y
|
||||
else:
|
||||
#! 6 type-error-operation
|
||||
z = x + y
|
||||
|
||||
# -----------------
|
||||
# With a function
|
||||
# -----------------
|
||||
|
||||
def addition(a, b):
|
||||
if type(a) == type(b):
|
||||
return a + b
|
||||
else:
|
||||
#! 9 type-error-operation
|
||||
return a + b
|
||||
|
||||
addition(1, 1)
|
||||
addition(1.0, '')
|
||||
11
test/static_analysis/builtins.py
Normal file
11
test/static_analysis/builtins.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# ----------
|
||||
# isinstance
|
||||
# ----------
|
||||
|
||||
isinstance(1, int)
|
||||
isinstance(1, (int, str))
|
||||
|
||||
#! 14 type-error-isinstance
|
||||
isinstance(1, 1)
|
||||
#! 14 type-error-isinstance
|
||||
isinstance(1, [int, str])
|
||||
13
test/static_analysis/class_simple.py
Normal file
13
test/static_analysis/class_simple.py
Normal file
@@ -0,0 +1,13 @@
|
||||
class Base(object):
|
||||
class Nested():
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
class X(Base.Nested):
|
||||
pass
|
||||
|
||||
|
||||
X().foo()
|
||||
#! 4 attribute-error
|
||||
X().bar()
|
||||
41
test/static_analysis/comprehensions.py
Normal file
41
test/static_analysis/comprehensions.py
Normal file
@@ -0,0 +1,41 @@
|
||||
[a + 1 for a in [1, 2]]
|
||||
|
||||
#! 3 type-error-operation
|
||||
[a + '' for a in [1, 2]]
|
||||
#! 3 type-error-operation
|
||||
(a + '' for a in [1, 2])
|
||||
|
||||
#! 12 type-error-not-iterable
|
||||
[a for a in 1]
|
||||
|
||||
tuple(str(a) for a in [1])
|
||||
|
||||
#! 8 type-error-operation
|
||||
tuple(a + 3 for a in [''])
|
||||
|
||||
# ----------
|
||||
# Some variables within are not defined
|
||||
# ----------
|
||||
|
||||
#! 12 name-error
|
||||
[1 for a in NOT_DEFINFED for b in a if 1]
|
||||
|
||||
#! 25 name-error
|
||||
[1 for a in [1] for b in NOT_DEFINED if 1]
|
||||
|
||||
#! 12 name-error
|
||||
[1 for a in NOT_DEFINFED for b in [1] if 1]
|
||||
|
||||
#! 19 name-error
|
||||
(1 for a in [1] if NOT_DEFINED)
|
||||
|
||||
# ----------
|
||||
# unbalanced sides.
|
||||
# ----------
|
||||
|
||||
# ok
|
||||
(1 for a, b in [(1, 2)])
|
||||
#! 13 value-error-too-few-values
|
||||
(1 for a, b, c in [(1, 2)])
|
||||
#! 10 value-error-too-many-values
|
||||
(1 for a, b in [(1, 2, 3)])
|
||||
@@ -1,7 +1,7 @@
|
||||
def generator():
|
||||
yield 1
|
||||
|
||||
#! 12 type-error-generator
|
||||
#! 11 type-error-not-subscriptable
|
||||
generator()[0]
|
||||
|
||||
list(generator())[0]
|
||||
|
||||
21
test/static_analysis/iterable.py
Normal file
21
test/static_analysis/iterable.py
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
a, b = {'asdf': 3, 'b': 'str'}
|
||||
a
|
||||
|
||||
x = [1]
|
||||
x[0], b = {'a': 1, 'b': '2'}
|
||||
|
||||
dct = {3: ''}
|
||||
for x in dct:
|
||||
pass
|
||||
|
||||
#! 4 type-error-not-iterable
|
||||
for x, y in dct:
|
||||
pass
|
||||
|
||||
# Shouldn't cause issues, because if there are no types (or we don't know what
|
||||
# the types are, we should just ignore it.
|
||||
#! 0 value-error-too-few-values
|
||||
a, b = []
|
||||
#! 7 name-error
|
||||
a, b = NOT_DEFINED
|
||||
7
test/static_analysis/keywords.py
Normal file
7
test/static_analysis/keywords.py
Normal file
@@ -0,0 +1,7 @@
|
||||
def raises():
|
||||
raise KeyError()
|
||||
|
||||
|
||||
def wrong_name():
|
||||
#! 6 name-error
|
||||
raise NotExistingException()
|
||||
@@ -9,3 +9,8 @@
|
||||
-1 - int()
|
||||
int() - float()
|
||||
float() - 3.0
|
||||
|
||||
a = 3
|
||||
b = ''
|
||||
#! 2 type-error-operation
|
||||
a + b
|
||||
|
||||
11
test/static_analysis/python2.py
Normal file
11
test/static_analysis/python2.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
Some special cases of Python 2.
|
||||
"""
|
||||
# python <= 2.7
|
||||
|
||||
# print is syntax:
|
||||
print 1
|
||||
print(1)
|
||||
|
||||
#! 6 name-error
|
||||
print NOT_DEFINED
|
||||
@@ -7,6 +7,7 @@ from textwrap import dedent
|
||||
from jedi import api
|
||||
from jedi._compatibility import is_py3
|
||||
from pytest import raises
|
||||
from jedi.parser import utils
|
||||
|
||||
|
||||
def test_preload_modules():
|
||||
@@ -16,16 +17,15 @@ def test_preload_modules():
|
||||
for i in modules:
|
||||
assert [i in k for k in parser_cache.keys() if k is not None]
|
||||
|
||||
from jedi import cache
|
||||
temp_cache, cache.parser_cache = cache.parser_cache, {}
|
||||
parser_cache = cache.parser_cache
|
||||
temp_cache, utils.parser_cache = utils.parser_cache, {}
|
||||
parser_cache = utils.parser_cache
|
||||
|
||||
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')
|
||||
|
||||
cache.parser_cache = temp_cache
|
||||
utils.parser_cache = temp_cache
|
||||
|
||||
|
||||
def test_empty_script():
|
||||
@@ -62,7 +62,8 @@ def _check_number(source, result='float'):
|
||||
|
||||
def test_completion_on_number_literals():
|
||||
# No completions on an int literal (is a float).
|
||||
assert api.Script('1.').completions() == []
|
||||
assert [c.name for c in api.Script('1.').completions()] \
|
||||
== ['and', 'if', 'in', 'is', 'not', 'or']
|
||||
|
||||
# Multiple points after an int literal basically mean that there's a float
|
||||
# and a call after that.
|
||||
@@ -84,7 +85,6 @@ def test_completion_on_hex_literals():
|
||||
# (invalid statements).
|
||||
assert api.Script('0b2.').completions() == []
|
||||
_check_number('0b1.', 'int') # binary
|
||||
_check_number('0o7.', 'int') # octal
|
||||
|
||||
_check_number('0x2e.', 'int')
|
||||
_check_number('0xE7.', 'int')
|
||||
|
||||
@@ -7,29 +7,29 @@ from jedi import cache
|
||||
from jedi._compatibility import is_py33
|
||||
|
||||
|
||||
def assert_signature(source, expected_name, expected_index=0, line=None, column=None):
|
||||
signatures = Script(source, line, column).call_signatures()
|
||||
|
||||
assert len(signatures) <= 1
|
||||
|
||||
if not signatures:
|
||||
assert expected_name is None, \
|
||||
'There are no signatures, but `%s` expected.' % expected_name
|
||||
else:
|
||||
assert signatures[0].name == expected_name
|
||||
assert signatures[0].index == expected_index
|
||||
return signatures[0]
|
||||
|
||||
|
||||
class TestCallSignatures(TestCase):
|
||||
def _run(self, source, expected_name, expected_index=0, line=None, column=None):
|
||||
signatures = Script(source, line, column).call_signatures()
|
||||
|
||||
assert len(signatures) <= 1
|
||||
|
||||
if not signatures:
|
||||
assert expected_name is None
|
||||
else:
|
||||
assert signatures[0].name == expected_name
|
||||
assert signatures[0].index == expected_index
|
||||
|
||||
def _run_simple(self, source, name, index=0, column=None, line=1):
|
||||
self._run(source, name, index, line, column)
|
||||
assert_signature(source, name, index, line, column)
|
||||
|
||||
def test_valid_call(self):
|
||||
self._run('str()', 'str', column=4)
|
||||
assert_signature('str()', 'str', column=4)
|
||||
|
||||
def test_simple(self):
|
||||
run = self._run_simple
|
||||
s7 = "str().upper().center("
|
||||
s8 = "str(int[zip("
|
||||
run(s7, 'center', 0)
|
||||
|
||||
# simple
|
||||
s1 = "sorted(a, str("
|
||||
@@ -48,13 +48,16 @@ class TestCallSignatures(TestCase):
|
||||
run(s3, None, column=5)
|
||||
run(s3, None)
|
||||
|
||||
# more complicated
|
||||
def test_more_complicated(self):
|
||||
run = self._run_simple
|
||||
'''
|
||||
s4 = 'abs(zip(), , set,'
|
||||
run(s4, None, column=3)
|
||||
run(s4, 'abs', 0, 4)
|
||||
run(s4, 'zip', 0, 8)
|
||||
run(s4, 'abs', 0, 9)
|
||||
#run(s4, 'abs', 1, 10)
|
||||
'''
|
||||
|
||||
s5 = "sorted(1,\nif 2:\n def a():"
|
||||
run(s5, 'sorted', 0, 7)
|
||||
@@ -72,10 +75,11 @@ class TestCallSignatures(TestCase):
|
||||
|
||||
run("import time; abc = time; abc.sleep(", 'sleep', 0)
|
||||
|
||||
def test_issue_57(self):
|
||||
# jedi #57
|
||||
s = "def func(alpha, beta): pass\n" \
|
||||
"func(alpha='101',"
|
||||
run(s, 'func', 0, column=13, line=2)
|
||||
self._run_simple(s, 'func', 0, column=13, line=2)
|
||||
|
||||
def test_flows(self):
|
||||
# jedi-vim #9
|
||||
@@ -96,14 +100,14 @@ class TestCallSignatures(TestCase):
|
||||
if 1:
|
||||
pass
|
||||
"""
|
||||
self._run(s, 'abc', 0, line=6, column=24)
|
||||
assert_signature(s, 'abc', 0, line=6, column=24)
|
||||
s = """
|
||||
import re
|
||||
def huhu(it):
|
||||
re.compile(
|
||||
return it * 2
|
||||
"""
|
||||
self._run(s, 'compile', 0, line=4, column=31)
|
||||
assert_signature(s, 'compile', 0, line=4, column=31)
|
||||
|
||||
# jedi-vim #70
|
||||
s = """def foo("""
|
||||
@@ -111,7 +115,7 @@ class TestCallSignatures(TestCase):
|
||||
|
||||
# jedi-vim #116
|
||||
s = """import itertools; test = getattr(itertools, 'chain'); test("""
|
||||
self._run(s, 'chain', 0)
|
||||
assert_signature(s, 'chain', 0)
|
||||
|
||||
def test_call_signature_on_module(self):
|
||||
"""github issue #240"""
|
||||
@@ -124,7 +128,7 @@ class TestCallSignatures(TestCase):
|
||||
def f(a, b):
|
||||
pass
|
||||
f( )""")
|
||||
self._run(s, 'f', 0, line=3, column=3)
|
||||
assert_signature(s, 'f', 0, line=3, column=3)
|
||||
|
||||
def test_multiple_signatures(self):
|
||||
s = dedent("""\
|
||||
@@ -143,7 +147,7 @@ class TestCallSignatures(TestCase):
|
||||
def x():
|
||||
pass
|
||||
""")
|
||||
self._run(s, 'abs', 0, line=1, column=5)
|
||||
assert_signature(s, 'abs', 0, line=1, column=5)
|
||||
|
||||
def test_decorator_in_class(self):
|
||||
"""
|
||||
@@ -169,28 +173,27 @@ class TestCallSignatures(TestCase):
|
||||
assert x == ['*args']
|
||||
|
||||
def test_additional_brackets(self):
|
||||
self._run('str((', 'str', 0)
|
||||
assert_signature('str((', 'str', 0)
|
||||
|
||||
def test_unterminated_strings(self):
|
||||
self._run('str(";', 'str', 0)
|
||||
assert_signature('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)
|
||||
assert_signature('str (', 'str', 0)
|
||||
assert_signature('str (";', 'str', 0)
|
||||
assert_signature('str\n(', None)
|
||||
|
||||
def test_brackets_in_string_literals(self):
|
||||
self._run('str (" (', 'str', 0)
|
||||
self._run('str (" )', 'str', 0)
|
||||
assert_signature('str (" (', 'str', 0)
|
||||
assert_signature('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()
|
||||
assert_signature('str(\ndef x', 'str', 0)
|
||||
assert not Script('str(\ndef x(): pass').call_signatures()
|
||||
|
||||
def test_flow_call(self):
|
||||
assert not Script('if (1').call_signatures()
|
||||
@@ -207,14 +210,14 @@ class TestCallSignatures(TestCase):
|
||||
|
||||
A().test1().test2(''')
|
||||
|
||||
self._run(source, 'test2', 0)
|
||||
assert_signature(source, 'test2', 0)
|
||||
|
||||
def test_return(self):
|
||||
source = dedent('''
|
||||
def foo():
|
||||
return '.'.join()''')
|
||||
|
||||
self._run(source, 'join', 0, column=len(" return '.'.join("))
|
||||
assert_signature(source, 'join', 0, column=len(" return '.'.join("))
|
||||
|
||||
|
||||
class TestParams(TestCase):
|
||||
@@ -249,7 +252,6 @@ class TestParams(TestCase):
|
||||
assert p[0].name == 'suffix'
|
||||
|
||||
|
||||
|
||||
def test_signature_is_definition():
|
||||
"""
|
||||
Through inheritance, a call signature is a sub class of Definition.
|
||||
@@ -257,7 +259,7 @@ def test_signature_is_definition():
|
||||
"""
|
||||
s = """class Spam(): pass\nSpam"""
|
||||
signature = Script(s + '(').call_signatures()[0]
|
||||
definition = Script(s + '(').goto_definitions()[0]
|
||||
definition = Script(s + '(', column=0).goto_definitions()[0]
|
||||
signature.line == 1
|
||||
signature.column == 6
|
||||
|
||||
@@ -315,13 +317,23 @@ def test_completion_interference():
|
||||
assert Script('open(').call_signatures()
|
||||
|
||||
|
||||
def test_signature_index():
|
||||
def get(source):
|
||||
return Script(source).call_signatures()[0]
|
||||
def test_keyword_argument_index():
|
||||
def get(source, column=None):
|
||||
return Script(source, column=column).call_signatures()[0]
|
||||
|
||||
assert get('sorted([], key=a').index == 2
|
||||
assert get('sorted([], key=').index == 2
|
||||
assert get('sorted([], no_key=a').index is None
|
||||
|
||||
kw_func = 'def foo(a, b): pass\nfoo(b=3, a=4)'
|
||||
assert get(kw_func, column=len('foo(b')).index == 0
|
||||
assert get(kw_func, column=len('foo(b=')).index == 1
|
||||
assert get(kw_func, column=len('foo(b=3, a=')).index == 0
|
||||
|
||||
kw_func_simple = 'def foo(a, b): pass\nfoo(b=4)'
|
||||
assert get(kw_func_simple, column=len('foo(b')).index == 0
|
||||
assert get(kw_func_simple, column=len('foo(b=')).index == 1
|
||||
|
||||
args_func = 'def foo(*kwargs): pass\n'
|
||||
assert get(args_func + 'foo(a').index == 0
|
||||
assert get(args_func + 'foo(a, b').index == 0
|
||||
@@ -333,6 +345,7 @@ def test_signature_index():
|
||||
both = 'def foo(*args, **kwargs): pass\n'
|
||||
assert get(both + 'foo(a=2').index == 1
|
||||
assert get(both + 'foo(a=2, b=2').index == 1
|
||||
assert get(both + 'foo(a=2, b=2)', column=len('foo(b=2, a=2')).index == 1
|
||||
assert get(both + 'foo(a, b, c').index == 0
|
||||
|
||||
|
||||
@@ -343,3 +356,24 @@ def test_bracket_start():
|
||||
return signatures[0].bracket_start
|
||||
|
||||
assert bracket_start('str(') == (1, 3)
|
||||
|
||||
|
||||
def test_different_caller():
|
||||
"""
|
||||
It's possible to not use names, but another function result or an array
|
||||
index and then get the call signature of it.
|
||||
"""
|
||||
|
||||
assert_signature('[str][0](', 'str', 0)
|
||||
assert_signature('[str][0]()', 'str', 0, column=len('[str][0]('))
|
||||
|
||||
assert_signature('(str)(', 'str', 0)
|
||||
assert_signature('(str)()', 'str', 0, column=len('(str)('))
|
||||
|
||||
|
||||
def test_in_function():
|
||||
code = dedent('''\
|
||||
class X():
|
||||
@property
|
||||
def func(''')
|
||||
assert not Script(code).call_signatures()
|
||||
|
||||
@@ -316,7 +316,7 @@ class TestGotoAssignments(TestCase):
|
||||
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')
|
||||
assert n.name in ('ntpath', 'posixpath', 'os2emxpath')
|
||||
assert n.type == 'module'
|
||||
|
||||
def test_import_alias(self):
|
||||
@@ -333,3 +333,22 @@ class TestGotoAssignments(TestCase):
|
||||
assert len(ass) == 1
|
||||
assert ass[0].name == 'json'
|
||||
assert ass[0].type == 'module'
|
||||
|
||||
|
||||
def test_added_equals_to_params():
|
||||
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'
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
Tests for `api.defined_names`.
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
from textwrap import dedent
|
||||
|
||||
from jedi import api
|
||||
from jedi import defined_names, names
|
||||
from ..helpers import TestCase
|
||||
|
||||
|
||||
class TestDefinedNames(TestCase):
|
||||
def assert_definition_names(self, definitions, names):
|
||||
assert [d.name for d in definitions] == names
|
||||
def assert_definition_names(self, definitions, names_):
|
||||
assert [d.name for d in definitions] == names_
|
||||
|
||||
def check_defined_names(self, source, names):
|
||||
definitions = api.names(textwrap.dedent(source))
|
||||
self.assert_definition_names(definitions, names)
|
||||
def check_defined_names(self, source, names_):
|
||||
definitions = names(dedent(source))
|
||||
self.assert_definition_names(definitions, names_)
|
||||
return definitions
|
||||
|
||||
def test_get_definitions_flat(self):
|
||||
@@ -76,7 +76,17 @@ class TestDefinedNames(TestCase):
|
||||
|
||||
def test_follow_imports():
|
||||
# github issue #344
|
||||
imp = api.defined_names('import datetime')[0]
|
||||
imp = defined_names('import datetime')[0]
|
||||
assert imp.name == 'datetime'
|
||||
datetime_names = [str(d.name) for d in imp.defined_names()]
|
||||
assert 'timedelta' in datetime_names
|
||||
|
||||
|
||||
def test_names_twice():
|
||||
source = dedent('''
|
||||
def lol():
|
||||
pass
|
||||
''')
|
||||
|
||||
defs = names(source=source)
|
||||
assert defs[0].defined_names() == []
|
||||
|
||||
@@ -5,6 +5,64 @@ Tests of ``jedi.api.Interpreter``.
|
||||
from ..helpers import TestCase
|
||||
import jedi
|
||||
from jedi._compatibility import is_py33
|
||||
from jedi.evaluate.compiled import mixed
|
||||
|
||||
class _GlobalNameSpace():
|
||||
class SideEffectContainer():
|
||||
pass
|
||||
|
||||
|
||||
def get_completion(source, namespace):
|
||||
i = jedi.Interpreter(source, [namespace])
|
||||
completions = i.completions()
|
||||
assert len(completions) == 1
|
||||
return completions[0]
|
||||
|
||||
|
||||
def test_builtin_details():
|
||||
import keyword
|
||||
|
||||
class EmptyClass:
|
||||
pass
|
||||
|
||||
variable = EmptyClass()
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
cls = get_completion('EmptyClass', locals())
|
||||
var = get_completion('variable', locals())
|
||||
f = get_completion('func', locals())
|
||||
m = get_completion('keyword', locals())
|
||||
assert cls.type == 'class'
|
||||
assert var.type == 'instance'
|
||||
assert f.type == 'function'
|
||||
assert m.type == 'module'
|
||||
|
||||
|
||||
def test_nested_resolve():
|
||||
class XX():
|
||||
def x():
|
||||
pass
|
||||
|
||||
cls = get_completion('XX', locals())
|
||||
func = get_completion('XX.x', locals())
|
||||
assert func.start_pos == (cls.start_pos[0] + 1, 12)
|
||||
|
||||
|
||||
def test_side_effect_completion():
|
||||
"""
|
||||
In the repl it's possible to cause side effects that are not documented in
|
||||
Python code, however we want references to Python code as well. Therefore
|
||||
we need some mixed kind of magic for tests.
|
||||
"""
|
||||
_GlobalNameSpace.SideEffectContainer.foo = 1
|
||||
side_effect = get_completion('SideEffectContainer', _GlobalNameSpace.__dict__)
|
||||
|
||||
# It's a class that contains MixedObject.
|
||||
assert isinstance(side_effect._definition.base, mixed.MixedObject)
|
||||
foo = get_completion('SideEffectContainer.foo', _GlobalNameSpace.__dict__)
|
||||
assert foo.name == 'foo'
|
||||
|
||||
|
||||
class TestInterpreterAPI(TestCase):
|
||||
@@ -17,19 +75,19 @@ class TestInterpreterAPI(TestCase):
|
||||
|
||||
def test_complete_raw_function(self):
|
||||
from os.path import join
|
||||
self.check_interpreter_complete('join().up',
|
||||
self.check_interpreter_complete('join("").up',
|
||||
locals(),
|
||||
['upper'])
|
||||
|
||||
def test_complete_raw_function_different_name(self):
|
||||
from os.path import join as pjoin
|
||||
self.check_interpreter_complete('pjoin().up',
|
||||
self.check_interpreter_complete('pjoin("").up',
|
||||
locals(),
|
||||
['upper'])
|
||||
|
||||
def test_complete_raw_module(self):
|
||||
import os
|
||||
self.check_interpreter_complete('os.path.join().up',
|
||||
self.check_interpreter_complete('os.path.join("a").up',
|
||||
locals(),
|
||||
['upper'])
|
||||
|
||||
|
||||
6
test/test_api/test_usages.py
Normal file
6
test/test_api/test_usages.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import jedi
|
||||
|
||||
|
||||
def test_import_usage():
|
||||
s = jedi.Script("from .. import foo", line=1, column=18, path="foo.py")
|
||||
assert [usage.line for usage in s.usages()] == [1]
|
||||
@@ -8,7 +8,7 @@ import pytest
|
||||
|
||||
import jedi
|
||||
from jedi import settings, cache
|
||||
from jedi.cache import ParserCacheItem, ParserPickling
|
||||
from jedi.parser.utils import ParserCacheItem, ParserPickling
|
||||
|
||||
|
||||
ParserPicklingCls = type(ParserPickling)
|
||||
|
||||
1
test/test_evaluate/buildout_project/bin/binary_file
Normal file
1
test/test_evaluate/buildout_project/bin/binary_file
Normal file
@@ -0,0 +1 @@
|
||||
‰PNG
|
||||
@@ -4,7 +4,7 @@ Python 2.X)
|
||||
"""
|
||||
import jedi
|
||||
from jedi._compatibility import u
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
from .. import helpers
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ def test_explicit_absolute_imports():
|
||||
"""
|
||||
Detect modules with ``from __future__ import absolute_import``.
|
||||
"""
|
||||
parser = Parser(load_grammar(), u("from __future__ import absolute_import"), "test.py")
|
||||
parser = ParserWithRecovery(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(load_grammar(), u("1"), "test.py")
|
||||
parser = ParserWithRecovery(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(load_grammar(), src, "test.py")
|
||||
parser = ParserWithRecovery(load_grammar(), src, "test.py")
|
||||
assert parser.module.has_explicit_absolute_import
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import pytest
|
||||
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.
|
||||
If annotations adhere to PEP-0484, we use them (they override inference),
|
||||
else they are parsed but ignored
|
||||
"""
|
||||
|
||||
source = dedent("""\
|
||||
@@ -27,3 +27,34 @@ def test_simple_annotations():
|
||||
|
||||
annot_ret('')""")
|
||||
assert [d.name for d in jedi.Script(source, ).goto_definitions()] == ['str']
|
||||
|
||||
source = dedent("""\
|
||||
def annot(a:int):
|
||||
return a
|
||||
|
||||
annot('')""")
|
||||
|
||||
assert [d.name for d in jedi.Script(source, ).goto_definitions()] == ['int']
|
||||
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[0] < 3')
|
||||
@pytest.mark.parametrize('reference', [
|
||||
'assert 1',
|
||||
'1',
|
||||
'def x(): pass',
|
||||
'1, 2',
|
||||
r'1\n'
|
||||
])
|
||||
def test_illegal_forward_references(reference):
|
||||
source = 'def foo(bar: "%s"): bar' % reference
|
||||
|
||||
assert not jedi.Script(source).goto_definitions()
|
||||
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[0] < 3')
|
||||
def test_lambda_forward_references():
|
||||
source = 'def foo(bar: "lambda: 3"): bar'
|
||||
|
||||
# For now just receiving the 3 is ok. I'm doubting that this is what we
|
||||
# want. We also execute functions. Should we only execute classes?
|
||||
assert jedi.Script(source).goto_definitions()
|
||||
|
||||
@@ -7,7 +7,7 @@ from jedi.evaluate.sys_path import (_get_parent_dir_with_file,
|
||||
sys_path_with_modifications,
|
||||
_check_module)
|
||||
from jedi.evaluate import Evaluator
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
|
||||
from ..helpers import cwd_at
|
||||
|
||||
@@ -37,7 +37,7 @@ def test_append_on_non_sys_path():
|
||||
d = Dummy()
|
||||
d.path.append('foo')"""))
|
||||
grammar = load_grammar()
|
||||
p = Parser(grammar, SRC)
|
||||
p = ParserWithRecovery(grammar, SRC)
|
||||
paths = _check_module(Evaluator(grammar), p.module)
|
||||
assert len(paths) > 0
|
||||
assert 'foo' not in paths
|
||||
@@ -48,7 +48,7 @@ def test_path_from_invalid_sys_path_assignment():
|
||||
import sys
|
||||
sys.path = 'invalid'"""))
|
||||
grammar = load_grammar()
|
||||
p = Parser(grammar, SRC)
|
||||
p = ParserWithRecovery(grammar, SRC)
|
||||
paths = _check_module(Evaluator(grammar), p.module)
|
||||
assert len(paths) > 0
|
||||
assert 'invalid' not in paths
|
||||
@@ -60,7 +60,7 @@ def test_sys_path_with_modifications():
|
||||
import os
|
||||
"""))
|
||||
grammar = load_grammar()
|
||||
p = Parser(grammar, SRC)
|
||||
p = ParserWithRecovery(grammar, SRC)
|
||||
p.module.path = os.path.abspath(os.path.join(os.curdir, 'module_name.py'))
|
||||
paths = sys_path_with_modifications(Evaluator(grammar), p.module)
|
||||
assert '/tmp/.buildout/eggs/important_package.egg' in paths
|
||||
@@ -83,7 +83,7 @@ def test_path_from_sys_path_assignment():
|
||||
if __name__ == '__main__':
|
||||
sys.exit(important_package.main())"""))
|
||||
grammar = load_grammar()
|
||||
p = Parser(grammar, SRC)
|
||||
p = ParserWithRecovery(grammar, SRC)
|
||||
paths = _check_module(Evaluator(grammar), p.module)
|
||||
assert 1 not in paths
|
||||
assert '/home/test/.buildout/eggs/important_package.egg' in paths
|
||||
|
||||
@@ -6,31 +6,33 @@ from jedi.evaluate import Evaluator
|
||||
from jedi import Script
|
||||
|
||||
|
||||
def _evaluator():
|
||||
return Evaluator(load_grammar())
|
||||
|
||||
|
||||
def test_simple():
|
||||
e = Evaluator(load_grammar())
|
||||
bltn = compiled.CompiledObject(builtins)
|
||||
obj = compiled.CompiledObject('_str_', bltn)
|
||||
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(upper[0]))
|
||||
objs = list(e.execute(list(upper)[0]))
|
||||
assert len(objs) == 1
|
||||
assert isinstance(objs[0], representation.Instance)
|
||||
|
||||
|
||||
def test_fake_loading():
|
||||
assert isinstance(compiled.create(Evaluator(load_grammar()), next), Function)
|
||||
e = _evaluator()
|
||||
assert isinstance(compiled.create(e, next), Function)
|
||||
|
||||
string = compiled.builtin.get_subscope_by_name('str')
|
||||
from_name = compiled._create_from_name(
|
||||
compiled.builtin,
|
||||
string,
|
||||
'__init__'
|
||||
)
|
||||
builtin = compiled.get_special_object(e, 'BUILTINS')
|
||||
string = builtin.get_subscope_by_name('str')
|
||||
from_name = compiled._create_from_name(e, builtin, string, '__init__')
|
||||
assert isinstance(from_name, Function)
|
||||
|
||||
|
||||
def test_fake_docstr():
|
||||
assert compiled.create(Evaluator(load_grammar()), next).raw_doc == next.__doc__
|
||||
assert compiled.create(_evaluator(), next).raw_doc == next.__doc__
|
||||
|
||||
|
||||
def test_parse_function_doc_illegal_docstr():
|
||||
@@ -47,13 +49,13 @@ def test_doc():
|
||||
Even CompiledObject docs always return empty docstrings - not None, that's
|
||||
just a Jedi API definition.
|
||||
"""
|
||||
obj = compiled.CompiledObject(''.__getnewargs__)
|
||||
obj = compiled.CompiledObject(_evaluator(), ''.__getnewargs__)
|
||||
assert obj.doc == ''
|
||||
|
||||
|
||||
def test_string_literals():
|
||||
def typ(string):
|
||||
d = Script(string).goto_definitions()[0]
|
||||
d = Script("a = %s; a" % string).goto_definitions()[0]
|
||||
return d.name
|
||||
|
||||
assert typ('""') == 'str'
|
||||
|
||||
16
test/test_evaluate/test_helpers.py
Normal file
16
test/test_evaluate/test_helpers.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from textwrap import dedent
|
||||
|
||||
from jedi import names
|
||||
from jedi.evaluate import helpers
|
||||
|
||||
|
||||
def test_call_of_leaf_in_brackets():
|
||||
s = dedent("""
|
||||
x = 1
|
||||
type(x)
|
||||
""")
|
||||
last_x = names(s, references=True, definitions=False)[-1]
|
||||
name = last_x._name
|
||||
|
||||
call = helpers.call_of_leaf(name)
|
||||
assert call == name
|
||||
@@ -56,6 +56,13 @@ def test_not_importable_file():
|
||||
assert not jedi.Script(src, path='example.py').completions()
|
||||
|
||||
|
||||
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]
|
||||
assert len(defs) == len(set(defs))
|
||||
|
||||
|
||||
def test_cache_works_with_sys_path_param(tmpdir):
|
||||
foo_path = tmpdir.join('foo')
|
||||
bar_path = tmpdir.join('bar')
|
||||
@@ -70,3 +77,15 @@ def test_cache_works_with_sys_path_param(tmpdir):
|
||||
|
||||
assert 'bar' in [c.name for c in bar_completions]
|
||||
assert 'foo' not in [c.name for c in bar_completions]
|
||||
|
||||
|
||||
def test_import_completion_docstring():
|
||||
import abc
|
||||
s = jedi.Script('"""test"""\nimport ab')
|
||||
completions = s.completions()
|
||||
assert len(completions) == 1
|
||||
assert completions[0].docstring(fast=False) == abc.__doc__
|
||||
|
||||
# However for performance reasons not all modules are loaded and the
|
||||
# docstring is empty in this case.
|
||||
assert completions[0].docstring() == ''
|
||||
|
||||
20
test/test_evaluate/test_precedence.py
Normal file
20
test/test_evaluate/test_precedence.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from jedi.parser import load_grammar, Parser
|
||||
from jedi.evaluate import Evaluator
|
||||
from jedi.evaluate.compiled import CompiledObject
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[0] < 3') # Ellipsis does not exists in 2
|
||||
@pytest.mark.parametrize('source', [
|
||||
'1 == 1',
|
||||
'1.0 == 1',
|
||||
'... == ...'
|
||||
])
|
||||
def test_equals(source):
|
||||
evaluator = Evaluator(load_grammar())
|
||||
node = Parser(load_grammar(), source, 'eval_input').get_parsed_node()
|
||||
results = evaluator.eval_element(node)
|
||||
assert len(results) == 1
|
||||
first = results.pop()
|
||||
assert isinstance(first, CompiledObject) and first.obj is True
|
||||
@@ -32,5 +32,5 @@ def test_class_mro():
|
||||
pass
|
||||
X"""
|
||||
cls, evaluator = get_definition_and_evaluator(s)
|
||||
mro = cls.py__mro__(evaluator)
|
||||
mro = cls.py__mro__()
|
||||
assert [str(c.name) for c in mro] == ['X', 'object']
|
||||
|
||||
@@ -5,22 +5,22 @@ import sys
|
||||
import pytest
|
||||
|
||||
from jedi._compatibility import unicode
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
from jedi.evaluate import sys_path, Evaluator
|
||||
|
||||
|
||||
def test_paths_from_assignment():
|
||||
def paths(src):
|
||||
grammar = load_grammar()
|
||||
stmt = Parser(grammar, unicode(src)).module.statements[0]
|
||||
return list(sys_path._paths_from_assignment(Evaluator(grammar), stmt))
|
||||
stmt = ParserWithRecovery(grammar, unicode(src)).module.statements[0]
|
||||
return set(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']
|
||||
assert paths('sys.path = a = ["a"]') == ['a']
|
||||
assert paths('sys.path[0:0] = ["a"]') == set(['a'])
|
||||
assert paths('sys.path = ["b", 1, x + 3, y, "c"]') == set(['b', 'c'])
|
||||
assert paths('sys.path = a = ["a"]') == set(['a'])
|
||||
|
||||
# Fail for complicated examples.
|
||||
assert paths('sys.path, other = ["a"], 2') == []
|
||||
assert paths('sys.path, other = ["a"], 2') == set()
|
||||
|
||||
|
||||
# Currently venv site-packages resolution only seeks pythonX.Y/site-packages
|
||||
|
||||
@@ -40,7 +40,10 @@ def test_completion(case, monkeypatch):
|
||||
|
||||
|
||||
def test_static_analysis(static_analysis_case):
|
||||
static_analysis_case.run(assert_static_analysis)
|
||||
if static_analysis_case.skip is not None:
|
||||
pytest.skip(static_analysis_case.skip)
|
||||
else:
|
||||
static_analysis_case.run(assert_static_analysis)
|
||||
|
||||
|
||||
def test_refactor(refactor_case):
|
||||
|
||||
@@ -18,17 +18,17 @@ def test_goto_definition_on_import():
|
||||
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 .", path='whatever.py').completions()) < 30
|
||||
|
||||
# Global import
|
||||
assert len(Script("from . import", 1, 5, '').completions()) > 30
|
||||
assert len(Script("from . import", 1, 5, 'whatever.py').completions()) > 30
|
||||
# relative import
|
||||
assert 10 < len(Script("from . import", 1, 6, '').completions()) < 30
|
||||
assert 10 < len(Script("from . import", 1, 6, 'whatever.py').completions()) < 30
|
||||
|
||||
# Global import
|
||||
assert len(Script("from . import classes", 1, 5, '').completions()) > 30
|
||||
assert len(Script("from . import classes", 1, 5, 'whatever.py').completions()) > 30
|
||||
# relative import
|
||||
assert 10 < len(Script("from . import classes", 1, 6, '').completions()) < 30
|
||||
assert 10 < len(Script("from . import classes", 1, 6, 'whatever.py').completions()) < 30
|
||||
|
||||
wanted = set(['ImportError', 'import', 'ImportWarning'])
|
||||
assert set([c.name for c in Script("import").completions()]) == wanted
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from jedi._compatibility import u
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, 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
|
||||
assert ParserWithRecovery(load_grammar(), string).module.get_code() == string
|
||||
|
||||
compare(u('\na #pass\n'))
|
||||
compare(u('wblabla* 1\t\n'))
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
|
||||
import jedi
|
||||
from jedi._compatibility import u
|
||||
from jedi import cache
|
||||
from jedi.parser import load_grammar
|
||||
from jedi.parser.fast import FastParser
|
||||
from jedi.parser.utils import save_parser
|
||||
|
||||
|
||||
def test_add_to_end():
|
||||
@@ -38,7 +41,7 @@ def test_class_in_docstr():
|
||||
Regression test for a problem with classes in docstrings.
|
||||
"""
|
||||
a = '"\nclasses\n"'
|
||||
jedi.Script(a, 1, 0)._parser
|
||||
jedi.Script(a, 1, 0)._get_module()
|
||||
|
||||
b = a + '\nimport os'
|
||||
assert jedi.Script(b, 4, 8).goto_assignments()
|
||||
@@ -75,16 +78,17 @@ def test_split_parts():
|
||||
test('a\n\n', 'def b(): pass\n', 'c\n')
|
||||
test('a\n', 'def b():\n pass\n', 'c\n')
|
||||
|
||||
test('from x\\\n')
|
||||
test('a\n\\\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, p, pickling=False)
|
||||
save_parser(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
|
||||
@@ -329,7 +333,7 @@ def test_wrong_indentation():
|
||||
b
|
||||
a
|
||||
""")
|
||||
check_fp(src, 1)
|
||||
#check_fp(src, 1)
|
||||
|
||||
src = dedent("""\
|
||||
def complex():
|
||||
@@ -346,13 +350,15 @@ def test_wrong_indentation():
|
||||
|
||||
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
|
||||
code = u('isinstance(\n\n' + func)
|
||||
p = FastParser(load_grammar(), code)
|
||||
# As you can see, the part that was failing is still there in the get_code
|
||||
# call. It is not relevant for evaluation, but still available as an
|
||||
# ErrorNode.
|
||||
assert p.module.get_code() == code
|
||||
assert p.number_of_splits == 2
|
||||
assert p.number_parsers_used == 2
|
||||
cache.save_parser(None, p, pickling=False)
|
||||
save_parser(None, p, pickling=False)
|
||||
|
||||
# Now with a correct parser it should work perfectly well.
|
||||
check_fp('isinstance()\n' + func, 1, 2)
|
||||
@@ -420,6 +426,16 @@ def test_fake_parentheses():
|
||||
check_fp(src, 3, 2, 1)
|
||||
|
||||
|
||||
def test_additional_indent():
|
||||
source = dedent('''\
|
||||
int(
|
||||
def x():
|
||||
pass
|
||||
''')
|
||||
|
||||
check_fp(source, 2)
|
||||
|
||||
|
||||
def test_incomplete_function():
|
||||
source = '''return ImportErr'''
|
||||
|
||||
@@ -437,4 +453,44 @@ def test_string_literals():
|
||||
""")
|
||||
|
||||
script = jedi.Script(dedent(source))
|
||||
script._get_module().end_pos == (6, 0)
|
||||
assert script.completions()
|
||||
|
||||
|
||||
def test_decorator_string_issue():
|
||||
"""
|
||||
Test case from #589
|
||||
"""
|
||||
source = dedent('''\
|
||||
"""
|
||||
@"""
|
||||
def bla():
|
||||
pass
|
||||
|
||||
bla.''')
|
||||
|
||||
s = jedi.Script(source)
|
||||
assert s.completions()
|
||||
assert s._get_module().get_code() == source
|
||||
|
||||
|
||||
def test_round_trip():
|
||||
source = dedent('''
|
||||
def x():
|
||||
"""hahaha"""
|
||||
func''')
|
||||
|
||||
f = FastParser(load_grammar(), u(source))
|
||||
assert f.get_parsed_node().get_code() == source
|
||||
|
||||
|
||||
@pytest.mark.xfail()
|
||||
def test_parentheses_in_string():
|
||||
code = dedent('''
|
||||
def x():
|
||||
'('
|
||||
|
||||
import abc
|
||||
|
||||
abc.''')
|
||||
check_fp(code, 2, 1, 1)
|
||||
|
||||
@@ -3,7 +3,7 @@ import difflib
|
||||
import pytest
|
||||
|
||||
from jedi._compatibility import u
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
|
||||
code_basic_features = u('''
|
||||
"""A mod docstring"""
|
||||
@@ -44,7 +44,7 @@ def diff_code_assert(a, b, n=4):
|
||||
def test_basic_parsing():
|
||||
"""Validate the parsing features"""
|
||||
|
||||
prs = Parser(load_grammar(), code_basic_features)
|
||||
prs = ParserWithRecovery(load_grammar(), code_basic_features)
|
||||
diff_code_assert(
|
||||
code_basic_features,
|
||||
prs.module.get_code()
|
||||
@@ -53,7 +53,7 @@ def test_basic_parsing():
|
||||
|
||||
def test_operators():
|
||||
src = u('5 * 3')
|
||||
prs = Parser(load_grammar(), src)
|
||||
prs = ParserWithRecovery(load_grammar(), src)
|
||||
diff_code_assert(src, prs.module.get_code())
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def method_with_docstring():
|
||||
"""class docstr"""
|
||||
pass
|
||||
''')
|
||||
assert Parser(load_grammar(), s).module.get_code() == s
|
||||
assert ParserWithRecovery(load_grammar(), s).module.get_code() == s
|
||||
|
||||
|
||||
def test_end_newlines():
|
||||
@@ -92,7 +92,7 @@ def test_end_newlines():
|
||||
line the parser needs.
|
||||
"""
|
||||
def test(source, end_pos):
|
||||
module = Parser(load_grammar(), u(source)).module
|
||||
module = ParserWithRecovery(load_grammar(), u(source)).module
|
||||
assert module.get_code() == source
|
||||
assert module.end_pos == end_pos
|
||||
|
||||
@@ -103,3 +103,5 @@ def test_end_newlines():
|
||||
test('a\n#comment', (2, 8))
|
||||
test('a#comment', (1, 9))
|
||||
test('def a():\n pass', (2, 5))
|
||||
|
||||
test('def a(', (1, 6))
|
||||
|
||||
34
test/test_parser/test_param_splitting.py
Normal file
34
test/test_parser/test_param_splitting.py
Normal file
@@ -0,0 +1,34 @@
|
||||
'''
|
||||
To make the life of any analysis easier, we are generating Param objects
|
||||
instead of simple parser objects.
|
||||
'''
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from jedi.parser import Parser, load_grammar
|
||||
|
||||
|
||||
def assert_params(param_string, **wanted_dct):
|
||||
source = dedent('''
|
||||
def x(%s):
|
||||
pass
|
||||
''') % param_string
|
||||
|
||||
parser = Parser(load_grammar(), dedent(source))
|
||||
funcdef = parser.get_parsed_node().subscopes[0]
|
||||
dct = dict((p.name.value, p.default and p.default.get_code())
|
||||
for p in funcdef.params)
|
||||
assert dct == wanted_dct
|
||||
assert parser.get_parsed_node().get_code() == source
|
||||
|
||||
|
||||
def test_split_params_with_separation_star():
|
||||
assert_params(u'x, y=1, *, z=3', x=None, y='1', z='3')
|
||||
assert_params(u'*, x', x=None)
|
||||
assert_params(u'*')
|
||||
|
||||
|
||||
def test_split_params_with_stars():
|
||||
assert_params(u'x, *args', x=None, args=None)
|
||||
assert_params(u'**kwargs', kwargs=None)
|
||||
assert_params(u'*args, **kwargs', args=None, kwargs=None)
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
import jedi
|
||||
from jedi._compatibility import u, is_py3
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser.user_context import UserContextParser
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
from jedi.parser import tree as pt
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
def test_user_statement_on_import():
|
||||
@@ -15,15 +14,16 @@ def test_user_statement_on_import():
|
||||
" time)")
|
||||
|
||||
for pos in [(2, 1), (2, 4)]:
|
||||
p = UserContextParser(load_grammar(), s, None, pos, None, lambda x: 1).user_stmt()
|
||||
assert isinstance(p, pt.Import)
|
||||
assert [str(n) for n in p.get_defined_names()] == ['time']
|
||||
p = ParserWithRecovery(load_grammar(), s)
|
||||
stmt = p.module.get_statement_for_position(pos)
|
||||
assert isinstance(stmt, pt.Import)
|
||||
assert [str(n) for n in stmt.get_defined_names()] == ['time']
|
||||
|
||||
|
||||
class TestCallAndName():
|
||||
def get_call(self, source):
|
||||
# Get the simple_stmt and then the first one.
|
||||
simple_stmt = Parser(load_grammar(), u(source)).module.children[0]
|
||||
simple_stmt = ParserWithRecovery(load_grammar(), u(source)).module.children[0]
|
||||
return simple_stmt.children[0]
|
||||
|
||||
def test_name_and_call_positions(self):
|
||||
@@ -58,7 +58,7 @@ class TestCallAndName():
|
||||
|
||||
class TestSubscopes():
|
||||
def get_sub(self, source):
|
||||
return Parser(load_grammar(), u(source)).module.subscopes[0]
|
||||
return ParserWithRecovery(load_grammar(), u(source)).module.subscopes[0]
|
||||
|
||||
def test_subscope_names(self):
|
||||
name = self.get_sub('class Foo: pass').name
|
||||
@@ -74,7 +74,7 @@ class TestSubscopes():
|
||||
|
||||
class TestImports():
|
||||
def get_import(self, source):
|
||||
return Parser(load_grammar(), source).module.imports[0]
|
||||
return ParserWithRecovery(load_grammar(), source).module.imports[0]
|
||||
|
||||
def test_import_names(self):
|
||||
imp = self.get_import(u('import math\n'))
|
||||
@@ -89,13 +89,13 @@ class TestImports():
|
||||
|
||||
|
||||
def test_module():
|
||||
module = Parser(load_grammar(), u('asdf'), 'example.py').module
|
||||
module = ParserWithRecovery(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(load_grammar(), u('asdf')).module
|
||||
module = ParserWithRecovery(load_grammar(), u('asdf')).module
|
||||
name = module.name
|
||||
assert str(name) == ''
|
||||
assert name.start_pos == (1, 0)
|
||||
@@ -108,7 +108,7 @@ def test_end_pos():
|
||||
def func():
|
||||
y = None
|
||||
'''))
|
||||
parser = Parser(load_grammar(), s)
|
||||
parser = ParserWithRecovery(load_grammar(), s)
|
||||
scope = parser.module.subscopes[0]
|
||||
assert scope.start_pos == (3, 0)
|
||||
assert scope.end_pos == (5, 0)
|
||||
@@ -121,7 +121,7 @@ def test_carriage_return_statements():
|
||||
# this is a namespace package
|
||||
'''))
|
||||
source = source.replace('\n', '\r\n')
|
||||
stmt = Parser(load_grammar(), source).module.statements[0]
|
||||
stmt = ParserWithRecovery(load_grammar(), source).module.statements[0]
|
||||
assert '#' not in stmt.get_code()
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ def test_incomplete_list_comprehension():
|
||||
""" Shouldn't raise an error, same bug as #418. """
|
||||
# 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 == []
|
||||
assert ParserWithRecovery(load_grammar(), u('(1 for def')).module.statements == []
|
||||
|
||||
|
||||
def test_hex_values_in_docstring():
|
||||
@@ -141,7 +141,7 @@ def test_hex_values_in_docstring():
|
||||
return 1
|
||||
'''
|
||||
|
||||
doc = Parser(load_grammar(), dedent(u(source))).module.subscopes[0].raw_doc
|
||||
doc = ParserWithRecovery(load_grammar(), dedent(u(source))).module.subscopes[0].raw_doc
|
||||
if is_py3:
|
||||
assert doc == '\xff'
|
||||
else:
|
||||
@@ -160,9 +160,9 @@ def test_error_correction_with():
|
||||
|
||||
|
||||
def test_newline_positions():
|
||||
endmarker = Parser(load_grammar(), u('a\n')).module.children[-1]
|
||||
endmarker = ParserWithRecovery(load_grammar(), u('a\n')).module.children[-1]
|
||||
assert endmarker.end_pos == (2, 0)
|
||||
new_line = endmarker.get_previous()
|
||||
new_line = endmarker.get_previous_leaf()
|
||||
assert new_line.start_pos == (1, 1)
|
||||
assert new_line.end_pos == (2, 0)
|
||||
|
||||
@@ -174,7 +174,7 @@ def test_end_pos_error_correction():
|
||||
end_pos, even if something breaks in the parser (error correction).
|
||||
"""
|
||||
s = u('def x():\n .')
|
||||
m = Parser(load_grammar(), s).module
|
||||
m = ParserWithRecovery(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
|
||||
@@ -190,8 +190,8 @@ def test_param_splitting():
|
||||
"""
|
||||
def check(src, result):
|
||||
# Python 2 tuple params should be ignored for now.
|
||||
grammar = load_grammar('grammar%s.%s' % sys.version_info[:2])
|
||||
m = Parser(grammar, u(src)).module
|
||||
grammar = load_grammar('%s.%s' % sys.version_info[:2])
|
||||
m = ParserWithRecovery(grammar, u(src)).module
|
||||
if is_py3:
|
||||
assert not m.subscopes
|
||||
else:
|
||||
@@ -211,5 +211,28 @@ def test_unicode_string():
|
||||
|
||||
def test_backslash_dos_style():
|
||||
grammar = load_grammar()
|
||||
m = Parser(grammar, u('\\\r\n')).module
|
||||
m = ParserWithRecovery(grammar, u('\\\r\n')).module
|
||||
assert m
|
||||
|
||||
|
||||
def test_started_lambda_stmt():
|
||||
p = ParserWithRecovery(load_grammar(), u'lambda a, b: a i')
|
||||
assert p.get_parsed_node().children[0].type == 'error_node'
|
||||
|
||||
|
||||
def test_python2_octal():
|
||||
parser = ParserWithRecovery(load_grammar(), u'0660')
|
||||
first = parser.get_parsed_node().children[0]
|
||||
if is_py3:
|
||||
assert first.type == 'error_node'
|
||||
else:
|
||||
assert first.children[0].type == 'number'
|
||||
|
||||
|
||||
def test_python3_octal():
|
||||
parser = ParserWithRecovery(load_grammar(), u'0o660')
|
||||
module = parser.get_parsed_node()
|
||||
if is_py3:
|
||||
assert module.children[0].children[0].type == 'number'
|
||||
else:
|
||||
assert module.children[0].type == 'error_node'
|
||||
|
||||
@@ -5,17 +5,18 @@ from textwrap import dedent
|
||||
import pytest
|
||||
|
||||
from jedi._compatibility import u, unicode
|
||||
from jedi.parser import Parser, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
from jedi.parser import tree as pt
|
||||
|
||||
|
||||
class TestsFunctionAndLambdaParsing(object):
|
||||
|
||||
FIXTURES = [
|
||||
('def my_function(x, y, z):\n return x + y * z\n', {
|
||||
('def my_function(x, y, z) -> str:\n return x + y * z\n', {
|
||||
'name': 'my_function',
|
||||
'call_sig': 'my_function(x, y, z)',
|
||||
'params': ['x', 'y', 'z'],
|
||||
'annotation': "str",
|
||||
}),
|
||||
('lambda x, y, z: x + y * z\n', {
|
||||
'name': '<lambda>',
|
||||
@@ -26,7 +27,7 @@ class TestsFunctionAndLambdaParsing(object):
|
||||
|
||||
@pytest.fixture(params=FIXTURES)
|
||||
def node(self, request):
|
||||
parsed = Parser(load_grammar(), dedent(u(request.param[0])))
|
||||
parsed = ParserWithRecovery(load_grammar(), dedent(u(request.param[0])))
|
||||
request.keywords['expected'] = request.param[1]
|
||||
return parsed.module.subscopes[0]
|
||||
|
||||
@@ -55,7 +56,11 @@ class TestsFunctionAndLambdaParsing(object):
|
||||
assert not node.yields
|
||||
|
||||
def test_annotation(self, node, expected):
|
||||
assert node.annotation() is expected.get('annotation', None)
|
||||
expected_annotation = expected.get('annotation', None)
|
||||
if expected_annotation is None:
|
||||
assert node.annotation() is None
|
||||
else:
|
||||
assert node.annotation().value == expected_annotation
|
||||
|
||||
def test_get_call_signature(self, node, expected):
|
||||
assert node.get_call_signature() == expected['call_sig']
|
||||
|
||||
279
test/test_parser/test_pgen2.py
Normal file
279
test/test_parser/test_pgen2.py
Normal file
@@ -0,0 +1,279 @@
|
||||
"""Test suite for 2to3's parser and grammar files.
|
||||
|
||||
This is the place to add tests for changes to 2to3's grammar, such as those
|
||||
merging the grammars for Python 2 and 3. In addition to specific tests for
|
||||
parts of the grammar we've changed, we also make sure we can parse the
|
||||
test_grammar.py files from both Python 2 and Python 3.
|
||||
"""
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
from jedi._compatibility import unicode, is_py3
|
||||
from jedi.parser import Parser, load_grammar, ParseError
|
||||
import pytest
|
||||
|
||||
from test.helpers import TestCase
|
||||
|
||||
|
||||
def parse(code, version='3.4'):
|
||||
code = dedent(code) + "\n\n"
|
||||
grammar = load_grammar(version=version)
|
||||
return Parser(grammar, unicode(code), 'file_input').get_parsed_node()
|
||||
|
||||
|
||||
class TestDriver(TestCase):
|
||||
|
||||
def test_formfeed(self):
|
||||
s = """print 1\n\x0Cprint 2\n"""
|
||||
t = parse(s, '2.7')
|
||||
self.assertEqual(t.children[0].children[0].type, 'print_stmt')
|
||||
self.assertEqual(t.children[1].children[0].type, 'print_stmt')
|
||||
s = """1\n\x0C\x0C2\n"""
|
||||
t = parse(s, '2.7')
|
||||
|
||||
|
||||
class GrammarTest(TestCase):
|
||||
def invalid_syntax(self, code, **kwargs):
|
||||
try:
|
||||
parse(code, **kwargs)
|
||||
except ParseError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("Syntax shouldn't have been valid")
|
||||
|
||||
|
||||
class TestMatrixMultiplication(GrammarTest):
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
def test_matrix_multiplication_operator(self):
|
||||
parse("a @ b", "3.5")
|
||||
parse("a @= b", "3.5")
|
||||
|
||||
|
||||
class TestYieldFrom(GrammarTest):
|
||||
def test_yield_from(self):
|
||||
parse("yield from x")
|
||||
parse("(yield from x) + y")
|
||||
self.invalid_syntax("yield from")
|
||||
|
||||
|
||||
class TestAsyncAwait(GrammarTest):
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
def test_await_expr(self):
|
||||
parse("""async def foo():
|
||||
await x
|
||||
""", "3.5")
|
||||
|
||||
parse("""async def foo():
|
||||
|
||||
def foo(): pass
|
||||
|
||||
def foo(): pass
|
||||
|
||||
await x
|
||||
""", "3.5")
|
||||
|
||||
parse("""async def foo(): return await a""", "3.5")
|
||||
|
||||
parse("""def foo():
|
||||
def foo(): pass
|
||||
async def foo(): await x
|
||||
""", "3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
@pytest.mark.xfail(reason="acting like python 3.7")
|
||||
def test_await_expr_invalid(self):
|
||||
self.invalid_syntax("await x", version="3.5")
|
||||
self.invalid_syntax("""def foo():
|
||||
await x""", version="3.5")
|
||||
|
||||
self.invalid_syntax("""def foo():
|
||||
def foo(): pass
|
||||
async def foo(): pass
|
||||
await x
|
||||
""", version="3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
@pytest.mark.xfail(reason="acting like python 3.7")
|
||||
def test_async_var(self):
|
||||
parse("""async = 1""", "3.5")
|
||||
parse("""await = 1""", "3.5")
|
||||
parse("""def async(): pass""", "3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
def test_async_for(self):
|
||||
parse("""async def foo():
|
||||
async for a in b: pass""", "3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
@pytest.mark.xfail(reason="acting like python 3.7")
|
||||
def test_async_for_invalid(self):
|
||||
self.invalid_syntax("""def foo():
|
||||
async for a in b: pass""", version="3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
def test_async_with(self):
|
||||
parse("""async def foo():
|
||||
async with a: pass""", "3.5")
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[:2] < (3, 5)')
|
||||
@pytest.mark.xfail(reason="acting like python 3.7")
|
||||
def test_async_with_invalid(self):
|
||||
self.invalid_syntax("""def foo():
|
||||
async with a: pass""", version="3.5")
|
||||
|
||||
|
||||
class TestRaiseChanges(GrammarTest):
|
||||
def test_2x_style_1(self):
|
||||
parse("raise")
|
||||
|
||||
def test_2x_style_2(self):
|
||||
parse("raise E, V", version='2.7')
|
||||
|
||||
def test_2x_style_3(self):
|
||||
parse("raise E, V, T", version='2.7')
|
||||
|
||||
def test_2x_style_invalid_1(self):
|
||||
self.invalid_syntax("raise E, V, T, Z", version='2.7')
|
||||
|
||||
def test_3x_style(self):
|
||||
parse("raise E1 from E2")
|
||||
|
||||
def test_3x_style_invalid_1(self):
|
||||
self.invalid_syntax("raise E, V from E1")
|
||||
|
||||
def test_3x_style_invalid_2(self):
|
||||
self.invalid_syntax("raise E from E1, E2")
|
||||
|
||||
def test_3x_style_invalid_3(self):
|
||||
self.invalid_syntax("raise from E1, E2")
|
||||
|
||||
def test_3x_style_invalid_4(self):
|
||||
self.invalid_syntax("raise E from")
|
||||
|
||||
|
||||
# Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testFuncdef
|
||||
class TestFunctionAnnotations(GrammarTest):
|
||||
def test_1(self):
|
||||
parse("""def f(x) -> list: pass""")
|
||||
|
||||
def test_2(self):
|
||||
parse("""def f(x:int): pass""")
|
||||
|
||||
def test_3(self):
|
||||
parse("""def f(*x:str): pass""")
|
||||
|
||||
def test_4(self):
|
||||
parse("""def f(**x:float): pass""")
|
||||
|
||||
def test_5(self):
|
||||
parse("""def f(x, y:1+2): pass""")
|
||||
|
||||
def test_6(self):
|
||||
self.invalid_syntax("""def f(a, (b:1, c:2, d)): pass""")
|
||||
|
||||
def test_7(self):
|
||||
self.invalid_syntax("""def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6): pass""")
|
||||
|
||||
def test_8(self):
|
||||
s = """def f(a, (b:1, c:2, d), e:3=4, f=5,
|
||||
*g:6, h:7, i=8, j:9=10, **k:11) -> 12: pass"""
|
||||
self.invalid_syntax(s)
|
||||
|
||||
|
||||
class TestExcept(GrammarTest):
|
||||
def test_new(self):
|
||||
s = """
|
||||
try:
|
||||
x
|
||||
except E as N:
|
||||
y"""
|
||||
parse(s)
|
||||
|
||||
def test_old(self):
|
||||
s = """
|
||||
try:
|
||||
x
|
||||
except E, N:
|
||||
y"""
|
||||
parse(s, version='2.7')
|
||||
|
||||
|
||||
# Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testAtoms
|
||||
class TestSetLiteral(GrammarTest):
|
||||
def test_1(self):
|
||||
parse("""x = {'one'}""")
|
||||
|
||||
def test_2(self):
|
||||
parse("""x = {'one', 1,}""")
|
||||
|
||||
def test_3(self):
|
||||
parse("""x = {'one', 'two', 'three'}""")
|
||||
|
||||
def test_4(self):
|
||||
parse("""x = {2, 3, 4,}""")
|
||||
|
||||
|
||||
class TestNumericLiterals(GrammarTest):
|
||||
def test_new_octal_notation(self):
|
||||
code = """0o7777777777777"""
|
||||
if is_py3:
|
||||
parse(code)
|
||||
else:
|
||||
self.invalid_syntax(code)
|
||||
self.invalid_syntax("""0o7324528887""")
|
||||
|
||||
def test_new_binary_notation(self):
|
||||
parse("""0b101010""")
|
||||
self.invalid_syntax("""0b0101021""")
|
||||
|
||||
|
||||
class TestClassDef(GrammarTest):
|
||||
def test_new_syntax(self):
|
||||
parse("class B(t=7): pass")
|
||||
parse("class B(t, *args): pass")
|
||||
parse("class B(t, **kwargs): pass")
|
||||
parse("class B(t, *args, **kwargs): pass")
|
||||
parse("class B(t, y=9, *args, **kwargs): pass")
|
||||
|
||||
|
||||
class TestParserIdempotency(TestCase):
|
||||
"""A cut-down version of pytree_idempotency.py."""
|
||||
def test_extended_unpacking(self):
|
||||
parse("a, *b, c = x\n")
|
||||
parse("[*a, b] = x\n")
|
||||
parse("(z, *y, w) = m\n")
|
||||
parse("for *z, m in d: pass\n")
|
||||
|
||||
|
||||
class TestLiterals(GrammarTest):
|
||||
# It's not possible to get the same result when using \xaa in Python 2/3,
|
||||
# because it's treated differently.
|
||||
@pytest.mark.skipif('sys.version_info[0] < 3')
|
||||
def test_multiline_bytes_literals(self):
|
||||
s = """
|
||||
md5test(b"\xaa" * 80,
|
||||
(b"Test Using Larger Than Block-Size Key "
|
||||
b"and Larger Than One Block-Size Data"),
|
||||
"6f630fad67cda0ee1fb1f562db3aa53e")
|
||||
"""
|
||||
parse(s)
|
||||
|
||||
def test_multiline_bytes_tripquote_literals(self):
|
||||
s = '''
|
||||
b"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN">
|
||||
"""
|
||||
'''
|
||||
parse(s)
|
||||
|
||||
@pytest.mark.skipif('sys.version_info[0] < 3')
|
||||
def test_multiline_str_literals(self):
|
||||
s = """
|
||||
md5test("\xaa" * 80,
|
||||
("Test Using Larger Than Block-Size Key "
|
||||
"and Larger Than One Block-Size Data"),
|
||||
"6f630fad67cda0ee1fb1f562db3aa53e")
|
||||
"""
|
||||
parse(s)
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
|
||||
from jedi._compatibility import u, is_py3
|
||||
from jedi.parser.token import NAME, OP, NEWLINE, STRING, INDENT
|
||||
from jedi.parser import Parser, load_grammar, tokenize
|
||||
from jedi.parser import ParserWithRecovery, load_grammar, tokenize
|
||||
|
||||
|
||||
from ..helpers import unittest
|
||||
@@ -15,7 +15,7 @@ from ..helpers import unittest
|
||||
|
||||
class TokenTest(unittest.TestCase):
|
||||
def test_end_pos_one_line(self):
|
||||
parsed = Parser(load_grammar(), dedent(u('''
|
||||
parsed = ParserWithRecovery(load_grammar(), dedent(u('''
|
||||
def testit():
|
||||
a = "huhu"
|
||||
''')))
|
||||
@@ -23,7 +23,7 @@ class TokenTest(unittest.TestCase):
|
||||
assert tok.end_pos == (3, 14)
|
||||
|
||||
def test_end_pos_multi_line(self):
|
||||
parsed = Parser(load_grammar(), dedent(u('''
|
||||
parsed = ParserWithRecovery(load_grammar(), dedent(u('''
|
||||
def testit():
|
||||
a = """huhu
|
||||
asdfasdf""" + "h"
|
||||
@@ -108,7 +108,7 @@ class TokenTest(unittest.TestCase):
|
||||
]
|
||||
|
||||
for s in string_tokens:
|
||||
parsed = Parser(load_grammar(), u('''a = %s\n''' % s))
|
||||
parsed = ParserWithRecovery(load_grammar(), u('''a = %s\n''' % s))
|
||||
simple_stmt = parsed.module.children[0]
|
||||
expr_stmt = simple_stmt.children[0]
|
||||
assert len(expr_stmt.children) == 3
|
||||
|
||||
@@ -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, load_grammar
|
||||
from jedi.parser import ParserWithRecovery, load_grammar
|
||||
|
||||
#jedi.set_debug_function()
|
||||
|
||||
@@ -102,7 +102,7 @@ class TestRegression(TestCase):
|
||||
def test_end_pos_line(self):
|
||||
# jedi issue #150
|
||||
s = u("x()\nx( )\nx( )\nx ( )")
|
||||
parser = Parser(load_grammar(), s)
|
||||
parser = ParserWithRecovery(load_grammar(), s)
|
||||
for i, s in enumerate(parser.module.statements):
|
||||
assert s.end_pos == (i + 1, i + 3)
|
||||
|
||||
@@ -146,7 +146,7 @@ class TestRegression(TestCase):
|
||||
x = 0
|
||||
a = \
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, (x)] # <-- here
|
||||
""", '(x)] # <-- here', [])
|
||||
""", '(x)] # <-- here', ['int'])
|
||||
|
||||
def test_generator(self):
|
||||
# Did have some problems with the usage of generator completions this
|
||||
|
||||
@@ -34,7 +34,7 @@ class TestSetupReadline(unittest.TestCase):
|
||||
def test_simple(self):
|
||||
assert self.completions('list') == ['list']
|
||||
assert self.completions('importerror') == ['ImportError']
|
||||
s = "print BaseE"
|
||||
s = "print(BaseE"
|
||||
assert self.completions(s) == [s + 'xception']
|
||||
|
||||
def test_nested(self):
|
||||
@@ -53,7 +53,8 @@ class TestSetupReadline(unittest.TestCase):
|
||||
|
||||
try:
|
||||
assert self.completions('os.path.join') == ['os.path.join']
|
||||
assert self.completions('os.path.join().upper') == ['os.path.join().upper']
|
||||
string = 'os.path.join("a").upper'
|
||||
assert self.completions(string) == [string]
|
||||
|
||||
c = set(['os.' + d for d in dir(os) if d.startswith('ch')])
|
||||
assert set(self.completions('os.ch')) == set(c)
|
||||
|
||||
Reference in New Issue
Block a user