##// END OF EJS Templates
bugfix. parse of non python code returns a tuple as expected
Martín Gaitán -
Show More
@@ -1,686 +1,687 b''
1 1 """Implementation of code management magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 # Stdlib
16 16 import inspect
17 17 import io
18 18 import os
19 19 import re
20 20 import sys
21 21 import ast
22 22 from itertools import chain
23 23
24 24 # Our own packages
25 25 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
26 26 from IPython.core.macro import Macro
27 27 from IPython.core.magic import Magics, magics_class, line_magic
28 28 from IPython.core.oinspect import find_file, find_source_lines
29 29 from IPython.testing.skipdoctest import skip_doctest
30 30 from IPython.utils import py3compat
31 31 from IPython.utils.contexts import preserve_keys
32 32 from IPython.utils.path import get_py_filename, unquote_filename
33 33 from IPython.utils.warn import warn
34 34 from IPython.utils.text import get_text_list
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Magic implementation classes
38 38 #-----------------------------------------------------------------------------
39 39
40 40 # Used for exception handling in magic_edit
41 41 class MacroToEdit(ValueError): pass
42 42
43 43 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
44 44
45 45 # To match, e.g. 8-10 1:5 :10 3-
46 46 range_re = re.compile(r"""
47 47 (?P<start>\d+)?
48 48 ((?P<sep>[\-:])
49 49 (?P<end>\d+)?)?
50 50 $""", re.VERBOSE)
51 51
52 52
53 53 def extract_code_ranges(ranges_str):
54 54 """Turn a string of range for %%load into 2-tuples of (start, stop)
55 55 ready to use as a slice of the content splitted by lines.
56 56
57 57 Examples
58 58 --------
59 59 list(extract_input_ranges("5-10 2"))
60 60 [(4, 10), (1, 2)]
61 61 """
62 62 for range_str in ranges_str.split():
63 63 rmatch = range_re.match(range_str)
64 64 if not rmatch:
65 65 continue
66 66 sep = rmatch.group("sep")
67 67 start = rmatch.group("start")
68 68 end = rmatch.group("end")
69 69
70 70 if sep == '-':
71 71 start = int(start) - 1 if start else None
72 72 end = int(end) if end else None
73 73 elif sep == ':':
74 74 start = int(start) - 1 if start else None
75 75 end = int(end) - 1 if end else None
76 76 else:
77 77 end = int(start)
78 78 start = int(start) - 1
79 79 yield (start, end)
80 80
81 81
82 82 @skip_doctest
83 83 def extract_symbols(code, symbols):
84 84 """
85 85 Return a tuple (blocks, not_found)
86 86 where ``blocks`` is a list of code fragments
87 87 for each symbol parsed from code, and ``not_found`` are
88 88 symbols not found in the code.
89 89
90 90 For example::
91 91
92 92 >>> code = '''a = 10
93 93
94 94 def b(): return 42
95 95
96 96 class A: pass'''
97 97
98 98 >>> extract_symbols(code, 'A,b,z')
99 99 (["class A: pass", "def b(): return 42"], ['z'])
100 100 """
101 symbols = symbols.split(',')
101 102 try:
102 103 py_code = ast.parse(code)
103 104 except SyntaxError:
104 105 # ignores non python code
105 return []
106 return [], symbols
106 107
107 108 marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
108 109 code = code.split('\n')
109 110
110 111 symbols_lines = {}
111 112
112 113 # we already know the start_lineno of each symbol (marks).
113 114 # To find each end_lineno, we traverse in reverse order until each
114 115 # non-blank line
115 116 end = len(code)
116 117 for name, start in reversed(marks):
117 118 while not code[end - 1].strip():
118 119 end -= 1
119 120 if name:
120 121 symbols_lines[name] = (start - 1, end)
121 122 end = start - 1
122 123
123 124 # Now symbols_lines is a map
124 125 # {'symbol_name': (start_lineno, end_lineno), ...}
125 126
126 127 # fill a list with chunks of codes for each requested symbol
127 128 blocks = []
128 129 not_found = []
129 for symbol in symbols.split(','):
130 for symbol in symbols:
130 131 if symbol in symbols_lines:
131 132 start, end = symbols_lines[symbol]
132 133 blocks.append('\n'.join(code[start:end]) + '\n')
133 134 else:
134 135 not_found.append(symbol)
135 136
136 137 return blocks, not_found
137 138
138 139
139 140 class InteractivelyDefined(Exception):
140 141 """Exception for interactively defined variable in magic_edit"""
141 142 def __init__(self, index):
142 143 self.index = index
143 144
144 145
145 146 @magics_class
146 147 class CodeMagics(Magics):
147 148 """Magics related to code management (loading, saving, editing, ...)."""
148 149
149 150 @line_magic
150 151 def save(self, parameter_s=''):
151 152 """Save a set of lines or a macro to a given filename.
152 153
153 154 Usage:\\
154 155 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
155 156
156 157 Options:
157 158
158 159 -r: use 'raw' input. By default, the 'processed' history is used,
159 160 so that magics are loaded in their transformed version to valid
160 161 Python. If this option is given, the raw input as typed as the
161 162 command line is used instead.
162 163
163 164 -f: force overwrite. If file exists, %save will prompt for overwrite
164 165 unless -f is given.
165 166
166 167 -a: append to the file instead of overwriting it.
167 168
168 169 This function uses the same syntax as %history for input ranges,
169 170 then saves the lines to the filename you specify.
170 171
171 172 It adds a '.py' extension to the file if you don't do so yourself, and
172 173 it asks for confirmation before overwriting existing files.
173 174
174 175 If `-r` option is used, the default extension is `.ipy`.
175 176 """
176 177
177 178 opts,args = self.parse_options(parameter_s,'fra',mode='list')
178 179 if not args:
179 180 raise UsageError('Missing filename.')
180 181 raw = 'r' in opts
181 182 force = 'f' in opts
182 183 append = 'a' in opts
183 184 mode = 'a' if append else 'w'
184 185 ext = u'.ipy' if raw else u'.py'
185 186 fname, codefrom = unquote_filename(args[0]), " ".join(args[1:])
186 187 if not fname.endswith((u'.py',u'.ipy')):
187 188 fname += ext
188 189 file_exists = os.path.isfile(fname)
189 190 if file_exists and not force and not append:
190 191 try:
191 192 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
192 193 except StdinNotImplementedError:
193 194 print "File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)
194 195 return
195 196 if not overwrite :
196 197 print 'Operation cancelled.'
197 198 return
198 199 try:
199 200 cmds = self.shell.find_user_code(codefrom,raw)
200 201 except (TypeError, ValueError) as e:
201 202 print e.args[0]
202 203 return
203 204 out = py3compat.cast_unicode(cmds)
204 205 with io.open(fname, mode, encoding="utf-8") as f:
205 206 if not file_exists or not append:
206 207 f.write(u"# coding: utf-8\n")
207 208 f.write(out)
208 209 # make sure we end on a newline
209 210 if not out.endswith(u'\n'):
210 211 f.write(u'\n')
211 212 print 'The following commands were written to file `%s`:' % fname
212 213 print cmds
213 214
214 215 @line_magic
215 216 def pastebin(self, parameter_s=''):
216 217 """Upload code to Github's Gist paste bin, returning the URL.
217 218
218 219 Usage:\\
219 220 %pastebin [-d "Custom description"] 1-7
220 221
221 222 The argument can be an input history range, a filename, or the name of a
222 223 string or macro.
223 224
224 225 Options:
225 226
226 227 -d: Pass a custom description for the gist. The default will say
227 228 "Pasted from IPython".
228 229 """
229 230 opts, args = self.parse_options(parameter_s, 'd:')
230 231
231 232 try:
232 233 code = self.shell.find_user_code(args)
233 234 except (ValueError, TypeError) as e:
234 235 print e.args[0]
235 236 return
236 237
237 238 from urllib2 import urlopen # Deferred import
238 239 import json
239 240 post_data = json.dumps({
240 241 "description": opts.get('d', "Pasted from IPython"),
241 242 "public": True,
242 243 "files": {
243 244 "file1.py": {
244 245 "content": code
245 246 }
246 247 }
247 248 }).encode('utf-8')
248 249
249 250 response = urlopen("https://api.github.com/gists", post_data)
250 251 response_data = json.loads(response.read().decode('utf-8'))
251 252 return response_data['html_url']
252 253
253 254 @line_magic
254 255 def loadpy(self, arg_s):
255 256 """Alias of `%load`
256 257
257 258 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
258 259 extension. So it has been renamed simply into %load. You can look at
259 260 `%load`'s docstring for more info.
260 261 """
261 262 self.load(arg_s)
262 263
263 264 @line_magic
264 265 def load(self, arg_s):
265 266 """Load code into the current frontend.
266 267
267 268 Usage:\\
268 269 %load [options] source
269 270
270 271 where source can be a filename, URL, input history range or macro
271 272
272 273 Options:
273 274 --------
274 275 -r <lines>: Specify lines or ranges of lines to load from the source.
275 276 Ranges could be specified as x-y (x..y) or in python-style x:y
276 277 (x..(y-1)). Both limits x and y can be left blank (meaning the
277 278 beginning and end of the file, respectively).
278 279
279 280 -s <symbols>: Specify function or classes to load from python source.
280 281
281 282 -y : Don't ask confirmation for loading source above 200 000 characters.
282 283
283 284 This magic command can either take a local filename, a URL, an history
284 285 range (see %history) or a macro as argument, it will prompt for
285 286 confirmation before loading source with more than 200 000 characters, unless
286 287 -y flag is passed or if the frontend does not support raw_input::
287 288
288 289 %load myscript.py
289 290 %load 7-27
290 291 %load myMacro
291 292 %load http://www.example.com/myscript.py
292 293 %load -r 5-10 myscript.py
293 294 %load -r 10-20,30,40: foo.py
294 295 %load -s MyClass,wonder_function myscript.py
295 296 """
296 297 opts,args = self.parse_options(arg_s,'ys:r:')
297 298
298 299 if not args:
299 300 raise UsageError('Missing filename, URL, input history range, '
300 301 'or macro.')
301 302
302 303 contents = self.shell.find_user_code(args)
303 304
304 305 if 's' in opts:
305 306 blocks, not_found = extract_symbols(contents, opts['s'])
306 307 if len(not_found) == 1:
307 308 warn('The symbol `%s` was not found' % not_found[0])
308 309 elif len(not_found) > 1:
309 310 warn('The symbols %s were not found' % get_text_list(not_found,
310 311 wrap_item_with='`')
311 312 )
312 313
313 314 contents = '\n'.join(blocks)
314 315
315 316 if 'r' in opts:
316 317 ranges = opts['r'].replace(',', ' ')
317 318 lines = contents.split('\n')
318 319 slices = extract_code_ranges(ranges)
319 320 contents = [lines[slice(*slc)] for slc in slices]
320 321 contents = '\n'.join(chain.from_iterable(contents))
321 322
322 323 l = len(contents)
323 324
324 325 # 200 000 is ~ 2500 full 80 caracter lines
325 326 # so in average, more than 5000 lines
326 327 if l > 200000 and 'y' not in opts:
327 328 try:
328 329 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
329 330 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
330 331 except StdinNotImplementedError:
331 332 #asume yes if raw input not implemented
332 333 ans = True
333 334
334 335 if ans is False :
335 336 print 'Operation cancelled.'
336 337 return
337 338
338 339 self.shell.set_next_input(contents)
339 340
340 341 @staticmethod
341 342 def _find_edit_target(shell, args, opts, last_call):
342 343 """Utility method used by magic_edit to find what to edit."""
343 344
344 345 def make_filename(arg):
345 346 "Make a filename from the given args"
346 347 arg = unquote_filename(arg)
347 348 try:
348 349 filename = get_py_filename(arg)
349 350 except IOError:
350 351 # If it ends with .py but doesn't already exist, assume we want
351 352 # a new file.
352 353 if arg.endswith('.py'):
353 354 filename = arg
354 355 else:
355 356 filename = None
356 357 return filename
357 358
358 359 # Set a few locals from the options for convenience:
359 360 opts_prev = 'p' in opts
360 361 opts_raw = 'r' in opts
361 362
362 363 # custom exceptions
363 364 class DataIsObject(Exception): pass
364 365
365 366 # Default line number value
366 367 lineno = opts.get('n',None)
367 368
368 369 if opts_prev:
369 370 args = '_%s' % last_call[0]
370 371 if args not in shell.user_ns:
371 372 args = last_call[1]
372 373
373 374 # by default this is done with temp files, except when the given
374 375 # arg is a filename
375 376 use_temp = True
376 377
377 378 data = ''
378 379
379 380 # First, see if the arguments should be a filename.
380 381 filename = make_filename(args)
381 382 if filename:
382 383 use_temp = False
383 384 elif args:
384 385 # Mode where user specifies ranges of lines, like in %macro.
385 386 data = shell.extract_input_lines(args, opts_raw)
386 387 if not data:
387 388 try:
388 389 # Load the parameter given as a variable. If not a string,
389 390 # process it as an object instead (below)
390 391
391 392 #print '*** args',args,'type',type(args) # dbg
392 393 data = eval(args, shell.user_ns)
393 394 if not isinstance(data, basestring):
394 395 raise DataIsObject
395 396
396 397 except (NameError,SyntaxError):
397 398 # given argument is not a variable, try as a filename
398 399 filename = make_filename(args)
399 400 if filename is None:
400 401 warn("Argument given (%s) can't be found as a variable "
401 402 "or as a filename." % args)
402 403 return (None, None, None)
403 404 use_temp = False
404 405
405 406 except DataIsObject:
406 407 # macros have a special edit function
407 408 if isinstance(data, Macro):
408 409 raise MacroToEdit(data)
409 410
410 411 # For objects, try to edit the file where they are defined
411 412 filename = find_file(data)
412 413 if filename:
413 414 if 'fakemodule' in filename.lower() and \
414 415 inspect.isclass(data):
415 416 # class created by %edit? Try to find source
416 417 # by looking for method definitions instead, the
417 418 # __module__ in those classes is FakeModule.
418 419 attrs = [getattr(data, aname) for aname in dir(data)]
419 420 for attr in attrs:
420 421 if not inspect.ismethod(attr):
421 422 continue
422 423 filename = find_file(attr)
423 424 if filename and \
424 425 'fakemodule' not in filename.lower():
425 426 # change the attribute to be the edit
426 427 # target instead
427 428 data = attr
428 429 break
429 430
430 431 m = ipython_input_pat.match(os.path.basename(filename))
431 432 if m:
432 433 raise InteractivelyDefined(int(m.groups()[0]))
433 434
434 435 datafile = 1
435 436 if filename is None:
436 437 filename = make_filename(args)
437 438 datafile = 1
438 439 if filename is not None:
439 440 # only warn about this if we get a real name
440 441 warn('Could not find file where `%s` is defined.\n'
441 442 'Opening a file named `%s`' % (args, filename))
442 443 # Now, make sure we can actually read the source (if it was
443 444 # in a temp file it's gone by now).
444 445 if datafile:
445 446 if lineno is None:
446 447 lineno = find_source_lines(data)
447 448 if lineno is None:
448 449 filename = make_filename(args)
449 450 if filename is None:
450 451 warn('The file where `%s` was defined '
451 452 'cannot be read or found.' % data)
452 453 return (None, None, None)
453 454 use_temp = False
454 455
455 456 if use_temp:
456 457 filename = shell.mktempfile(data)
457 458 print 'IPython will make a temporary file named:',filename
458 459
459 460 # use last_call to remember the state of the previous call, but don't
460 461 # let it be clobbered by successive '-p' calls.
461 462 try:
462 463 last_call[0] = shell.displayhook.prompt_count
463 464 if not opts_prev:
464 465 last_call[1] = args
465 466 except:
466 467 pass
467 468
468 469
469 470 return filename, lineno, use_temp
470 471
471 472 def _edit_macro(self,mname,macro):
472 473 """open an editor with the macro data in a file"""
473 474 filename = self.shell.mktempfile(macro.value)
474 475 self.shell.hooks.editor(filename)
475 476
476 477 # and make a new macro object, to replace the old one
477 478 mfile = open(filename)
478 479 mvalue = mfile.read()
479 480 mfile.close()
480 481 self.shell.user_ns[mname] = Macro(mvalue)
481 482
482 483 @skip_doctest
483 484 @line_magic
484 485 def edit(self, parameter_s='',last_call=['','']):
485 486 """Bring up an editor and execute the resulting code.
486 487
487 488 Usage:
488 489 %edit [options] [args]
489 490
490 491 %edit runs IPython's editor hook. The default version of this hook is
491 492 set to call the editor specified by your $EDITOR environment variable.
492 493 If this isn't found, it will default to vi under Linux/Unix and to
493 494 notepad under Windows. See the end of this docstring for how to change
494 495 the editor hook.
495 496
496 497 You can also set the value of this editor via the
497 498 ``TerminalInteractiveShell.editor`` option in your configuration file.
498 499 This is useful if you wish to use a different editor from your typical
499 500 default with IPython (and for Windows users who typically don't set
500 501 environment variables).
501 502
502 503 This command allows you to conveniently edit multi-line code right in
503 504 your IPython session.
504 505
505 506 If called without arguments, %edit opens up an empty editor with a
506 507 temporary file and will execute the contents of this file when you
507 508 close it (don't forget to save it!).
508 509
509 510
510 511 Options:
511 512
512 513 -n <number>: open the editor at a specified line number. By default,
513 514 the IPython editor hook uses the unix syntax 'editor +N filename', but
514 515 you can configure this by providing your own modified hook if your
515 516 favorite editor supports line-number specifications with a different
516 517 syntax.
517 518
518 519 -p: this will call the editor with the same data as the previous time
519 520 it was used, regardless of how long ago (in your current session) it
520 521 was.
521 522
522 523 -r: use 'raw' input. This option only applies to input taken from the
523 524 user's history. By default, the 'processed' history is used, so that
524 525 magics are loaded in their transformed version to valid Python. If
525 526 this option is given, the raw input as typed as the command line is
526 527 used instead. When you exit the editor, it will be executed by
527 528 IPython's own processor.
528 529
529 530 -x: do not execute the edited code immediately upon exit. This is
530 531 mainly useful if you are editing programs which need to be called with
531 532 command line arguments, which you can then do using %run.
532 533
533 534
534 535 Arguments:
535 536
536 537 If arguments are given, the following possibilities exist:
537 538
538 539 - If the argument is a filename, IPython will load that into the
539 540 editor. It will execute its contents with execfile() when you exit,
540 541 loading any code in the file into your interactive namespace.
541 542
542 543 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
543 544 The syntax is the same as in the %history magic.
544 545
545 546 - If the argument is a string variable, its contents are loaded
546 547 into the editor. You can thus edit any string which contains
547 548 python code (including the result of previous edits).
548 549
549 550 - If the argument is the name of an object (other than a string),
550 551 IPython will try to locate the file where it was defined and open the
551 552 editor at the point where it is defined. You can use `%edit function`
552 553 to load an editor exactly at the point where 'function' is defined,
553 554 edit it and have the file be executed automatically.
554 555
555 556 - If the object is a macro (see %macro for details), this opens up your
556 557 specified editor with a temporary file containing the macro's data.
557 558 Upon exit, the macro is reloaded with the contents of the file.
558 559
559 560 Note: opening at an exact line is only supported under Unix, and some
560 561 editors (like kedit and gedit up to Gnome 2.8) do not understand the
561 562 '+NUMBER' parameter necessary for this feature. Good editors like
562 563 (X)Emacs, vi, jed, pico and joe all do.
563 564
564 565 After executing your code, %edit will return as output the code you
565 566 typed in the editor (except when it was an existing file). This way
566 567 you can reload the code in further invocations of %edit as a variable,
567 568 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
568 569 the output.
569 570
570 571 Note that %edit is also available through the alias %ed.
571 572
572 573 This is an example of creating a simple function inside the editor and
573 574 then modifying it. First, start up the editor::
574 575
575 576 In [1]: edit
576 577 Editing... done. Executing edited code...
577 578 Out[1]: 'def foo():\\n print "foo() was defined in an editing
578 579 session"\\n'
579 580
580 581 We can then call the function foo()::
581 582
582 583 In [2]: foo()
583 584 foo() was defined in an editing session
584 585
585 586 Now we edit foo. IPython automatically loads the editor with the
586 587 (temporary) file where foo() was previously defined::
587 588
588 589 In [3]: edit foo
589 590 Editing... done. Executing edited code...
590 591
591 592 And if we call foo() again we get the modified version::
592 593
593 594 In [4]: foo()
594 595 foo() has now been changed!
595 596
596 597 Here is an example of how to edit a code snippet successive
597 598 times. First we call the editor::
598 599
599 600 In [5]: edit
600 601 Editing... done. Executing edited code...
601 602 hello
602 603 Out[5]: "print 'hello'\\n"
603 604
604 605 Now we call it again with the previous output (stored in _)::
605 606
606 607 In [6]: edit _
607 608 Editing... done. Executing edited code...
608 609 hello world
609 610 Out[6]: "print 'hello world'\\n"
610 611
611 612 Now we call it with the output #8 (stored in _8, also as Out[8])::
612 613
613 614 In [7]: edit _8
614 615 Editing... done. Executing edited code...
615 616 hello again
616 617 Out[7]: "print 'hello again'\\n"
617 618
618 619
619 620 Changing the default editor hook:
620 621
621 622 If you wish to write your own editor hook, you can put it in a
622 623 configuration file which you load at startup time. The default hook
623 624 is defined in the IPython.core.hooks module, and you can use that as a
624 625 starting example for further modifications. That file also has
625 626 general instructions on how to set a new hook for use once you've
626 627 defined it."""
627 628 opts,args = self.parse_options(parameter_s,'prxn:')
628 629
629 630 try:
630 631 filename, lineno, is_temp = self._find_edit_target(self.shell,
631 632 args, opts, last_call)
632 633 except MacroToEdit as e:
633 634 self._edit_macro(args, e.args[0])
634 635 return
635 636 except InteractivelyDefined as e:
636 637 print "Editing In[%i]" % e.index
637 638 args = str(e.index)
638 639 filename, lineno, is_temp = self._find_edit_target(self.shell,
639 640 args, opts, last_call)
640 641 if filename is None:
641 642 # nothing was found, warnings have already been issued,
642 643 # just give up.
643 644 return
644 645
645 646 # do actual editing here
646 647 print 'Editing...',
647 648 sys.stdout.flush()
648 649 try:
649 650 # Quote filenames that may have spaces in them
650 651 if ' ' in filename:
651 652 filename = "'%s'" % filename
652 653 self.shell.hooks.editor(filename,lineno)
653 654 except TryNext:
654 655 warn('Could not open editor')
655 656 return
656 657
657 658 # XXX TODO: should this be generalized for all string vars?
658 659 # For now, this is special-cased to blocks created by cpaste
659 660 if args.strip() == 'pasted_block':
660 661 with open(filename, 'r') as f:
661 662 self.shell.user_ns['pasted_block'] = f.read()
662 663
663 664 if 'x' in opts: # -x prevents actual execution
664 665 print
665 666 else:
666 667 print 'done. Executing edited code...'
667 668 with preserve_keys(self.shell.user_ns, '__file__'):
668 669 if not is_temp:
669 670 self.shell.user_ns['__file__'] = filename
670 671 if 'r' in opts: # Untranslated IPython code
671 672 with open(filename, 'r') as f:
672 673 source = f.read()
673 674 self.shell.run_cell(source, store_history=False)
674 675 else:
675 676 self.shell.safe_execfile(filename, self.shell.user_ns,
676 677 self.shell.user_ns)
677 678
678 679 if is_temp:
679 680 try:
680 681 return open(filename).read()
681 682 except IOError as msg:
682 683 if msg.filename == filename:
683 684 warn('File not found. Did you forget to save?')
684 685 return
685 686 else:
686 687 self.shell.showtraceback()
@@ -1,932 +1,932 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 from __future__ import absolute_import
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Imports
10 10 #-----------------------------------------------------------------------------
11 11
12 12 import io
13 13 import os
14 14 import sys
15 15 from StringIO import StringIO
16 16 from unittest import TestCase
17 17
18 18 try:
19 19 from importlib import invalidate_caches # Required from Python 3.3
20 20 except ImportError:
21 21 def invalidate_caches():
22 22 pass
23 23
24 24 import nose.tools as nt
25 25
26 26 from IPython.core import magic
27 27 from IPython.core.magic import (Magics, magics_class, line_magic,
28 28 cell_magic, line_cell_magic,
29 29 register_line_magic, register_cell_magic,
30 30 register_line_cell_magic)
31 31 from IPython.core.magics import execution, script, code
32 32 from IPython.nbformat.v3.tests.nbexamples import nb0
33 33 from IPython.nbformat import current
34 34 from IPython.testing import decorators as dec
35 35 from IPython.testing import tools as tt
36 36 from IPython.utils import py3compat
37 37 from IPython.utils.io import capture_output
38 38 from IPython.utils.tempdir import TemporaryDirectory
39 39 from IPython.utils.process import find_cmd
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Test functions begin
43 43 #-----------------------------------------------------------------------------
44 44
45 45 @magic.magics_class
46 46 class DummyMagics(magic.Magics): pass
47 47
48 48 def test_extract_code_ranges():
49 49 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
50 50 expected = [(0, 1),
51 51 (2, 3),
52 52 (4, 6),
53 53 (6, 9),
54 54 (9, 14),
55 55 (16, None),
56 56 (None, 9),
57 57 (9, None),
58 58 (None, 13),
59 59 (None, None)]
60 60 actual = list(code.extract_code_ranges(instr))
61 61 nt.assert_equal(actual, expected)
62 62
63 63
64 64 def test_extract_symbols():
65 65 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
66 66 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
67 67 expected = [([], ['a']),
68 68 (["def b():\n return 42\n"], []),
69 69 (["class A: pass\n"], []),
70 70 (["class A: pass\n", "def b():\n return 42\n"], []),
71 71 (["class A: pass\n"], ['a']),
72 72 ([], ['z'])]
73 73 for symbols, exp in zip(symbols_args, expected):
74 74 nt.assert_equal(code.extract_symbols(source, symbols), exp)
75 75
76 76
77 77 def test_extract_symbols_ignores_non_python_code():
78 78 source = ("=begin A Ruby program :)=end\n"
79 79 "def hello\n"
80 80 "puts 'Hello world'\n"
81 81 "end")
82 nt.assert_equal(code.extract_symbols(source, "hello"), [])
82 nt.assert_equal(code.extract_symbols(source, "hello"), ([], ['hello']))
83 83
84 84
85 85 def test_rehashx():
86 86 # clear up everything
87 87 _ip = get_ipython()
88 88 _ip.alias_manager.clear_aliases()
89 89 del _ip.db['syscmdlist']
90 90
91 91 _ip.magic('rehashx')
92 92 # Practically ALL ipython development systems will have more than 10 aliases
93 93
94 94 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
95 95 for name, cmd in _ip.alias_manager.aliases:
96 96 # we must strip dots from alias names
97 97 nt.assert_not_in('.', name)
98 98
99 99 # rehashx must fill up syscmdlist
100 100 scoms = _ip.db['syscmdlist']
101 101 nt.assert_true(len(scoms) > 10)
102 102
103 103
104 104 def test_magic_parse_options():
105 105 """Test that we don't mangle paths when parsing magic options."""
106 106 ip = get_ipython()
107 107 path = 'c:\\x'
108 108 m = DummyMagics(ip)
109 109 opts = m.parse_options('-f %s' % path,'f:')[0]
110 110 # argv splitting is os-dependent
111 111 if os.name == 'posix':
112 112 expected = 'c:x'
113 113 else:
114 114 expected = path
115 115 nt.assert_equal(opts['f'], expected)
116 116
117 117 def test_magic_parse_long_options():
118 118 """Magic.parse_options can handle --foo=bar long options"""
119 119 ip = get_ipython()
120 120 m = DummyMagics(ip)
121 121 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
122 122 nt.assert_in('foo', opts)
123 123 nt.assert_in('bar', opts)
124 124 nt.assert_equal(opts['bar'], "bubble")
125 125
126 126
127 127 @dec.skip_without('sqlite3')
128 128 def doctest_hist_f():
129 129 """Test %hist -f with temporary filename.
130 130
131 131 In [9]: import tempfile
132 132
133 133 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
134 134
135 135 In [11]: %hist -nl -f $tfile 3
136 136
137 137 In [13]: import os; os.unlink(tfile)
138 138 """
139 139
140 140
141 141 @dec.skip_without('sqlite3')
142 142 def doctest_hist_r():
143 143 """Test %hist -r
144 144
145 145 XXX - This test is not recording the output correctly. For some reason, in
146 146 testing mode the raw history isn't getting populated. No idea why.
147 147 Disabling the output checking for now, though at least we do run it.
148 148
149 149 In [1]: 'hist' in _ip.lsmagic()
150 150 Out[1]: True
151 151
152 152 In [2]: x=1
153 153
154 154 In [3]: %hist -rl 2
155 155 x=1 # random
156 156 %hist -r 2
157 157 """
158 158
159 159
160 160 @dec.skip_without('sqlite3')
161 161 def doctest_hist_op():
162 162 """Test %hist -op
163 163
164 164 In [1]: class b(float):
165 165 ...: pass
166 166 ...:
167 167
168 168 In [2]: class s(object):
169 169 ...: def __str__(self):
170 170 ...: return 's'
171 171 ...:
172 172
173 173 In [3]:
174 174
175 175 In [4]: class r(b):
176 176 ...: def __repr__(self):
177 177 ...: return 'r'
178 178 ...:
179 179
180 180 In [5]: class sr(s,r): pass
181 181 ...:
182 182
183 183 In [6]:
184 184
185 185 In [7]: bb=b()
186 186
187 187 In [8]: ss=s()
188 188
189 189 In [9]: rr=r()
190 190
191 191 In [10]: ssrr=sr()
192 192
193 193 In [11]: 4.5
194 194 Out[11]: 4.5
195 195
196 196 In [12]: str(ss)
197 197 Out[12]: 's'
198 198
199 199 In [13]:
200 200
201 201 In [14]: %hist -op
202 202 >>> class b:
203 203 ... pass
204 204 ...
205 205 >>> class s(b):
206 206 ... def __str__(self):
207 207 ... return 's'
208 208 ...
209 209 >>>
210 210 >>> class r(b):
211 211 ... def __repr__(self):
212 212 ... return 'r'
213 213 ...
214 214 >>> class sr(s,r): pass
215 215 >>>
216 216 >>> bb=b()
217 217 >>> ss=s()
218 218 >>> rr=r()
219 219 >>> ssrr=sr()
220 220 >>> 4.5
221 221 4.5
222 222 >>> str(ss)
223 223 's'
224 224 >>>
225 225 """
226 226
227 227
228 228 @dec.skip_without('sqlite3')
229 229 def test_macro():
230 230 ip = get_ipython()
231 231 ip.history_manager.reset() # Clear any existing history.
232 232 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
233 233 for i, cmd in enumerate(cmds, start=1):
234 234 ip.history_manager.store_inputs(i, cmd)
235 235 ip.magic("macro test 1-3")
236 236 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
237 237
238 238 # List macros
239 239 nt.assert_in("test", ip.magic("macro"))
240 240
241 241
242 242 @dec.skip_without('sqlite3')
243 243 def test_macro_run():
244 244 """Test that we can run a multi-line macro successfully."""
245 245 ip = get_ipython()
246 246 ip.history_manager.reset()
247 247 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
248 248 "%macro test 2-3"]
249 249 for cmd in cmds:
250 250 ip.run_cell(cmd, store_history=True)
251 251 nt.assert_equal(ip.user_ns["test"].value,
252 252 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
253 253 with tt.AssertPrints("12"):
254 254 ip.run_cell("test")
255 255 with tt.AssertPrints("13"):
256 256 ip.run_cell("test")
257 257
258 258
259 259 def test_magic_magic():
260 260 """Test %magic"""
261 261 ip = get_ipython()
262 262 with capture_output() as captured:
263 263 ip.magic("magic")
264 264
265 265 stdout = captured.stdout
266 266 nt.assert_in('%magic', stdout)
267 267 nt.assert_in('IPython', stdout)
268 268 nt.assert_in('Available', stdout)
269 269
270 270
271 271 @dec.skipif_not_numpy
272 272 def test_numpy_reset_array_undec():
273 273 "Test '%reset array' functionality"
274 274 _ip.ex('import numpy as np')
275 275 _ip.ex('a = np.empty(2)')
276 276 nt.assert_in('a', _ip.user_ns)
277 277 _ip.magic('reset -f array')
278 278 nt.assert_not_in('a', _ip.user_ns)
279 279
280 280 def test_reset_out():
281 281 "Test '%reset out' magic"
282 282 _ip.run_cell("parrot = 'dead'", store_history=True)
283 283 # test '%reset -f out', make an Out prompt
284 284 _ip.run_cell("parrot", store_history=True)
285 285 nt.assert_true('dead' in [_ip.user_ns[x] for x in '_','__','___'])
286 286 _ip.magic('reset -f out')
287 287 nt.assert_false('dead' in [_ip.user_ns[x] for x in '_','__','___'])
288 288 nt.assert_equal(len(_ip.user_ns['Out']), 0)
289 289
290 290 def test_reset_in():
291 291 "Test '%reset in' magic"
292 292 # test '%reset -f in'
293 293 _ip.run_cell("parrot", store_history=True)
294 294 nt.assert_true('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
295 295 _ip.magic('%reset -f in')
296 296 nt.assert_false('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
297 297 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
298 298
299 299 def test_reset_dhist():
300 300 "Test '%reset dhist' magic"
301 301 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
302 302 _ip.magic('cd ' + os.path.dirname(nt.__file__))
303 303 _ip.magic('cd -')
304 304 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
305 305 _ip.magic('reset -f dhist')
306 306 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
307 307 _ip.run_cell("_dh = [d for d in tmp]") #restore
308 308
309 309 def test_reset_in_length():
310 310 "Test that '%reset in' preserves In[] length"
311 311 _ip.run_cell("print 'foo'")
312 312 _ip.run_cell("reset -f in")
313 313 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
314 314
315 315 def test_tb_syntaxerror():
316 316 """test %tb after a SyntaxError"""
317 317 ip = get_ipython()
318 318 ip.run_cell("for")
319 319
320 320 # trap and validate stdout
321 321 save_stdout = sys.stdout
322 322 try:
323 323 sys.stdout = StringIO()
324 324 ip.run_cell("%tb")
325 325 out = sys.stdout.getvalue()
326 326 finally:
327 327 sys.stdout = save_stdout
328 328 # trim output, and only check the last line
329 329 last_line = out.rstrip().splitlines()[-1].strip()
330 330 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
331 331
332 332
333 333 def test_time():
334 334 ip = get_ipython()
335 335
336 336 with tt.AssertPrints("Wall time: "):
337 337 ip.run_cell("%time None")
338 338
339 339 ip.run_cell("def f(kmjy):\n"
340 340 " %time print (2*kmjy)")
341 341
342 342 with tt.AssertPrints("Wall time: "):
343 343 with tt.AssertPrints("hihi", suppress=False):
344 344 ip.run_cell("f('hi')")
345 345
346 346
347 347 @dec.skip_win32
348 348 def test_time2():
349 349 ip = get_ipython()
350 350
351 351 with tt.AssertPrints("CPU times: user "):
352 352 ip.run_cell("%time None")
353 353
354 354 def test_time3():
355 355 """Erroneous magic function calls, issue gh-3334"""
356 356 ip = get_ipython()
357 357 ip.user_ns.pop('run', None)
358 358
359 359 with tt.AssertNotPrints("not found", channel='stderr'):
360 360 ip.run_cell("%%time\n"
361 361 "run = 0\n"
362 362 "run += 1")
363 363
364 364 def test_doctest_mode():
365 365 "Toggle doctest_mode twice, it should be a no-op and run without error"
366 366 _ip.magic('doctest_mode')
367 367 _ip.magic('doctest_mode')
368 368
369 369
370 370 def test_parse_options():
371 371 """Tests for basic options parsing in magics."""
372 372 # These are only the most minimal of tests, more should be added later. At
373 373 # the very least we check that basic text/unicode calls work OK.
374 374 m = DummyMagics(_ip)
375 375 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
376 376 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
377 377
378 378
379 379 def test_dirops():
380 380 """Test various directory handling operations."""
381 381 # curpath = lambda :os.path.splitdrive(os.getcwdu())[1].replace('\\','/')
382 382 curpath = os.getcwdu
383 383 startdir = os.getcwdu()
384 384 ipdir = os.path.realpath(_ip.ipython_dir)
385 385 try:
386 386 _ip.magic('cd "%s"' % ipdir)
387 387 nt.assert_equal(curpath(), ipdir)
388 388 _ip.magic('cd -')
389 389 nt.assert_equal(curpath(), startdir)
390 390 _ip.magic('pushd "%s"' % ipdir)
391 391 nt.assert_equal(curpath(), ipdir)
392 392 _ip.magic('popd')
393 393 nt.assert_equal(curpath(), startdir)
394 394 finally:
395 395 os.chdir(startdir)
396 396
397 397
398 398 def test_xmode():
399 399 # Calling xmode three times should be a no-op
400 400 xmode = _ip.InteractiveTB.mode
401 401 for i in range(3):
402 402 _ip.magic("xmode")
403 403 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
404 404
405 405 def test_reset_hard():
406 406 monitor = []
407 407 class A(object):
408 408 def __del__(self):
409 409 monitor.append(1)
410 410 def __repr__(self):
411 411 return "<A instance>"
412 412
413 413 _ip.user_ns["a"] = A()
414 414 _ip.run_cell("a")
415 415
416 416 nt.assert_equal(monitor, [])
417 417 _ip.magic("reset -f")
418 418 nt.assert_equal(monitor, [1])
419 419
420 420 class TestXdel(tt.TempFileMixin):
421 421 def test_xdel(self):
422 422 """Test that references from %run are cleared by xdel."""
423 423 src = ("class A(object):\n"
424 424 " monitor = []\n"
425 425 " def __del__(self):\n"
426 426 " self.monitor.append(1)\n"
427 427 "a = A()\n")
428 428 self.mktmp(src)
429 429 # %run creates some hidden references...
430 430 _ip.magic("run %s" % self.fname)
431 431 # ... as does the displayhook.
432 432 _ip.run_cell("a")
433 433
434 434 monitor = _ip.user_ns["A"].monitor
435 435 nt.assert_equal(monitor, [])
436 436
437 437 _ip.magic("xdel a")
438 438
439 439 # Check that a's __del__ method has been called.
440 440 nt.assert_equal(monitor, [1])
441 441
442 442 def doctest_who():
443 443 """doctest for %who
444 444
445 445 In [1]: %reset -f
446 446
447 447 In [2]: alpha = 123
448 448
449 449 In [3]: beta = 'beta'
450 450
451 451 In [4]: %who int
452 452 alpha
453 453
454 454 In [5]: %who str
455 455 beta
456 456
457 457 In [6]: %whos
458 458 Variable Type Data/Info
459 459 ----------------------------
460 460 alpha int 123
461 461 beta str beta
462 462
463 463 In [7]: %who_ls
464 464 Out[7]: ['alpha', 'beta']
465 465 """
466 466
467 467 def test_whos():
468 468 """Check that whos is protected against objects where repr() fails."""
469 469 class A(object):
470 470 def __repr__(self):
471 471 raise Exception()
472 472 _ip.user_ns['a'] = A()
473 473 _ip.magic("whos")
474 474
475 475 @py3compat.u_format
476 476 def doctest_precision():
477 477 """doctest for %precision
478 478
479 479 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
480 480
481 481 In [2]: %precision 5
482 482 Out[2]: {u}'%.5f'
483 483
484 484 In [3]: f.float_format
485 485 Out[3]: {u}'%.5f'
486 486
487 487 In [4]: %precision %e
488 488 Out[4]: {u}'%e'
489 489
490 490 In [5]: f(3.1415927)
491 491 Out[5]: {u}'3.141593e+00'
492 492 """
493 493
494 494 def test_psearch():
495 495 with tt.AssertPrints("dict.fromkeys"):
496 496 _ip.run_cell("dict.fr*?")
497 497
498 498 def test_timeit_shlex():
499 499 """test shlex issues with timeit (#1109)"""
500 500 _ip.ex("def f(*a,**kw): pass")
501 501 _ip.magic('timeit -n1 "this is a bug".count(" ")')
502 502 _ip.magic('timeit -r1 -n1 f(" ", 1)')
503 503 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
504 504 _ip.magic('timeit -r1 -n1 ("a " + "b")')
505 505 _ip.magic('timeit -r1 -n1 f("a " + "b")')
506 506 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
507 507
508 508
509 509 def test_timeit_arguments():
510 510 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
511 511 _ip.magic("timeit ('#')")
512 512
513 513
514 514 def test_timeit_special_syntax():
515 515 "Test %%timeit with IPython special syntax"
516 516 @register_line_magic
517 517 def lmagic(line):
518 518 ip = get_ipython()
519 519 ip.user_ns['lmagic_out'] = line
520 520
521 521 # line mode test
522 522 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
523 523 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
524 524 # cell mode test
525 525 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
526 526 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
527 527
528 528 def test_timeit_return():
529 529 """
530 530 test wether timeit -o return object
531 531 """
532 532
533 533 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
534 534 assert(res is not None)
535 535
536 536 def test_timeit_quiet():
537 537 """
538 538 test quiet option of timeit magic
539 539 """
540 540 with tt.AssertNotPrints("loops"):
541 541 _ip.run_cell("%timeit -n1 -r1 -q 1")
542 542
543 543 @dec.skipif(execution.profile is None)
544 544 def test_prun_special_syntax():
545 545 "Test %%prun with IPython special syntax"
546 546 @register_line_magic
547 547 def lmagic(line):
548 548 ip = get_ipython()
549 549 ip.user_ns['lmagic_out'] = line
550 550
551 551 # line mode test
552 552 _ip.run_line_magic('prun', '-q %lmagic my line')
553 553 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
554 554 # cell mode test
555 555 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
556 556 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
557 557
558 558 @dec.skipif(execution.profile is None)
559 559 def test_prun_quotes():
560 560 "Test that prun does not clobber string escapes (GH #1302)"
561 561 _ip.magic(r"prun -q x = '\t'")
562 562 nt.assert_equal(_ip.user_ns['x'], '\t')
563 563
564 564 def test_extension():
565 565 tmpdir = TemporaryDirectory()
566 566 orig_ipython_dir = _ip.ipython_dir
567 567 try:
568 568 _ip.ipython_dir = tmpdir.name
569 569 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
570 570 url = os.path.join(os.path.dirname(__file__), "daft_extension.py")
571 571 _ip.magic("install_ext %s" % url)
572 572 _ip.user_ns.pop('arq', None)
573 573 invalidate_caches() # Clear import caches
574 574 _ip.magic("load_ext daft_extension")
575 575 nt.assert_equal(_ip.user_ns['arq'], 185)
576 576 _ip.magic("unload_ext daft_extension")
577 577 assert 'arq' not in _ip.user_ns
578 578 finally:
579 579 _ip.ipython_dir = orig_ipython_dir
580 580 tmpdir.cleanup()
581 581
582 582 def test_notebook_export_json():
583 583 with TemporaryDirectory() as td:
584 584 outfile = os.path.join(td, "nb.ipynb")
585 585 _ip.ex(py3compat.u_format(u"u = {u}'héllo'"))
586 586 _ip.magic("notebook -e %s" % outfile)
587 587
588 588 def test_notebook_export_py():
589 589 with TemporaryDirectory() as td:
590 590 outfile = os.path.join(td, "nb.py")
591 591 _ip.ex(py3compat.u_format(u"u = {u}'héllo'"))
592 592 _ip.magic("notebook -e %s" % outfile)
593 593
594 594 def test_notebook_reformat_py():
595 595 with TemporaryDirectory() as td:
596 596 infile = os.path.join(td, "nb.ipynb")
597 597 with io.open(infile, 'w', encoding='utf-8') as f:
598 598 current.write(nb0, f, 'json')
599 599
600 600 _ip.ex(py3compat.u_format(u"u = {u}'héllo'"))
601 601 _ip.magic("notebook -f py %s" % infile)
602 602
603 603 def test_notebook_reformat_json():
604 604 with TemporaryDirectory() as td:
605 605 infile = os.path.join(td, "nb.py")
606 606 with io.open(infile, 'w', encoding='utf-8') as f:
607 607 current.write(nb0, f, 'py')
608 608
609 609 _ip.ex(py3compat.u_format(u"u = {u}'héllo'"))
610 610 _ip.magic("notebook -f ipynb %s" % infile)
611 611 _ip.magic("notebook -f json %s" % infile)
612 612
613 613 def test_env():
614 614 env = _ip.magic("env")
615 615 assert isinstance(env, dict), type(env)
616 616
617 617
618 618 class CellMagicTestCase(TestCase):
619 619
620 620 def check_ident(self, magic):
621 621 # Manually called, we get the result
622 622 out = _ip.run_cell_magic(magic, 'a', 'b')
623 623 nt.assert_equal(out, ('a','b'))
624 624 # Via run_cell, it goes into the user's namespace via displayhook
625 625 _ip.run_cell('%%' + magic +' c\nd')
626 626 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
627 627
628 628 def test_cell_magic_func_deco(self):
629 629 "Cell magic using simple decorator"
630 630 @register_cell_magic
631 631 def cellm(line, cell):
632 632 return line, cell
633 633
634 634 self.check_ident('cellm')
635 635
636 636 def test_cell_magic_reg(self):
637 637 "Cell magic manually registered"
638 638 def cellm(line, cell):
639 639 return line, cell
640 640
641 641 _ip.register_magic_function(cellm, 'cell', 'cellm2')
642 642 self.check_ident('cellm2')
643 643
644 644 def test_cell_magic_class(self):
645 645 "Cell magics declared via a class"
646 646 @magics_class
647 647 class MyMagics(Magics):
648 648
649 649 @cell_magic
650 650 def cellm3(self, line, cell):
651 651 return line, cell
652 652
653 653 _ip.register_magics(MyMagics)
654 654 self.check_ident('cellm3')
655 655
656 656 def test_cell_magic_class2(self):
657 657 "Cell magics declared via a class, #2"
658 658 @magics_class
659 659 class MyMagics2(Magics):
660 660
661 661 @cell_magic('cellm4')
662 662 def cellm33(self, line, cell):
663 663 return line, cell
664 664
665 665 _ip.register_magics(MyMagics2)
666 666 self.check_ident('cellm4')
667 667 # Check that nothing is registered as 'cellm33'
668 668 c33 = _ip.find_cell_magic('cellm33')
669 669 nt.assert_equal(c33, None)
670 670
671 671 def test_file():
672 672 """Basic %%file"""
673 673 ip = get_ipython()
674 674 with TemporaryDirectory() as td:
675 675 fname = os.path.join(td, 'file1')
676 676 ip.run_cell_magic("file", fname, u'\n'.join([
677 677 'line1',
678 678 'line2',
679 679 ]))
680 680 with open(fname) as f:
681 681 s = f.read()
682 682 nt.assert_in('line1\n', s)
683 683 nt.assert_in('line2', s)
684 684
685 685 def test_file_var_expand():
686 686 """%%file $filename"""
687 687 ip = get_ipython()
688 688 with TemporaryDirectory() as td:
689 689 fname = os.path.join(td, 'file1')
690 690 ip.user_ns['filename'] = fname
691 691 ip.run_cell_magic("file", '$filename', u'\n'.join([
692 692 'line1',
693 693 'line2',
694 694 ]))
695 695 with open(fname) as f:
696 696 s = f.read()
697 697 nt.assert_in('line1\n', s)
698 698 nt.assert_in('line2', s)
699 699
700 700 def test_file_unicode():
701 701 """%%file with unicode cell"""
702 702 ip = get_ipython()
703 703 with TemporaryDirectory() as td:
704 704 fname = os.path.join(td, 'file1')
705 705 ip.run_cell_magic("file", fname, u'\n'.join([
706 706 u'liné1',
707 707 u'liné2',
708 708 ]))
709 709 with io.open(fname, encoding='utf-8') as f:
710 710 s = f.read()
711 711 nt.assert_in(u'liné1\n', s)
712 712 nt.assert_in(u'liné2', s)
713 713
714 714 def test_file_amend():
715 715 """%%file -a amends files"""
716 716 ip = get_ipython()
717 717 with TemporaryDirectory() as td:
718 718 fname = os.path.join(td, 'file2')
719 719 ip.run_cell_magic("file", fname, u'\n'.join([
720 720 'line1',
721 721 'line2',
722 722 ]))
723 723 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
724 724 'line3',
725 725 'line4',
726 726 ]))
727 727 with open(fname) as f:
728 728 s = f.read()
729 729 nt.assert_in('line1\n', s)
730 730 nt.assert_in('line3\n', s)
731 731
732 732
733 733 def test_script_config():
734 734 ip = get_ipython()
735 735 ip.config.ScriptMagics.script_magics = ['whoda']
736 736 sm = script.ScriptMagics(shell=ip)
737 737 nt.assert_in('whoda', sm.magics['cell'])
738 738
739 739 @dec.skip_win32
740 740 def test_script_out():
741 741 ip = get_ipython()
742 742 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
743 743 nt.assert_equal(ip.user_ns['output'], 'hi\n')
744 744
745 745 @dec.skip_win32
746 746 def test_script_err():
747 747 ip = get_ipython()
748 748 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
749 749 nt.assert_equal(ip.user_ns['error'], 'hello\n')
750 750
751 751 @dec.skip_win32
752 752 def test_script_out_err():
753 753 ip = get_ipython()
754 754 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
755 755 nt.assert_equal(ip.user_ns['output'], 'hi\n')
756 756 nt.assert_equal(ip.user_ns['error'], 'hello\n')
757 757
758 758 @dec.skip_win32
759 759 def test_script_bg_out():
760 760 ip = get_ipython()
761 761 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
762 762 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
763 763
764 764 @dec.skip_win32
765 765 def test_script_bg_err():
766 766 ip = get_ipython()
767 767 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
768 768 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
769 769
770 770 @dec.skip_win32
771 771 def test_script_bg_out_err():
772 772 ip = get_ipython()
773 773 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
774 774 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
775 775 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
776 776
777 777 def test_script_defaults():
778 778 ip = get_ipython()
779 779 for cmd in ['sh', 'bash', 'perl', 'ruby']:
780 780 try:
781 781 find_cmd(cmd)
782 782 except Exception:
783 783 pass
784 784 else:
785 785 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
786 786
787 787
788 788 @magics_class
789 789 class FooFoo(Magics):
790 790 """class with both %foo and %%foo magics"""
791 791 @line_magic('foo')
792 792 def line_foo(self, line):
793 793 "I am line foo"
794 794 pass
795 795
796 796 @cell_magic("foo")
797 797 def cell_foo(self, line, cell):
798 798 "I am cell foo, not line foo"
799 799 pass
800 800
801 801 def test_line_cell_info():
802 802 """%%foo and %foo magics are distinguishable to inspect"""
803 803 ip = get_ipython()
804 804 ip.magics_manager.register(FooFoo)
805 805 oinfo = ip.object_inspect('foo')
806 806 nt.assert_true(oinfo['found'])
807 807 nt.assert_true(oinfo['ismagic'])
808 808
809 809 oinfo = ip.object_inspect('%%foo')
810 810 nt.assert_true(oinfo['found'])
811 811 nt.assert_true(oinfo['ismagic'])
812 812 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
813 813
814 814 oinfo = ip.object_inspect('%foo')
815 815 nt.assert_true(oinfo['found'])
816 816 nt.assert_true(oinfo['ismagic'])
817 817 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
818 818
819 819 def test_multiple_magics():
820 820 ip = get_ipython()
821 821 foo1 = FooFoo(ip)
822 822 foo2 = FooFoo(ip)
823 823 mm = ip.magics_manager
824 824 mm.register(foo1)
825 825 nt.assert_true(mm.magics['line']['foo'].im_self is foo1)
826 826 mm.register(foo2)
827 827 nt.assert_true(mm.magics['line']['foo'].im_self is foo2)
828 828
829 829 def test_alias_magic():
830 830 """Test %alias_magic."""
831 831 ip = get_ipython()
832 832 mm = ip.magics_manager
833 833
834 834 # Basic operation: both cell and line magics are created, if possible.
835 835 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
836 836 nt.assert_in('timeit_alias', mm.magics['line'])
837 837 nt.assert_in('timeit_alias', mm.magics['cell'])
838 838
839 839 # --cell is specified, line magic not created.
840 840 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
841 841 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
842 842 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
843 843
844 844 # Test that line alias is created successfully.
845 845 ip.run_line_magic('alias_magic', '--line env_alias env')
846 846 nt.assert_equal(ip.run_line_magic('env', ''),
847 847 ip.run_line_magic('env_alias', ''))
848 848
849 849 def test_save():
850 850 """Test %save."""
851 851 ip = get_ipython()
852 852 ip.history_manager.reset() # Clear any existing history.
853 853 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
854 854 for i, cmd in enumerate(cmds, start=1):
855 855 ip.history_manager.store_inputs(i, cmd)
856 856 with TemporaryDirectory() as tmpdir:
857 857 file = os.path.join(tmpdir, "testsave.py")
858 858 ip.run_line_magic("save", "%s 1-10" % file)
859 859 with open(file) as f:
860 860 content = f.read()
861 861 nt.assert_equal(content.count(cmds[0]), 1)
862 862 nt.assert_in('coding: utf-8', content)
863 863 ip.run_line_magic("save", "-a %s 1-10" % file)
864 864 with open(file) as f:
865 865 content = f.read()
866 866 nt.assert_equal(content.count(cmds[0]), 2)
867 867 nt.assert_in('coding: utf-8', content)
868 868
869 869
870 870 def test_store():
871 871 """Test %store."""
872 872 ip = get_ipython()
873 873 ip.run_line_magic('load_ext', 'storemagic')
874 874
875 875 # make sure the storage is empty
876 876 ip.run_line_magic('store', '-z')
877 877 ip.user_ns['var'] = 42
878 878 ip.run_line_magic('store', 'var')
879 879 ip.user_ns['var'] = 39
880 880 ip.run_line_magic('store', '-r')
881 881 nt.assert_equal(ip.user_ns['var'], 42)
882 882
883 883 ip.run_line_magic('store', '-d var')
884 884 ip.user_ns['var'] = 39
885 885 ip.run_line_magic('store' , '-r')
886 886 nt.assert_equal(ip.user_ns['var'], 39)
887 887
888 888
889 889 def _run_edit_test(arg_s, exp_filename=None,
890 890 exp_lineno=-1,
891 891 exp_contents=None,
892 892 exp_is_temp=None):
893 893 ip = get_ipython()
894 894 M = code.CodeMagics(ip)
895 895 last_call = ['','']
896 896 opts,args = M.parse_options(arg_s,'prxn:')
897 897 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
898 898
899 899 if exp_filename is not None:
900 900 nt.assert_equal(exp_filename, filename)
901 901 if exp_contents is not None:
902 902 with io.open(filename, 'r') as f:
903 903 contents = f.read()
904 904 nt.assert_equal(exp_contents, contents)
905 905 if exp_lineno != -1:
906 906 nt.assert_equal(exp_lineno, lineno)
907 907 if exp_is_temp is not None:
908 908 nt.assert_equal(exp_is_temp, is_temp)
909 909
910 910
911 911 def test_edit_interactive():
912 912 """%edit on interactively defined objects"""
913 913 ip = get_ipython()
914 914 n = ip.execution_count
915 915 ip.run_cell(u"def foo(): return 1", store_history=True)
916 916
917 917 try:
918 918 _run_edit_test("foo")
919 919 except code.InteractivelyDefined as e:
920 920 nt.assert_equal(e.index, n)
921 921 else:
922 922 raise AssertionError("Should have raised InteractivelyDefined")
923 923
924 924
925 925 def test_edit_cell():
926 926 """%edit [cell id]"""
927 927 ip = get_ipython()
928 928
929 929 ip.run_cell(u"def foo(): return 1", store_history=True)
930 930
931 931 # test
932 932 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
General Comments 0
You need to be logged in to leave comments. Login now