##// END OF EJS Templates
Merge pull request #12544 from rchiodo/rchiodo/add_pathlib_to_edit
Matthias Bussonnier -
r26012:fedd6a8e merge
parent child Browse files
Show More
@@ -1,730 +1,730 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 from urllib.request import urlopen
24 24 from urllib.parse import urlencode
25 from pathlib import Path
25 26
26 27 # Our own packages
27 28 from IPython.core.error import TryNext, StdinNotImplementedError, UsageError
28 29 from IPython.core.macro import Macro
29 30 from IPython.core.magic import Magics, magics_class, line_magic
30 31 from IPython.core.oinspect import find_file, find_source_lines
31 32 from IPython.testing.skipdoctest import skip_doctest
32 33 from IPython.utils.contexts import preserve_keys
33 34 from IPython.utils.path import get_py_filename
34 35 from warnings import warn
35 36 from logging import error
36 37 from IPython.utils.text import get_text_list
37 38
38 39 #-----------------------------------------------------------------------------
39 40 # Magic implementation classes
40 41 #-----------------------------------------------------------------------------
41 42
42 43 # Used for exception handling in magic_edit
43 44 class MacroToEdit(ValueError): pass
44 45
45 46 ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$")
46 47
47 48 # To match, e.g. 8-10 1:5 :10 3-
48 49 range_re = re.compile(r"""
49 50 (?P<start>\d+)?
50 51 ((?P<sep>[\-:])
51 52 (?P<end>\d+)?)?
52 53 $""", re.VERBOSE)
53 54
54 55
55 56 def extract_code_ranges(ranges_str):
56 57 """Turn a string of range for %%load into 2-tuples of (start, stop)
57 58 ready to use as a slice of the content split by lines.
58 59
59 60 Examples
60 61 --------
61 62 list(extract_input_ranges("5-10 2"))
62 63 [(4, 10), (1, 2)]
63 64 """
64 65 for range_str in ranges_str.split():
65 66 rmatch = range_re.match(range_str)
66 67 if not rmatch:
67 68 continue
68 69 sep = rmatch.group("sep")
69 70 start = rmatch.group("start")
70 71 end = rmatch.group("end")
71 72
72 73 if sep == '-':
73 74 start = int(start) - 1 if start else None
74 75 end = int(end) if end else None
75 76 elif sep == ':':
76 77 start = int(start) - 1 if start else None
77 78 end = int(end) - 1 if end else None
78 79 else:
79 80 end = int(start)
80 81 start = int(start) - 1
81 82 yield (start, end)
82 83
83 84
84 85 def extract_symbols(code, symbols):
85 86 """
86 87 Return a tuple (blocks, not_found)
87 88 where ``blocks`` is a list of code fragments
88 89 for each symbol parsed from code, and ``not_found`` are
89 90 symbols not found in the code.
90 91
91 92 For example::
92 93
93 94 In [1]: code = '''a = 10
94 95 ...: def b(): return 42
95 96 ...: class A: pass'''
96 97
97 98 In [2]: extract_symbols(code, 'A,b,z')
98 99 Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z'])
99 100 """
100 101 symbols = symbols.split(',')
101 102
102 103 # this will raise SyntaxError if code isn't valid Python
103 104 py_code = ast.parse(code)
104 105
105 106 marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body]
106 107 code = code.split('\n')
107 108
108 109 symbols_lines = {}
109 110
110 111 # we already know the start_lineno of each symbol (marks).
111 112 # To find each end_lineno, we traverse in reverse order until each
112 113 # non-blank line
113 114 end = len(code)
114 115 for name, start in reversed(marks):
115 116 while not code[end - 1].strip():
116 117 end -= 1
117 118 if name:
118 119 symbols_lines[name] = (start - 1, end)
119 120 end = start - 1
120 121
121 122 # Now symbols_lines is a map
122 123 # {'symbol_name': (start_lineno, end_lineno), ...}
123 124
124 125 # fill a list with chunks of codes for each requested symbol
125 126 blocks = []
126 127 not_found = []
127 128 for symbol in symbols:
128 129 if symbol in symbols_lines:
129 130 start, end = symbols_lines[symbol]
130 131 blocks.append('\n'.join(code[start:end]) + '\n')
131 132 else:
132 133 not_found.append(symbol)
133 134
134 135 return blocks, not_found
135 136
136 137 def strip_initial_indent(lines):
137 138 """For %load, strip indent from lines until finding an unindented line.
138 139
139 140 https://github.com/ipython/ipython/issues/9775
140 141 """
141 142 indent_re = re.compile(r'\s+')
142 143
143 144 it = iter(lines)
144 145 first_line = next(it)
145 146 indent_match = indent_re.match(first_line)
146 147
147 148 if indent_match:
148 149 # First line was indented
149 150 indent = indent_match.group()
150 151 yield first_line[len(indent):]
151 152
152 153 for line in it:
153 154 if line.startswith(indent):
154 155 yield line[len(indent):]
155 156 else:
156 157 # Less indented than the first line - stop dedenting
157 158 yield line
158 159 break
159 160 else:
160 161 yield first_line
161 162
162 163 # Pass the remaining lines through without dedenting
163 164 for line in it:
164 165 yield line
165 166
166 167
167 168 class InteractivelyDefined(Exception):
168 169 """Exception for interactively defined variable in magic_edit"""
169 170 def __init__(self, index):
170 171 self.index = index
171 172
172 173
173 174 @magics_class
174 175 class CodeMagics(Magics):
175 176 """Magics related to code management (loading, saving, editing, ...)."""
176 177
177 178 def __init__(self, *args, **kwargs):
178 179 self._knowntemps = set()
179 180 super(CodeMagics, self).__init__(*args, **kwargs)
180 181
181 182 @line_magic
182 183 def save(self, parameter_s=''):
183 184 """Save a set of lines or a macro to a given filename.
184 185
185 186 Usage:\\
186 187 %save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...
187 188
188 189 Options:
189 190
190 191 -r: use 'raw' input. By default, the 'processed' history is used,
191 192 so that magics are loaded in their transformed version to valid
192 193 Python. If this option is given, the raw input as typed as the
193 194 command line is used instead.
194 195
195 196 -f: force overwrite. If file exists, %save will prompt for overwrite
196 197 unless -f is given.
197 198
198 199 -a: append to the file instead of overwriting it.
199 200
200 201 This function uses the same syntax as %history for input ranges,
201 202 then saves the lines to the filename you specify.
202 203
203 204 It adds a '.py' extension to the file if you don't do so yourself, and
204 205 it asks for confirmation before overwriting existing files.
205 206
206 207 If `-r` option is used, the default extension is `.ipy`.
207 208 """
208 209
209 210 opts,args = self.parse_options(parameter_s,'fra',mode='list')
210 211 if not args:
211 212 raise UsageError('Missing filename.')
212 213 raw = 'r' in opts
213 214 force = 'f' in opts
214 215 append = 'a' in opts
215 216 mode = 'a' if append else 'w'
216 217 ext = '.ipy' if raw else '.py'
217 218 fname, codefrom = args[0], " ".join(args[1:])
218 219 if not fname.endswith(('.py','.ipy')):
219 220 fname += ext
220 221 file_exists = os.path.isfile(fname)
221 222 if file_exists and not force and not append:
222 223 try:
223 224 overwrite = self.shell.ask_yes_no('File `%s` exists. Overwrite (y/[N])? ' % fname, default='n')
224 225 except StdinNotImplementedError:
225 226 print("File `%s` exists. Use `%%save -f %s` to force overwrite" % (fname, parameter_s))
226 227 return
227 228 if not overwrite :
228 229 print('Operation cancelled.')
229 230 return
230 231 try:
231 232 cmds = self.shell.find_user_code(codefrom,raw)
232 233 except (TypeError, ValueError) as e:
233 234 print(e.args[0])
234 235 return
235 236 with io.open(fname, mode, encoding="utf-8") as f:
236 237 if not file_exists or not append:
237 238 f.write("# coding: utf-8\n")
238 239 f.write(cmds)
239 240 # make sure we end on a newline
240 241 if not cmds.endswith('\n'):
241 242 f.write('\n')
242 243 print('The following commands were written to file `%s`:' % fname)
243 244 print(cmds)
244 245
245 246 @line_magic
246 247 def pastebin(self, parameter_s=''):
247 248 """Upload code to dpaste's paste bin, returning the URL.
248 249
249 250 Usage:\\
250 251 %pastebin [-d "Custom description"] 1-7
251 252
252 253 The argument can be an input history range, a filename, or the name of a
253 254 string or macro.
254 255
255 256 Options:
256 257
257 258 -d: Pass a custom description for the gist. The default will say
258 259 "Pasted from IPython".
259 260 """
260 261 opts, args = self.parse_options(parameter_s, 'd:')
261 262
262 263 try:
263 264 code = self.shell.find_user_code(args)
264 265 except (ValueError, TypeError) as e:
265 266 print(e.args[0])
266 267 return
267 268
268 269 post_data = urlencode({
269 270 "title": opts.get('d', "Pasted from IPython"),
270 271 "syntax": "python3",
271 272 "content": code
272 273 }).encode('utf-8')
273 274
274 275 response = urlopen("http://dpaste.com/api/v2/", post_data)
275 276 return response.headers.get('Location')
276 277
277 278 @line_magic
278 279 def loadpy(self, arg_s):
279 280 """Alias of `%load`
280 281
281 282 `%loadpy` has gained some flexibility and dropped the requirement of a `.py`
282 283 extension. So it has been renamed simply into %load. You can look at
283 284 `%load`'s docstring for more info.
284 285 """
285 286 self.load(arg_s)
286 287
287 288 @line_magic
288 289 def load(self, arg_s):
289 290 """Load code into the current frontend.
290 291
291 292 Usage:\\
292 293 %load [options] source
293 294
294 295 where source can be a filename, URL, input history range, macro, or
295 296 element in the user namespace
296 297
297 298 Options:
298 299
299 300 -r <lines>: Specify lines or ranges of lines to load from the source.
300 301 Ranges could be specified as x-y (x..y) or in python-style x:y
301 302 (x..(y-1)). Both limits x and y can be left blank (meaning the
302 303 beginning and end of the file, respectively).
303 304
304 305 -s <symbols>: Specify function or classes to load from python source.
305 306
306 307 -y : Don't ask confirmation for loading source above 200 000 characters.
307 308
308 309 -n : Include the user's namespace when searching for source code.
309 310
310 311 This magic command can either take a local filename, a URL, an history
311 312 range (see %history) or a macro as argument, it will prompt for
312 313 confirmation before loading source with more than 200 000 characters, unless
313 314 -y flag is passed or if the frontend does not support raw_input::
314 315
315 316 %load myscript.py
316 317 %load 7-27
317 318 %load myMacro
318 319 %load http://www.example.com/myscript.py
319 320 %load -r 5-10 myscript.py
320 321 %load -r 10-20,30,40: foo.py
321 322 %load -s MyClass,wonder_function myscript.py
322 323 %load -n MyClass
323 324 %load -n my_module.wonder_function
324 325 """
325 326 opts,args = self.parse_options(arg_s,'yns:r:')
326 327
327 328 if not args:
328 329 raise UsageError('Missing filename, URL, input history range, '
329 330 'macro, or element in the user namespace.')
330 331
331 332 search_ns = 'n' in opts
332 333
333 334 contents = self.shell.find_user_code(args, search_ns=search_ns)
334 335
335 336 if 's' in opts:
336 337 try:
337 338 blocks, not_found = extract_symbols(contents, opts['s'])
338 339 except SyntaxError:
339 340 # non python code
340 341 error("Unable to parse the input as valid Python code")
341 342 return
342 343
343 344 if len(not_found) == 1:
344 345 warn('The symbol `%s` was not found' % not_found[0])
345 346 elif len(not_found) > 1:
346 347 warn('The symbols %s were not found' % get_text_list(not_found,
347 348 wrap_item_with='`')
348 349 )
349 350
350 351 contents = '\n'.join(blocks)
351 352
352 353 if 'r' in opts:
353 354 ranges = opts['r'].replace(',', ' ')
354 355 lines = contents.split('\n')
355 356 slices = extract_code_ranges(ranges)
356 357 contents = [lines[slice(*slc)] for slc in slices]
357 358 contents = '\n'.join(strip_initial_indent(chain.from_iterable(contents)))
358 359
359 360 l = len(contents)
360 361
361 362 # 200 000 is ~ 2500 full 80 character lines
362 363 # so in average, more than 5000 lines
363 364 if l > 200000 and 'y' not in opts:
364 365 try:
365 366 ans = self.shell.ask_yes_no(("The text you're trying to load seems pretty big"\
366 367 " (%d characters). Continue (y/[N]) ?" % l), default='n' )
367 368 except StdinNotImplementedError:
368 369 #assume yes if raw input not implemented
369 370 ans = True
370 371
371 372 if ans is False :
372 373 print('Operation cancelled.')
373 374 return
374 375
375 376 contents = "# %load {}\n".format(arg_s) + contents
376 377
377 378 self.shell.set_next_input(contents, replace=True)
378 379
379 380 @staticmethod
380 381 def _find_edit_target(shell, args, opts, last_call):
381 382 """Utility method used by magic_edit to find what to edit."""
382 383
383 384 def make_filename(arg):
384 385 "Make a filename from the given args"
385 386 try:
386 387 filename = get_py_filename(arg)
387 388 except IOError:
388 389 # If it ends with .py but doesn't already exist, assume we want
389 390 # a new file.
390 391 if arg.endswith('.py'):
391 392 filename = arg
392 393 else:
393 394 filename = None
394 395 return filename
395 396
396 397 # Set a few locals from the options for convenience:
397 398 opts_prev = 'p' in opts
398 399 opts_raw = 'r' in opts
399 400
400 401 # custom exceptions
401 402 class DataIsObject(Exception): pass
402 403
403 404 # Default line number value
404 405 lineno = opts.get('n',None)
405 406
406 407 if opts_prev:
407 408 args = '_%s' % last_call[0]
408 409 if args not in shell.user_ns:
409 410 args = last_call[1]
410 411
411 412 # by default this is done with temp files, except when the given
412 413 # arg is a filename
413 414 use_temp = True
414 415
415 416 data = ''
416 417
417 418 # First, see if the arguments should be a filename.
418 419 filename = make_filename(args)
419 420 if filename:
420 421 use_temp = False
421 422 elif args:
422 423 # Mode where user specifies ranges of lines, like in %macro.
423 424 data = shell.extract_input_lines(args, opts_raw)
424 425 if not data:
425 426 try:
426 427 # Load the parameter given as a variable. If not a string,
427 428 # process it as an object instead (below)
428 429
429 430 #print '*** args',args,'type',type(args) # dbg
430 431 data = eval(args, shell.user_ns)
431 432 if not isinstance(data, str):
432 433 raise DataIsObject
433 434
434 435 except (NameError,SyntaxError):
435 436 # given argument is not a variable, try as a filename
436 437 filename = make_filename(args)
437 438 if filename is None:
438 439 warn("Argument given (%s) can't be found as a variable "
439 440 "or as a filename." % args)
440 441 return (None, None, None)
441 442 use_temp = False
442 443
443 444 except DataIsObject as e:
444 445 # macros have a special edit function
445 446 if isinstance(data, Macro):
446 447 raise MacroToEdit(data) from e
447 448
448 449 # For objects, try to edit the file where they are defined
449 450 filename = find_file(data)
450 451 if filename:
451 452 if 'fakemodule' in filename.lower() and \
452 453 inspect.isclass(data):
453 454 # class created by %edit? Try to find source
454 455 # by looking for method definitions instead, the
455 456 # __module__ in those classes is FakeModule.
456 457 attrs = [getattr(data, aname) for aname in dir(data)]
457 458 for attr in attrs:
458 459 if not inspect.ismethod(attr):
459 460 continue
460 461 filename = find_file(attr)
461 462 if filename and \
462 463 'fakemodule' not in filename.lower():
463 464 # change the attribute to be the edit
464 465 # target instead
465 466 data = attr
466 467 break
467 468
468 469 m = ipython_input_pat.match(os.path.basename(filename))
469 470 if m:
470 471 raise InteractivelyDefined(int(m.groups()[0])) from e
471 472
472 473 datafile = 1
473 474 if filename is None:
474 475 filename = make_filename(args)
475 476 datafile = 1
476 477 if filename is not None:
477 478 # only warn about this if we get a real name
478 479 warn('Could not find file where `%s` is defined.\n'
479 480 'Opening a file named `%s`' % (args, filename))
480 481 # Now, make sure we can actually read the source (if it was
481 482 # in a temp file it's gone by now).
482 483 if datafile:
483 484 if lineno is None:
484 485 lineno = find_source_lines(data)
485 486 if lineno is None:
486 487 filename = make_filename(args)
487 488 if filename is None:
488 489 warn('The file where `%s` was defined '
489 490 'cannot be read or found.' % data)
490 491 return (None, None, None)
491 492 use_temp = False
492 493
493 494 if use_temp:
494 495 filename = shell.mktempfile(data)
495 496 print('IPython will make a temporary file named:',filename)
496 497
497 498 # use last_call to remember the state of the previous call, but don't
498 499 # let it be clobbered by successive '-p' calls.
499 500 try:
500 501 last_call[0] = shell.displayhook.prompt_count
501 502 if not opts_prev:
502 503 last_call[1] = args
503 504 except:
504 505 pass
505 506
506 507
507 508 return filename, lineno, use_temp
508 509
509 510 def _edit_macro(self,mname,macro):
510 511 """open an editor with the macro data in a file"""
511 512 filename = self.shell.mktempfile(macro.value)
512 513 self.shell.hooks.editor(filename)
513 514
514 515 # and make a new macro object, to replace the old one
515 with open(filename) as mfile:
516 mvalue = mfile.read()
516 mvalue = Path(filename).read_text()
517 517 self.shell.user_ns[mname] = Macro(mvalue)
518 518
519 519 @skip_doctest
520 520 @line_magic
521 521 def edit(self, parameter_s='',last_call=['','']):
522 522 """Bring up an editor and execute the resulting code.
523 523
524 524 Usage:
525 525 %edit [options] [args]
526 526
527 527 %edit runs IPython's editor hook. The default version of this hook is
528 528 set to call the editor specified by your $EDITOR environment variable.
529 529 If this isn't found, it will default to vi under Linux/Unix and to
530 530 notepad under Windows. See the end of this docstring for how to change
531 531 the editor hook.
532 532
533 533 You can also set the value of this editor via the
534 534 ``TerminalInteractiveShell.editor`` option in your configuration file.
535 535 This is useful if you wish to use a different editor from your typical
536 536 default with IPython (and for Windows users who typically don't set
537 537 environment variables).
538 538
539 539 This command allows you to conveniently edit multi-line code right in
540 540 your IPython session.
541 541
542 542 If called without arguments, %edit opens up an empty editor with a
543 543 temporary file and will execute the contents of this file when you
544 544 close it (don't forget to save it!).
545 545
546 546
547 547 Options:
548 548
549 549 -n <number>: open the editor at a specified line number. By default,
550 550 the IPython editor hook uses the unix syntax 'editor +N filename', but
551 551 you can configure this by providing your own modified hook if your
552 552 favorite editor supports line-number specifications with a different
553 553 syntax.
554 554
555 555 -p: this will call the editor with the same data as the previous time
556 556 it was used, regardless of how long ago (in your current session) it
557 557 was.
558 558
559 559 -r: use 'raw' input. This option only applies to input taken from the
560 560 user's history. By default, the 'processed' history is used, so that
561 561 magics are loaded in their transformed version to valid Python. If
562 562 this option is given, the raw input as typed as the command line is
563 563 used instead. When you exit the editor, it will be executed by
564 564 IPython's own processor.
565 565
566 566 -x: do not execute the edited code immediately upon exit. This is
567 567 mainly useful if you are editing programs which need to be called with
568 568 command line arguments, which you can then do using %run.
569 569
570 570
571 571 Arguments:
572 572
573 573 If arguments are given, the following possibilities exist:
574 574
575 575 - If the argument is a filename, IPython will load that into the
576 576 editor. It will execute its contents with execfile() when you exit,
577 577 loading any code in the file into your interactive namespace.
578 578
579 579 - The arguments are ranges of input history, e.g. "7 ~1/4-6".
580 580 The syntax is the same as in the %history magic.
581 581
582 582 - If the argument is a string variable, its contents are loaded
583 583 into the editor. You can thus edit any string which contains
584 584 python code (including the result of previous edits).
585 585
586 586 - If the argument is the name of an object (other than a string),
587 587 IPython will try to locate the file where it was defined and open the
588 588 editor at the point where it is defined. You can use `%edit function`
589 589 to load an editor exactly at the point where 'function' is defined,
590 590 edit it and have the file be executed automatically.
591 591
592 592 - If the object is a macro (see %macro for details), this opens up your
593 593 specified editor with a temporary file containing the macro's data.
594 594 Upon exit, the macro is reloaded with the contents of the file.
595 595
596 596 Note: opening at an exact line is only supported under Unix, and some
597 597 editors (like kedit and gedit up to Gnome 2.8) do not understand the
598 598 '+NUMBER' parameter necessary for this feature. Good editors like
599 599 (X)Emacs, vi, jed, pico and joe all do.
600 600
601 601 After executing your code, %edit will return as output the code you
602 602 typed in the editor (except when it was an existing file). This way
603 603 you can reload the code in further invocations of %edit as a variable,
604 604 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
605 605 the output.
606 606
607 607 Note that %edit is also available through the alias %ed.
608 608
609 609 This is an example of creating a simple function inside the editor and
610 610 then modifying it. First, start up the editor::
611 611
612 612 In [1]: edit
613 613 Editing... done. Executing edited code...
614 614 Out[1]: 'def foo():\\n print "foo() was defined in an editing
615 615 session"\\n'
616 616
617 617 We can then call the function foo()::
618 618
619 619 In [2]: foo()
620 620 foo() was defined in an editing session
621 621
622 622 Now we edit foo. IPython automatically loads the editor with the
623 623 (temporary) file where foo() was previously defined::
624 624
625 625 In [3]: edit foo
626 626 Editing... done. Executing edited code...
627 627
628 628 And if we call foo() again we get the modified version::
629 629
630 630 In [4]: foo()
631 631 foo() has now been changed!
632 632
633 633 Here is an example of how to edit a code snippet successive
634 634 times. First we call the editor::
635 635
636 636 In [5]: edit
637 637 Editing... done. Executing edited code...
638 638 hello
639 639 Out[5]: "print 'hello'\\n"
640 640
641 641 Now we call it again with the previous output (stored in _)::
642 642
643 643 In [6]: edit _
644 644 Editing... done. Executing edited code...
645 645 hello world
646 646 Out[6]: "print 'hello world'\\n"
647 647
648 648 Now we call it with the output #8 (stored in _8, also as Out[8])::
649 649
650 650 In [7]: edit _8
651 651 Editing... done. Executing edited code...
652 652 hello again
653 653 Out[7]: "print 'hello again'\\n"
654 654
655 655
656 656 Changing the default editor hook:
657 657
658 658 If you wish to write your own editor hook, you can put it in a
659 659 configuration file which you load at startup time. The default hook
660 660 is defined in the IPython.core.hooks module, and you can use that as a
661 661 starting example for further modifications. That file also has
662 662 general instructions on how to set a new hook for use once you've
663 663 defined it."""
664 664 opts,args = self.parse_options(parameter_s,'prxn:')
665 665
666 666 try:
667 667 filename, lineno, is_temp = self._find_edit_target(self.shell,
668 668 args, opts, last_call)
669 669 except MacroToEdit as e:
670 670 self._edit_macro(args, e.args[0])
671 671 return
672 672 except InteractivelyDefined as e:
673 673 print("Editing In[%i]" % e.index)
674 674 args = str(e.index)
675 675 filename, lineno, is_temp = self._find_edit_target(self.shell,
676 676 args, opts, last_call)
677 677 if filename is None:
678 678 # nothing was found, warnings have already been issued,
679 679 # just give up.
680 680 return
681 681
682 682 if is_temp:
683 683 self._knowntemps.add(filename)
684 684 elif (filename in self._knowntemps):
685 685 is_temp = True
686 686
687 687
688 688 # do actual editing here
689 689 print('Editing...', end=' ')
690 690 sys.stdout.flush()
691 filepath = Path(filename)
691 692 try:
692 # Quote filenames that may have spaces in them
693 if ' ' in filename:
694 filename = "'%s'" % filename
695 self.shell.hooks.editor(filename,lineno)
693 # Quote filenames that may have spaces in them when opening
694 # the editor
695 quoted = filename = str(filepath.absolute())
696 if " " in quoted:
697 quoted = "'%s'" % quoted
698 self.shell.hooks.editor(quoted, lineno)
696 699 except TryNext:
697 700 warn('Could not open editor')
698 701 return
699 702
700 703 # XXX TODO: should this be generalized for all string vars?
701 704 # For now, this is special-cased to blocks created by cpaste
702 if args.strip() == 'pasted_block':
703 with open(filename, 'r') as f:
704 self.shell.user_ns['pasted_block'] = f.read()
705 if args.strip() == "pasted_block":
706 self.shell.user_ns["pasted_block"] = filepath.read_text()
705 707
706 708 if 'x' in opts: # -x prevents actual execution
707 709 print()
708 710 else:
709 711 print('done. Executing edited code...')
710 712 with preserve_keys(self.shell.user_ns, '__file__'):
711 713 if not is_temp:
712 714 self.shell.user_ns['__file__'] = filename
713 715 if 'r' in opts: # Untranslated IPython code
714 with open(filename, 'r') as f:
715 source = f.read()
716 source = filepath.read_text()
716 717 self.shell.run_cell(source, store_history=False)
717 718 else:
718 719 self.shell.safe_execfile(filename, self.shell.user_ns,
719 720 self.shell.user_ns)
720 721
721 722 if is_temp:
722 723 try:
723 with open(filename) as f:
724 return f.read()
724 return filepath.read_text()
725 725 except IOError as msg:
726 if msg.filename == filename:
726 if Path(msg.filename) == filepath:
727 727 warn('File not found. Did you forget to save?')
728 728 return
729 729 else:
730 730 self.shell.showtraceback()
@@ -1,1261 +1,1266 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for various magic functions.
3 3
4 4 Needs to be run by nose (to make ipython session available).
5 5 """
6 6
7 7 import io
8 8 import os
9 9 import re
10 10 import sys
11 11 import warnings
12 12 from textwrap import dedent
13 13 from unittest import TestCase
14 14 from unittest import mock
15 15 from importlib import invalidate_caches
16 16 from io import StringIO
17 17
18 18 import nose.tools as nt
19 19
20 20 import shlex
21 21
22 22 from IPython import get_ipython
23 23 from IPython.core import magic
24 24 from IPython.core.error import UsageError
25 25 from IPython.core.magic import (Magics, magics_class, line_magic,
26 26 cell_magic,
27 27 register_line_magic, register_cell_magic)
28 28 from IPython.core.magics import execution, script, code, logging, osm
29 29 from IPython.testing import decorators as dec
30 30 from IPython.testing import tools as tt
31 31 from IPython.utils.io import capture_output
32 32 from IPython.utils.tempdir import (TemporaryDirectory,
33 33 TemporaryWorkingDirectory)
34 34 from IPython.utils.process import find_cmd
35 35 from .test_debugger import PdbTestInput
36 36
37 37
38 38 @magic.magics_class
39 39 class DummyMagics(magic.Magics): pass
40 40
41 41 def test_extract_code_ranges():
42 42 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
43 43 expected = [(0, 1),
44 44 (2, 3),
45 45 (4, 6),
46 46 (6, 9),
47 47 (9, 14),
48 48 (16, None),
49 49 (None, 9),
50 50 (9, None),
51 51 (None, 13),
52 52 (None, None)]
53 53 actual = list(code.extract_code_ranges(instr))
54 54 nt.assert_equal(actual, expected)
55 55
56 56 def test_extract_symbols():
57 57 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
58 58 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
59 59 expected = [([], ['a']),
60 60 (["def b():\n return 42\n"], []),
61 61 (["class A: pass\n"], []),
62 62 (["class A: pass\n", "def b():\n return 42\n"], []),
63 63 (["class A: pass\n"], ['a']),
64 64 ([], ['z'])]
65 65 for symbols, exp in zip(symbols_args, expected):
66 66 nt.assert_equal(code.extract_symbols(source, symbols), exp)
67 67
68 68
69 69 def test_extract_symbols_raises_exception_with_non_python_code():
70 70 source = ("=begin A Ruby program :)=end\n"
71 71 "def hello\n"
72 72 "puts 'Hello world'\n"
73 73 "end")
74 74 with nt.assert_raises(SyntaxError):
75 75 code.extract_symbols(source, "hello")
76 76
77 77
78 78 def test_magic_not_found():
79 79 # magic not found raises UsageError
80 80 with nt.assert_raises(UsageError):
81 81 _ip.magic('doesntexist')
82 82
83 83 # ensure result isn't success when a magic isn't found
84 84 result = _ip.run_cell('%doesntexist')
85 85 assert isinstance(result.error_in_exec, UsageError)
86 86
87 87
88 88 def test_cell_magic_not_found():
89 89 # magic not found raises UsageError
90 90 with nt.assert_raises(UsageError):
91 91 _ip.run_cell_magic('doesntexist', 'line', 'cell')
92 92
93 93 # ensure result isn't success when a magic isn't found
94 94 result = _ip.run_cell('%%doesntexist')
95 95 assert isinstance(result.error_in_exec, UsageError)
96 96
97 97
98 98 def test_magic_error_status():
99 99 def fail(shell):
100 100 1/0
101 101 _ip.register_magic_function(fail)
102 102 result = _ip.run_cell('%fail')
103 103 assert isinstance(result.error_in_exec, ZeroDivisionError)
104 104
105 105
106 106 def test_config():
107 107 """ test that config magic does not raise
108 108 can happen if Configurable init is moved too early into
109 109 Magics.__init__ as then a Config object will be registered as a
110 110 magic.
111 111 """
112 112 ## should not raise.
113 113 _ip.magic('config')
114 114
115 115 def test_config_available_configs():
116 116 """ test that config magic prints available configs in unique and
117 117 sorted order. """
118 118 with capture_output() as captured:
119 119 _ip.magic('config')
120 120
121 121 stdout = captured.stdout
122 122 config_classes = stdout.strip().split('\n')[1:]
123 123 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
124 124
125 125 def test_config_print_class():
126 126 """ test that config with a classname prints the class's options. """
127 127 with capture_output() as captured:
128 128 _ip.magic('config TerminalInteractiveShell')
129 129
130 130 stdout = captured.stdout
131 131 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
132 132 print(stdout)
133 133 raise AssertionError("1st line of stdout not like "
134 134 "'TerminalInteractiveShell.* options'")
135 135
136 136 def test_rehashx():
137 137 # clear up everything
138 138 _ip.alias_manager.clear_aliases()
139 139 del _ip.db['syscmdlist']
140 140
141 141 _ip.magic('rehashx')
142 142 # Practically ALL ipython development systems will have more than 10 aliases
143 143
144 144 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
145 145 for name, cmd in _ip.alias_manager.aliases:
146 146 # we must strip dots from alias names
147 147 nt.assert_not_in('.', name)
148 148
149 149 # rehashx must fill up syscmdlist
150 150 scoms = _ip.db['syscmdlist']
151 151 nt.assert_true(len(scoms) > 10)
152 152
153 153
154 154
155 155 def test_magic_parse_options():
156 156 """Test that we don't mangle paths when parsing magic options."""
157 157 ip = get_ipython()
158 158 path = 'c:\\x'
159 159 m = DummyMagics(ip)
160 160 opts = m.parse_options('-f %s' % path,'f:')[0]
161 161 # argv splitting is os-dependent
162 162 if os.name == 'posix':
163 163 expected = 'c:x'
164 164 else:
165 165 expected = path
166 166 nt.assert_equal(opts['f'], expected)
167 167
168 168 def test_magic_parse_long_options():
169 169 """Magic.parse_options can handle --foo=bar long options"""
170 170 ip = get_ipython()
171 171 m = DummyMagics(ip)
172 172 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
173 173 nt.assert_in('foo', opts)
174 174 nt.assert_in('bar', opts)
175 175 nt.assert_equal(opts['bar'], "bubble")
176 176
177 177
178 178 def doctest_hist_f():
179 179 """Test %hist -f with temporary filename.
180 180
181 181 In [9]: import tempfile
182 182
183 183 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
184 184
185 185 In [11]: %hist -nl -f $tfile 3
186 186
187 187 In [13]: import os; os.unlink(tfile)
188 188 """
189 189
190 190
191 191 def doctest_hist_op():
192 192 """Test %hist -op
193 193
194 194 In [1]: class b(float):
195 195 ...: pass
196 196 ...:
197 197
198 198 In [2]: class s(object):
199 199 ...: def __str__(self):
200 200 ...: return 's'
201 201 ...:
202 202
203 203 In [3]:
204 204
205 205 In [4]: class r(b):
206 206 ...: def __repr__(self):
207 207 ...: return 'r'
208 208 ...:
209 209
210 210 In [5]: class sr(s,r): pass
211 211 ...:
212 212
213 213 In [6]:
214 214
215 215 In [7]: bb=b()
216 216
217 217 In [8]: ss=s()
218 218
219 219 In [9]: rr=r()
220 220
221 221 In [10]: ssrr=sr()
222 222
223 223 In [11]: 4.5
224 224 Out[11]: 4.5
225 225
226 226 In [12]: str(ss)
227 227 Out[12]: 's'
228 228
229 229 In [13]:
230 230
231 231 In [14]: %hist -op
232 232 >>> class b:
233 233 ... pass
234 234 ...
235 235 >>> class s(b):
236 236 ... def __str__(self):
237 237 ... return 's'
238 238 ...
239 239 >>>
240 240 >>> class r(b):
241 241 ... def __repr__(self):
242 242 ... return 'r'
243 243 ...
244 244 >>> class sr(s,r): pass
245 245 >>>
246 246 >>> bb=b()
247 247 >>> ss=s()
248 248 >>> rr=r()
249 249 >>> ssrr=sr()
250 250 >>> 4.5
251 251 4.5
252 252 >>> str(ss)
253 253 's'
254 254 >>>
255 255 """
256 256
257 257 def test_hist_pof():
258 258 ip = get_ipython()
259 259 ip.run_cell(u"1+2", store_history=True)
260 260 #raise Exception(ip.history_manager.session_number)
261 261 #raise Exception(list(ip.history_manager._get_range_session()))
262 262 with TemporaryDirectory() as td:
263 263 tf = os.path.join(td, 'hist.py')
264 264 ip.run_line_magic('history', '-pof %s' % tf)
265 265 assert os.path.isfile(tf)
266 266
267 267
268 268 def test_macro():
269 269 ip = get_ipython()
270 270 ip.history_manager.reset() # Clear any existing history.
271 271 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
272 272 for i, cmd in enumerate(cmds, start=1):
273 273 ip.history_manager.store_inputs(i, cmd)
274 274 ip.magic("macro test 1-3")
275 275 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
276 276
277 277 # List macros
278 278 nt.assert_in("test", ip.magic("macro"))
279 279
280 280
281 281 def test_macro_run():
282 282 """Test that we can run a multi-line macro successfully."""
283 283 ip = get_ipython()
284 284 ip.history_manager.reset()
285 285 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
286 286 for cmd in cmds:
287 287 ip.run_cell(cmd, store_history=True)
288 288 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint(a)\n")
289 289 with tt.AssertPrints("12"):
290 290 ip.run_cell("test")
291 291 with tt.AssertPrints("13"):
292 292 ip.run_cell("test")
293 293
294 294
295 295 def test_magic_magic():
296 296 """Test %magic"""
297 297 ip = get_ipython()
298 298 with capture_output() as captured:
299 299 ip.magic("magic")
300 300
301 301 stdout = captured.stdout
302 302 nt.assert_in('%magic', stdout)
303 303 nt.assert_in('IPython', stdout)
304 304 nt.assert_in('Available', stdout)
305 305
306 306
307 307 @dec.skipif_not_numpy
308 308 def test_numpy_reset_array_undec():
309 309 "Test '%reset array' functionality"
310 310 _ip.ex('import numpy as np')
311 311 _ip.ex('a = np.empty(2)')
312 312 nt.assert_in('a', _ip.user_ns)
313 313 _ip.magic('reset -f array')
314 314 nt.assert_not_in('a', _ip.user_ns)
315 315
316 316 def test_reset_out():
317 317 "Test '%reset out' magic"
318 318 _ip.run_cell("parrot = 'dead'", store_history=True)
319 319 # test '%reset -f out', make an Out prompt
320 320 _ip.run_cell("parrot", store_history=True)
321 321 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
322 322 _ip.magic('reset -f out')
323 323 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
324 324 nt.assert_equal(len(_ip.user_ns['Out']), 0)
325 325
326 326 def test_reset_in():
327 327 "Test '%reset in' magic"
328 328 # test '%reset -f in'
329 329 _ip.run_cell("parrot", store_history=True)
330 330 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
331 331 _ip.magic('%reset -f in')
332 332 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
333 333 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
334 334
335 335 def test_reset_dhist():
336 336 "Test '%reset dhist' magic"
337 337 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
338 338 _ip.magic('cd ' + os.path.dirname(nt.__file__))
339 339 _ip.magic('cd -')
340 340 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
341 341 _ip.magic('reset -f dhist')
342 342 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
343 343 _ip.run_cell("_dh = [d for d in tmp]") #restore
344 344
345 345 def test_reset_in_length():
346 346 "Test that '%reset in' preserves In[] length"
347 347 _ip.run_cell("print 'foo'")
348 348 _ip.run_cell("reset -f in")
349 349 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
350 350
351 351 class TestResetErrors(TestCase):
352 352
353 353 def test_reset_redefine(self):
354 354
355 355 @magics_class
356 356 class KernelMagics(Magics):
357 357 @line_magic
358 358 def less(self, shell): pass
359 359
360 360 _ip.register_magics(KernelMagics)
361 361
362 362 with self.assertLogs() as cm:
363 363 # hack, we want to just capture logs, but assertLogs fails if not
364 364 # logs get produce.
365 365 # so log one things we ignore.
366 366 import logging as log_mod
367 367 log = log_mod.getLogger()
368 368 log.info('Nothing')
369 369 # end hack.
370 370 _ip.run_cell("reset -f")
371 371
372 372 assert len(cm.output) == 1
373 373 for out in cm.output:
374 374 assert "Invalid alias" not in out
375 375
376 376 def test_tb_syntaxerror():
377 377 """test %tb after a SyntaxError"""
378 378 ip = get_ipython()
379 379 ip.run_cell("for")
380 380
381 381 # trap and validate stdout
382 382 save_stdout = sys.stdout
383 383 try:
384 384 sys.stdout = StringIO()
385 385 ip.run_cell("%tb")
386 386 out = sys.stdout.getvalue()
387 387 finally:
388 388 sys.stdout = save_stdout
389 389 # trim output, and only check the last line
390 390 last_line = out.rstrip().splitlines()[-1].strip()
391 391 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
392 392
393 393
394 394 def test_time():
395 395 ip = get_ipython()
396 396
397 397 with tt.AssertPrints("Wall time: "):
398 398 ip.run_cell("%time None")
399 399
400 400 ip.run_cell("def f(kmjy):\n"
401 401 " %time print (2*kmjy)")
402 402
403 403 with tt.AssertPrints("Wall time: "):
404 404 with tt.AssertPrints("hihi", suppress=False):
405 405 ip.run_cell("f('hi')")
406 406
407 407 def test_time_last_not_expression():
408 408 ip.run_cell("%%time\n"
409 409 "var_1 = 1\n"
410 410 "var_2 = 2\n")
411 411 assert ip.user_ns['var_1'] == 1
412 412 del ip.user_ns['var_1']
413 413 assert ip.user_ns['var_2'] == 2
414 414 del ip.user_ns['var_2']
415 415
416 416
417 417 @dec.skip_win32
418 418 def test_time2():
419 419 ip = get_ipython()
420 420
421 421 with tt.AssertPrints("CPU times: user "):
422 422 ip.run_cell("%time None")
423 423
424 424 def test_time3():
425 425 """Erroneous magic function calls, issue gh-3334"""
426 426 ip = get_ipython()
427 427 ip.user_ns.pop('run', None)
428 428
429 429 with tt.AssertNotPrints("not found", channel='stderr'):
430 430 ip.run_cell("%%time\n"
431 431 "run = 0\n"
432 432 "run += 1")
433 433
434 434 def test_multiline_time():
435 435 """Make sure last statement from time return a value."""
436 436 ip = get_ipython()
437 437 ip.user_ns.pop('run', None)
438 438
439 439 ip.run_cell(dedent("""\
440 440 %%time
441 441 a = "ho"
442 442 b = "hey"
443 443 a+b
444 444 """))
445 445 nt.assert_equal(ip.user_ns_hidden['_'], 'hohey')
446 446
447 447 def test_time_local_ns():
448 448 """
449 449 Test that local_ns is actually global_ns when running a cell magic
450 450 """
451 451 ip = get_ipython()
452 452 ip.run_cell("%%time\n"
453 453 "myvar = 1")
454 454 nt.assert_equal(ip.user_ns['myvar'], 1)
455 455 del ip.user_ns['myvar']
456 456
457 457 def test_doctest_mode():
458 458 "Toggle doctest_mode twice, it should be a no-op and run without error"
459 459 _ip.magic('doctest_mode')
460 460 _ip.magic('doctest_mode')
461 461
462 462
463 463 def test_parse_options():
464 464 """Tests for basic options parsing in magics."""
465 465 # These are only the most minimal of tests, more should be added later. At
466 466 # the very least we check that basic text/unicode calls work OK.
467 467 m = DummyMagics(_ip)
468 468 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
469 469 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
470 470
471 471
472 472 def test_dirops():
473 473 """Test various directory handling operations."""
474 474 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
475 475 curpath = os.getcwd
476 476 startdir = os.getcwd()
477 477 ipdir = os.path.realpath(_ip.ipython_dir)
478 478 try:
479 479 _ip.magic('cd "%s"' % ipdir)
480 480 nt.assert_equal(curpath(), ipdir)
481 481 _ip.magic('cd -')
482 482 nt.assert_equal(curpath(), startdir)
483 483 _ip.magic('pushd "%s"' % ipdir)
484 484 nt.assert_equal(curpath(), ipdir)
485 485 _ip.magic('popd')
486 486 nt.assert_equal(curpath(), startdir)
487 487 finally:
488 488 os.chdir(startdir)
489 489
490 490
491 491 def test_cd_force_quiet():
492 492 """Test OSMagics.cd_force_quiet option"""
493 493 _ip.config.OSMagics.cd_force_quiet = True
494 494 osmagics = osm.OSMagics(shell=_ip)
495 495
496 496 startdir = os.getcwd()
497 497 ipdir = os.path.realpath(_ip.ipython_dir)
498 498
499 499 try:
500 500 with tt.AssertNotPrints(ipdir):
501 501 osmagics.cd('"%s"' % ipdir)
502 502 with tt.AssertNotPrints(startdir):
503 503 osmagics.cd('-')
504 504 finally:
505 505 os.chdir(startdir)
506 506
507 507
508 508 def test_xmode():
509 509 # Calling xmode three times should be a no-op
510 510 xmode = _ip.InteractiveTB.mode
511 511 for i in range(4):
512 512 _ip.magic("xmode")
513 513 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
514 514
515 515 def test_reset_hard():
516 516 monitor = []
517 517 class A(object):
518 518 def __del__(self):
519 519 monitor.append(1)
520 520 def __repr__(self):
521 521 return "<A instance>"
522 522
523 523 _ip.user_ns["a"] = A()
524 524 _ip.run_cell("a")
525 525
526 526 nt.assert_equal(monitor, [])
527 527 _ip.magic("reset -f")
528 528 nt.assert_equal(monitor, [1])
529 529
530 530 class TestXdel(tt.TempFileMixin):
531 531 def test_xdel(self):
532 532 """Test that references from %run are cleared by xdel."""
533 533 src = ("class A(object):\n"
534 534 " monitor = []\n"
535 535 " def __del__(self):\n"
536 536 " self.monitor.append(1)\n"
537 537 "a = A()\n")
538 538 self.mktmp(src)
539 539 # %run creates some hidden references...
540 540 _ip.magic("run %s" % self.fname)
541 541 # ... as does the displayhook.
542 542 _ip.run_cell("a")
543 543
544 544 monitor = _ip.user_ns["A"].monitor
545 545 nt.assert_equal(monitor, [])
546 546
547 547 _ip.magic("xdel a")
548 548
549 549 # Check that a's __del__ method has been called.
550 550 nt.assert_equal(monitor, [1])
551 551
552 552 def doctest_who():
553 553 """doctest for %who
554 554
555 555 In [1]: %reset -f
556 556
557 557 In [2]: alpha = 123
558 558
559 559 In [3]: beta = 'beta'
560 560
561 561 In [4]: %who int
562 562 alpha
563 563
564 564 In [5]: %who str
565 565 beta
566 566
567 567 In [6]: %whos
568 568 Variable Type Data/Info
569 569 ----------------------------
570 570 alpha int 123
571 571 beta str beta
572 572
573 573 In [7]: %who_ls
574 574 Out[7]: ['alpha', 'beta']
575 575 """
576 576
577 577 def test_whos():
578 578 """Check that whos is protected against objects where repr() fails."""
579 579 class A(object):
580 580 def __repr__(self):
581 581 raise Exception()
582 582 _ip.user_ns['a'] = A()
583 583 _ip.magic("whos")
584 584
585 585 def doctest_precision():
586 586 """doctest for %precision
587 587
588 588 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
589 589
590 590 In [2]: %precision 5
591 591 Out[2]: '%.5f'
592 592
593 593 In [3]: f.float_format
594 594 Out[3]: '%.5f'
595 595
596 596 In [4]: %precision %e
597 597 Out[4]: '%e'
598 598
599 599 In [5]: f(3.1415927)
600 600 Out[5]: '3.141593e+00'
601 601 """
602 602
603 603 def test_debug_magic():
604 604 """Test debugging a small code with %debug
605 605
606 606 In [1]: with PdbTestInput(['c']):
607 607 ...: %debug print("a b") #doctest: +ELLIPSIS
608 608 ...:
609 609 ...
610 610 ipdb> c
611 611 a b
612 612 In [2]:
613 613 """
614 614
615 615 def test_psearch():
616 616 with tt.AssertPrints("dict.fromkeys"):
617 617 _ip.run_cell("dict.fr*?")
618 618 with tt.AssertPrints("Ο€.is_integer"):
619 619 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
620 620
621 621 def test_timeit_shlex():
622 622 """test shlex issues with timeit (#1109)"""
623 623 _ip.ex("def f(*a,**kw): pass")
624 624 _ip.magic('timeit -n1 "this is a bug".count(" ")')
625 625 _ip.magic('timeit -r1 -n1 f(" ", 1)')
626 626 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
627 627 _ip.magic('timeit -r1 -n1 ("a " + "b")')
628 628 _ip.magic('timeit -r1 -n1 f("a " + "b")')
629 629 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
630 630
631 631
632 632 def test_timeit_special_syntax():
633 633 "Test %%timeit with IPython special syntax"
634 634 @register_line_magic
635 635 def lmagic(line):
636 636 ip = get_ipython()
637 637 ip.user_ns['lmagic_out'] = line
638 638
639 639 # line mode test
640 640 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
641 641 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
642 642 # cell mode test
643 643 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
644 644 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
645 645
646 646 def test_timeit_return():
647 647 """
648 648 test whether timeit -o return object
649 649 """
650 650
651 651 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
652 652 assert(res is not None)
653 653
654 654 def test_timeit_quiet():
655 655 """
656 656 test quiet option of timeit magic
657 657 """
658 658 with tt.AssertNotPrints("loops"):
659 659 _ip.run_cell("%timeit -n1 -r1 -q 1")
660 660
661 661 def test_timeit_return_quiet():
662 662 with tt.AssertNotPrints("loops"):
663 663 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
664 664 assert (res is not None)
665 665
666 666 def test_timeit_invalid_return():
667 667 with nt.assert_raises_regex(SyntaxError, "outside function"):
668 668 _ip.run_line_magic('timeit', 'return')
669 669
670 670 @dec.skipif(execution.profile is None)
671 671 def test_prun_special_syntax():
672 672 "Test %%prun with IPython special syntax"
673 673 @register_line_magic
674 674 def lmagic(line):
675 675 ip = get_ipython()
676 676 ip.user_ns['lmagic_out'] = line
677 677
678 678 # line mode test
679 679 _ip.run_line_magic('prun', '-q %lmagic my line')
680 680 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
681 681 # cell mode test
682 682 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
683 683 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
684 684
685 685 @dec.skipif(execution.profile is None)
686 686 def test_prun_quotes():
687 687 "Test that prun does not clobber string escapes (GH #1302)"
688 688 _ip.magic(r"prun -q x = '\t'")
689 689 nt.assert_equal(_ip.user_ns['x'], '\t')
690 690
691 691 def test_extension():
692 692 # Debugging information for failures of this test
693 693 print('sys.path:')
694 694 for p in sys.path:
695 695 print(' ', p)
696 696 print('CWD', os.getcwd())
697 697
698 698 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
699 699 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
700 700 sys.path.insert(0, daft_path)
701 701 try:
702 702 _ip.user_ns.pop('arq', None)
703 703 invalidate_caches() # Clear import caches
704 704 _ip.magic("load_ext daft_extension")
705 705 nt.assert_equal(_ip.user_ns['arq'], 185)
706 706 _ip.magic("unload_ext daft_extension")
707 707 assert 'arq' not in _ip.user_ns
708 708 finally:
709 709 sys.path.remove(daft_path)
710 710
711 711
712 712 def test_notebook_export_json():
713 713 _ip = get_ipython()
714 714 _ip.history_manager.reset() # Clear any existing history.
715 715 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
716 716 for i, cmd in enumerate(cmds, start=1):
717 717 _ip.history_manager.store_inputs(i, cmd)
718 718 with TemporaryDirectory() as td:
719 719 outfile = os.path.join(td, "nb.ipynb")
720 720 _ip.magic("notebook -e %s" % outfile)
721 721
722 722
723 723 class TestEnv(TestCase):
724 724
725 725 def test_env(self):
726 726 env = _ip.magic("env")
727 727 self.assertTrue(isinstance(env, dict))
728 728
729 729 def test_env_secret(self):
730 730 env = _ip.magic("env")
731 731 hidden = "<hidden>"
732 732 with mock.patch.dict(
733 733 os.environ,
734 734 {
735 735 "API_KEY": "abc123",
736 736 "SECRET_THING": "ssshhh",
737 737 "JUPYTER_TOKEN": "",
738 738 "VAR": "abc"
739 739 }
740 740 ):
741 741 env = _ip.magic("env")
742 742 assert env["API_KEY"] == hidden
743 743 assert env["SECRET_THING"] == hidden
744 744 assert env["JUPYTER_TOKEN"] == hidden
745 745 assert env["VAR"] == "abc"
746 746
747 747 def test_env_get_set_simple(self):
748 748 env = _ip.magic("env var val1")
749 749 self.assertEqual(env, None)
750 750 self.assertEqual(os.environ['var'], 'val1')
751 751 self.assertEqual(_ip.magic("env var"), 'val1')
752 752 env = _ip.magic("env var=val2")
753 753 self.assertEqual(env, None)
754 754 self.assertEqual(os.environ['var'], 'val2')
755 755
756 756 def test_env_get_set_complex(self):
757 757 env = _ip.magic("env var 'val1 '' 'val2")
758 758 self.assertEqual(env, None)
759 759 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
760 760 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
761 761 env = _ip.magic('env var=val2 val3="val4')
762 762 self.assertEqual(env, None)
763 763 self.assertEqual(os.environ['var'], 'val2 val3="val4')
764 764
765 765 def test_env_set_bad_input(self):
766 766 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
767 767
768 768 def test_env_set_whitespace(self):
769 769 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
770 770
771 771
772 772 class CellMagicTestCase(TestCase):
773 773
774 774 def check_ident(self, magic):
775 775 # Manually called, we get the result
776 776 out = _ip.run_cell_magic(magic, 'a', 'b')
777 777 nt.assert_equal(out, ('a','b'))
778 778 # Via run_cell, it goes into the user's namespace via displayhook
779 779 _ip.run_cell('%%' + magic +' c\nd\n')
780 780 nt.assert_equal(_ip.user_ns['_'], ('c','d\n'))
781 781
782 782 def test_cell_magic_func_deco(self):
783 783 "Cell magic using simple decorator"
784 784 @register_cell_magic
785 785 def cellm(line, cell):
786 786 return line, cell
787 787
788 788 self.check_ident('cellm')
789 789
790 790 def test_cell_magic_reg(self):
791 791 "Cell magic manually registered"
792 792 def cellm(line, cell):
793 793 return line, cell
794 794
795 795 _ip.register_magic_function(cellm, 'cell', 'cellm2')
796 796 self.check_ident('cellm2')
797 797
798 798 def test_cell_magic_class(self):
799 799 "Cell magics declared via a class"
800 800 @magics_class
801 801 class MyMagics(Magics):
802 802
803 803 @cell_magic
804 804 def cellm3(self, line, cell):
805 805 return line, cell
806 806
807 807 _ip.register_magics(MyMagics)
808 808 self.check_ident('cellm3')
809 809
810 810 def test_cell_magic_class2(self):
811 811 "Cell magics declared via a class, #2"
812 812 @magics_class
813 813 class MyMagics2(Magics):
814 814
815 815 @cell_magic('cellm4')
816 816 def cellm33(self, line, cell):
817 817 return line, cell
818 818
819 819 _ip.register_magics(MyMagics2)
820 820 self.check_ident('cellm4')
821 821 # Check that nothing is registered as 'cellm33'
822 822 c33 = _ip.find_cell_magic('cellm33')
823 823 nt.assert_equal(c33, None)
824 824
825 825 def test_file():
826 826 """Basic %%writefile"""
827 827 ip = get_ipython()
828 828 with TemporaryDirectory() as td:
829 829 fname = os.path.join(td, 'file1')
830 830 ip.run_cell_magic("writefile", fname, u'\n'.join([
831 831 'line1',
832 832 'line2',
833 833 ]))
834 834 with open(fname) as f:
835 835 s = f.read()
836 836 nt.assert_in('line1\n', s)
837 837 nt.assert_in('line2', s)
838 838
839 839 @dec.skip_win32
840 840 def test_file_single_quote():
841 841 """Basic %%writefile with embedded single quotes"""
842 842 ip = get_ipython()
843 843 with TemporaryDirectory() as td:
844 844 fname = os.path.join(td, '\'file1\'')
845 845 ip.run_cell_magic("writefile", fname, u'\n'.join([
846 846 'line1',
847 847 'line2',
848 848 ]))
849 849 with open(fname) as f:
850 850 s = f.read()
851 851 nt.assert_in('line1\n', s)
852 852 nt.assert_in('line2', s)
853 853
854 854 @dec.skip_win32
855 855 def test_file_double_quote():
856 856 """Basic %%writefile with embedded double quotes"""
857 857 ip = get_ipython()
858 858 with TemporaryDirectory() as td:
859 859 fname = os.path.join(td, '"file1"')
860 860 ip.run_cell_magic("writefile", fname, u'\n'.join([
861 861 'line1',
862 862 'line2',
863 863 ]))
864 864 with open(fname) as f:
865 865 s = f.read()
866 866 nt.assert_in('line1\n', s)
867 867 nt.assert_in('line2', s)
868 868
869 869 def test_file_var_expand():
870 870 """%%writefile $filename"""
871 871 ip = get_ipython()
872 872 with TemporaryDirectory() as td:
873 873 fname = os.path.join(td, 'file1')
874 874 ip.user_ns['filename'] = fname
875 875 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
876 876 'line1',
877 877 'line2',
878 878 ]))
879 879 with open(fname) as f:
880 880 s = f.read()
881 881 nt.assert_in('line1\n', s)
882 882 nt.assert_in('line2', s)
883 883
884 884 def test_file_unicode():
885 885 """%%writefile with unicode cell"""
886 886 ip = get_ipython()
887 887 with TemporaryDirectory() as td:
888 888 fname = os.path.join(td, 'file1')
889 889 ip.run_cell_magic("writefile", fname, u'\n'.join([
890 890 u'linΓ©1',
891 891 u'linΓ©2',
892 892 ]))
893 893 with io.open(fname, encoding='utf-8') as f:
894 894 s = f.read()
895 895 nt.assert_in(u'linΓ©1\n', s)
896 896 nt.assert_in(u'linΓ©2', s)
897 897
898 898 def test_file_amend():
899 899 """%%writefile -a amends files"""
900 900 ip = get_ipython()
901 901 with TemporaryDirectory() as td:
902 902 fname = os.path.join(td, 'file2')
903 903 ip.run_cell_magic("writefile", fname, u'\n'.join([
904 904 'line1',
905 905 'line2',
906 906 ]))
907 907 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
908 908 'line3',
909 909 'line4',
910 910 ]))
911 911 with open(fname) as f:
912 912 s = f.read()
913 913 nt.assert_in('line1\n', s)
914 914 nt.assert_in('line3\n', s)
915 915
916 916 def test_file_spaces():
917 917 """%%file with spaces in filename"""
918 918 ip = get_ipython()
919 919 with TemporaryWorkingDirectory() as td:
920 920 fname = "file name"
921 921 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
922 922 'line1',
923 923 'line2',
924 924 ]))
925 925 with open(fname) as f:
926 926 s = f.read()
927 927 nt.assert_in('line1\n', s)
928 928 nt.assert_in('line2', s)
929 929
930 930 def test_script_config():
931 931 ip = get_ipython()
932 932 ip.config.ScriptMagics.script_magics = ['whoda']
933 933 sm = script.ScriptMagics(shell=ip)
934 934 nt.assert_in('whoda', sm.magics['cell'])
935 935
936 936 @dec.skip_win32
937 937 def test_script_out():
938 938 ip = get_ipython()
939 939 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
940 940 nt.assert_equal(ip.user_ns['output'], 'hi\n')
941 941
942 942 @dec.skip_win32
943 943 def test_script_err():
944 944 ip = get_ipython()
945 945 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
946 946 nt.assert_equal(ip.user_ns['error'], 'hello\n')
947 947
948 948 @dec.skip_win32
949 949 def test_script_out_err():
950 950 ip = get_ipython()
951 951 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
952 952 nt.assert_equal(ip.user_ns['output'], 'hi\n')
953 953 nt.assert_equal(ip.user_ns['error'], 'hello\n')
954 954
955 955 @dec.skip_win32
956 956 def test_script_bg_out():
957 957 ip = get_ipython()
958 958 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
959 959
960 960 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
961 961 ip.user_ns['output'].close()
962 962
963 963 @dec.skip_win32
964 964 def test_script_bg_err():
965 965 ip = get_ipython()
966 966 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
967 967 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
968 968 ip.user_ns['error'].close()
969 969
970 970 @dec.skip_win32
971 971 def test_script_bg_out_err():
972 972 ip = get_ipython()
973 973 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
974 974 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
975 975 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
976 976 ip.user_ns['output'].close()
977 977 ip.user_ns['error'].close()
978 978
979 979 def test_script_defaults():
980 980 ip = get_ipython()
981 981 for cmd in ['sh', 'bash', 'perl', 'ruby']:
982 982 try:
983 983 find_cmd(cmd)
984 984 except Exception:
985 985 pass
986 986 else:
987 987 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
988 988
989 989
990 990 @magics_class
991 991 class FooFoo(Magics):
992 992 """class with both %foo and %%foo magics"""
993 993 @line_magic('foo')
994 994 def line_foo(self, line):
995 995 "I am line foo"
996 996 pass
997 997
998 998 @cell_magic("foo")
999 999 def cell_foo(self, line, cell):
1000 1000 "I am cell foo, not line foo"
1001 1001 pass
1002 1002
1003 1003 def test_line_cell_info():
1004 1004 """%%foo and %foo magics are distinguishable to inspect"""
1005 1005 ip = get_ipython()
1006 1006 ip.magics_manager.register(FooFoo)
1007 1007 oinfo = ip.object_inspect('foo')
1008 1008 nt.assert_true(oinfo['found'])
1009 1009 nt.assert_true(oinfo['ismagic'])
1010 1010
1011 1011 oinfo = ip.object_inspect('%%foo')
1012 1012 nt.assert_true(oinfo['found'])
1013 1013 nt.assert_true(oinfo['ismagic'])
1014 1014 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
1015 1015
1016 1016 oinfo = ip.object_inspect('%foo')
1017 1017 nt.assert_true(oinfo['found'])
1018 1018 nt.assert_true(oinfo['ismagic'])
1019 1019 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
1020 1020
1021 1021 def test_multiple_magics():
1022 1022 ip = get_ipython()
1023 1023 foo1 = FooFoo(ip)
1024 1024 foo2 = FooFoo(ip)
1025 1025 mm = ip.magics_manager
1026 1026 mm.register(foo1)
1027 1027 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
1028 1028 mm.register(foo2)
1029 1029 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
1030 1030
1031 1031 def test_alias_magic():
1032 1032 """Test %alias_magic."""
1033 1033 ip = get_ipython()
1034 1034 mm = ip.magics_manager
1035 1035
1036 1036 # Basic operation: both cell and line magics are created, if possible.
1037 1037 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
1038 1038 nt.assert_in('timeit_alias', mm.magics['line'])
1039 1039 nt.assert_in('timeit_alias', mm.magics['cell'])
1040 1040
1041 1041 # --cell is specified, line magic not created.
1042 1042 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
1043 1043 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
1044 1044 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
1045 1045
1046 1046 # Test that line alias is created successfully.
1047 1047 ip.run_line_magic('alias_magic', '--line env_alias env')
1048 1048 nt.assert_equal(ip.run_line_magic('env', ''),
1049 1049 ip.run_line_magic('env_alias', ''))
1050 1050
1051 1051 # Test that line alias with parameters passed in is created successfully.
1052 1052 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
1053 1053 nt.assert_in('history_alias', mm.magics['line'])
1054 1054
1055 1055
1056 1056 def test_save():
1057 1057 """Test %save."""
1058 1058 ip = get_ipython()
1059 1059 ip.history_manager.reset() # Clear any existing history.
1060 1060 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
1061 1061 for i, cmd in enumerate(cmds, start=1):
1062 1062 ip.history_manager.store_inputs(i, cmd)
1063 1063 with TemporaryDirectory() as tmpdir:
1064 1064 file = os.path.join(tmpdir, "testsave.py")
1065 1065 ip.run_line_magic("save", "%s 1-10" % file)
1066 1066 with open(file) as f:
1067 1067 content = f.read()
1068 1068 nt.assert_equal(content.count(cmds[0]), 1)
1069 1069 nt.assert_in('coding: utf-8', content)
1070 1070 ip.run_line_magic("save", "-a %s 1-10" % file)
1071 1071 with open(file) as f:
1072 1072 content = f.read()
1073 1073 nt.assert_equal(content.count(cmds[0]), 2)
1074 1074 nt.assert_in('coding: utf-8', content)
1075 1075
1076 1076
1077 1077 def test_store():
1078 1078 """Test %store."""
1079 1079 ip = get_ipython()
1080 1080 ip.run_line_magic('load_ext', 'storemagic')
1081 1081
1082 1082 # make sure the storage is empty
1083 1083 ip.run_line_magic('store', '-z')
1084 1084 ip.user_ns['var'] = 42
1085 1085 ip.run_line_magic('store', 'var')
1086 1086 ip.user_ns['var'] = 39
1087 1087 ip.run_line_magic('store', '-r')
1088 1088 nt.assert_equal(ip.user_ns['var'], 42)
1089 1089
1090 1090 ip.run_line_magic('store', '-d var')
1091 1091 ip.user_ns['var'] = 39
1092 1092 ip.run_line_magic('store' , '-r')
1093 1093 nt.assert_equal(ip.user_ns['var'], 39)
1094 1094
1095 1095
1096 1096 def _run_edit_test(arg_s, exp_filename=None,
1097 1097 exp_lineno=-1,
1098 1098 exp_contents=None,
1099 1099 exp_is_temp=None):
1100 1100 ip = get_ipython()
1101 1101 M = code.CodeMagics(ip)
1102 1102 last_call = ['','']
1103 1103 opts,args = M.parse_options(arg_s,'prxn:')
1104 1104 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1105 1105
1106 1106 if exp_filename is not None:
1107 1107 nt.assert_equal(exp_filename, filename)
1108 1108 if exp_contents is not None:
1109 1109 with io.open(filename, 'r', encoding='utf-8') as f:
1110 1110 contents = f.read()
1111 1111 nt.assert_equal(exp_contents, contents)
1112 1112 if exp_lineno != -1:
1113 1113 nt.assert_equal(exp_lineno, lineno)
1114 1114 if exp_is_temp is not None:
1115 1115 nt.assert_equal(exp_is_temp, is_temp)
1116 1116
1117 1117
1118 1118 def test_edit_interactive():
1119 1119 """%edit on interactively defined objects"""
1120 1120 ip = get_ipython()
1121 1121 n = ip.execution_count
1122 1122 ip.run_cell(u"def foo(): return 1", store_history=True)
1123 1123
1124 1124 try:
1125 1125 _run_edit_test("foo")
1126 1126 except code.InteractivelyDefined as e:
1127 1127 nt.assert_equal(e.index, n)
1128 1128 else:
1129 1129 raise AssertionError("Should have raised InteractivelyDefined")
1130 1130
1131 1131
1132 1132 def test_edit_cell():
1133 1133 """%edit [cell id]"""
1134 1134 ip = get_ipython()
1135 1135
1136 1136 ip.run_cell(u"def foo(): return 1", store_history=True)
1137 1137
1138 1138 # test
1139 1139 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1140 1140
1141 def test_edit_fname():
1142 """%edit file"""
1143 # test
1144 _run_edit_test("test file.py", exp_filename="test file.py")
1145
1141 1146 def test_bookmark():
1142 1147 ip = get_ipython()
1143 1148 ip.run_line_magic('bookmark', 'bmname')
1144 1149 with tt.AssertPrints('bmname'):
1145 1150 ip.run_line_magic('bookmark', '-l')
1146 1151 ip.run_line_magic('bookmark', '-d bmname')
1147 1152
1148 1153 def test_ls_magic():
1149 1154 ip = get_ipython()
1150 1155 json_formatter = ip.display_formatter.formatters['application/json']
1151 1156 json_formatter.enabled = True
1152 1157 lsmagic = ip.magic('lsmagic')
1153 1158 with warnings.catch_warnings(record=True) as w:
1154 1159 j = json_formatter(lsmagic)
1155 1160 nt.assert_equal(sorted(j), ['cell', 'line'])
1156 1161 nt.assert_equal(w, []) # no warnings
1157 1162
1158 1163 def test_strip_initial_indent():
1159 1164 def sii(s):
1160 1165 lines = s.splitlines()
1161 1166 return '\n'.join(code.strip_initial_indent(lines))
1162 1167
1163 1168 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1164 1169 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1165 1170 nt.assert_equal(sii("a\n b"), "a\n b")
1166 1171
1167 1172 def test_logging_magic_quiet_from_arg():
1168 1173 _ip.config.LoggingMagics.quiet = False
1169 1174 lm = logging.LoggingMagics(shell=_ip)
1170 1175 with TemporaryDirectory() as td:
1171 1176 try:
1172 1177 with tt.AssertNotPrints(re.compile("Activating.*")):
1173 1178 lm.logstart('-q {}'.format(
1174 1179 os.path.join(td, "quiet_from_arg.log")))
1175 1180 finally:
1176 1181 _ip.logger.logstop()
1177 1182
1178 1183 def test_logging_magic_quiet_from_config():
1179 1184 _ip.config.LoggingMagics.quiet = True
1180 1185 lm = logging.LoggingMagics(shell=_ip)
1181 1186 with TemporaryDirectory() as td:
1182 1187 try:
1183 1188 with tt.AssertNotPrints(re.compile("Activating.*")):
1184 1189 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1185 1190 finally:
1186 1191 _ip.logger.logstop()
1187 1192
1188 1193
1189 1194 def test_logging_magic_not_quiet():
1190 1195 _ip.config.LoggingMagics.quiet = False
1191 1196 lm = logging.LoggingMagics(shell=_ip)
1192 1197 with TemporaryDirectory() as td:
1193 1198 try:
1194 1199 with tt.AssertPrints(re.compile("Activating.*")):
1195 1200 lm.logstart(os.path.join(td, "not_quiet.log"))
1196 1201 finally:
1197 1202 _ip.logger.logstop()
1198 1203
1199 1204
1200 1205 def test_time_no_var_expand():
1201 1206 _ip.user_ns['a'] = 5
1202 1207 _ip.user_ns['b'] = []
1203 1208 _ip.magic('time b.append("{a}")')
1204 1209 assert _ip.user_ns['b'] == ['{a}']
1205 1210
1206 1211
1207 1212 # this is slow, put at the end for local testing.
1208 1213 def test_timeit_arguments():
1209 1214 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1210 1215 if sys.version_info < (3,7):
1211 1216 _ip.magic("timeit -n1 -r1 ('#')")
1212 1217 else:
1213 1218 # 3.7 optimize no-op statement like above out, and complain there is
1214 1219 # nothing in the for loop.
1215 1220 _ip.magic("timeit -n1 -r1 a=('#')")
1216 1221
1217 1222
1218 1223 TEST_MODULE = """
1219 1224 print('Loaded my_tmp')
1220 1225 if __name__ == "__main__":
1221 1226 print('I just ran a script')
1222 1227 """
1223 1228
1224 1229
1225 1230 def test_run_module_from_import_hook():
1226 1231 "Test that a module can be loaded via an import hook"
1227 1232 with TemporaryDirectory() as tmpdir:
1228 1233 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1229 1234 with open(fullpath, 'w') as f:
1230 1235 f.write(TEST_MODULE)
1231 1236
1232 1237 class MyTempImporter(object):
1233 1238 def __init__(self):
1234 1239 pass
1235 1240
1236 1241 def find_module(self, fullname, path=None):
1237 1242 if 'my_tmp' in fullname:
1238 1243 return self
1239 1244 return None
1240 1245
1241 1246 def load_module(self, name):
1242 1247 import imp
1243 1248 return imp.load_source('my_tmp', fullpath)
1244 1249
1245 1250 def get_code(self, fullname):
1246 1251 with open(fullpath, 'r') as f:
1247 1252 return compile(f.read(), 'foo', 'exec')
1248 1253
1249 1254 def is_package(self, __):
1250 1255 return False
1251 1256
1252 1257 sys.meta_path.insert(0, MyTempImporter())
1253 1258
1254 1259 with capture_output() as captured:
1255 1260 _ip.magic("run -m my_tmp")
1256 1261 _ip.run_cell("import my_tmp")
1257 1262
1258 1263 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1259 1264 nt.assert_equal(output, captured.stdout)
1260 1265
1261 1266 sys.meta_path.pop(0)
General Comments 0
You need to be logged in to leave comments. Login now