##// END OF EJS Templates
Fix cell magic transformation
Thomas Kluyver -
Show More
@@ -0,0 +1,21 b''
1 """Tests for the line-based transformers in IPython.core.inputtransformer2
2
3 Line-based transformers are the simpler ones; token-based transformers are
4 more complex.
5 """
6 import nose.tools as nt
7
8 from IPython.core import inputtransformer2 as ipt2
9
10 SIMPLE = ("""\
11 %%foo arg
12 body 1
13 body 2
14 """, """\
15 get_ipython().run_cell_magic('foo', 'arg', 'body 1\\nbody 2\\n')
16 """)
17
18 def test_cell_magic():
19 for sample, expected in [SIMPLE]:
20 nt.assert_equal(ipt2.cell_magic(sample.splitlines(keepends=True)),
21 expected.splitlines(keepends=True))
@@ -1,434 +1,434 b''
1 1 import re
2 2 from typing import List, Tuple
3 3 from IPython.utils import tokenize2
4 4 from IPython.utils.tokenutil import generate_tokens
5 5
6 6 def leading_indent(lines):
7 7 """Remove leading indentation.
8 8
9 9 If the first line starts with a spaces or tabs, the same whitespace will be
10 10 removed from each following line.
11 11 """
12 12 m = re.match(r'^[ \t]+', lines[0])
13 13 if not m:
14 14 return lines
15 15 space = m.group(0)
16 16 n = len(space)
17 17 return [l[n:] if l.startswith(space) else l
18 18 for l in lines]
19 19
20 20 class PromptStripper:
21 21 """Remove matching input prompts from a block of input.
22 22
23 23 Parameters
24 24 ----------
25 25 prompt_re : regular expression
26 26 A regular expression matching any input prompt (including continuation)
27 27 initial_re : regular expression, optional
28 28 A regular expression matching only the initial prompt, but not continuation.
29 29 If no initial expression is given, prompt_re will be used everywhere.
30 30 Used mainly for plain Python prompts, where the continuation prompt
31 31 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
32 32
33 33 If initial_re and prompt_re differ,
34 34 only initial_re will be tested against the first line.
35 35 If any prompt is found on the first two lines,
36 36 prompts will be stripped from the rest of the block.
37 37 """
38 38 def __init__(self, prompt_re, initial_re=None):
39 39 self.prompt_re = prompt_re
40 40 self.initial_re = initial_re or prompt_re
41 41
42 42 def _strip(self, lines):
43 43 return [self.prompt_re.sub('', l, count=1) for l in lines]
44 44
45 45 def __call__(self, lines):
46 46 if self.initial_re.match(lines[0]) or \
47 47 (len(lines) > 1 and self.prompt_re.match(lines[1])):
48 48 return self._strip(lines)
49 49 return lines
50 50
51 51 classic_prompt = PromptStripper(
52 52 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
53 53 initial_re=re.compile(r'^>>>( |$)')
54 54 )
55 55
56 56 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
57 57
58 58 def cell_magic(lines):
59 59 if not lines[0].startswith('%%'):
60 60 return lines
61 61 if re.match('%%\w+\?', lines[0]):
62 62 # This case will be handled by help_end
63 63 return lines
64 magic_name, first_line = lines[0][2:].partition(' ')
65 body = '\n'.join(lines[1:])
66 return ['get_ipython().run_cell_magic(%r, %r, %r)' % (magic_name, first_line, body)]
64 magic_name, _, first_line = lines[0][2:-1].partition(' ')
65 body = ''.join(lines[1:])
66 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
67 % (magic_name, first_line, body)]
67 68
68 69 line_transforms = [
69 70 leading_indent,
70 71 classic_prompt,
71 72 ipython_prompt,
72 73 cell_magic,
73 74 ]
74 75
75 76 # -----
76 77
77 78 def _find_assign_op(token_line):
78 79 # Find the first assignment in the line ('=' not inside brackets)
79 80 # We don't try to support multiple special assignment (a = b = %foo)
80 81 paren_level = 0
81 82 for i, ti in enumerate(token_line):
82 83 s = ti.string
83 84 if s == '=' and paren_level == 0:
84 85 return i
85 86 if s in '([{':
86 87 paren_level += 1
87 88 elif s in ')]}':
88 89 paren_level -= 1
89 90
90 91 def find_end_of_continued_line(lines, start_line: int):
91 92 """Find the last line of a line explicitly extended using backslashes.
92 93
93 94 Uses 0-indexed line numbers.
94 95 """
95 96 end_line = start_line
96 97 while lines[end_line].endswith('\\\n'):
97 98 end_line += 1
98 99 if end_line >= len(lines):
99 100 break
100 101 return end_line
101 102
102 103 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
103 104 """Assemble pieces of a continued line into a single line.
104 105
105 106 Uses 0-indexed line numbers. *start* is (lineno, colno).
106 107 """
107 108 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
108 109 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
109 110 + [parts[-1][:-1]]) # Strip newline from last line
110 111
111 112 class TokenTransformBase:
112 113 # Lower numbers -> higher priority (for matches in the same location)
113 114 priority = 10
114 115
115 116 def sortby(self):
116 117 return self.start_line, self.start_col, self.priority
117 118
118 119 def __init__(self, start):
119 120 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
120 121 self.start_col = start[1]
121 122
122 123 def transform(self, lines: List[str]):
123 124 raise NotImplementedError
124 125
125 126 class MagicAssign(TokenTransformBase):
126 127 @classmethod
127 128 def find(cls, tokens_by_line):
128 129 """Find the first magic assignment (a = %foo) in the cell.
129 130
130 131 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
131 132 """
132 133 for line in tokens_by_line:
133 134 assign_ix = _find_assign_op(line)
134 135 if (assign_ix is not None) \
135 136 and (len(line) >= assign_ix + 2) \
136 137 and (line[assign_ix+1].string == '%') \
137 138 and (line[assign_ix+2].type == tokenize2.NAME):
138 139 return cls(line[assign_ix+1].start)
139 140
140 141 def transform(self, lines: List[str]):
141 142 """Transform a magic assignment found by find
142 143 """
143 144 start_line, start_col = self.start_line, self.start_col
144 145 lhs = lines[start_line][:start_col]
145 146 end_line = find_end_of_continued_line(lines, start_line)
146 147 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
147 148 assert rhs.startswith('%'), rhs
148 149 magic_name, _, args = rhs[1:].partition(' ')
149 150
150 151 lines_before = lines[:start_line]
151 152 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
152 153 new_line = lhs + call + '\n'
153 154 lines_after = lines[end_line+1:]
154 155
155 156 return lines_before + [new_line] + lines_after
156 157
157 158
158 159 class SystemAssign(TokenTransformBase):
159 160 @classmethod
160 161 def find(cls, tokens_by_line):
161 162 """Find the first system assignment (a = !foo) in the cell.
162 163
163 164 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
164 165 """
165 166 for line in tokens_by_line:
166 167 assign_ix = _find_assign_op(line)
167 168 if (assign_ix is not None) \
168 169 and (len(line) >= assign_ix + 2) \
169 170 and (line[assign_ix + 1].type == tokenize2.ERRORTOKEN):
170 171 ix = assign_ix + 1
171 172
172 173 while ix < len(line) and line[ix].type == tokenize2.ERRORTOKEN:
173 174 if line[ix].string == '!':
174 175 return cls(line[ix].start)
175 176 elif not line[ix].string.isspace():
176 177 break
177 178 ix += 1
178 179
179 180 def transform(self, lines: List[str]):
180 181 """Transform a system assignment found by find
181 182 """
182 183 start_line, start_col = self.start_line, self.start_col
183 184
184 185 lhs = lines[start_line][:start_col]
185 186 end_line = find_end_of_continued_line(lines, start_line)
186 187 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
187 188 assert rhs.startswith('!'), rhs
188 189 cmd = rhs[1:]
189 190
190 191 lines_before = lines[:start_line]
191 192 call = "get_ipython().getoutput({!r})".format(cmd)
192 193 new_line = lhs + call + '\n'
193 194 lines_after = lines[end_line + 1:]
194 195
195 196 return lines_before + [new_line] + lines_after
196 197
197 198 # The escape sequences that define the syntax transformations IPython will
198 199 # apply to user input. These can NOT be just changed here: many regular
199 200 # expressions and other parts of the code may use their hardcoded values, and
200 201 # for all intents and purposes they constitute the 'IPython syntax', so they
201 202 # should be considered fixed.
202 203
203 204 ESC_SHELL = '!' # Send line to underlying system shell
204 205 ESC_SH_CAP = '!!' # Send line to system shell and capture output
205 206 ESC_HELP = '?' # Find information about object
206 207 ESC_HELP2 = '??' # Find extra-detailed information about object
207 208 ESC_MAGIC = '%' # Call magic function
208 209 ESC_MAGIC2 = '%%' # Call cell-magic function
209 210 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
210 211 ESC_QUOTE2 = ';' # Quote all args as a single string, call
211 212 ESC_PAREN = '/' # Call first argument with rest of line as arguments
212 213
213 214 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
214 215 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
215 216
216 217 def _make_help_call(target, esc, next_input=None):
217 218 """Prepares a pinfo(2)/psearch call from a target name and the escape
218 219 (i.e. ? or ??)"""
219 220 method = 'pinfo2' if esc == '??' \
220 221 else 'psearch' if '*' in target \
221 222 else 'pinfo'
222 223 arg = " ".join([method, target])
223 224 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
224 225 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
225 226 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
226 227 if next_input is None:
227 228 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
228 229 else:
229 230 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
230 231 (next_input, t_magic_name, t_magic_arg_s)
231 232
232 233 def _tr_help(content):
233 234 "Translate lines escaped with: ?"
234 235 # A naked help line should just fire the intro help screen
235 236 if not content:
236 237 return 'get_ipython().show_usage()'
237 238
238 239 return _make_help_call(content, '?')
239 240
240 241 def _tr_help2(content):
241 242 "Translate lines escaped with: ??"
242 243 # A naked help line should just fire the intro help screen
243 244 if not content:
244 245 return 'get_ipython().show_usage()'
245 246
246 247 return _make_help_call(content, '??')
247 248
248 249 def _tr_magic(content):
249 250 "Translate lines escaped with: %"
250 251 name, _, args = content.partition(' ')
251 252 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
252 253
253 254 def _tr_quote(content):
254 255 "Translate lines escaped with: ,"
255 256 name, _, args = content.partition(' ')
256 257 return '%s("%s")' % (name, '", "'.join(args.split()) )
257 258
258 259 def _tr_quote2(content):
259 260 "Translate lines escaped with: ;"
260 261 name, _, args = content.partition(' ')
261 262 return '%s("%s")' % (name, args)
262 263
263 264 def _tr_paren(content):
264 265 "Translate lines escaped with: /"
265 266 name, _, args = content.partition(' ')
266 267 return '%s(%s)' % (name, ", ".join(args.split()))
267 268
268 269 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
269 270 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
270 271 ESC_HELP : _tr_help,
271 272 ESC_HELP2 : _tr_help2,
272 273 ESC_MAGIC : _tr_magic,
273 274 ESC_QUOTE : _tr_quote,
274 275 ESC_QUOTE2 : _tr_quote2,
275 276 ESC_PAREN : _tr_paren }
276 277
277 278 class EscapedCommand(TokenTransformBase):
278 279 @classmethod
279 280 def find(cls, tokens_by_line):
280 281 """Find the first escaped command (%foo, !foo, etc.) in the cell.
281 282
282 283 Returns (line, column) of the escape if found, or None. *line* is 1-indexed.
283 284 """
284 285 for line in tokens_by_line:
285 286 ix = 0
286 287 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
287 288 ix += 1
288 289 if line[ix].string in ESCAPE_SINGLES:
289 290 return cls(line[ix].start)
290 291
291 292 def transform(self, lines):
292 293 start_line, start_col = self.start_line, self.start_col
293 294
294 295 indent = lines[start_line][:start_col]
295 296 end_line = find_end_of_continued_line(lines, start_line)
296 297 line = assemble_continued_line(lines, (start_line, start_col), end_line)
297 298
298 299 if line[:2] in ESCAPE_DOUBLES:
299 300 escape, content = line[:2], line[2:]
300 301 else:
301 302 escape, content = line[:1], line[1:]
302 303 call = tr[escape](content)
303 304
304 305 lines_before = lines[:start_line]
305 306 new_line = indent + call + '\n'
306 307 lines_after = lines[end_line + 1:]
307 308
308 309 return lines_before + [new_line] + lines_after
309 310
310 311 _help_end_re = re.compile(r"""(%{0,2}
311 312 [a-zA-Z_*][\w*]* # Variable name
312 313 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
313 314 )
314 315 (\?\??)$ # ? or ??
315 316 """,
316 317 re.VERBOSE)
317 318
318 319 class HelpEnd(TokenTransformBase):
319 320 # This needs to be higher priority (lower number) than EscapedCommand so
320 321 # that inspecting magics (%foo?) works.
321 322 priority = 5
322 323
323 324 def __init__(self, start, q_locn):
324 325 super().__init__(start)
325 326 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
326 327 self.q_col = q_locn[1]
327 328
328 329 @classmethod
329 330 def find(cls, tokens_by_line):
330 331 for line in tokens_by_line:
331 332 # Last token is NEWLINE; look at last but one
332 333 if len(line) > 2 and line[-2].string == '?':
333 334 # Find the first token that's not INDENT/DEDENT
334 335 ix = 0
335 336 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
336 337 ix += 1
337 338 return cls(line[ix].start, line[-2].start)
338 339
339 340 def transform(self, lines):
340 341 piece = ''.join(lines[self.start_line:self.q_line+1])
341 342 indent, content = piece[:self.start_col], piece[self.start_col:]
342 343 lines_before = lines[:self.start_line]
343 344 lines_after = lines[self.q_line + 1:]
344 345
345 346 m = _help_end_re.search(content)
346 347 assert m is not None, content
347 348 target = m.group(1)
348 349 esc = m.group(3)
349 350
350 351 # If we're mid-command, put it back on the next prompt for the user.
351 352 next_input = None
352 353 if (not lines_before) and (not lines_after) \
353 354 and content.strip() != m.group(0):
354 355 next_input = content.rstrip('?\n')
355 356
356 357 call = _make_help_call(target, esc, next_input=next_input)
357 358 new_line = indent + call + '\n'
358 359
359 360 return lines_before + [new_line] + lines_after
360 361
361 362 def make_tokens_by_line(lines):
362 363 tokens_by_line = [[]]
363 364 for token in generate_tokens(iter(lines).__next__):
364 365 tokens_by_line[-1].append(token)
365 366 if token.type == tokenize2.NEWLINE:
366 367 tokens_by_line.append([])
367 368
368 369 return tokens_by_line
369 370
370 371 def show_linewise_tokens(s: str):
371 372 """For investigation"""
372 373 if not s.endswith('\n'):
373 374 s += '\n'
374 375 lines = s.splitlines(keepends=True)
375 376 for line in make_tokens_by_line(lines):
376 377 print("Line -------")
377 378 for tokinfo in line:
378 379 print(" ", tokinfo)
379 380
380 381 class TokenTransformers:
381 382 def __init__(self):
382 383 self.transformers = [
383 384 MagicAssign,
384 385 SystemAssign,
385 386 EscapedCommand,
386 387 HelpEnd,
387 388 ]
388 389
389 390 def do_one_transform(self, lines):
390 391 """Find and run the transform earliest in the code.
391 392
392 393 Returns (changed, lines).
393 394
394 395 This method is called repeatedly until changed is False, indicating
395 396 that all available transformations are complete.
396 397
397 398 The tokens following IPython special syntax might not be valid, so
398 399 the transformed code is retokenised every time to identify the next
399 400 piece of special syntax. Hopefully long code cells are mostly valid
400 401 Python, not using lots of IPython special syntax, so this shouldn't be
401 402 a performance issue.
402 403 """
403 404 tokens_by_line = make_tokens_by_line(lines)
404 405 candidates = []
405 406 for transformer_cls in self.transformers:
406 407 transformer = transformer_cls.find(tokens_by_line)
407 408 if transformer:
408 409 candidates.append(transformer)
409 410
410 411 if not candidates:
411 412 # Nothing to transform
412 413 return False, lines
413 414
414 415 transformer = min(candidates, key=TokenTransformBase.sortby)
415 416 return True, transformer.transform(lines)
416 417
417 418 def __call__(self, lines):
418 419 while True:
419 420 changed, lines = self.do_one_transform(lines)
420 421 if not changed:
421 422 return lines
422 423
423 424
424 425 def transform_cell(cell):
425 426 if not cell.endswith('\n'):
426 427 cell += '\n' # Ensure every line has a newline
427 428 lines = cell.splitlines(keepends=True)
428 429 for transform in line_transforms:
429 430 #print(transform, lines)
430 431 lines = transform(lines)
431 432
432 433 lines = TokenTransformers()(lines)
433 for line in lines:
434 print('~~', line)
434 return ''.join(lines)
@@ -1,1073 +1,1073 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for various magic functions.
3 3
4 4 Needs to be run by nose (to make ipython session available).
5 5 """
6 6
7 7 import io
8 8 import os
9 9 import re
10 10 import sys
11 11 import warnings
12 12 from unittest import TestCase
13 13 from importlib import invalidate_caches
14 14 from io import StringIO
15 15
16 16 import nose.tools as nt
17 17
18 18 import shlex
19 19
20 20 from IPython import get_ipython
21 21 from IPython.core import magic
22 22 from IPython.core.error import UsageError
23 23 from IPython.core.magic import (Magics, magics_class, line_magic,
24 24 cell_magic,
25 25 register_line_magic, register_cell_magic)
26 26 from IPython.core.magics import execution, script, code, logging
27 27 from IPython.testing import decorators as dec
28 28 from IPython.testing import tools as tt
29 29 from IPython.utils import py3compat
30 30 from IPython.utils.io import capture_output
31 31 from IPython.utils.tempdir import TemporaryDirectory
32 32 from IPython.utils.process import find_cmd
33 33
34 34
35 35
36 36 _ip = get_ipython()
37 37
38 38 @magic.magics_class
39 39 class DummyMagics(magic.Magics): pass
40 40
41 41 def test_extract_code_ranges():
42 42 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
43 43 expected = [(0, 1),
44 44 (2, 3),
45 45 (4, 6),
46 46 (6, 9),
47 47 (9, 14),
48 48 (16, None),
49 49 (None, 9),
50 50 (9, None),
51 51 (None, 13),
52 52 (None, None)]
53 53 actual = list(code.extract_code_ranges(instr))
54 54 nt.assert_equal(actual, expected)
55 55
56 56 def test_extract_symbols():
57 57 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
58 58 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
59 59 expected = [([], ['a']),
60 60 (["def b():\n return 42\n"], []),
61 61 (["class A: pass\n"], []),
62 62 (["class A: pass\n", "def b():\n return 42\n"], []),
63 63 (["class A: pass\n"], ['a']),
64 64 ([], ['z'])]
65 65 for symbols, exp in zip(symbols_args, expected):
66 66 nt.assert_equal(code.extract_symbols(source, symbols), exp)
67 67
68 68
69 69 def test_extract_symbols_raises_exception_with_non_python_code():
70 70 source = ("=begin A Ruby program :)=end\n"
71 71 "def hello\n"
72 72 "puts 'Hello world'\n"
73 73 "end")
74 74 with nt.assert_raises(SyntaxError):
75 75 code.extract_symbols(source, "hello")
76 76
77 77
78 78 def test_magic_not_found():
79 79 # magic not found raises UsageError
80 80 with nt.assert_raises(UsageError):
81 81 _ip.magic('doesntexist')
82 82
83 83 # ensure result isn't success when a magic isn't found
84 84 result = _ip.run_cell('%doesntexist')
85 85 assert isinstance(result.error_in_exec, UsageError)
86 86
87 87
88 88 def test_cell_magic_not_found():
89 89 # magic not found raises UsageError
90 90 with nt.assert_raises(UsageError):
91 91 _ip.run_cell_magic('doesntexist', 'line', 'cell')
92 92
93 93 # ensure result isn't success when a magic isn't found
94 94 result = _ip.run_cell('%%doesntexist')
95 95 assert isinstance(result.error_in_exec, UsageError)
96 96
97 97
98 98 def test_magic_error_status():
99 99 def fail(shell):
100 100 1/0
101 101 _ip.register_magic_function(fail)
102 102 result = _ip.run_cell('%fail')
103 103 assert isinstance(result.error_in_exec, ZeroDivisionError)
104 104
105 105
106 106 def test_config():
107 107 """ test that config magic does not raise
108 108 can happen if Configurable init is moved too early into
109 109 Magics.__init__ as then a Config object will be registered as a
110 110 magic.
111 111 """
112 112 ## should not raise.
113 113 _ip.magic('config')
114 114
115 115 def test_config_available_configs():
116 116 """ test that config magic prints available configs in unique and
117 117 sorted order. """
118 118 with capture_output() as captured:
119 119 _ip.magic('config')
120 120
121 121 stdout = captured.stdout
122 122 config_classes = stdout.strip().split('\n')[1:]
123 123 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
124 124
125 125 def test_config_print_class():
126 126 """ test that config with a classname prints the class's options. """
127 127 with capture_output() as captured:
128 128 _ip.magic('config TerminalInteractiveShell')
129 129
130 130 stdout = captured.stdout
131 131 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
132 132 print(stdout)
133 133 raise AssertionError("1st line of stdout not like "
134 134 "'TerminalInteractiveShell.* options'")
135 135
136 136 def test_rehashx():
137 137 # clear up everything
138 138 _ip.alias_manager.clear_aliases()
139 139 del _ip.db['syscmdlist']
140 140
141 141 _ip.magic('rehashx')
142 142 # Practically ALL ipython development systems will have more than 10 aliases
143 143
144 144 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
145 145 for name, cmd in _ip.alias_manager.aliases:
146 146 # we must strip dots from alias names
147 147 nt.assert_not_in('.', name)
148 148
149 149 # rehashx must fill up syscmdlist
150 150 scoms = _ip.db['syscmdlist']
151 151 nt.assert_true(len(scoms) > 10)
152 152
153 153
154 154 def test_magic_parse_options():
155 155 """Test that we don't mangle paths when parsing magic options."""
156 156 ip = get_ipython()
157 157 path = 'c:\\x'
158 158 m = DummyMagics(ip)
159 159 opts = m.parse_options('-f %s' % path,'f:')[0]
160 160 # argv splitting is os-dependent
161 161 if os.name == 'posix':
162 162 expected = 'c:x'
163 163 else:
164 164 expected = path
165 165 nt.assert_equal(opts['f'], expected)
166 166
167 167 def test_magic_parse_long_options():
168 168 """Magic.parse_options can handle --foo=bar long options"""
169 169 ip = get_ipython()
170 170 m = DummyMagics(ip)
171 171 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
172 172 nt.assert_in('foo', opts)
173 173 nt.assert_in('bar', opts)
174 174 nt.assert_equal(opts['bar'], "bubble")
175 175
176 176
177 177 @dec.skip_without('sqlite3')
178 178 def doctest_hist_f():
179 179 """Test %hist -f with temporary filename.
180 180
181 181 In [9]: import tempfile
182 182
183 183 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
184 184
185 185 In [11]: %hist -nl -f $tfile 3
186 186
187 187 In [13]: import os; os.unlink(tfile)
188 188 """
189 189
190 190
191 191 @dec.skip_without('sqlite3')
192 192 def doctest_hist_r():
193 193 """Test %hist -r
194 194
195 195 XXX - This test is not recording the output correctly. For some reason, in
196 196 testing mode the raw history isn't getting populated. No idea why.
197 197 Disabling the output checking for now, though at least we do run it.
198 198
199 199 In [1]: 'hist' in _ip.lsmagic()
200 200 Out[1]: True
201 201
202 202 In [2]: x=1
203 203
204 204 In [3]: %hist -rl 2
205 205 x=1 # random
206 206 %hist -r 2
207 207 """
208 208
209 209
210 210 @dec.skip_without('sqlite3')
211 211 def doctest_hist_op():
212 212 """Test %hist -op
213 213
214 214 In [1]: class b(float):
215 215 ...: pass
216 216 ...:
217 217
218 218 In [2]: class s(object):
219 219 ...: def __str__(self):
220 220 ...: return 's'
221 221 ...:
222 222
223 223 In [3]:
224 224
225 225 In [4]: class r(b):
226 226 ...: def __repr__(self):
227 227 ...: return 'r'
228 228 ...:
229 229
230 230 In [5]: class sr(s,r): pass
231 231 ...:
232 232
233 233 In [6]:
234 234
235 235 In [7]: bb=b()
236 236
237 237 In [8]: ss=s()
238 238
239 239 In [9]: rr=r()
240 240
241 241 In [10]: ssrr=sr()
242 242
243 243 In [11]: 4.5
244 244 Out[11]: 4.5
245 245
246 246 In [12]: str(ss)
247 247 Out[12]: 's'
248 248
249 249 In [13]:
250 250
251 251 In [14]: %hist -op
252 252 >>> class b:
253 253 ... pass
254 254 ...
255 255 >>> class s(b):
256 256 ... def __str__(self):
257 257 ... return 's'
258 258 ...
259 259 >>>
260 260 >>> class r(b):
261 261 ... def __repr__(self):
262 262 ... return 'r'
263 263 ...
264 264 >>> class sr(s,r): pass
265 265 >>>
266 266 >>> bb=b()
267 267 >>> ss=s()
268 268 >>> rr=r()
269 269 >>> ssrr=sr()
270 270 >>> 4.5
271 271 4.5
272 272 >>> str(ss)
273 273 's'
274 274 >>>
275 275 """
276 276
277 277 def test_hist_pof():
278 278 ip = get_ipython()
279 279 ip.run_cell(u"1+2", store_history=True)
280 280 #raise Exception(ip.history_manager.session_number)
281 281 #raise Exception(list(ip.history_manager._get_range_session()))
282 282 with TemporaryDirectory() as td:
283 283 tf = os.path.join(td, 'hist.py')
284 284 ip.run_line_magic('history', '-pof %s' % tf)
285 285 assert os.path.isfile(tf)
286 286
287 287
288 288 @dec.skip_without('sqlite3')
289 289 def test_macro():
290 290 ip = get_ipython()
291 291 ip.history_manager.reset() # Clear any existing history.
292 292 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
293 293 for i, cmd in enumerate(cmds, start=1):
294 294 ip.history_manager.store_inputs(i, cmd)
295 295 ip.magic("macro test 1-3")
296 296 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
297 297
298 298 # List macros
299 299 nt.assert_in("test", ip.magic("macro"))
300 300
301 301
302 302 @dec.skip_without('sqlite3')
303 303 def test_macro_run():
304 304 """Test that we can run a multi-line macro successfully."""
305 305 ip = get_ipython()
306 306 ip.history_manager.reset()
307 307 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
308 308 "%macro test 2-3"]
309 309 for cmd in cmds:
310 310 ip.run_cell(cmd, store_history=True)
311 311 nt.assert_equal(ip.user_ns["test"].value,
312 312 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
313 313 with tt.AssertPrints("12"):
314 314 ip.run_cell("test")
315 315 with tt.AssertPrints("13"):
316 316 ip.run_cell("test")
317 317
318 318
319 319 def test_magic_magic():
320 320 """Test %magic"""
321 321 ip = get_ipython()
322 322 with capture_output() as captured:
323 323 ip.magic("magic")
324 324
325 325 stdout = captured.stdout
326 326 nt.assert_in('%magic', stdout)
327 327 nt.assert_in('IPython', stdout)
328 328 nt.assert_in('Available', stdout)
329 329
330 330
331 331 @dec.skipif_not_numpy
332 332 def test_numpy_reset_array_undec():
333 333 "Test '%reset array' functionality"
334 334 _ip.ex('import numpy as np')
335 335 _ip.ex('a = np.empty(2)')
336 336 nt.assert_in('a', _ip.user_ns)
337 337 _ip.magic('reset -f array')
338 338 nt.assert_not_in('a', _ip.user_ns)
339 339
340 340 def test_reset_out():
341 341 "Test '%reset out' magic"
342 342 _ip.run_cell("parrot = 'dead'", store_history=True)
343 343 # test '%reset -f out', make an Out prompt
344 344 _ip.run_cell("parrot", store_history=True)
345 345 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
346 346 _ip.magic('reset -f out')
347 347 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
348 348 nt.assert_equal(len(_ip.user_ns['Out']), 0)
349 349
350 350 def test_reset_in():
351 351 "Test '%reset in' magic"
352 352 # test '%reset -f in'
353 353 _ip.run_cell("parrot", store_history=True)
354 354 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
355 355 _ip.magic('%reset -f in')
356 356 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
357 357 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
358 358
359 359 def test_reset_dhist():
360 360 "Test '%reset dhist' magic"
361 361 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
362 362 _ip.magic('cd ' + os.path.dirname(nt.__file__))
363 363 _ip.magic('cd -')
364 364 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
365 365 _ip.magic('reset -f dhist')
366 366 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
367 367 _ip.run_cell("_dh = [d for d in tmp]") #restore
368 368
369 369 def test_reset_in_length():
370 370 "Test that '%reset in' preserves In[] length"
371 371 _ip.run_cell("print 'foo'")
372 372 _ip.run_cell("reset -f in")
373 373 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
374 374
375 375 def test_tb_syntaxerror():
376 376 """test %tb after a SyntaxError"""
377 377 ip = get_ipython()
378 378 ip.run_cell("for")
379 379
380 380 # trap and validate stdout
381 381 save_stdout = sys.stdout
382 382 try:
383 383 sys.stdout = StringIO()
384 384 ip.run_cell("%tb")
385 385 out = sys.stdout.getvalue()
386 386 finally:
387 387 sys.stdout = save_stdout
388 388 # trim output, and only check the last line
389 389 last_line = out.rstrip().splitlines()[-1].strip()
390 390 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
391 391
392 392
393 393 def test_time():
394 394 ip = get_ipython()
395 395
396 396 with tt.AssertPrints("Wall time: "):
397 397 ip.run_cell("%time None")
398 398
399 399 ip.run_cell("def f(kmjy):\n"
400 400 " %time print (2*kmjy)")
401 401
402 402 with tt.AssertPrints("Wall time: "):
403 403 with tt.AssertPrints("hihi", suppress=False):
404 404 ip.run_cell("f('hi')")
405 405
406 406
407 407 @dec.skip_win32
408 408 def test_time2():
409 409 ip = get_ipython()
410 410
411 411 with tt.AssertPrints("CPU times: user "):
412 412 ip.run_cell("%time None")
413 413
414 414 def test_time3():
415 415 """Erroneous magic function calls, issue gh-3334"""
416 416 ip = get_ipython()
417 417 ip.user_ns.pop('run', None)
418 418
419 419 with tt.AssertNotPrints("not found", channel='stderr'):
420 420 ip.run_cell("%%time\n"
421 421 "run = 0\n"
422 422 "run += 1")
423 423
424 424 def test_doctest_mode():
425 425 "Toggle doctest_mode twice, it should be a no-op and run without error"
426 426 _ip.magic('doctest_mode')
427 427 _ip.magic('doctest_mode')
428 428
429 429
430 430 def test_parse_options():
431 431 """Tests for basic options parsing in magics."""
432 432 # These are only the most minimal of tests, more should be added later. At
433 433 # the very least we check that basic text/unicode calls work OK.
434 434 m = DummyMagics(_ip)
435 435 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
436 436 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
437 437
438 438
439 439 def test_dirops():
440 440 """Test various directory handling operations."""
441 441 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
442 442 curpath = os.getcwd
443 443 startdir = os.getcwd()
444 444 ipdir = os.path.realpath(_ip.ipython_dir)
445 445 try:
446 446 _ip.magic('cd "%s"' % ipdir)
447 447 nt.assert_equal(curpath(), ipdir)
448 448 _ip.magic('cd -')
449 449 nt.assert_equal(curpath(), startdir)
450 450 _ip.magic('pushd "%s"' % ipdir)
451 451 nt.assert_equal(curpath(), ipdir)
452 452 _ip.magic('popd')
453 453 nt.assert_equal(curpath(), startdir)
454 454 finally:
455 455 os.chdir(startdir)
456 456
457 457
458 458 def test_xmode():
459 459 # Calling xmode three times should be a no-op
460 460 xmode = _ip.InteractiveTB.mode
461 461 for i in range(3):
462 462 _ip.magic("xmode")
463 463 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
464 464
465 465 def test_reset_hard():
466 466 monitor = []
467 467 class A(object):
468 468 def __del__(self):
469 469 monitor.append(1)
470 470 def __repr__(self):
471 471 return "<A instance>"
472 472
473 473 _ip.user_ns["a"] = A()
474 474 _ip.run_cell("a")
475 475
476 476 nt.assert_equal(monitor, [])
477 477 _ip.magic("reset -f")
478 478 nt.assert_equal(monitor, [1])
479 479
480 480 class TestXdel(tt.TempFileMixin):
481 481 def test_xdel(self):
482 482 """Test that references from %run are cleared by xdel."""
483 483 src = ("class A(object):\n"
484 484 " monitor = []\n"
485 485 " def __del__(self):\n"
486 486 " self.monitor.append(1)\n"
487 487 "a = A()\n")
488 488 self.mktmp(src)
489 489 # %run creates some hidden references...
490 490 _ip.magic("run %s" % self.fname)
491 491 # ... as does the displayhook.
492 492 _ip.run_cell("a")
493 493
494 494 monitor = _ip.user_ns["A"].monitor
495 495 nt.assert_equal(monitor, [])
496 496
497 497 _ip.magic("xdel a")
498 498
499 499 # Check that a's __del__ method has been called.
500 500 nt.assert_equal(monitor, [1])
501 501
502 502 def doctest_who():
503 503 """doctest for %who
504 504
505 505 In [1]: %reset -f
506 506
507 507 In [2]: alpha = 123
508 508
509 509 In [3]: beta = 'beta'
510 510
511 511 In [4]: %who int
512 512 alpha
513 513
514 514 In [5]: %who str
515 515 beta
516 516
517 517 In [6]: %whos
518 518 Variable Type Data/Info
519 519 ----------------------------
520 520 alpha int 123
521 521 beta str beta
522 522
523 523 In [7]: %who_ls
524 524 Out[7]: ['alpha', 'beta']
525 525 """
526 526
527 527 def test_whos():
528 528 """Check that whos is protected against objects where repr() fails."""
529 529 class A(object):
530 530 def __repr__(self):
531 531 raise Exception()
532 532 _ip.user_ns['a'] = A()
533 533 _ip.magic("whos")
534 534
535 535 @py3compat.u_format
536 536 def doctest_precision():
537 537 """doctest for %precision
538 538
539 539 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
540 540
541 541 In [2]: %precision 5
542 542 Out[2]: '%.5f'
543 543
544 544 In [3]: f.float_format
545 545 Out[3]: '%.5f'
546 546
547 547 In [4]: %precision %e
548 548 Out[4]: '%e'
549 549
550 550 In [5]: f(3.1415927)
551 551 Out[5]: '3.141593e+00'
552 552 """
553 553
554 554 def test_psearch():
555 555 with tt.AssertPrints("dict.fromkeys"):
556 556 _ip.run_cell("dict.fr*?")
557 557
558 558 def test_timeit_shlex():
559 559 """test shlex issues with timeit (#1109)"""
560 560 _ip.ex("def f(*a,**kw): pass")
561 561 _ip.magic('timeit -n1 "this is a bug".count(" ")')
562 562 _ip.magic('timeit -r1 -n1 f(" ", 1)')
563 563 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
564 564 _ip.magic('timeit -r1 -n1 ("a " + "b")')
565 565 _ip.magic('timeit -r1 -n1 f("a " + "b")')
566 566 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
567 567
568 568
569 569 def test_timeit_arguments():
570 570 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
571 571 _ip.magic("timeit ('#')")
572 572
573 573
574 574 def test_timeit_special_syntax():
575 575 "Test %%timeit with IPython special syntax"
576 576 @register_line_magic
577 577 def lmagic(line):
578 578 ip = get_ipython()
579 579 ip.user_ns['lmagic_out'] = line
580 580
581 581 # line mode test
582 582 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
583 583 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
584 584 # cell mode test
585 585 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
586 586 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
587 587
588 588 def test_timeit_return():
589 589 """
590 590 test whether timeit -o return object
591 591 """
592 592
593 593 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
594 594 assert(res is not None)
595 595
596 596 def test_timeit_quiet():
597 597 """
598 598 test quiet option of timeit magic
599 599 """
600 600 with tt.AssertNotPrints("loops"):
601 601 _ip.run_cell("%timeit -n1 -r1 -q 1")
602 602
603 603 def test_timeit_return_quiet():
604 604 with tt.AssertNotPrints("loops"):
605 605 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
606 606 assert (res is not None)
607 607
608 608 def test_timeit_invalid_return():
609 609 with nt.assert_raises_regex(SyntaxError, "outside function"):
610 610 _ip.run_line_magic('timeit', 'return')
611 611
612 612 @dec.skipif(execution.profile is None)
613 613 def test_prun_special_syntax():
614 614 "Test %%prun with IPython special syntax"
615 615 @register_line_magic
616 616 def lmagic(line):
617 617 ip = get_ipython()
618 618 ip.user_ns['lmagic_out'] = line
619 619
620 620 # line mode test
621 621 _ip.run_line_magic('prun', '-q %lmagic my line')
622 622 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
623 623 # cell mode test
624 624 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
625 625 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
626 626
627 627 @dec.skipif(execution.profile is None)
628 628 def test_prun_quotes():
629 629 "Test that prun does not clobber string escapes (GH #1302)"
630 630 _ip.magic(r"prun -q x = '\t'")
631 631 nt.assert_equal(_ip.user_ns['x'], '\t')
632 632
633 633 def test_extension():
634 634 # Debugging information for failures of this test
635 635 print('sys.path:')
636 636 for p in sys.path:
637 637 print(' ', p)
638 638 print('CWD', os.getcwd())
639 639
640 640 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
641 641 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
642 642 sys.path.insert(0, daft_path)
643 643 try:
644 644 _ip.user_ns.pop('arq', None)
645 645 invalidate_caches() # Clear import caches
646 646 _ip.magic("load_ext daft_extension")
647 647 nt.assert_equal(_ip.user_ns['arq'], 185)
648 648 _ip.magic("unload_ext daft_extension")
649 649 assert 'arq' not in _ip.user_ns
650 650 finally:
651 651 sys.path.remove(daft_path)
652 652
653 653
654 654 def test_notebook_export_json():
655 655 _ip = get_ipython()
656 656 _ip.history_manager.reset() # Clear any existing history.
657 657 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
658 658 for i, cmd in enumerate(cmds, start=1):
659 659 _ip.history_manager.store_inputs(i, cmd)
660 660 with TemporaryDirectory() as td:
661 661 outfile = os.path.join(td, "nb.ipynb")
662 662 _ip.magic("notebook -e %s" % outfile)
663 663
664 664
665 665 class TestEnv(TestCase):
666 666
667 667 def test_env(self):
668 668 env = _ip.magic("env")
669 669 self.assertTrue(isinstance(env, dict))
670 670
671 671 def test_env_get_set_simple(self):
672 672 env = _ip.magic("env var val1")
673 673 self.assertEqual(env, None)
674 674 self.assertEqual(os.environ['var'], 'val1')
675 675 self.assertEqual(_ip.magic("env var"), 'val1')
676 676 env = _ip.magic("env var=val2")
677 677 self.assertEqual(env, None)
678 678 self.assertEqual(os.environ['var'], 'val2')
679 679
680 680 def test_env_get_set_complex(self):
681 681 env = _ip.magic("env var 'val1 '' 'val2")
682 682 self.assertEqual(env, None)
683 683 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
684 684 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
685 685 env = _ip.magic('env var=val2 val3="val4')
686 686 self.assertEqual(env, None)
687 687 self.assertEqual(os.environ['var'], 'val2 val3="val4')
688 688
689 689 def test_env_set_bad_input(self):
690 690 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
691 691
692 692 def test_env_set_whitespace(self):
693 693 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
694 694
695 695
696 696 class CellMagicTestCase(TestCase):
697 697
698 698 def check_ident(self, magic):
699 699 # Manually called, we get the result
700 700 out = _ip.run_cell_magic(magic, 'a', 'b')
701 701 nt.assert_equal(out, ('a','b'))
702 702 # Via run_cell, it goes into the user's namespace via displayhook
703 _ip.run_cell('%%' + magic +' c\nd')
704 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
703 _ip.run_cell('%%' + magic +' c\nd\n')
704 nt.assert_equal(_ip.user_ns['_'], ('c','d\n'))
705 705
706 706 def test_cell_magic_func_deco(self):
707 707 "Cell magic using simple decorator"
708 708 @register_cell_magic
709 709 def cellm(line, cell):
710 710 return line, cell
711 711
712 712 self.check_ident('cellm')
713 713
714 714 def test_cell_magic_reg(self):
715 715 "Cell magic manually registered"
716 716 def cellm(line, cell):
717 717 return line, cell
718 718
719 719 _ip.register_magic_function(cellm, 'cell', 'cellm2')
720 720 self.check_ident('cellm2')
721 721
722 722 def test_cell_magic_class(self):
723 723 "Cell magics declared via a class"
724 724 @magics_class
725 725 class MyMagics(Magics):
726 726
727 727 @cell_magic
728 728 def cellm3(self, line, cell):
729 729 return line, cell
730 730
731 731 _ip.register_magics(MyMagics)
732 732 self.check_ident('cellm3')
733 733
734 734 def test_cell_magic_class2(self):
735 735 "Cell magics declared via a class, #2"
736 736 @magics_class
737 737 class MyMagics2(Magics):
738 738
739 739 @cell_magic('cellm4')
740 740 def cellm33(self, line, cell):
741 741 return line, cell
742 742
743 743 _ip.register_magics(MyMagics2)
744 744 self.check_ident('cellm4')
745 745 # Check that nothing is registered as 'cellm33'
746 746 c33 = _ip.find_cell_magic('cellm33')
747 747 nt.assert_equal(c33, None)
748 748
749 749 def test_file():
750 750 """Basic %%file"""
751 751 ip = get_ipython()
752 752 with TemporaryDirectory() as td:
753 753 fname = os.path.join(td, 'file1')
754 754 ip.run_cell_magic("file", fname, u'\n'.join([
755 755 'line1',
756 756 'line2',
757 757 ]))
758 758 with open(fname) as f:
759 759 s = f.read()
760 760 nt.assert_in('line1\n', s)
761 761 nt.assert_in('line2', s)
762 762
763 763 def test_file_var_expand():
764 764 """%%file $filename"""
765 765 ip = get_ipython()
766 766 with TemporaryDirectory() as td:
767 767 fname = os.path.join(td, 'file1')
768 768 ip.user_ns['filename'] = fname
769 769 ip.run_cell_magic("file", '$filename', u'\n'.join([
770 770 'line1',
771 771 'line2',
772 772 ]))
773 773 with open(fname) as f:
774 774 s = f.read()
775 775 nt.assert_in('line1\n', s)
776 776 nt.assert_in('line2', s)
777 777
778 778 def test_file_unicode():
779 779 """%%file with unicode cell"""
780 780 ip = get_ipython()
781 781 with TemporaryDirectory() as td:
782 782 fname = os.path.join(td, 'file1')
783 783 ip.run_cell_magic("file", fname, u'\n'.join([
784 784 u'linΓ©1',
785 785 u'linΓ©2',
786 786 ]))
787 787 with io.open(fname, encoding='utf-8') as f:
788 788 s = f.read()
789 789 nt.assert_in(u'linΓ©1\n', s)
790 790 nt.assert_in(u'linΓ©2', s)
791 791
792 792 def test_file_amend():
793 793 """%%file -a amends files"""
794 794 ip = get_ipython()
795 795 with TemporaryDirectory() as td:
796 796 fname = os.path.join(td, 'file2')
797 797 ip.run_cell_magic("file", fname, u'\n'.join([
798 798 'line1',
799 799 'line2',
800 800 ]))
801 801 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
802 802 'line3',
803 803 'line4',
804 804 ]))
805 805 with open(fname) as f:
806 806 s = f.read()
807 807 nt.assert_in('line1\n', s)
808 808 nt.assert_in('line3\n', s)
809 809
810 810
811 811 def test_script_config():
812 812 ip = get_ipython()
813 813 ip.config.ScriptMagics.script_magics = ['whoda']
814 814 sm = script.ScriptMagics(shell=ip)
815 815 nt.assert_in('whoda', sm.magics['cell'])
816 816
817 817 @dec.skip_win32
818 818 def test_script_out():
819 819 ip = get_ipython()
820 820 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
821 821 nt.assert_equal(ip.user_ns['output'], 'hi\n')
822 822
823 823 @dec.skip_win32
824 824 def test_script_err():
825 825 ip = get_ipython()
826 826 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
827 827 nt.assert_equal(ip.user_ns['error'], 'hello\n')
828 828
829 829 @dec.skip_win32
830 830 def test_script_out_err():
831 831 ip = get_ipython()
832 832 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
833 833 nt.assert_equal(ip.user_ns['output'], 'hi\n')
834 834 nt.assert_equal(ip.user_ns['error'], 'hello\n')
835 835
836 836 @dec.skip_win32
837 837 def test_script_bg_out():
838 838 ip = get_ipython()
839 839 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
840 840 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
841 841
842 842 @dec.skip_win32
843 843 def test_script_bg_err():
844 844 ip = get_ipython()
845 845 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
846 846 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
847 847
848 848 @dec.skip_win32
849 849 def test_script_bg_out_err():
850 850 ip = get_ipython()
851 851 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
852 852 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
853 853 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
854 854
855 855 def test_script_defaults():
856 856 ip = get_ipython()
857 857 for cmd in ['sh', 'bash', 'perl', 'ruby']:
858 858 try:
859 859 find_cmd(cmd)
860 860 except Exception:
861 861 pass
862 862 else:
863 863 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
864 864
865 865
866 866 @magics_class
867 867 class FooFoo(Magics):
868 868 """class with both %foo and %%foo magics"""
869 869 @line_magic('foo')
870 870 def line_foo(self, line):
871 871 "I am line foo"
872 872 pass
873 873
874 874 @cell_magic("foo")
875 875 def cell_foo(self, line, cell):
876 876 "I am cell foo, not line foo"
877 877 pass
878 878
879 879 def test_line_cell_info():
880 880 """%%foo and %foo magics are distinguishable to inspect"""
881 881 ip = get_ipython()
882 882 ip.magics_manager.register(FooFoo)
883 883 oinfo = ip.object_inspect('foo')
884 884 nt.assert_true(oinfo['found'])
885 885 nt.assert_true(oinfo['ismagic'])
886 886
887 887 oinfo = ip.object_inspect('%%foo')
888 888 nt.assert_true(oinfo['found'])
889 889 nt.assert_true(oinfo['ismagic'])
890 890 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
891 891
892 892 oinfo = ip.object_inspect('%foo')
893 893 nt.assert_true(oinfo['found'])
894 894 nt.assert_true(oinfo['ismagic'])
895 895 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
896 896
897 897 def test_multiple_magics():
898 898 ip = get_ipython()
899 899 foo1 = FooFoo(ip)
900 900 foo2 = FooFoo(ip)
901 901 mm = ip.magics_manager
902 902 mm.register(foo1)
903 903 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
904 904 mm.register(foo2)
905 905 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
906 906
907 907 def test_alias_magic():
908 908 """Test %alias_magic."""
909 909 ip = get_ipython()
910 910 mm = ip.magics_manager
911 911
912 912 # Basic operation: both cell and line magics are created, if possible.
913 913 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
914 914 nt.assert_in('timeit_alias', mm.magics['line'])
915 915 nt.assert_in('timeit_alias', mm.magics['cell'])
916 916
917 917 # --cell is specified, line magic not created.
918 918 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
919 919 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
920 920 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
921 921
922 922 # Test that line alias is created successfully.
923 923 ip.run_line_magic('alias_magic', '--line env_alias env')
924 924 nt.assert_equal(ip.run_line_magic('env', ''),
925 925 ip.run_line_magic('env_alias', ''))
926 926
927 927 # Test that line alias with parameters passed in is created successfully.
928 928 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
929 929 nt.assert_in('history_alias', mm.magics['line'])
930 930
931 931
932 932 def test_save():
933 933 """Test %save."""
934 934 ip = get_ipython()
935 935 ip.history_manager.reset() # Clear any existing history.
936 936 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
937 937 for i, cmd in enumerate(cmds, start=1):
938 938 ip.history_manager.store_inputs(i, cmd)
939 939 with TemporaryDirectory() as tmpdir:
940 940 file = os.path.join(tmpdir, "testsave.py")
941 941 ip.run_line_magic("save", "%s 1-10" % file)
942 942 with open(file) as f:
943 943 content = f.read()
944 944 nt.assert_equal(content.count(cmds[0]), 1)
945 945 nt.assert_in('coding: utf-8', content)
946 946 ip.run_line_magic("save", "-a %s 1-10" % file)
947 947 with open(file) as f:
948 948 content = f.read()
949 949 nt.assert_equal(content.count(cmds[0]), 2)
950 950 nt.assert_in('coding: utf-8', content)
951 951
952 952
953 953 def test_store():
954 954 """Test %store."""
955 955 ip = get_ipython()
956 956 ip.run_line_magic('load_ext', 'storemagic')
957 957
958 958 # make sure the storage is empty
959 959 ip.run_line_magic('store', '-z')
960 960 ip.user_ns['var'] = 42
961 961 ip.run_line_magic('store', 'var')
962 962 ip.user_ns['var'] = 39
963 963 ip.run_line_magic('store', '-r')
964 964 nt.assert_equal(ip.user_ns['var'], 42)
965 965
966 966 ip.run_line_magic('store', '-d var')
967 967 ip.user_ns['var'] = 39
968 968 ip.run_line_magic('store' , '-r')
969 969 nt.assert_equal(ip.user_ns['var'], 39)
970 970
971 971
972 972 def _run_edit_test(arg_s, exp_filename=None,
973 973 exp_lineno=-1,
974 974 exp_contents=None,
975 975 exp_is_temp=None):
976 976 ip = get_ipython()
977 977 M = code.CodeMagics(ip)
978 978 last_call = ['','']
979 979 opts,args = M.parse_options(arg_s,'prxn:')
980 980 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
981 981
982 982 if exp_filename is not None:
983 983 nt.assert_equal(exp_filename, filename)
984 984 if exp_contents is not None:
985 985 with io.open(filename, 'r', encoding='utf-8') as f:
986 986 contents = f.read()
987 987 nt.assert_equal(exp_contents, contents)
988 988 if exp_lineno != -1:
989 989 nt.assert_equal(exp_lineno, lineno)
990 990 if exp_is_temp is not None:
991 991 nt.assert_equal(exp_is_temp, is_temp)
992 992
993 993
994 994 def test_edit_interactive():
995 995 """%edit on interactively defined objects"""
996 996 ip = get_ipython()
997 997 n = ip.execution_count
998 998 ip.run_cell(u"def foo(): return 1", store_history=True)
999 999
1000 1000 try:
1001 1001 _run_edit_test("foo")
1002 1002 except code.InteractivelyDefined as e:
1003 1003 nt.assert_equal(e.index, n)
1004 1004 else:
1005 1005 raise AssertionError("Should have raised InteractivelyDefined")
1006 1006
1007 1007
1008 1008 def test_edit_cell():
1009 1009 """%edit [cell id]"""
1010 1010 ip = get_ipython()
1011 1011
1012 1012 ip.run_cell(u"def foo(): return 1", store_history=True)
1013 1013
1014 1014 # test
1015 1015 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1016 1016
1017 1017 def test_bookmark():
1018 1018 ip = get_ipython()
1019 1019 ip.run_line_magic('bookmark', 'bmname')
1020 1020 with tt.AssertPrints('bmname'):
1021 1021 ip.run_line_magic('bookmark', '-l')
1022 1022 ip.run_line_magic('bookmark', '-d bmname')
1023 1023
1024 1024 def test_ls_magic():
1025 1025 ip = get_ipython()
1026 1026 json_formatter = ip.display_formatter.formatters['application/json']
1027 1027 json_formatter.enabled = True
1028 1028 lsmagic = ip.magic('lsmagic')
1029 1029 with warnings.catch_warnings(record=True) as w:
1030 1030 j = json_formatter(lsmagic)
1031 1031 nt.assert_equal(sorted(j), ['cell', 'line'])
1032 1032 nt.assert_equal(w, []) # no warnings
1033 1033
1034 1034 def test_strip_initial_indent():
1035 1035 def sii(s):
1036 1036 lines = s.splitlines()
1037 1037 return '\n'.join(code.strip_initial_indent(lines))
1038 1038
1039 1039 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1040 1040 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1041 1041 nt.assert_equal(sii("a\n b"), "a\n b")
1042 1042
1043 1043 def test_logging_magic_quiet_from_arg():
1044 1044 _ip.config.LoggingMagics.quiet = False
1045 1045 lm = logging.LoggingMagics(shell=_ip)
1046 1046 with TemporaryDirectory() as td:
1047 1047 try:
1048 1048 with tt.AssertNotPrints(re.compile("Activating.*")):
1049 1049 lm.logstart('-q {}'.format(
1050 1050 os.path.join(td, "quiet_from_arg.log")))
1051 1051 finally:
1052 1052 _ip.logger.logstop()
1053 1053
1054 1054 def test_logging_magic_quiet_from_config():
1055 1055 _ip.config.LoggingMagics.quiet = True
1056 1056 lm = logging.LoggingMagics(shell=_ip)
1057 1057 with TemporaryDirectory() as td:
1058 1058 try:
1059 1059 with tt.AssertNotPrints(re.compile("Activating.*")):
1060 1060 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1061 1061 finally:
1062 1062 _ip.logger.logstop()
1063 1063
1064 1064 def test_logging_magic_not_quiet():
1065 1065 _ip.config.LoggingMagics.quiet = False
1066 1066 lm = logging.LoggingMagics(shell=_ip)
1067 1067 with TemporaryDirectory() as td:
1068 1068 try:
1069 1069 with tt.AssertPrints(re.compile("Activating.*")):
1070 1070 lm.logstart(os.path.join(td, "not_quiet.log"))
1071 1071 finally:
1072 1072 _ip.logger.logstop()
1073 1073
General Comments 0
You need to be logged in to leave comments. Login now