##// END OF EJS Templates
Merge pull request #2476 from minrk/editmagic...
Min RK -
r9024:c679a6cc merge
parent child Browse files
Show More
@@ -1,531 +1,554
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 json
19 19 import os
20 import re
20 21 import sys
21 22 from urllib2 import urlopen
22 23
23 24 # Our own packages
24 25 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
25 26 from IPython.core.macro import Macro
26 27 from IPython.core.magic import Magics, magics_class, line_magic
27 28 from IPython.core.oinspect import find_file, find_source_lines
28 29 from IPython.testing.skipdoctest import skip_doctest
29 30 from IPython.utils import openpy
30 31 from IPython.utils import py3compat
31 32 from IPython.utils.io import file_read
32 33 from IPython.utils.path import get_py_filename, unquote_filename
33 34 from IPython.utils.warn import warn
34 35
35 36 #-----------------------------------------------------------------------------
36 37 # Magic implementation classes
37 38 #-----------------------------------------------------------------------------
38 39
39 40 # Used for exception handling in magic_edit
40 41 class MacroToEdit(ValueError): pass
41 42
43 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
44
45 class InteractivelyDefined(Exception):
46 """Exception for interactively defined variable in magic_edit"""
47 def __init__(self, index):
48 self.index = index
49
42 50
43 51 @magics_class
44 52 class CodeMagics(Magics):
45 53 """Magics related to code management (loading, saving, editing, ...)."""
46 54
47 55 @line_magic
48 56 def save(self, parameter_s=''):
49 57 """Save a set of lines or a macro to a given filename.
50 58
51 59 Usage:\\
52 60 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
53 61
54 62 Options:
55 63
56 64 -r: use 'raw' input. By default, the 'processed' history is used,
57 65 so that magics are loaded in their transformed version to valid
58 66 Python. If this option is given, the raw input as typed as the
59 67 command line is used instead.
60 68
61 69 -f: force overwrite. If file exists, %save will prompt for overwrite
62 70 unless -f is given.
63 71
64 72 -a: append to the file instead of overwriting it.
65 73
66 74 This function uses the same syntax as %history for input ranges,
67 75 then saves the lines to the filename you specify.
68 76
69 77 It adds a '.py' extension to the file if you don't do so yourself, and
70 78 it asks for confirmation before overwriting existing files.
71 79
72 80 If `-r` option is used, the default extension is `.ipy`.
73 81 """
74 82
75 83 opts,args = self.parse_options(parameter_s,'fra',mode='list')
76 84 if not args:
77 85 raise UsageError('Missing filename.')
78 86 raw = 'r' in opts
79 87 force = 'f' in opts
80 88 append = 'a' in opts
81 89 mode = 'a' if append else 'w'
82 90 ext = u'.ipy' if raw else u'.py'
83 91 fname, codefrom = unquote_filename(args[0]), " ".join(args[1:])
84 92 if not fname.endswith((u'.py',u'.ipy')):
85 93 fname += ext
86 94 file_exists = os.path.isfile(fname)
87 95 if file_exists and not force and not append:
88 96 try:
89 97 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
90 98 except StdinNotImplementedError:
91 99 print "File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s)
92 100 return
93 101 if not overwrite :
94 102 print 'Operation cancelled.'
95 103 return
96 104 try:
97 105 cmds = self.shell.find_user_code(codefrom,raw)
98 106 except (TypeError, ValueError) as e:
99 107 print e.args[0]
100 108 return
101 109 out = py3compat.cast_unicode(cmds)
102 110 with io.open(fname, mode, encoding="utf-8") as f:
103 111 if not file_exists or not append:
104 112 f.write(u"# coding: utf-8\n")
105 113 f.write(out)
106 114 # make sure we end on a newline
107 115 if not out.endswith(u'\n'):
108 116 f.write(u'\n')
109 117 print 'The following commands were written to file `%s`:' % fname
110 118 print cmds
111 119
112 120 @line_magic
113 121 def pastebin(self, parameter_s=''):
114 122 """Upload code to Github's Gist paste bin, returning the URL.
115 123
116 124 Usage:\\
117 125 %pastebin [-d "Custom description"] 1-7
118 126
119 127 The argument can be an input history range, a filename, or the name of a
120 128 string or macro.
121 129
122 130 Options:
123 131
124 132 -d: Pass a custom description for the gist. The default will say
125 133 "Pasted from IPython".
126 134 """
127 135 opts, args = self.parse_options(parameter_s, 'd:')
128 136
129 137 try:
130 138 code = self.shell.find_user_code(args)
131 139 except (ValueError, TypeError) as e:
132 140 print e.args[0]
133 141 return
134 142
135 143 post_data = json.dumps({
136 144 "description": opts.get('d', "Pasted from IPython"),
137 145 "public": True,
138 146 "files": {
139 147 "file1.py": {
140 148 "content": code
141 149 }
142 150 }
143 151 }).encode('utf-8')
144 152
145 153 response = urlopen("https://api.github.com/gists", post_data)
146 154 response_data = json.loads(response.read().decode('utf-8'))
147 155 return response_data['html_url']
148 156
149 157 @line_magic
150 158 def loadpy(self, arg_s):
151 159 """Alias of `%load`
152 160
153 161 `%loadpy` has gained some flexibility and droped the requirement of a `.py`
154 162 extension. So it has been renamed simply into %load. You can look at
155 163 `%load`'s docstring for more info.
156 164 """
157 165 self.load(arg_s)
158 166
159 167 @line_magic
160 168 def load(self, arg_s):
161 169 """Load code into the current frontend.
162 170
163 171 Usage:\\
164 172 %load [options] source
165 173
166 174 where source can be a filename, URL, input history range or macro
167 175
168 176 Options:
169 177 --------
170 178 -y : Don't ask confirmation for loading source above 200 000 characters.
171 179
172 180 This magic command can either take a local filename, a URL, an history
173 181 range (see %history) or a macro as argument, it will prompt for
174 182 confirmation before loading source with more than 200 000 characters, unless
175 183 -y flag is passed or if the frontend does not support raw_input::
176 184
177 185 %load myscript.py
178 186 %load 7-27
179 187 %load myMacro
180 188 %load http://www.example.com/myscript.py
181 189 """
182 190 opts,args = self.parse_options(arg_s,'y')
183 191 if not args:
184 192 raise UsageError('Missing filename, URL, input history range, '
185 193 'or macro.')
186 194
187 195 contents = self.shell.find_user_code(args)
188 196 l = len(contents)
189 197
190 198 # 200 000 is ~ 2500 full 80 caracter lines
191 199 # so in average, more than 5000 lines
192 200 if l > 200000 and 'y' not in opts:
193 201 try:
194 202 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
195 203 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
196 204 except StdinNotImplementedError:
197 205 #asume yes if raw input not implemented
198 206 ans = True
199 207
200 208 if ans is False :
201 209 print 'Operation cancelled.'
202 210 return
203 211
204 212 self.shell.set_next_input(contents)
205 213
206 214 @staticmethod
207 215 def _find_edit_target(shell, args, opts, last_call):
208 216 """Utility method used by magic_edit to find what to edit."""
209 217
210 218 def make_filename(arg):
211 219 "Make a filename from the given args"
212 220 arg = unquote_filename(arg)
213 221 try:
214 222 filename = get_py_filename(arg)
215 223 except IOError:
216 224 # If it ends with .py but doesn't already exist, assume we want
217 225 # a new file.
218 226 if arg.endswith('.py'):
219 227 filename = arg
220 228 else:
221 229 filename = None
222 230 return filename
223 231
224 232 # Set a few locals from the options for convenience:
225 233 opts_prev = 'p' in opts
226 234 opts_raw = 'r' in opts
227 235
228 236 # custom exceptions
229 237 class DataIsObject(Exception): pass
230 238
231 239 # Default line number value
232 240 lineno = opts.get('n',None)
233 241
234 242 if opts_prev:
235 243 args = '_%s' % last_call[0]
236 244 if args not in shell.user_ns:
237 245 args = last_call[1]
238 246
239 247 # use last_call to remember the state of the previous call, but don't
240 248 # let it be clobbered by successive '-p' calls.
241 249 try:
242 250 last_call[0] = shell.displayhook.prompt_count
243 251 if not opts_prev:
244 252 last_call[1] = args
245 253 except:
246 254 pass
247 255
248 256 # by default this is done with temp files, except when the given
249 257 # arg is a filename
250 258 use_temp = True
251 259
252 260 data = ''
253 261
254 262 # First, see if the arguments should be a filename.
255 263 filename = make_filename(args)
256 264 if filename:
257 265 use_temp = False
258 266 elif args:
259 267 # Mode where user specifies ranges of lines, like in %macro.
260 268 data = shell.extract_input_lines(args, opts_raw)
261 269 if not data:
262 270 try:
263 271 # Load the parameter given as a variable. If not a string,
264 272 # process it as an object instead (below)
265 273
266 274 #print '*** args',args,'type',type(args) # dbg
267 275 data = eval(args, shell.user_ns)
268 276 if not isinstance(data, basestring):
269 277 raise DataIsObject
270 278
271 279 except (NameError,SyntaxError):
272 280 # given argument is not a variable, try as a filename
273 281 filename = make_filename(args)
274 282 if filename is None:
275 283 warn("Argument given (%s) can't be found as a variable "
276 284 "or as a filename." % args)
277 return
285 return (None, None, None)
278 286 use_temp = False
279 287
280 288 except DataIsObject:
281 289 # macros have a special edit function
282 290 if isinstance(data, Macro):
283 291 raise MacroToEdit(data)
284 292
285 293 # For objects, try to edit the file where they are defined
286 294 filename = find_file(data)
287 295 if filename:
288 296 if 'fakemodule' in filename.lower() and \
289 297 inspect.isclass(data):
290 298 # class created by %edit? Try to find source
291 299 # by looking for method definitions instead, the
292 300 # __module__ in those classes is FakeModule.
293 301 attrs = [getattr(data, aname) for aname in dir(data)]
294 302 for attr in attrs:
295 303 if not inspect.ismethod(attr):
296 304 continue
297 305 filename = find_file(attr)
298 306 if filename and \
299 307 'fakemodule' not in filename.lower():
300 308 # change the attribute to be the edit
301 309 # target instead
302 310 data = attr
303 311 break
304
312
313 m = ipython_input_pat.match(os.path.basename(filename))
314 if m:
315 raise InteractivelyDefined(int(m.groups()[0]))
316
305 317 datafile = 1
306 318 if filename is None:
307 319 filename = make_filename(args)
308 320 datafile = 1
309 warn('Could not find file where `%s` is defined.\n'
321 if filename is not None:
322 # only warn about this if we get a real name
323 warn('Could not find file where `%s` is defined.\n'
310 324 'Opening a file named `%s`' % (args, filename))
311 325 # Now, make sure we can actually read the source (if it was
312 326 # in a temp file it's gone by now).
313 327 if datafile:
314 328 if lineno is None:
315 329 lineno = find_source_lines(data)
316 330 if lineno is None:
317 331 filename = make_filename(args)
318 332 if filename is None:
319 warn('The file `%s` where `%s` was defined '
320 'cannot be read.' % (filename, data))
321 return
333 warn('The file where `%s` was defined '
334 'cannot be read or found.' % data)
335 return (None, None, None)
322 336 use_temp = False
323 337
324 338 if use_temp:
325 339 filename = shell.mktempfile(data)
326 340 print 'IPython will make a temporary file named:',filename
327 341
328 342 return filename, lineno, use_temp
329 343
330 344 def _edit_macro(self,mname,macro):
331 345 """open an editor with the macro data in a file"""
332 346 filename = self.shell.mktempfile(macro.value)
333 347 self.shell.hooks.editor(filename)
334 348
335 349 # and make a new macro object, to replace the old one
336 350 mfile = open(filename)
337 351 mvalue = mfile.read()
338 352 mfile.close()
339 353 self.shell.user_ns[mname] = Macro(mvalue)
340 354
341 355 @skip_doctest
342 356 @line_magic
343 357 def edit(self, parameter_s='',last_call=['','']):
344 358 """Bring up an editor and execute the resulting code.
345 359
346 360 Usage:
347 361 %edit [options] [args]
348 362
349 363 %edit runs IPython's editor hook. The default version of this hook is
350 364 set to call the editor specified by your $EDITOR environment variable.
351 365 If this isn't found, it will default to vi under Linux/Unix and to
352 366 notepad under Windows. See the end of this docstring for how to change
353 367 the editor hook.
354 368
355 369 You can also set the value of this editor via the
356 370 ``TerminalInteractiveShell.editor`` option in your configuration file.
357 371 This is useful if you wish to use a different editor from your typical
358 372 default with IPython (and for Windows users who typically don't set
359 373 environment variables).
360 374
361 375 This command allows you to conveniently edit multi-line code right in
362 376 your IPython session.
363 377
364 378 If called without arguments, %edit opens up an empty editor with a
365 379 temporary file and will execute the contents of this file when you
366 380 close it (don't forget to save it!).
367 381
368 382
369 383 Options:
370 384
371 385 -n <number>: open the editor at a specified line number. By default,
372 386 the IPython editor hook uses the unix syntax 'editor +N filename', but
373 387 you can configure this by providing your own modified hook if your
374 388 favorite editor supports line-number specifications with a different
375 389 syntax.
376 390
377 391 -p: this will call the editor with the same data as the previous time
378 392 it was used, regardless of how long ago (in your current session) it
379 393 was.
380 394
381 395 -r: use 'raw' input. This option only applies to input taken from the
382 396 user's history. By default, the 'processed' history is used, so that
383 397 magics are loaded in their transformed version to valid Python. If
384 398 this option is given, the raw input as typed as the command line is
385 399 used instead. When you exit the editor, it will be executed by
386 400 IPython's own processor.
387 401
388 402 -x: do not execute the edited code immediately upon exit. This is
389 403 mainly useful if you are editing programs which need to be called with
390 404 command line arguments, which you can then do using %run.
391 405
392 406
393 407 Arguments:
394 408
395 409 If arguments are given, the following possibilities exist:
396 410
397 411 - If the argument is a filename, IPython will load that into the
398 412 editor. It will execute its contents with execfile() when you exit,
399 413 loading any code in the file into your interactive namespace.
400 414
401 415 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
402 416 The syntax is the same as in the %history magic.
403 417
404 418 - If the argument is a string variable, its contents are loaded
405 419 into the editor. You can thus edit any string which contains
406 420 python code (including the result of previous edits).
407 421
408 422 - If the argument is the name of an object (other than a string),
409 423 IPython will try to locate the file where it was defined and open the
410 424 editor at the point where it is defined. You can use `%edit function`
411 425 to load an editor exactly at the point where 'function' is defined,
412 426 edit it and have the file be executed automatically.
413 427
414 428 - If the object is a macro (see %macro for details), this opens up your
415 429 specified editor with a temporary file containing the macro's data.
416 430 Upon exit, the macro is reloaded with the contents of the file.
417 431
418 432 Note: opening at an exact line is only supported under Unix, and some
419 433 editors (like kedit and gedit up to Gnome 2.8) do not understand the
420 434 '+NUMBER' parameter necessary for this feature. Good editors like
421 435 (X)Emacs, vi, jed, pico and joe all do.
422 436
423 437 After executing your code, %edit will return as output the code you
424 438 typed in the editor (except when it was an existing file). This way
425 439 you can reload the code in further invocations of %edit as a variable,
426 440 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
427 441 the output.
428 442
429 443 Note that %edit is also available through the alias %ed.
430 444
431 445 This is an example of creating a simple function inside the editor and
432 446 then modifying it. First, start up the editor::
433 447
434 448 In [1]: edit
435 449 Editing... done. Executing edited code...
436 450 Out[1]: 'def foo():\\n print "foo() was defined in an editing
437 451 session"\\n'
438 452
439 453 We can then call the function foo()::
440 454
441 455 In [2]: foo()
442 456 foo() was defined in an editing session
443 457
444 458 Now we edit foo. IPython automatically loads the editor with the
445 459 (temporary) file where foo() was previously defined::
446 460
447 461 In [3]: edit foo
448 462 Editing... done. Executing edited code...
449 463
450 464 And if we call foo() again we get the modified version::
451 465
452 466 In [4]: foo()
453 467 foo() has now been changed!
454 468
455 469 Here is an example of how to edit a code snippet successive
456 470 times. First we call the editor::
457 471
458 472 In [5]: edit
459 473 Editing... done. Executing edited code...
460 474 hello
461 475 Out[5]: "print 'hello'\\n"
462 476
463 477 Now we call it again with the previous output (stored in _)::
464 478
465 479 In [6]: edit _
466 480 Editing... done. Executing edited code...
467 481 hello world
468 482 Out[6]: "print 'hello world'\\n"
469 483
470 484 Now we call it with the output #8 (stored in _8, also as Out[8])::
471 485
472 486 In [7]: edit _8
473 487 Editing... done. Executing edited code...
474 488 hello again
475 489 Out[7]: "print 'hello again'\\n"
476 490
477 491
478 492 Changing the default editor hook:
479 493
480 494 If you wish to write your own editor hook, you can put it in a
481 495 configuration file which you load at startup time. The default hook
482 496 is defined in the IPython.core.hooks module, and you can use that as a
483 497 starting example for further modifications. That file also has
484 498 general instructions on how to set a new hook for use once you've
485 499 defined it."""
486 500 opts,args = self.parse_options(parameter_s,'prxn:')
487 501
488 502 try:
489 503 filename, lineno, is_temp = self._find_edit_target(self.shell,
490 504 args, opts, last_call)
491 505 except MacroToEdit as e:
492 506 self._edit_macro(args, e.args[0])
493 507 return
508 except InteractivelyDefined as e:
509 print "Editing In[%i]" % e.index
510 args = str(e.index)
511 filename, lineno, is_temp = self._find_edit_target(self.shell,
512 args, opts, last_call)
513 if filename is None:
514 # nothing was found, warnings have already been issued,
515 # just give up.
516 return
494 517
495 518 # do actual editing here
496 519 print 'Editing...',
497 520 sys.stdout.flush()
498 521 try:
499 522 # Quote filenames that may have spaces in them
500 523 if ' ' in filename:
501 524 filename = "'%s'" % filename
502 525 self.shell.hooks.editor(filename,lineno)
503 526 except TryNext:
504 527 warn('Could not open editor')
505 528 return
506 529
507 530 # XXX TODO: should this be generalized for all string vars?
508 531 # For now, this is special-cased to blocks created by cpaste
509 532 if args.strip() == 'pasted_block':
510 533 self.shell.user_ns['pasted_block'] = file_read(filename)
511 534
512 535 if 'x' in opts: # -x prevents actual execution
513 536 print
514 537 else:
515 538 print 'done. Executing edited code...'
516 539 if 'r' in opts: # Untranslated IPython code
517 540 self.shell.run_cell(file_read(filename),
518 541 store_history=False)
519 542 else:
520 543 self.shell.safe_execfile(filename, self.shell.user_ns,
521 544 self.shell.user_ns)
522 545
523 546 if is_temp:
524 547 try:
525 548 return open(filename).read()
526 549 except IOError as msg:
527 550 if msg.filename == filename:
528 551 warn('File not found. Did you forget to save?')
529 552 return
530 553 else:
531 554 self.shell.showtraceback()
@@ -1,873 +1,875
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for inspecting Python objects.
3 3
4 4 Uses syntax highlighting for presenting the various information elements.
5 5
6 6 Similar in spirit to the inspect module, but all calls take a name argument to
7 7 reference the name under which an object is being read.
8 8 """
9 9
10 10 #*****************************************************************************
11 11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #*****************************************************************************
16 16 from __future__ import print_function
17 17
18 18 __all__ = ['Inspector','InspectColors']
19 19
20 20 # stdlib modules
21 21 import __builtin__
22 22 import inspect
23 23 import linecache
24 24 import os
25 25 import sys
26 26 import types
27 27 import io as stdlib_io
28 28
29 29 from collections import namedtuple
30 30 try:
31 31 from itertools import izip_longest
32 32 except ImportError:
33 33 from itertools import zip_longest as izip_longest
34 34
35 35 # IPython's own
36 36 from IPython.core import page
37 37 from IPython.testing.skipdoctest import skip_doctest_py3
38 38 from IPython.utils import PyColorize
39 39 from IPython.utils import io
40 40 from IPython.utils import openpy
41 41 from IPython.utils import py3compat
42 42 from IPython.utils.text import indent
43 43 from IPython.utils.wildcard import list_namespace
44 44 from IPython.utils.coloransi import *
45 45 from IPython.utils.py3compat import cast_unicode
46 46
47 47 #****************************************************************************
48 48 # Builtin color schemes
49 49
50 50 Colors = TermColors # just a shorthand
51 51
52 52 # Build a few color schemes
53 53 NoColor = ColorScheme(
54 54 'NoColor',{
55 55 'header' : Colors.NoColor,
56 56 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
57 57 } )
58 58
59 59 LinuxColors = ColorScheme(
60 60 'Linux',{
61 61 'header' : Colors.LightRed,
62 62 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 63 } )
64 64
65 65 LightBGColors = ColorScheme(
66 66 'LightBG',{
67 67 'header' : Colors.Red,
68 68 'normal' : Colors.Normal # color off (usu. Colors.Normal)
69 69 } )
70 70
71 71 # Build table of color schemes (needed by the parser)
72 72 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
73 73 'Linux')
74 74
75 75 #****************************************************************************
76 76 # Auxiliary functions and objects
77 77
78 78 # See the messaging spec for the definition of all these fields. This list
79 79 # effectively defines the order of display
80 80 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
81 81 'length', 'file', 'definition', 'docstring', 'source',
82 82 'init_definition', 'class_docstring', 'init_docstring',
83 83 'call_def', 'call_docstring',
84 84 # These won't be printed but will be used to determine how to
85 85 # format the object
86 86 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
87 87 ]
88 88
89 89
90 90 def object_info(**kw):
91 91 """Make an object info dict with all fields present."""
92 92 infodict = dict(izip_longest(info_fields, [None]))
93 93 infodict.update(kw)
94 94 return infodict
95 95
96 96
97 97 def get_encoding(obj):
98 98 """Get encoding for python source file defining obj
99 99
100 100 Returns None if obj is not defined in a sourcefile.
101 101 """
102 102 ofile = find_file(obj)
103 103 # run contents of file through pager starting at line where the object
104 104 # is defined, as long as the file isn't binary and is actually on the
105 105 # filesystem.
106 106 if ofile is None:
107 107 return None
108 108 elif ofile.endswith(('.so', '.dll', '.pyd')):
109 109 return None
110 110 elif not os.path.isfile(ofile):
111 111 return None
112 112 else:
113 113 # Print only text files, not extension binaries. Note that
114 114 # getsourcelines returns lineno with 1-offset and page() uses
115 115 # 0-offset, so we must adjust.
116 116 buffer = stdlib_io.open(ofile, 'rb') # Tweaked to use io.open for Python 2
117 117 encoding, lines = openpy.detect_encoding(buffer.readline)
118 118 return encoding
119 119
120 120 def getdoc(obj):
121 121 """Stable wrapper around inspect.getdoc.
122 122
123 123 This can't crash because of attribute problems.
124 124
125 125 It also attempts to call a getdoc() method on the given object. This
126 126 allows objects which provide their docstrings via non-standard mechanisms
127 127 (like Pyro proxies) to still be inspected by ipython's ? system."""
128 128 # Allow objects to offer customized documentation via a getdoc method:
129 129 try:
130 130 ds = obj.getdoc()
131 131 except Exception:
132 132 pass
133 133 else:
134 134 # if we get extra info, we add it to the normal docstring.
135 135 if isinstance(ds, basestring):
136 136 return inspect.cleandoc(ds)
137 137
138 138 try:
139 139 docstr = inspect.getdoc(obj)
140 140 encoding = get_encoding(obj)
141 141 return py3compat.cast_unicode(docstr, encoding=encoding)
142 142 except Exception:
143 143 # Harden against an inspect failure, which can occur with
144 144 # SWIG-wrapped extensions.
145 145 raise
146 146 return None
147 147
148 148
149 149 def getsource(obj,is_binary=False):
150 150 """Wrapper around inspect.getsource.
151 151
152 152 This can be modified by other projects to provide customized source
153 153 extraction.
154 154
155 155 Inputs:
156 156
157 157 - obj: an object whose source code we will attempt to extract.
158 158
159 159 Optional inputs:
160 160
161 161 - is_binary: whether the object is known to come from a binary source.
162 162 This implementation will skip returning any output for binary objects, but
163 163 custom extractors may know how to meaningfully process them."""
164 164
165 165 if is_binary:
166 166 return None
167 167 else:
168 168 # get source if obj was decorated with @decorator
169 169 if hasattr(obj,"__wrapped__"):
170 170 obj = obj.__wrapped__
171 171 try:
172 172 src = inspect.getsource(obj)
173 173 except TypeError:
174 174 if hasattr(obj,'__class__'):
175 175 src = inspect.getsource(obj.__class__)
176 176 encoding = get_encoding(obj)
177 177 return cast_unicode(src, encoding=encoding)
178 178
179 179 def getargspec(obj):
180 180 """Get the names and default values of a function's arguments.
181 181
182 182 A tuple of four things is returned: (args, varargs, varkw, defaults).
183 183 'args' is a list of the argument names (it may contain nested lists).
184 184 'varargs' and 'varkw' are the names of the * and ** arguments or None.
185 185 'defaults' is an n-tuple of the default values of the last n arguments.
186 186
187 187 Modified version of inspect.getargspec from the Python Standard
188 188 Library."""
189 189
190 190 if inspect.isfunction(obj):
191 191 func_obj = obj
192 192 elif inspect.ismethod(obj):
193 193 func_obj = obj.im_func
194 194 elif hasattr(obj, '__call__'):
195 195 func_obj = obj.__call__
196 196 else:
197 197 raise TypeError('arg is not a Python function')
198 198 args, varargs, varkw = inspect.getargs(func_obj.func_code)
199 199 return args, varargs, varkw, func_obj.func_defaults
200 200
201 201
202 202 def format_argspec(argspec):
203 203 """Format argspect, convenience wrapper around inspect's.
204 204
205 205 This takes a dict instead of ordered arguments and calls
206 206 inspect.format_argspec with the arguments in the necessary order.
207 207 """
208 208 return inspect.formatargspec(argspec['args'], argspec['varargs'],
209 209 argspec['varkw'], argspec['defaults'])
210 210
211 211
212 212 def call_tip(oinfo, format_call=True):
213 213 """Extract call tip data from an oinfo dict.
214 214
215 215 Parameters
216 216 ----------
217 217 oinfo : dict
218 218
219 219 format_call : bool, optional
220 220 If True, the call line is formatted and returned as a string. If not, a
221 221 tuple of (name, argspec) is returned.
222 222
223 223 Returns
224 224 -------
225 225 call_info : None, str or (str, dict) tuple.
226 226 When format_call is True, the whole call information is formattted as a
227 227 single string. Otherwise, the object's name and its argspec dict are
228 228 returned. If no call information is available, None is returned.
229 229
230 230 docstring : str or None
231 231 The most relevant docstring for calling purposes is returned, if
232 232 available. The priority is: call docstring for callable instances, then
233 233 constructor docstring for classes, then main object's docstring otherwise
234 234 (regular functions).
235 235 """
236 236 # Get call definition
237 237 argspec = oinfo.get('argspec')
238 238 if argspec is None:
239 239 call_line = None
240 240 else:
241 241 # Callable objects will have 'self' as their first argument, prune
242 242 # it out if it's there for clarity (since users do *not* pass an
243 243 # extra first argument explicitly).
244 244 try:
245 245 has_self = argspec['args'][0] == 'self'
246 246 except (KeyError, IndexError):
247 247 pass
248 248 else:
249 249 if has_self:
250 250 argspec['args'] = argspec['args'][1:]
251 251
252 252 call_line = oinfo['name']+format_argspec(argspec)
253 253
254 254 # Now get docstring.
255 255 # The priority is: call docstring, constructor docstring, main one.
256 256 doc = oinfo.get('call_docstring')
257 257 if doc is None:
258 258 doc = oinfo.get('init_docstring')
259 259 if doc is None:
260 260 doc = oinfo.get('docstring','')
261 261
262 262 return call_line, doc
263 263
264 264
265 265 def find_file(obj):
266 266 """Find the absolute path to the file where an object was defined.
267 267
268 268 This is essentially a robust wrapper around `inspect.getabsfile`.
269 269
270 270 Returns None if no file can be found.
271 271
272 272 Parameters
273 273 ----------
274 274 obj : any Python object
275 275
276 276 Returns
277 277 -------
278 278 fname : str
279 279 The absolute path to the file where the object was defined.
280 280 """
281 281 # get source if obj was decorated with @decorator
282 282 if hasattr(obj, '__wrapped__'):
283 283 obj = obj.__wrapped__
284 284
285 285 fname = None
286 286 try:
287 287 fname = inspect.getabsfile(obj)
288 288 except TypeError:
289 289 # For an instance, the file that matters is where its class was
290 290 # declared.
291 291 if hasattr(obj, '__class__'):
292 292 try:
293 293 fname = inspect.getabsfile(obj.__class__)
294 294 except TypeError:
295 295 # Can happen for builtins
296 296 pass
297 297 except:
298 298 pass
299 return fname
299 return cast_unicode(fname)
300 300
301 301
302 302 def find_source_lines(obj):
303 303 """Find the line number in a file where an object was defined.
304 304
305 305 This is essentially a robust wrapper around `inspect.getsourcelines`.
306 306
307 307 Returns None if no file can be found.
308 308
309 309 Parameters
310 310 ----------
311 311 obj : any Python object
312 312
313 313 Returns
314 314 -------
315 315 lineno : int
316 316 The line number where the object definition starts.
317 317 """
318 318 # get source if obj was decorated with @decorator
319 319 if hasattr(obj, '__wrapped__'):
320 320 obj = obj.__wrapped__
321 321
322 322 try:
323 323 try:
324 324 lineno = inspect.getsourcelines(obj)[1]
325 325 except TypeError:
326 326 # For instances, try the class object like getsource() does
327 327 if hasattr(obj, '__class__'):
328 328 lineno = inspect.getsourcelines(obj.__class__)[1]
329 else:
330 lineno = None
329 331 except:
330 332 return None
331 333
332 334 return lineno
333 335
334 336
335 337 class Inspector:
336 338 def __init__(self, color_table=InspectColors,
337 339 code_color_table=PyColorize.ANSICodeColors,
338 340 scheme='NoColor',
339 341 str_detail_level=0):
340 342 self.color_table = color_table
341 343 self.parser = PyColorize.Parser(code_color_table,out='str')
342 344 self.format = self.parser.format
343 345 self.str_detail_level = str_detail_level
344 346 self.set_active_scheme(scheme)
345 347
346 348 def _getdef(self,obj,oname=''):
347 349 """Return the call signature for any callable object.
348 350
349 351 If any exception is generated, None is returned instead and the
350 352 exception is suppressed."""
351 353
352 354 try:
353 355 hdef = oname + inspect.formatargspec(*getargspec(obj))
354 356 return cast_unicode(hdef)
355 357 except:
356 358 return None
357 359
358 360 def __head(self,h):
359 361 """Return a header string with proper colors."""
360 362 return '%s%s%s' % (self.color_table.active_colors.header,h,
361 363 self.color_table.active_colors.normal)
362 364
363 365 def set_active_scheme(self, scheme):
364 366 self.color_table.set_active_scheme(scheme)
365 367 self.parser.color_table.set_active_scheme(scheme)
366 368
367 369 def noinfo(self, msg, oname):
368 370 """Generic message when no information is found."""
369 371 print('No %s found' % msg, end=' ')
370 372 if oname:
371 373 print('for %s' % oname)
372 374 else:
373 375 print()
374 376
375 377 def pdef(self, obj, oname=''):
376 378 """Print the call signature for any callable object.
377 379
378 380 If the object is a class, print the constructor information."""
379 381
380 382 if not callable(obj):
381 383 print('Object is not callable.')
382 384 return
383 385
384 386 header = ''
385 387
386 388 if inspect.isclass(obj):
387 389 header = self.__head('Class constructor information:\n')
388 390 obj = obj.__init__
389 391 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
390 392 obj = obj.__call__
391 393
392 394 output = self._getdef(obj,oname)
393 395 if output is None:
394 396 self.noinfo('definition header',oname)
395 397 else:
396 398 print(header,self.format(output), end=' ', file=io.stdout)
397 399
398 400 # In Python 3, all classes are new-style, so they all have __init__.
399 401 @skip_doctest_py3
400 402 def pdoc(self,obj,oname='',formatter = None):
401 403 """Print the docstring for any object.
402 404
403 405 Optional:
404 406 -formatter: a function to run the docstring through for specially
405 407 formatted docstrings.
406 408
407 409 Examples
408 410 --------
409 411
410 412 In [1]: class NoInit:
411 413 ...: pass
412 414
413 415 In [2]: class NoDoc:
414 416 ...: def __init__(self):
415 417 ...: pass
416 418
417 419 In [3]: %pdoc NoDoc
418 420 No documentation found for NoDoc
419 421
420 422 In [4]: %pdoc NoInit
421 423 No documentation found for NoInit
422 424
423 425 In [5]: obj = NoInit()
424 426
425 427 In [6]: %pdoc obj
426 428 No documentation found for obj
427 429
428 430 In [5]: obj2 = NoDoc()
429 431
430 432 In [6]: %pdoc obj2
431 433 No documentation found for obj2
432 434 """
433 435
434 436 head = self.__head # For convenience
435 437 lines = []
436 438 ds = getdoc(obj)
437 439 if formatter:
438 440 ds = formatter(ds)
439 441 if ds:
440 442 lines.append(head("Class Docstring:"))
441 443 lines.append(indent(ds))
442 444 if inspect.isclass(obj) and hasattr(obj, '__init__'):
443 445 init_ds = getdoc(obj.__init__)
444 446 if init_ds is not None:
445 447 lines.append(head("Constructor Docstring:"))
446 448 lines.append(indent(init_ds))
447 449 elif hasattr(obj,'__call__'):
448 450 call_ds = getdoc(obj.__call__)
449 451 if call_ds:
450 452 lines.append(head("Calling Docstring:"))
451 453 lines.append(indent(call_ds))
452 454
453 455 if not lines:
454 456 self.noinfo('documentation',oname)
455 457 else:
456 458 page.page('\n'.join(lines))
457 459
458 460 def psource(self,obj,oname=''):
459 461 """Print the source code for an object."""
460 462
461 463 # Flush the source cache because inspect can return out-of-date source
462 464 linecache.checkcache()
463 465 try:
464 466 src = getsource(obj)
465 467 except:
466 468 self.noinfo('source',oname)
467 469 else:
468 470 page.page(self.format(src))
469 471
470 472 def pfile(self, obj, oname=''):
471 473 """Show the whole file where an object was defined."""
472 474
473 475 lineno = find_source_lines(obj)
474 476 if lineno is None:
475 477 self.noinfo('file', oname)
476 478 return
477 479
478 480 ofile = find_file(obj)
479 481 # run contents of file through pager starting at line where the object
480 482 # is defined, as long as the file isn't binary and is actually on the
481 483 # filesystem.
482 484 if ofile.endswith(('.so', '.dll', '.pyd')):
483 485 print('File %r is binary, not printing.' % ofile)
484 486 elif not os.path.isfile(ofile):
485 487 print('File %r does not exist, not printing.' % ofile)
486 488 else:
487 489 # Print only text files, not extension binaries. Note that
488 490 # getsourcelines returns lineno with 1-offset and page() uses
489 491 # 0-offset, so we must adjust.
490 492 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
491 493
492 494 def _format_fields(self, fields, title_width=12):
493 495 """Formats a list of fields for display.
494 496
495 497 Parameters
496 498 ----------
497 499 fields : list
498 500 A list of 2-tuples: (field_title, field_content)
499 501 title_width : int
500 502 How many characters to pad titles to. Default 12.
501 503 """
502 504 out = []
503 505 header = self.__head
504 506 for title, content in fields:
505 507 if len(content.splitlines()) > 1:
506 508 title = header(title + ":") + "\n"
507 509 else:
508 510 title = header((title+":").ljust(title_width))
509 511 out.append(cast_unicode(title) + cast_unicode(content))
510 512 return "\n".join(out)
511 513
512 514 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
513 515 pinfo_fields1 = [("Type", "type_name"),
514 516 ]
515 517
516 518 pinfo_fields2 = [("String Form", "string_form"),
517 519 ]
518 520
519 521 pinfo_fields3 = [("Length", "length"),
520 522 ("File", "file"),
521 523 ("Definition", "definition"),
522 524 ]
523 525
524 526 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
525 527 ("Constructor Docstring","init_docstring"),
526 528 ("Call def", "call_def"),
527 529 ("Call docstring", "call_docstring")]
528 530
529 531 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
530 532 """Show detailed information about an object.
531 533
532 534 Optional arguments:
533 535
534 536 - oname: name of the variable pointing to the object.
535 537
536 538 - formatter: special formatter for docstrings (see pdoc)
537 539
538 540 - info: a structure with some information fields which may have been
539 541 precomputed already.
540 542
541 543 - detail_level: if set to 1, more information is given.
542 544 """
543 545 info = self.info(obj, oname=oname, formatter=formatter,
544 546 info=info, detail_level=detail_level)
545 547 displayfields = []
546 548 def add_fields(fields):
547 549 for title, key in fields:
548 550 field = info[key]
549 551 if field is not None:
550 552 displayfields.append((title, field.rstrip()))
551 553
552 554 add_fields(self.pinfo_fields1)
553 555
554 556 # Base class for old-style instances
555 557 if (not py3compat.PY3) and isinstance(obj, types.InstanceType) and info['base_class']:
556 558 displayfields.append(("Base Class", info['base_class'].rstrip()))
557 559
558 560 add_fields(self.pinfo_fields2)
559 561
560 562 # Namespace
561 563 if info['namespace'] != 'Interactive':
562 564 displayfields.append(("Namespace", info['namespace'].rstrip()))
563 565
564 566 add_fields(self.pinfo_fields3)
565 567
566 568 # Source or docstring, depending on detail level and whether
567 569 # source found.
568 570 if detail_level > 0 and info['source'] is not None:
569 571 displayfields.append(("Source",
570 572 self.format(cast_unicode(info['source']))))
571 573 elif info['docstring'] is not None:
572 574 displayfields.append(("Docstring", info["docstring"]))
573 575
574 576 # Constructor info for classes
575 577 if info['isclass']:
576 578 if info['init_definition'] or info['init_docstring']:
577 579 displayfields.append(("Constructor information", ""))
578 580 if info['init_definition'] is not None:
579 581 displayfields.append((" Definition",
580 582 info['init_definition'].rstrip()))
581 583 if info['init_docstring'] is not None:
582 584 displayfields.append((" Docstring",
583 585 indent(info['init_docstring'])))
584 586
585 587 # Info for objects:
586 588 else:
587 589 add_fields(self.pinfo_fields_obj)
588 590
589 591 # Finally send to printer/pager:
590 592 if displayfields:
591 593 page.page(self._format_fields(displayfields))
592 594
593 595 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
594 596 """Compute a dict with detailed information about an object.
595 597
596 598 Optional arguments:
597 599
598 600 - oname: name of the variable pointing to the object.
599 601
600 602 - formatter: special formatter for docstrings (see pdoc)
601 603
602 604 - info: a structure with some information fields which may have been
603 605 precomputed already.
604 606
605 607 - detail_level: if set to 1, more information is given.
606 608 """
607 609
608 610 obj_type = type(obj)
609 611
610 612 header = self.__head
611 613 if info is None:
612 614 ismagic = 0
613 615 isalias = 0
614 616 ospace = ''
615 617 else:
616 618 ismagic = info.ismagic
617 619 isalias = info.isalias
618 620 ospace = info.namespace
619 621
620 622 # Get docstring, special-casing aliases:
621 623 if isalias:
622 624 if not callable(obj):
623 625 try:
624 626 ds = "Alias to the system command:\n %s" % obj[1]
625 627 except:
626 628 ds = "Alias: " + str(obj)
627 629 else:
628 630 ds = "Alias to " + str(obj)
629 631 if obj.__doc__:
630 632 ds += "\nDocstring:\n" + obj.__doc__
631 633 else:
632 634 ds = getdoc(obj)
633 635 if ds is None:
634 636 ds = '<no docstring>'
635 637 if formatter is not None:
636 638 ds = formatter(ds)
637 639
638 640 # store output in a dict, we initialize it here and fill it as we go
639 641 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
640 642
641 643 string_max = 200 # max size of strings to show (snipped if longer)
642 644 shalf = int((string_max -5)/2)
643 645
644 646 if ismagic:
645 647 obj_type_name = 'Magic function'
646 648 elif isalias:
647 649 obj_type_name = 'System alias'
648 650 else:
649 651 obj_type_name = obj_type.__name__
650 652 out['type_name'] = obj_type_name
651 653
652 654 try:
653 655 bclass = obj.__class__
654 656 out['base_class'] = str(bclass)
655 657 except: pass
656 658
657 659 # String form, but snip if too long in ? form (full in ??)
658 660 if detail_level >= self.str_detail_level:
659 661 try:
660 662 ostr = str(obj)
661 663 str_head = 'string_form'
662 664 if not detail_level and len(ostr)>string_max:
663 665 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
664 666 ostr = ("\n" + " " * len(str_head.expandtabs())).\
665 667 join(q.strip() for q in ostr.split("\n"))
666 668 out[str_head] = ostr
667 669 except:
668 670 pass
669 671
670 672 if ospace:
671 673 out['namespace'] = ospace
672 674
673 675 # Length (for strings and lists)
674 676 try:
675 677 out['length'] = str(len(obj))
676 678 except: pass
677 679
678 680 # Filename where object was defined
679 681 binary_file = False
680 682 fname = find_file(obj)
681 683 if fname is None:
682 684 # if anything goes wrong, we don't want to show source, so it's as
683 685 # if the file was binary
684 686 binary_file = True
685 687 else:
686 688 if fname.endswith(('.so', '.dll', '.pyd')):
687 689 binary_file = True
688 690 elif fname.endswith('<string>'):
689 691 fname = 'Dynamically generated function. No source code available.'
690 692 out['file'] = fname
691 693
692 694 # reconstruct the function definition and print it:
693 695 defln = self._getdef(obj, oname)
694 696 if defln:
695 697 out['definition'] = self.format(defln)
696 698
697 699 # Docstrings only in detail 0 mode, since source contains them (we
698 700 # avoid repetitions). If source fails, we add them back, see below.
699 701 if ds and detail_level == 0:
700 702 out['docstring'] = ds
701 703
702 704 # Original source code for any callable
703 705 if detail_level:
704 706 # Flush the source cache because inspect can return out-of-date
705 707 # source
706 708 linecache.checkcache()
707 709 source = None
708 710 try:
709 711 try:
710 712 source = getsource(obj, binary_file)
711 713 except TypeError:
712 714 if hasattr(obj, '__class__'):
713 715 source = getsource(obj.__class__, binary_file)
714 716 if source is not None:
715 717 out['source'] = source.rstrip()
716 718 except Exception:
717 719 pass
718 720
719 721 if ds and source is None:
720 722 out['docstring'] = ds
721 723
722 724
723 725 # Constructor docstring for classes
724 726 if inspect.isclass(obj):
725 727 out['isclass'] = True
726 728 # reconstruct the function definition and print it:
727 729 try:
728 730 obj_init = obj.__init__
729 731 except AttributeError:
730 732 init_def = init_ds = None
731 733 else:
732 734 init_def = self._getdef(obj_init,oname)
733 735 init_ds = getdoc(obj_init)
734 736 # Skip Python's auto-generated docstrings
735 737 if init_ds and \
736 738 init_ds.startswith('x.__init__(...) initializes'):
737 739 init_ds = None
738 740
739 741 if init_def or init_ds:
740 742 if init_def:
741 743 out['init_definition'] = self.format(init_def)
742 744 if init_ds:
743 745 out['init_docstring'] = init_ds
744 746
745 747 # and class docstring for instances:
746 748 else:
747 749 # First, check whether the instance docstring is identical to the
748 750 # class one, and print it separately if they don't coincide. In
749 751 # most cases they will, but it's nice to print all the info for
750 752 # objects which use instance-customized docstrings.
751 753 if ds:
752 754 try:
753 755 cls = getattr(obj,'__class__')
754 756 except:
755 757 class_ds = None
756 758 else:
757 759 class_ds = getdoc(cls)
758 760 # Skip Python's auto-generated docstrings
759 761 if class_ds and \
760 762 (class_ds.startswith('function(code, globals[,') or \
761 763 class_ds.startswith('instancemethod(function, instance,') or \
762 764 class_ds.startswith('module(name[,') ):
763 765 class_ds = None
764 766 if class_ds and ds != class_ds:
765 767 out['class_docstring'] = class_ds
766 768
767 769 # Next, try to show constructor docstrings
768 770 try:
769 771 init_ds = getdoc(obj.__init__)
770 772 # Skip Python's auto-generated docstrings
771 773 if init_ds and \
772 774 init_ds.startswith('x.__init__(...) initializes'):
773 775 init_ds = None
774 776 except AttributeError:
775 777 init_ds = None
776 778 if init_ds:
777 779 out['init_docstring'] = init_ds
778 780
779 781 # Call form docstring for callable instances
780 782 if hasattr(obj, '__call__'):
781 783 call_def = self._getdef(obj.__call__, oname)
782 784 if call_def is not None:
783 785 out['call_def'] = self.format(call_def)
784 786 call_ds = getdoc(obj.__call__)
785 787 # Skip Python's auto-generated docstrings
786 788 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
787 789 call_ds = None
788 790 if call_ds:
789 791 out['call_docstring'] = call_ds
790 792
791 793 # Compute the object's argspec as a callable. The key is to decide
792 794 # whether to pull it from the object itself, from its __init__ or
793 795 # from its __call__ method.
794 796
795 797 if inspect.isclass(obj):
796 798 # Old-style classes need not have an __init__
797 799 callable_obj = getattr(obj, "__init__", None)
798 800 elif callable(obj):
799 801 callable_obj = obj
800 802 else:
801 803 callable_obj = None
802 804
803 805 if callable_obj:
804 806 try:
805 807 args, varargs, varkw, defaults = getargspec(callable_obj)
806 808 except (TypeError, AttributeError):
807 809 # For extensions/builtins we can't retrieve the argspec
808 810 pass
809 811 else:
810 812 out['argspec'] = dict(args=args, varargs=varargs,
811 813 varkw=varkw, defaults=defaults)
812 814
813 815 return object_info(**out)
814 816
815 817
816 818 def psearch(self,pattern,ns_table,ns_search=[],
817 819 ignore_case=False,show_all=False):
818 820 """Search namespaces with wildcards for objects.
819 821
820 822 Arguments:
821 823
822 824 - pattern: string containing shell-like wildcards to use in namespace
823 825 searches and optionally a type specification to narrow the search to
824 826 objects of that type.
825 827
826 828 - ns_table: dict of name->namespaces for search.
827 829
828 830 Optional arguments:
829 831
830 832 - ns_search: list of namespace names to include in search.
831 833
832 834 - ignore_case(False): make the search case-insensitive.
833 835
834 836 - show_all(False): show all names, including those starting with
835 837 underscores.
836 838 """
837 839 #print 'ps pattern:<%r>' % pattern # dbg
838 840
839 841 # defaults
840 842 type_pattern = 'all'
841 843 filter = ''
842 844
843 845 cmds = pattern.split()
844 846 len_cmds = len(cmds)
845 847 if len_cmds == 1:
846 848 # Only filter pattern given
847 849 filter = cmds[0]
848 850 elif len_cmds == 2:
849 851 # Both filter and type specified
850 852 filter,type_pattern = cmds
851 853 else:
852 854 raise ValueError('invalid argument string for psearch: <%s>' %
853 855 pattern)
854 856
855 857 # filter search namespaces
856 858 for name in ns_search:
857 859 if name not in ns_table:
858 860 raise ValueError('invalid namespace <%s>. Valid names: %s' %
859 861 (name,ns_table.keys()))
860 862
861 863 #print 'type_pattern:',type_pattern # dbg
862 864 search_result, namespaces_seen = set(), set()
863 865 for ns_name in ns_search:
864 866 ns = ns_table[ns_name]
865 867 # Normally, locals and globals are the same, so we just check one.
866 868 if id(ns) in namespaces_seen:
867 869 continue
868 870 namespaces_seen.add(id(ns))
869 871 tmp_res = list_namespace(ns, type_pattern, filter,
870 872 ignore_case=ignore_case, show_all=show_all)
871 873 search_result.update(tmp_res)
872 874
873 875 page.page('\n'.join(sorted(search_result)))
@@ -1,794 +1,840
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 from IPython.core.magics import execution, script
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.tempdir import TemporaryDirectory
38 38 from IPython.utils.process import find_cmd
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Test functions begin
42 42 #-----------------------------------------------------------------------------
43 43
44 44 @magic.magics_class
45 45 class DummyMagics(magic.Magics): pass
46 46
47 47 def test_rehashx():
48 48 # clear up everything
49 49 _ip = get_ipython()
50 50 _ip.alias_manager.alias_table.clear()
51 51 del _ip.db['syscmdlist']
52 52
53 53 _ip.magic('rehashx')
54 54 # Practically ALL ipython development systems will have more than 10 aliases
55 55
56 56 yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10)
57 57 for key, val in _ip.alias_manager.alias_table.iteritems():
58 58 # we must strip dots from alias names
59 59 nt.assert_true('.' not in key)
60 60
61 61 # rehashx must fill up syscmdlist
62 62 scoms = _ip.db['syscmdlist']
63 63 yield (nt.assert_true, len(scoms) > 10)
64 64
65 65
66 66 def test_magic_parse_options():
67 67 """Test that we don't mangle paths when parsing magic options."""
68 68 ip = get_ipython()
69 69 path = 'c:\\x'
70 70 m = DummyMagics(ip)
71 71 opts = m.parse_options('-f %s' % path,'f:')[0]
72 72 # argv splitting is os-dependent
73 73 if os.name == 'posix':
74 74 expected = 'c:x'
75 75 else:
76 76 expected = path
77 77 nt.assert_equal(opts['f'], expected)
78 78
79 79 def test_magic_parse_long_options():
80 80 """Magic.parse_options can handle --foo=bar long options"""
81 81 ip = get_ipython()
82 82 m = DummyMagics(ip)
83 83 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
84 84 nt.assert_true('foo' in opts)
85 85 nt.assert_true('bar' in opts)
86 86 nt.assert_true(opts['bar'], "bubble")
87 87
88 88
89 89 @dec.skip_without('sqlite3')
90 90 def doctest_hist_f():
91 91 """Test %hist -f with temporary filename.
92 92
93 93 In [9]: import tempfile
94 94
95 95 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
96 96
97 97 In [11]: %hist -nl -f $tfile 3
98 98
99 99 In [13]: import os; os.unlink(tfile)
100 100 """
101 101
102 102
103 103 @dec.skip_without('sqlite3')
104 104 def doctest_hist_r():
105 105 """Test %hist -r
106 106
107 107 XXX - This test is not recording the output correctly. For some reason, in
108 108 testing mode the raw history isn't getting populated. No idea why.
109 109 Disabling the output checking for now, though at least we do run it.
110 110
111 111 In [1]: 'hist' in _ip.lsmagic()
112 112 Out[1]: True
113 113
114 114 In [2]: x=1
115 115
116 116 In [3]: %hist -rl 2
117 117 x=1 # random
118 118 %hist -r 2
119 119 """
120 120
121 121
122 122 @dec.skip_without('sqlite3')
123 123 def doctest_hist_op():
124 124 """Test %hist -op
125 125
126 126 In [1]: class b(float):
127 127 ...: pass
128 128 ...:
129 129
130 130 In [2]: class s(object):
131 131 ...: def __str__(self):
132 132 ...: return 's'
133 133 ...:
134 134
135 135 In [3]:
136 136
137 137 In [4]: class r(b):
138 138 ...: def __repr__(self):
139 139 ...: return 'r'
140 140 ...:
141 141
142 142 In [5]: class sr(s,r): pass
143 143 ...:
144 144
145 145 In [6]:
146 146
147 147 In [7]: bb=b()
148 148
149 149 In [8]: ss=s()
150 150
151 151 In [9]: rr=r()
152 152
153 153 In [10]: ssrr=sr()
154 154
155 155 In [11]: 4.5
156 156 Out[11]: 4.5
157 157
158 158 In [12]: str(ss)
159 159 Out[12]: 's'
160 160
161 161 In [13]:
162 162
163 163 In [14]: %hist -op
164 164 >>> class b:
165 165 ... pass
166 166 ...
167 167 >>> class s(b):
168 168 ... def __str__(self):
169 169 ... return 's'
170 170 ...
171 171 >>>
172 172 >>> class r(b):
173 173 ... def __repr__(self):
174 174 ... return 'r'
175 175 ...
176 176 >>> class sr(s,r): pass
177 177 >>>
178 178 >>> bb=b()
179 179 >>> ss=s()
180 180 >>> rr=r()
181 181 >>> ssrr=sr()
182 182 >>> 4.5
183 183 4.5
184 184 >>> str(ss)
185 185 's'
186 186 >>>
187 187 """
188 188
189 189
190 190 @dec.skip_without('sqlite3')
191 191 def test_macro():
192 192 ip = get_ipython()
193 193 ip.history_manager.reset() # Clear any existing history.
194 194 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
195 195 for i, cmd in enumerate(cmds, start=1):
196 196 ip.history_manager.store_inputs(i, cmd)
197 197 ip.magic("macro test 1-3")
198 198 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
199 199
200 200 # List macros.
201 201 assert "test" in ip.magic("macro")
202 202
203 203
204 204 @dec.skip_without('sqlite3')
205 205 def test_macro_run():
206 206 """Test that we can run a multi-line macro successfully."""
207 207 ip = get_ipython()
208 208 ip.history_manager.reset()
209 209 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
210 210 "%macro test 2-3"]
211 211 for cmd in cmds:
212 212 ip.run_cell(cmd, store_history=True)
213 213 nt.assert_equal(ip.user_ns["test"].value,
214 214 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
215 215 with tt.AssertPrints("12"):
216 216 ip.run_cell("test")
217 217 with tt.AssertPrints("13"):
218 218 ip.run_cell("test")
219 219
220 220
221 221 @dec.skipif_not_numpy
222 222 def test_numpy_reset_array_undec():
223 223 "Test '%reset array' functionality"
224 224 _ip.ex('import numpy as np')
225 225 _ip.ex('a = np.empty(2)')
226 226 yield (nt.assert_true, 'a' in _ip.user_ns)
227 227 _ip.magic('reset -f array')
228 228 yield (nt.assert_false, 'a' in _ip.user_ns)
229 229
230 230 def test_reset_out():
231 231 "Test '%reset out' magic"
232 232 _ip.run_cell("parrot = 'dead'", store_history=True)
233 233 # test '%reset -f out', make an Out prompt
234 234 _ip.run_cell("parrot", store_history=True)
235 235 nt.assert_true('dead' in [_ip.user_ns[x] for x in '_','__','___'])
236 236 _ip.magic('reset -f out')
237 237 nt.assert_false('dead' in [_ip.user_ns[x] for x in '_','__','___'])
238 238 nt.assert_true(len(_ip.user_ns['Out']) == 0)
239 239
240 240 def test_reset_in():
241 241 "Test '%reset in' magic"
242 242 # test '%reset -f in'
243 243 _ip.run_cell("parrot", store_history=True)
244 244 nt.assert_true('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
245 245 _ip.magic('%reset -f in')
246 246 nt.assert_false('parrot' in [_ip.user_ns[x] for x in '_i','_ii','_iii'])
247 247 nt.assert_true(len(set(_ip.user_ns['In'])) == 1)
248 248
249 249 def test_reset_dhist():
250 250 "Test '%reset dhist' magic"
251 251 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
252 252 _ip.magic('cd ' + os.path.dirname(nt.__file__))
253 253 _ip.magic('cd -')
254 254 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
255 255 _ip.magic('reset -f dhist')
256 256 nt.assert_true(len(_ip.user_ns['_dh']) == 0)
257 257 _ip.run_cell("_dh = [d for d in tmp]") #restore
258 258
259 259 def test_reset_in_length():
260 260 "Test that '%reset in' preserves In[] length"
261 261 _ip.run_cell("print 'foo'")
262 262 _ip.run_cell("reset -f in")
263 263 nt.assert_true(len(_ip.user_ns['In']) == _ip.displayhook.prompt_count+1)
264 264
265 265 def test_time():
266 266 _ip.magic('time None')
267 267
268 268 def test_tb_syntaxerror():
269 269 """test %tb after a SyntaxError"""
270 270 ip = get_ipython()
271 271 ip.run_cell("for")
272 272
273 273 # trap and validate stdout
274 274 save_stdout = sys.stdout
275 275 try:
276 276 sys.stdout = StringIO()
277 277 ip.run_cell("%tb")
278 278 out = sys.stdout.getvalue()
279 279 finally:
280 280 sys.stdout = save_stdout
281 281 # trim output, and only check the last line
282 282 last_line = out.rstrip().splitlines()[-1].strip()
283 283 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
284 284
285 285
286 286 def test_time():
287 287 ip = get_ipython()
288 288
289 289 with tt.AssertPrints("CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s"):
290 290 ip.run_cell("%time None")
291 291
292 292 ip.run_cell("def f(kmjy):\n"
293 293 " %time print (2*kmjy)")
294 294
295 295 with tt.AssertPrints("CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s"):
296 296 with tt.AssertPrints("hihi", suppress=False):
297 297 ip.run_cell("f('hi')")
298 298
299 299 def test_doctest_mode():
300 300 "Toggle doctest_mode twice, it should be a no-op and run without error"
301 301 _ip.magic('doctest_mode')
302 302 _ip.magic('doctest_mode')
303 303
304 304
305 305 def test_parse_options():
306 306 """Tests for basic options parsing in magics."""
307 307 # These are only the most minimal of tests, more should be added later. At
308 308 # the very least we check that basic text/unicode calls work OK.
309 309 m = DummyMagics(_ip)
310 310 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
311 311 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
312 312
313 313
314 314 def test_dirops():
315 315 """Test various directory handling operations."""
316 316 # curpath = lambda :os.path.splitdrive(os.getcwdu())[1].replace('\\','/')
317 317 curpath = os.getcwdu
318 318 startdir = os.getcwdu()
319 319 ipdir = os.path.realpath(_ip.ipython_dir)
320 320 try:
321 321 _ip.magic('cd "%s"' % ipdir)
322 322 nt.assert_equal(curpath(), ipdir)
323 323 _ip.magic('cd -')
324 324 nt.assert_equal(curpath(), startdir)
325 325 _ip.magic('pushd "%s"' % ipdir)
326 326 nt.assert_equal(curpath(), ipdir)
327 327 _ip.magic('popd')
328 328 nt.assert_equal(curpath(), startdir)
329 329 finally:
330 330 os.chdir(startdir)
331 331
332 332
333 333 def test_xmode():
334 334 # Calling xmode three times should be a no-op
335 335 xmode = _ip.InteractiveTB.mode
336 336 for i in range(3):
337 337 _ip.magic("xmode")
338 338 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
339 339
340 340 def test_reset_hard():
341 341 monitor = []
342 342 class A(object):
343 343 def __del__(self):
344 344 monitor.append(1)
345 345 def __repr__(self):
346 346 return "<A instance>"
347 347
348 348 _ip.user_ns["a"] = A()
349 349 _ip.run_cell("a")
350 350
351 351 nt.assert_equal(monitor, [])
352 352 _ip.magic("reset -f")
353 353 nt.assert_equal(monitor, [1])
354 354
355 355 class TestXdel(tt.TempFileMixin):
356 356 def test_xdel(self):
357 357 """Test that references from %run are cleared by xdel."""
358 358 src = ("class A(object):\n"
359 359 " monitor = []\n"
360 360 " def __del__(self):\n"
361 361 " self.monitor.append(1)\n"
362 362 "a = A()\n")
363 363 self.mktmp(src)
364 364 # %run creates some hidden references...
365 365 _ip.magic("run %s" % self.fname)
366 366 # ... as does the displayhook.
367 367 _ip.run_cell("a")
368 368
369 369 monitor = _ip.user_ns["A"].monitor
370 370 nt.assert_equal(monitor, [])
371 371
372 372 _ip.magic("xdel a")
373 373
374 374 # Check that a's __del__ method has been called.
375 375 nt.assert_equal(monitor, [1])
376 376
377 377 def doctest_who():
378 378 """doctest for %who
379 379
380 380 In [1]: %reset -f
381 381
382 382 In [2]: alpha = 123
383 383
384 384 In [3]: beta = 'beta'
385 385
386 386 In [4]: %who int
387 387 alpha
388 388
389 389 In [5]: %who str
390 390 beta
391 391
392 392 In [6]: %whos
393 393 Variable Type Data/Info
394 394 ----------------------------
395 395 alpha int 123
396 396 beta str beta
397 397
398 398 In [7]: %who_ls
399 399 Out[7]: ['alpha', 'beta']
400 400 """
401 401
402 402 def test_whos():
403 403 """Check that whos is protected against objects where repr() fails."""
404 404 class A(object):
405 405 def __repr__(self):
406 406 raise Exception()
407 407 _ip.user_ns['a'] = A()
408 408 _ip.magic("whos")
409 409
410 410 @py3compat.u_format
411 411 def doctest_precision():
412 412 """doctest for %precision
413 413
414 414 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
415 415
416 416 In [2]: %precision 5
417 417 Out[2]: {u}'%.5f'
418 418
419 419 In [3]: f.float_format
420 420 Out[3]: {u}'%.5f'
421 421
422 422 In [4]: %precision %e
423 423 Out[4]: {u}'%e'
424 424
425 425 In [5]: f(3.1415927)
426 426 Out[5]: {u}'3.141593e+00'
427 427 """
428 428
429 429 def test_psearch():
430 430 with tt.AssertPrints("dict.fromkeys"):
431 431 _ip.run_cell("dict.fr*?")
432 432
433 433 def test_timeit_shlex():
434 434 """test shlex issues with timeit (#1109)"""
435 435 _ip.ex("def f(*a,**kw): pass")
436 436 _ip.magic('timeit -n1 "this is a bug".count(" ")')
437 437 _ip.magic('timeit -r1 -n1 f(" ", 1)')
438 438 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
439 439 _ip.magic('timeit -r1 -n1 ("a " + "b")')
440 440 _ip.magic('timeit -r1 -n1 f("a " + "b")')
441 441 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
442 442
443 443
444 444 def test_timeit_arguments():
445 445 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
446 446 _ip.magic("timeit ('#')")
447 447
448 448
449 449 def test_timeit_special_syntax():
450 450 "Test %%timeit with IPython special syntax"
451 451 from IPython.core.magic import register_line_magic
452 452
453 453 @register_line_magic
454 454 def lmagic(line):
455 455 ip = get_ipython()
456 456 ip.user_ns['lmagic_out'] = line
457 457
458 458 # line mode test
459 459 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
460 460 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
461 461 # cell mode test
462 462 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
463 463 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
464 464
465 465
466 466 @dec.skipif(execution.profile is None)
467 467 def test_prun_quotes():
468 468 "Test that prun does not clobber string escapes (GH #1302)"
469 469 _ip.magic(r"prun -q x = '\t'")
470 470 nt.assert_equal(_ip.user_ns['x'], '\t')
471 471
472 472 def test_extension():
473 473 tmpdir = TemporaryDirectory()
474 474 orig_ipython_dir = _ip.ipython_dir
475 475 try:
476 476 _ip.ipython_dir = tmpdir.name
477 477 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
478 478 url = os.path.join(os.path.dirname(__file__), "daft_extension.py")
479 479 _ip.magic("install_ext %s" % url)
480 480 _ip.user_ns.pop('arq', None)
481 481 invalidate_caches() # Clear import caches
482 482 _ip.magic("load_ext daft_extension")
483 483 nt.assert_equal(_ip.user_ns['arq'], 185)
484 484 _ip.magic("unload_ext daft_extension")
485 485 assert 'arq' not in _ip.user_ns
486 486 finally:
487 487 _ip.ipython_dir = orig_ipython_dir
488 488 tmpdir.cleanup()
489 489
490 490 def test_notebook_export_json():
491 491 with TemporaryDirectory() as td:
492 492 outfile = os.path.join(td, "nb.ipynb")
493 493 _ip.ex(py3compat.u_format(u"u = {u}'hΓ©llo'"))
494 494 _ip.magic("notebook -e %s" % outfile)
495 495
496 496 def test_notebook_export_py():
497 497 with TemporaryDirectory() as td:
498 498 outfile = os.path.join(td, "nb.py")
499 499 _ip.ex(py3compat.u_format(u"u = {u}'hΓ©llo'"))
500 500 _ip.magic("notebook -e %s" % outfile)
501 501
502 502 def test_notebook_reformat_py():
503 503 with TemporaryDirectory() as td:
504 504 infile = os.path.join(td, "nb.ipynb")
505 505 with io.open(infile, 'w', encoding='utf-8') as f:
506 506 current.write(nb0, f, 'json')
507 507
508 508 _ip.ex(py3compat.u_format(u"u = {u}'hΓ©llo'"))
509 509 _ip.magic("notebook -f py %s" % infile)
510 510
511 511 def test_notebook_reformat_json():
512 512 with TemporaryDirectory() as td:
513 513 infile = os.path.join(td, "nb.py")
514 514 with io.open(infile, 'w', encoding='utf-8') as f:
515 515 current.write(nb0, f, 'py')
516 516
517 517 _ip.ex(py3compat.u_format(u"u = {u}'hΓ©llo'"))
518 518 _ip.magic("notebook -f ipynb %s" % infile)
519 519 _ip.magic("notebook -f json %s" % infile)
520 520
521 521 def test_env():
522 522 env = _ip.magic("env")
523 523 assert isinstance(env, dict), type(env)
524 524
525 525
526 526 class CellMagicTestCase(TestCase):
527 527
528 528 def check_ident(self, magic):
529 529 # Manually called, we get the result
530 530 out = _ip.run_cell_magic(magic, 'a', 'b')
531 531 nt.assert_equal(out, ('a','b'))
532 532 # Via run_cell, it goes into the user's namespace via displayhook
533 533 _ip.run_cell('%%' + magic +' c\nd')
534 534 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
535 535
536 536 def test_cell_magic_func_deco(self):
537 537 "Cell magic using simple decorator"
538 538 @register_cell_magic
539 539 def cellm(line, cell):
540 540 return line, cell
541 541
542 542 self.check_ident('cellm')
543 543
544 544 def test_cell_magic_reg(self):
545 545 "Cell magic manually registered"
546 546 def cellm(line, cell):
547 547 return line, cell
548 548
549 549 _ip.register_magic_function(cellm, 'cell', 'cellm2')
550 550 self.check_ident('cellm2')
551 551
552 552 def test_cell_magic_class(self):
553 553 "Cell magics declared via a class"
554 554 @magics_class
555 555 class MyMagics(Magics):
556 556
557 557 @cell_magic
558 558 def cellm3(self, line, cell):
559 559 return line, cell
560 560
561 561 _ip.register_magics(MyMagics)
562 562 self.check_ident('cellm3')
563 563
564 564 def test_cell_magic_class2(self):
565 565 "Cell magics declared via a class, #2"
566 566 @magics_class
567 567 class MyMagics2(Magics):
568 568
569 569 @cell_magic('cellm4')
570 570 def cellm33(self, line, cell):
571 571 return line, cell
572 572
573 573 _ip.register_magics(MyMagics2)
574 574 self.check_ident('cellm4')
575 575 # Check that nothing is registered as 'cellm33'
576 576 c33 = _ip.find_cell_magic('cellm33')
577 577 nt.assert_equal(c33, None)
578 578
579 579 def test_file():
580 580 """Basic %%file"""
581 581 ip = get_ipython()
582 582 with TemporaryDirectory() as td:
583 583 fname = os.path.join(td, 'file1')
584 584 ip.run_cell_magic("file", fname, u'\n'.join([
585 585 'line1',
586 586 'line2',
587 587 ]))
588 588 with open(fname) as f:
589 589 s = f.read()
590 590 nt.assert_in('line1\n', s)
591 591 nt.assert_in('line2', s)
592 592
593 593 def test_file_var_expand():
594 594 """%%file $filename"""
595 595 ip = get_ipython()
596 596 with TemporaryDirectory() as td:
597 597 fname = os.path.join(td, 'file1')
598 598 ip.user_ns['filename'] = fname
599 599 ip.run_cell_magic("file", '$filename', u'\n'.join([
600 600 'line1',
601 601 'line2',
602 602 ]))
603 603 with open(fname) as f:
604 604 s = f.read()
605 605 nt.assert_in('line1\n', s)
606 606 nt.assert_in('line2', s)
607 607
608 608 def test_file_unicode():
609 609 """%%file with unicode cell"""
610 610 ip = get_ipython()
611 611 with TemporaryDirectory() as td:
612 612 fname = os.path.join(td, 'file1')
613 613 ip.run_cell_magic("file", fname, u'\n'.join([
614 614 u'linΓ©1',
615 615 u'linΓ©2',
616 616 ]))
617 617 with io.open(fname, encoding='utf-8') as f:
618 618 s = f.read()
619 619 nt.assert_in(u'linΓ©1\n', s)
620 620 nt.assert_in(u'linΓ©2', s)
621 621
622 622 def test_file_amend():
623 623 """%%file -a amends files"""
624 624 ip = get_ipython()
625 625 with TemporaryDirectory() as td:
626 626 fname = os.path.join(td, 'file2')
627 627 ip.run_cell_magic("file", fname, u'\n'.join([
628 628 'line1',
629 629 'line2',
630 630 ]))
631 631 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
632 632 'line3',
633 633 'line4',
634 634 ]))
635 635 with open(fname) as f:
636 636 s = f.read()
637 637 nt.assert_in('line1\n', s)
638 638 nt.assert_in('line3\n', s)
639 639
640 640
641 641 def test_script_config():
642 642 ip = get_ipython()
643 643 ip.config.ScriptMagics.script_magics = ['whoda']
644 644 sm = script.ScriptMagics(shell=ip)
645 645 nt.assert_in('whoda', sm.magics['cell'])
646 646
647 647 @dec.skip_win32
648 648 def test_script_out():
649 649 ip = get_ipython()
650 650 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
651 651 nt.assert_equal(ip.user_ns['output'], 'hi\n')
652 652
653 653 @dec.skip_win32
654 654 def test_script_err():
655 655 ip = get_ipython()
656 656 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
657 657 nt.assert_equal(ip.user_ns['error'], 'hello\n')
658 658
659 659 @dec.skip_win32
660 660 def test_script_out_err():
661 661 ip = get_ipython()
662 662 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
663 663 nt.assert_equal(ip.user_ns['output'], 'hi\n')
664 664 nt.assert_equal(ip.user_ns['error'], 'hello\n')
665 665
666 666 @dec.skip_win32
667 667 def test_script_bg_out():
668 668 ip = get_ipython()
669 669 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
670 670 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
671 671
672 672 @dec.skip_win32
673 673 def test_script_bg_err():
674 674 ip = get_ipython()
675 675 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
676 676 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
677 677
678 678 @dec.skip_win32
679 679 def test_script_bg_out_err():
680 680 ip = get_ipython()
681 681 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
682 682 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
683 683 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
684 684
685 685 def test_script_defaults():
686 686 ip = get_ipython()
687 687 for cmd in ['sh', 'bash', 'perl', 'ruby']:
688 688 try:
689 689 find_cmd(cmd)
690 690 except Exception:
691 691 pass
692 692 else:
693 693 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
694 694
695 695
696 696 @magics_class
697 697 class FooFoo(Magics):
698 698 """class with both %foo and %%foo magics"""
699 699 @line_magic('foo')
700 700 def line_foo(self, line):
701 701 "I am line foo"
702 702 pass
703 703
704 704 @cell_magic("foo")
705 705 def cell_foo(self, line, cell):
706 706 "I am cell foo, not line foo"
707 707 pass
708 708
709 709 def test_line_cell_info():
710 710 """%%foo and %foo magics are distinguishable to inspect"""
711 711 ip = get_ipython()
712 712 ip.magics_manager.register(FooFoo)
713 713 oinfo = ip.object_inspect('foo')
714 714 nt.assert_true(oinfo['found'])
715 715 nt.assert_true(oinfo['ismagic'])
716 716
717 717 oinfo = ip.object_inspect('%%foo')
718 718 nt.assert_true(oinfo['found'])
719 719 nt.assert_true(oinfo['ismagic'])
720 720 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
721 721
722 722 oinfo = ip.object_inspect('%foo')
723 723 nt.assert_true(oinfo['found'])
724 724 nt.assert_true(oinfo['ismagic'])
725 725 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
726 726
727 727 def test_multiple_magics():
728 728 ip = get_ipython()
729 729 foo1 = FooFoo(ip)
730 730 foo2 = FooFoo(ip)
731 731 mm = ip.magics_manager
732 732 mm.register(foo1)
733 733 nt.assert_true(mm.magics['line']['foo'].im_self is foo1)
734 734 mm.register(foo2)
735 735 nt.assert_true(mm.magics['line']['foo'].im_self is foo2)
736 736
737 737 def test_alias_magic():
738 738 """Test %alias_magic."""
739 739 ip = get_ipython()
740 740 mm = ip.magics_manager
741 741
742 742 # Basic operation: both cell and line magics are created, if possible.
743 743 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
744 744 nt.assert_true('timeit_alias' in mm.magics['line'])
745 745 nt.assert_true('timeit_alias' in mm.magics['cell'])
746 746
747 747 # --cell is specified, line magic not created.
748 748 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
749 749 nt.assert_false('timeit_cell_alias' in mm.magics['line'])
750 750 nt.assert_true('timeit_cell_alias' in mm.magics['cell'])
751 751
752 752 # Test that line alias is created successfully.
753 753 ip.run_line_magic('alias_magic', '--line env_alias env')
754 754 nt.assert_equal(ip.run_line_magic('env', ''),
755 755 ip.run_line_magic('env_alias', ''))
756 756
757 757 def test_save():
758 758 """Test %save."""
759 759 ip = get_ipython()
760 760 ip.history_manager.reset() # Clear any existing history.
761 761 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
762 762 for i, cmd in enumerate(cmds, start=1):
763 763 ip.history_manager.store_inputs(i, cmd)
764 764 with TemporaryDirectory() as tmpdir:
765 765 file = os.path.join(tmpdir, "testsave.py")
766 766 ip.run_line_magic("save", "%s 1-10" % file)
767 767 with open(file) as f:
768 768 content = f.read()
769 769 nt.assert_equal(content.count(cmds[0]), 1)
770 770 nt.assert_true('coding: utf-8' in content)
771 771 ip.run_line_magic("save", "-a %s 1-10" % file)
772 772 with open(file) as f:
773 773 content = f.read()
774 774 nt.assert_equal(content.count(cmds[0]), 2)
775 775 nt.assert_true('coding: utf-8' in content)
776 776
777 777
778 778 def test_store():
779 779 """Test %store."""
780 780 ip = get_ipython()
781 781 ip.run_line_magic('load_ext', 'storemagic')
782 782
783 783 # make sure the storage is empty
784 784 ip.run_line_magic('store', '-z')
785 785 ip.user_ns['var'] = 42
786 786 ip.run_line_magic('store', 'var')
787 787 ip.user_ns['var'] = 39
788 788 ip.run_line_magic('store', '-r')
789 789 nt.assert_equal(ip.user_ns['var'], 42)
790 790
791 791 ip.run_line_magic('store', '-d var')
792 792 ip.user_ns['var'] = 39
793 793 ip.run_line_magic('store' , '-r')
794 794 nt.assert_equal(ip.user_ns['var'], 39)
795
796
797 def _run_edit_test(arg_s, exp_filename=None,
798 exp_lineno=-1,
799 exp_contents=None,
800 exp_is_temp=None):
801 ip = get_ipython()
802 M = code.CodeMagics(ip)
803 last_call = ['','']
804 opts,args = M.parse_options(arg_s,'prxn:')
805 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
806
807 if exp_filename is not None:
808 nt.assert_equal(exp_filename, filename)
809 if exp_contents is not None:
810 with io.open(filename, 'r') as f:
811 contents = f.read()
812 nt.assert_equal(exp_contents, contents)
813 if exp_lineno != -1:
814 nt.assert_equal(exp_lineno, lineno)
815 if exp_is_temp is not None:
816 nt.assert_equal(exp_is_temp, is_temp)
817
818
819 def test_edit_interactive():
820 """%edit on interactively defined objects"""
821 ip = get_ipython()
822 n = ip.execution_count
823 ip.run_cell(u"def foo(): return 1", store_history=True)
824
825 try:
826 _run_edit_test("foo")
827 except code.InteractivelyDefined as e:
828 nt.assert_equal(e.index, n)
829 else:
830 nt.fail("Should have raised InteractivelyDefined")
831
832
833 def test_edit_cell():
834 """%edit [cell id]"""
835 ip = get_ipython()
836
837 ip.run_cell(u"def foo(): return 1", store_history=True)
838
839 # test
840 _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