Merge remote-tracking branch 'upstream/dev' into bugfix/performances_degradation

# Conflicts:
#	jedi/evaluate/compiled/fake.py
This commit is contained in:
ColinDuquesnoy
2016-07-21 10:41:11 +02:00
124 changed files with 6536 additions and 2897 deletions

View File

@@ -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]

View File

@@ -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
# -----------------

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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(
)

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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']

View File

@@ -0,0 +1,4 @@
if name:
env = 1
else:
env = 2

View File

@@ -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
#?

View File

@@ -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

View File

@@ -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())

View File

@@ -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

View File

@@ -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

View File

@@ -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']

View File

@@ -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

View File

@@ -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
View 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

View 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

View 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

View File

@@ -48,6 +48,14 @@ a = 3 * "a"
#? str()
a
a = 3 * "a"
#? str()
a
#? int()
(3 ** 3)
#? int() str()
(3 ** 'a')
# -----------------
# assignments

View File

@@ -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

View File

@@ -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
# -----------------

View File

@@ -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),

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View 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, '')

View 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])

View 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()

View 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)])

View File

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

View 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

View File

@@ -0,0 +1,7 @@
def raises():
raise KeyError()
def wrong_name():
#! 6 name-error
raise NotExistingException()

View File

@@ -9,3 +9,8 @@
-1 - int()
int() - float()
float() - 3.0
a = 3
b = ''
#! 2 type-error-operation
a + b

View 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

View File

@@ -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')

View File

@@ -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()

View File

@@ -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'

View File

@@ -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() == []

View File

@@ -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'])

View 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]

View File

@@ -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)

View File

@@ -0,0 +1 @@
‰PNG

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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'

View 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

View File

@@ -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() == ''

View 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

View File

@@ -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']

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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'))

View File

@@ -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)

View File

@@ -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))

View 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)

View File

@@ -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'

View File

@@ -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']

View 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)

View File

@@ -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

View File

@@ -15,7 +15,7 @@ from jedi._compatibility import u
from jedi import Script
from jedi import api
from jedi.evaluate import imports
from jedi.parser import Parser, 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

View File

@@ -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)