##// END OF EJS Templates
Fix cell magic transformation
Thomas Kluyver -
Show More
@@ -0,0 +1,21 b''
1 """Tests for the line-based transformers in IPython.core.inputtransformer2
2
3 Line-based transformers are the simpler ones; token-based transformers are
4 more complex.
5 """
6 import nose.tools as nt
7
8 from IPython.core import inputtransformer2 as ipt2
9
10 SIMPLE = ("""\
11 %%foo arg
12 body 1
13 body 2
14 """, """\
15 get_ipython().run_cell_magic('foo', 'arg', 'body 1\\nbody 2\\n')
16 """)
17
18 def test_cell_magic():
19 for sample, expected in [SIMPLE]:
20 nt.assert_equal(ipt2.cell_magic(sample.splitlines(keepends=True)),
21 expected.splitlines(keepends=True))
@@ -1,434 +1,434 b''
1 import re
1 import re
2 from typing import List, Tuple
2 from typing import List, Tuple
3 from IPython.utils import tokenize2
3 from IPython.utils import tokenize2
4 from IPython.utils.tokenutil import generate_tokens
4 from IPython.utils.tokenutil import generate_tokens
5
5
6 def leading_indent(lines):
6 def leading_indent(lines):
7 """Remove leading indentation.
7 """Remove leading indentation.
8
8
9 If the first line starts with a spaces or tabs, the same whitespace will be
9 If the first line starts with a spaces or tabs, the same whitespace will be
10 removed from each following line.
10 removed from each following line.
11 """
11 """
12 m = re.match(r'^[ \t]+', lines[0])
12 m = re.match(r'^[ \t]+', lines[0])
13 if not m:
13 if not m:
14 return lines
14 return lines
15 space = m.group(0)
15 space = m.group(0)
16 n = len(space)
16 n = len(space)
17 return [l[n:] if l.startswith(space) else l
17 return [l[n:] if l.startswith(space) else l
18 for l in lines]
18 for l in lines]
19
19
20 class PromptStripper:
20 class PromptStripper:
21 """Remove matching input prompts from a block of input.
21 """Remove matching input prompts from a block of input.
22
22
23 Parameters
23 Parameters
24 ----------
24 ----------
25 prompt_re : regular expression
25 prompt_re : regular expression
26 A regular expression matching any input prompt (including continuation)
26 A regular expression matching any input prompt (including continuation)
27 initial_re : regular expression, optional
27 initial_re : regular expression, optional
28 A regular expression matching only the initial prompt, but not continuation.
28 A regular expression matching only the initial prompt, but not continuation.
29 If no initial expression is given, prompt_re will be used everywhere.
29 If no initial expression is given, prompt_re will be used everywhere.
30 Used mainly for plain Python prompts, where the continuation prompt
30 Used mainly for plain Python prompts, where the continuation prompt
31 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
31 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
32
32
33 If initial_re and prompt_re differ,
33 If initial_re and prompt_re differ,
34 only initial_re will be tested against the first line.
34 only initial_re will be tested against the first line.
35 If any prompt is found on the first two lines,
35 If any prompt is found on the first two lines,
36 prompts will be stripped from the rest of the block.
36 prompts will be stripped from the rest of the block.
37 """
37 """
38 def __init__(self, prompt_re, initial_re=None):
38 def __init__(self, prompt_re, initial_re=None):
39 self.prompt_re = prompt_re
39 self.prompt_re = prompt_re
40 self.initial_re = initial_re or prompt_re
40 self.initial_re = initial_re or prompt_re
41
41
42 def _strip(self, lines):
42 def _strip(self, lines):
43 return [self.prompt_re.sub('', l, count=1) for l in lines]
43 return [self.prompt_re.sub('', l, count=1) for l in lines]
44
44
45 def __call__(self, lines):
45 def __call__(self, lines):
46 if self.initial_re.match(lines[0]) or \
46 if self.initial_re.match(lines[0]) or \
47 (len(lines) > 1 and self.prompt_re.match(lines[1])):
47 (len(lines) > 1 and self.prompt_re.match(lines[1])):
48 return self._strip(lines)
48 return self._strip(lines)
49 return lines
49 return lines
50
50
51 classic_prompt = PromptStripper(
51 classic_prompt = PromptStripper(
52 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
52 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
53 initial_re=re.compile(r'^>>>( |$)')
53 initial_re=re.compile(r'^>>>( |$)')
54 )
54 )
55
55
56 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
56 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
57
57
58 def cell_magic(lines):
58 def cell_magic(lines):
59 if not lines[0].startswith('%%'):
59 if not lines[0].startswith('%%'):
60 return lines
60 return lines
61 if re.match('%%\w+\?', lines[0]):
61 if re.match('%%\w+\?', lines[0]):
62 # This case will be handled by help_end
62 # This case will be handled by help_end
63 return lines
63 return lines
64 magic_name, first_line = lines[0][2:].partition(' ')
64 magic_name, _, first_line = lines[0][2:-1].partition(' ')
65 body = '\n'.join(lines[1:])
65 body = ''.join(lines[1:])
66 return ['get_ipython().run_cell_magic(%r, %r, %r)' % (magic_name, first_line, body)]
66 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
67 % (magic_name, first_line, body)]
67
68
68 line_transforms = [
69 line_transforms = [
69 leading_indent,
70 leading_indent,
70 classic_prompt,
71 classic_prompt,
71 ipython_prompt,
72 ipython_prompt,
72 cell_magic,
73 cell_magic,
73 ]
74 ]
74
75
75 # -----
76 # -----
76
77
77 def _find_assign_op(token_line):
78 def _find_assign_op(token_line):
78 # Find the first assignment in the line ('=' not inside brackets)
79 # Find the first assignment in the line ('=' not inside brackets)
79 # We don't try to support multiple special assignment (a = b = %foo)
80 # We don't try to support multiple special assignment (a = b = %foo)
80 paren_level = 0
81 paren_level = 0
81 for i, ti in enumerate(token_line):
82 for i, ti in enumerate(token_line):
82 s = ti.string
83 s = ti.string
83 if s == '=' and paren_level == 0:
84 if s == '=' and paren_level == 0:
84 return i
85 return i
85 if s in '([{':
86 if s in '([{':
86 paren_level += 1
87 paren_level += 1
87 elif s in ')]}':
88 elif s in ')]}':
88 paren_level -= 1
89 paren_level -= 1
89
90
90 def find_end_of_continued_line(lines, start_line: int):
91 def find_end_of_continued_line(lines, start_line: int):
91 """Find the last line of a line explicitly extended using backslashes.
92 """Find the last line of a line explicitly extended using backslashes.
92
93
93 Uses 0-indexed line numbers.
94 Uses 0-indexed line numbers.
94 """
95 """
95 end_line = start_line
96 end_line = start_line
96 while lines[end_line].endswith('\\\n'):
97 while lines[end_line].endswith('\\\n'):
97 end_line += 1
98 end_line += 1
98 if end_line >= len(lines):
99 if end_line >= len(lines):
99 break
100 break
100 return end_line
101 return end_line
101
102
102 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
103 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
103 """Assemble pieces of a continued line into a single line.
104 """Assemble pieces of a continued line into a single line.
104
105
105 Uses 0-indexed line numbers. *start* is (lineno, colno).
106 Uses 0-indexed line numbers. *start* is (lineno, colno).
106 """
107 """
107 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
108 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
108 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
109 return ' '.join([p[:-2] for p in parts[:-1]] # Strip backslash+newline
109 + [parts[-1][:-1]]) # Strip newline from last line
110 + [parts[-1][:-1]]) # Strip newline from last line
110
111
111 class TokenTransformBase:
112 class TokenTransformBase:
112 # Lower numbers -> higher priority (for matches in the same location)
113 # Lower numbers -> higher priority (for matches in the same location)
113 priority = 10
114 priority = 10
114
115
115 def sortby(self):
116 def sortby(self):
116 return self.start_line, self.start_col, self.priority
117 return self.start_line, self.start_col, self.priority
117
118
118 def __init__(self, start):
119 def __init__(self, start):
119 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
120 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
120 self.start_col = start[1]
121 self.start_col = start[1]
121
122
122 def transform(self, lines: List[str]):
123 def transform(self, lines: List[str]):
123 raise NotImplementedError
124 raise NotImplementedError
124
125
125 class MagicAssign(TokenTransformBase):
126 class MagicAssign(TokenTransformBase):
126 @classmethod
127 @classmethod
127 def find(cls, tokens_by_line):
128 def find(cls, tokens_by_line):
128 """Find the first magic assignment (a = %foo) in the cell.
129 """Find the first magic assignment (a = %foo) in the cell.
129
130
130 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
131 Returns (line, column) of the % if found, or None. *line* is 1-indexed.
131 """
132 """
132 for line in tokens_by_line:
133 for line in tokens_by_line:
133 assign_ix = _find_assign_op(line)
134 assign_ix = _find_assign_op(line)
134 if (assign_ix is not None) \
135 if (assign_ix is not None) \
135 and (len(line) >= assign_ix + 2) \
136 and (len(line) >= assign_ix + 2) \
136 and (line[assign_ix+1].string == '%') \
137 and (line[assign_ix+1].string == '%') \
137 and (line[assign_ix+2].type == tokenize2.NAME):
138 and (line[assign_ix+2].type == tokenize2.NAME):
138 return cls(line[assign_ix+1].start)
139 return cls(line[assign_ix+1].start)
139
140
140 def transform(self, lines: List[str]):
141 def transform(self, lines: List[str]):
141 """Transform a magic assignment found by find
142 """Transform a magic assignment found by find
142 """
143 """
143 start_line, start_col = self.start_line, self.start_col
144 start_line, start_col = self.start_line, self.start_col
144 lhs = lines[start_line][:start_col]
145 lhs = lines[start_line][:start_col]
145 end_line = find_end_of_continued_line(lines, start_line)
146 end_line = find_end_of_continued_line(lines, start_line)
146 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
147 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
147 assert rhs.startswith('%'), rhs
148 assert rhs.startswith('%'), rhs
148 magic_name, _, args = rhs[1:].partition(' ')
149 magic_name, _, args = rhs[1:].partition(' ')
149
150
150 lines_before = lines[:start_line]
151 lines_before = lines[:start_line]
151 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
152 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
152 new_line = lhs + call + '\n'
153 new_line = lhs + call + '\n'
153 lines_after = lines[end_line+1:]
154 lines_after = lines[end_line+1:]
154
155
155 return lines_before + [new_line] + lines_after
156 return lines_before + [new_line] + lines_after
156
157
157
158
158 class SystemAssign(TokenTransformBase):
159 class SystemAssign(TokenTransformBase):
159 @classmethod
160 @classmethod
160 def find(cls, tokens_by_line):
161 def find(cls, tokens_by_line):
161 """Find the first system assignment (a = !foo) in the cell.
162 """Find the first system assignment (a = !foo) in the cell.
162
163
163 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
164 Returns (line, column) of the ! if found, or None. *line* is 1-indexed.
164 """
165 """
165 for line in tokens_by_line:
166 for line in tokens_by_line:
166 assign_ix = _find_assign_op(line)
167 assign_ix = _find_assign_op(line)
167 if (assign_ix is not None) \
168 if (assign_ix is not None) \
168 and (len(line) >= assign_ix + 2) \
169 and (len(line) >= assign_ix + 2) \
169 and (line[assign_ix + 1].type == tokenize2.ERRORTOKEN):
170 and (line[assign_ix + 1].type == tokenize2.ERRORTOKEN):
170 ix = assign_ix + 1
171 ix = assign_ix + 1
171
172
172 while ix < len(line) and line[ix].type == tokenize2.ERRORTOKEN:
173 while ix < len(line) and line[ix].type == tokenize2.ERRORTOKEN:
173 if line[ix].string == '!':
174 if line[ix].string == '!':
174 return cls(line[ix].start)
175 return cls(line[ix].start)
175 elif not line[ix].string.isspace():
176 elif not line[ix].string.isspace():
176 break
177 break
177 ix += 1
178 ix += 1
178
179
179 def transform(self, lines: List[str]):
180 def transform(self, lines: List[str]):
180 """Transform a system assignment found by find
181 """Transform a system assignment found by find
181 """
182 """
182 start_line, start_col = self.start_line, self.start_col
183 start_line, start_col = self.start_line, self.start_col
183
184
184 lhs = lines[start_line][:start_col]
185 lhs = lines[start_line][:start_col]
185 end_line = find_end_of_continued_line(lines, start_line)
186 end_line = find_end_of_continued_line(lines, start_line)
186 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
187 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
187 assert rhs.startswith('!'), rhs
188 assert rhs.startswith('!'), rhs
188 cmd = rhs[1:]
189 cmd = rhs[1:]
189
190
190 lines_before = lines[:start_line]
191 lines_before = lines[:start_line]
191 call = "get_ipython().getoutput({!r})".format(cmd)
192 call = "get_ipython().getoutput({!r})".format(cmd)
192 new_line = lhs + call + '\n'
193 new_line = lhs + call + '\n'
193 lines_after = lines[end_line + 1:]
194 lines_after = lines[end_line + 1:]
194
195
195 return lines_before + [new_line] + lines_after
196 return lines_before + [new_line] + lines_after
196
197
197 # The escape sequences that define the syntax transformations IPython will
198 # The escape sequences that define the syntax transformations IPython will
198 # apply to user input. These can NOT be just changed here: many regular
199 # apply to user input. These can NOT be just changed here: many regular
199 # expressions and other parts of the code may use their hardcoded values, and
200 # expressions and other parts of the code may use their hardcoded values, and
200 # for all intents and purposes they constitute the 'IPython syntax', so they
201 # for all intents and purposes they constitute the 'IPython syntax', so they
201 # should be considered fixed.
202 # should be considered fixed.
202
203
203 ESC_SHELL = '!' # Send line to underlying system shell
204 ESC_SHELL = '!' # Send line to underlying system shell
204 ESC_SH_CAP = '!!' # Send line to system shell and capture output
205 ESC_SH_CAP = '!!' # Send line to system shell and capture output
205 ESC_HELP = '?' # Find information about object
206 ESC_HELP = '?' # Find information about object
206 ESC_HELP2 = '??' # Find extra-detailed information about object
207 ESC_HELP2 = '??' # Find extra-detailed information about object
207 ESC_MAGIC = '%' # Call magic function
208 ESC_MAGIC = '%' # Call magic function
208 ESC_MAGIC2 = '%%' # Call cell-magic function
209 ESC_MAGIC2 = '%%' # Call cell-magic function
209 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
210 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
210 ESC_QUOTE2 = ';' # Quote all args as a single string, call
211 ESC_QUOTE2 = ';' # Quote all args as a single string, call
211 ESC_PAREN = '/' # Call first argument with rest of line as arguments
212 ESC_PAREN = '/' # Call first argument with rest of line as arguments
212
213
213 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
214 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
214 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
215 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
215
216
216 def _make_help_call(target, esc, next_input=None):
217 def _make_help_call(target, esc, next_input=None):
217 """Prepares a pinfo(2)/psearch call from a target name and the escape
218 """Prepares a pinfo(2)/psearch call from a target name and the escape
218 (i.e. ? or ??)"""
219 (i.e. ? or ??)"""
219 method = 'pinfo2' if esc == '??' \
220 method = 'pinfo2' if esc == '??' \
220 else 'psearch' if '*' in target \
221 else 'psearch' if '*' in target \
221 else 'pinfo'
222 else 'pinfo'
222 arg = " ".join([method, target])
223 arg = " ".join([method, target])
223 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
224 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
224 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
225 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
225 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
226 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
226 if next_input is None:
227 if next_input is None:
227 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
228 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
228 else:
229 else:
229 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
230 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
230 (next_input, t_magic_name, t_magic_arg_s)
231 (next_input, t_magic_name, t_magic_arg_s)
231
232
232 def _tr_help(content):
233 def _tr_help(content):
233 "Translate lines escaped with: ?"
234 "Translate lines escaped with: ?"
234 # A naked help line should just fire the intro help screen
235 # A naked help line should just fire the intro help screen
235 if not content:
236 if not content:
236 return 'get_ipython().show_usage()'
237 return 'get_ipython().show_usage()'
237
238
238 return _make_help_call(content, '?')
239 return _make_help_call(content, '?')
239
240
240 def _tr_help2(content):
241 def _tr_help2(content):
241 "Translate lines escaped with: ??"
242 "Translate lines escaped with: ??"
242 # A naked help line should just fire the intro help screen
243 # A naked help line should just fire the intro help screen
243 if not content:
244 if not content:
244 return 'get_ipython().show_usage()'
245 return 'get_ipython().show_usage()'
245
246
246 return _make_help_call(content, '??')
247 return _make_help_call(content, '??')
247
248
248 def _tr_magic(content):
249 def _tr_magic(content):
249 "Translate lines escaped with: %"
250 "Translate lines escaped with: %"
250 name, _, args = content.partition(' ')
251 name, _, args = content.partition(' ')
251 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
252 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
252
253
253 def _tr_quote(content):
254 def _tr_quote(content):
254 "Translate lines escaped with: ,"
255 "Translate lines escaped with: ,"
255 name, _, args = content.partition(' ')
256 name, _, args = content.partition(' ')
256 return '%s("%s")' % (name, '", "'.join(args.split()) )
257 return '%s("%s")' % (name, '", "'.join(args.split()) )
257
258
258 def _tr_quote2(content):
259 def _tr_quote2(content):
259 "Translate lines escaped with: ;"
260 "Translate lines escaped with: ;"
260 name, _, args = content.partition(' ')
261 name, _, args = content.partition(' ')
261 return '%s("%s")' % (name, args)
262 return '%s("%s")' % (name, args)
262
263
263 def _tr_paren(content):
264 def _tr_paren(content):
264 "Translate lines escaped with: /"
265 "Translate lines escaped with: /"
265 name, _, args = content.partition(' ')
266 name, _, args = content.partition(' ')
266 return '%s(%s)' % (name, ", ".join(args.split()))
267 return '%s(%s)' % (name, ", ".join(args.split()))
267
268
268 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
269 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
269 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
270 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
270 ESC_HELP : _tr_help,
271 ESC_HELP : _tr_help,
271 ESC_HELP2 : _tr_help2,
272 ESC_HELP2 : _tr_help2,
272 ESC_MAGIC : _tr_magic,
273 ESC_MAGIC : _tr_magic,
273 ESC_QUOTE : _tr_quote,
274 ESC_QUOTE : _tr_quote,
274 ESC_QUOTE2 : _tr_quote2,
275 ESC_QUOTE2 : _tr_quote2,
275 ESC_PAREN : _tr_paren }
276 ESC_PAREN : _tr_paren }
276
277
277 class EscapedCommand(TokenTransformBase):
278 class EscapedCommand(TokenTransformBase):
278 @classmethod
279 @classmethod
279 def find(cls, tokens_by_line):
280 def find(cls, tokens_by_line):
280 """Find the first escaped command (%foo, !foo, etc.) in the cell.
281 """Find the first escaped command (%foo, !foo, etc.) in the cell.
281
282
282 Returns (line, column) of the escape if found, or None. *line* is 1-indexed.
283 Returns (line, column) of the escape if found, or None. *line* is 1-indexed.
283 """
284 """
284 for line in tokens_by_line:
285 for line in tokens_by_line:
285 ix = 0
286 ix = 0
286 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
287 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
287 ix += 1
288 ix += 1
288 if line[ix].string in ESCAPE_SINGLES:
289 if line[ix].string in ESCAPE_SINGLES:
289 return cls(line[ix].start)
290 return cls(line[ix].start)
290
291
291 def transform(self, lines):
292 def transform(self, lines):
292 start_line, start_col = self.start_line, self.start_col
293 start_line, start_col = self.start_line, self.start_col
293
294
294 indent = lines[start_line][:start_col]
295 indent = lines[start_line][:start_col]
295 end_line = find_end_of_continued_line(lines, start_line)
296 end_line = find_end_of_continued_line(lines, start_line)
296 line = assemble_continued_line(lines, (start_line, start_col), end_line)
297 line = assemble_continued_line(lines, (start_line, start_col), end_line)
297
298
298 if line[:2] in ESCAPE_DOUBLES:
299 if line[:2] in ESCAPE_DOUBLES:
299 escape, content = line[:2], line[2:]
300 escape, content = line[:2], line[2:]
300 else:
301 else:
301 escape, content = line[:1], line[1:]
302 escape, content = line[:1], line[1:]
302 call = tr[escape](content)
303 call = tr[escape](content)
303
304
304 lines_before = lines[:start_line]
305 lines_before = lines[:start_line]
305 new_line = indent + call + '\n'
306 new_line = indent + call + '\n'
306 lines_after = lines[end_line + 1:]
307 lines_after = lines[end_line + 1:]
307
308
308 return lines_before + [new_line] + lines_after
309 return lines_before + [new_line] + lines_after
309
310
310 _help_end_re = re.compile(r"""(%{0,2}
311 _help_end_re = re.compile(r"""(%{0,2}
311 [a-zA-Z_*][\w*]* # Variable name
312 [a-zA-Z_*][\w*]* # Variable name
312 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
313 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
313 )
314 )
314 (\?\??)$ # ? or ??
315 (\?\??)$ # ? or ??
315 """,
316 """,
316 re.VERBOSE)
317 re.VERBOSE)
317
318
318 class HelpEnd(TokenTransformBase):
319 class HelpEnd(TokenTransformBase):
319 # This needs to be higher priority (lower number) than EscapedCommand so
320 # This needs to be higher priority (lower number) than EscapedCommand so
320 # that inspecting magics (%foo?) works.
321 # that inspecting magics (%foo?) works.
321 priority = 5
322 priority = 5
322
323
323 def __init__(self, start, q_locn):
324 def __init__(self, start, q_locn):
324 super().__init__(start)
325 super().__init__(start)
325 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
326 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
326 self.q_col = q_locn[1]
327 self.q_col = q_locn[1]
327
328
328 @classmethod
329 @classmethod
329 def find(cls, tokens_by_line):
330 def find(cls, tokens_by_line):
330 for line in tokens_by_line:
331 for line in tokens_by_line:
331 # Last token is NEWLINE; look at last but one
332 # Last token is NEWLINE; look at last but one
332 if len(line) > 2 and line[-2].string == '?':
333 if len(line) > 2 and line[-2].string == '?':
333 # Find the first token that's not INDENT/DEDENT
334 # Find the first token that's not INDENT/DEDENT
334 ix = 0
335 ix = 0
335 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
336 while line[ix].type in {tokenize2.INDENT, tokenize2.DEDENT}:
336 ix += 1
337 ix += 1
337 return cls(line[ix].start, line[-2].start)
338 return cls(line[ix].start, line[-2].start)
338
339
339 def transform(self, lines):
340 def transform(self, lines):
340 piece = ''.join(lines[self.start_line:self.q_line+1])
341 piece = ''.join(lines[self.start_line:self.q_line+1])
341 indent, content = piece[:self.start_col], piece[self.start_col:]
342 indent, content = piece[:self.start_col], piece[self.start_col:]
342 lines_before = lines[:self.start_line]
343 lines_before = lines[:self.start_line]
343 lines_after = lines[self.q_line + 1:]
344 lines_after = lines[self.q_line + 1:]
344
345
345 m = _help_end_re.search(content)
346 m = _help_end_re.search(content)
346 assert m is not None, content
347 assert m is not None, content
347 target = m.group(1)
348 target = m.group(1)
348 esc = m.group(3)
349 esc = m.group(3)
349
350
350 # If we're mid-command, put it back on the next prompt for the user.
351 # If we're mid-command, put it back on the next prompt for the user.
351 next_input = None
352 next_input = None
352 if (not lines_before) and (not lines_after) \
353 if (not lines_before) and (not lines_after) \
353 and content.strip() != m.group(0):
354 and content.strip() != m.group(0):
354 next_input = content.rstrip('?\n')
355 next_input = content.rstrip('?\n')
355
356
356 call = _make_help_call(target, esc, next_input=next_input)
357 call = _make_help_call(target, esc, next_input=next_input)
357 new_line = indent + call + '\n'
358 new_line = indent + call + '\n'
358
359
359 return lines_before + [new_line] + lines_after
360 return lines_before + [new_line] + lines_after
360
361
361 def make_tokens_by_line(lines):
362 def make_tokens_by_line(lines):
362 tokens_by_line = [[]]
363 tokens_by_line = [[]]
363 for token in generate_tokens(iter(lines).__next__):
364 for token in generate_tokens(iter(lines).__next__):
364 tokens_by_line[-1].append(token)
365 tokens_by_line[-1].append(token)
365 if token.type == tokenize2.NEWLINE:
366 if token.type == tokenize2.NEWLINE:
366 tokens_by_line.append([])
367 tokens_by_line.append([])
367
368
368 return tokens_by_line
369 return tokens_by_line
369
370
370 def show_linewise_tokens(s: str):
371 def show_linewise_tokens(s: str):
371 """For investigation"""
372 """For investigation"""
372 if not s.endswith('\n'):
373 if not s.endswith('\n'):
373 s += '\n'
374 s += '\n'
374 lines = s.splitlines(keepends=True)
375 lines = s.splitlines(keepends=True)
375 for line in make_tokens_by_line(lines):
376 for line in make_tokens_by_line(lines):
376 print("Line -------")
377 print("Line -------")
377 for tokinfo in line:
378 for tokinfo in line:
378 print(" ", tokinfo)
379 print(" ", tokinfo)
379
380
380 class TokenTransformers:
381 class TokenTransformers:
381 def __init__(self):
382 def __init__(self):
382 self.transformers = [
383 self.transformers = [
383 MagicAssign,
384 MagicAssign,
384 SystemAssign,
385 SystemAssign,
385 EscapedCommand,
386 EscapedCommand,
386 HelpEnd,
387 HelpEnd,
387 ]
388 ]
388
389
389 def do_one_transform(self, lines):
390 def do_one_transform(self, lines):
390 """Find and run the transform earliest in the code.
391 """Find and run the transform earliest in the code.
391
392
392 Returns (changed, lines).
393 Returns (changed, lines).
393
394
394 This method is called repeatedly until changed is False, indicating
395 This method is called repeatedly until changed is False, indicating
395 that all available transformations are complete.
396 that all available transformations are complete.
396
397
397 The tokens following IPython special syntax might not be valid, so
398 The tokens following IPython special syntax might not be valid, so
398 the transformed code is retokenised every time to identify the next
399 the transformed code is retokenised every time to identify the next
399 piece of special syntax. Hopefully long code cells are mostly valid
400 piece of special syntax. Hopefully long code cells are mostly valid
400 Python, not using lots of IPython special syntax, so this shouldn't be
401 Python, not using lots of IPython special syntax, so this shouldn't be
401 a performance issue.
402 a performance issue.
402 """
403 """
403 tokens_by_line = make_tokens_by_line(lines)
404 tokens_by_line = make_tokens_by_line(lines)
404 candidates = []
405 candidates = []
405 for transformer_cls in self.transformers:
406 for transformer_cls in self.transformers:
406 transformer = transformer_cls.find(tokens_by_line)
407 transformer = transformer_cls.find(tokens_by_line)
407 if transformer:
408 if transformer:
408 candidates.append(transformer)
409 candidates.append(transformer)
409
410
410 if not candidates:
411 if not candidates:
411 # Nothing to transform
412 # Nothing to transform
412 return False, lines
413 return False, lines
413
414
414 transformer = min(candidates, key=TokenTransformBase.sortby)
415 transformer = min(candidates, key=TokenTransformBase.sortby)
415 return True, transformer.transform(lines)
416 return True, transformer.transform(lines)
416
417
417 def __call__(self, lines):
418 def __call__(self, lines):
418 while True:
419 while True:
419 changed, lines = self.do_one_transform(lines)
420 changed, lines = self.do_one_transform(lines)
420 if not changed:
421 if not changed:
421 return lines
422 return lines
422
423
423
424
424 def transform_cell(cell):
425 def transform_cell(cell):
425 if not cell.endswith('\n'):
426 if not cell.endswith('\n'):
426 cell += '\n' # Ensure every line has a newline
427 cell += '\n' # Ensure every line has a newline
427 lines = cell.splitlines(keepends=True)
428 lines = cell.splitlines(keepends=True)
428 for transform in line_transforms:
429 for transform in line_transforms:
429 #print(transform, lines)
430 #print(transform, lines)
430 lines = transform(lines)
431 lines = transform(lines)
431
432
432 lines = TokenTransformers()(lines)
433 lines = TokenTransformers()(lines)
433 for line in lines:
434 return ''.join(lines)
434 print('~~', line)
@@ -1,1073 +1,1073 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions.
2 """Tests for various magic functions.
3
3
4 Needs to be run by nose (to make ipython session available).
4 Needs to be run by nose (to make ipython session available).
5 """
5 """
6
6
7 import io
7 import io
8 import os
8 import os
9 import re
9 import re
10 import sys
10 import sys
11 import warnings
11 import warnings
12 from unittest import TestCase
12 from unittest import TestCase
13 from importlib import invalidate_caches
13 from importlib import invalidate_caches
14 from io import StringIO
14 from io import StringIO
15
15
16 import nose.tools as nt
16 import nose.tools as nt
17
17
18 import shlex
18 import shlex
19
19
20 from IPython import get_ipython
20 from IPython import get_ipython
21 from IPython.core import magic
21 from IPython.core import magic
22 from IPython.core.error import UsageError
22 from IPython.core.error import UsageError
23 from IPython.core.magic import (Magics, magics_class, line_magic,
23 from IPython.core.magic import (Magics, magics_class, line_magic,
24 cell_magic,
24 cell_magic,
25 register_line_magic, register_cell_magic)
25 register_line_magic, register_cell_magic)
26 from IPython.core.magics import execution, script, code, logging
26 from IPython.core.magics import execution, script, code, logging
27 from IPython.testing import decorators as dec
27 from IPython.testing import decorators as dec
28 from IPython.testing import tools as tt
28 from IPython.testing import tools as tt
29 from IPython.utils import py3compat
29 from IPython.utils import py3compat
30 from IPython.utils.io import capture_output
30 from IPython.utils.io import capture_output
31 from IPython.utils.tempdir import TemporaryDirectory
31 from IPython.utils.tempdir import TemporaryDirectory
32 from IPython.utils.process import find_cmd
32 from IPython.utils.process import find_cmd
33
33
34
34
35
35
36 _ip = get_ipython()
36 _ip = get_ipython()
37
37
38 @magic.magics_class
38 @magic.magics_class
39 class DummyMagics(magic.Magics): pass
39 class DummyMagics(magic.Magics): pass
40
40
41 def test_extract_code_ranges():
41 def test_extract_code_ranges():
42 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
42 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
43 expected = [(0, 1),
43 expected = [(0, 1),
44 (2, 3),
44 (2, 3),
45 (4, 6),
45 (4, 6),
46 (6, 9),
46 (6, 9),
47 (9, 14),
47 (9, 14),
48 (16, None),
48 (16, None),
49 (None, 9),
49 (None, 9),
50 (9, None),
50 (9, None),
51 (None, 13),
51 (None, 13),
52 (None, None)]
52 (None, None)]
53 actual = list(code.extract_code_ranges(instr))
53 actual = list(code.extract_code_ranges(instr))
54 nt.assert_equal(actual, expected)
54 nt.assert_equal(actual, expected)
55
55
56 def test_extract_symbols():
56 def test_extract_symbols():
57 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
57 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
58 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
58 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
59 expected = [([], ['a']),
59 expected = [([], ['a']),
60 (["def b():\n return 42\n"], []),
60 (["def b():\n return 42\n"], []),
61 (["class A: pass\n"], []),
61 (["class A: pass\n"], []),
62 (["class A: pass\n", "def b():\n return 42\n"], []),
62 (["class A: pass\n", "def b():\n return 42\n"], []),
63 (["class A: pass\n"], ['a']),
63 (["class A: pass\n"], ['a']),
64 ([], ['z'])]
64 ([], ['z'])]
65 for symbols, exp in zip(symbols_args, expected):
65 for symbols, exp in zip(symbols_args, expected):
66 nt.assert_equal(code.extract_symbols(source, symbols), exp)
66 nt.assert_equal(code.extract_symbols(source, symbols), exp)
67
67
68
68
69 def test_extract_symbols_raises_exception_with_non_python_code():
69 def test_extract_symbols_raises_exception_with_non_python_code():
70 source = ("=begin A Ruby program :)=end\n"
70 source = ("=begin A Ruby program :)=end\n"
71 "def hello\n"
71 "def hello\n"
72 "puts 'Hello world'\n"
72 "puts 'Hello world'\n"
73 "end")
73 "end")
74 with nt.assert_raises(SyntaxError):
74 with nt.assert_raises(SyntaxError):
75 code.extract_symbols(source, "hello")
75 code.extract_symbols(source, "hello")
76
76
77
77
78 def test_magic_not_found():
78 def test_magic_not_found():
79 # magic not found raises UsageError
79 # magic not found raises UsageError
80 with nt.assert_raises(UsageError):
80 with nt.assert_raises(UsageError):
81 _ip.magic('doesntexist')
81 _ip.magic('doesntexist')
82
82
83 # ensure result isn't success when a magic isn't found
83 # ensure result isn't success when a magic isn't found
84 result = _ip.run_cell('%doesntexist')
84 result = _ip.run_cell('%doesntexist')
85 assert isinstance(result.error_in_exec, UsageError)
85 assert isinstance(result.error_in_exec, UsageError)
86
86
87
87
88 def test_cell_magic_not_found():
88 def test_cell_magic_not_found():
89 # magic not found raises UsageError
89 # magic not found raises UsageError
90 with nt.assert_raises(UsageError):
90 with nt.assert_raises(UsageError):
91 _ip.run_cell_magic('doesntexist', 'line', 'cell')
91 _ip.run_cell_magic('doesntexist', 'line', 'cell')
92
92
93 # ensure result isn't success when a magic isn't found
93 # ensure result isn't success when a magic isn't found
94 result = _ip.run_cell('%%doesntexist')
94 result = _ip.run_cell('%%doesntexist')
95 assert isinstance(result.error_in_exec, UsageError)
95 assert isinstance(result.error_in_exec, UsageError)
96
96
97
97
98 def test_magic_error_status():
98 def test_magic_error_status():
99 def fail(shell):
99 def fail(shell):
100 1/0
100 1/0
101 _ip.register_magic_function(fail)
101 _ip.register_magic_function(fail)
102 result = _ip.run_cell('%fail')
102 result = _ip.run_cell('%fail')
103 assert isinstance(result.error_in_exec, ZeroDivisionError)
103 assert isinstance(result.error_in_exec, ZeroDivisionError)
104
104
105
105
106 def test_config():
106 def test_config():
107 """ test that config magic does not raise
107 """ test that config magic does not raise
108 can happen if Configurable init is moved too early into
108 can happen if Configurable init is moved too early into
109 Magics.__init__ as then a Config object will be registered as a
109 Magics.__init__ as then a Config object will be registered as a
110 magic.
110 magic.
111 """
111 """
112 ## should not raise.
112 ## should not raise.
113 _ip.magic('config')
113 _ip.magic('config')
114
114
115 def test_config_available_configs():
115 def test_config_available_configs():
116 """ test that config magic prints available configs in unique and
116 """ test that config magic prints available configs in unique and
117 sorted order. """
117 sorted order. """
118 with capture_output() as captured:
118 with capture_output() as captured:
119 _ip.magic('config')
119 _ip.magic('config')
120
120
121 stdout = captured.stdout
121 stdout = captured.stdout
122 config_classes = stdout.strip().split('\n')[1:]
122 config_classes = stdout.strip().split('\n')[1:]
123 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
123 nt.assert_list_equal(config_classes, sorted(set(config_classes)))
124
124
125 def test_config_print_class():
125 def test_config_print_class():
126 """ test that config with a classname prints the class's options. """
126 """ test that config with a classname prints the class's options. """
127 with capture_output() as captured:
127 with capture_output() as captured:
128 _ip.magic('config TerminalInteractiveShell')
128 _ip.magic('config TerminalInteractiveShell')
129
129
130 stdout = captured.stdout
130 stdout = captured.stdout
131 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
131 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
132 print(stdout)
132 print(stdout)
133 raise AssertionError("1st line of stdout not like "
133 raise AssertionError("1st line of stdout not like "
134 "'TerminalInteractiveShell.* options'")
134 "'TerminalInteractiveShell.* options'")
135
135
136 def test_rehashx():
136 def test_rehashx():
137 # clear up everything
137 # clear up everything
138 _ip.alias_manager.clear_aliases()
138 _ip.alias_manager.clear_aliases()
139 del _ip.db['syscmdlist']
139 del _ip.db['syscmdlist']
140
140
141 _ip.magic('rehashx')
141 _ip.magic('rehashx')
142 # Practically ALL ipython development systems will have more than 10 aliases
142 # Practically ALL ipython development systems will have more than 10 aliases
143
143
144 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
144 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
145 for name, cmd in _ip.alias_manager.aliases:
145 for name, cmd in _ip.alias_manager.aliases:
146 # we must strip dots from alias names
146 # we must strip dots from alias names
147 nt.assert_not_in('.', name)
147 nt.assert_not_in('.', name)
148
148
149 # rehashx must fill up syscmdlist
149 # rehashx must fill up syscmdlist
150 scoms = _ip.db['syscmdlist']
150 scoms = _ip.db['syscmdlist']
151 nt.assert_true(len(scoms) > 10)
151 nt.assert_true(len(scoms) > 10)
152
152
153
153
154 def test_magic_parse_options():
154 def test_magic_parse_options():
155 """Test that we don't mangle paths when parsing magic options."""
155 """Test that we don't mangle paths when parsing magic options."""
156 ip = get_ipython()
156 ip = get_ipython()
157 path = 'c:\\x'
157 path = 'c:\\x'
158 m = DummyMagics(ip)
158 m = DummyMagics(ip)
159 opts = m.parse_options('-f %s' % path,'f:')[0]
159 opts = m.parse_options('-f %s' % path,'f:')[0]
160 # argv splitting is os-dependent
160 # argv splitting is os-dependent
161 if os.name == 'posix':
161 if os.name == 'posix':
162 expected = 'c:x'
162 expected = 'c:x'
163 else:
163 else:
164 expected = path
164 expected = path
165 nt.assert_equal(opts['f'], expected)
165 nt.assert_equal(opts['f'], expected)
166
166
167 def test_magic_parse_long_options():
167 def test_magic_parse_long_options():
168 """Magic.parse_options can handle --foo=bar long options"""
168 """Magic.parse_options can handle --foo=bar long options"""
169 ip = get_ipython()
169 ip = get_ipython()
170 m = DummyMagics(ip)
170 m = DummyMagics(ip)
171 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
171 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
172 nt.assert_in('foo', opts)
172 nt.assert_in('foo', opts)
173 nt.assert_in('bar', opts)
173 nt.assert_in('bar', opts)
174 nt.assert_equal(opts['bar'], "bubble")
174 nt.assert_equal(opts['bar'], "bubble")
175
175
176
176
177 @dec.skip_without('sqlite3')
177 @dec.skip_without('sqlite3')
178 def doctest_hist_f():
178 def doctest_hist_f():
179 """Test %hist -f with temporary filename.
179 """Test %hist -f with temporary filename.
180
180
181 In [9]: import tempfile
181 In [9]: import tempfile
182
182
183 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
183 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
184
184
185 In [11]: %hist -nl -f $tfile 3
185 In [11]: %hist -nl -f $tfile 3
186
186
187 In [13]: import os; os.unlink(tfile)
187 In [13]: import os; os.unlink(tfile)
188 """
188 """
189
189
190
190
191 @dec.skip_without('sqlite3')
191 @dec.skip_without('sqlite3')
192 def doctest_hist_r():
192 def doctest_hist_r():
193 """Test %hist -r
193 """Test %hist -r
194
194
195 XXX - This test is not recording the output correctly. For some reason, in
195 XXX - This test is not recording the output correctly. For some reason, in
196 testing mode the raw history isn't getting populated. No idea why.
196 testing mode the raw history isn't getting populated. No idea why.
197 Disabling the output checking for now, though at least we do run it.
197 Disabling the output checking for now, though at least we do run it.
198
198
199 In [1]: 'hist' in _ip.lsmagic()
199 In [1]: 'hist' in _ip.lsmagic()
200 Out[1]: True
200 Out[1]: True
201
201
202 In [2]: x=1
202 In [2]: x=1
203
203
204 In [3]: %hist -rl 2
204 In [3]: %hist -rl 2
205 x=1 # random
205 x=1 # random
206 %hist -r 2
206 %hist -r 2
207 """
207 """
208
208
209
209
210 @dec.skip_without('sqlite3')
210 @dec.skip_without('sqlite3')
211 def doctest_hist_op():
211 def doctest_hist_op():
212 """Test %hist -op
212 """Test %hist -op
213
213
214 In [1]: class b(float):
214 In [1]: class b(float):
215 ...: pass
215 ...: pass
216 ...:
216 ...:
217
217
218 In [2]: class s(object):
218 In [2]: class s(object):
219 ...: def __str__(self):
219 ...: def __str__(self):
220 ...: return 's'
220 ...: return 's'
221 ...:
221 ...:
222
222
223 In [3]:
223 In [3]:
224
224
225 In [4]: class r(b):
225 In [4]: class r(b):
226 ...: def __repr__(self):
226 ...: def __repr__(self):
227 ...: return 'r'
227 ...: return 'r'
228 ...:
228 ...:
229
229
230 In [5]: class sr(s,r): pass
230 In [5]: class sr(s,r): pass
231 ...:
231 ...:
232
232
233 In [6]:
233 In [6]:
234
234
235 In [7]: bb=b()
235 In [7]: bb=b()
236
236
237 In [8]: ss=s()
237 In [8]: ss=s()
238
238
239 In [9]: rr=r()
239 In [9]: rr=r()
240
240
241 In [10]: ssrr=sr()
241 In [10]: ssrr=sr()
242
242
243 In [11]: 4.5
243 In [11]: 4.5
244 Out[11]: 4.5
244 Out[11]: 4.5
245
245
246 In [12]: str(ss)
246 In [12]: str(ss)
247 Out[12]: 's'
247 Out[12]: 's'
248
248
249 In [13]:
249 In [13]:
250
250
251 In [14]: %hist -op
251 In [14]: %hist -op
252 >>> class b:
252 >>> class b:
253 ... pass
253 ... pass
254 ...
254 ...
255 >>> class s(b):
255 >>> class s(b):
256 ... def __str__(self):
256 ... def __str__(self):
257 ... return 's'
257 ... return 's'
258 ...
258 ...
259 >>>
259 >>>
260 >>> class r(b):
260 >>> class r(b):
261 ... def __repr__(self):
261 ... def __repr__(self):
262 ... return 'r'
262 ... return 'r'
263 ...
263 ...
264 >>> class sr(s,r): pass
264 >>> class sr(s,r): pass
265 >>>
265 >>>
266 >>> bb=b()
266 >>> bb=b()
267 >>> ss=s()
267 >>> ss=s()
268 >>> rr=r()
268 >>> rr=r()
269 >>> ssrr=sr()
269 >>> ssrr=sr()
270 >>> 4.5
270 >>> 4.5
271 4.5
271 4.5
272 >>> str(ss)
272 >>> str(ss)
273 's'
273 's'
274 >>>
274 >>>
275 """
275 """
276
276
277 def test_hist_pof():
277 def test_hist_pof():
278 ip = get_ipython()
278 ip = get_ipython()
279 ip.run_cell(u"1+2", store_history=True)
279 ip.run_cell(u"1+2", store_history=True)
280 #raise Exception(ip.history_manager.session_number)
280 #raise Exception(ip.history_manager.session_number)
281 #raise Exception(list(ip.history_manager._get_range_session()))
281 #raise Exception(list(ip.history_manager._get_range_session()))
282 with TemporaryDirectory() as td:
282 with TemporaryDirectory() as td:
283 tf = os.path.join(td, 'hist.py')
283 tf = os.path.join(td, 'hist.py')
284 ip.run_line_magic('history', '-pof %s' % tf)
284 ip.run_line_magic('history', '-pof %s' % tf)
285 assert os.path.isfile(tf)
285 assert os.path.isfile(tf)
286
286
287
287
288 @dec.skip_without('sqlite3')
288 @dec.skip_without('sqlite3')
289 def test_macro():
289 def test_macro():
290 ip = get_ipython()
290 ip = get_ipython()
291 ip.history_manager.reset() # Clear any existing history.
291 ip.history_manager.reset() # Clear any existing history.
292 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
292 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
293 for i, cmd in enumerate(cmds, start=1):
293 for i, cmd in enumerate(cmds, start=1):
294 ip.history_manager.store_inputs(i, cmd)
294 ip.history_manager.store_inputs(i, cmd)
295 ip.magic("macro test 1-3")
295 ip.magic("macro test 1-3")
296 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
296 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
297
297
298 # List macros
298 # List macros
299 nt.assert_in("test", ip.magic("macro"))
299 nt.assert_in("test", ip.magic("macro"))
300
300
301
301
302 @dec.skip_without('sqlite3')
302 @dec.skip_without('sqlite3')
303 def test_macro_run():
303 def test_macro_run():
304 """Test that we can run a multi-line macro successfully."""
304 """Test that we can run a multi-line macro successfully."""
305 ip = get_ipython()
305 ip = get_ipython()
306 ip.history_manager.reset()
306 ip.history_manager.reset()
307 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
307 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
308 "%macro test 2-3"]
308 "%macro test 2-3"]
309 for cmd in cmds:
309 for cmd in cmds:
310 ip.run_cell(cmd, store_history=True)
310 ip.run_cell(cmd, store_history=True)
311 nt.assert_equal(ip.user_ns["test"].value,
311 nt.assert_equal(ip.user_ns["test"].value,
312 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
312 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
313 with tt.AssertPrints("12"):
313 with tt.AssertPrints("12"):
314 ip.run_cell("test")
314 ip.run_cell("test")
315 with tt.AssertPrints("13"):
315 with tt.AssertPrints("13"):
316 ip.run_cell("test")
316 ip.run_cell("test")
317
317
318
318
319 def test_magic_magic():
319 def test_magic_magic():
320 """Test %magic"""
320 """Test %magic"""
321 ip = get_ipython()
321 ip = get_ipython()
322 with capture_output() as captured:
322 with capture_output() as captured:
323 ip.magic("magic")
323 ip.magic("magic")
324
324
325 stdout = captured.stdout
325 stdout = captured.stdout
326 nt.assert_in('%magic', stdout)
326 nt.assert_in('%magic', stdout)
327 nt.assert_in('IPython', stdout)
327 nt.assert_in('IPython', stdout)
328 nt.assert_in('Available', stdout)
328 nt.assert_in('Available', stdout)
329
329
330
330
331 @dec.skipif_not_numpy
331 @dec.skipif_not_numpy
332 def test_numpy_reset_array_undec():
332 def test_numpy_reset_array_undec():
333 "Test '%reset array' functionality"
333 "Test '%reset array' functionality"
334 _ip.ex('import numpy as np')
334 _ip.ex('import numpy as np')
335 _ip.ex('a = np.empty(2)')
335 _ip.ex('a = np.empty(2)')
336 nt.assert_in('a', _ip.user_ns)
336 nt.assert_in('a', _ip.user_ns)
337 _ip.magic('reset -f array')
337 _ip.magic('reset -f array')
338 nt.assert_not_in('a', _ip.user_ns)
338 nt.assert_not_in('a', _ip.user_ns)
339
339
340 def test_reset_out():
340 def test_reset_out():
341 "Test '%reset out' magic"
341 "Test '%reset out' magic"
342 _ip.run_cell("parrot = 'dead'", store_history=True)
342 _ip.run_cell("parrot = 'dead'", store_history=True)
343 # test '%reset -f out', make an Out prompt
343 # test '%reset -f out', make an Out prompt
344 _ip.run_cell("parrot", store_history=True)
344 _ip.run_cell("parrot", store_history=True)
345 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
345 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
346 _ip.magic('reset -f out')
346 _ip.magic('reset -f out')
347 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
347 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
348 nt.assert_equal(len(_ip.user_ns['Out']), 0)
348 nt.assert_equal(len(_ip.user_ns['Out']), 0)
349
349
350 def test_reset_in():
350 def test_reset_in():
351 "Test '%reset in' magic"
351 "Test '%reset in' magic"
352 # test '%reset -f in'
352 # test '%reset -f in'
353 _ip.run_cell("parrot", store_history=True)
353 _ip.run_cell("parrot", store_history=True)
354 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
354 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
355 _ip.magic('%reset -f in')
355 _ip.magic('%reset -f in')
356 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
356 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
357 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
357 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
358
358
359 def test_reset_dhist():
359 def test_reset_dhist():
360 "Test '%reset dhist' magic"
360 "Test '%reset dhist' magic"
361 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
361 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
362 _ip.magic('cd ' + os.path.dirname(nt.__file__))
362 _ip.magic('cd ' + os.path.dirname(nt.__file__))
363 _ip.magic('cd -')
363 _ip.magic('cd -')
364 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
364 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
365 _ip.magic('reset -f dhist')
365 _ip.magic('reset -f dhist')
366 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
366 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
367 _ip.run_cell("_dh = [d for d in tmp]") #restore
367 _ip.run_cell("_dh = [d for d in tmp]") #restore
368
368
369 def test_reset_in_length():
369 def test_reset_in_length():
370 "Test that '%reset in' preserves In[] length"
370 "Test that '%reset in' preserves In[] length"
371 _ip.run_cell("print 'foo'")
371 _ip.run_cell("print 'foo'")
372 _ip.run_cell("reset -f in")
372 _ip.run_cell("reset -f in")
373 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
373 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
374
374
375 def test_tb_syntaxerror():
375 def test_tb_syntaxerror():
376 """test %tb after a SyntaxError"""
376 """test %tb after a SyntaxError"""
377 ip = get_ipython()
377 ip = get_ipython()
378 ip.run_cell("for")
378 ip.run_cell("for")
379
379
380 # trap and validate stdout
380 # trap and validate stdout
381 save_stdout = sys.stdout
381 save_stdout = sys.stdout
382 try:
382 try:
383 sys.stdout = StringIO()
383 sys.stdout = StringIO()
384 ip.run_cell("%tb")
384 ip.run_cell("%tb")
385 out = sys.stdout.getvalue()
385 out = sys.stdout.getvalue()
386 finally:
386 finally:
387 sys.stdout = save_stdout
387 sys.stdout = save_stdout
388 # trim output, and only check the last line
388 # trim output, and only check the last line
389 last_line = out.rstrip().splitlines()[-1].strip()
389 last_line = out.rstrip().splitlines()[-1].strip()
390 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
390 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
391
391
392
392
393 def test_time():
393 def test_time():
394 ip = get_ipython()
394 ip = get_ipython()
395
395
396 with tt.AssertPrints("Wall time: "):
396 with tt.AssertPrints("Wall time: "):
397 ip.run_cell("%time None")
397 ip.run_cell("%time None")
398
398
399 ip.run_cell("def f(kmjy):\n"
399 ip.run_cell("def f(kmjy):\n"
400 " %time print (2*kmjy)")
400 " %time print (2*kmjy)")
401
401
402 with tt.AssertPrints("Wall time: "):
402 with tt.AssertPrints("Wall time: "):
403 with tt.AssertPrints("hihi", suppress=False):
403 with tt.AssertPrints("hihi", suppress=False):
404 ip.run_cell("f('hi')")
404 ip.run_cell("f('hi')")
405
405
406
406
407 @dec.skip_win32
407 @dec.skip_win32
408 def test_time2():
408 def test_time2():
409 ip = get_ipython()
409 ip = get_ipython()
410
410
411 with tt.AssertPrints("CPU times: user "):
411 with tt.AssertPrints("CPU times: user "):
412 ip.run_cell("%time None")
412 ip.run_cell("%time None")
413
413
414 def test_time3():
414 def test_time3():
415 """Erroneous magic function calls, issue gh-3334"""
415 """Erroneous magic function calls, issue gh-3334"""
416 ip = get_ipython()
416 ip = get_ipython()
417 ip.user_ns.pop('run', None)
417 ip.user_ns.pop('run', None)
418
418
419 with tt.AssertNotPrints("not found", channel='stderr'):
419 with tt.AssertNotPrints("not found", channel='stderr'):
420 ip.run_cell("%%time\n"
420 ip.run_cell("%%time\n"
421 "run = 0\n"
421 "run = 0\n"
422 "run += 1")
422 "run += 1")
423
423
424 def test_doctest_mode():
424 def test_doctest_mode():
425 "Toggle doctest_mode twice, it should be a no-op and run without error"
425 "Toggle doctest_mode twice, it should be a no-op and run without error"
426 _ip.magic('doctest_mode')
426 _ip.magic('doctest_mode')
427 _ip.magic('doctest_mode')
427 _ip.magic('doctest_mode')
428
428
429
429
430 def test_parse_options():
430 def test_parse_options():
431 """Tests for basic options parsing in magics."""
431 """Tests for basic options parsing in magics."""
432 # These are only the most minimal of tests, more should be added later. At
432 # These are only the most minimal of tests, more should be added later. At
433 # the very least we check that basic text/unicode calls work OK.
433 # the very least we check that basic text/unicode calls work OK.
434 m = DummyMagics(_ip)
434 m = DummyMagics(_ip)
435 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
435 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
436 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
436 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
437
437
438
438
439 def test_dirops():
439 def test_dirops():
440 """Test various directory handling operations."""
440 """Test various directory handling operations."""
441 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
441 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
442 curpath = os.getcwd
442 curpath = os.getcwd
443 startdir = os.getcwd()
443 startdir = os.getcwd()
444 ipdir = os.path.realpath(_ip.ipython_dir)
444 ipdir = os.path.realpath(_ip.ipython_dir)
445 try:
445 try:
446 _ip.magic('cd "%s"' % ipdir)
446 _ip.magic('cd "%s"' % ipdir)
447 nt.assert_equal(curpath(), ipdir)
447 nt.assert_equal(curpath(), ipdir)
448 _ip.magic('cd -')
448 _ip.magic('cd -')
449 nt.assert_equal(curpath(), startdir)
449 nt.assert_equal(curpath(), startdir)
450 _ip.magic('pushd "%s"' % ipdir)
450 _ip.magic('pushd "%s"' % ipdir)
451 nt.assert_equal(curpath(), ipdir)
451 nt.assert_equal(curpath(), ipdir)
452 _ip.magic('popd')
452 _ip.magic('popd')
453 nt.assert_equal(curpath(), startdir)
453 nt.assert_equal(curpath(), startdir)
454 finally:
454 finally:
455 os.chdir(startdir)
455 os.chdir(startdir)
456
456
457
457
458 def test_xmode():
458 def test_xmode():
459 # Calling xmode three times should be a no-op
459 # Calling xmode three times should be a no-op
460 xmode = _ip.InteractiveTB.mode
460 xmode = _ip.InteractiveTB.mode
461 for i in range(3):
461 for i in range(3):
462 _ip.magic("xmode")
462 _ip.magic("xmode")
463 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
463 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
464
464
465 def test_reset_hard():
465 def test_reset_hard():
466 monitor = []
466 monitor = []
467 class A(object):
467 class A(object):
468 def __del__(self):
468 def __del__(self):
469 monitor.append(1)
469 monitor.append(1)
470 def __repr__(self):
470 def __repr__(self):
471 return "<A instance>"
471 return "<A instance>"
472
472
473 _ip.user_ns["a"] = A()
473 _ip.user_ns["a"] = A()
474 _ip.run_cell("a")
474 _ip.run_cell("a")
475
475
476 nt.assert_equal(monitor, [])
476 nt.assert_equal(monitor, [])
477 _ip.magic("reset -f")
477 _ip.magic("reset -f")
478 nt.assert_equal(monitor, [1])
478 nt.assert_equal(monitor, [1])
479
479
480 class TestXdel(tt.TempFileMixin):
480 class TestXdel(tt.TempFileMixin):
481 def test_xdel(self):
481 def test_xdel(self):
482 """Test that references from %run are cleared by xdel."""
482 """Test that references from %run are cleared by xdel."""
483 src = ("class A(object):\n"
483 src = ("class A(object):\n"
484 " monitor = []\n"
484 " monitor = []\n"
485 " def __del__(self):\n"
485 " def __del__(self):\n"
486 " self.monitor.append(1)\n"
486 " self.monitor.append(1)\n"
487 "a = A()\n")
487 "a = A()\n")
488 self.mktmp(src)
488 self.mktmp(src)
489 # %run creates some hidden references...
489 # %run creates some hidden references...
490 _ip.magic("run %s" % self.fname)
490 _ip.magic("run %s" % self.fname)
491 # ... as does the displayhook.
491 # ... as does the displayhook.
492 _ip.run_cell("a")
492 _ip.run_cell("a")
493
493
494 monitor = _ip.user_ns["A"].monitor
494 monitor = _ip.user_ns["A"].monitor
495 nt.assert_equal(monitor, [])
495 nt.assert_equal(monitor, [])
496
496
497 _ip.magic("xdel a")
497 _ip.magic("xdel a")
498
498
499 # Check that a's __del__ method has been called.
499 # Check that a's __del__ method has been called.
500 nt.assert_equal(monitor, [1])
500 nt.assert_equal(monitor, [1])
501
501
502 def doctest_who():
502 def doctest_who():
503 """doctest for %who
503 """doctest for %who
504
504
505 In [1]: %reset -f
505 In [1]: %reset -f
506
506
507 In [2]: alpha = 123
507 In [2]: alpha = 123
508
508
509 In [3]: beta = 'beta'
509 In [3]: beta = 'beta'
510
510
511 In [4]: %who int
511 In [4]: %who int
512 alpha
512 alpha
513
513
514 In [5]: %who str
514 In [5]: %who str
515 beta
515 beta
516
516
517 In [6]: %whos
517 In [6]: %whos
518 Variable Type Data/Info
518 Variable Type Data/Info
519 ----------------------------
519 ----------------------------
520 alpha int 123
520 alpha int 123
521 beta str beta
521 beta str beta
522
522
523 In [7]: %who_ls
523 In [7]: %who_ls
524 Out[7]: ['alpha', 'beta']
524 Out[7]: ['alpha', 'beta']
525 """
525 """
526
526
527 def test_whos():
527 def test_whos():
528 """Check that whos is protected against objects where repr() fails."""
528 """Check that whos is protected against objects where repr() fails."""
529 class A(object):
529 class A(object):
530 def __repr__(self):
530 def __repr__(self):
531 raise Exception()
531 raise Exception()
532 _ip.user_ns['a'] = A()
532 _ip.user_ns['a'] = A()
533 _ip.magic("whos")
533 _ip.magic("whos")
534
534
535 @py3compat.u_format
535 @py3compat.u_format
536 def doctest_precision():
536 def doctest_precision():
537 """doctest for %precision
537 """doctest for %precision
538
538
539 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
539 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
540
540
541 In [2]: %precision 5
541 In [2]: %precision 5
542 Out[2]: '%.5f'
542 Out[2]: '%.5f'
543
543
544 In [3]: f.float_format
544 In [3]: f.float_format
545 Out[3]: '%.5f'
545 Out[3]: '%.5f'
546
546
547 In [4]: %precision %e
547 In [4]: %precision %e
548 Out[4]: '%e'
548 Out[4]: '%e'
549
549
550 In [5]: f(3.1415927)
550 In [5]: f(3.1415927)
551 Out[5]: '3.141593e+00'
551 Out[5]: '3.141593e+00'
552 """
552 """
553
553
554 def test_psearch():
554 def test_psearch():
555 with tt.AssertPrints("dict.fromkeys"):
555 with tt.AssertPrints("dict.fromkeys"):
556 _ip.run_cell("dict.fr*?")
556 _ip.run_cell("dict.fr*?")
557
557
558 def test_timeit_shlex():
558 def test_timeit_shlex():
559 """test shlex issues with timeit (#1109)"""
559 """test shlex issues with timeit (#1109)"""
560 _ip.ex("def f(*a,**kw): pass")
560 _ip.ex("def f(*a,**kw): pass")
561 _ip.magic('timeit -n1 "this is a bug".count(" ")')
561 _ip.magic('timeit -n1 "this is a bug".count(" ")')
562 _ip.magic('timeit -r1 -n1 f(" ", 1)')
562 _ip.magic('timeit -r1 -n1 f(" ", 1)')
563 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
563 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
564 _ip.magic('timeit -r1 -n1 ("a " + "b")')
564 _ip.magic('timeit -r1 -n1 ("a " + "b")')
565 _ip.magic('timeit -r1 -n1 f("a " + "b")')
565 _ip.magic('timeit -r1 -n1 f("a " + "b")')
566 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
566 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
567
567
568
568
569 def test_timeit_arguments():
569 def test_timeit_arguments():
570 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
570 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
571 _ip.magic("timeit ('#')")
571 _ip.magic("timeit ('#')")
572
572
573
573
574 def test_timeit_special_syntax():
574 def test_timeit_special_syntax():
575 "Test %%timeit with IPython special syntax"
575 "Test %%timeit with IPython special syntax"
576 @register_line_magic
576 @register_line_magic
577 def lmagic(line):
577 def lmagic(line):
578 ip = get_ipython()
578 ip = get_ipython()
579 ip.user_ns['lmagic_out'] = line
579 ip.user_ns['lmagic_out'] = line
580
580
581 # line mode test
581 # line mode test
582 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
582 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
583 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
583 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
584 # cell mode test
584 # cell mode test
585 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
585 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
586 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
586 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
587
587
588 def test_timeit_return():
588 def test_timeit_return():
589 """
589 """
590 test whether timeit -o return object
590 test whether timeit -o return object
591 """
591 """
592
592
593 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
593 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
594 assert(res is not None)
594 assert(res is not None)
595
595
596 def test_timeit_quiet():
596 def test_timeit_quiet():
597 """
597 """
598 test quiet option of timeit magic
598 test quiet option of timeit magic
599 """
599 """
600 with tt.AssertNotPrints("loops"):
600 with tt.AssertNotPrints("loops"):
601 _ip.run_cell("%timeit -n1 -r1 -q 1")
601 _ip.run_cell("%timeit -n1 -r1 -q 1")
602
602
603 def test_timeit_return_quiet():
603 def test_timeit_return_quiet():
604 with tt.AssertNotPrints("loops"):
604 with tt.AssertNotPrints("loops"):
605 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
605 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
606 assert (res is not None)
606 assert (res is not None)
607
607
608 def test_timeit_invalid_return():
608 def test_timeit_invalid_return():
609 with nt.assert_raises_regex(SyntaxError, "outside function"):
609 with nt.assert_raises_regex(SyntaxError, "outside function"):
610 _ip.run_line_magic('timeit', 'return')
610 _ip.run_line_magic('timeit', 'return')
611
611
612 @dec.skipif(execution.profile is None)
612 @dec.skipif(execution.profile is None)
613 def test_prun_special_syntax():
613 def test_prun_special_syntax():
614 "Test %%prun with IPython special syntax"
614 "Test %%prun with IPython special syntax"
615 @register_line_magic
615 @register_line_magic
616 def lmagic(line):
616 def lmagic(line):
617 ip = get_ipython()
617 ip = get_ipython()
618 ip.user_ns['lmagic_out'] = line
618 ip.user_ns['lmagic_out'] = line
619
619
620 # line mode test
620 # line mode test
621 _ip.run_line_magic('prun', '-q %lmagic my line')
621 _ip.run_line_magic('prun', '-q %lmagic my line')
622 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
622 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
623 # cell mode test
623 # cell mode test
624 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
624 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
625 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
625 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
626
626
627 @dec.skipif(execution.profile is None)
627 @dec.skipif(execution.profile is None)
628 def test_prun_quotes():
628 def test_prun_quotes():
629 "Test that prun does not clobber string escapes (GH #1302)"
629 "Test that prun does not clobber string escapes (GH #1302)"
630 _ip.magic(r"prun -q x = '\t'")
630 _ip.magic(r"prun -q x = '\t'")
631 nt.assert_equal(_ip.user_ns['x'], '\t')
631 nt.assert_equal(_ip.user_ns['x'], '\t')
632
632
633 def test_extension():
633 def test_extension():
634 # Debugging information for failures of this test
634 # Debugging information for failures of this test
635 print('sys.path:')
635 print('sys.path:')
636 for p in sys.path:
636 for p in sys.path:
637 print(' ', p)
637 print(' ', p)
638 print('CWD', os.getcwd())
638 print('CWD', os.getcwd())
639
639
640 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
640 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
641 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
641 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
642 sys.path.insert(0, daft_path)
642 sys.path.insert(0, daft_path)
643 try:
643 try:
644 _ip.user_ns.pop('arq', None)
644 _ip.user_ns.pop('arq', None)
645 invalidate_caches() # Clear import caches
645 invalidate_caches() # Clear import caches
646 _ip.magic("load_ext daft_extension")
646 _ip.magic("load_ext daft_extension")
647 nt.assert_equal(_ip.user_ns['arq'], 185)
647 nt.assert_equal(_ip.user_ns['arq'], 185)
648 _ip.magic("unload_ext daft_extension")
648 _ip.magic("unload_ext daft_extension")
649 assert 'arq' not in _ip.user_ns
649 assert 'arq' not in _ip.user_ns
650 finally:
650 finally:
651 sys.path.remove(daft_path)
651 sys.path.remove(daft_path)
652
652
653
653
654 def test_notebook_export_json():
654 def test_notebook_export_json():
655 _ip = get_ipython()
655 _ip = get_ipython()
656 _ip.history_manager.reset() # Clear any existing history.
656 _ip.history_manager.reset() # Clear any existing history.
657 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
657 cmds = [u"a=1", u"def b():\n return a**2", u"print('noΓ«l, Γ©tΓ©', b())"]
658 for i, cmd in enumerate(cmds, start=1):
658 for i, cmd in enumerate(cmds, start=1):
659 _ip.history_manager.store_inputs(i, cmd)
659 _ip.history_manager.store_inputs(i, cmd)
660 with TemporaryDirectory() as td:
660 with TemporaryDirectory() as td:
661 outfile = os.path.join(td, "nb.ipynb")
661 outfile = os.path.join(td, "nb.ipynb")
662 _ip.magic("notebook -e %s" % outfile)
662 _ip.magic("notebook -e %s" % outfile)
663
663
664
664
665 class TestEnv(TestCase):
665 class TestEnv(TestCase):
666
666
667 def test_env(self):
667 def test_env(self):
668 env = _ip.magic("env")
668 env = _ip.magic("env")
669 self.assertTrue(isinstance(env, dict))
669 self.assertTrue(isinstance(env, dict))
670
670
671 def test_env_get_set_simple(self):
671 def test_env_get_set_simple(self):
672 env = _ip.magic("env var val1")
672 env = _ip.magic("env var val1")
673 self.assertEqual(env, None)
673 self.assertEqual(env, None)
674 self.assertEqual(os.environ['var'], 'val1')
674 self.assertEqual(os.environ['var'], 'val1')
675 self.assertEqual(_ip.magic("env var"), 'val1')
675 self.assertEqual(_ip.magic("env var"), 'val1')
676 env = _ip.magic("env var=val2")
676 env = _ip.magic("env var=val2")
677 self.assertEqual(env, None)
677 self.assertEqual(env, None)
678 self.assertEqual(os.environ['var'], 'val2')
678 self.assertEqual(os.environ['var'], 'val2')
679
679
680 def test_env_get_set_complex(self):
680 def test_env_get_set_complex(self):
681 env = _ip.magic("env var 'val1 '' 'val2")
681 env = _ip.magic("env var 'val1 '' 'val2")
682 self.assertEqual(env, None)
682 self.assertEqual(env, None)
683 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
683 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
684 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
684 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
685 env = _ip.magic('env var=val2 val3="val4')
685 env = _ip.magic('env var=val2 val3="val4')
686 self.assertEqual(env, None)
686 self.assertEqual(env, None)
687 self.assertEqual(os.environ['var'], 'val2 val3="val4')
687 self.assertEqual(os.environ['var'], 'val2 val3="val4')
688
688
689 def test_env_set_bad_input(self):
689 def test_env_set_bad_input(self):
690 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
690 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
691
691
692 def test_env_set_whitespace(self):
692 def test_env_set_whitespace(self):
693 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
693 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
694
694
695
695
696 class CellMagicTestCase(TestCase):
696 class CellMagicTestCase(TestCase):
697
697
698 def check_ident(self, magic):
698 def check_ident(self, magic):
699 # Manually called, we get the result
699 # Manually called, we get the result
700 out = _ip.run_cell_magic(magic, 'a', 'b')
700 out = _ip.run_cell_magic(magic, 'a', 'b')
701 nt.assert_equal(out, ('a','b'))
701 nt.assert_equal(out, ('a','b'))
702 # Via run_cell, it goes into the user's namespace via displayhook
702 # Via run_cell, it goes into the user's namespace via displayhook
703 _ip.run_cell('%%' + magic +' c\nd')
703 _ip.run_cell('%%' + magic +' c\nd\n')
704 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
704 nt.assert_equal(_ip.user_ns['_'], ('c','d\n'))
705
705
706 def test_cell_magic_func_deco(self):
706 def test_cell_magic_func_deco(self):
707 "Cell magic using simple decorator"
707 "Cell magic using simple decorator"
708 @register_cell_magic
708 @register_cell_magic
709 def cellm(line, cell):
709 def cellm(line, cell):
710 return line, cell
710 return line, cell
711
711
712 self.check_ident('cellm')
712 self.check_ident('cellm')
713
713
714 def test_cell_magic_reg(self):
714 def test_cell_magic_reg(self):
715 "Cell magic manually registered"
715 "Cell magic manually registered"
716 def cellm(line, cell):
716 def cellm(line, cell):
717 return line, cell
717 return line, cell
718
718
719 _ip.register_magic_function(cellm, 'cell', 'cellm2')
719 _ip.register_magic_function(cellm, 'cell', 'cellm2')
720 self.check_ident('cellm2')
720 self.check_ident('cellm2')
721
721
722 def test_cell_magic_class(self):
722 def test_cell_magic_class(self):
723 "Cell magics declared via a class"
723 "Cell magics declared via a class"
724 @magics_class
724 @magics_class
725 class MyMagics(Magics):
725 class MyMagics(Magics):
726
726
727 @cell_magic
727 @cell_magic
728 def cellm3(self, line, cell):
728 def cellm3(self, line, cell):
729 return line, cell
729 return line, cell
730
730
731 _ip.register_magics(MyMagics)
731 _ip.register_magics(MyMagics)
732 self.check_ident('cellm3')
732 self.check_ident('cellm3')
733
733
734 def test_cell_magic_class2(self):
734 def test_cell_magic_class2(self):
735 "Cell magics declared via a class, #2"
735 "Cell magics declared via a class, #2"
736 @magics_class
736 @magics_class
737 class MyMagics2(Magics):
737 class MyMagics2(Magics):
738
738
739 @cell_magic('cellm4')
739 @cell_magic('cellm4')
740 def cellm33(self, line, cell):
740 def cellm33(self, line, cell):
741 return line, cell
741 return line, cell
742
742
743 _ip.register_magics(MyMagics2)
743 _ip.register_magics(MyMagics2)
744 self.check_ident('cellm4')
744 self.check_ident('cellm4')
745 # Check that nothing is registered as 'cellm33'
745 # Check that nothing is registered as 'cellm33'
746 c33 = _ip.find_cell_magic('cellm33')
746 c33 = _ip.find_cell_magic('cellm33')
747 nt.assert_equal(c33, None)
747 nt.assert_equal(c33, None)
748
748
749 def test_file():
749 def test_file():
750 """Basic %%file"""
750 """Basic %%file"""
751 ip = get_ipython()
751 ip = get_ipython()
752 with TemporaryDirectory() as td:
752 with TemporaryDirectory() as td:
753 fname = os.path.join(td, 'file1')
753 fname = os.path.join(td, 'file1')
754 ip.run_cell_magic("file", fname, u'\n'.join([
754 ip.run_cell_magic("file", fname, u'\n'.join([
755 'line1',
755 'line1',
756 'line2',
756 'line2',
757 ]))
757 ]))
758 with open(fname) as f:
758 with open(fname) as f:
759 s = f.read()
759 s = f.read()
760 nt.assert_in('line1\n', s)
760 nt.assert_in('line1\n', s)
761 nt.assert_in('line2', s)
761 nt.assert_in('line2', s)
762
762
763 def test_file_var_expand():
763 def test_file_var_expand():
764 """%%file $filename"""
764 """%%file $filename"""
765 ip = get_ipython()
765 ip = get_ipython()
766 with TemporaryDirectory() as td:
766 with TemporaryDirectory() as td:
767 fname = os.path.join(td, 'file1')
767 fname = os.path.join(td, 'file1')
768 ip.user_ns['filename'] = fname
768 ip.user_ns['filename'] = fname
769 ip.run_cell_magic("file", '$filename', u'\n'.join([
769 ip.run_cell_magic("file", '$filename', u'\n'.join([
770 'line1',
770 'line1',
771 'line2',
771 'line2',
772 ]))
772 ]))
773 with open(fname) as f:
773 with open(fname) as f:
774 s = f.read()
774 s = f.read()
775 nt.assert_in('line1\n', s)
775 nt.assert_in('line1\n', s)
776 nt.assert_in('line2', s)
776 nt.assert_in('line2', s)
777
777
778 def test_file_unicode():
778 def test_file_unicode():
779 """%%file with unicode cell"""
779 """%%file with unicode cell"""
780 ip = get_ipython()
780 ip = get_ipython()
781 with TemporaryDirectory() as td:
781 with TemporaryDirectory() as td:
782 fname = os.path.join(td, 'file1')
782 fname = os.path.join(td, 'file1')
783 ip.run_cell_magic("file", fname, u'\n'.join([
783 ip.run_cell_magic("file", fname, u'\n'.join([
784 u'linΓ©1',
784 u'linΓ©1',
785 u'linΓ©2',
785 u'linΓ©2',
786 ]))
786 ]))
787 with io.open(fname, encoding='utf-8') as f:
787 with io.open(fname, encoding='utf-8') as f:
788 s = f.read()
788 s = f.read()
789 nt.assert_in(u'linΓ©1\n', s)
789 nt.assert_in(u'linΓ©1\n', s)
790 nt.assert_in(u'linΓ©2', s)
790 nt.assert_in(u'linΓ©2', s)
791
791
792 def test_file_amend():
792 def test_file_amend():
793 """%%file -a amends files"""
793 """%%file -a amends files"""
794 ip = get_ipython()
794 ip = get_ipython()
795 with TemporaryDirectory() as td:
795 with TemporaryDirectory() as td:
796 fname = os.path.join(td, 'file2')
796 fname = os.path.join(td, 'file2')
797 ip.run_cell_magic("file", fname, u'\n'.join([
797 ip.run_cell_magic("file", fname, u'\n'.join([
798 'line1',
798 'line1',
799 'line2',
799 'line2',
800 ]))
800 ]))
801 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
801 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
802 'line3',
802 'line3',
803 'line4',
803 'line4',
804 ]))
804 ]))
805 with open(fname) as f:
805 with open(fname) as f:
806 s = f.read()
806 s = f.read()
807 nt.assert_in('line1\n', s)
807 nt.assert_in('line1\n', s)
808 nt.assert_in('line3\n', s)
808 nt.assert_in('line3\n', s)
809
809
810
810
811 def test_script_config():
811 def test_script_config():
812 ip = get_ipython()
812 ip = get_ipython()
813 ip.config.ScriptMagics.script_magics = ['whoda']
813 ip.config.ScriptMagics.script_magics = ['whoda']
814 sm = script.ScriptMagics(shell=ip)
814 sm = script.ScriptMagics(shell=ip)
815 nt.assert_in('whoda', sm.magics['cell'])
815 nt.assert_in('whoda', sm.magics['cell'])
816
816
817 @dec.skip_win32
817 @dec.skip_win32
818 def test_script_out():
818 def test_script_out():
819 ip = get_ipython()
819 ip = get_ipython()
820 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
820 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
821 nt.assert_equal(ip.user_ns['output'], 'hi\n')
821 nt.assert_equal(ip.user_ns['output'], 'hi\n')
822
822
823 @dec.skip_win32
823 @dec.skip_win32
824 def test_script_err():
824 def test_script_err():
825 ip = get_ipython()
825 ip = get_ipython()
826 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
826 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
827 nt.assert_equal(ip.user_ns['error'], 'hello\n')
827 nt.assert_equal(ip.user_ns['error'], 'hello\n')
828
828
829 @dec.skip_win32
829 @dec.skip_win32
830 def test_script_out_err():
830 def test_script_out_err():
831 ip = get_ipython()
831 ip = get_ipython()
832 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
832 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
833 nt.assert_equal(ip.user_ns['output'], 'hi\n')
833 nt.assert_equal(ip.user_ns['output'], 'hi\n')
834 nt.assert_equal(ip.user_ns['error'], 'hello\n')
834 nt.assert_equal(ip.user_ns['error'], 'hello\n')
835
835
836 @dec.skip_win32
836 @dec.skip_win32
837 def test_script_bg_out():
837 def test_script_bg_out():
838 ip = get_ipython()
838 ip = get_ipython()
839 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
839 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
840 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
840 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
841
841
842 @dec.skip_win32
842 @dec.skip_win32
843 def test_script_bg_err():
843 def test_script_bg_err():
844 ip = get_ipython()
844 ip = get_ipython()
845 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
845 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
846 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
846 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
847
847
848 @dec.skip_win32
848 @dec.skip_win32
849 def test_script_bg_out_err():
849 def test_script_bg_out_err():
850 ip = get_ipython()
850 ip = get_ipython()
851 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
851 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
852 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
852 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
853 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
853 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
854
854
855 def test_script_defaults():
855 def test_script_defaults():
856 ip = get_ipython()
856 ip = get_ipython()
857 for cmd in ['sh', 'bash', 'perl', 'ruby']:
857 for cmd in ['sh', 'bash', 'perl', 'ruby']:
858 try:
858 try:
859 find_cmd(cmd)
859 find_cmd(cmd)
860 except Exception:
860 except Exception:
861 pass
861 pass
862 else:
862 else:
863 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
863 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
864
864
865
865
866 @magics_class
866 @magics_class
867 class FooFoo(Magics):
867 class FooFoo(Magics):
868 """class with both %foo and %%foo magics"""
868 """class with both %foo and %%foo magics"""
869 @line_magic('foo')
869 @line_magic('foo')
870 def line_foo(self, line):
870 def line_foo(self, line):
871 "I am line foo"
871 "I am line foo"
872 pass
872 pass
873
873
874 @cell_magic("foo")
874 @cell_magic("foo")
875 def cell_foo(self, line, cell):
875 def cell_foo(self, line, cell):
876 "I am cell foo, not line foo"
876 "I am cell foo, not line foo"
877 pass
877 pass
878
878
879 def test_line_cell_info():
879 def test_line_cell_info():
880 """%%foo and %foo magics are distinguishable to inspect"""
880 """%%foo and %foo magics are distinguishable to inspect"""
881 ip = get_ipython()
881 ip = get_ipython()
882 ip.magics_manager.register(FooFoo)
882 ip.magics_manager.register(FooFoo)
883 oinfo = ip.object_inspect('foo')
883 oinfo = ip.object_inspect('foo')
884 nt.assert_true(oinfo['found'])
884 nt.assert_true(oinfo['found'])
885 nt.assert_true(oinfo['ismagic'])
885 nt.assert_true(oinfo['ismagic'])
886
886
887 oinfo = ip.object_inspect('%%foo')
887 oinfo = ip.object_inspect('%%foo')
888 nt.assert_true(oinfo['found'])
888 nt.assert_true(oinfo['found'])
889 nt.assert_true(oinfo['ismagic'])
889 nt.assert_true(oinfo['ismagic'])
890 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
890 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
891
891
892 oinfo = ip.object_inspect('%foo')
892 oinfo = ip.object_inspect('%foo')
893 nt.assert_true(oinfo['found'])
893 nt.assert_true(oinfo['found'])
894 nt.assert_true(oinfo['ismagic'])
894 nt.assert_true(oinfo['ismagic'])
895 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
895 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
896
896
897 def test_multiple_magics():
897 def test_multiple_magics():
898 ip = get_ipython()
898 ip = get_ipython()
899 foo1 = FooFoo(ip)
899 foo1 = FooFoo(ip)
900 foo2 = FooFoo(ip)
900 foo2 = FooFoo(ip)
901 mm = ip.magics_manager
901 mm = ip.magics_manager
902 mm.register(foo1)
902 mm.register(foo1)
903 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
903 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
904 mm.register(foo2)
904 mm.register(foo2)
905 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
905 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
906
906
907 def test_alias_magic():
907 def test_alias_magic():
908 """Test %alias_magic."""
908 """Test %alias_magic."""
909 ip = get_ipython()
909 ip = get_ipython()
910 mm = ip.magics_manager
910 mm = ip.magics_manager
911
911
912 # Basic operation: both cell and line magics are created, if possible.
912 # Basic operation: both cell and line magics are created, if possible.
913 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
913 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
914 nt.assert_in('timeit_alias', mm.magics['line'])
914 nt.assert_in('timeit_alias', mm.magics['line'])
915 nt.assert_in('timeit_alias', mm.magics['cell'])
915 nt.assert_in('timeit_alias', mm.magics['cell'])
916
916
917 # --cell is specified, line magic not created.
917 # --cell is specified, line magic not created.
918 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
918 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
919 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
919 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
920 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
920 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
921
921
922 # Test that line alias is created successfully.
922 # Test that line alias is created successfully.
923 ip.run_line_magic('alias_magic', '--line env_alias env')
923 ip.run_line_magic('alias_magic', '--line env_alias env')
924 nt.assert_equal(ip.run_line_magic('env', ''),
924 nt.assert_equal(ip.run_line_magic('env', ''),
925 ip.run_line_magic('env_alias', ''))
925 ip.run_line_magic('env_alias', ''))
926
926
927 # Test that line alias with parameters passed in is created successfully.
927 # Test that line alias with parameters passed in is created successfully.
928 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
928 ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3'))
929 nt.assert_in('history_alias', mm.magics['line'])
929 nt.assert_in('history_alias', mm.magics['line'])
930
930
931
931
932 def test_save():
932 def test_save():
933 """Test %save."""
933 """Test %save."""
934 ip = get_ipython()
934 ip = get_ipython()
935 ip.history_manager.reset() # Clear any existing history.
935 ip.history_manager.reset() # Clear any existing history.
936 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
936 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
937 for i, cmd in enumerate(cmds, start=1):
937 for i, cmd in enumerate(cmds, start=1):
938 ip.history_manager.store_inputs(i, cmd)
938 ip.history_manager.store_inputs(i, cmd)
939 with TemporaryDirectory() as tmpdir:
939 with TemporaryDirectory() as tmpdir:
940 file = os.path.join(tmpdir, "testsave.py")
940 file = os.path.join(tmpdir, "testsave.py")
941 ip.run_line_magic("save", "%s 1-10" % file)
941 ip.run_line_magic("save", "%s 1-10" % file)
942 with open(file) as f:
942 with open(file) as f:
943 content = f.read()
943 content = f.read()
944 nt.assert_equal(content.count(cmds[0]), 1)
944 nt.assert_equal(content.count(cmds[0]), 1)
945 nt.assert_in('coding: utf-8', content)
945 nt.assert_in('coding: utf-8', content)
946 ip.run_line_magic("save", "-a %s 1-10" % file)
946 ip.run_line_magic("save", "-a %s 1-10" % file)
947 with open(file) as f:
947 with open(file) as f:
948 content = f.read()
948 content = f.read()
949 nt.assert_equal(content.count(cmds[0]), 2)
949 nt.assert_equal(content.count(cmds[0]), 2)
950 nt.assert_in('coding: utf-8', content)
950 nt.assert_in('coding: utf-8', content)
951
951
952
952
953 def test_store():
953 def test_store():
954 """Test %store."""
954 """Test %store."""
955 ip = get_ipython()
955 ip = get_ipython()
956 ip.run_line_magic('load_ext', 'storemagic')
956 ip.run_line_magic('load_ext', 'storemagic')
957
957
958 # make sure the storage is empty
958 # make sure the storage is empty
959 ip.run_line_magic('store', '-z')
959 ip.run_line_magic('store', '-z')
960 ip.user_ns['var'] = 42
960 ip.user_ns['var'] = 42
961 ip.run_line_magic('store', 'var')
961 ip.run_line_magic('store', 'var')
962 ip.user_ns['var'] = 39
962 ip.user_ns['var'] = 39
963 ip.run_line_magic('store', '-r')
963 ip.run_line_magic('store', '-r')
964 nt.assert_equal(ip.user_ns['var'], 42)
964 nt.assert_equal(ip.user_ns['var'], 42)
965
965
966 ip.run_line_magic('store', '-d var')
966 ip.run_line_magic('store', '-d var')
967 ip.user_ns['var'] = 39
967 ip.user_ns['var'] = 39
968 ip.run_line_magic('store' , '-r')
968 ip.run_line_magic('store' , '-r')
969 nt.assert_equal(ip.user_ns['var'], 39)
969 nt.assert_equal(ip.user_ns['var'], 39)
970
970
971
971
972 def _run_edit_test(arg_s, exp_filename=None,
972 def _run_edit_test(arg_s, exp_filename=None,
973 exp_lineno=-1,
973 exp_lineno=-1,
974 exp_contents=None,
974 exp_contents=None,
975 exp_is_temp=None):
975 exp_is_temp=None):
976 ip = get_ipython()
976 ip = get_ipython()
977 M = code.CodeMagics(ip)
977 M = code.CodeMagics(ip)
978 last_call = ['','']
978 last_call = ['','']
979 opts,args = M.parse_options(arg_s,'prxn:')
979 opts,args = M.parse_options(arg_s,'prxn:')
980 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
980 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
981
981
982 if exp_filename is not None:
982 if exp_filename is not None:
983 nt.assert_equal(exp_filename, filename)
983 nt.assert_equal(exp_filename, filename)
984 if exp_contents is not None:
984 if exp_contents is not None:
985 with io.open(filename, 'r', encoding='utf-8') as f:
985 with io.open(filename, 'r', encoding='utf-8') as f:
986 contents = f.read()
986 contents = f.read()
987 nt.assert_equal(exp_contents, contents)
987 nt.assert_equal(exp_contents, contents)
988 if exp_lineno != -1:
988 if exp_lineno != -1:
989 nt.assert_equal(exp_lineno, lineno)
989 nt.assert_equal(exp_lineno, lineno)
990 if exp_is_temp is not None:
990 if exp_is_temp is not None:
991 nt.assert_equal(exp_is_temp, is_temp)
991 nt.assert_equal(exp_is_temp, is_temp)
992
992
993
993
994 def test_edit_interactive():
994 def test_edit_interactive():
995 """%edit on interactively defined objects"""
995 """%edit on interactively defined objects"""
996 ip = get_ipython()
996 ip = get_ipython()
997 n = ip.execution_count
997 n = ip.execution_count
998 ip.run_cell(u"def foo(): return 1", store_history=True)
998 ip.run_cell(u"def foo(): return 1", store_history=True)
999
999
1000 try:
1000 try:
1001 _run_edit_test("foo")
1001 _run_edit_test("foo")
1002 except code.InteractivelyDefined as e:
1002 except code.InteractivelyDefined as e:
1003 nt.assert_equal(e.index, n)
1003 nt.assert_equal(e.index, n)
1004 else:
1004 else:
1005 raise AssertionError("Should have raised InteractivelyDefined")
1005 raise AssertionError("Should have raised InteractivelyDefined")
1006
1006
1007
1007
1008 def test_edit_cell():
1008 def test_edit_cell():
1009 """%edit [cell id]"""
1009 """%edit [cell id]"""
1010 ip = get_ipython()
1010 ip = get_ipython()
1011
1011
1012 ip.run_cell(u"def foo(): return 1", store_history=True)
1012 ip.run_cell(u"def foo(): return 1", store_history=True)
1013
1013
1014 # test
1014 # test
1015 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1015 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1016
1016
1017 def test_bookmark():
1017 def test_bookmark():
1018 ip = get_ipython()
1018 ip = get_ipython()
1019 ip.run_line_magic('bookmark', 'bmname')
1019 ip.run_line_magic('bookmark', 'bmname')
1020 with tt.AssertPrints('bmname'):
1020 with tt.AssertPrints('bmname'):
1021 ip.run_line_magic('bookmark', '-l')
1021 ip.run_line_magic('bookmark', '-l')
1022 ip.run_line_magic('bookmark', '-d bmname')
1022 ip.run_line_magic('bookmark', '-d bmname')
1023
1023
1024 def test_ls_magic():
1024 def test_ls_magic():
1025 ip = get_ipython()
1025 ip = get_ipython()
1026 json_formatter = ip.display_formatter.formatters['application/json']
1026 json_formatter = ip.display_formatter.formatters['application/json']
1027 json_formatter.enabled = True
1027 json_formatter.enabled = True
1028 lsmagic = ip.magic('lsmagic')
1028 lsmagic = ip.magic('lsmagic')
1029 with warnings.catch_warnings(record=True) as w:
1029 with warnings.catch_warnings(record=True) as w:
1030 j = json_formatter(lsmagic)
1030 j = json_formatter(lsmagic)
1031 nt.assert_equal(sorted(j), ['cell', 'line'])
1031 nt.assert_equal(sorted(j), ['cell', 'line'])
1032 nt.assert_equal(w, []) # no warnings
1032 nt.assert_equal(w, []) # no warnings
1033
1033
1034 def test_strip_initial_indent():
1034 def test_strip_initial_indent():
1035 def sii(s):
1035 def sii(s):
1036 lines = s.splitlines()
1036 lines = s.splitlines()
1037 return '\n'.join(code.strip_initial_indent(lines))
1037 return '\n'.join(code.strip_initial_indent(lines))
1038
1038
1039 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1039 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1040 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1040 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1041 nt.assert_equal(sii("a\n b"), "a\n b")
1041 nt.assert_equal(sii("a\n b"), "a\n b")
1042
1042
1043 def test_logging_magic_quiet_from_arg():
1043 def test_logging_magic_quiet_from_arg():
1044 _ip.config.LoggingMagics.quiet = False
1044 _ip.config.LoggingMagics.quiet = False
1045 lm = logging.LoggingMagics(shell=_ip)
1045 lm = logging.LoggingMagics(shell=_ip)
1046 with TemporaryDirectory() as td:
1046 with TemporaryDirectory() as td:
1047 try:
1047 try:
1048 with tt.AssertNotPrints(re.compile("Activating.*")):
1048 with tt.AssertNotPrints(re.compile("Activating.*")):
1049 lm.logstart('-q {}'.format(
1049 lm.logstart('-q {}'.format(
1050 os.path.join(td, "quiet_from_arg.log")))
1050 os.path.join(td, "quiet_from_arg.log")))
1051 finally:
1051 finally:
1052 _ip.logger.logstop()
1052 _ip.logger.logstop()
1053
1053
1054 def test_logging_magic_quiet_from_config():
1054 def test_logging_magic_quiet_from_config():
1055 _ip.config.LoggingMagics.quiet = True
1055 _ip.config.LoggingMagics.quiet = True
1056 lm = logging.LoggingMagics(shell=_ip)
1056 lm = logging.LoggingMagics(shell=_ip)
1057 with TemporaryDirectory() as td:
1057 with TemporaryDirectory() as td:
1058 try:
1058 try:
1059 with tt.AssertNotPrints(re.compile("Activating.*")):
1059 with tt.AssertNotPrints(re.compile("Activating.*")):
1060 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1060 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1061 finally:
1061 finally:
1062 _ip.logger.logstop()
1062 _ip.logger.logstop()
1063
1063
1064 def test_logging_magic_not_quiet():
1064 def test_logging_magic_not_quiet():
1065 _ip.config.LoggingMagics.quiet = False
1065 _ip.config.LoggingMagics.quiet = False
1066 lm = logging.LoggingMagics(shell=_ip)
1066 lm = logging.LoggingMagics(shell=_ip)
1067 with TemporaryDirectory() as td:
1067 with TemporaryDirectory() as td:
1068 try:
1068 try:
1069 with tt.AssertPrints(re.compile("Activating.*")):
1069 with tt.AssertPrints(re.compile("Activating.*")):
1070 lm.logstart(os.path.join(td, "not_quiet.log"))
1070 lm.logstart(os.path.join(td, "not_quiet.log"))
1071 finally:
1071 finally:
1072 _ip.logger.logstop()
1072 _ip.logger.logstop()
1073
1073
General Comments 0
You need to be logged in to leave comments. Login now