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