forked from ipython/ipython
-
Notifications
You must be signed in to change notification settings - Fork 2
/
test_inputtransformer2.py
337 lines (269 loc) · 9.4 KB
/
test_inputtransformer2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
"""Tests for the token-based transformers in IPython.core.inputtransformer2
Line-based transformers are the simpler ones; token-based transformers are
more complex. See test_inputtransformer2_line for tests for line-based
transformations.
"""
import nose.tools as nt
import string
from IPython.core import inputtransformer2 as ipt2
from IPython.core.inputtransformer2 import make_tokens_by_line, _find_assign_op
from textwrap import dedent
MULTILINE_MAGIC = ("""\
a = f()
%foo \\
bar
g()
""".splitlines(keepends=True), (2, 0), """\
a = f()
get_ipython().run_line_magic('foo', ' bar')
g()
""".splitlines(keepends=True))
INDENTED_MAGIC = ("""\
for a in range(5):
%ls
""".splitlines(keepends=True), (2, 4), """\
for a in range(5):
get_ipython().run_line_magic('ls', '')
""".splitlines(keepends=True))
CRLF_MAGIC = ([
"a = f()\n",
"%ls\r\n",
"g()\n"
], (2, 0), [
"a = f()\n",
"get_ipython().run_line_magic('ls', '')\n",
"g()\n"
])
MULTILINE_MAGIC_ASSIGN = ("""\
a = f()
b = %foo \\
bar
g()
""".splitlines(keepends=True), (2, 4), """\
a = f()
b = get_ipython().run_line_magic('foo', ' bar')
g()
""".splitlines(keepends=True))
MULTILINE_SYSTEM_ASSIGN = ("""\
a = f()
b = !foo \\
bar
g()
""".splitlines(keepends=True), (2, 4), """\
a = f()
b = get_ipython().getoutput('foo bar')
g()
""".splitlines(keepends=True))
#####
MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
def test():
for i in range(1):
print(i)
res =! ls
""".splitlines(keepends=True), (4, 7), '''\
def test():
for i in range(1):
print(i)
res =get_ipython().getoutput(\' ls\')
'''.splitlines(keepends=True))
######
AUTOCALL_QUOTE = (
[",f 1 2 3\n"], (1, 0),
['f("1", "2", "3")\n']
)
AUTOCALL_QUOTE2 = (
[";f 1 2 3\n"], (1, 0),
['f("1 2 3")\n']
)
AUTOCALL_PAREN = (
["/f 1 2 3\n"], (1, 0),
['f(1, 2, 3)\n']
)
SIMPLE_HELP = (
["foo?\n"], (1, 0),
["get_ipython().run_line_magic('pinfo', 'foo')\n"]
)
DETAILED_HELP = (
["foo??\n"], (1, 0),
["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
)
MAGIC_HELP = (
["%foo?\n"], (1, 0),
["get_ipython().run_line_magic('pinfo', '%foo')\n"]
)
HELP_IN_EXPR = (
["a = b + c?\n"], (1, 0),
["get_ipython().set_next_input('a = b + c');"
"get_ipython().run_line_magic('pinfo', 'c')\n"]
)
HELP_CONTINUED_LINE = ("""\
a = \\
zip?
""".splitlines(keepends=True), (1, 0),
[r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
)
HELP_MULTILINE = ("""\
(a,
b) = zip?
""".splitlines(keepends=True), (1, 0),
[r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
)
HELP_UNICODE = (
["π.foo?\n"], (1, 0),
["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
)
def null_cleanup_transformer(lines):
"""
A cleanup transform that returns an empty list.
"""
return []
def check_make_token_by_line_never_ends_empty():
"""
Check that not sequence of single or double characters ends up leading to en empty list of tokens
"""
from string import printable
for c in printable:
nt.assert_not_equal(make_tokens_by_line(c)[-1], [])
for k in printable:
nt.assert_not_equal(make_tokens_by_line(c+k)[-1], [])
def check_find(transformer, case, match=True):
sample, expected_start, _ = case
tbl = make_tokens_by_line(sample)
res = transformer.find(tbl)
if match:
# start_line is stored 0-indexed, expected values are 1-indexed
nt.assert_equal((res.start_line+1, res.start_col), expected_start)
return res
else:
nt.assert_is(res, None)
def check_transform(transformer_cls, case):
lines, start, expected = case
transformer = transformer_cls(start)
nt.assert_equal(transformer.transform(lines), expected)
def test_continued_line():
lines = MULTILINE_MAGIC_ASSIGN[0]
nt.assert_equal(ipt2.find_end_of_continued_line(lines, 1), 2)
nt.assert_equal(ipt2.assemble_continued_line(lines, (1, 5), 2), "foo bar")
def test_find_assign_magic():
check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
def test_transform_assign_magic():
check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
def test_find_assign_system():
check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None))
check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
def test_transform_assign_system():
check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
def test_find_magic_escape():
check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
def test_transform_magic_escape():
check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
def test_find_autocalls():
for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
print("Testing %r" % case[0])
check_find(ipt2.EscapedCommand, case)
def test_transform_autocall():
for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
print("Testing %r" % case[0])
check_transform(ipt2.EscapedCommand, case)
def test_find_help():
for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
check_find(ipt2.HelpEnd, case)
tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE)
nt.assert_equal(tf.q_line, 1)
nt.assert_equal(tf.q_col, 3)
tf = check_find(ipt2.HelpEnd, HELP_MULTILINE)
nt.assert_equal(tf.q_line, 1)
nt.assert_equal(tf.q_col, 8)
# ? in a comment does not trigger help
check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False)
# Nor in a string
check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
def test_transform_help():
tf = ipt2.HelpEnd((1, 0), (1, 9))
nt.assert_equal(tf.transform(HELP_IN_EXPR[0]), HELP_IN_EXPR[2])
tf = ipt2.HelpEnd((1, 0), (2, 3))
nt.assert_equal(tf.transform(HELP_CONTINUED_LINE[0]), HELP_CONTINUED_LINE[2])
tf = ipt2.HelpEnd((1, 0), (2, 8))
nt.assert_equal(tf.transform(HELP_MULTILINE[0]), HELP_MULTILINE[2])
tf = ipt2.HelpEnd((1, 0), (1, 0))
nt.assert_equal(tf.transform(HELP_UNICODE[0]), HELP_UNICODE[2])
def test_find_assign_op_dedent():
"""
be careful that empty token like dedent are not counted as parens
"""
class Tk:
def __init__(self, s):
self.string = s
nt.assert_equal(_find_assign_op([Tk(s) for s in ('','a','=','b')]), 2)
nt.assert_equal(_find_assign_op([Tk(s) for s in ('','(', 'a','=','b', ')', '=' ,'5')]), 6)
def test_check_complete():
cc = ipt2.TransformerManager().check_complete
nt.assert_equal(cc("a = 1"), ('complete', None))
nt.assert_equal(cc("for a in range(5):"), ('incomplete', 4))
nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ('incomplete', 8))
nt.assert_equal(cc("raise = 2"), ('invalid', None))
nt.assert_equal(cc("a = [1,\n2,"), ('incomplete', 0))
nt.assert_equal(cc(")"), ('incomplete', 0))
nt.assert_equal(cc("\\\r\n"), ('incomplete', 0))
nt.assert_equal(cc("a = '''\n hi"), ('incomplete', 3))
nt.assert_equal(cc("def a():\n x=1\n global x"), ('invalid', None))
nt.assert_equal(cc("a \\ "), ('invalid', None)) # Nothing allowed after backslash
nt.assert_equal(cc("1\\\n+2"), ('complete', None))
nt.assert_equal(cc("exit"), ('complete', None))
example = dedent("""
if True:
a=1""" )
nt.assert_equal(cc(example), ('incomplete', 4))
nt.assert_equal(cc(example+'\n'), ('complete', None))
nt.assert_equal(cc(example+'\n '), ('complete', None))
# no need to loop on all the letters/numbers.
short = '12abAB'+string.printable[62:]
for c in short:
# test does not raise:
cc(c)
for k in short:
cc(c+k)
nt.assert_equal(cc("def f():\n x=0\n \\\n "), ('incomplete', 2))
def test_check_complete_II():
"""
Test that multiple line strings are properly handled.
Separate test function for convenience
"""
cc = ipt2.TransformerManager().check_complete
nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4))
def test_null_cleanup_transformer():
manager = ipt2.TransformerManager()
manager.cleanup_transforms.insert(0, null_cleanup_transformer)
assert manager.transform_cell("") == ""
def test_side_effects_I():
count = 0
def counter(lines):
nonlocal count
count += 1
return lines
counter.has_side_effects = True
manager = ipt2.TransformerManager()
manager.cleanup_transforms.insert(0, counter)
assert manager.check_complete("a=1\n") == ('complete', None)
assert count == 0
def test_side_effects_II():
count = 0
def counter(lines):
nonlocal count
count += 1
return lines
counter.has_side_effects = True
manager = ipt2.TransformerManager()
manager.line_transforms.insert(0, counter)
assert manager.check_complete("b=1\n") == ('complete', None)
assert count == 0