##// END OF EJS Templates
More cleanup of pre-3.8 codepath
Matthias Bussonnier -
Show More
@@ -1,799 +1,796 b''
1 1 """Input transformer machinery to support IPython special syntax.
2 2
3 3 This includes the machinery to recognise and transform ``%magic`` commands,
4 4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5 5
6 6 Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
7 7 deprecated in 7.0.
8 8 """
9 9
10 10 # Copyright (c) IPython Development Team.
11 11 # Distributed under the terms of the Modified BSD License.
12 12
13 13 import ast
14 14 import sys
15 15 from codeop import CommandCompiler, Compile
16 16 import re
17 17 import tokenize
18 18 from typing import List, Tuple, Optional, Any
19 19 import warnings
20 20
21 21 _indent_re = re.compile(r'^[ \t]+')
22 22
23 23 def leading_empty_lines(lines):
24 24 """Remove leading empty lines
25 25
26 26 If the leading lines are empty or contain only whitespace, they will be
27 27 removed.
28 28 """
29 29 if not lines:
30 30 return lines
31 31 for i, line in enumerate(lines):
32 32 if line and not line.isspace():
33 33 return lines[i:]
34 34 return lines
35 35
36 36 def leading_indent(lines):
37 37 """Remove leading indentation.
38 38
39 39 If the first line starts with a spaces or tabs, the same whitespace will be
40 40 removed from each following line in the cell.
41 41 """
42 42 if not lines:
43 43 return lines
44 44 m = _indent_re.match(lines[0])
45 45 if not m:
46 46 return lines
47 47 space = m.group(0)
48 48 n = len(space)
49 49 return [l[n:] if l.startswith(space) else l
50 50 for l in lines]
51 51
52 52 class PromptStripper:
53 53 """Remove matching input prompts from a block of input.
54 54
55 55 Parameters
56 56 ----------
57 57 prompt_re : regular expression
58 58 A regular expression matching any input prompt (including continuation,
59 59 e.g. ``...``)
60 60 initial_re : regular expression, optional
61 61 A regular expression matching only the initial prompt, but not continuation.
62 62 If no initial expression is given, prompt_re will be used everywhere.
63 63 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
64 64 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
65 65
66 66 Notes
67 67 -----
68 68
69 69 If initial_re and prompt_re differ,
70 70 only initial_re will be tested against the first line.
71 71 If any prompt is found on the first two lines,
72 72 prompts will be stripped from the rest of the block.
73 73 """
74 74 def __init__(self, prompt_re, initial_re=None):
75 75 self.prompt_re = prompt_re
76 76 self.initial_re = initial_re or prompt_re
77 77
78 78 def _strip(self, lines):
79 79 return [self.prompt_re.sub('', l, count=1) for l in lines]
80 80
81 81 def __call__(self, lines):
82 82 if not lines:
83 83 return lines
84 84 if self.initial_re.match(lines[0]) or \
85 85 (len(lines) > 1 and self.prompt_re.match(lines[1])):
86 86 return self._strip(lines)
87 87 return lines
88 88
89 89 classic_prompt = PromptStripper(
90 90 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
91 91 initial_re=re.compile(r'^>>>( |$)')
92 92 )
93 93
94 94 ipython_prompt = PromptStripper(
95 95 re.compile(
96 96 r"""
97 97 ^( # Match from the beginning of a line, either:
98 98
99 99 # 1. First-line prompt:
100 100 ((\[nav\]|\[ins\])?\ )? # Vi editing mode prompt, if it's there
101 101 In\ # The 'In' of the prompt, with a space
102 102 \[\d+\]: # Command index, as displayed in the prompt
103 103 \ # With a mandatory trailing space
104 104
105 105 | # ... or ...
106 106
107 107 # 2. The three dots of the multiline prompt
108 108 \s* # All leading whitespace characters
109 109 \.{3,}: # The three (or more) dots
110 110 \ ? # With an optional trailing space
111 111
112 112 )
113 113 """,
114 114 re.VERBOSE,
115 115 )
116 116 )
117 117
118 118
119 119 def cell_magic(lines):
120 120 if not lines or not lines[0].startswith('%%'):
121 121 return lines
122 122 if re.match(r'%%\w+\?', lines[0]):
123 123 # This case will be handled by help_end
124 124 return lines
125 125 magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
126 126 body = ''.join(lines[1:])
127 127 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
128 128 % (magic_name, first_line, body)]
129 129
130 130
131 131 def _find_assign_op(token_line) -> Optional[int]:
132 132 """Get the index of the first assignment in the line ('=' not inside brackets)
133 133
134 134 Note: We don't try to support multiple special assignment (a = b = %foo)
135 135 """
136 136 paren_level = 0
137 137 for i, ti in enumerate(token_line):
138 138 s = ti.string
139 139 if s == '=' and paren_level == 0:
140 140 return i
141 141 if s in {'(','[','{'}:
142 142 paren_level += 1
143 143 elif s in {')', ']', '}'}:
144 144 if paren_level > 0:
145 145 paren_level -= 1
146 146 return None
147 147
148 148 def find_end_of_continued_line(lines, start_line: int):
149 149 """Find the last line of a line explicitly extended using backslashes.
150 150
151 151 Uses 0-indexed line numbers.
152 152 """
153 153 end_line = start_line
154 154 while lines[end_line].endswith('\\\n'):
155 155 end_line += 1
156 156 if end_line >= len(lines):
157 157 break
158 158 return end_line
159 159
160 160 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
161 161 r"""Assemble a single line from multiple continued line pieces
162 162
163 163 Continued lines are lines ending in ``\``, and the line following the last
164 164 ``\`` in the block.
165 165
166 166 For example, this code continues over multiple lines::
167 167
168 168 if (assign_ix is not None) \
169 169 and (len(line) >= assign_ix + 2) \
170 170 and (line[assign_ix+1].string == '%') \
171 171 and (line[assign_ix+2].type == tokenize.NAME):
172 172
173 173 This statement contains four continued line pieces.
174 174 Assembling these pieces into a single line would give::
175 175
176 176 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
177 177
178 178 This uses 0-indexed line numbers. *start* is (lineno, colno).
179 179
180 180 Used to allow ``%magic`` and ``!system`` commands to be continued over
181 181 multiple lines.
182 182 """
183 183 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
184 184 return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
185 185 + [parts[-1].rstrip()]) # Strip newline from last line
186 186
187 187 class TokenTransformBase:
188 188 """Base class for transformations which examine tokens.
189 189
190 190 Special syntax should not be transformed when it occurs inside strings or
191 191 comments. This is hard to reliably avoid with regexes. The solution is to
192 192 tokenise the code as Python, and recognise the special syntax in the tokens.
193 193
194 194 IPython's special syntax is not valid Python syntax, so tokenising may go
195 195 wrong after the special syntax starts. These classes therefore find and
196 196 transform *one* instance of special syntax at a time into regular Python
197 197 syntax. After each transformation, tokens are regenerated to find the next
198 198 piece of special syntax.
199 199
200 200 Subclasses need to implement one class method (find)
201 201 and one regular method (transform).
202 202
203 203 The priority attribute can select which transformation to apply if multiple
204 204 transformers match in the same place. Lower numbers have higher priority.
205 205 This allows "%magic?" to be turned into a help call rather than a magic call.
206 206 """
207 207 # Lower numbers -> higher priority (for matches in the same location)
208 208 priority = 10
209 209
210 210 def sortby(self):
211 211 return self.start_line, self.start_col, self.priority
212 212
213 213 def __init__(self, start):
214 214 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
215 215 self.start_col = start[1]
216 216
217 217 @classmethod
218 218 def find(cls, tokens_by_line):
219 219 """Find one instance of special syntax in the provided tokens.
220 220
221 221 Tokens are grouped into logical lines for convenience,
222 222 so it is easy to e.g. look at the first token of each line.
223 223 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
224 224
225 225 This should return an instance of its class, pointing to the start
226 226 position it has found, or None if it found no match.
227 227 """
228 228 raise NotImplementedError
229 229
230 230 def transform(self, lines: List[str]):
231 231 """Transform one instance of special syntax found by ``find()``
232 232
233 233 Takes a list of strings representing physical lines,
234 234 returns a similar list of transformed lines.
235 235 """
236 236 raise NotImplementedError
237 237
238 238 class MagicAssign(TokenTransformBase):
239 239 """Transformer for assignments from magics (a = %foo)"""
240 240 @classmethod
241 241 def find(cls, tokens_by_line):
242 242 """Find the first magic assignment (a = %foo) in the cell.
243 243 """
244 244 for line in tokens_by_line:
245 245 assign_ix = _find_assign_op(line)
246 246 if (assign_ix is not None) \
247 247 and (len(line) >= assign_ix + 2) \
248 248 and (line[assign_ix+1].string == '%') \
249 249 and (line[assign_ix+2].type == tokenize.NAME):
250 250 return cls(line[assign_ix+1].start)
251 251
252 252 def transform(self, lines: List[str]):
253 253 """Transform a magic assignment found by the ``find()`` classmethod.
254 254 """
255 255 start_line, start_col = self.start_line, self.start_col
256 256 lhs = lines[start_line][:start_col]
257 257 end_line = find_end_of_continued_line(lines, start_line)
258 258 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
259 259 assert rhs.startswith('%'), rhs
260 260 magic_name, _, args = rhs[1:].partition(' ')
261 261
262 262 lines_before = lines[:start_line]
263 263 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
264 264 new_line = lhs + call + '\n'
265 265 lines_after = lines[end_line+1:]
266 266
267 267 return lines_before + [new_line] + lines_after
268 268
269 269
270 270 class SystemAssign(TokenTransformBase):
271 271 """Transformer for assignments from system commands (a = !foo)"""
272 272 @classmethod
273 273 def find(cls, tokens_by_line):
274 274 """Find the first system assignment (a = !foo) in the cell.
275 275 """
276 276 for line in tokens_by_line:
277 277 assign_ix = _find_assign_op(line)
278 278 if (assign_ix is not None) \
279 279 and not line[assign_ix].line.strip().startswith('=') \
280 280 and (len(line) >= assign_ix + 2) \
281 281 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
282 282 ix = assign_ix + 1
283 283
284 284 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
285 285 if line[ix].string == '!':
286 286 return cls(line[ix].start)
287 287 elif not line[ix].string.isspace():
288 288 break
289 289 ix += 1
290 290
291 291 def transform(self, lines: List[str]):
292 292 """Transform a system assignment found by the ``find()`` classmethod.
293 293 """
294 294 start_line, start_col = self.start_line, self.start_col
295 295
296 296 lhs = lines[start_line][:start_col]
297 297 end_line = find_end_of_continued_line(lines, start_line)
298 298 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
299 299 assert rhs.startswith('!'), rhs
300 300 cmd = rhs[1:]
301 301
302 302 lines_before = lines[:start_line]
303 303 call = "get_ipython().getoutput({!r})".format(cmd)
304 304 new_line = lhs + call + '\n'
305 305 lines_after = lines[end_line + 1:]
306 306
307 307 return lines_before + [new_line] + lines_after
308 308
309 309 # The escape sequences that define the syntax transformations IPython will
310 310 # apply to user input. These can NOT be just changed here: many regular
311 311 # expressions and other parts of the code may use their hardcoded values, and
312 312 # for all intents and purposes they constitute the 'IPython syntax', so they
313 313 # should be considered fixed.
314 314
315 315 ESC_SHELL = '!' # Send line to underlying system shell
316 316 ESC_SH_CAP = '!!' # Send line to system shell and capture output
317 317 ESC_HELP = '?' # Find information about object
318 318 ESC_HELP2 = '??' # Find extra-detailed information about object
319 319 ESC_MAGIC = '%' # Call magic function
320 320 ESC_MAGIC2 = '%%' # Call cell-magic function
321 321 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
322 322 ESC_QUOTE2 = ';' # Quote all args as a single string, call
323 323 ESC_PAREN = '/' # Call first argument with rest of line as arguments
324 324
325 325 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
326 326 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
327 327
328 328 def _make_help_call(target, esc, next_input=None):
329 329 """Prepares a pinfo(2)/psearch call from a target name and the escape
330 330 (i.e. ? or ??)"""
331 331 method = 'pinfo2' if esc == '??' \
332 332 else 'psearch' if '*' in target \
333 333 else 'pinfo'
334 334 arg = " ".join([method, target])
335 335 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
336 336 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
337 337 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
338 338 if next_input is None:
339 339 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
340 340 else:
341 341 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
342 342 (next_input, t_magic_name, t_magic_arg_s)
343 343
344 344 def _tr_help(content):
345 345 """Translate lines escaped with: ?
346 346
347 347 A naked help line should fire the intro help screen (shell.show_usage())
348 348 """
349 349 if not content:
350 350 return 'get_ipython().show_usage()'
351 351
352 352 return _make_help_call(content, '?')
353 353
354 354 def _tr_help2(content):
355 355 """Translate lines escaped with: ??
356 356
357 357 A naked help line should fire the intro help screen (shell.show_usage())
358 358 """
359 359 if not content:
360 360 return 'get_ipython().show_usage()'
361 361
362 362 return _make_help_call(content, '??')
363 363
364 364 def _tr_magic(content):
365 365 "Translate lines escaped with a percent sign: %"
366 366 name, _, args = content.partition(' ')
367 367 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
368 368
369 369 def _tr_quote(content):
370 370 "Translate lines escaped with a comma: ,"
371 371 name, _, args = content.partition(' ')
372 372 return '%s("%s")' % (name, '", "'.join(args.split()) )
373 373
374 374 def _tr_quote2(content):
375 375 "Translate lines escaped with a semicolon: ;"
376 376 name, _, args = content.partition(' ')
377 377 return '%s("%s")' % (name, args)
378 378
379 379 def _tr_paren(content):
380 380 "Translate lines escaped with a slash: /"
381 381 name, _, args = content.partition(' ')
382 382 return '%s(%s)' % (name, ", ".join(args.split()))
383 383
384 384 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
385 385 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
386 386 ESC_HELP : _tr_help,
387 387 ESC_HELP2 : _tr_help2,
388 388 ESC_MAGIC : _tr_magic,
389 389 ESC_QUOTE : _tr_quote,
390 390 ESC_QUOTE2 : _tr_quote2,
391 391 ESC_PAREN : _tr_paren }
392 392
393 393 class EscapedCommand(TokenTransformBase):
394 394 """Transformer for escaped commands like %foo, !foo, or /foo"""
395 395 @classmethod
396 396 def find(cls, tokens_by_line):
397 397 """Find the first escaped command (%foo, !foo, etc.) in the cell.
398 398 """
399 399 for line in tokens_by_line:
400 400 if not line:
401 401 continue
402 402 ix = 0
403 403 ll = len(line)
404 404 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
405 405 ix += 1
406 406 if ix >= ll:
407 407 continue
408 408 if line[ix].string in ESCAPE_SINGLES:
409 409 return cls(line[ix].start)
410 410
411 411 def transform(self, lines):
412 412 """Transform an escaped line found by the ``find()`` classmethod.
413 413 """
414 414 start_line, start_col = self.start_line, self.start_col
415 415
416 416 indent = lines[start_line][:start_col]
417 417 end_line = find_end_of_continued_line(lines, start_line)
418 418 line = assemble_continued_line(lines, (start_line, start_col), end_line)
419 419
420 420 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
421 421 escape, content = line[:2], line[2:]
422 422 else:
423 423 escape, content = line[:1], line[1:]
424 424
425 425 if escape in tr:
426 426 call = tr[escape](content)
427 427 else:
428 428 call = ''
429 429
430 430 lines_before = lines[:start_line]
431 431 new_line = indent + call + '\n'
432 432 lines_after = lines[end_line + 1:]
433 433
434 434 return lines_before + [new_line] + lines_after
435 435
436 436 _help_end_re = re.compile(r"""(%{0,2}
437 437 (?!\d)[\w*]+ # Variable name
438 438 (\.(?!\d)[\w*]+)* # .etc.etc
439 439 )
440 440 (\?\??)$ # ? or ??
441 441 """,
442 442 re.VERBOSE)
443 443
444 444 class HelpEnd(TokenTransformBase):
445 445 """Transformer for help syntax: obj? and obj??"""
446 446 # This needs to be higher priority (lower number) than EscapedCommand so
447 447 # that inspecting magics (%foo?) works.
448 448 priority = 5
449 449
450 450 def __init__(self, start, q_locn):
451 451 super().__init__(start)
452 452 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
453 453 self.q_col = q_locn[1]
454 454
455 455 @classmethod
456 456 def find(cls, tokens_by_line):
457 457 """Find the first help command (foo?) in the cell.
458 458 """
459 459 for line in tokens_by_line:
460 460 # Last token is NEWLINE; look at last but one
461 461 if len(line) > 2 and line[-2].string == '?':
462 462 # Find the first token that's not INDENT/DEDENT
463 463 ix = 0
464 464 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
465 465 ix += 1
466 466 return cls(line[ix].start, line[-2].start)
467 467
468 468 def transform(self, lines):
469 469 """Transform a help command found by the ``find()`` classmethod.
470 470 """
471 471 piece = ''.join(lines[self.start_line:self.q_line+1])
472 472 indent, content = piece[:self.start_col], piece[self.start_col:]
473 473 lines_before = lines[:self.start_line]
474 474 lines_after = lines[self.q_line + 1:]
475 475
476 476 m = _help_end_re.search(content)
477 477 if not m:
478 478 raise SyntaxError(content)
479 479 assert m is not None, content
480 480 target = m.group(1)
481 481 esc = m.group(3)
482 482
483 483 # If we're mid-command, put it back on the next prompt for the user.
484 484 next_input = None
485 485 if (not lines_before) and (not lines_after) \
486 486 and content.strip() != m.group(0):
487 487 next_input = content.rstrip('?\n')
488 488
489 489 call = _make_help_call(target, esc, next_input=next_input)
490 490 new_line = indent + call + '\n'
491 491
492 492 return lines_before + [new_line] + lines_after
493 493
494 494 def make_tokens_by_line(lines:List[str]):
495 495 """Tokenize a series of lines and group tokens by line.
496 496
497 497 The tokens for a multiline Python string or expression are grouped as one
498 498 line. All lines except the last lines should keep their line ending ('\\n',
499 499 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
500 500 for example when passing block of text to this function.
501 501
502 502 """
503 503 # NL tokens are used inside multiline expressions, but also after blank
504 504 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
505 505 # We want to group the former case together but split the latter, so we
506 506 # track parentheses level, similar to the internals of tokenize.
507 507
508 508 # reexported from token on 3.7+
509 509 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
510 510 tokens_by_line:List[List[Any]] = [[]]
511 511 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
512 512 warnings.warn("`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified")
513 513 parenlev = 0
514 514 try:
515 515 for token in tokenize.generate_tokens(iter(lines).__next__):
516 516 tokens_by_line[-1].append(token)
517 517 if (token.type == NEWLINE) \
518 518 or ((token.type == NL) and (parenlev <= 0)):
519 519 tokens_by_line.append([])
520 520 elif token.string in {'(', '[', '{'}:
521 521 parenlev += 1
522 522 elif token.string in {')', ']', '}'}:
523 523 if parenlev > 0:
524 524 parenlev -= 1
525 525 except tokenize.TokenError:
526 526 # Input ended in a multiline string or expression. That's OK for us.
527 527 pass
528 528
529 529
530 530 if not tokens_by_line[-1]:
531 531 tokens_by_line.pop()
532 532
533 533
534 534 return tokens_by_line
535 535
536 536
537 537 def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):
538 538 """Check if the depth of brackets in the list of tokens drops below 0"""
539 539 parenlev = 0
540 540 for token in tokens:
541 541 if token.string in {"(", "[", "{"}:
542 542 parenlev += 1
543 543 elif token.string in {")", "]", "}"}:
544 544 parenlev -= 1
545 545 if parenlev < 0:
546 546 return True
547 547 return False
548 548
549 549
550 550 def show_linewise_tokens(s: str):
551 551 """For investigation and debugging"""
552 552 if not s.endswith('\n'):
553 553 s += '\n'
554 554 lines = s.splitlines(keepends=True)
555 555 for line in make_tokens_by_line(lines):
556 556 print("Line -------")
557 557 for tokinfo in line:
558 558 print(" ", tokinfo)
559 559
560 560 # Arbitrary limit to prevent getting stuck in infinite loops
561 561 TRANSFORM_LOOP_LIMIT = 500
562 562
563 563 class TransformerManager:
564 564 """Applies various transformations to a cell or code block.
565 565
566 566 The key methods for external use are ``transform_cell()``
567 567 and ``check_complete()``.
568 568 """
569 569 def __init__(self):
570 570 self.cleanup_transforms = [
571 571 leading_empty_lines,
572 572 leading_indent,
573 573 classic_prompt,
574 574 ipython_prompt,
575 575 ]
576 576 self.line_transforms = [
577 577 cell_magic,
578 578 ]
579 579 self.token_transformers = [
580 580 MagicAssign,
581 581 SystemAssign,
582 582 EscapedCommand,
583 583 HelpEnd,
584 584 ]
585 585
586 586 def do_one_token_transform(self, lines):
587 587 """Find and run the transform earliest in the code.
588 588
589 589 Returns (changed, lines).
590 590
591 591 This method is called repeatedly until changed is False, indicating
592 592 that all available transformations are complete.
593 593
594 594 The tokens following IPython special syntax might not be valid, so
595 595 the transformed code is retokenised every time to identify the next
596 596 piece of special syntax. Hopefully long code cells are mostly valid
597 597 Python, not using lots of IPython special syntax, so this shouldn't be
598 598 a performance issue.
599 599 """
600 600 tokens_by_line = make_tokens_by_line(lines)
601 601 candidates = []
602 602 for transformer_cls in self.token_transformers:
603 603 transformer = transformer_cls.find(tokens_by_line)
604 604 if transformer:
605 605 candidates.append(transformer)
606 606
607 607 if not candidates:
608 608 # Nothing to transform
609 609 return False, lines
610 610 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
611 611 for transformer in ordered_transformers:
612 612 try:
613 613 return True, transformer.transform(lines)
614 614 except SyntaxError:
615 615 pass
616 616 return False, lines
617 617
618 618 def do_token_transforms(self, lines):
619 619 for _ in range(TRANSFORM_LOOP_LIMIT):
620 620 changed, lines = self.do_one_token_transform(lines)
621 621 if not changed:
622 622 return lines
623 623
624 624 raise RuntimeError("Input transformation still changing after "
625 625 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
626 626
627 627 def transform_cell(self, cell: str) -> str:
628 628 """Transforms a cell of input code"""
629 629 if not cell.endswith('\n'):
630 630 cell += '\n' # Ensure the cell has a trailing newline
631 631 lines = cell.splitlines(keepends=True)
632 632 for transform in self.cleanup_transforms + self.line_transforms:
633 633 lines = transform(lines)
634 634
635 635 lines = self.do_token_transforms(lines)
636 636 return ''.join(lines)
637 637
638 638 def check_complete(self, cell: str):
639 639 """Return whether a block of code is ready to execute, or should be continued
640 640
641 641 Parameters
642 642 ----------
643 643 source : string
644 644 Python input code, which can be multiline.
645 645
646 646 Returns
647 647 -------
648 648 status : str
649 649 One of 'complete', 'incomplete', or 'invalid' if source is not a
650 650 prefix of valid code.
651 651 indent_spaces : int or None
652 652 The number of spaces by which to indent the next line of code. If
653 653 status is not 'incomplete', this is None.
654 654 """
655 655 # Remember if the lines ends in a new line.
656 656 ends_with_newline = False
657 657 for character in reversed(cell):
658 658 if character == '\n':
659 659 ends_with_newline = True
660 660 break
661 661 elif character.strip():
662 662 break
663 663 else:
664 664 continue
665 665
666 666 if not ends_with_newline:
667 667 # Append an newline for consistent tokenization
668 668 # See https://bugs.python.org/issue33899
669 669 cell += '\n'
670 670
671 671 lines = cell.splitlines(keepends=True)
672 672
673 673 if not lines:
674 674 return 'complete', None
675 675
676 676 if lines[-1].endswith('\\'):
677 677 # Explicit backslash continuation
678 678 return 'incomplete', find_last_indent(lines)
679 679
680 680 try:
681 681 for transform in self.cleanup_transforms:
682 682 if not getattr(transform, 'has_side_effects', False):
683 683 lines = transform(lines)
684 684 except SyntaxError:
685 685 return 'invalid', None
686 686
687 687 if lines[0].startswith('%%'):
688 688 # Special case for cell magics - completion marked by blank line
689 689 if lines[-1].strip():
690 690 return 'incomplete', find_last_indent(lines)
691 691 else:
692 692 return 'complete', None
693 693
694 694 try:
695 695 for transform in self.line_transforms:
696 696 if not getattr(transform, 'has_side_effects', False):
697 697 lines = transform(lines)
698 698 lines = self.do_token_transforms(lines)
699 699 except SyntaxError:
700 700 return 'invalid', None
701 701
702 702 tokens_by_line = make_tokens_by_line(lines)
703 703
704 704 # Bail if we got one line and there are more closing parentheses than
705 705 # the opening ones
706 706 if (
707 707 len(lines) == 1
708 708 and tokens_by_line
709 709 and has_sunken_brackets(tokens_by_line[0])
710 710 ):
711 711 return "invalid", None
712 712
713 713 if not tokens_by_line:
714 714 return 'incomplete', find_last_indent(lines)
715 715
716 716 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
717 717 # We're in a multiline string or expression
718 718 return 'incomplete', find_last_indent(lines)
719 719
720 720 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
721 721
722 722 # Pop the last line which only contains DEDENTs and ENDMARKER
723 723 last_token_line = None
724 724 if {t.type for t in tokens_by_line[-1]} in [
725 725 {tokenize.DEDENT, tokenize.ENDMARKER},
726 726 {tokenize.ENDMARKER}
727 727 ] and len(tokens_by_line) > 1:
728 728 last_token_line = tokens_by_line.pop()
729 729
730 730 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
731 731 tokens_by_line[-1].pop()
732 732
733 733 if not tokens_by_line[-1]:
734 734 return 'incomplete', find_last_indent(lines)
735 735
736 736 if tokens_by_line[-1][-1].string == ':':
737 737 # The last line starts a block (e.g. 'if foo:')
738 738 ix = 0
739 739 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
740 740 ix += 1
741 741
742 742 indent = tokens_by_line[-1][ix].start[1]
743 743 return 'incomplete', indent + 4
744 744
745 745 if tokens_by_line[-1][0].line.endswith('\\'):
746 746 return 'incomplete', None
747 747
748 748 # At this point, our checks think the code is complete (or invalid).
749 749 # We'll use codeop.compile_command to check this with the real parser
750 750 try:
751 751 with warnings.catch_warnings():
752 752 warnings.simplefilter('error', SyntaxWarning)
753 753 res = compile_command(''.join(lines), symbol='exec')
754 754 except (SyntaxError, OverflowError, ValueError, TypeError,
755 755 MemoryError, SyntaxWarning):
756 756 return 'invalid', None
757 757 else:
758 758 if res is None:
759 759 return 'incomplete', find_last_indent(lines)
760 760
761 761 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
762 762 if ends_with_newline:
763 763 return 'complete', None
764 764 return 'incomplete', find_last_indent(lines)
765 765
766 766 # If there's a blank line at the end, assume we're ready to execute
767 767 if not lines[-1].strip():
768 768 return 'complete', None
769 769
770 770 return 'complete', None
771 771
772 772
773 773 def find_last_indent(lines):
774 774 m = _indent_re.match(lines[-1])
775 775 if not m:
776 776 return 0
777 777 return len(m.group(0).replace('\t', ' '*4))
778 778
779 779
780 780 class MaybeAsyncCompile(Compile):
781 781 def __init__(self, extra_flags=0):
782 782 super().__init__()
783 783 self.flags |= extra_flags
784 784
785 785 def __call__(self, *args, **kwds):
786 786 return compile(*args, **kwds)
787 787
788 788
789 789 class MaybeAsyncCommandCompiler(CommandCompiler):
790 790 def __init__(self, extra_flags=0):
791 791 self.compiler = MaybeAsyncCompile(extra_flags=extra_flags)
792 792
793 793
794 if (sys.version_info.major, sys.version_info.minor) >= (3, 8):
795 _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
796 else:
797 _extra_flags = ast.PyCF_ONLY_AST
794 _extra_flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
798 795
799 796 compile_command = MaybeAsyncCommandCompiler(extra_flags=_extra_flags)
@@ -1,1516 +1,1511 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Implementation of execution-related magic functions."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 import ast
9 9 import bdb
10 10 import builtins as builtin_mod
11 import cProfile as profile
11 12 import gc
12 13 import itertools
14 import math
13 15 import os
16 import pstats
17 import re
14 18 import shlex
15 19 import sys
16 20 import time
17 21 import timeit
18 import math
19 import re
22 from ast import Module
23 from io import StringIO
24 from logging import error
25 from pathlib import Path
20 26 from pdb import Restart
27 from warnings import warn
21 28
22 import cProfile as profile
23 import pstats
24
25 from IPython.core import oinspect
26 from IPython.core import magic_arguments
27 from IPython.core import page
29 from IPython.core import magic_arguments, oinspect, page
28 30 from IPython.core.error import UsageError
29 31 from IPython.core.macro import Macro
30 from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic,
31 line_cell_magic, on_off, needs_local_scope,
32 no_var_expand)
32 from IPython.core.magic import (
33 Magics,
34 cell_magic,
35 line_cell_magic,
36 line_magic,
37 magics_class,
38 needs_local_scope,
39 no_var_expand,
40 on_off,
41 )
33 42 from IPython.testing.skipdoctest import skip_doctest
34 from IPython.utils.contexts import preserve_keys
35 43 from IPython.utils.capture import capture_output
44 from IPython.utils.contexts import preserve_keys
36 45 from IPython.utils.ipstruct import Struct
37 46 from IPython.utils.module_paths import find_mod
38 47 from IPython.utils.path import get_py_filename, shellglob
39 48 from IPython.utils.timing import clock, clock2
40 from warnings import warn
41 from logging import error
42 from pathlib import Path
43 from io import StringIO
44 from pathlib import Path
45
46 if sys.version_info > (3,8):
47 from ast import Module
48 else :
49 # mock the new API, ignore second argument
50 # see https://github.com/ipython/ipython/issues/11590
51 from ast import Module as OriginalModule
52 Module = lambda nodelist, type_ignores: OriginalModule(nodelist)
53
54 49
55 50 #-----------------------------------------------------------------------------
56 51 # Magic implementation classes
57 52 #-----------------------------------------------------------------------------
58 53
59 54
60 55 class TimeitResult(object):
61 56 """
62 57 Object returned by the timeit magic with info about the run.
63 58
64 59 Contains the following attributes :
65 60
66 61 loops: (int) number of loops done per measurement
67 62 repeat: (int) number of times the measurement has been repeated
68 63 best: (float) best execution time / number
69 64 all_runs: (list of float) execution time of each run (in s)
70 65 compile_time: (float) time of statement compilation (s)
71 66
72 67 """
73 68 def __init__(self, loops, repeat, best, worst, all_runs, compile_time, precision):
74 69 self.loops = loops
75 70 self.repeat = repeat
76 71 self.best = best
77 72 self.worst = worst
78 73 self.all_runs = all_runs
79 74 self.compile_time = compile_time
80 75 self._precision = precision
81 76 self.timings = [ dt / self.loops for dt in all_runs]
82 77
83 78 @property
84 79 def average(self):
85 80 return math.fsum(self.timings) / len(self.timings)
86 81
87 82 @property
88 83 def stdev(self):
89 84 mean = self.average
90 85 return (math.fsum([(x - mean) ** 2 for x in self.timings]) / len(self.timings)) ** 0.5
91 86
92 87 def __str__(self):
93 88 pm = '+-'
94 89 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
95 90 try:
96 91 u'\xb1'.encode(sys.stdout.encoding)
97 92 pm = u'\xb1'
98 93 except:
99 94 pass
100 95 return (
101 96 u"{mean} {pm} {std} per loop (mean {pm} std. dev. of {runs} run{run_plural}, {loops} loop{loop_plural} each)"
102 97 .format(
103 98 pm = pm,
104 99 runs = self.repeat,
105 100 loops = self.loops,
106 101 loop_plural = "" if self.loops == 1 else "s",
107 102 run_plural = "" if self.repeat == 1 else "s",
108 103 mean = _format_time(self.average, self._precision),
109 104 std = _format_time(self.stdev, self._precision))
110 105 )
111 106
112 107 def _repr_pretty_(self, p , cycle):
113 108 unic = self.__str__()
114 109 p.text(u'<TimeitResult : '+unic+u'>')
115 110
116 111
117 112 class TimeitTemplateFiller(ast.NodeTransformer):
118 113 """Fill in the AST template for timing execution.
119 114
120 115 This is quite closely tied to the template definition, which is in
121 116 :meth:`ExecutionMagics.timeit`.
122 117 """
123 118 def __init__(self, ast_setup, ast_stmt):
124 119 self.ast_setup = ast_setup
125 120 self.ast_stmt = ast_stmt
126 121
127 122 def visit_FunctionDef(self, node):
128 123 "Fill in the setup statement"
129 124 self.generic_visit(node)
130 125 if node.name == "inner":
131 126 node.body[:1] = self.ast_setup.body
132 127
133 128 return node
134 129
135 130 def visit_For(self, node):
136 131 "Fill in the statement to be timed"
137 132 if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt':
138 133 node.body = self.ast_stmt.body
139 134 return node
140 135
141 136
142 137 class Timer(timeit.Timer):
143 138 """Timer class that explicitly uses self.inner
144 139
145 140 which is an undocumented implementation detail of CPython,
146 141 not shared by PyPy.
147 142 """
148 143 # Timer.timeit copied from CPython 3.4.2
149 144 def timeit(self, number=timeit.default_number):
150 145 """Time 'number' executions of the main statement.
151 146
152 147 To be precise, this executes the setup statement once, and
153 148 then returns the time it takes to execute the main statement
154 149 a number of times, as a float measured in seconds. The
155 150 argument is the number of times through the loop, defaulting
156 151 to one million. The main statement, the setup statement and
157 152 the timer function to be used are passed to the constructor.
158 153 """
159 154 it = itertools.repeat(None, number)
160 155 gcold = gc.isenabled()
161 156 gc.disable()
162 157 try:
163 158 timing = self.inner(it, self.timer)
164 159 finally:
165 160 if gcold:
166 161 gc.enable()
167 162 return timing
168 163
169 164
170 165 @magics_class
171 166 class ExecutionMagics(Magics):
172 167 """Magics related to code execution, debugging, profiling, etc.
173 168
174 169 """
175 170
176 171 def __init__(self, shell):
177 172 super(ExecutionMagics, self).__init__(shell)
178 173 # Default execution function used to actually run user code.
179 174 self.default_runner = None
180 175
181 176 @skip_doctest
182 177 @no_var_expand
183 178 @line_cell_magic
184 179 def prun(self, parameter_s='', cell=None):
185 180
186 181 """Run a statement through the python code profiler.
187 182
188 183 Usage, in line mode:
189 184 %prun [options] statement
190 185
191 186 Usage, in cell mode:
192 187 %%prun [options] [statement]
193 188 code...
194 189 code...
195 190
196 191 In cell mode, the additional code lines are appended to the (possibly
197 192 empty) statement in the first line. Cell mode allows you to easily
198 193 profile multiline blocks without having to put them in a separate
199 194 function.
200 195
201 196 The given statement (which doesn't require quote marks) is run via the
202 197 python profiler in a manner similar to the profile.run() function.
203 198 Namespaces are internally managed to work correctly; profile.run
204 199 cannot be used in IPython because it makes certain assumptions about
205 200 namespaces which do not hold under IPython.
206 201
207 202 Options:
208 203
209 204 -l <limit>
210 205 you can place restrictions on what or how much of the
211 206 profile gets printed. The limit value can be:
212 207
213 208 * A string: only information for function names containing this string
214 209 is printed.
215 210
216 211 * An integer: only these many lines are printed.
217 212
218 213 * A float (between 0 and 1): this fraction of the report is printed
219 214 (for example, use a limit of 0.4 to see the topmost 40% only).
220 215
221 216 You can combine several limits with repeated use of the option. For
222 217 example, ``-l __init__ -l 5`` will print only the topmost 5 lines of
223 218 information about class constructors.
224 219
225 220 -r
226 221 return the pstats.Stats object generated by the profiling. This
227 222 object has all the information about the profile in it, and you can
228 223 later use it for further analysis or in other functions.
229 224
230 225 -s <key>
231 226 sort profile by given key. You can provide more than one key
232 227 by using the option several times: '-s key1 -s key2 -s key3...'. The
233 228 default sorting key is 'time'.
234 229
235 230 The following is copied verbatim from the profile documentation
236 231 referenced below:
237 232
238 233 When more than one key is provided, additional keys are used as
239 234 secondary criteria when the there is equality in all keys selected
240 235 before them.
241 236
242 237 Abbreviations can be used for any key names, as long as the
243 238 abbreviation is unambiguous. The following are the keys currently
244 239 defined:
245 240
246 241 ============ =====================
247 242 Valid Arg Meaning
248 243 ============ =====================
249 244 "calls" call count
250 245 "cumulative" cumulative time
251 246 "file" file name
252 247 "module" file name
253 248 "pcalls" primitive call count
254 249 "line" line number
255 250 "name" function name
256 251 "nfl" name/file/line
257 252 "stdname" standard name
258 253 "time" internal time
259 254 ============ =====================
260 255
261 256 Note that all sorts on statistics are in descending order (placing
262 257 most time consuming items first), where as name, file, and line number
263 258 searches are in ascending order (i.e., alphabetical). The subtle
264 259 distinction between "nfl" and "stdname" is that the standard name is a
265 260 sort of the name as printed, which means that the embedded line
266 261 numbers get compared in an odd way. For example, lines 3, 20, and 40
267 262 would (if the file names were the same) appear in the string order
268 263 "20" "3" and "40". In contrast, "nfl" does a numeric compare of the
269 264 line numbers. In fact, sort_stats("nfl") is the same as
270 265 sort_stats("name", "file", "line").
271 266
272 267 -T <filename>
273 268 save profile results as shown on screen to a text
274 269 file. The profile is still shown on screen.
275 270
276 271 -D <filename>
277 272 save (via dump_stats) profile statistics to given
278 273 filename. This data is in a format understood by the pstats module, and
279 274 is generated by a call to the dump_stats() method of profile
280 275 objects. The profile is still shown on screen.
281 276
282 277 -q
283 278 suppress output to the pager. Best used with -T and/or -D above.
284 279
285 280 If you want to run complete programs under the profiler's control, use
286 281 ``%run -p [prof_opts] filename.py [args to program]`` where prof_opts
287 282 contains profiler specific options as described here.
288 283
289 284 You can read the complete documentation for the profile module with::
290 285
291 286 In [1]: import profile; profile.help()
292 287
293 288 .. versionchanged:: 7.3
294 289 User variables are no longer expanded,
295 290 the magic line is always left unmodified.
296 291
297 292 """
298 293 opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q',
299 294 list_all=True, posix=False)
300 295 if cell is not None:
301 296 arg_str += '\n' + cell
302 297 arg_str = self.shell.transform_cell(arg_str)
303 298 return self._run_with_profiler(arg_str, opts, self.shell.user_ns)
304 299
305 300 def _run_with_profiler(self, code, opts, namespace):
306 301 """
307 302 Run `code` with profiler. Used by ``%prun`` and ``%run -p``.
308 303
309 304 Parameters
310 305 ----------
311 306 code : str
312 307 Code to be executed.
313 308 opts : Struct
314 309 Options parsed by `self.parse_options`.
315 310 namespace : dict
316 311 A dictionary for Python namespace (e.g., `self.shell.user_ns`).
317 312
318 313 """
319 314
320 315 # Fill default values for unspecified options:
321 316 opts.merge(Struct(D=[''], l=[], s=['time'], T=['']))
322 317
323 318 prof = profile.Profile()
324 319 try:
325 320 prof = prof.runctx(code, namespace, namespace)
326 321 sys_exit = ''
327 322 except SystemExit:
328 323 sys_exit = """*** SystemExit exception caught in code being profiled."""
329 324
330 325 stats = pstats.Stats(prof).strip_dirs().sort_stats(*opts.s)
331 326
332 327 lims = opts.l
333 328 if lims:
334 329 lims = [] # rebuild lims with ints/floats/strings
335 330 for lim in opts.l:
336 331 try:
337 332 lims.append(int(lim))
338 333 except ValueError:
339 334 try:
340 335 lims.append(float(lim))
341 336 except ValueError:
342 337 lims.append(lim)
343 338
344 339 # Trap output.
345 340 stdout_trap = StringIO()
346 341 stats_stream = stats.stream
347 342 try:
348 343 stats.stream = stdout_trap
349 344 stats.print_stats(*lims)
350 345 finally:
351 346 stats.stream = stats_stream
352 347
353 348 output = stdout_trap.getvalue()
354 349 output = output.rstrip()
355 350
356 351 if 'q' not in opts:
357 352 page.page(output)
358 353 print(sys_exit, end=' ')
359 354
360 355 dump_file = opts.D[0]
361 356 text_file = opts.T[0]
362 357 if dump_file:
363 358 prof.dump_stats(dump_file)
364 359 print(
365 360 f"\n*** Profile stats marshalled to file {repr(dump_file)}.{sys_exit}"
366 361 )
367 362 if text_file:
368 363 pfile = Path(text_file)
369 364 pfile.touch(exist_ok=True)
370 365 pfile.write_text(output)
371 366
372 367 print(
373 368 f"\n*** Profile printout saved to text file {repr(text_file)}.{sys_exit}"
374 369 )
375 370
376 371 if 'r' in opts:
377 372 return stats
378 373
379 374 return None
380 375
381 376 @line_magic
382 377 def pdb(self, parameter_s=''):
383 378 """Control the automatic calling of the pdb interactive debugger.
384 379
385 380 Call as '%pdb on', '%pdb 1', '%pdb off' or '%pdb 0'. If called without
386 381 argument it works as a toggle.
387 382
388 383 When an exception is triggered, IPython can optionally call the
389 384 interactive pdb debugger after the traceback printout. %pdb toggles
390 385 this feature on and off.
391 386
392 387 The initial state of this feature is set in your configuration
393 388 file (the option is ``InteractiveShell.pdb``).
394 389
395 390 If you want to just activate the debugger AFTER an exception has fired,
396 391 without having to type '%pdb on' and rerunning your code, you can use
397 392 the %debug magic."""
398 393
399 394 par = parameter_s.strip().lower()
400 395
401 396 if par:
402 397 try:
403 398 new_pdb = {'off':0,'0':0,'on':1,'1':1}[par]
404 399 except KeyError:
405 400 print ('Incorrect argument. Use on/1, off/0, '
406 401 'or nothing for a toggle.')
407 402 return
408 403 else:
409 404 # toggle
410 405 new_pdb = not self.shell.call_pdb
411 406
412 407 # set on the shell
413 408 self.shell.call_pdb = new_pdb
414 409 print('Automatic pdb calling has been turned',on_off(new_pdb))
415 410
416 411 @magic_arguments.magic_arguments()
417 412 @magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
418 413 help="""
419 414 Set break point at LINE in FILE.
420 415 """
421 416 )
422 417 @magic_arguments.argument('statement', nargs='*',
423 418 help="""
424 419 Code to run in debugger.
425 420 You can omit this in cell magic mode.
426 421 """
427 422 )
428 423 @no_var_expand
429 424 @line_cell_magic
430 425 def debug(self, line='', cell=None):
431 426 """Activate the interactive debugger.
432 427
433 428 This magic command support two ways of activating debugger.
434 429 One is to activate debugger before executing code. This way, you
435 430 can set a break point, to step through the code from the point.
436 431 You can use this mode by giving statements to execute and optionally
437 432 a breakpoint.
438 433
439 434 The other one is to activate debugger in post-mortem mode. You can
440 435 activate this mode simply running %debug without any argument.
441 436 If an exception has just occurred, this lets you inspect its stack
442 437 frames interactively. Note that this will always work only on the last
443 438 traceback that occurred, so you must call this quickly after an
444 439 exception that you wish to inspect has fired, because if another one
445 440 occurs, it clobbers the previous one.
446 441
447 442 If you want IPython to automatically do this on every exception, see
448 443 the %pdb magic for more details.
449 444
450 445 .. versionchanged:: 7.3
451 446 When running code, user variables are no longer expanded,
452 447 the magic line is always left unmodified.
453 448
454 449 """
455 450 args = magic_arguments.parse_argstring(self.debug, line)
456 451
457 452 if not (args.breakpoint or args.statement or cell):
458 453 self._debug_post_mortem()
459 454 elif not (args.breakpoint or cell):
460 455 # If there is no breakpoints, the line is just code to execute
461 456 self._debug_exec(line, None)
462 457 else:
463 458 # Here we try to reconstruct the code from the output of
464 459 # parse_argstring. This might not work if the code has spaces
465 460 # For example this fails for `print("a b")`
466 461 code = "\n".join(args.statement)
467 462 if cell:
468 463 code += "\n" + cell
469 464 self._debug_exec(code, args.breakpoint)
470 465
471 466 def _debug_post_mortem(self):
472 467 self.shell.debugger(force=True)
473 468
474 469 def _debug_exec(self, code, breakpoint):
475 470 if breakpoint:
476 471 (filename, bp_line) = breakpoint.rsplit(':', 1)
477 472 bp_line = int(bp_line)
478 473 else:
479 474 (filename, bp_line) = (None, None)
480 475 self._run_with_debugger(code, self.shell.user_ns, filename, bp_line)
481 476
482 477 @line_magic
483 478 def tb(self, s):
484 479 """Print the last traceback.
485 480
486 481 Optionally, specify an exception reporting mode, tuning the
487 482 verbosity of the traceback. By default the currently-active exception
488 483 mode is used. See %xmode for changing exception reporting modes.
489 484
490 485 Valid modes: Plain, Context, Verbose, and Minimal.
491 486 """
492 487 interactive_tb = self.shell.InteractiveTB
493 488 if s:
494 489 # Switch exception reporting mode for this one call.
495 490 # Ensure it is switched back.
496 491 def xmode_switch_err(name):
497 492 warn('Error changing %s exception modes.\n%s' %
498 493 (name,sys.exc_info()[1]))
499 494
500 495 new_mode = s.strip().capitalize()
501 496 original_mode = interactive_tb.mode
502 497 try:
503 498 try:
504 499 interactive_tb.set_mode(mode=new_mode)
505 500 except Exception:
506 501 xmode_switch_err('user')
507 502 else:
508 503 self.shell.showtraceback()
509 504 finally:
510 505 interactive_tb.set_mode(mode=original_mode)
511 506 else:
512 507 self.shell.showtraceback()
513 508
514 509 @skip_doctest
515 510 @line_magic
516 511 def run(self, parameter_s='', runner=None,
517 512 file_finder=get_py_filename):
518 513 """Run the named file inside IPython as a program.
519 514
520 515 Usage::
521 516
522 517 %run [-n -i -e -G]
523 518 [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )]
524 519 ( -m mod | filename ) [args]
525 520
526 521 The filename argument should be either a pure Python script (with
527 522 extension ``.py``), or a file with custom IPython syntax (such as
528 523 magics). If the latter, the file can be either a script with ``.ipy``
529 524 extension, or a Jupyter notebook with ``.ipynb`` extension. When running
530 525 a Jupyter notebook, the output from print statements and other
531 526 displayed objects will appear in the terminal (even matplotlib figures
532 527 will open, if a terminal-compliant backend is being used). Note that,
533 528 at the system command line, the ``jupyter run`` command offers similar
534 529 functionality for executing notebooks (albeit currently with some
535 530 differences in supported options).
536 531
537 532 Parameters after the filename are passed as command-line arguments to
538 533 the program (put in sys.argv). Then, control returns to IPython's
539 534 prompt.
540 535
541 536 This is similar to running at a system prompt ``python file args``,
542 537 but with the advantage of giving you IPython's tracebacks, and of
543 538 loading all variables into your interactive namespace for further use
544 539 (unless -p is used, see below).
545 540
546 541 The file is executed in a namespace initially consisting only of
547 542 ``__name__=='__main__'`` and sys.argv constructed as indicated. It thus
548 543 sees its environment as if it were being run as a stand-alone program
549 544 (except for sharing global objects such as previously imported
550 545 modules). But after execution, the IPython interactive namespace gets
551 546 updated with all variables defined in the program (except for __name__
552 547 and sys.argv). This allows for very convenient loading of code for
553 548 interactive work, while giving each program a 'clean sheet' to run in.
554 549
555 550 Arguments are expanded using shell-like glob match. Patterns
556 551 '*', '?', '[seq]' and '[!seq]' can be used. Additionally,
557 552 tilde '~' will be expanded into user's home directory. Unlike
558 553 real shells, quotation does not suppress expansions. Use
559 554 *two* back slashes (e.g. ``\\\\*``) to suppress expansions.
560 555 To completely disable these expansions, you can use -G flag.
561 556
562 557 On Windows systems, the use of single quotes `'` when specifying
563 558 a file is not supported. Use double quotes `"`.
564 559
565 560 Options:
566 561
567 562 -n
568 563 __name__ is NOT set to '__main__', but to the running file's name
569 564 without extension (as python does under import). This allows running
570 565 scripts and reloading the definitions in them without calling code
571 566 protected by an ``if __name__ == "__main__"`` clause.
572 567
573 568 -i
574 569 run the file in IPython's namespace instead of an empty one. This
575 570 is useful if you are experimenting with code written in a text editor
576 571 which depends on variables defined interactively.
577 572
578 573 -e
579 574 ignore sys.exit() calls or SystemExit exceptions in the script
580 575 being run. This is particularly useful if IPython is being used to
581 576 run unittests, which always exit with a sys.exit() call. In such
582 577 cases you are interested in the output of the test results, not in
583 578 seeing a traceback of the unittest module.
584 579
585 580 -t
586 581 print timing information at the end of the run. IPython will give
587 582 you an estimated CPU time consumption for your script, which under
588 583 Unix uses the resource module to avoid the wraparound problems of
589 584 time.clock(). Under Unix, an estimate of time spent on system tasks
590 585 is also given (for Windows platforms this is reported as 0.0).
591 586
592 587 If -t is given, an additional ``-N<N>`` option can be given, where <N>
593 588 must be an integer indicating how many times you want the script to
594 589 run. The final timing report will include total and per run results.
595 590
596 591 For example (testing the script uniq_stable.py)::
597 592
598 593 In [1]: run -t uniq_stable
599 594
600 595 IPython CPU timings (estimated):
601 596 User : 0.19597 s.
602 597 System: 0.0 s.
603 598
604 599 In [2]: run -t -N5 uniq_stable
605 600
606 601 IPython CPU timings (estimated):
607 602 Total runs performed: 5
608 603 Times : Total Per run
609 604 User : 0.910862 s, 0.1821724 s.
610 605 System: 0.0 s, 0.0 s.
611 606
612 607 -d
613 608 run your program under the control of pdb, the Python debugger.
614 609 This allows you to execute your program step by step, watch variables,
615 610 etc. Internally, what IPython does is similar to calling::
616 611
617 612 pdb.run('execfile("YOURFILENAME")')
618 613
619 614 with a breakpoint set on line 1 of your file. You can change the line
620 615 number for this automatic breakpoint to be <N> by using the -bN option
621 616 (where N must be an integer). For example::
622 617
623 618 %run -d -b40 myscript
624 619
625 620 will set the first breakpoint at line 40 in myscript.py. Note that
626 621 the first breakpoint must be set on a line which actually does
627 622 something (not a comment or docstring) for it to stop execution.
628 623
629 624 Or you can specify a breakpoint in a different file::
630 625
631 626 %run -d -b myotherfile.py:20 myscript
632 627
633 628 When the pdb debugger starts, you will see a (Pdb) prompt. You must
634 629 first enter 'c' (without quotes) to start execution up to the first
635 630 breakpoint.
636 631
637 632 Entering 'help' gives information about the use of the debugger. You
638 633 can easily see pdb's full documentation with "import pdb;pdb.help()"
639 634 at a prompt.
640 635
641 636 -p
642 637 run program under the control of the Python profiler module (which
643 638 prints a detailed report of execution times, function calls, etc).
644 639
645 640 You can pass other options after -p which affect the behavior of the
646 641 profiler itself. See the docs for %prun for details.
647 642
648 643 In this mode, the program's variables do NOT propagate back to the
649 644 IPython interactive namespace (because they remain in the namespace
650 645 where the profiler executes them).
651 646
652 647 Internally this triggers a call to %prun, see its documentation for
653 648 details on the options available specifically for profiling.
654 649
655 650 There is one special usage for which the text above doesn't apply:
656 651 if the filename ends with .ipy[nb], the file is run as ipython script,
657 652 just as if the commands were written on IPython prompt.
658 653
659 654 -m
660 655 specify module name to load instead of script path. Similar to
661 656 the -m option for the python interpreter. Use this option last if you
662 657 want to combine with other %run options. Unlike the python interpreter
663 658 only source modules are allowed no .pyc or .pyo files.
664 659 For example::
665 660
666 661 %run -m example
667 662
668 663 will run the example module.
669 664
670 665 -G
671 666 disable shell-like glob expansion of arguments.
672 667
673 668 """
674 669
675 670 # Logic to handle issue #3664
676 671 # Add '--' after '-m <module_name>' to ignore additional args passed to a module.
677 672 if '-m' in parameter_s and '--' not in parameter_s:
678 673 argv = shlex.split(parameter_s, posix=(os.name == 'posix'))
679 674 for idx, arg in enumerate(argv):
680 675 if arg and arg.startswith('-') and arg != '-':
681 676 if arg == '-m':
682 677 argv.insert(idx + 2, '--')
683 678 break
684 679 else:
685 680 # Positional arg, break
686 681 break
687 682 parameter_s = ' '.join(shlex.quote(arg) for arg in argv)
688 683
689 684 # get arguments and set sys.argv for program to be run.
690 685 opts, arg_lst = self.parse_options(parameter_s,
691 686 'nidtN:b:pD:l:rs:T:em:G',
692 687 mode='list', list_all=1)
693 688 if "m" in opts:
694 689 modulename = opts["m"][0]
695 690 modpath = find_mod(modulename)
696 691 if modpath is None:
697 692 msg = '%r is not a valid modulename on sys.path'%modulename
698 693 raise Exception(msg)
699 694 arg_lst = [modpath] + arg_lst
700 695 try:
701 696 fpath = None # initialize to make sure fpath is in scope later
702 697 fpath = arg_lst[0]
703 698 filename = file_finder(fpath)
704 699 except IndexError as e:
705 700 msg = 'you must provide at least a filename.'
706 701 raise Exception(msg) from e
707 702 except IOError as e:
708 703 try:
709 704 msg = str(e)
710 705 except UnicodeError:
711 706 msg = e.message
712 707 if os.name == 'nt' and re.match(r"^'.*'$",fpath):
713 708 warn('For Windows, use double quotes to wrap a filename: %run "mypath\\myfile.py"')
714 709 raise Exception(msg) from e
715 710 except TypeError:
716 711 if fpath in sys.meta_path:
717 712 filename = ""
718 713 else:
719 714 raise
720 715
721 716 if filename.lower().endswith(('.ipy', '.ipynb')):
722 717 with preserve_keys(self.shell.user_ns, '__file__'):
723 718 self.shell.user_ns['__file__'] = filename
724 719 self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
725 720 return
726 721
727 722 # Control the response to exit() calls made by the script being run
728 723 exit_ignore = 'e' in opts
729 724
730 725 # Make sure that the running script gets a proper sys.argv as if it
731 726 # were run from a system shell.
732 727 save_argv = sys.argv # save it for later restoring
733 728
734 729 if 'G' in opts:
735 730 args = arg_lst[1:]
736 731 else:
737 732 # tilde and glob expansion
738 733 args = shellglob(map(os.path.expanduser, arg_lst[1:]))
739 734
740 735 sys.argv = [filename] + args # put in the proper filename
741 736
742 737 if 'n' in opts:
743 738 name = Path(filename).stem
744 739 else:
745 740 name = '__main__'
746 741
747 742 if 'i' in opts:
748 743 # Run in user's interactive namespace
749 744 prog_ns = self.shell.user_ns
750 745 __name__save = self.shell.user_ns['__name__']
751 746 prog_ns['__name__'] = name
752 747 main_mod = self.shell.user_module
753 748
754 749 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
755 750 # set the __file__ global in the script's namespace
756 751 # TK: Is this necessary in interactive mode?
757 752 prog_ns['__file__'] = filename
758 753 else:
759 754 # Run in a fresh, empty namespace
760 755
761 756 # The shell MUST hold a reference to prog_ns so after %run
762 757 # exits, the python deletion mechanism doesn't zero it out
763 758 # (leaving dangling references). See interactiveshell for details
764 759 main_mod = self.shell.new_main_mod(filename, name)
765 760 prog_ns = main_mod.__dict__
766 761
767 762 # pickle fix. See interactiveshell for an explanation. But we need to
768 763 # make sure that, if we overwrite __main__, we replace it at the end
769 764 main_mod_name = prog_ns['__name__']
770 765
771 766 if main_mod_name == '__main__':
772 767 restore_main = sys.modules['__main__']
773 768 else:
774 769 restore_main = False
775 770
776 771 # This needs to be undone at the end to prevent holding references to
777 772 # every single object ever created.
778 773 sys.modules[main_mod_name] = main_mod
779 774
780 775 if 'p' in opts or 'd' in opts:
781 776 if 'm' in opts:
782 777 code = 'run_module(modulename, prog_ns)'
783 778 code_ns = {
784 779 'run_module': self.shell.safe_run_module,
785 780 'prog_ns': prog_ns,
786 781 'modulename': modulename,
787 782 }
788 783 else:
789 784 if 'd' in opts:
790 785 # allow exceptions to raise in debug mode
791 786 code = 'execfile(filename, prog_ns, raise_exceptions=True)'
792 787 else:
793 788 code = 'execfile(filename, prog_ns)'
794 789 code_ns = {
795 790 'execfile': self.shell.safe_execfile,
796 791 'prog_ns': prog_ns,
797 792 'filename': get_py_filename(filename),
798 793 }
799 794
800 795 try:
801 796 stats = None
802 797 if 'p' in opts:
803 798 stats = self._run_with_profiler(code, opts, code_ns)
804 799 else:
805 800 if 'd' in opts:
806 801 bp_file, bp_line = parse_breakpoint(
807 802 opts.get('b', ['1'])[0], filename)
808 803 self._run_with_debugger(
809 804 code, code_ns, filename, bp_line, bp_file)
810 805 else:
811 806 if 'm' in opts:
812 807 def run():
813 808 self.shell.safe_run_module(modulename, prog_ns)
814 809 else:
815 810 if runner is None:
816 811 runner = self.default_runner
817 812 if runner is None:
818 813 runner = self.shell.safe_execfile
819 814
820 815 def run():
821 816 runner(filename, prog_ns, prog_ns,
822 817 exit_ignore=exit_ignore)
823 818
824 819 if 't' in opts:
825 820 # timed execution
826 821 try:
827 822 nruns = int(opts['N'][0])
828 823 if nruns < 1:
829 824 error('Number of runs must be >=1')
830 825 return
831 826 except (KeyError):
832 827 nruns = 1
833 828 self._run_with_timing(run, nruns)
834 829 else:
835 830 # regular execution
836 831 run()
837 832
838 833 if 'i' in opts:
839 834 self.shell.user_ns['__name__'] = __name__save
840 835 else:
841 836 # update IPython interactive namespace
842 837
843 838 # Some forms of read errors on the file may mean the
844 839 # __name__ key was never set; using pop we don't have to
845 840 # worry about a possible KeyError.
846 841 prog_ns.pop('__name__', None)
847 842
848 843 with preserve_keys(self.shell.user_ns, '__file__'):
849 844 self.shell.user_ns.update(prog_ns)
850 845 finally:
851 846 # It's a bit of a mystery why, but __builtins__ can change from
852 847 # being a module to becoming a dict missing some key data after
853 848 # %run. As best I can see, this is NOT something IPython is doing
854 849 # at all, and similar problems have been reported before:
855 850 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
856 851 # Since this seems to be done by the interpreter itself, the best
857 852 # we can do is to at least restore __builtins__ for the user on
858 853 # exit.
859 854 self.shell.user_ns['__builtins__'] = builtin_mod
860 855
861 856 # Ensure key global structures are restored
862 857 sys.argv = save_argv
863 858 if restore_main:
864 859 sys.modules['__main__'] = restore_main
865 860 if '__mp_main__' in sys.modules:
866 861 sys.modules['__mp_main__'] = restore_main
867 862 else:
868 863 # Remove from sys.modules the reference to main_mod we'd
869 864 # added. Otherwise it will trap references to objects
870 865 # contained therein.
871 866 del sys.modules[main_mod_name]
872 867
873 868 return stats
874 869
875 870 def _run_with_debugger(self, code, code_ns, filename=None,
876 871 bp_line=None, bp_file=None):
877 872 """
878 873 Run `code` in debugger with a break point.
879 874
880 875 Parameters
881 876 ----------
882 877 code : str
883 878 Code to execute.
884 879 code_ns : dict
885 880 A namespace in which `code` is executed.
886 881 filename : str
887 882 `code` is ran as if it is in `filename`.
888 883 bp_line : int, optional
889 884 Line number of the break point.
890 885 bp_file : str, optional
891 886 Path to the file in which break point is specified.
892 887 `filename` is used if not given.
893 888
894 889 Raises
895 890 ------
896 891 UsageError
897 892 If the break point given by `bp_line` is not valid.
898 893
899 894 """
900 895 deb = self.shell.InteractiveTB.pdb
901 896 if not deb:
902 897 self.shell.InteractiveTB.pdb = self.shell.InteractiveTB.debugger_cls()
903 898 deb = self.shell.InteractiveTB.pdb
904 899
905 900 # deb.checkline() fails if deb.curframe exists but is None; it can
906 901 # handle it not existing. https://github.com/ipython/ipython/issues/10028
907 902 if hasattr(deb, 'curframe'):
908 903 del deb.curframe
909 904
910 905 # reset Breakpoint state, which is moronically kept
911 906 # in a class
912 907 bdb.Breakpoint.next = 1
913 908 bdb.Breakpoint.bplist = {}
914 909 bdb.Breakpoint.bpbynumber = [None]
915 910 deb.clear_all_breaks()
916 911 if bp_line is not None:
917 912 # Set an initial breakpoint to stop execution
918 913 maxtries = 10
919 914 bp_file = bp_file or filename
920 915 checkline = deb.checkline(bp_file, bp_line)
921 916 if not checkline:
922 917 for bp in range(bp_line + 1, bp_line + maxtries + 1):
923 918 if deb.checkline(bp_file, bp):
924 919 break
925 920 else:
926 921 msg = ("\nI failed to find a valid line to set "
927 922 "a breakpoint\n"
928 923 "after trying up to line: %s.\n"
929 924 "Please set a valid breakpoint manually "
930 925 "with the -b option." % bp)
931 926 raise UsageError(msg)
932 927 # if we find a good linenumber, set the breakpoint
933 928 deb.do_break('%s:%s' % (bp_file, bp_line))
934 929
935 930 if filename:
936 931 # Mimic Pdb._runscript(...)
937 932 deb._wait_for_mainpyfile = True
938 933 deb.mainpyfile = deb.canonic(filename)
939 934
940 935 # Start file run
941 936 print("NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt)
942 937 try:
943 938 if filename:
944 939 # save filename so it can be used by methods on the deb object
945 940 deb._exec_filename = filename
946 941 while True:
947 942 try:
948 943 trace = sys.gettrace()
949 944 deb.run(code, code_ns)
950 945 except Restart:
951 946 print("Restarting")
952 947 if filename:
953 948 deb._wait_for_mainpyfile = True
954 949 deb.mainpyfile = deb.canonic(filename)
955 950 continue
956 951 else:
957 952 break
958 953 finally:
959 954 sys.settrace(trace)
960 955
961 956
962 957 except:
963 958 etype, value, tb = sys.exc_info()
964 959 # Skip three frames in the traceback: the %run one,
965 960 # one inside bdb.py, and the command-line typed by the
966 961 # user (run by exec in pdb itself).
967 962 self.shell.InteractiveTB(etype, value, tb, tb_offset=3)
968 963
969 964 @staticmethod
970 965 def _run_with_timing(run, nruns):
971 966 """
972 967 Run function `run` and print timing information.
973 968
974 969 Parameters
975 970 ----------
976 971 run : callable
977 972 Any callable object which takes no argument.
978 973 nruns : int
979 974 Number of times to execute `run`.
980 975
981 976 """
982 977 twall0 = time.perf_counter()
983 978 if nruns == 1:
984 979 t0 = clock2()
985 980 run()
986 981 t1 = clock2()
987 982 t_usr = t1[0] - t0[0]
988 983 t_sys = t1[1] - t0[1]
989 984 print("\nIPython CPU timings (estimated):")
990 985 print(" User : %10.2f s." % t_usr)
991 986 print(" System : %10.2f s." % t_sys)
992 987 else:
993 988 runs = range(nruns)
994 989 t0 = clock2()
995 990 for nr in runs:
996 991 run()
997 992 t1 = clock2()
998 993 t_usr = t1[0] - t0[0]
999 994 t_sys = t1[1] - t0[1]
1000 995 print("\nIPython CPU timings (estimated):")
1001 996 print("Total runs performed:", nruns)
1002 997 print(" Times : %10s %10s" % ('Total', 'Per run'))
1003 998 print(" User : %10.2f s, %10.2f s." % (t_usr, t_usr / nruns))
1004 999 print(" System : %10.2f s, %10.2f s." % (t_sys, t_sys / nruns))
1005 1000 twall1 = time.perf_counter()
1006 1001 print("Wall time: %10.2f s." % (twall1 - twall0))
1007 1002
1008 1003 @skip_doctest
1009 1004 @no_var_expand
1010 1005 @line_cell_magic
1011 1006 @needs_local_scope
1012 1007 def timeit(self, line='', cell=None, local_ns=None):
1013 1008 """Time execution of a Python statement or expression
1014 1009
1015 1010 Usage, in line mode:
1016 1011 %timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] statement
1017 1012 or in cell mode:
1018 1013 %%timeit [-n<N> -r<R> [-t|-c] -q -p<P> -o] setup_code
1019 1014 code
1020 1015 code...
1021 1016
1022 1017 Time execution of a Python statement or expression using the timeit
1023 1018 module. This function can be used both as a line and cell magic:
1024 1019
1025 1020 - In line mode you can time a single-line statement (though multiple
1026 1021 ones can be chained with using semicolons).
1027 1022
1028 1023 - In cell mode, the statement in the first line is used as setup code
1029 1024 (executed but not timed) and the body of the cell is timed. The cell
1030 1025 body has access to any variables created in the setup code.
1031 1026
1032 1027 Options:
1033 1028 -n<N>: execute the given statement <N> times in a loop. If <N> is not
1034 1029 provided, <N> is determined so as to get sufficient accuracy.
1035 1030
1036 1031 -r<R>: number of repeats <R>, each consisting of <N> loops, and take the
1037 1032 best result.
1038 1033 Default: 7
1039 1034
1040 1035 -t: use time.time to measure the time, which is the default on Unix.
1041 1036 This function measures wall time.
1042 1037
1043 1038 -c: use time.clock to measure the time, which is the default on
1044 1039 Windows and measures wall time. On Unix, resource.getrusage is used
1045 1040 instead and returns the CPU user time.
1046 1041
1047 1042 -p<P>: use a precision of <P> digits to display the timing result.
1048 1043 Default: 3
1049 1044
1050 1045 -q: Quiet, do not print result.
1051 1046
1052 1047 -o: return a TimeitResult that can be stored in a variable to inspect
1053 1048 the result in more details.
1054 1049
1055 1050 .. versionchanged:: 7.3
1056 1051 User variables are no longer expanded,
1057 1052 the magic line is always left unmodified.
1058 1053
1059 1054 Examples
1060 1055 --------
1061 1056 ::
1062 1057
1063 1058 In [1]: %timeit pass
1064 1059 8.26 ns Β± 0.12 ns per loop (mean Β± std. dev. of 7 runs, 100000000 loops each)
1065 1060
1066 1061 In [2]: u = None
1067 1062
1068 1063 In [3]: %timeit u is None
1069 1064 29.9 ns Β± 0.643 ns per loop (mean Β± std. dev. of 7 runs, 10000000 loops each)
1070 1065
1071 1066 In [4]: %timeit -r 4 u == None
1072 1067
1073 1068 In [5]: import time
1074 1069
1075 1070 In [6]: %timeit -n1 time.sleep(2)
1076 1071
1077 1072
1078 1073 The times reported by %timeit will be slightly higher than those
1079 1074 reported by the timeit.py script when variables are accessed. This is
1080 1075 due to the fact that %timeit executes the statement in the namespace
1081 1076 of the shell, compared with timeit.py, which uses a single setup
1082 1077 statement to import function or create variables. Generally, the bias
1083 1078 does not matter as long as results from timeit.py are not mixed with
1084 1079 those from %timeit."""
1085 1080
1086 1081 opts, stmt = self.parse_options(
1087 1082 line, "n:r:tcp:qo", posix=False, strict=False, preserve_non_opts=True
1088 1083 )
1089 1084 if stmt == "" and cell is None:
1090 1085 return
1091 1086
1092 1087 timefunc = timeit.default_timer
1093 1088 number = int(getattr(opts, "n", 0))
1094 1089 default_repeat = 7 if timeit.default_repeat < 7 else timeit.default_repeat
1095 1090 repeat = int(getattr(opts, "r", default_repeat))
1096 1091 precision = int(getattr(opts, "p", 3))
1097 1092 quiet = 'q' in opts
1098 1093 return_result = 'o' in opts
1099 1094 if hasattr(opts, "t"):
1100 1095 timefunc = time.time
1101 1096 if hasattr(opts, "c"):
1102 1097 timefunc = clock
1103 1098
1104 1099 timer = Timer(timer=timefunc)
1105 1100 # this code has tight coupling to the inner workings of timeit.Timer,
1106 1101 # but is there a better way to achieve that the code stmt has access
1107 1102 # to the shell namespace?
1108 1103 transform = self.shell.transform_cell
1109 1104
1110 1105 if cell is None:
1111 1106 # called as line magic
1112 1107 ast_setup = self.shell.compile.ast_parse("pass")
1113 1108 ast_stmt = self.shell.compile.ast_parse(transform(stmt))
1114 1109 else:
1115 1110 ast_setup = self.shell.compile.ast_parse(transform(stmt))
1116 1111 ast_stmt = self.shell.compile.ast_parse(transform(cell))
1117 1112
1118 1113 ast_setup = self.shell.transform_ast(ast_setup)
1119 1114 ast_stmt = self.shell.transform_ast(ast_stmt)
1120 1115
1121 1116 # Check that these compile to valid Python code *outside* the timer func
1122 1117 # Invalid code may become valid when put inside the function & loop,
1123 1118 # which messes up error messages.
1124 1119 # https://github.com/ipython/ipython/issues/10636
1125 1120 self.shell.compile(ast_setup, "<magic-timeit-setup>", "exec")
1126 1121 self.shell.compile(ast_stmt, "<magic-timeit-stmt>", "exec")
1127 1122
1128 1123 # This codestring is taken from timeit.template - we fill it in as an
1129 1124 # AST, so that we can apply our AST transformations to the user code
1130 1125 # without affecting the timing code.
1131 1126 timeit_ast_template = ast.parse('def inner(_it, _timer):\n'
1132 1127 ' setup\n'
1133 1128 ' _t0 = _timer()\n'
1134 1129 ' for _i in _it:\n'
1135 1130 ' stmt\n'
1136 1131 ' _t1 = _timer()\n'
1137 1132 ' return _t1 - _t0\n')
1138 1133
1139 1134 timeit_ast = TimeitTemplateFiller(ast_setup, ast_stmt).visit(timeit_ast_template)
1140 1135 timeit_ast = ast.fix_missing_locations(timeit_ast)
1141 1136
1142 1137 # Track compilation time so it can be reported if too long
1143 1138 # Minimum time above which compilation time will be reported
1144 1139 tc_min = 0.1
1145 1140
1146 1141 t0 = clock()
1147 1142 code = self.shell.compile(timeit_ast, "<magic-timeit>", "exec")
1148 1143 tc = clock()-t0
1149 1144
1150 1145 ns = {}
1151 1146 glob = self.shell.user_ns
1152 1147 # handles global vars with same name as local vars. We store them in conflict_globs.
1153 1148 conflict_globs = {}
1154 1149 if local_ns and cell is None:
1155 1150 for var_name, var_val in glob.items():
1156 1151 if var_name in local_ns:
1157 1152 conflict_globs[var_name] = var_val
1158 1153 glob.update(local_ns)
1159 1154
1160 1155 exec(code, glob, ns)
1161 1156 timer.inner = ns["inner"]
1162 1157
1163 1158 # This is used to check if there is a huge difference between the
1164 1159 # best and worst timings.
1165 1160 # Issue: https://github.com/ipython/ipython/issues/6471
1166 1161 if number == 0:
1167 1162 # determine number so that 0.2 <= total time < 2.0
1168 1163 for index in range(0, 10):
1169 1164 number = 10 ** index
1170 1165 time_number = timer.timeit(number)
1171 1166 if time_number >= 0.2:
1172 1167 break
1173 1168
1174 1169 all_runs = timer.repeat(repeat, number)
1175 1170 best = min(all_runs) / number
1176 1171 worst = max(all_runs) / number
1177 1172 timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
1178 1173
1179 1174 # Restore global vars from conflict_globs
1180 1175 if conflict_globs:
1181 1176 glob.update(conflict_globs)
1182 1177
1183 1178 if not quiet :
1184 1179 # Check best timing is greater than zero to avoid a
1185 1180 # ZeroDivisionError.
1186 1181 # In cases where the slowest timing is lesser than a microsecond
1187 1182 # we assume that it does not really matter if the fastest
1188 1183 # timing is 4 times faster than the slowest timing or not.
1189 1184 if worst > 4 * best and best > 0 and worst > 1e-6:
1190 1185 print("The slowest run took %0.2f times longer than the "
1191 1186 "fastest. This could mean that an intermediate result "
1192 1187 "is being cached." % (worst / best))
1193 1188
1194 1189 print( timeit_result )
1195 1190
1196 1191 if tc > tc_min:
1197 1192 print("Compiler time: %.2f s" % tc)
1198 1193 if return_result:
1199 1194 return timeit_result
1200 1195
1201 1196 @skip_doctest
1202 1197 @no_var_expand
1203 1198 @needs_local_scope
1204 1199 @line_cell_magic
1205 1200 def time(self,line='', cell=None, local_ns=None):
1206 1201 """Time execution of a Python statement or expression.
1207 1202
1208 1203 The CPU and wall clock times are printed, and the value of the
1209 1204 expression (if any) is returned. Note that under Win32, system time
1210 1205 is always reported as 0, since it can not be measured.
1211 1206
1212 1207 This function can be used both as a line and cell magic:
1213 1208
1214 1209 - In line mode you can time a single-line statement (though multiple
1215 1210 ones can be chained with using semicolons).
1216 1211
1217 1212 - In cell mode, you can time the cell body (a directly
1218 1213 following statement raises an error).
1219 1214
1220 1215 This function provides very basic timing functionality. Use the timeit
1221 1216 magic for more control over the measurement.
1222 1217
1223 1218 .. versionchanged:: 7.3
1224 1219 User variables are no longer expanded,
1225 1220 the magic line is always left unmodified.
1226 1221
1227 1222 Examples
1228 1223 --------
1229 1224 ::
1230 1225
1231 1226 In [1]: %time 2**128
1232 1227 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1233 1228 Wall time: 0.00
1234 1229 Out[1]: 340282366920938463463374607431768211456L
1235 1230
1236 1231 In [2]: n = 1000000
1237 1232
1238 1233 In [3]: %time sum(range(n))
1239 1234 CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
1240 1235 Wall time: 1.37
1241 1236 Out[3]: 499999500000L
1242 1237
1243 1238 In [4]: %time print 'hello world'
1244 1239 hello world
1245 1240 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1246 1241 Wall time: 0.00
1247 1242
1248 1243
1249 1244 .. note::
1250 1245 The time needed by Python to compile the given expression will be
1251 1246 reported if it is more than 0.1s.
1252 1247
1253 1248 In the example below, the actual exponentiation is done by Python
1254 1249 at compilation time, so while the expression can take a noticeable
1255 1250 amount of time to compute, that time is purely due to the
1256 1251 compilation::
1257 1252
1258 1253 In [5]: %time 3**9999;
1259 1254 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1260 1255 Wall time: 0.00 s
1261 1256
1262 1257 In [6]: %time 3**999999;
1263 1258 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
1264 1259 Wall time: 0.00 s
1265 1260 Compiler : 0.78 s
1266 1261 """
1267 1262 # fail immediately if the given expression can't be compiled
1268 1263
1269 1264 if line and cell:
1270 1265 raise UsageError("Can't use statement directly after '%%time'!")
1271 1266
1272 1267 if cell:
1273 1268 expr = self.shell.transform_cell(cell)
1274 1269 else:
1275 1270 expr = self.shell.transform_cell(line)
1276 1271
1277 1272 # Minimum time above which parse time will be reported
1278 1273 tp_min = 0.1
1279 1274
1280 1275 t0 = clock()
1281 1276 expr_ast = self.shell.compile.ast_parse(expr)
1282 1277 tp = clock()-t0
1283 1278
1284 1279 # Apply AST transformations
1285 1280 expr_ast = self.shell.transform_ast(expr_ast)
1286 1281
1287 1282 # Minimum time above which compilation time will be reported
1288 1283 tc_min = 0.1
1289 1284
1290 1285 expr_val=None
1291 1286 if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr):
1292 1287 mode = 'eval'
1293 1288 source = '<timed eval>'
1294 1289 expr_ast = ast.Expression(expr_ast.body[0].value)
1295 1290 else:
1296 1291 mode = 'exec'
1297 1292 source = '<timed exec>'
1298 1293 # multi-line %%time case
1299 1294 if len(expr_ast.body) > 1 and isinstance(expr_ast.body[-1], ast.Expr):
1300 1295 expr_val= expr_ast.body[-1]
1301 1296 expr_ast = expr_ast.body[:-1]
1302 1297 expr_ast = Module(expr_ast, [])
1303 1298 expr_val = ast.Expression(expr_val.value)
1304 1299
1305 1300 t0 = clock()
1306 1301 code = self.shell.compile(expr_ast, source, mode)
1307 1302 tc = clock()-t0
1308 1303
1309 1304 # skew measurement as little as possible
1310 1305 glob = self.shell.user_ns
1311 1306 wtime = time.time
1312 1307 # time execution
1313 1308 wall_st = wtime()
1314 1309 if mode=='eval':
1315 1310 st = clock2()
1316 1311 try:
1317 1312 out = eval(code, glob, local_ns)
1318 1313 except:
1319 1314 self.shell.showtraceback()
1320 1315 return
1321 1316 end = clock2()
1322 1317 else:
1323 1318 st = clock2()
1324 1319 try:
1325 1320 exec(code, glob, local_ns)
1326 1321 out=None
1327 1322 # multi-line %%time case
1328 1323 if expr_val is not None:
1329 1324 code_2 = self.shell.compile(expr_val, source, 'eval')
1330 1325 out = eval(code_2, glob, local_ns)
1331 1326 except:
1332 1327 self.shell.showtraceback()
1333 1328 return
1334 1329 end = clock2()
1335 1330
1336 1331 wall_end = wtime()
1337 1332 # Compute actual times and report
1338 1333 wall_time = wall_end-wall_st
1339 1334 cpu_user = end[0]-st[0]
1340 1335 cpu_sys = end[1]-st[1]
1341 1336 cpu_tot = cpu_user+cpu_sys
1342 1337 # On windows cpu_sys is always zero, so no new information to the next print
1343 1338 if sys.platform != 'win32':
1344 1339 print("CPU times: user %s, sys: %s, total: %s" % \
1345 1340 (_format_time(cpu_user),_format_time(cpu_sys),_format_time(cpu_tot)))
1346 1341 print("Wall time: %s" % _format_time(wall_time))
1347 1342 if tc > tc_min:
1348 1343 print("Compiler : %s" % _format_time(tc))
1349 1344 if tp > tp_min:
1350 1345 print("Parser : %s" % _format_time(tp))
1351 1346 return out
1352 1347
1353 1348 @skip_doctest
1354 1349 @line_magic
1355 1350 def macro(self, parameter_s=''):
1356 1351 """Define a macro for future re-execution. It accepts ranges of history,
1357 1352 filenames or string objects.
1358 1353
1359 1354 Usage:\\
1360 1355 %macro [options] name n1-n2 n3-n4 ... n5 .. n6 ...
1361 1356
1362 1357 Options:
1363 1358
1364 1359 -r: use 'raw' input. By default, the 'processed' history is used,
1365 1360 so that magics are loaded in their transformed version to valid
1366 1361 Python. If this option is given, the raw input as typed at the
1367 1362 command line is used instead.
1368 1363
1369 1364 -q: quiet macro definition. By default, a tag line is printed
1370 1365 to indicate the macro has been created, and then the contents of
1371 1366 the macro are printed. If this option is given, then no printout
1372 1367 is produced once the macro is created.
1373 1368
1374 1369 This will define a global variable called `name` which is a string
1375 1370 made of joining the slices and lines you specify (n1,n2,... numbers
1376 1371 above) from your input history into a single string. This variable
1377 1372 acts like an automatic function which re-executes those lines as if
1378 1373 you had typed them. You just type 'name' at the prompt and the code
1379 1374 executes.
1380 1375
1381 1376 The syntax for indicating input ranges is described in %history.
1382 1377
1383 1378 Note: as a 'hidden' feature, you can also use traditional python slice
1384 1379 notation, where N:M means numbers N through M-1.
1385 1380
1386 1381 For example, if your history contains (print using %hist -n )::
1387 1382
1388 1383 44: x=1
1389 1384 45: y=3
1390 1385 46: z=x+y
1391 1386 47: print x
1392 1387 48: a=5
1393 1388 49: print 'x',x,'y',y
1394 1389
1395 1390 you can create a macro with lines 44 through 47 (included) and line 49
1396 1391 called my_macro with::
1397 1392
1398 1393 In [55]: %macro my_macro 44-47 49
1399 1394
1400 1395 Now, typing `my_macro` (without quotes) will re-execute all this code
1401 1396 in one pass.
1402 1397
1403 1398 You don't need to give the line-numbers in order, and any given line
1404 1399 number can appear multiple times. You can assemble macros with any
1405 1400 lines from your input history in any order.
1406 1401
1407 1402 The macro is a simple object which holds its value in an attribute,
1408 1403 but IPython's display system checks for macros and executes them as
1409 1404 code instead of printing them when you type their name.
1410 1405
1411 1406 You can view a macro's contents by explicitly printing it with::
1412 1407
1413 1408 print macro_name
1414 1409
1415 1410 """
1416 1411 opts,args = self.parse_options(parameter_s,'rq',mode='list')
1417 1412 if not args: # List existing macros
1418 1413 return sorted(k for k,v in self.shell.user_ns.items() if isinstance(v, Macro))
1419 1414 if len(args) == 1:
1420 1415 raise UsageError(
1421 1416 "%macro insufficient args; usage '%macro name n1-n2 n3-4...")
1422 1417 name, codefrom = args[0], " ".join(args[1:])
1423 1418
1424 1419 #print 'rng',ranges # dbg
1425 1420 try:
1426 1421 lines = self.shell.find_user_code(codefrom, 'r' in opts)
1427 1422 except (ValueError, TypeError) as e:
1428 1423 print(e.args[0])
1429 1424 return
1430 1425 macro = Macro(lines)
1431 1426 self.shell.define_macro(name, macro)
1432 1427 if not ( 'q' in opts) :
1433 1428 print('Macro `%s` created. To execute, type its name (without quotes).' % name)
1434 1429 print('=== Macro contents: ===')
1435 1430 print(macro, end=' ')
1436 1431
1437 1432 @magic_arguments.magic_arguments()
1438 1433 @magic_arguments.argument('output', type=str, default='', nargs='?',
1439 1434 help="""The name of the variable in which to store output.
1440 1435 This is a utils.io.CapturedIO object with stdout/err attributes
1441 1436 for the text of the captured output.
1442 1437
1443 1438 CapturedOutput also has a show() method for displaying the output,
1444 1439 and __call__ as well, so you can use that to quickly display the
1445 1440 output.
1446 1441
1447 1442 If unspecified, captured output is discarded.
1448 1443 """
1449 1444 )
1450 1445 @magic_arguments.argument('--no-stderr', action="store_true",
1451 1446 help="""Don't capture stderr."""
1452 1447 )
1453 1448 @magic_arguments.argument('--no-stdout', action="store_true",
1454 1449 help="""Don't capture stdout."""
1455 1450 )
1456 1451 @magic_arguments.argument('--no-display', action="store_true",
1457 1452 help="""Don't capture IPython's rich display."""
1458 1453 )
1459 1454 @cell_magic
1460 1455 def capture(self, line, cell):
1461 1456 """run the cell, capturing stdout, stderr, and IPython's rich display() calls."""
1462 1457 args = magic_arguments.parse_argstring(self.capture, line)
1463 1458 out = not args.no_stdout
1464 1459 err = not args.no_stderr
1465 1460 disp = not args.no_display
1466 1461 with capture_output(out, err, disp) as io:
1467 1462 self.shell.run_cell(cell)
1468 1463 if args.output:
1469 1464 self.shell.user_ns[args.output] = io
1470 1465
1471 1466 def parse_breakpoint(text, current_file):
1472 1467 '''Returns (file, line) for file:line and (current_file, line) for line'''
1473 1468 colon = text.find(':')
1474 1469 if colon == -1:
1475 1470 return current_file, int(text)
1476 1471 else:
1477 1472 return text[:colon], int(text[colon+1:])
1478 1473
1479 1474 def _format_time(timespan, precision=3):
1480 1475 """Formats the timespan in a human readable form"""
1481 1476
1482 1477 if timespan >= 60.0:
1483 1478 # we have more than a minute, format that in a human readable form
1484 1479 # Idea from http://snipplr.com/view/5713/
1485 1480 parts = [("d", 60*60*24),("h", 60*60),("min", 60), ("s", 1)]
1486 1481 time = []
1487 1482 leftover = timespan
1488 1483 for suffix, length in parts:
1489 1484 value = int(leftover / length)
1490 1485 if value > 0:
1491 1486 leftover = leftover % length
1492 1487 time.append(u'%s%s' % (str(value), suffix))
1493 1488 if leftover < 1:
1494 1489 break
1495 1490 return " ".join(time)
1496 1491
1497 1492
1498 1493 # Unfortunately the unicode 'micro' symbol can cause problems in
1499 1494 # certain terminals.
1500 1495 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1501 1496 # Try to prevent crashes by being more secure than it needs to
1502 1497 # E.g. eclipse is able to print a Β΅, but has no sys.stdout.encoding set.
1503 1498 units = [u"s", u"ms",u'us',"ns"] # the save value
1504 1499 if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding:
1505 1500 try:
1506 1501 u'\xb5'.encode(sys.stdout.encoding)
1507 1502 units = [u"s", u"ms",u'\xb5s',"ns"]
1508 1503 except:
1509 1504 pass
1510 1505 scaling = [1, 1e3, 1e6, 1e9]
1511 1506
1512 1507 if timespan > 0.0:
1513 1508 order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
1514 1509 else:
1515 1510 order = 3
1516 1511 return u"%.*g %s" % (precision, timespan * scaling[order], units[order])
@@ -1,1359 +1,1354 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for various magic functions."""
3 3
4 4 import asyncio
5 5 import io
6 6 import os
7 7 import re
8 8 import shlex
9 9 import sys
10 10 import warnings
11 11 from importlib import invalidate_caches
12 12 from io import StringIO
13 13 from pathlib import Path
14 14 from textwrap import dedent
15 15 from unittest import TestCase, mock
16 16
17 17 import pytest
18 18
19 19 from IPython import get_ipython
20 20 from IPython.core import magic
21 21 from IPython.core.error import UsageError
22 22 from IPython.core.magic import (
23 23 Magics,
24 24 cell_magic,
25 25 line_magic,
26 26 magics_class,
27 27 register_cell_magic,
28 28 register_line_magic,
29 29 )
30 30 from IPython.core.magics import code, execution, logging, osm, script
31 31 from IPython.testing import decorators as dec
32 32 from IPython.testing import tools as tt
33 33 from IPython.utils.io import capture_output
34 34 from IPython.utils.process import find_cmd
35 35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36 36
37 37 from .test_debugger import PdbTestInput
38 38
39 39
40 40 @magic.magics_class
41 41 class DummyMagics(magic.Magics): pass
42 42
43 43 def test_extract_code_ranges():
44 44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
45 45 expected = [
46 46 (0, 1),
47 47 (2, 3),
48 48 (4, 6),
49 49 (6, 9),
50 50 (9, 14),
51 51 (16, None),
52 52 (None, 9),
53 53 (9, None),
54 54 (None, 13),
55 55 (None, None),
56 56 ]
57 57 actual = list(code.extract_code_ranges(instr))
58 58 assert actual == expected
59 59
60 60 def test_extract_symbols():
61 61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
62 62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
63 63 expected = [([], ['a']),
64 64 (["def b():\n return 42\n"], []),
65 65 (["class A: pass\n"], []),
66 66 (["class A: pass\n", "def b():\n return 42\n"], []),
67 67 (["class A: pass\n"], ['a']),
68 68 ([], ['z'])]
69 69 for symbols, exp in zip(symbols_args, expected):
70 70 assert code.extract_symbols(source, symbols) == exp
71 71
72 72
73 73 def test_extract_symbols_raises_exception_with_non_python_code():
74 74 source = ("=begin A Ruby program :)=end\n"
75 75 "def hello\n"
76 76 "puts 'Hello world'\n"
77 77 "end")
78 78 with pytest.raises(SyntaxError):
79 79 code.extract_symbols(source, "hello")
80 80
81 81
82 82 def test_magic_not_found():
83 83 # magic not found raises UsageError
84 84 with pytest.raises(UsageError):
85 85 _ip.magic('doesntexist')
86 86
87 87 # ensure result isn't success when a magic isn't found
88 88 result = _ip.run_cell('%doesntexist')
89 89 assert isinstance(result.error_in_exec, UsageError)
90 90
91 91
92 92 def test_cell_magic_not_found():
93 93 # magic not found raises UsageError
94 94 with pytest.raises(UsageError):
95 95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
96 96
97 97 # ensure result isn't success when a magic isn't found
98 98 result = _ip.run_cell('%%doesntexist')
99 99 assert isinstance(result.error_in_exec, UsageError)
100 100
101 101
102 102 def test_magic_error_status():
103 103 def fail(shell):
104 104 1/0
105 105 _ip.register_magic_function(fail)
106 106 result = _ip.run_cell('%fail')
107 107 assert isinstance(result.error_in_exec, ZeroDivisionError)
108 108
109 109
110 110 def test_config():
111 111 """ test that config magic does not raise
112 112 can happen if Configurable init is moved too early into
113 113 Magics.__init__ as then a Config object will be registered as a
114 114 magic.
115 115 """
116 116 ## should not raise.
117 117 _ip.magic('config')
118 118
119 119 def test_config_available_configs():
120 120 """ test that config magic prints available configs in unique and
121 121 sorted order. """
122 122 with capture_output() as captured:
123 123 _ip.magic('config')
124 124
125 125 stdout = captured.stdout
126 126 config_classes = stdout.strip().split('\n')[1:]
127 127 assert config_classes == sorted(set(config_classes))
128 128
129 129 def test_config_print_class():
130 130 """ test that config with a classname prints the class's options. """
131 131 with capture_output() as captured:
132 132 _ip.magic('config TerminalInteractiveShell')
133 133
134 134 stdout = captured.stdout
135 135 assert re.match(
136 136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
137 137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
138 138
139 139
140 140 def test_rehashx():
141 141 # clear up everything
142 142 _ip.alias_manager.clear_aliases()
143 143 del _ip.db['syscmdlist']
144 144
145 145 _ip.magic('rehashx')
146 146 # Practically ALL ipython development systems will have more than 10 aliases
147 147
148 148 assert len(_ip.alias_manager.aliases) > 10
149 149 for name, cmd in _ip.alias_manager.aliases:
150 150 # we must strip dots from alias names
151 151 assert "." not in name
152 152
153 153 # rehashx must fill up syscmdlist
154 154 scoms = _ip.db['syscmdlist']
155 155 assert len(scoms) > 10
156 156
157 157
158 158 def test_magic_parse_options():
159 159 """Test that we don't mangle paths when parsing magic options."""
160 160 ip = get_ipython()
161 161 path = 'c:\\x'
162 162 m = DummyMagics(ip)
163 163 opts = m.parse_options('-f %s' % path,'f:')[0]
164 164 # argv splitting is os-dependent
165 165 if os.name == 'posix':
166 166 expected = 'c:x'
167 167 else:
168 168 expected = path
169 169 assert opts["f"] == expected
170 170
171 171
172 172 def test_magic_parse_long_options():
173 173 """Magic.parse_options can handle --foo=bar long options"""
174 174 ip = get_ipython()
175 175 m = DummyMagics(ip)
176 176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
177 177 assert "foo" in opts
178 178 assert "bar" in opts
179 179 assert opts["bar"] == "bubble"
180 180
181 181
182 182 def doctest_hist_f():
183 183 """Test %hist -f with temporary filename.
184 184
185 185 In [9]: import tempfile
186 186
187 187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
188 188
189 189 In [11]: %hist -nl -f $tfile 3
190 190
191 191 In [13]: import os; os.unlink(tfile)
192 192 """
193 193
194 194
195 195 def doctest_hist_op():
196 196 """Test %hist -op
197 197
198 198 In [1]: class b(float):
199 199 ...: pass
200 200 ...:
201 201
202 202 In [2]: class s(object):
203 203 ...: def __str__(self):
204 204 ...: return 's'
205 205 ...:
206 206
207 207 In [3]:
208 208
209 209 In [4]: class r(b):
210 210 ...: def __repr__(self):
211 211 ...: return 'r'
212 212 ...:
213 213
214 214 In [5]: class sr(s,r): pass
215 215 ...:
216 216
217 217 In [6]:
218 218
219 219 In [7]: bb=b()
220 220
221 221 In [8]: ss=s()
222 222
223 223 In [9]: rr=r()
224 224
225 225 In [10]: ssrr=sr()
226 226
227 227 In [11]: 4.5
228 228 Out[11]: 4.5
229 229
230 230 In [12]: str(ss)
231 231 Out[12]: 's'
232 232
233 233 In [13]:
234 234
235 235 In [14]: %hist -op
236 236 >>> class b:
237 237 ... pass
238 238 ...
239 239 >>> class s(b):
240 240 ... def __str__(self):
241 241 ... return 's'
242 242 ...
243 243 >>>
244 244 >>> class r(b):
245 245 ... def __repr__(self):
246 246 ... return 'r'
247 247 ...
248 248 >>> class sr(s,r): pass
249 249 >>>
250 250 >>> bb=b()
251 251 >>> ss=s()
252 252 >>> rr=r()
253 253 >>> ssrr=sr()
254 254 >>> 4.5
255 255 4.5
256 256 >>> str(ss)
257 257 's'
258 258 >>>
259 259 """
260 260
261 261 def test_hist_pof():
262 262 ip = get_ipython()
263 263 ip.run_cell("1+2", store_history=True)
264 264 #raise Exception(ip.history_manager.session_number)
265 265 #raise Exception(list(ip.history_manager._get_range_session()))
266 266 with TemporaryDirectory() as td:
267 267 tf = os.path.join(td, 'hist.py')
268 268 ip.run_line_magic('history', '-pof %s' % tf)
269 269 assert os.path.isfile(tf)
270 270
271 271
272 272 def test_macro():
273 273 ip = get_ipython()
274 274 ip.history_manager.reset() # Clear any existing history.
275 275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
276 276 for i, cmd in enumerate(cmds, start=1):
277 277 ip.history_manager.store_inputs(i, cmd)
278 278 ip.magic("macro test 1-3")
279 279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
280 280
281 281 # List macros
282 282 assert "test" in ip.magic("macro")
283 283
284 284
285 285 def test_macro_run():
286 286 """Test that we can run a multi-line macro successfully."""
287 287 ip = get_ipython()
288 288 ip.history_manager.reset()
289 289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
290 290 for cmd in cmds:
291 291 ip.run_cell(cmd, store_history=True)
292 292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
293 293 with tt.AssertPrints("12"):
294 294 ip.run_cell("test")
295 295 with tt.AssertPrints("13"):
296 296 ip.run_cell("test")
297 297
298 298
299 299 def test_magic_magic():
300 300 """Test %magic"""
301 301 ip = get_ipython()
302 302 with capture_output() as captured:
303 303 ip.magic("magic")
304 304
305 305 stdout = captured.stdout
306 306 assert "%magic" in stdout
307 307 assert "IPython" in stdout
308 308 assert "Available" in stdout
309 309
310 310
311 311 @dec.skipif_not_numpy
312 312 def test_numpy_reset_array_undec():
313 313 "Test '%reset array' functionality"
314 314 _ip.ex("import numpy as np")
315 315 _ip.ex("a = np.empty(2)")
316 316 assert "a" in _ip.user_ns
317 317 _ip.magic("reset -f array")
318 318 assert "a" not in _ip.user_ns
319 319
320 320
321 321 def test_reset_out():
322 322 "Test '%reset out' magic"
323 323 _ip.run_cell("parrot = 'dead'", store_history=True)
324 324 # test '%reset -f out', make an Out prompt
325 325 _ip.run_cell("parrot", store_history=True)
326 326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
327 327 _ip.magic("reset -f out")
328 328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 329 assert len(_ip.user_ns["Out"]) == 0
330 330
331 331
332 332 def test_reset_in():
333 333 "Test '%reset in' magic"
334 334 # test '%reset -f in'
335 335 _ip.run_cell("parrot", store_history=True)
336 336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
337 337 _ip.magic("%reset -f in")
338 338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 339 assert len(set(_ip.user_ns["In"])) == 1
340 340
341 341
342 342 def test_reset_dhist():
343 343 "Test '%reset dhist' magic"
344 344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
345 345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
346 346 _ip.magic("cd -")
347 347 assert len(_ip.user_ns["_dh"]) > 0
348 348 _ip.magic("reset -f dhist")
349 349 assert len(_ip.user_ns["_dh"]) == 0
350 350 _ip.run_cell("_dh = [d for d in tmp]") # restore
351 351
352 352
353 353 def test_reset_in_length():
354 354 "Test that '%reset in' preserves In[] length"
355 355 _ip.run_cell("print 'foo'")
356 356 _ip.run_cell("reset -f in")
357 357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
358 358
359 359
360 360 class TestResetErrors(TestCase):
361 361
362 362 def test_reset_redefine(self):
363 363
364 364 @magics_class
365 365 class KernelMagics(Magics):
366 366 @line_magic
367 367 def less(self, shell): pass
368 368
369 369 _ip.register_magics(KernelMagics)
370 370
371 371 with self.assertLogs() as cm:
372 372 # hack, we want to just capture logs, but assertLogs fails if not
373 373 # logs get produce.
374 374 # so log one things we ignore.
375 375 import logging as log_mod
376 376 log = log_mod.getLogger()
377 377 log.info('Nothing')
378 378 # end hack.
379 379 _ip.run_cell("reset -f")
380 380
381 381 assert len(cm.output) == 1
382 382 for out in cm.output:
383 383 assert "Invalid alias" not in out
384 384
385 385 def test_tb_syntaxerror():
386 386 """test %tb after a SyntaxError"""
387 387 ip = get_ipython()
388 388 ip.run_cell("for")
389 389
390 390 # trap and validate stdout
391 391 save_stdout = sys.stdout
392 392 try:
393 393 sys.stdout = StringIO()
394 394 ip.run_cell("%tb")
395 395 out = sys.stdout.getvalue()
396 396 finally:
397 397 sys.stdout = save_stdout
398 398 # trim output, and only check the last line
399 399 last_line = out.rstrip().splitlines()[-1].strip()
400 400 assert last_line == "SyntaxError: invalid syntax"
401 401
402 402
403 403 def test_time():
404 404 ip = get_ipython()
405 405
406 406 with tt.AssertPrints("Wall time: "):
407 407 ip.run_cell("%time None")
408 408
409 409 ip.run_cell("def f(kmjy):\n"
410 410 " %time print (2*kmjy)")
411 411
412 412 with tt.AssertPrints("Wall time: "):
413 413 with tt.AssertPrints("hihi", suppress=False):
414 414 ip.run_cell("f('hi')")
415 415
416 416 def test_time_last_not_expression():
417 417 ip.run_cell("%%time\n"
418 418 "var_1 = 1\n"
419 419 "var_2 = 2\n")
420 420 assert ip.user_ns['var_1'] == 1
421 421 del ip.user_ns['var_1']
422 422 assert ip.user_ns['var_2'] == 2
423 423 del ip.user_ns['var_2']
424 424
425 425
426 426 @dec.skip_win32
427 427 def test_time2():
428 428 ip = get_ipython()
429 429
430 430 with tt.AssertPrints("CPU times: user "):
431 431 ip.run_cell("%time None")
432 432
433 433 def test_time3():
434 434 """Erroneous magic function calls, issue gh-3334"""
435 435 ip = get_ipython()
436 436 ip.user_ns.pop('run', None)
437 437
438 438 with tt.AssertNotPrints("not found", channel='stderr'):
439 439 ip.run_cell("%%time\n"
440 440 "run = 0\n"
441 441 "run += 1")
442 442
443 443 def test_multiline_time():
444 444 """Make sure last statement from time return a value."""
445 445 ip = get_ipython()
446 446 ip.user_ns.pop('run', None)
447 447
448 448 ip.run_cell(dedent("""\
449 449 %%time
450 450 a = "ho"
451 451 b = "hey"
452 452 a+b
453 453 """
454 454 )
455 455 )
456 456 assert ip.user_ns_hidden["_"] == "hohey"
457 457
458 458
459 459 def test_time_local_ns():
460 460 """
461 461 Test that local_ns is actually global_ns when running a cell magic
462 462 """
463 463 ip = get_ipython()
464 464 ip.run_cell("%%time\n" "myvar = 1")
465 465 assert ip.user_ns["myvar"] == 1
466 466 del ip.user_ns["myvar"]
467 467
468 468
469 469 def test_doctest_mode():
470 470 "Toggle doctest_mode twice, it should be a no-op and run without error"
471 471 _ip.magic('doctest_mode')
472 472 _ip.magic('doctest_mode')
473 473
474 474
475 475 def test_parse_options():
476 476 """Tests for basic options parsing in magics."""
477 477 # These are only the most minimal of tests, more should be added later. At
478 478 # the very least we check that basic text/unicode calls work OK.
479 479 m = DummyMagics(_ip)
480 480 assert m.parse_options("foo", "")[1] == "foo"
481 481 assert m.parse_options("foo", "")[1] == "foo"
482 482
483 483
484 484 def test_parse_options_preserve_non_option_string():
485 485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
486 486 m = DummyMagics(_ip)
487 487 opts, stmt = m.parse_options(
488 488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
489 489 )
490 490 assert opts == {"n": "1", "r": "13"}
491 491 assert stmt == "_ = 314 + foo"
492 492
493 493
494 494 def test_run_magic_preserve_code_block():
495 495 """Test to assert preservation of non-option part of magic-block, while running magic."""
496 496 _ip.user_ns["spaces"] = []
497 497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
498 498 assert _ip.user_ns["spaces"] == [[0]]
499 499
500 500
501 501 def test_dirops():
502 502 """Test various directory handling operations."""
503 503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
504 504 curpath = os.getcwd
505 505 startdir = os.getcwd()
506 506 ipdir = os.path.realpath(_ip.ipython_dir)
507 507 try:
508 508 _ip.magic('cd "%s"' % ipdir)
509 509 assert curpath() == ipdir
510 510 _ip.magic('cd -')
511 511 assert curpath() == startdir
512 512 _ip.magic('pushd "%s"' % ipdir)
513 513 assert curpath() == ipdir
514 514 _ip.magic('popd')
515 515 assert curpath() == startdir
516 516 finally:
517 517 os.chdir(startdir)
518 518
519 519
520 520 def test_cd_force_quiet():
521 521 """Test OSMagics.cd_force_quiet option"""
522 522 _ip.config.OSMagics.cd_force_quiet = True
523 523 osmagics = osm.OSMagics(shell=_ip)
524 524
525 525 startdir = os.getcwd()
526 526 ipdir = os.path.realpath(_ip.ipython_dir)
527 527
528 528 try:
529 529 with tt.AssertNotPrints(ipdir):
530 530 osmagics.cd('"%s"' % ipdir)
531 531 with tt.AssertNotPrints(startdir):
532 532 osmagics.cd('-')
533 533 finally:
534 534 os.chdir(startdir)
535 535
536 536
537 537 def test_xmode():
538 538 # Calling xmode three times should be a no-op
539 539 xmode = _ip.InteractiveTB.mode
540 540 for i in range(4):
541 541 _ip.magic("xmode")
542 542 assert _ip.InteractiveTB.mode == xmode
543 543
544 544 def test_reset_hard():
545 545 monitor = []
546 546 class A(object):
547 547 def __del__(self):
548 548 monitor.append(1)
549 549 def __repr__(self):
550 550 return "<A instance>"
551 551
552 552 _ip.user_ns["a"] = A()
553 553 _ip.run_cell("a")
554 554
555 555 assert monitor == []
556 556 _ip.magic("reset -f")
557 557 assert monitor == [1]
558 558
559 559 class TestXdel(tt.TempFileMixin):
560 560 def test_xdel(self):
561 561 """Test that references from %run are cleared by xdel."""
562 562 src = ("class A(object):\n"
563 563 " monitor = []\n"
564 564 " def __del__(self):\n"
565 565 " self.monitor.append(1)\n"
566 566 "a = A()\n")
567 567 self.mktmp(src)
568 568 # %run creates some hidden references...
569 569 _ip.magic("run %s" % self.fname)
570 570 # ... as does the displayhook.
571 571 _ip.run_cell("a")
572 572
573 573 monitor = _ip.user_ns["A"].monitor
574 574 assert monitor == []
575 575
576 576 _ip.magic("xdel a")
577 577
578 578 # Check that a's __del__ method has been called.
579 579 assert monitor == [1]
580 580
581 581 def doctest_who():
582 582 """doctest for %who
583 583
584 584 In [1]: %reset -sf
585 585
586 586 In [2]: alpha = 123
587 587
588 588 In [3]: beta = 'beta'
589 589
590 590 In [4]: %who int
591 591 alpha
592 592
593 593 In [5]: %who str
594 594 beta
595 595
596 596 In [6]: %whos
597 597 Variable Type Data/Info
598 598 ----------------------------
599 599 alpha int 123
600 600 beta str beta
601 601
602 602 In [7]: %who_ls
603 603 Out[7]: ['alpha', 'beta']
604 604 """
605 605
606 606 def test_whos():
607 607 """Check that whos is protected against objects where repr() fails."""
608 608 class A(object):
609 609 def __repr__(self):
610 610 raise Exception()
611 611 _ip.user_ns['a'] = A()
612 612 _ip.magic("whos")
613 613
614 614 def doctest_precision():
615 615 """doctest for %precision
616 616
617 617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
618 618
619 619 In [2]: %precision 5
620 620 Out[2]: '%.5f'
621 621
622 622 In [3]: f.float_format
623 623 Out[3]: '%.5f'
624 624
625 625 In [4]: %precision %e
626 626 Out[4]: '%e'
627 627
628 628 In [5]: f(3.1415927)
629 629 Out[5]: '3.141593e+00'
630 630 """
631 631
632 632 def test_debug_magic():
633 633 """Test debugging a small code with %debug
634 634
635 635 In [1]: with PdbTestInput(['c']):
636 636 ...: %debug print("a b") #doctest: +ELLIPSIS
637 637 ...:
638 638 ...
639 639 ipdb> c
640 640 a b
641 641 In [2]:
642 642 """
643 643
644 644 def test_psearch():
645 645 with tt.AssertPrints("dict.fromkeys"):
646 646 _ip.run_cell("dict.fr*?")
647 647 with tt.AssertPrints("Ο€.is_integer"):
648 648 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
649 649
650 650 def test_timeit_shlex():
651 651 """test shlex issues with timeit (#1109)"""
652 652 _ip.ex("def f(*a,**kw): pass")
653 653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
654 654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
655 655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
656 656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
657 657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
658 658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
659 659
660 660
661 661 def test_timeit_special_syntax():
662 662 "Test %%timeit with IPython special syntax"
663 663 @register_line_magic
664 664 def lmagic(line):
665 665 ip = get_ipython()
666 666 ip.user_ns['lmagic_out'] = line
667 667
668 668 # line mode test
669 669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
670 670 assert _ip.user_ns["lmagic_out"] == "my line"
671 671 # cell mode test
672 672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
673 673 assert _ip.user_ns["lmagic_out"] == "my line2"
674 674
675 675
676 676 def test_timeit_return():
677 677 """
678 678 test whether timeit -o return object
679 679 """
680 680
681 681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
682 682 assert(res is not None)
683 683
684 684 def test_timeit_quiet():
685 685 """
686 686 test quiet option of timeit magic
687 687 """
688 688 with tt.AssertNotPrints("loops"):
689 689 _ip.run_cell("%timeit -n1 -r1 -q 1")
690 690
691 691 def test_timeit_return_quiet():
692 692 with tt.AssertNotPrints("loops"):
693 693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
694 694 assert (res is not None)
695 695
696 696 def test_timeit_invalid_return():
697 697 with pytest.raises(SyntaxError):
698 698 _ip.run_line_magic('timeit', 'return')
699 699
700 700 @dec.skipif(execution.profile is None)
701 701 def test_prun_special_syntax():
702 702 "Test %%prun with IPython special syntax"
703 703 @register_line_magic
704 704 def lmagic(line):
705 705 ip = get_ipython()
706 706 ip.user_ns['lmagic_out'] = line
707 707
708 708 # line mode test
709 709 _ip.run_line_magic("prun", "-q %lmagic my line")
710 710 assert _ip.user_ns["lmagic_out"] == "my line"
711 711 # cell mode test
712 712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
713 713 assert _ip.user_ns["lmagic_out"] == "my line2"
714 714
715 715
716 716 @dec.skipif(execution.profile is None)
717 717 def test_prun_quotes():
718 718 "Test that prun does not clobber string escapes (GH #1302)"
719 719 _ip.magic(r"prun -q x = '\t'")
720 720 assert _ip.user_ns["x"] == "\t"
721 721
722 722
723 723 def test_extension():
724 724 # Debugging information for failures of this test
725 725 print('sys.path:')
726 726 for p in sys.path:
727 727 print(' ', p)
728 728 print('CWD', os.getcwd())
729 729
730 730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
731 731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
732 732 sys.path.insert(0, daft_path)
733 733 try:
734 734 _ip.user_ns.pop('arq', None)
735 735 invalidate_caches() # Clear import caches
736 736 _ip.magic("load_ext daft_extension")
737 737 assert _ip.user_ns["arq"] == 185
738 738 _ip.magic("unload_ext daft_extension")
739 739 assert 'arq' not in _ip.user_ns
740 740 finally:
741 741 sys.path.remove(daft_path)
742 742
743 743
744 744 def test_notebook_export_json():
745 745 pytest.importorskip("nbformat")
746 746 _ip = get_ipython()
747 747 _ip.history_manager.reset() # Clear any existing history.
748 748 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
749 749 for i, cmd in enumerate(cmds, start=1):
750 750 _ip.history_manager.store_inputs(i, cmd)
751 751 with TemporaryDirectory() as td:
752 752 outfile = os.path.join(td, "nb.ipynb")
753 753 _ip.magic("notebook -e %s" % outfile)
754 754
755 755
756 756 class TestEnv(TestCase):
757 757
758 758 def test_env(self):
759 759 env = _ip.magic("env")
760 760 self.assertTrue(isinstance(env, dict))
761 761
762 762 def test_env_secret(self):
763 763 env = _ip.magic("env")
764 764 hidden = "<hidden>"
765 765 with mock.patch.dict(
766 766 os.environ,
767 767 {
768 768 "API_KEY": "abc123",
769 769 "SECRET_THING": "ssshhh",
770 770 "JUPYTER_TOKEN": "",
771 771 "VAR": "abc"
772 772 }
773 773 ):
774 774 env = _ip.magic("env")
775 775 assert env["API_KEY"] == hidden
776 776 assert env["SECRET_THING"] == hidden
777 777 assert env["JUPYTER_TOKEN"] == hidden
778 778 assert env["VAR"] == "abc"
779 779
780 780 def test_env_get_set_simple(self):
781 781 env = _ip.magic("env var val1")
782 782 self.assertEqual(env, None)
783 783 self.assertEqual(os.environ['var'], 'val1')
784 784 self.assertEqual(_ip.magic("env var"), 'val1')
785 785 env = _ip.magic("env var=val2")
786 786 self.assertEqual(env, None)
787 787 self.assertEqual(os.environ['var'], 'val2')
788 788
789 789 def test_env_get_set_complex(self):
790 790 env = _ip.magic("env var 'val1 '' 'val2")
791 791 self.assertEqual(env, None)
792 792 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
793 793 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
794 794 env = _ip.magic('env var=val2 val3="val4')
795 795 self.assertEqual(env, None)
796 796 self.assertEqual(os.environ['var'], 'val2 val3="val4')
797 797
798 798 def test_env_set_bad_input(self):
799 799 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
800 800
801 801 def test_env_set_whitespace(self):
802 802 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
803 803
804 804
805 805 class CellMagicTestCase(TestCase):
806 806
807 807 def check_ident(self, magic):
808 808 # Manually called, we get the result
809 809 out = _ip.run_cell_magic(magic, "a", "b")
810 810 assert out == ("a", "b")
811 811 # Via run_cell, it goes into the user's namespace via displayhook
812 812 _ip.run_cell("%%" + magic + " c\nd\n")
813 813 assert _ip.user_ns["_"] == ("c", "d\n")
814 814
815 815 def test_cell_magic_func_deco(self):
816 816 "Cell magic using simple decorator"
817 817 @register_cell_magic
818 818 def cellm(line, cell):
819 819 return line, cell
820 820
821 821 self.check_ident('cellm')
822 822
823 823 def test_cell_magic_reg(self):
824 824 "Cell magic manually registered"
825 825 def cellm(line, cell):
826 826 return line, cell
827 827
828 828 _ip.register_magic_function(cellm, 'cell', 'cellm2')
829 829 self.check_ident('cellm2')
830 830
831 831 def test_cell_magic_class(self):
832 832 "Cell magics declared via a class"
833 833 @magics_class
834 834 class MyMagics(Magics):
835 835
836 836 @cell_magic
837 837 def cellm3(self, line, cell):
838 838 return line, cell
839 839
840 840 _ip.register_magics(MyMagics)
841 841 self.check_ident('cellm3')
842 842
843 843 def test_cell_magic_class2(self):
844 844 "Cell magics declared via a class, #2"
845 845 @magics_class
846 846 class MyMagics2(Magics):
847 847
848 848 @cell_magic('cellm4')
849 849 def cellm33(self, line, cell):
850 850 return line, cell
851 851
852 852 _ip.register_magics(MyMagics2)
853 853 self.check_ident('cellm4')
854 854 # Check that nothing is registered as 'cellm33'
855 855 c33 = _ip.find_cell_magic('cellm33')
856 856 assert c33 == None
857 857
858 858 def test_file():
859 859 """Basic %%writefile"""
860 860 ip = get_ipython()
861 861 with TemporaryDirectory() as td:
862 862 fname = os.path.join(td, 'file1')
863 863 ip.run_cell_magic("writefile", fname, u'\n'.join([
864 864 'line1',
865 865 'line2',
866 866 ]))
867 867 s = Path(fname).read_text()
868 868 assert "line1\n" in s
869 869 assert "line2" in s
870 870
871 871
872 872 @dec.skip_win32
873 873 def test_file_single_quote():
874 874 """Basic %%writefile with embedded single quotes"""
875 875 ip = get_ipython()
876 876 with TemporaryDirectory() as td:
877 877 fname = os.path.join(td, '\'file1\'')
878 878 ip.run_cell_magic("writefile", fname, u'\n'.join([
879 879 'line1',
880 880 'line2',
881 881 ]))
882 882 s = Path(fname).read_text()
883 883 assert "line1\n" in s
884 884 assert "line2" in s
885 885
886 886
887 887 @dec.skip_win32
888 888 def test_file_double_quote():
889 889 """Basic %%writefile with embedded double quotes"""
890 890 ip = get_ipython()
891 891 with TemporaryDirectory() as td:
892 892 fname = os.path.join(td, '"file1"')
893 893 ip.run_cell_magic("writefile", fname, u'\n'.join([
894 894 'line1',
895 895 'line2',
896 896 ]))
897 897 s = Path(fname).read_text()
898 898 assert "line1\n" in s
899 899 assert "line2" in s
900 900
901 901
902 902 def test_file_var_expand():
903 903 """%%writefile $filename"""
904 904 ip = get_ipython()
905 905 with TemporaryDirectory() as td:
906 906 fname = os.path.join(td, 'file1')
907 907 ip.user_ns['filename'] = fname
908 908 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
909 909 'line1',
910 910 'line2',
911 911 ]))
912 912 s = Path(fname).read_text()
913 913 assert "line1\n" in s
914 914 assert "line2" in s
915 915
916 916
917 917 def test_file_unicode():
918 918 """%%writefile with unicode cell"""
919 919 ip = get_ipython()
920 920 with TemporaryDirectory() as td:
921 921 fname = os.path.join(td, 'file1')
922 922 ip.run_cell_magic("writefile", fname, u'\n'.join([
923 923 u'linΓ©1',
924 924 u'linΓ©2',
925 925 ]))
926 926 with io.open(fname, encoding='utf-8') as f:
927 927 s = f.read()
928 928 assert "linΓ©1\n" in s
929 929 assert "linΓ©2" in s
930 930
931 931
932 932 def test_file_amend():
933 933 """%%writefile -a amends files"""
934 934 ip = get_ipython()
935 935 with TemporaryDirectory() as td:
936 936 fname = os.path.join(td, 'file2')
937 937 ip.run_cell_magic("writefile", fname, u'\n'.join([
938 938 'line1',
939 939 'line2',
940 940 ]))
941 941 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
942 942 'line3',
943 943 'line4',
944 944 ]))
945 945 s = Path(fname).read_text()
946 946 assert "line1\n" in s
947 947 assert "line3\n" in s
948 948
949 949
950 950 def test_file_spaces():
951 951 """%%file with spaces in filename"""
952 952 ip = get_ipython()
953 953 with TemporaryWorkingDirectory() as td:
954 954 fname = "file name"
955 955 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
956 956 'line1',
957 957 'line2',
958 958 ]))
959 959 s = Path(fname).read_text()
960 960 assert "line1\n" in s
961 961 assert "line2" in s
962 962
963 963
964 964 def test_script_config():
965 965 ip = get_ipython()
966 966 ip.config.ScriptMagics.script_magics = ['whoda']
967 967 sm = script.ScriptMagics(shell=ip)
968 968 assert "whoda" in sm.magics["cell"]
969 969
970 970
971 971 @pytest.fixture
972 972 def event_loop():
973 973 yield asyncio.get_event_loop_policy().get_event_loop()
974 974
975 975
976 976 @dec.skip_win32
977 977 @pytest.mark.skipif(
978 978 sys.platform == "win32", reason="This test does not run under Windows"
979 979 )
980 980 def test_script_out(event_loop):
981 981 assert event_loop.is_running() is False
982 982
983 983 ip = get_ipython()
984 984 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
985 985 assert event_loop.is_running() is False
986 986 assert ip.user_ns["output"] == "hi\n"
987 987
988 988
989 989 @dec.skip_win32
990 990 @pytest.mark.skipif(
991 991 sys.platform == "win32", reason="This test does not run under Windows"
992 992 )
993 993 def test_script_err(event_loop):
994 994 ip = get_ipython()
995 995 assert event_loop.is_running() is False
996 996 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
997 997 assert event_loop.is_running() is False
998 998 assert ip.user_ns["error"] == "hello\n"
999 999
1000 1000
1001 1001 @dec.skip_win32
1002 1002 @pytest.mark.skipif(
1003 1003 sys.platform == "win32", reason="This test does not run under Windows"
1004 1004 )
1005 1005 def test_script_out_err():
1006 1006
1007 1007 ip = get_ipython()
1008 1008 ip.run_cell_magic(
1009 1009 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1010 1010 )
1011 1011 assert ip.user_ns["output"] == "hi\n"
1012 1012 assert ip.user_ns["error"] == "hello\n"
1013 1013
1014 1014
1015 1015 @dec.skip_win32
1016 1016 @pytest.mark.skipif(
1017 1017 sys.platform == "win32", reason="This test does not run under Windows"
1018 1018 )
1019 1019 async def test_script_bg_out(event_loop):
1020 1020 ip = get_ipython()
1021 1021 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1022 1022 assert (await ip.user_ns["output"].read()) == b"hi\n"
1023 1023 ip.user_ns["output"].close()
1024 1024 event_loop.stop()
1025 1025
1026 1026
1027 1027 @dec.skip_win32
1028 1028 @pytest.mark.skipif(
1029 1029 sys.platform == "win32", reason="This test does not run under Windows"
1030 1030 )
1031 1031 async def test_script_bg_err():
1032 1032 ip = get_ipython()
1033 1033 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1034 1034 assert (await ip.user_ns["error"].read()) == b"hello\n"
1035 1035 ip.user_ns["error"].close()
1036 1036
1037 1037
1038 1038 @dec.skip_win32
1039 1039 @pytest.mark.skipif(
1040 1040 sys.platform == "win32", reason="This test does not run under Windows"
1041 1041 )
1042 1042 async def test_script_bg_out_err():
1043 1043 ip = get_ipython()
1044 1044 ip.run_cell_magic(
1045 1045 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1046 1046 )
1047 1047 assert (await ip.user_ns["output"].read()) == b"hi\n"
1048 1048 assert (await ip.user_ns["error"].read()) == b"hello\n"
1049 1049 ip.user_ns["output"].close()
1050 1050 ip.user_ns["error"].close()
1051 1051
1052 1052
1053 1053 def test_script_defaults():
1054 1054 ip = get_ipython()
1055 1055 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1056 1056 try:
1057 1057 find_cmd(cmd)
1058 1058 except Exception:
1059 1059 pass
1060 1060 else:
1061 1061 assert cmd in ip.magics_manager.magics["cell"]
1062 1062
1063 1063
1064 1064 @magics_class
1065 1065 class FooFoo(Magics):
1066 1066 """class with both %foo and %%foo magics"""
1067 1067 @line_magic('foo')
1068 1068 def line_foo(self, line):
1069 1069 "I am line foo"
1070 1070 pass
1071 1071
1072 1072 @cell_magic("foo")
1073 1073 def cell_foo(self, line, cell):
1074 1074 "I am cell foo, not line foo"
1075 1075 pass
1076 1076
1077 1077 def test_line_cell_info():
1078 1078 """%%foo and %foo magics are distinguishable to inspect"""
1079 1079 ip = get_ipython()
1080 1080 ip.magics_manager.register(FooFoo)
1081 1081 oinfo = ip.object_inspect("foo")
1082 1082 assert oinfo["found"] is True
1083 1083 assert oinfo["ismagic"] is True
1084 1084
1085 1085 oinfo = ip.object_inspect("%%foo")
1086 1086 assert oinfo["found"] is True
1087 1087 assert oinfo["ismagic"] is True
1088 1088 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1089 1089
1090 1090 oinfo = ip.object_inspect("%foo")
1091 1091 assert oinfo["found"] is True
1092 1092 assert oinfo["ismagic"] is True
1093 1093 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1094 1094
1095 1095
1096 1096 def test_multiple_magics():
1097 1097 ip = get_ipython()
1098 1098 foo1 = FooFoo(ip)
1099 1099 foo2 = FooFoo(ip)
1100 1100 mm = ip.magics_manager
1101 1101 mm.register(foo1)
1102 1102 assert mm.magics["line"]["foo"].__self__ is foo1
1103 1103 mm.register(foo2)
1104 1104 assert mm.magics["line"]["foo"].__self__ is foo2
1105 1105
1106 1106
1107 1107 def test_alias_magic():
1108 1108 """Test %alias_magic."""
1109 1109 ip = get_ipython()
1110 1110 mm = ip.magics_manager
1111 1111
1112 1112 # Basic operation: both cell and line magics are created, if possible.
1113 1113 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1114 1114 assert "timeit_alias" in mm.magics["line"]
1115 1115 assert "timeit_alias" in mm.magics["cell"]
1116 1116
1117 1117 # --cell is specified, line magic not created.
1118 1118 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1119 1119 assert "timeit_cell_alias" not in mm.magics["line"]
1120 1120 assert "timeit_cell_alias" in mm.magics["cell"]
1121 1121
1122 1122 # Test that line alias is created successfully.
1123 1123 ip.run_line_magic("alias_magic", "--line env_alias env")
1124 1124 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1125 1125
1126 1126 # Test that line alias with parameters passed in is created successfully.
1127 1127 ip.run_line_magic(
1128 1128 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1129 1129 )
1130 1130 assert "history_alias" in mm.magics["line"]
1131 1131
1132 1132
1133 1133 def test_save():
1134 1134 """Test %save."""
1135 1135 ip = get_ipython()
1136 1136 ip.history_manager.reset() # Clear any existing history.
1137 1137 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1138 1138 for i, cmd in enumerate(cmds, start=1):
1139 1139 ip.history_manager.store_inputs(i, cmd)
1140 1140 with TemporaryDirectory() as tmpdir:
1141 1141 file = os.path.join(tmpdir, "testsave.py")
1142 1142 ip.run_line_magic("save", "%s 1-10" % file)
1143 1143 content = Path(file).read_text()
1144 1144 assert content.count(cmds[0]) == 1
1145 1145 assert "coding: utf-8" in content
1146 1146 ip.run_line_magic("save", "-a %s 1-10" % file)
1147 1147 content = Path(file).read_text()
1148 1148 assert content.count(cmds[0]) == 2
1149 1149 assert "coding: utf-8" in content
1150 1150
1151 1151
1152 1152 def test_save_with_no_args():
1153 1153 ip = get_ipython()
1154 1154 ip.history_manager.reset() # Clear any existing history.
1155 1155 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1156 1156 for i, cmd in enumerate(cmds, start=1):
1157 1157 ip.history_manager.store_inputs(i, cmd)
1158 1158
1159 1159 with TemporaryDirectory() as tmpdir:
1160 1160 path = os.path.join(tmpdir, "testsave.py")
1161 1161 ip.run_line_magic("save", path)
1162 1162 content = Path(path).read_text()
1163 1163 expected_content = dedent(
1164 1164 """\
1165 1165 # coding: utf-8
1166 1166 a=1
1167 1167 def b():
1168 1168 return a**2
1169 1169 print(a, b())
1170 1170 """
1171 1171 )
1172 1172 assert content == expected_content
1173 1173
1174 1174
1175 1175 def test_store():
1176 1176 """Test %store."""
1177 1177 ip = get_ipython()
1178 1178 ip.run_line_magic('load_ext', 'storemagic')
1179 1179
1180 1180 # make sure the storage is empty
1181 1181 ip.run_line_magic("store", "-z")
1182 1182 ip.user_ns["var"] = 42
1183 1183 ip.run_line_magic("store", "var")
1184 1184 ip.user_ns["var"] = 39
1185 1185 ip.run_line_magic("store", "-r")
1186 1186 assert ip.user_ns["var"] == 42
1187 1187
1188 1188 ip.run_line_magic("store", "-d var")
1189 1189 ip.user_ns["var"] = 39
1190 1190 ip.run_line_magic("store", "-r")
1191 1191 assert ip.user_ns["var"] == 39
1192 1192
1193 1193
1194 1194 def _run_edit_test(arg_s, exp_filename=None,
1195 1195 exp_lineno=-1,
1196 1196 exp_contents=None,
1197 1197 exp_is_temp=None):
1198 1198 ip = get_ipython()
1199 1199 M = code.CodeMagics(ip)
1200 1200 last_call = ['','']
1201 1201 opts,args = M.parse_options(arg_s,'prxn:')
1202 1202 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1203 1203
1204 1204 if exp_filename is not None:
1205 1205 assert exp_filename == filename
1206 1206 if exp_contents is not None:
1207 1207 with io.open(filename, 'r', encoding='utf-8') as f:
1208 1208 contents = f.read()
1209 1209 assert exp_contents == contents
1210 1210 if exp_lineno != -1:
1211 1211 assert exp_lineno == lineno
1212 1212 if exp_is_temp is not None:
1213 1213 assert exp_is_temp == is_temp
1214 1214
1215 1215
1216 1216 def test_edit_interactive():
1217 1217 """%edit on interactively defined objects"""
1218 1218 ip = get_ipython()
1219 1219 n = ip.execution_count
1220 1220 ip.run_cell("def foo(): return 1", store_history=True)
1221 1221
1222 1222 with pytest.raises(code.InteractivelyDefined) as e:
1223 1223 _run_edit_test("foo")
1224 1224 assert e.value.index == n
1225 1225
1226 1226
1227 1227 def test_edit_cell():
1228 1228 """%edit [cell id]"""
1229 1229 ip = get_ipython()
1230 1230
1231 1231 ip.run_cell("def foo(): return 1", store_history=True)
1232 1232
1233 1233 # test
1234 1234 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1235 1235
1236 1236 def test_edit_fname():
1237 1237 """%edit file"""
1238 1238 # test
1239 1239 _run_edit_test("test file.py", exp_filename="test file.py")
1240 1240
1241 1241 def test_bookmark():
1242 1242 ip = get_ipython()
1243 1243 ip.run_line_magic('bookmark', 'bmname')
1244 1244 with tt.AssertPrints('bmname'):
1245 1245 ip.run_line_magic('bookmark', '-l')
1246 1246 ip.run_line_magic('bookmark', '-d bmname')
1247 1247
1248 1248 def test_ls_magic():
1249 1249 ip = get_ipython()
1250 1250 json_formatter = ip.display_formatter.formatters['application/json']
1251 1251 json_formatter.enabled = True
1252 1252 lsmagic = ip.magic('lsmagic')
1253 1253 with warnings.catch_warnings(record=True) as w:
1254 1254 j = json_formatter(lsmagic)
1255 1255 assert sorted(j) == ["cell", "line"]
1256 1256 assert w == [] # no warnings
1257 1257
1258 1258
1259 1259 def test_strip_initial_indent():
1260 1260 def sii(s):
1261 1261 lines = s.splitlines()
1262 1262 return '\n'.join(code.strip_initial_indent(lines))
1263 1263
1264 1264 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1265 1265 assert sii(" a\n b\nc") == "a\n b\nc"
1266 1266 assert sii("a\n b") == "a\n b"
1267 1267
1268 1268 def test_logging_magic_quiet_from_arg():
1269 1269 _ip.config.LoggingMagics.quiet = False
1270 1270 lm = logging.LoggingMagics(shell=_ip)
1271 1271 with TemporaryDirectory() as td:
1272 1272 try:
1273 1273 with tt.AssertNotPrints(re.compile("Activating.*")):
1274 1274 lm.logstart('-q {}'.format(
1275 1275 os.path.join(td, "quiet_from_arg.log")))
1276 1276 finally:
1277 1277 _ip.logger.logstop()
1278 1278
1279 1279 def test_logging_magic_quiet_from_config():
1280 1280 _ip.config.LoggingMagics.quiet = True
1281 1281 lm = logging.LoggingMagics(shell=_ip)
1282 1282 with TemporaryDirectory() as td:
1283 1283 try:
1284 1284 with tt.AssertNotPrints(re.compile("Activating.*")):
1285 1285 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1286 1286 finally:
1287 1287 _ip.logger.logstop()
1288 1288
1289 1289
1290 1290 def test_logging_magic_not_quiet():
1291 1291 _ip.config.LoggingMagics.quiet = False
1292 1292 lm = logging.LoggingMagics(shell=_ip)
1293 1293 with TemporaryDirectory() as td:
1294 1294 try:
1295 1295 with tt.AssertPrints(re.compile("Activating.*")):
1296 1296 lm.logstart(os.path.join(td, "not_quiet.log"))
1297 1297 finally:
1298 1298 _ip.logger.logstop()
1299 1299
1300 1300
1301 1301 def test_time_no_var_expand():
1302 1302 _ip.user_ns['a'] = 5
1303 1303 _ip.user_ns['b'] = []
1304 1304 _ip.magic('time b.append("{a}")')
1305 1305 assert _ip.user_ns['b'] == ['{a}']
1306 1306
1307 1307
1308 1308 # this is slow, put at the end for local testing.
1309 1309 def test_timeit_arguments():
1310 1310 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1311 if sys.version_info < (3,7):
1312 _ip.magic("timeit -n1 -r1 ('#')")
1313 else:
1314 # 3.7 optimize no-op statement like above out, and complain there is
1315 # nothing in the for loop.
1316 _ip.magic("timeit -n1 -r1 a=('#')")
1311 _ip.magic("timeit -n1 -r1 a=('#')")
1317 1312
1318 1313
1319 1314 TEST_MODULE = """
1320 1315 print('Loaded my_tmp')
1321 1316 if __name__ == "__main__":
1322 1317 print('I just ran a script')
1323 1318 """
1324 1319
1325 1320
1326 1321 def test_run_module_from_import_hook():
1327 1322 "Test that a module can be loaded via an import hook"
1328 1323 with TemporaryDirectory() as tmpdir:
1329 1324 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1330 1325 Path(fullpath).write_text(TEST_MODULE)
1331 1326
1332 1327 import importlib.abc
1333 1328 import importlib.util
1334 1329
1335 1330 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1336 1331 def find_spec(self, fullname, path, target=None):
1337 1332 if fullname == "my_tmp":
1338 1333 return importlib.util.spec_from_loader(fullname, self)
1339 1334
1340 1335 def get_filename(self, fullname):
1341 1336 if fullname != "my_tmp":
1342 1337 raise ImportError(f"unexpected module name '{fullname}'")
1343 1338 return fullpath
1344 1339
1345 1340 def get_data(self, path):
1346 1341 if not Path(path).samefile(fullpath):
1347 1342 raise OSError(f"expected path '{fullpath}', got '{path}'")
1348 1343 return Path(fullpath).read_text()
1349 1344
1350 1345 sys.meta_path.insert(0, MyTempImporter())
1351 1346
1352 1347 with capture_output() as captured:
1353 1348 _ip.magic("run -m my_tmp")
1354 1349 _ip.run_cell("import my_tmp")
1355 1350
1356 1351 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1357 1352 assert output == captured.stdout
1358 1353
1359 1354 sys.meta_path.pop(0)
General Comments 0
You need to be logged in to leave comments. Login now