##// END OF EJS Templates
Escaped commands
Thomas Kluyver -
Show More
@@ -1,268 +1,383 b''
1 import re
1 import re
2 from typing import List, Tuple
2 from typing import List, Tuple
3 from IPython.utils import tokenize2
3 from IPython.utils import tokenize2
4 from IPython.utils.tokenutil import generate_tokens
4 from IPython.utils.tokenutil import generate_tokens
5
5
6 def leading_indent(lines):
6 def leading_indent(lines):
7 """Remove leading indentation.
7 """Remove leading indentation.
8
8
9 If the first line starts with a spaces or tabs, the same whitespace will be
9 If the first line starts with a spaces or tabs, the same whitespace will be
10 removed from each following line.
10 removed from each following line.
11 """
11 """
12 m = re.match(r'^[ \t]+', lines[0])
12 m = re.match(r'^[ \t]+', lines[0])
13 if not m:
13 if not m:
14 return lines
14 return lines
15 space = m.group(0)
15 space = m.group(0)
16 n = len(space)
16 n = len(space)
17 return [l[n:] if l.startswith(space) else l
17 return [l[n:] if l.startswith(space) else l
18 for l in lines]
18 for l in lines]
19
19
20 class PromptStripper:
20 class PromptStripper:
21 """Remove matching input prompts from a block of input.
21 """Remove matching input prompts from a block of input.
22
22
23 Parameters
23 Parameters
24 ----------
24 ----------
25 prompt_re : regular expression
25 prompt_re : regular expression
26 A regular expression matching any input prompt (including continuation)
26 A regular expression matching any input prompt (including continuation)
27 initial_re : regular expression, optional
27 initial_re : regular expression, optional
28 A regular expression matching only the initial prompt, but not continuation.
28 A regular expression matching only the initial prompt, but not continuation.
29 If no initial expression is given, prompt_re will be used everywhere.
29 If no initial expression is given, prompt_re will be used everywhere.
30 Used mainly for plain Python prompts, where the continuation prompt
30 Used mainly for plain Python prompts, where the continuation prompt
31 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
31 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
32
32
33 If initial_re and prompt_re differ,
33 If initial_re and prompt_re differ,
34 only initial_re will be tested against the first line.
34 only initial_re will be tested against the first line.
35 If any prompt is found on the first two lines,
35 If any prompt is found on the first two lines,
36 prompts will be stripped from the rest of the block.
36 prompts will be stripped from the rest of the block.
37 """
37 """
38 def __init__(self, prompt_re, initial_re=None):
38 def __init__(self, prompt_re, initial_re=None):
39 self.prompt_re = prompt_re
39 self.prompt_re = prompt_re
40 self.initial_re = initial_re or prompt_re
40 self.initial_re = initial_re or prompt_re
41
41
42 def _strip(self, lines):
42 def _strip(self, lines):
43 return [self.prompt_re.sub('', l, count=1) for l in lines]
43 return [self.prompt_re.sub('', l, count=1) for l in lines]
44
44
45 def __call__(self, lines):
45 def __call__(self, lines):
46 if self.initial_re.match(lines[0]) or \
46 if self.initial_re.match(lines[0]) or \
47 (len(lines) > 1 and self.prompt_re.match(lines[1])):
47 (len(lines) > 1 and self.prompt_re.match(lines[1])):
48 return self._strip(lines)
48 return self._strip(lines)
49 return lines
49 return lines
50
50
51 classic_prompt = PromptStripper(
51 classic_prompt = PromptStripper(
52 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
52 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
53 initial_re=re.compile(r'^>>>( |$)')
53 initial_re=re.compile(r'^>>>( |$)')
54 )
54 )
55
55
56 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
56 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
57
57
58 def cell_magic(lines):
58 def cell_magic(lines):
59 if not lines[0].startswith('%%'):
59 if not lines[0].startswith('%%'):
60 return lines
60 return lines
61 if re.match('%%\w+\?', lines[0]):
61 if re.match('%%\w+\?', lines[0]):
62 # This case will be handled by help_end
62 # This case will be handled by help_end
63 return lines
63 return lines
64 magic_name, first_line = lines[0][2:].partition(' ')
64 magic_name, first_line = lines[0][2:].partition(' ')
65 body = '\n'.join(lines[1:])
65 body = '\n'.join(lines[1:])
66 return ['get_ipython().run_cell_magic(%r, %r, %r)' % (magic_name, first_line, body)]
66 return ['get_ipython().run_cell_magic(%r, %r, %r)' % (magic_name, first_line, body)]
67
67
68 line_transforms = [
68 line_transforms = [
69 leading_indent,
69 leading_indent,
70 classic_prompt,
70 classic_prompt,
71 ipython_prompt,
71 ipython_prompt,
72 cell_magic,
72 cell_magic,
73 ]
73 ]
74
74
75 # -----
75 # -----
76
76
77 def help_end(tokens_by_line):
77 def help_end(tokens_by_line):
78 pass
78 pass
79
79
80 def escaped_command(tokens_by_line):
80 def escaped_command(tokens_by_line):
81 pass
81 pass
82
82
83 def _find_assign_op(token_line):
83 def _find_assign_op(token_line):
84 # Find the first assignment in the line ('=' not inside brackets)
84 # Find the first assignment in the line ('=' not inside brackets)
85 # We don't try to support multiple special assignment (a = b = %foo)
85 # We don't try to support multiple special assignment (a = b = %foo)
86 paren_level = 0
86 paren_level = 0
87 for i, ti in enumerate(token_line):
87 for i, ti in enumerate(token_line):
88 s = ti.string
88 s = ti.string
89 if s == '=' and paren_level == 0:
89 if s == '=' and paren_level == 0:
90 return i
90 return i
91 if s in '([{':
91 if s in '([{':
92 paren_level += 1
92 paren_level += 1
93 elif s in ')]}':
93 elif s in ')]}':
94 paren_level -= 1
94 paren_level -= 1
95
95
96 def find_end_of_continued_line(lines, start_line: int):
96 def find_end_of_continued_line(lines, start_line: int):
97 """Find the last line of a line explicitly extended using backslashes.
97 """Find the last line of a line explicitly extended using backslashes.
98
98
99 Uses 0-indexed line numbers.
99 Uses 0-indexed line numbers.
100 """
100 """
101 end_line = start_line
101 end_line = start_line
102 while lines[end_line].endswith('\\\n'):
102 while lines[end_line].endswith('\\\n'):
103 end_line += 1
103 end_line += 1
104 if end_line >= len(lines):
104 if end_line >= len(lines):
105 break
105 break
106 return end_line
106 return end_line
107
107
108 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
108 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
109 """Assemble pieces of a continued line into a single line.
109 """Assemble pieces of a continued line into a single line.
110
110
111 Uses 0-indexed line numbers. *start* is (lineno, colno).
111 Uses 0-indexed line numbers. *start* is (lineno, colno).
112 """
112 """
113 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
113 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
114 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
114 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
115 + [parts[-1][:-1]]) # Strip newline from last line
115 + [parts[-1][:-1]]) # Strip newline from last line
116
116
117 class MagicAssign:
117 class MagicAssign:
118 @staticmethod
118 @staticmethod
119 def find(tokens_by_line):
119 def find(tokens_by_line):
120 """Find the first magic assignment (a = %foo) in the cell.
120 """Find the first magic assignment (a = %foo) in the cell.
121
121
122 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
122 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
123 """
123 """
124 for line in tokens_by_line:
124 for line in tokens_by_line:
125 assign_ix = _find_assign_op(line)
125 assign_ix = _find_assign_op(line)
126 if (assign_ix is not None) \
126 if (assign_ix is not None) \
127 and (len(line) >= assign_ix + 2) \
127 and (len(line) >= assign_ix + 2) \
128 and (line[assign_ix+1].string == '%') \
128 and (line[assign_ix+1].string == '%') \
129 and (line[assign_ix+2].type == tokenize2.NAME):
129 and (line[assign_ix+2].type == tokenize2.NAME):
130 return line[assign_ix+1].start
130 return line[assign_ix+1].start
131
131
132 @staticmethod
132 @staticmethod
133 def transform(lines: List[str], start: Tuple[int, int]):
133 def transform(lines: List[str], start: Tuple[int, int]):
134 """Transform a magic assignment found by find
134 """Transform a magic assignment found by find
135 """
135 """
136 start_line = start[0] - 1 # Shift from 1-index to 0-index
136 start_line = start[0] - 1 # Shift from 1-index to 0-index
137 start_col = start[1]
137 start_col = start[1]
138
138
139 lhs = lines[start_line][:start_col]
139 lhs = lines[start_line][:start_col]
140 end_line = find_end_of_continued_line(lines, start_line)
140 end_line = find_end_of_continued_line(lines, start_line)
141 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
141 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
142 assert rhs.startswith('%'), rhs
142 assert rhs.startswith('%'), rhs
143 magic_name, _, args = rhs[1:].partition(' ')
143 magic_name, _, args = rhs[1:].partition(' ')
144
144
145 lines_before = lines[:start_line]
145 lines_before = lines[:start_line]
146 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
146 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
147 new_line = lhs + call + '\n'
147 new_line = lhs + call + '\n'
148 lines_after = lines[end_line+1:]
148 lines_after = lines[end_line+1:]
149
149
150 return lines_before + [new_line] + lines_after
150 return lines_before + [new_line] + lines_after
151
151
152
152
153 class SystemAssign:
153 class SystemAssign:
154 @staticmethod
154 @staticmethod
155 def find(tokens_by_line):
155 def find(tokens_by_line):
156 """Find the first system assignment (a = !foo) in the cell.
156 """Find the first system assignment (a = !foo) in the cell.
157
157
158 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
158 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
159 """
159 """
160 for line in tokens_by_line:
160 for line in tokens_by_line:
161 assign_ix = _find_assign_op(line)
161 assign_ix = _find_assign_op(line)
162 if (assign_ix is not None) \
162 if (assign_ix is not None) \
163 and (len(line) >= assign_ix + 2) \
163 and (len(line) >= assign_ix + 2) \
164 and (line[assign_ix + 1].type == tokenize2.ERRORTOKEN):
164 and (line[assign_ix + 1].type == tokenize2.ERRORTOKEN):
165 ix = assign_ix + 1
165 ix = assign_ix + 1
166
166
167 while ix < len(line) and line[ix].type == tokenize2.ERRORTOKEN:
167 while ix < len(line) and line[ix].type == tokenize2.ERRORTOKEN:
168 if line[ix].string == '!':
168 if line[ix].string == '!':
169 return line[ix].start
169 return line[ix].start
170 elif not line[ix].string.isspace():
170 elif not line[ix].string.isspace():
171 break
171 break
172 ix += 1
172 ix += 1
173
173
174 @staticmethod
174 @staticmethod
175 def transform(lines: List[str], start: Tuple[int, int]):
175 def transform(lines: List[str], start: Tuple[int, int]):
176 """Transform a system assignment found by find
176 """Transform a system assignment found by find
177 """
177 """
178 start_line = start[0] - 1 # Shift from 1-index to 0-index
178 start_line = start[0] - 1 # Shift from 1-index to 0-index
179 start_col = start[1]
179 start_col = start[1]
180
180
181 lhs = lines[start_line][:start_col]
181 lhs = lines[start_line][:start_col]
182 end_line = find_end_of_continued_line(lines, start_line)
182 end_line = find_end_of_continued_line(lines, start_line)
183 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
183 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
184 assert rhs.startswith('!'), rhs
184 assert rhs.startswith('!'), rhs
185 cmd = rhs[1:]
185 cmd = rhs[1:]
186
186
187 lines_before = lines[:start_line]
187 lines_before = lines[:start_line]
188 call = "get_ipython().getoutput({!r})".format(cmd)
188 call = "get_ipython().getoutput({!r})".format(cmd)
189 new_line = lhs + call + '\n'
189 new_line = lhs + call + '\n'
190 lines_after = lines[end_line + 1:]
190 lines_after = lines[end_line + 1:]
191
191
192 return lines_before + [new_line] + lines_after
192 return lines_before + [new_line] + lines_after
193
193
194 # The escape sequences that define the syntax transformations IPython will
195 # apply to user input. These can NOT be just changed here: many regular
196 # expressions and other parts of the code may use their hardcoded values, and
197 # for all intents and purposes they constitute the 'IPython syntax', so they
198 # should be considered fixed.
199
200 ESC_SHELL = '!' # Send line to underlying system shell
201 ESC_SH_CAP = '!!' # Send line to system shell and capture output
202 ESC_HELP = '?' # Find information about object
203 ESC_HELP2 = '??' # Find extra-detailed information about object
204 ESC_MAGIC = '%' # Call magic function
205 ESC_MAGIC2 = '%%' # Call cell-magic function
206 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
207 ESC_QUOTE2 = ';' # Quote all args as a single string, call
208 ESC_PAREN = '/' # Call first argument with rest of line as arguments
209
210 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
211 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
212
213 def _make_help_call(target, esc, next_input=None):
214 """Prepares a pinfo(2)/psearch call from a target name and the escape
215 (i.e. ? or ??)"""
216 method = 'pinfo2' if esc == '??' \
217 else 'psearch' if '*' in target \
218 else 'pinfo'
219 arg = " ".join([method, target])
220 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
221 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
222 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
223 if next_input is None:
224 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
225 else:
226 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
227 (next_input, t_magic_name, t_magic_arg_s)
228
229 def _tr_help(content):
230 "Translate lines escaped with: ?"
231 # A naked help line should just fire the intro help screen
232 if not content:
233 return 'get_ipython().show_usage()'
234
235 return _make_help_call(content, '?')
236
237 def _tr_help2(content):
238 "Translate lines escaped with: ??"
239 # A naked help line should just fire the intro help screen
240 if not content:
241 return 'get_ipython().show_usage()'
242
243 return _make_help_call(content, '??')
244
245 def _tr_magic(content):
246 "Translate lines escaped with: %"
247 name, _, args = content.partition(' ')
248 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
249
250 def _tr_quote(content):
251 "Translate lines escaped with: ,"
252 name, _, args = content.partition(' ')
253 return '%s("%s")' % (name, '", "'.join(args.split()) )
254
255 def _tr_quote2(content):
256 "Translate lines escaped with: ;"
257 name, _, args = content.partition(' ')
258 return '%s("%s")' % (name, args)
259
260 def _tr_paren(content):
261 "Translate lines escaped with: /"
262 name, _, args = content.partition(' ')
263 return '%s(%s)' % (name, ", ".join(args.split()))
264
265 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
266 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
267 ESC_HELP : _tr_help,
268 ESC_HELP2 : _tr_help2,
269 ESC_MAGIC : _tr_magic,
270 ESC_QUOTE : _tr_quote,
271 ESC_QUOTE2 : _tr_quote2,
272 ESC_PAREN : _tr_paren }
273
274 class EscapedCommand:
275 @staticmethod
276 def find(tokens_by_line):
277 """Find the first escaped command (%foo, !foo, etc.) in the cell.
278
279 Returns (line, column) of the escape if found, or None. *line* is 1-indexed.
280 """
281 for line in tokens_by_line:
282 ix = 0
283 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
284 ix += 1
285 if line[ix].string in ESCAPE_SINGLES:
286 return line[ix].start
287
288 @staticmethod
289 def transform(lines, start):
290 start_line = start[0] - 1 # Shift from 1-index to 0-index
291 start_col = start[1]
292
293 indent = lines[start_line][:start_col]
294 end_line = find_end_of_continued_line(lines, start_line)
295 line = assemble_continued_line(lines, (start_line, start_col), end_line)
296
297 if line[:2] in ESCAPE_DOUBLES:
298 escape, content = line[:2], line[2:]
299 else:
300 escape, content = line[:1], line[1:]
301 call = tr[escape](content)
302
303 lines_before = lines[:start_line]
304 new_line = indent + call + '\n'
305 lines_after = lines[end_line + 1:]
306
307 return lines_before + [new_line] + lines_after
308
194 def make_tokens_by_line(lines):
309 def make_tokens_by_line(lines):
195 tokens_by_line = [[]]
310 tokens_by_line = [[]]
196 for token in generate_tokens(iter(lines).__next__):
311 for token in generate_tokens(iter(lines).__next__):
197 tokens_by_line[-1].append(token)
312 tokens_by_line[-1].append(token)
198 if token.type == tokenize2.NEWLINE:
313 if token.type == tokenize2.NEWLINE:
199 tokens_by_line.append([])
314 tokens_by_line.append([])
200
315
201 return tokens_by_line
316 return tokens_by_line
202
317
203 def show_linewise_tokens(s: str):
318 def show_linewise_tokens(s: str):
204 """For investigation"""
319 """For investigation"""
205 if not s.endswith('\n'):
320 if not s.endswith('\n'):
206 s += '\n'
321 s += '\n'
207 lines = s.splitlines(keepends=True)
322 lines = s.splitlines(keepends=True)
208 for line in make_tokens_by_line(lines):
323 for line in make_tokens_by_line(lines):
209 print("Line -------")
324 print("Line -------")
210 for tokinfo in line:
325 for tokinfo in line:
211 print(" ", tokinfo)
326 print(" ", tokinfo)
212
327
213 class TokenTransformers:
328 class TokenTransformers:
214 def __init__(self):
329 def __init__(self):
215 self.transformers = [
330 self.transformers = [
216 MagicAssign,
331 MagicAssign,
217 SystemAssign,
332 SystemAssign,
218 ]
333 ]
219
334
220 def do_one_transform(self, lines):
335 def do_one_transform(self, lines):
221 """Find and run the transform earliest in the code.
336 """Find and run the transform earliest in the code.
222
337
223 Returns (changed, lines).
338 Returns (changed, lines).
224
339
225 This method is called repeatedly until changed is False, indicating
340 This method is called repeatedly until changed is False, indicating
226 that all available transformations are complete.
341 that all available transformations are complete.
227
342
228 The tokens following IPython special syntax might not be valid, so
343 The tokens following IPython special syntax might not be valid, so
229 the transformed code is retokenised every time to identify the next
344 the transformed code is retokenised every time to identify the next
230 piece of special syntax. Hopefully long code cells are mostly valid
345 piece of special syntax. Hopefully long code cells are mostly valid
231 Python, not using lots of IPython special syntax, so this shouldn't be
346 Python, not using lots of IPython special syntax, so this shouldn't be
232 a performance issue.
347 a performance issue.
233 """
348 """
234 tokens_by_line = make_tokens_by_line(lines)
349 tokens_by_line = make_tokens_by_line(lines)
235 candidates = []
350 candidates = []
236 for transformer in self.transformers:
351 for transformer in self.transformers:
237 locn = transformer.find(tokens_by_line)
352 locn = transformer.find(tokens_by_line)
238 if locn:
353 if locn:
239 candidates.append((locn, transformer))
354 candidates.append((locn, transformer))
240
355
241 if not candidates:
356 if not candidates:
242 # Nothing to transform
357 # Nothing to transform
243 return False, lines
358 return False, lines
244
359
245 first_locn, transformer = min(candidates)
360 first_locn, transformer = min(candidates)
246 return True, transformer.transform(lines, first_locn)
361 return True, transformer.transform(lines, first_locn)
247
362
248 def __call__(self, lines):
363 def __call__(self, lines):
249 while True:
364 while True:
250 changed, lines = self.do_one_transform(lines)
365 changed, lines = self.do_one_transform(lines)
251 if not changed:
366 if not changed:
252 return lines
367 return lines
253
368
254 def assign_from_system(tokens_by_line, lines):
369 def assign_from_system(tokens_by_line, lines):
255 pass
370 pass
256
371
257
372
258 def transform_cell(cell):
373 def transform_cell(cell):
259 if not cell.endswith('\n'):
374 if not cell.endswith('\n'):
260 cell += '\n' # Ensure every line has a newline
375 cell += '\n' # Ensure every line has a newline
261 lines = cell.splitlines(keepends=True)
376 lines = cell.splitlines(keepends=True)
262 for transform in line_transforms:
377 for transform in line_transforms:
263 #print(transform, lines)
378 #print(transform, lines)
264 lines = transform(lines)
379 lines = transform(lines)
265
380
266 lines = TokenTransformers()(lines)
381 lines = TokenTransformers()(lines)
267 for line in lines:
382 for line in lines:
268 print('~~', line)
383 print('~~', line)
@@ -1,60 +1,82 b''
1 import nose.tools as nt
1 import nose.tools as nt
2
2
3 from IPython.core import inputtransformer2 as ipt2
3 from IPython.core import inputtransformer2 as ipt2
4 from IPython.core.inputtransformer2 import make_tokens_by_line
4 from IPython.core.inputtransformer2 import make_tokens_by_line
5
5
6 MULTILINE_MAGIC = ("""\
7 a = f()
8 %foo \\
9 bar
10 g()
11 """.splitlines(keepends=True), """\
12 a = f()
13 get_ipython().run_line_magic('foo', ' bar')
14 g()
15 """.splitlines(keepends=True))
16
6 MULTILINE_MAGIC_ASSIGN = ("""\
17 MULTILINE_MAGIC_ASSIGN = ("""\
7 a = f()
18 a = f()
8 b = %foo \\
19 b = %foo \\
9 bar
20 bar
10 g()
21 g()
11 """.splitlines(keepends=True), """\
22 """.splitlines(keepends=True), """\
12 a = f()
23 a = f()
13 b = get_ipython().run_line_magic('foo', ' bar')
24 b = get_ipython().run_line_magic('foo', ' bar')
14 g()
25 g()
15 """.splitlines(keepends=True))
26 """.splitlines(keepends=True))
16
27
17 MULTILINE_SYSTEM_ASSIGN = ("""\
28 MULTILINE_SYSTEM_ASSIGN = ("""\
18 a = f()
29 a = f()
19 b = !foo \\
30 b = !foo \\
20 bar
31 bar
21 g()
32 g()
22 """.splitlines(keepends=True), """\
33 """.splitlines(keepends=True), """\
23 a = f()
34 a = f()
24 b = get_ipython().getoutput('foo bar')
35 b = get_ipython().getoutput('foo bar')
25 g()
36 g()
26 """.splitlines(keepends=True))
37 """.splitlines(keepends=True))
27
38
28 def test_continued_line():
39 def test_continued_line():
29 lines = MULTILINE_MAGIC_ASSIGN[0]
40 lines = MULTILINE_MAGIC_ASSIGN[0]
30 nt.assert_equal(ipt2.find_end_of_continued_line(lines, 1), 2)
41 nt.assert_equal(ipt2.find_end_of_continued_line(lines, 1), 2)
31
42
32 nt.assert_equal(ipt2.assemble_continued_line(lines, (1, 5), 2), "foo bar")
43 nt.assert_equal(ipt2.assemble_continued_line(lines, (1, 5), 2), "foo bar")
33
44
34 def test_find_assign_magic():
45 def test_find_assign_magic():
35 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0])
46 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0])
36 nt.assert_equal(ipt2.MagicAssign.find(tbl), (2, 4))
47 nt.assert_equal(ipt2.MagicAssign.find(tbl), (2, 4))
37
48
38 tbl = make_tokens_by_line(MULTILINE_SYSTEM_ASSIGN[0]) # Nothing to find
49 tbl = make_tokens_by_line(MULTILINE_SYSTEM_ASSIGN[0]) # Nothing to find
39 nt.assert_equal(ipt2.MagicAssign.find(tbl), None)
50 nt.assert_equal(ipt2.MagicAssign.find(tbl), None)
40
51
41 def test_transform_assign_magic():
52 def test_transform_assign_magic():
42 res = ipt2.MagicAssign.transform(MULTILINE_MAGIC_ASSIGN[0], (2, 4))
53 res = ipt2.MagicAssign.transform(MULTILINE_MAGIC_ASSIGN[0], (2, 4))
43 nt.assert_equal(res, MULTILINE_MAGIC_ASSIGN[1])
54 nt.assert_equal(res, MULTILINE_MAGIC_ASSIGN[1])
44
55
45 def test_find_assign_system():
56 def test_find_assign_system():
46 tbl = make_tokens_by_line(MULTILINE_SYSTEM_ASSIGN[0])
57 tbl = make_tokens_by_line(MULTILINE_SYSTEM_ASSIGN[0])
47 nt.assert_equal(ipt2.SystemAssign.find(tbl), (2, 4))
58 nt.assert_equal(ipt2.SystemAssign.find(tbl), (2, 4))
48
59
49 tbl = make_tokens_by_line(["a = !ls\n"])
60 tbl = make_tokens_by_line(["a = !ls\n"])
50 nt.assert_equal(ipt2.SystemAssign.find(tbl), (1, 5))
61 nt.assert_equal(ipt2.SystemAssign.find(tbl), (1, 5))
51
62
52 tbl = make_tokens_by_line(["a=!ls\n"])
63 tbl = make_tokens_by_line(["a=!ls\n"])
53 nt.assert_equal(ipt2.SystemAssign.find(tbl), (1, 2))
64 nt.assert_equal(ipt2.SystemAssign.find(tbl), (1, 2))
54
65
55 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0]) # Nothing to find
66 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0]) # Nothing to find
56 nt.assert_equal(ipt2.SystemAssign.find(tbl), None)
67 nt.assert_equal(ipt2.SystemAssign.find(tbl), None)
57
68
58 def test_transform_assign_system():
69 def test_transform_assign_system():
59 res = ipt2.SystemAssign.transform(MULTILINE_SYSTEM_ASSIGN[0], (2, 4))
70 res = ipt2.SystemAssign.transform(MULTILINE_SYSTEM_ASSIGN[0], (2, 4))
60 nt.assert_equal(res, MULTILINE_SYSTEM_ASSIGN[1])
71 nt.assert_equal(res, MULTILINE_SYSTEM_ASSIGN[1])
72
73 def test_find_magic_escape():
74 tbl = make_tokens_by_line(MULTILINE_MAGIC[0])
75 nt.assert_equal(ipt2.EscapedCommand.find(tbl), (2, 0))
76
77 tbl = make_tokens_by_line(MULTILINE_MAGIC_ASSIGN[0]) # Shouldn't find a = %foo
78 nt.assert_equal(ipt2.EscapedCommand.find(tbl), None)
79
80 def test_transform_magic_escape():
81 res = ipt2.EscapedCommand.transform(MULTILINE_MAGIC[0], (2, 0))
82 nt.assert_equal(res, MULTILINE_MAGIC[1])
General Comments 0
You need to be logged in to leave comments. Login now