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