##// END OF EJS Templates
remove sys_version for Python 3...
Paul Ivanov -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,631 +1,630 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 http://www.python.org/2.2.3/license.html"""
16 http://www.python.org/2.2.3/license.html"""
17
17
18 #*****************************************************************************
18 #*****************************************************************************
19 #
19 #
20 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
21 #
21 #
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 #
24 #
25 #
25 #
26 #*****************************************************************************
26 #*****************************************************************************
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import sys
32 import sys
33 import warnings
33 import warnings
34
34
35 from IPython import get_ipython
35 from IPython import get_ipython
36 from IPython.utils import PyColorize, ulinecache
36 from IPython.utils import PyColorize, ulinecache
37 from IPython.utils import coloransi, py3compat
37 from IPython.utils import coloransi, py3compat
38 from IPython.core.excolors import exception_colors
38 from IPython.core.excolors import exception_colors
39 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.testing.skipdoctest import skip_doctest
40
40
41
41
42 prompt = 'ipdb> '
42 prompt = 'ipdb> '
43
43
44 #We have to check this directly from sys.argv, config struct not yet available
44 #We have to check this directly from sys.argv, config struct not yet available
45 from pdb import Pdb as OldPdb
45 from pdb import Pdb as OldPdb
46
46
47 # Allow the set_trace code to operate outside of an ipython instance, even if
47 # Allow the set_trace code to operate outside of an ipython instance, even if
48 # it does so with some limitations. The rest of this support is implemented in
48 # it does so with some limitations. The rest of this support is implemented in
49 # the Tracer constructor.
49 # the Tracer constructor.
50
50
51 def make_arrow(pad):
51 def make_arrow(pad):
52 """generate the leading arrow in front of traceback or debugger"""
52 """generate the leading arrow in front of traceback or debugger"""
53 if pad >= 2:
53 if pad >= 2:
54 return '-'*(pad-2) + '> '
54 return '-'*(pad-2) + '> '
55 elif pad == 1:
55 elif pad == 1:
56 return '>'
56 return '>'
57 return ''
57 return ''
58
58
59
59
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
61 """Exception hook which handles `BdbQuit` exceptions.
61 """Exception hook which handles `BdbQuit` exceptions.
62
62
63 All other exceptions are processed using the `excepthook`
63 All other exceptions are processed using the `excepthook`
64 parameter.
64 parameter.
65 """
65 """
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
67 DeprecationWarning)
67 DeprecationWarning)
68 if et==bdb.BdbQuit:
68 if et==bdb.BdbQuit:
69 print('Exiting Debugger.')
69 print('Exiting Debugger.')
70 elif excepthook is not None:
70 elif excepthook is not None:
71 excepthook(et, ev, tb)
71 excepthook(et, ev, tb)
72 else:
72 else:
73 # Backwards compatibility. Raise deprecation warning?
73 # Backwards compatibility. Raise deprecation warning?
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
75
75
76
76
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 warnings.warn(
78 warnings.warn(
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
80 DeprecationWarning)
80 DeprecationWarning)
81 print('Exiting Debugger.')
81 print('Exiting Debugger.')
82
82
83
83
84 class Tracer(object):
84 class Tracer(object):
85 """
85 """
86 DEPRECATED
86 DEPRECATED
87
87
88 Class for local debugging, similar to pdb.set_trace.
88 Class for local debugging, similar to pdb.set_trace.
89
89
90 Instances of this class, when called, behave like pdb.set_trace, but
90 Instances of this class, when called, behave like pdb.set_trace, but
91 providing IPython's enhanced capabilities.
91 providing IPython's enhanced capabilities.
92
92
93 This is implemented as a class which must be initialized in your own code
93 This is implemented as a class which must be initialized in your own code
94 and not as a standalone function because we need to detect at runtime
94 and not as a standalone function because we need to detect at runtime
95 whether IPython is already active or not. That detection is done in the
95 whether IPython is already active or not. That detection is done in the
96 constructor, ensuring that this code plays nicely with a running IPython,
96 constructor, ensuring that this code plays nicely with a running IPython,
97 while functioning acceptably (though with limitations) if outside of it.
97 while functioning acceptably (though with limitations) if outside of it.
98 """
98 """
99
99
100 @skip_doctest
100 @skip_doctest
101 def __init__(self, colors=None):
101 def __init__(self, colors=None):
102 """
102 """
103 DEPRECATED
103 DEPRECATED
104
104
105 Create a local debugger instance.
105 Create a local debugger instance.
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109
109
110 colors : str, optional
110 colors : str, optional
111 The name of the color scheme to use, it must be one of IPython's
111 The name of the color scheme to use, it must be one of IPython's
112 valid color schemes. If not given, the function will default to
112 valid color schemes. If not given, the function will default to
113 the current IPython scheme when running inside IPython, and to
113 the current IPython scheme when running inside IPython, and to
114 'NoColor' otherwise.
114 'NoColor' otherwise.
115
115
116 Examples
116 Examples
117 --------
117 --------
118 ::
118 ::
119
119
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
121
121
122 Later in your code::
122 Later in your code::
123
123
124 debug_here() # -> will open up the debugger at that point.
124 debug_here() # -> will open up the debugger at that point.
125
125
126 Once the debugger activates, you can use all of its regular commands to
126 Once the debugger activates, you can use all of its regular commands to
127 step through code, set breakpoints, etc. See the pdb documentation
127 step through code, set breakpoints, etc. See the pdb documentation
128 from the Python standard library for usage details.
128 from the Python standard library for usage details.
129 """
129 """
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
131 "`IPython.core.debugger.Pdb.set_trace()`",
131 "`IPython.core.debugger.Pdb.set_trace()`",
132 DeprecationWarning)
132 DeprecationWarning)
133
133
134 ip = get_ipython()
134 ip = get_ipython()
135 if ip is None:
135 if ip is None:
136 # Outside of ipython, we set our own exception hook manually
136 # Outside of ipython, we set our own exception hook manually
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
138 excepthook=sys.excepthook)
138 excepthook=sys.excepthook)
139 def_colors = 'NoColor'
139 def_colors = 'NoColor'
140 else:
140 else:
141 # In ipython, we use its custom exception handler mechanism
141 # In ipython, we use its custom exception handler mechanism
142 def_colors = ip.colors
142 def_colors = ip.colors
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
144
144
145 if colors is None:
145 if colors is None:
146 colors = def_colors
146 colors = def_colors
147
147
148 # The stdlib debugger internally uses a modified repr from the `repr`
148 # The stdlib debugger internally uses a modified repr from the `repr`
149 # module, that limits the length of printed strings to a hardcoded
149 # module, that limits the length of printed strings to a hardcoded
150 # limit of 30 characters. That much trimming is too aggressive, let's
150 # limit of 30 characters. That much trimming is too aggressive, let's
151 # at least raise that limit to 80 chars, which should be enough for
151 # at least raise that limit to 80 chars, which should be enough for
152 # most interactive uses.
152 # most interactive uses.
153 try:
153 try:
154 try:
154 try:
155 from reprlib import aRepr # Py 3
155 from reprlib import aRepr # Py 3
156 except ImportError:
156 except ImportError:
157 from repr import aRepr # Py 2
157 from repr import aRepr # Py 2
158 aRepr.maxstring = 80
158 aRepr.maxstring = 80
159 except:
159 except:
160 # This is only a user-facing convenience, so any error we encounter
160 # This is only a user-facing convenience, so any error we encounter
161 # here can be warned about but can be otherwise ignored. These
161 # here can be warned about but can be otherwise ignored. These
162 # printouts will tell us about problems if this API changes
162 # printouts will tell us about problems if this API changes
163 import traceback
163 import traceback
164 traceback.print_exc()
164 traceback.print_exc()
165
165
166 self.debugger = Pdb(colors)
166 self.debugger = Pdb(colors)
167
167
168 def __call__(self):
168 def __call__(self):
169 """Starts an interactive debugger at the point where called.
169 """Starts an interactive debugger at the point where called.
170
170
171 This is similar to the pdb.set_trace() function from the std lib, but
171 This is similar to the pdb.set_trace() function from the std lib, but
172 using IPython's enhanced debugger."""
172 using IPython's enhanced debugger."""
173
173
174 self.debugger.set_trace(sys._getframe().f_back)
174 self.debugger.set_trace(sys._getframe().f_back)
175
175
176
176
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
178 """Make new_fn have old_fn's doc string. This is particularly useful
178 """Make new_fn have old_fn's doc string. This is particularly useful
179 for the ``do_...`` commands that hook into the help system.
179 for the ``do_...`` commands that hook into the help system.
180 Adapted from from a comp.lang.python posting
180 Adapted from from a comp.lang.python posting
181 by Duncan Booth."""
181 by Duncan Booth."""
182 def wrapper(*args, **kw):
182 def wrapper(*args, **kw):
183 return new_fn(*args, **kw)
183 return new_fn(*args, **kw)
184 if old_fn.__doc__:
184 if old_fn.__doc__:
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
186 return wrapper
186 return wrapper
187
187
188
188
189 def _file_lines(fname):
189 def _file_lines(fname):
190 """Return the contents of a named file as a list of lines.
190 """Return the contents of a named file as a list of lines.
191
191
192 This function never raises an IOError exception: if the file can't be
192 This function never raises an IOError exception: if the file can't be
193 read, it simply returns an empty list."""
193 read, it simply returns an empty list."""
194
194
195 try:
195 try:
196 outfile = open(fname)
196 outfile = open(fname)
197 except IOError:
197 except IOError:
198 return []
198 return []
199 else:
199 else:
200 out = outfile.readlines()
200 out = outfile.readlines()
201 outfile.close()
201 outfile.close()
202 return out
202 return out
203
203
204
204
205 class Pdb(OldPdb, object):
205 class Pdb(OldPdb, object):
206 """Modified Pdb class, does not load readline.
206 """Modified Pdb class, does not load readline.
207
207
208 for a standalone version that uses prompt_toolkit, see
208 for a standalone version that uses prompt_toolkit, see
209 `IPython.terminal.debugger.TerminalPdb` and
209 `IPython.terminal.debugger.TerminalPdb` and
210 `IPython.terminal.debugger.set_trace()`
210 `IPython.terminal.debugger.set_trace()`
211 """
211 """
212
212
213 def __init__(self, color_scheme=None, completekey=None,
213 def __init__(self, color_scheme=None, completekey=None,
214 stdin=None, stdout=None, context=5):
214 stdin=None, stdout=None, context=5):
215
215
216 # Parent constructor:
216 # Parent constructor:
217 try:
217 try:
218 self.context = int(context)
218 self.context = int(context)
219 if self.context <= 0:
219 if self.context <= 0:
220 raise ValueError("Context must be a positive integer")
220 raise ValueError("Context must be a positive integer")
221 except (TypeError, ValueError):
221 except (TypeError, ValueError):
222 raise ValueError("Context must be a positive integer")
222 raise ValueError("Context must be a positive integer")
223
223
224 OldPdb.__init__(self, completekey, stdin, stdout)
224 OldPdb.__init__(self, completekey, stdin, stdout)
225
225
226 # IPython changes...
226 # IPython changes...
227 self.shell = get_ipython()
227 self.shell = get_ipython()
228
228
229 if self.shell is None:
229 if self.shell is None:
230 save_main = sys.modules['__main__']
230 save_main = sys.modules['__main__']
231 # No IPython instance running, we must create one
231 # No IPython instance running, we must create one
232 from IPython.terminal.interactiveshell import \
232 from IPython.terminal.interactiveshell import \
233 TerminalInteractiveShell
233 TerminalInteractiveShell
234 self.shell = TerminalInteractiveShell.instance()
234 self.shell = TerminalInteractiveShell.instance()
235 # needed by any code which calls __import__("__main__") after
235 # needed by any code which calls __import__("__main__") after
236 # the debugger was entered. See also #9941.
236 # the debugger was entered. See also #9941.
237 sys.modules['__main__'] = save_main
237 sys.modules['__main__'] = save_main
238
238
239 if color_scheme is not None:
239 if color_scheme is not None:
240 warnings.warn(
240 warnings.warn(
241 "The `color_scheme` argument is deprecated since version 5.1",
241 "The `color_scheme` argument is deprecated since version 5.1",
242 DeprecationWarning, stacklevel=2)
242 DeprecationWarning, stacklevel=2)
243 else:
243 else:
244 color_scheme = self.shell.colors
244 color_scheme = self.shell.colors
245
245
246 self.aliases = {}
246 self.aliases = {}
247
247
248 # Create color table: we copy the default one from the traceback
248 # Create color table: we copy the default one from the traceback
249 # module and add a few attributes needed for debugging
249 # module and add a few attributes needed for debugging
250 self.color_scheme_table = exception_colors()
250 self.color_scheme_table = exception_colors()
251
251
252 # shorthands
252 # shorthands
253 C = coloransi.TermColors
253 C = coloransi.TermColors
254 cst = self.color_scheme_table
254 cst = self.color_scheme_table
255
255
256 cst['NoColor'].colors.prompt = C.NoColor
256 cst['NoColor'].colors.prompt = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259
259
260 cst['Linux'].colors.prompt = C.Green
260 cst['Linux'].colors.prompt = C.Green
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
262 cst['Linux'].colors.breakpoint_disabled = C.Red
262 cst['Linux'].colors.breakpoint_disabled = C.Red
263
263
264 cst['LightBG'].colors.prompt = C.Blue
264 cst['LightBG'].colors.prompt = C.Blue
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
267
267
268 cst['Neutral'].colors.prompt = C.Blue
268 cst['Neutral'].colors.prompt = C.Blue
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
271
271
272
272
273 # Add a python parser so we can syntax highlight source while
273 # Add a python parser so we can syntax highlight source while
274 # debugging.
274 # debugging.
275 self.parser = PyColorize.Parser(style=color_scheme)
275 self.parser = PyColorize.Parser(style=color_scheme)
276 self.set_colors(color_scheme)
276 self.set_colors(color_scheme)
277
277
278 # Set the prompt - the default prompt is '(Pdb)'
278 # Set the prompt - the default prompt is '(Pdb)'
279 self.prompt = prompt
279 self.prompt = prompt
280
280
281 def set_colors(self, scheme):
281 def set_colors(self, scheme):
282 """Shorthand access to the color table scheme selector method."""
282 """Shorthand access to the color table scheme selector method."""
283 self.color_scheme_table.set_active_scheme(scheme)
283 self.color_scheme_table.set_active_scheme(scheme)
284 self.parser.style = scheme
284 self.parser.style = scheme
285
285
286 def interaction(self, frame, traceback):
286 def interaction(self, frame, traceback):
287 try:
287 try:
288 OldPdb.interaction(self, frame, traceback)
288 OldPdb.interaction(self, frame, traceback)
289 except KeyboardInterrupt:
289 except KeyboardInterrupt:
290 sys.stdout.write('\n' + self.shell.get_exception_only())
290 sys.stdout.write('\n' + self.shell.get_exception_only())
291
291
292 def parseline(self, line):
292 def parseline(self, line):
293 if line.startswith("!!"):
293 if line.startswith("!!"):
294 # Force standard behavior.
294 # Force standard behavior.
295 return super(Pdb, self).parseline(line[2:])
295 return super(Pdb, self).parseline(line[2:])
296 # "Smart command mode" from pdb++: don't execute commands if a variable
296 # "Smart command mode" from pdb++: don't execute commands if a variable
297 # with the same name exists.
297 # with the same name exists.
298 cmd, arg, newline = super(Pdb, self).parseline(line)
298 cmd, arg, newline = super(Pdb, self).parseline(line)
299 # Fix for #9611: Do not trigger smart command if the command is `exit`
299 # Fix for #9611: Do not trigger smart command if the command is `exit`
300 # or `quit` and it would resolve to their *global* value (the
300 # or `quit` and it would resolve to their *global* value (the
301 # `ExitAutocall` object). Just checking that it is not present in the
301 # `ExitAutocall` object). Just checking that it is not present in the
302 # locals dict is not enough as locals and globals match at the
302 # locals dict is not enough as locals and globals match at the
303 # toplevel.
303 # toplevel.
304 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
304 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
305 and not (cmd in ["exit", "quit"]
305 and not (cmd in ["exit", "quit"]
306 and (self.curframe.f_locals is self.curframe.f_globals
306 and (self.curframe.f_locals is self.curframe.f_globals
307 or cmd not in self.curframe.f_locals))):
307 or cmd not in self.curframe.f_locals))):
308 return super(Pdb, self).parseline("!" + line)
308 return super(Pdb, self).parseline("!" + line)
309 return super(Pdb, self).parseline(line)
309 return super(Pdb, self).parseline(line)
310
310
311 def new_do_up(self, arg):
311 def new_do_up(self, arg):
312 OldPdb.do_up(self, arg)
312 OldPdb.do_up(self, arg)
313 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
313 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
314
314
315 def new_do_down(self, arg):
315 def new_do_down(self, arg):
316 OldPdb.do_down(self, arg)
316 OldPdb.do_down(self, arg)
317
317
318 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
318 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
319
319
320 def new_do_frame(self, arg):
320 def new_do_frame(self, arg):
321 OldPdb.do_frame(self, arg)
321 OldPdb.do_frame(self, arg)
322
322
323 def new_do_quit(self, arg):
323 def new_do_quit(self, arg):
324
324
325 if hasattr(self, 'old_all_completions'):
325 if hasattr(self, 'old_all_completions'):
326 self.shell.Completer.all_completions=self.old_all_completions
326 self.shell.Completer.all_completions=self.old_all_completions
327
327
328 return OldPdb.do_quit(self, arg)
328 return OldPdb.do_quit(self, arg)
329
329
330 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
330 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
331
331
332 def new_do_restart(self, arg):
332 def new_do_restart(self, arg):
333 """Restart command. In the context of ipython this is exactly the same
333 """Restart command. In the context of ipython this is exactly the same
334 thing as 'quit'."""
334 thing as 'quit'."""
335 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
335 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
336 return self.do_quit(arg)
336 return self.do_quit(arg)
337
337
338 def print_stack_trace(self, context=None):
338 def print_stack_trace(self, context=None):
339 if context is None:
339 if context is None:
340 context = self.context
340 context = self.context
341 try:
341 try:
342 context=int(context)
342 context=int(context)
343 if context <= 0:
343 if context <= 0:
344 raise ValueError("Context must be a positive integer")
344 raise ValueError("Context must be a positive integer")
345 except (TypeError, ValueError):
345 except (TypeError, ValueError):
346 raise ValueError("Context must be a positive integer")
346 raise ValueError("Context must be a positive integer")
347 try:
347 try:
348 for frame_lineno in self.stack:
348 for frame_lineno in self.stack:
349 self.print_stack_entry(frame_lineno, context=context)
349 self.print_stack_entry(frame_lineno, context=context)
350 except KeyboardInterrupt:
350 except KeyboardInterrupt:
351 pass
351 pass
352
352
353 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
353 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
354 context=None):
354 context=None):
355 if context is None:
355 if context is None:
356 context = self.context
356 context = self.context
357 try:
357 try:
358 context=int(context)
358 context=int(context)
359 if context <= 0:
359 if context <= 0:
360 raise ValueError("Context must be a positive integer")
360 raise ValueError("Context must be a positive integer")
361 except (TypeError, ValueError):
361 except (TypeError, ValueError):
362 raise ValueError("Context must be a positive integer")
362 raise ValueError("Context must be a positive integer")
363 print(self.format_stack_entry(frame_lineno, '', context))
363 print(self.format_stack_entry(frame_lineno, '', context))
364
364
365 # vds: >>
365 # vds: >>
366 frame, lineno = frame_lineno
366 frame, lineno = frame_lineno
367 filename = frame.f_code.co_filename
367 filename = frame.f_code.co_filename
368 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
368 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
369 # vds: <<
369 # vds: <<
370
370
371 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
371 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
372 if context is None:
372 if context is None:
373 context = self.context
373 context = self.context
374 try:
374 try:
375 context=int(context)
375 context=int(context)
376 if context <= 0:
376 if context <= 0:
377 print("Context must be a positive integer")
377 print("Context must be a positive integer")
378 except (TypeError, ValueError):
378 except (TypeError, ValueError):
379 print("Context must be a positive integer")
379 print("Context must be a positive integer")
380 try:
380 try:
381 import reprlib # Py 3
381 import reprlib # Py 3
382 except ImportError:
382 except ImportError:
383 import repr as reprlib # Py 2
383 import repr as reprlib # Py 2
384
384
385 ret = []
385 ret = []
386
386
387 Colors = self.color_scheme_table.active_colors
387 Colors = self.color_scheme_table.active_colors
388 ColorsNormal = Colors.Normal
388 ColorsNormal = Colors.Normal
389 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
389 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
390 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
390 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
391 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
391 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
392 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
392 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
393 ColorsNormal)
393 ColorsNormal)
394
394
395 frame, lineno = frame_lineno
395 frame, lineno = frame_lineno
396
396
397 return_value = ''
397 return_value = ''
398 if '__return__' in frame.f_locals:
398 if '__return__' in frame.f_locals:
399 rv = frame.f_locals['__return__']
399 rv = frame.f_locals['__return__']
400 #return_value += '->'
400 #return_value += '->'
401 return_value += reprlib.repr(rv) + '\n'
401 return_value += reprlib.repr(rv) + '\n'
402 ret.append(return_value)
402 ret.append(return_value)
403
403
404 #s = filename + '(' + `lineno` + ')'
404 #s = filename + '(' + `lineno` + ')'
405 filename = self.canonic(frame.f_code.co_filename)
405 filename = self.canonic(frame.f_code.co_filename)
406 link = tpl_link % py3compat.cast_unicode(filename)
406 link = tpl_link % py3compat.cast_unicode(filename)
407
407
408 if frame.f_code.co_name:
408 if frame.f_code.co_name:
409 func = frame.f_code.co_name
409 func = frame.f_code.co_name
410 else:
410 else:
411 func = "<lambda>"
411 func = "<lambda>"
412
412
413 call = ''
413 call = ''
414 if func != '?':
414 if func != '?':
415 if '__args__' in frame.f_locals:
415 if '__args__' in frame.f_locals:
416 args = reprlib.repr(frame.f_locals['__args__'])
416 args = reprlib.repr(frame.f_locals['__args__'])
417 else:
417 else:
418 args = '()'
418 args = '()'
419 call = tpl_call % (func, args)
419 call = tpl_call % (func, args)
420
420
421 # The level info should be generated in the same format pdb uses, to
421 # The level info should be generated in the same format pdb uses, to
422 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
422 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
423 if frame is self.curframe:
423 if frame is self.curframe:
424 ret.append('> ')
424 ret.append('> ')
425 else:
425 else:
426 ret.append(' ')
426 ret.append(' ')
427 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
427 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
428
428
429 start = lineno - 1 - context//2
429 start = lineno - 1 - context//2
430 lines = ulinecache.getlines(filename)
430 lines = ulinecache.getlines(filename)
431 start = min(start, len(lines) - context)
431 start = min(start, len(lines) - context)
432 start = max(start, 0)
432 start = max(start, 0)
433 lines = lines[start : start + context]
433 lines = lines[start : start + context]
434
434
435 for i,line in enumerate(lines):
435 for i,line in enumerate(lines):
436 show_arrow = (start + 1 + i == lineno)
436 show_arrow = (start + 1 + i == lineno)
437 linetpl = (frame is self.curframe or show_arrow) \
437 linetpl = (frame is self.curframe or show_arrow) \
438 and tpl_line_em \
438 and tpl_line_em \
439 or tpl_line
439 or tpl_line
440 ret.append(self.__format_line(linetpl, filename,
440 ret.append(self.__format_line(linetpl, filename,
441 start + 1 + i, line,
441 start + 1 + i, line,
442 arrow = show_arrow) )
442 arrow = show_arrow) )
443 return ''.join(ret)
443 return ''.join(ret)
444
444
445 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
445 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
446 bp_mark = ""
446 bp_mark = ""
447 bp_mark_color = ""
447 bp_mark_color = ""
448
448
449 new_line, err = self.parser.format2(line, 'str')
449 new_line, err = self.parser.format2(line, 'str')
450 if not err:
450 if not err:
451 line = new_line
451 line = new_line
452
452
453 bp = None
453 bp = None
454 if lineno in self.get_file_breaks(filename):
454 if lineno in self.get_file_breaks(filename):
455 bps = self.get_breaks(filename, lineno)
455 bps = self.get_breaks(filename, lineno)
456 bp = bps[-1]
456 bp = bps[-1]
457
457
458 if bp:
458 if bp:
459 Colors = self.color_scheme_table.active_colors
459 Colors = self.color_scheme_table.active_colors
460 bp_mark = str(bp.number)
460 bp_mark = str(bp.number)
461 bp_mark_color = Colors.breakpoint_enabled
461 bp_mark_color = Colors.breakpoint_enabled
462 if not bp.enabled:
462 if not bp.enabled:
463 bp_mark_color = Colors.breakpoint_disabled
463 bp_mark_color = Colors.breakpoint_disabled
464
464
465 numbers_width = 7
465 numbers_width = 7
466 if arrow:
466 if arrow:
467 # This is the line with the error
467 # This is the line with the error
468 pad = numbers_width - len(str(lineno)) - len(bp_mark)
468 pad = numbers_width - len(str(lineno)) - len(bp_mark)
469 num = '%s%s' % (make_arrow(pad), str(lineno))
469 num = '%s%s' % (make_arrow(pad), str(lineno))
470 else:
470 else:
471 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
471 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
472
472
473 return tpl_line % (bp_mark_color + bp_mark, num, line)
473 return tpl_line % (bp_mark_color + bp_mark, num, line)
474
474
475
475
476 def print_list_lines(self, filename, first, last):
476 def print_list_lines(self, filename, first, last):
477 """The printing (as opposed to the parsing part of a 'list'
477 """The printing (as opposed to the parsing part of a 'list'
478 command."""
478 command."""
479 try:
479 try:
480 Colors = self.color_scheme_table.active_colors
480 Colors = self.color_scheme_table.active_colors
481 ColorsNormal = Colors.Normal
481 ColorsNormal = Colors.Normal
482 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
482 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
483 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
483 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
484 src = []
484 src = []
485 if filename == "<string>" and hasattr(self, "_exec_filename"):
485 if filename == "<string>" and hasattr(self, "_exec_filename"):
486 filename = self._exec_filename
486 filename = self._exec_filename
487
487
488 for lineno in range(first, last+1):
488 for lineno in range(first, last+1):
489 line = ulinecache.getline(filename, lineno)
489 line = ulinecache.getline(filename, lineno)
490 if not line:
490 if not line:
491 break
491 break
492
492
493 if lineno == self.curframe.f_lineno:
493 if lineno == self.curframe.f_lineno:
494 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
494 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
495 else:
495 else:
496 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
496 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
497
497
498 src.append(line)
498 src.append(line)
499 self.lineno = lineno
499 self.lineno = lineno
500
500
501 print(''.join(src))
501 print(''.join(src))
502
502
503 except KeyboardInterrupt:
503 except KeyboardInterrupt:
504 pass
504 pass
505
505
506 def do_list(self, arg):
506 def do_list(self, arg):
507 self.lastcmd = 'list'
507 self.lastcmd = 'list'
508 last = None
508 last = None
509 if arg:
509 if arg:
510 try:
510 try:
511 x = eval(arg, {}, {})
511 x = eval(arg, {}, {})
512 if type(x) == type(()):
512 if type(x) == type(()):
513 first, last = x
513 first, last = x
514 first = int(first)
514 first = int(first)
515 last = int(last)
515 last = int(last)
516 if last < first:
516 if last < first:
517 # Assume it's a count
517 # Assume it's a count
518 last = first + last
518 last = first + last
519 else:
519 else:
520 first = max(1, int(x) - 5)
520 first = max(1, int(x) - 5)
521 except:
521 except:
522 print('*** Error in argument:', repr(arg))
522 print('*** Error in argument:', repr(arg))
523 return
523 return
524 elif self.lineno is None:
524 elif self.lineno is None:
525 first = max(1, self.curframe.f_lineno - 5)
525 first = max(1, self.curframe.f_lineno - 5)
526 else:
526 else:
527 first = self.lineno + 1
527 first = self.lineno + 1
528 if last is None:
528 if last is None:
529 last = first + 10
529 last = first + 10
530 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
530 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
531
531
532 # vds: >>
532 # vds: >>
533 lineno = first
533 lineno = first
534 filename = self.curframe.f_code.co_filename
534 filename = self.curframe.f_code.co_filename
535 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
535 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
536 # vds: <<
536 # vds: <<
537
537
538 do_l = do_list
538 do_l = do_list
539
539
540 def getsourcelines(self, obj):
540 def getsourcelines(self, obj):
541 lines, lineno = inspect.findsource(obj)
541 lines, lineno = inspect.findsource(obj)
542 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
542 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
543 # must be a module frame: do not try to cut a block out of it
543 # must be a module frame: do not try to cut a block out of it
544 return lines, 1
544 return lines, 1
545 elif inspect.ismodule(obj):
545 elif inspect.ismodule(obj):
546 return lines, 1
546 return lines, 1
547 return inspect.getblock(lines[lineno:]), lineno+1
547 return inspect.getblock(lines[lineno:]), lineno+1
548
548
549 def do_longlist(self, arg):
549 def do_longlist(self, arg):
550 self.lastcmd = 'longlist'
550 self.lastcmd = 'longlist'
551 try:
551 try:
552 lines, lineno = self.getsourcelines(self.curframe)
552 lines, lineno = self.getsourcelines(self.curframe)
553 except OSError as err:
553 except OSError as err:
554 self.error(err)
554 self.error(err)
555 return
555 return
556 last = lineno + len(lines)
556 last = lineno + len(lines)
557 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
557 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
558 do_ll = do_longlist
558 do_ll = do_longlist
559
559
560 def do_pdef(self, arg):
560 def do_pdef(self, arg):
561 """Print the call signature for any callable object.
561 """Print the call signature for any callable object.
562
562
563 The debugger interface to %pdef"""
563 The debugger interface to %pdef"""
564 namespaces = [('Locals', self.curframe.f_locals),
564 namespaces = [('Locals', self.curframe.f_locals),
565 ('Globals', self.curframe.f_globals)]
565 ('Globals', self.curframe.f_globals)]
566 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
566 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
567
567
568 def do_pdoc(self, arg):
568 def do_pdoc(self, arg):
569 """Print the docstring for an object.
569 """Print the docstring for an object.
570
570
571 The debugger interface to %pdoc."""
571 The debugger interface to %pdoc."""
572 namespaces = [('Locals', self.curframe.f_locals),
572 namespaces = [('Locals', self.curframe.f_locals),
573 ('Globals', self.curframe.f_globals)]
573 ('Globals', self.curframe.f_globals)]
574 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
574 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
575
575
576 def do_pfile(self, arg):
576 def do_pfile(self, arg):
577 """Print (or run through pager) the file where an object is defined.
577 """Print (or run through pager) the file where an object is defined.
578
578
579 The debugger interface to %pfile.
579 The debugger interface to %pfile.
580 """
580 """
581 namespaces = [('Locals', self.curframe.f_locals),
581 namespaces = [('Locals', self.curframe.f_locals),
582 ('Globals', self.curframe.f_globals)]
582 ('Globals', self.curframe.f_globals)]
583 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
583 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
584
584
585 def do_pinfo(self, arg):
585 def do_pinfo(self, arg):
586 """Provide detailed information about an object.
586 """Provide detailed information about an object.
587
587
588 The debugger interface to %pinfo, i.e., obj?."""
588 The debugger interface to %pinfo, i.e., obj?."""
589 namespaces = [('Locals', self.curframe.f_locals),
589 namespaces = [('Locals', self.curframe.f_locals),
590 ('Globals', self.curframe.f_globals)]
590 ('Globals', self.curframe.f_globals)]
591 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
591 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
592
592
593 def do_pinfo2(self, arg):
593 def do_pinfo2(self, arg):
594 """Provide extra detailed information about an object.
594 """Provide extra detailed information about an object.
595
595
596 The debugger interface to %pinfo2, i.e., obj??."""
596 The debugger interface to %pinfo2, i.e., obj??."""
597 namespaces = [('Locals', self.curframe.f_locals),
597 namespaces = [('Locals', self.curframe.f_locals),
598 ('Globals', self.curframe.f_globals)]
598 ('Globals', self.curframe.f_globals)]
599 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
599 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
600
600
601 def do_psource(self, arg):
601 def do_psource(self, arg):
602 """Print (or run through pager) the source code for an object."""
602 """Print (or run through pager) the source code for an object."""
603 namespaces = [('Locals', self.curframe.f_locals),
603 namespaces = [('Locals', self.curframe.f_locals),
604 ('Globals', self.curframe.f_globals)]
604 ('Globals', self.curframe.f_globals)]
605 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
605 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
606
606
607 if sys.version_info > (3, ):
608 def do_where(self, arg):
607 def do_where(self, arg):
609 """w(here)
608 """w(here)
610 Print a stack trace, with the most recent frame at the bottom.
609 Print a stack trace, with the most recent frame at the bottom.
611 An arrow indicates the "current frame", which determines the
610 An arrow indicates the "current frame", which determines the
612 context of most commands. 'bt' is an alias for this command.
611 context of most commands. 'bt' is an alias for this command.
613
612
614 Take a number as argument as an (optional) number of context line to
613 Take a number as argument as an (optional) number of context line to
615 print"""
614 print"""
616 if arg:
615 if arg:
617 context = int(arg)
616 context = int(arg)
618 self.print_stack_trace(context)
617 self.print_stack_trace(context)
619 else:
618 else:
620 self.print_stack_trace()
619 self.print_stack_trace()
621
620
622 do_w = do_where
621 do_w = do_where
623
622
624
623
625 def set_trace(frame=None):
624 def set_trace(frame=None):
626 """
625 """
627 Start debugging from `frame`.
626 Start debugging from `frame`.
628
627
629 If frame is not specified, debugging starts from caller's frame.
628 If frame is not specified, debugging starts from caller's frame.
630 """
629 """
631 Pdb().set_trace(frame or sys._getframe().f_back)
630 Pdb().set_trace(frame or sys._getframe().f_back)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,806 +1,799 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for the IPython tab-completion machinery."""
2 """Tests for the IPython tab-completion machinery."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import sys
8 import sys
9 import unittest
9 import unittest
10
10
11 from contextlib import contextmanager
11 from contextlib import contextmanager
12
12
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 from traitlets.config.loader import Config
15 from traitlets.config.loader import Config
16 from IPython import get_ipython
16 from IPython import get_ipython
17 from IPython.core import completer
17 from IPython.core import completer
18 from IPython.external.decorators import knownfailureif
18 from IPython.external.decorators import knownfailureif
19 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
20 from IPython.utils.generics import complete_object
20 from IPython.utils.generics import complete_object
21 from IPython.utils.py3compat import string_types, unicode_type
21 from IPython.utils.py3compat import string_types, unicode_type
22 from IPython.testing import decorators as dec
22 from IPython.testing import decorators as dec
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Test functions
25 # Test functions
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 @contextmanager
28 @contextmanager
29 def greedy_completion():
29 def greedy_completion():
30 ip = get_ipython()
30 ip = get_ipython()
31 greedy_original = ip.Completer.greedy
31 greedy_original = ip.Completer.greedy
32 try:
32 try:
33 ip.Completer.greedy = True
33 ip.Completer.greedy = True
34 yield
34 yield
35 finally:
35 finally:
36 ip.Completer.greedy = greedy_original
36 ip.Completer.greedy = greedy_original
37
37
38 def test_protect_filename():
38 def test_protect_filename():
39 if sys.platform == 'win32':
39 if sys.platform == 'win32':
40 pairs = [('abc','abc'),
40 pairs = [('abc','abc'),
41 (' abc','" abc"'),
41 (' abc','" abc"'),
42 ('a bc','"a bc"'),
42 ('a bc','"a bc"'),
43 ('a bc','"a bc"'),
43 ('a bc','"a bc"'),
44 (' bc','" bc"'),
44 (' bc','" bc"'),
45 ]
45 ]
46 else:
46 else:
47 pairs = [('abc','abc'),
47 pairs = [('abc','abc'),
48 (' abc',r'\ abc'),
48 (' abc',r'\ abc'),
49 ('a bc',r'a\ bc'),
49 ('a bc',r'a\ bc'),
50 ('a bc',r'a\ \ bc'),
50 ('a bc',r'a\ \ bc'),
51 (' bc',r'\ \ bc'),
51 (' bc',r'\ \ bc'),
52 # On posix, we also protect parens and other special characters.
52 # On posix, we also protect parens and other special characters.
53 ('a(bc',r'a\(bc'),
53 ('a(bc',r'a\(bc'),
54 ('a)bc',r'a\)bc'),
54 ('a)bc',r'a\)bc'),
55 ('a( )bc',r'a\(\ \)bc'),
55 ('a( )bc',r'a\(\ \)bc'),
56 ('a[1]bc', r'a\[1\]bc'),
56 ('a[1]bc', r'a\[1\]bc'),
57 ('a{1}bc', r'a\{1\}bc'),
57 ('a{1}bc', r'a\{1\}bc'),
58 ('a#bc', r'a\#bc'),
58 ('a#bc', r'a\#bc'),
59 ('a?bc', r'a\?bc'),
59 ('a?bc', r'a\?bc'),
60 ('a=bc', r'a\=bc'),
60 ('a=bc', r'a\=bc'),
61 ('a\\bc', r'a\\bc'),
61 ('a\\bc', r'a\\bc'),
62 ('a|bc', r'a\|bc'),
62 ('a|bc', r'a\|bc'),
63 ('a;bc', r'a\;bc'),
63 ('a;bc', r'a\;bc'),
64 ('a:bc', r'a\:bc'),
64 ('a:bc', r'a\:bc'),
65 ("a'bc", r"a\'bc"),
65 ("a'bc", r"a\'bc"),
66 ('a*bc', r'a\*bc'),
66 ('a*bc', r'a\*bc'),
67 ('a"bc', r'a\"bc'),
67 ('a"bc', r'a\"bc'),
68 ('a^bc', r'a\^bc'),
68 ('a^bc', r'a\^bc'),
69 ('a&bc', r'a\&bc'),
69 ('a&bc', r'a\&bc'),
70 ]
70 ]
71 # run the actual tests
71 # run the actual tests
72 for s1, s2 in pairs:
72 for s1, s2 in pairs:
73 s1p = completer.protect_filename(s1)
73 s1p = completer.protect_filename(s1)
74 nt.assert_equal(s1p, s2)
74 nt.assert_equal(s1p, s2)
75
75
76
76
77 def check_line_split(splitter, test_specs):
77 def check_line_split(splitter, test_specs):
78 for part1, part2, split in test_specs:
78 for part1, part2, split in test_specs:
79 cursor_pos = len(part1)
79 cursor_pos = len(part1)
80 line = part1+part2
80 line = part1+part2
81 out = splitter.split_line(line, cursor_pos)
81 out = splitter.split_line(line, cursor_pos)
82 nt.assert_equal(out, split)
82 nt.assert_equal(out, split)
83
83
84
84
85 def test_line_split():
85 def test_line_split():
86 """Basic line splitter test with default specs."""
86 """Basic line splitter test with default specs."""
87 sp = completer.CompletionSplitter()
87 sp = completer.CompletionSplitter()
88 # The format of the test specs is: part1, part2, expected answer. Parts 1
88 # The format of the test specs is: part1, part2, expected answer. Parts 1
89 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
89 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
90 # was at the end of part1. So an empty part2 represents someone hitting
90 # was at the end of part1. So an empty part2 represents someone hitting
91 # tab at the end of the line, the most common case.
91 # tab at the end of the line, the most common case.
92 t = [('run some/scrip', '', 'some/scrip'),
92 t = [('run some/scrip', '', 'some/scrip'),
93 ('run scripts/er', 'ror.py foo', 'scripts/er'),
93 ('run scripts/er', 'ror.py foo', 'scripts/er'),
94 ('echo $HOM', '', 'HOM'),
94 ('echo $HOM', '', 'HOM'),
95 ('print sys.pa', '', 'sys.pa'),
95 ('print sys.pa', '', 'sys.pa'),
96 ('print(sys.pa', '', 'sys.pa'),
96 ('print(sys.pa', '', 'sys.pa'),
97 ("execfile('scripts/er", '', 'scripts/er'),
97 ("execfile('scripts/er", '', 'scripts/er'),
98 ('a[x.', '', 'x.'),
98 ('a[x.', '', 'x.'),
99 ('a[x.', 'y', 'x.'),
99 ('a[x.', 'y', 'x.'),
100 ('cd "some_file/', '', 'some_file/'),
100 ('cd "some_file/', '', 'some_file/'),
101 ]
101 ]
102 check_line_split(sp, t)
102 check_line_split(sp, t)
103 # Ensure splitting works OK with unicode by re-running the tests with
103 # Ensure splitting works OK with unicode by re-running the tests with
104 # all inputs turned into unicode
104 # all inputs turned into unicode
105 check_line_split(sp, [ map(unicode_type, p) for p in t] )
105 check_line_split(sp, [ map(unicode_type, p) for p in t] )
106
106
107
107
108 def test_custom_completion_error():
108 def test_custom_completion_error():
109 """Test that errors from custom attribute completers are silenced."""
109 """Test that errors from custom attribute completers are silenced."""
110 ip = get_ipython()
110 ip = get_ipython()
111 class A(object): pass
111 class A(object): pass
112 ip.user_ns['a'] = A()
112 ip.user_ns['a'] = A()
113
113
114 @complete_object.when_type(A)
114 @complete_object.when_type(A)
115 def complete_A(a, existing_completions):
115 def complete_A(a, existing_completions):
116 raise TypeError("this should be silenced")
116 raise TypeError("this should be silenced")
117
117
118 ip.complete("a.")
118 ip.complete("a.")
119
119
120
120
121 def test_unicode_completions():
121 def test_unicode_completions():
122 ip = get_ipython()
122 ip = get_ipython()
123 # Some strings that trigger different types of completion. Check them both
123 # Some strings that trigger different types of completion. Check them both
124 # in str and unicode forms
124 # in str and unicode forms
125 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
125 s = ['ru', '%ru', 'cd /', 'floa', 'float(x)/']
126 for t in s + list(map(unicode_type, s)):
126 for t in s + list(map(unicode_type, s)):
127 # We don't need to check exact completion values (they may change
127 # We don't need to check exact completion values (they may change
128 # depending on the state of the namespace, but at least no exceptions
128 # depending on the state of the namespace, but at least no exceptions
129 # should be thrown and the return value should be a pair of text, list
129 # should be thrown and the return value should be a pair of text, list
130 # values.
130 # values.
131 text, matches = ip.complete(t)
131 text, matches = ip.complete(t)
132 nt.assert_true(isinstance(text, string_types))
132 nt.assert_true(isinstance(text, string_types))
133 nt.assert_true(isinstance(matches, list))
133 nt.assert_true(isinstance(matches, list))
134
134
135 def test_latex_completions():
135 def test_latex_completions():
136 from IPython.core.latex_symbols import latex_symbols
136 from IPython.core.latex_symbols import latex_symbols
137 import random
137 import random
138 ip = get_ipython()
138 ip = get_ipython()
139 # Test some random unicode symbols
139 # Test some random unicode symbols
140 keys = random.sample(latex_symbols.keys(), 10)
140 keys = random.sample(latex_symbols.keys(), 10)
141 for k in keys:
141 for k in keys:
142 text, matches = ip.complete(k)
142 text, matches = ip.complete(k)
143 nt.assert_equal(len(matches),1)
143 nt.assert_equal(len(matches),1)
144 nt.assert_equal(text, k)
144 nt.assert_equal(text, k)
145 nt.assert_equal(matches[0], latex_symbols[k])
145 nt.assert_equal(matches[0], latex_symbols[k])
146 # Test a more complex line
146 # Test a more complex line
147 text, matches = ip.complete(u'print(\\alpha')
147 text, matches = ip.complete(u'print(\\alpha')
148 nt.assert_equals(text, u'\\alpha')
148 nt.assert_equals(text, u'\\alpha')
149 nt.assert_equals(matches[0], latex_symbols['\\alpha'])
149 nt.assert_equals(matches[0], latex_symbols['\\alpha'])
150 # Test multiple matching latex symbols
150 # Test multiple matching latex symbols
151 text, matches = ip.complete(u'\\al')
151 text, matches = ip.complete(u'\\al')
152 nt.assert_in('\\alpha', matches)
152 nt.assert_in('\\alpha', matches)
153 nt.assert_in('\\aleph', matches)
153 nt.assert_in('\\aleph', matches)
154
154
155
155
156
156
157
157
158 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
159 def test_back_latex_completion():
158 def test_back_latex_completion():
160 ip = get_ipython()
159 ip = get_ipython()
161
160
162 # do not return more than 1 matches fro \beta, only the latex one.
161 # do not return more than 1 matches fro \beta, only the latex one.
163 name, matches = ip.complete('\\β')
162 name, matches = ip.complete('\\β')
164 nt.assert_equal(len(matches), 1)
163 nt.assert_equal(len(matches), 1)
165 nt.assert_equal(matches[0], '\\beta')
164 nt.assert_equal(matches[0], '\\beta')
166
165
167 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
168 def test_back_unicode_completion():
166 def test_back_unicode_completion():
169 ip = get_ipython()
167 ip = get_ipython()
170
168
171 name, matches = ip.complete('\\Ⅴ')
169 name, matches = ip.complete('\\Ⅴ')
172 nt.assert_equal(len(matches), 1)
170 nt.assert_equal(len(matches), 1)
173 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
171 nt.assert_equal(matches[0], '\\ROMAN NUMERAL FIVE')
174
172
175
173
176 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
177 def test_forward_unicode_completion():
174 def test_forward_unicode_completion():
178 ip = get_ipython()
175 ip = get_ipython()
179
176
180 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
177 name, matches = ip.complete('\\ROMAN NUMERAL FIVE')
181 nt.assert_equal(len(matches), 1)
178 nt.assert_equal(len(matches), 1)
182 nt.assert_equal(matches[0], 'Ⅴ')
179 nt.assert_equal(matches[0], 'Ⅴ')
183
180
184 @dec.onlyif(sys.version_info[0] >= 3, 'This test only apply on python3')
185 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
181 @dec.knownfailureif(sys.platform == 'win32', 'Fails if there is a C:\\j... path')
186 def test_no_ascii_back_completion():
182 def test_no_ascii_back_completion():
187 ip = get_ipython()
183 ip = get_ipython()
188 with TemporaryWorkingDirectory(): # Avoid any filename completions
184 with TemporaryWorkingDirectory(): # Avoid any filename completions
189 # single ascii letter that don't have yet completions
185 # single ascii letter that don't have yet completions
190 for letter in 'jJ' :
186 for letter in 'jJ' :
191 name, matches = ip.complete('\\'+letter)
187 name, matches = ip.complete('\\'+letter)
192 nt.assert_equal(matches, [])
188 nt.assert_equal(matches, [])
193
189
194
190
195
191
196
192
197 class CompletionSplitterTestCase(unittest.TestCase):
193 class CompletionSplitterTestCase(unittest.TestCase):
198 def setUp(self):
194 def setUp(self):
199 self.sp = completer.CompletionSplitter()
195 self.sp = completer.CompletionSplitter()
200
196
201 def test_delim_setting(self):
197 def test_delim_setting(self):
202 self.sp.delims = ' '
198 self.sp.delims = ' '
203 nt.assert_equal(self.sp.delims, ' ')
199 nt.assert_equal(self.sp.delims, ' ')
204 nt.assert_equal(self.sp._delim_expr, '[\ ]')
200 nt.assert_equal(self.sp._delim_expr, '[\ ]')
205
201
206 def test_spaces(self):
202 def test_spaces(self):
207 """Test with only spaces as split chars."""
203 """Test with only spaces as split chars."""
208 self.sp.delims = ' '
204 self.sp.delims = ' '
209 t = [('foo', '', 'foo'),
205 t = [('foo', '', 'foo'),
210 ('run foo', '', 'foo'),
206 ('run foo', '', 'foo'),
211 ('run foo', 'bar', 'foo'),
207 ('run foo', 'bar', 'foo'),
212 ]
208 ]
213 check_line_split(self.sp, t)
209 check_line_split(self.sp, t)
214
210
215
211
216 def test_has_open_quotes1():
212 def test_has_open_quotes1():
217 for s in ["'", "'''", "'hi' '"]:
213 for s in ["'", "'''", "'hi' '"]:
218 nt.assert_equal(completer.has_open_quotes(s), "'")
214 nt.assert_equal(completer.has_open_quotes(s), "'")
219
215
220
216
221 def test_has_open_quotes2():
217 def test_has_open_quotes2():
222 for s in ['"', '"""', '"hi" "']:
218 for s in ['"', '"""', '"hi" "']:
223 nt.assert_equal(completer.has_open_quotes(s), '"')
219 nt.assert_equal(completer.has_open_quotes(s), '"')
224
220
225
221
226 def test_has_open_quotes3():
222 def test_has_open_quotes3():
227 for s in ["''", "''' '''", "'hi' 'ipython'"]:
223 for s in ["''", "''' '''", "'hi' 'ipython'"]:
228 nt.assert_false(completer.has_open_quotes(s))
224 nt.assert_false(completer.has_open_quotes(s))
229
225
230
226
231 def test_has_open_quotes4():
227 def test_has_open_quotes4():
232 for s in ['""', '""" """', '"hi" "ipython"']:
228 for s in ['""', '""" """', '"hi" "ipython"']:
233 nt.assert_false(completer.has_open_quotes(s))
229 nt.assert_false(completer.has_open_quotes(s))
234
230
235
231
236 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
232 @knownfailureif(sys.platform == 'win32', "abspath completions fail on Windows")
237 def test_abspath_file_completions():
233 def test_abspath_file_completions():
238 ip = get_ipython()
234 ip = get_ipython()
239 with TemporaryDirectory() as tmpdir:
235 with TemporaryDirectory() as tmpdir:
240 prefix = os.path.join(tmpdir, 'foo')
236 prefix = os.path.join(tmpdir, 'foo')
241 suffixes = ['1', '2']
237 suffixes = ['1', '2']
242 names = [prefix+s for s in suffixes]
238 names = [prefix+s for s in suffixes]
243 for n in names:
239 for n in names:
244 open(n, 'w').close()
240 open(n, 'w').close()
245
241
246 # Check simple completion
242 # Check simple completion
247 c = ip.complete(prefix)[1]
243 c = ip.complete(prefix)[1]
248 nt.assert_equal(c, names)
244 nt.assert_equal(c, names)
249
245
250 # Now check with a function call
246 # Now check with a function call
251 cmd = 'a = f("%s' % prefix
247 cmd = 'a = f("%s' % prefix
252 c = ip.complete(prefix, cmd)[1]
248 c = ip.complete(prefix, cmd)[1]
253 comp = [prefix+s for s in suffixes]
249 comp = [prefix+s for s in suffixes]
254 nt.assert_equal(c, comp)
250 nt.assert_equal(c, comp)
255
251
256
252
257 def test_local_file_completions():
253 def test_local_file_completions():
258 ip = get_ipython()
254 ip = get_ipython()
259 with TemporaryWorkingDirectory():
255 with TemporaryWorkingDirectory():
260 prefix = './foo'
256 prefix = './foo'
261 suffixes = ['1', '2']
257 suffixes = ['1', '2']
262 names = [prefix+s for s in suffixes]
258 names = [prefix+s for s in suffixes]
263 for n in names:
259 for n in names:
264 open(n, 'w').close()
260 open(n, 'w').close()
265
261
266 # Check simple completion
262 # Check simple completion
267 c = ip.complete(prefix)[1]
263 c = ip.complete(prefix)[1]
268 nt.assert_equal(c, names)
264 nt.assert_equal(c, names)
269
265
270 # Now check with a function call
266 # Now check with a function call
271 cmd = 'a = f("%s' % prefix
267 cmd = 'a = f("%s' % prefix
272 c = ip.complete(prefix, cmd)[1]
268 c = ip.complete(prefix, cmd)[1]
273 comp = set(prefix+s for s in suffixes)
269 comp = set(prefix+s for s in suffixes)
274 nt.assert_true(comp.issubset(set(c)))
270 nt.assert_true(comp.issubset(set(c)))
275
271
276
272
277 def test_greedy_completions():
273 def test_greedy_completions():
278 ip = get_ipython()
274 ip = get_ipython()
279 ip.ex('a=list(range(5))')
275 ip.ex('a=list(range(5))')
280 _,c = ip.complete('.',line='a[0].')
276 _,c = ip.complete('.',line='a[0].')
281 nt.assert_false('.real' in c,
277 nt.assert_false('.real' in c,
282 "Shouldn't have completed on a[0]: %s"%c)
278 "Shouldn't have completed on a[0]: %s"%c)
283 with greedy_completion():
279 with greedy_completion():
284 def _(line, cursor_pos, expect, message):
280 def _(line, cursor_pos, expect, message):
285 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
281 _,c = ip.complete('.', line=line, cursor_pos=cursor_pos)
286 nt.assert_in(expect, c, message%c)
282 nt.assert_in(expect, c, message%c)
287
283
288 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s"
284 yield _, 'a[0].', 5, 'a[0].real', "Should have completed on a[0].: %s"
289 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s"
285 yield _, 'a[0].r', 6, 'a[0].real', "Should have completed on a[0].r: %s"
290
286
291 if sys.version_info > (3,4):
287 if sys.version_info > (3,4):
292 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s"
288 yield _, 'a[0].from_', 10, 'a[0].from_bytes', "Should have completed on a[0].from_: %s"
293
289
294
290
295
291
296 def test_omit__names():
292 def test_omit__names():
297 # also happens to test IPCompleter as a configurable
293 # also happens to test IPCompleter as a configurable
298 ip = get_ipython()
294 ip = get_ipython()
299 ip._hidden_attr = 1
295 ip._hidden_attr = 1
300 ip._x = {}
296 ip._x = {}
301 c = ip.Completer
297 c = ip.Completer
302 ip.ex('ip=get_ipython()')
298 ip.ex('ip=get_ipython()')
303 cfg = Config()
299 cfg = Config()
304 cfg.IPCompleter.omit__names = 0
300 cfg.IPCompleter.omit__names = 0
305 c.update_config(cfg)
301 c.update_config(cfg)
306 s,matches = c.complete('ip.')
302 s,matches = c.complete('ip.')
307 nt.assert_in('ip.__str__', matches)
303 nt.assert_in('ip.__str__', matches)
308 nt.assert_in('ip._hidden_attr', matches)
304 nt.assert_in('ip._hidden_attr', matches)
309 cfg = Config()
305 cfg = Config()
310 cfg.IPCompleter.omit__names = 1
306 cfg.IPCompleter.omit__names = 1
311 c.update_config(cfg)
307 c.update_config(cfg)
312 s,matches = c.complete('ip.')
308 s,matches = c.complete('ip.')
313 nt.assert_not_in('ip.__str__', matches)
309 nt.assert_not_in('ip.__str__', matches)
314 nt.assert_in('ip._hidden_attr', matches)
310 nt.assert_in('ip._hidden_attr', matches)
315 cfg = Config()
311 cfg = Config()
316 cfg.IPCompleter.omit__names = 2
312 cfg.IPCompleter.omit__names = 2
317 c.update_config(cfg)
313 c.update_config(cfg)
318 s,matches = c.complete('ip.')
314 s,matches = c.complete('ip.')
319 nt.assert_not_in('ip.__str__', matches)
315 nt.assert_not_in('ip.__str__', matches)
320 nt.assert_not_in('ip._hidden_attr', matches)
316 nt.assert_not_in('ip._hidden_attr', matches)
321 s,matches = c.complete('ip._x.')
317 s,matches = c.complete('ip._x.')
322 nt.assert_in('ip._x.keys', matches)
318 nt.assert_in('ip._x.keys', matches)
323 del ip._hidden_attr
319 del ip._hidden_attr
324
320
325
321
326 def test_limit_to__all__False_ok():
322 def test_limit_to__all__False_ok():
327 ip = get_ipython()
323 ip = get_ipython()
328 c = ip.Completer
324 c = ip.Completer
329 ip.ex('class D: x=24')
325 ip.ex('class D: x=24')
330 ip.ex('d=D()')
326 ip.ex('d=D()')
331 cfg = Config()
327 cfg = Config()
332 cfg.IPCompleter.limit_to__all__ = False
328 cfg.IPCompleter.limit_to__all__ = False
333 c.update_config(cfg)
329 c.update_config(cfg)
334 s, matches = c.complete('d.')
330 s, matches = c.complete('d.')
335 nt.assert_in('d.x', matches)
331 nt.assert_in('d.x', matches)
336
332
337
333
338 def test_get__all__entries_ok():
334 def test_get__all__entries_ok():
339 class A(object):
335 class A(object):
340 __all__ = ['x', 1]
336 __all__ = ['x', 1]
341 words = completer.get__all__entries(A())
337 words = completer.get__all__entries(A())
342 nt.assert_equal(words, ['x'])
338 nt.assert_equal(words, ['x'])
343
339
344
340
345 def test_get__all__entries_no__all__ok():
341 def test_get__all__entries_no__all__ok():
346 class A(object):
342 class A(object):
347 pass
343 pass
348 words = completer.get__all__entries(A())
344 words = completer.get__all__entries(A())
349 nt.assert_equal(words, [])
345 nt.assert_equal(words, [])
350
346
351
347
352 def test_func_kw_completions():
348 def test_func_kw_completions():
353 ip = get_ipython()
349 ip = get_ipython()
354 c = ip.Completer
350 c = ip.Completer
355 ip.ex('def myfunc(a=1,b=2): return a+b')
351 ip.ex('def myfunc(a=1,b=2): return a+b')
356 s, matches = c.complete(None, 'myfunc(1,b')
352 s, matches = c.complete(None, 'myfunc(1,b')
357 nt.assert_in('b=', matches)
353 nt.assert_in('b=', matches)
358 # Simulate completing with cursor right after b (pos==10):
354 # Simulate completing with cursor right after b (pos==10):
359 s, matches = c.complete(None, 'myfunc(1,b)', 10)
355 s, matches = c.complete(None, 'myfunc(1,b)', 10)
360 nt.assert_in('b=', matches)
356 nt.assert_in('b=', matches)
361 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
357 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
362 nt.assert_in('b=', matches)
358 nt.assert_in('b=', matches)
363 #builtin function
359 #builtin function
364 s, matches = c.complete(None, 'min(k, k')
360 s, matches = c.complete(None, 'min(k, k')
365 nt.assert_in('key=', matches)
361 nt.assert_in('key=', matches)
366
362
367
363
368 def test_default_arguments_from_docstring():
364 def test_default_arguments_from_docstring():
369 ip = get_ipython()
365 ip = get_ipython()
370 c = ip.Completer
366 c = ip.Completer
371 kwd = c._default_arguments_from_docstring(
367 kwd = c._default_arguments_from_docstring(
372 'min(iterable[, key=func]) -> value')
368 'min(iterable[, key=func]) -> value')
373 nt.assert_equal(kwd, ['key'])
369 nt.assert_equal(kwd, ['key'])
374 #with cython type etc
370 #with cython type etc
375 kwd = c._default_arguments_from_docstring(
371 kwd = c._default_arguments_from_docstring(
376 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
372 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
377 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
373 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
378 #white spaces
374 #white spaces
379 kwd = c._default_arguments_from_docstring(
375 kwd = c._default_arguments_from_docstring(
380 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
376 '\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n')
381 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
377 nt.assert_equal(kwd, ['ncall', 'resume', 'nsplit'])
382
378
383 def test_line_magics():
379 def test_line_magics():
384 ip = get_ipython()
380 ip = get_ipython()
385 c = ip.Completer
381 c = ip.Completer
386 s, matches = c.complete(None, 'lsmag')
382 s, matches = c.complete(None, 'lsmag')
387 nt.assert_in('%lsmagic', matches)
383 nt.assert_in('%lsmagic', matches)
388 s, matches = c.complete(None, '%lsmag')
384 s, matches = c.complete(None, '%lsmag')
389 nt.assert_in('%lsmagic', matches)
385 nt.assert_in('%lsmagic', matches)
390
386
391
387
392 def test_cell_magics():
388 def test_cell_magics():
393 from IPython.core.magic import register_cell_magic
389 from IPython.core.magic import register_cell_magic
394
390
395 @register_cell_magic
391 @register_cell_magic
396 def _foo_cellm(line, cell):
392 def _foo_cellm(line, cell):
397 pass
393 pass
398
394
399 ip = get_ipython()
395 ip = get_ipython()
400 c = ip.Completer
396 c = ip.Completer
401
397
402 s, matches = c.complete(None, '_foo_ce')
398 s, matches = c.complete(None, '_foo_ce')
403 nt.assert_in('%%_foo_cellm', matches)
399 nt.assert_in('%%_foo_cellm', matches)
404 s, matches = c.complete(None, '%%_foo_ce')
400 s, matches = c.complete(None, '%%_foo_ce')
405 nt.assert_in('%%_foo_cellm', matches)
401 nt.assert_in('%%_foo_cellm', matches)
406
402
407
403
408 def test_line_cell_magics():
404 def test_line_cell_magics():
409 from IPython.core.magic import register_line_cell_magic
405 from IPython.core.magic import register_line_cell_magic
410
406
411 @register_line_cell_magic
407 @register_line_cell_magic
412 def _bar_cellm(line, cell):
408 def _bar_cellm(line, cell):
413 pass
409 pass
414
410
415 ip = get_ipython()
411 ip = get_ipython()
416 c = ip.Completer
412 c = ip.Completer
417
413
418 # The policy here is trickier, see comments in completion code. The
414 # The policy here is trickier, see comments in completion code. The
419 # returned values depend on whether the user passes %% or not explicitly,
415 # returned values depend on whether the user passes %% or not explicitly,
420 # and this will show a difference if the same name is both a line and cell
416 # and this will show a difference if the same name is both a line and cell
421 # magic.
417 # magic.
422 s, matches = c.complete(None, '_bar_ce')
418 s, matches = c.complete(None, '_bar_ce')
423 nt.assert_in('%_bar_cellm', matches)
419 nt.assert_in('%_bar_cellm', matches)
424 nt.assert_in('%%_bar_cellm', matches)
420 nt.assert_in('%%_bar_cellm', matches)
425 s, matches = c.complete(None, '%_bar_ce')
421 s, matches = c.complete(None, '%_bar_ce')
426 nt.assert_in('%_bar_cellm', matches)
422 nt.assert_in('%_bar_cellm', matches)
427 nt.assert_in('%%_bar_cellm', matches)
423 nt.assert_in('%%_bar_cellm', matches)
428 s, matches = c.complete(None, '%%_bar_ce')
424 s, matches = c.complete(None, '%%_bar_ce')
429 nt.assert_not_in('%_bar_cellm', matches)
425 nt.assert_not_in('%_bar_cellm', matches)
430 nt.assert_in('%%_bar_cellm', matches)
426 nt.assert_in('%%_bar_cellm', matches)
431
427
432
428
433 def test_magic_completion_order():
429 def test_magic_completion_order():
434
430
435 ip = get_ipython()
431 ip = get_ipython()
436 c = ip.Completer
432 c = ip.Completer
437
433
438 # Test ordering of magics and non-magics with the same name
434 # Test ordering of magics and non-magics with the same name
439 # We want the non-magic first
435 # We want the non-magic first
440
436
441 # Before importing matplotlib, there should only be one option:
437 # Before importing matplotlib, there should only be one option:
442
438
443 text, matches = c.complete('mat')
439 text, matches = c.complete('mat')
444 nt.assert_equal(matches, ["%matplotlib"])
440 nt.assert_equal(matches, ["%matplotlib"])
445
441
446
442
447 ip.run_cell("matplotlib = 1") # introduce name into namespace
443 ip.run_cell("matplotlib = 1") # introduce name into namespace
448
444
449 # After the import, there should be two options, ordered like this:
445 # After the import, there should be two options, ordered like this:
450 text, matches = c.complete('mat')
446 text, matches = c.complete('mat')
451 nt.assert_equal(matches, ["matplotlib", "%matplotlib"])
447 nt.assert_equal(matches, ["matplotlib", "%matplotlib"])
452
448
453
449
454 ip.run_cell("timeit = 1") # define a user variable called 'timeit'
450 ip.run_cell("timeit = 1") # define a user variable called 'timeit'
455
451
456 # Order of user variable and line and cell magics with same name:
452 # Order of user variable and line and cell magics with same name:
457 text, matches = c.complete('timeit')
453 text, matches = c.complete('timeit')
458 nt.assert_equal(matches, ["timeit", "%timeit","%%timeit"])
454 nt.assert_equal(matches, ["timeit", "%timeit","%%timeit"])
459
455
460
456
461 def test_dict_key_completion_string():
457 def test_dict_key_completion_string():
462 """Test dictionary key completion for string keys"""
458 """Test dictionary key completion for string keys"""
463 ip = get_ipython()
459 ip = get_ipython()
464 complete = ip.Completer.complete
460 complete = ip.Completer.complete
465
461
466 ip.user_ns['d'] = {'abc': None}
462 ip.user_ns['d'] = {'abc': None}
467
463
468 # check completion at different stages
464 # check completion at different stages
469 _, matches = complete(line_buffer="d[")
465 _, matches = complete(line_buffer="d[")
470 nt.assert_in("'abc'", matches)
466 nt.assert_in("'abc'", matches)
471 nt.assert_not_in("'abc']", matches)
467 nt.assert_not_in("'abc']", matches)
472
468
473 _, matches = complete(line_buffer="d['")
469 _, matches = complete(line_buffer="d['")
474 nt.assert_in("abc", matches)
470 nt.assert_in("abc", matches)
475 nt.assert_not_in("abc']", matches)
471 nt.assert_not_in("abc']", matches)
476
472
477 _, matches = complete(line_buffer="d['a")
473 _, matches = complete(line_buffer="d['a")
478 nt.assert_in("abc", matches)
474 nt.assert_in("abc", matches)
479 nt.assert_not_in("abc']", matches)
475 nt.assert_not_in("abc']", matches)
480
476
481 # check use of different quoting
477 # check use of different quoting
482 _, matches = complete(line_buffer="d[\"")
478 _, matches = complete(line_buffer="d[\"")
483 nt.assert_in("abc", matches)
479 nt.assert_in("abc", matches)
484 nt.assert_not_in('abc\"]', matches)
480 nt.assert_not_in('abc\"]', matches)
485
481
486 _, matches = complete(line_buffer="d[\"a")
482 _, matches = complete(line_buffer="d[\"a")
487 nt.assert_in("abc", matches)
483 nt.assert_in("abc", matches)
488 nt.assert_not_in('abc\"]', matches)
484 nt.assert_not_in('abc\"]', matches)
489
485
490 # check sensitivity to following context
486 # check sensitivity to following context
491 _, matches = complete(line_buffer="d[]", cursor_pos=2)
487 _, matches = complete(line_buffer="d[]", cursor_pos=2)
492 nt.assert_in("'abc'", matches)
488 nt.assert_in("'abc'", matches)
493
489
494 _, matches = complete(line_buffer="d['']", cursor_pos=3)
490 _, matches = complete(line_buffer="d['']", cursor_pos=3)
495 nt.assert_in("abc", matches)
491 nt.assert_in("abc", matches)
496 nt.assert_not_in("abc'", matches)
492 nt.assert_not_in("abc'", matches)
497 nt.assert_not_in("abc']", matches)
493 nt.assert_not_in("abc']", matches)
498
494
499 # check multiple solutions are correctly returned and that noise is not
495 # check multiple solutions are correctly returned and that noise is not
500 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
496 ip.user_ns['d'] = {'abc': None, 'abd': None, 'bad': None, object(): None,
501 5: None}
497 5: None}
502
498
503 _, matches = complete(line_buffer="d['a")
499 _, matches = complete(line_buffer="d['a")
504 nt.assert_in("abc", matches)
500 nt.assert_in("abc", matches)
505 nt.assert_in("abd", matches)
501 nt.assert_in("abd", matches)
506 nt.assert_not_in("bad", matches)
502 nt.assert_not_in("bad", matches)
507 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
503 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
508
504
509 # check escaping and whitespace
505 # check escaping and whitespace
510 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
506 ip.user_ns['d'] = {'a\nb': None, 'a\'b': None, 'a"b': None, 'a word': None}
511 _, matches = complete(line_buffer="d['a")
507 _, matches = complete(line_buffer="d['a")
512 nt.assert_in("a\\nb", matches)
508 nt.assert_in("a\\nb", matches)
513 nt.assert_in("a\\'b", matches)
509 nt.assert_in("a\\'b", matches)
514 nt.assert_in("a\"b", matches)
510 nt.assert_in("a\"b", matches)
515 nt.assert_in("a word", matches)
511 nt.assert_in("a word", matches)
516 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
512 assert not any(m.endswith((']', '"', "'")) for m in matches), matches
517
513
518 # - can complete on non-initial word of the string
514 # - can complete on non-initial word of the string
519 _, matches = complete(line_buffer="d['a w")
515 _, matches = complete(line_buffer="d['a w")
520 nt.assert_in("word", matches)
516 nt.assert_in("word", matches)
521
517
522 # - understands quote escaping
518 # - understands quote escaping
523 _, matches = complete(line_buffer="d['a\\'")
519 _, matches = complete(line_buffer="d['a\\'")
524 nt.assert_in("b", matches)
520 nt.assert_in("b", matches)
525
521
526 # - default quoting should work like repr
522 # - default quoting should work like repr
527 _, matches = complete(line_buffer="d[")
523 _, matches = complete(line_buffer="d[")
528 nt.assert_in("\"a'b\"", matches)
524 nt.assert_in("\"a'b\"", matches)
529
525
530 # - when opening quote with ", possible to match with unescaped apostrophe
526 # - when opening quote with ", possible to match with unescaped apostrophe
531 _, matches = complete(line_buffer="d[\"a'")
527 _, matches = complete(line_buffer="d[\"a'")
532 nt.assert_in("b", matches)
528 nt.assert_in("b", matches)
533
529
534 # need to not split at delims that readline won't split at
530 # need to not split at delims that readline won't split at
535 if '-' not in ip.Completer.splitter.delims:
531 if '-' not in ip.Completer.splitter.delims:
536 ip.user_ns['d'] = {'before-after': None}
532 ip.user_ns['d'] = {'before-after': None}
537 _, matches = complete(line_buffer="d['before-af")
533 _, matches = complete(line_buffer="d['before-af")
538 nt.assert_in('before-after', matches)
534 nt.assert_in('before-after', matches)
539
535
540 def test_dict_key_completion_contexts():
536 def test_dict_key_completion_contexts():
541 """Test expression contexts in which dict key completion occurs"""
537 """Test expression contexts in which dict key completion occurs"""
542 ip = get_ipython()
538 ip = get_ipython()
543 complete = ip.Completer.complete
539 complete = ip.Completer.complete
544 d = {'abc': None}
540 d = {'abc': None}
545 ip.user_ns['d'] = d
541 ip.user_ns['d'] = d
546
542
547 class C:
543 class C:
548 data = d
544 data = d
549 ip.user_ns['C'] = C
545 ip.user_ns['C'] = C
550 ip.user_ns['get'] = lambda: d
546 ip.user_ns['get'] = lambda: d
551
547
552 def assert_no_completion(**kwargs):
548 def assert_no_completion(**kwargs):
553 _, matches = complete(**kwargs)
549 _, matches = complete(**kwargs)
554 nt.assert_not_in('abc', matches)
550 nt.assert_not_in('abc', matches)
555 nt.assert_not_in('abc\'', matches)
551 nt.assert_not_in('abc\'', matches)
556 nt.assert_not_in('abc\']', matches)
552 nt.assert_not_in('abc\']', matches)
557 nt.assert_not_in('\'abc\'', matches)
553 nt.assert_not_in('\'abc\'', matches)
558 nt.assert_not_in('\'abc\']', matches)
554 nt.assert_not_in('\'abc\']', matches)
559
555
560 def assert_completion(**kwargs):
556 def assert_completion(**kwargs):
561 _, matches = complete(**kwargs)
557 _, matches = complete(**kwargs)
562 nt.assert_in("'abc'", matches)
558 nt.assert_in("'abc'", matches)
563 nt.assert_not_in("'abc']", matches)
559 nt.assert_not_in("'abc']", matches)
564
560
565 # no completion after string closed, even if reopened
561 # no completion after string closed, even if reopened
566 assert_no_completion(line_buffer="d['a'")
562 assert_no_completion(line_buffer="d['a'")
567 assert_no_completion(line_buffer="d[\"a\"")
563 assert_no_completion(line_buffer="d[\"a\"")
568 assert_no_completion(line_buffer="d['a' + ")
564 assert_no_completion(line_buffer="d['a' + ")
569 assert_no_completion(line_buffer="d['a' + '")
565 assert_no_completion(line_buffer="d['a' + '")
570
566
571 # completion in non-trivial expressions
567 # completion in non-trivial expressions
572 assert_completion(line_buffer="+ d[")
568 assert_completion(line_buffer="+ d[")
573 assert_completion(line_buffer="(d[")
569 assert_completion(line_buffer="(d[")
574 assert_completion(line_buffer="C.data[")
570 assert_completion(line_buffer="C.data[")
575
571
576 # greedy flag
572 # greedy flag
577 def assert_completion(**kwargs):
573 def assert_completion(**kwargs):
578 _, matches = complete(**kwargs)
574 _, matches = complete(**kwargs)
579 nt.assert_in("get()['abc']", matches)
575 nt.assert_in("get()['abc']", matches)
580
576
581 assert_no_completion(line_buffer="get()[")
577 assert_no_completion(line_buffer="get()[")
582 with greedy_completion():
578 with greedy_completion():
583 assert_completion(line_buffer="get()[")
579 assert_completion(line_buffer="get()[")
584 assert_completion(line_buffer="get()['")
580 assert_completion(line_buffer="get()['")
585 assert_completion(line_buffer="get()['a")
581 assert_completion(line_buffer="get()['a")
586 assert_completion(line_buffer="get()['ab")
582 assert_completion(line_buffer="get()['ab")
587 assert_completion(line_buffer="get()['abc")
583 assert_completion(line_buffer="get()['abc")
588
584
589
585
590
586
591 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
592 def test_dict_key_completion_bytes():
587 def test_dict_key_completion_bytes():
593 """Test handling of bytes in dict key completion"""
588 """Test handling of bytes in dict key completion"""
594 ip = get_ipython()
589 ip = get_ipython()
595 complete = ip.Completer.complete
590 complete = ip.Completer.complete
596
591
597 ip.user_ns['d'] = {'abc': None, b'abd': None}
592 ip.user_ns['d'] = {'abc': None, b'abd': None}
598
593
599 _, matches = complete(line_buffer="d[")
594 _, matches = complete(line_buffer="d[")
600 nt.assert_in("'abc'", matches)
595 nt.assert_in("'abc'", matches)
601 nt.assert_in("b'abd'", matches)
596 nt.assert_in("b'abd'", matches)
602
597
603 if False: # not currently implemented
598 if False: # not currently implemented
604 _, matches = complete(line_buffer="d[b")
599 _, matches = complete(line_buffer="d[b")
605 nt.assert_in("b'abd'", matches)
600 nt.assert_in("b'abd'", matches)
606 nt.assert_not_in("b'abc'", matches)
601 nt.assert_not_in("b'abc'", matches)
607
602
608 _, matches = complete(line_buffer="d[b'")
603 _, matches = complete(line_buffer="d[b'")
609 nt.assert_in("abd", matches)
604 nt.assert_in("abd", matches)
610 nt.assert_not_in("abc", matches)
605 nt.assert_not_in("abc", matches)
611
606
612 _, matches = complete(line_buffer="d[B'")
607 _, matches = complete(line_buffer="d[B'")
613 nt.assert_in("abd", matches)
608 nt.assert_in("abd", matches)
614 nt.assert_not_in("abc", matches)
609 nt.assert_not_in("abc", matches)
615
610
616 _, matches = complete(line_buffer="d['")
611 _, matches = complete(line_buffer="d['")
617 nt.assert_in("abc", matches)
612 nt.assert_in("abc", matches)
618 nt.assert_not_in("abd", matches)
613 nt.assert_not_in("abd", matches)
619
614
620
615
621 @dec.onlyif(sys.version_info[0] < 3, 'This test only applies in Py<3')
622 def test_dict_key_completion_unicode_py2():
616 def test_dict_key_completion_unicode_py2():
623 """Test handling of unicode in dict key completion"""
617 """Test handling of unicode in dict key completion"""
624 ip = get_ipython()
618 ip = get_ipython()
625 complete = ip.Completer.complete
619 complete = ip.Completer.complete
626
620
627 ip.user_ns['d'] = {u'abc': None,
621 ip.user_ns['d'] = {u'abc': None,
628 u'a\u05d0b': None}
622 u'a\u05d0b': None}
629
623
630 _, matches = complete(line_buffer="d[")
624 _, matches = complete(line_buffer="d[")
631 nt.assert_in("u'abc'", matches)
625 nt.assert_in("u'abc'", matches)
632 nt.assert_in("u'a\\u05d0b'", matches)
626 nt.assert_in("u'a\\u05d0b'", matches)
633
627
634 _, matches = complete(line_buffer="d['a")
628 _, matches = complete(line_buffer="d['a")
635 nt.assert_in("abc", matches)
629 nt.assert_in("abc", matches)
636 nt.assert_not_in("a\\u05d0b", matches)
630 nt.assert_not_in("a\\u05d0b", matches)
637
631
638 _, matches = complete(line_buffer="d[u'a")
632 _, matches = complete(line_buffer="d[u'a")
639 nt.assert_in("abc", matches)
633 nt.assert_in("abc", matches)
640 nt.assert_in("a\\u05d0b", matches)
634 nt.assert_in("a\\u05d0b", matches)
641
635
642 _, matches = complete(line_buffer="d[U'a")
636 _, matches = complete(line_buffer="d[U'a")
643 nt.assert_in("abc", matches)
637 nt.assert_in("abc", matches)
644 nt.assert_in("a\\u05d0b", matches)
638 nt.assert_in("a\\u05d0b", matches)
645
639
646 # query using escape
640 # query using escape
647 if sys.platform != 'win32':
641 if sys.platform != 'win32':
648 # Known failure on Windows
642 # Known failure on Windows
649 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
643 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
650 nt.assert_in("u05d0b", matches) # tokenized after \\
644 nt.assert_in("u05d0b", matches) # tokenized after \\
651
645
652 # query using character
646 # query using character
653 _, matches = complete(line_buffer=u"d[u'a\u05d0")
647 _, matches = complete(line_buffer=u"d[u'a\u05d0")
654 nt.assert_in(u"a\u05d0b", matches)
648 nt.assert_in(u"a\u05d0b", matches)
655
649
656 with greedy_completion():
650 with greedy_completion():
657 _, matches = complete(line_buffer="d[")
651 _, matches = complete(line_buffer="d[")
658 nt.assert_in("d[u'abc']", matches)
652 nt.assert_in("d[u'abc']", matches)
659 nt.assert_in("d[u'a\\u05d0b']", matches)
653 nt.assert_in("d[u'a\\u05d0b']", matches)
660
654
661 _, matches = complete(line_buffer="d['a")
655 _, matches = complete(line_buffer="d['a")
662 nt.assert_in("d['abc']", matches)
656 nt.assert_in("d['abc']", matches)
663 nt.assert_not_in("d[u'a\\u05d0b']", matches)
657 nt.assert_not_in("d[u'a\\u05d0b']", matches)
664
658
665 _, matches = complete(line_buffer="d[u'a")
659 _, matches = complete(line_buffer="d[u'a")
666 nt.assert_in("d[u'abc']", matches)
660 nt.assert_in("d[u'abc']", matches)
667 nt.assert_in("d[u'a\\u05d0b']", matches)
661 nt.assert_in("d[u'a\\u05d0b']", matches)
668
662
669 _, matches = complete(line_buffer="d[U'a")
663 _, matches = complete(line_buffer="d[U'a")
670 nt.assert_in("d[U'abc']", matches)
664 nt.assert_in("d[U'abc']", matches)
671 nt.assert_in("d[U'a\\u05d0b']", matches)
665 nt.assert_in("d[U'a\\u05d0b']", matches)
672
666
673 # query using escape
667 # query using escape
674 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
668 _, matches = complete(line_buffer=u"d[u'a\\u05d0")
675 nt.assert_in("d[u'a\\u05d0b']", matches) # tokenized after \\
669 nt.assert_in("d[u'a\\u05d0b']", matches) # tokenized after \\
676
670
677 # query using character
671 # query using character
678 _, matches = complete(line_buffer=u"d[u'a\u05d0")
672 _, matches = complete(line_buffer=u"d[u'a\u05d0")
679 nt.assert_in(u"d[u'a\u05d0b']", matches)
673 nt.assert_in(u"d[u'a\u05d0b']", matches)
680
674
681
675
682 @dec.onlyif(sys.version_info[0] >= 3, 'This test only applies in Py>=3')
683 def test_dict_key_completion_unicode_py3():
676 def test_dict_key_completion_unicode_py3():
684 """Test handling of unicode in dict key completion"""
677 """Test handling of unicode in dict key completion"""
685 ip = get_ipython()
678 ip = get_ipython()
686 complete = ip.Completer.complete
679 complete = ip.Completer.complete
687
680
688 ip.user_ns['d'] = {u'a\u05d0': None}
681 ip.user_ns['d'] = {u'a\u05d0': None}
689
682
690 # query using escape
683 # query using escape
691 if sys.platform != 'win32':
684 if sys.platform != 'win32':
692 # Known failure on Windows
685 # Known failure on Windows
693 _, matches = complete(line_buffer="d['a\\u05d0")
686 _, matches = complete(line_buffer="d['a\\u05d0")
694 nt.assert_in("u05d0", matches) # tokenized after \\
687 nt.assert_in("u05d0", matches) # tokenized after \\
695
688
696 # query using character
689 # query using character
697 _, matches = complete(line_buffer="d['a\u05d0")
690 _, matches = complete(line_buffer="d['a\u05d0")
698 nt.assert_in(u"a\u05d0", matches)
691 nt.assert_in(u"a\u05d0", matches)
699
692
700 with greedy_completion():
693 with greedy_completion():
701 # query using escape
694 # query using escape
702 _, matches = complete(line_buffer="d['a\\u05d0")
695 _, matches = complete(line_buffer="d['a\\u05d0")
703 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
696 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
704
697
705 # query using character
698 # query using character
706 _, matches = complete(line_buffer="d['a\u05d0")
699 _, matches = complete(line_buffer="d['a\u05d0")
707 nt.assert_in(u"d['a\u05d0']", matches)
700 nt.assert_in(u"d['a\u05d0']", matches)
708
701
709
702
710
703
711 @dec.skip_without('numpy')
704 @dec.skip_without('numpy')
712 def test_struct_array_key_completion():
705 def test_struct_array_key_completion():
713 """Test dict key completion applies to numpy struct arrays"""
706 """Test dict key completion applies to numpy struct arrays"""
714 import numpy
707 import numpy
715 ip = get_ipython()
708 ip = get_ipython()
716 complete = ip.Completer.complete
709 complete = ip.Completer.complete
717 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
710 ip.user_ns['d'] = numpy.array([], dtype=[('hello', 'f'), ('world', 'f')])
718 _, matches = complete(line_buffer="d['")
711 _, matches = complete(line_buffer="d['")
719 nt.assert_in("hello", matches)
712 nt.assert_in("hello", matches)
720 nt.assert_in("world", matches)
713 nt.assert_in("world", matches)
721 # complete on the numpy struct itself
714 # complete on the numpy struct itself
722 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
715 dt = numpy.dtype([('my_head', [('my_dt', '>u4'), ('my_df', '>u4')]),
723 ('my_data', '>f4', 5)])
716 ('my_data', '>f4', 5)])
724 x = numpy.zeros(2, dtype=dt)
717 x = numpy.zeros(2, dtype=dt)
725 ip.user_ns['d'] = x[1]
718 ip.user_ns['d'] = x[1]
726 _, matches = complete(line_buffer="d['")
719 _, matches = complete(line_buffer="d['")
727 nt.assert_in("my_head", matches)
720 nt.assert_in("my_head", matches)
728 nt.assert_in("my_data", matches)
721 nt.assert_in("my_data", matches)
729 # complete on a nested level
722 # complete on a nested level
730 with greedy_completion():
723 with greedy_completion():
731 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
724 ip.user_ns['d'] = numpy.zeros(2, dtype=dt)
732 _, matches = complete(line_buffer="d[1]['my_head']['")
725 _, matches = complete(line_buffer="d[1]['my_head']['")
733 nt.assert_true(any(["my_dt" in m for m in matches]))
726 nt.assert_true(any(["my_dt" in m for m in matches]))
734 nt.assert_true(any(["my_df" in m for m in matches]))
727 nt.assert_true(any(["my_df" in m for m in matches]))
735
728
736
729
737 @dec.skip_without('pandas')
730 @dec.skip_without('pandas')
738 def test_dataframe_key_completion():
731 def test_dataframe_key_completion():
739 """Test dict key completion applies to pandas DataFrames"""
732 """Test dict key completion applies to pandas DataFrames"""
740 import pandas
733 import pandas
741 ip = get_ipython()
734 ip = get_ipython()
742 complete = ip.Completer.complete
735 complete = ip.Completer.complete
743 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
736 ip.user_ns['d'] = pandas.DataFrame({'hello': [1], 'world': [2]})
744 _, matches = complete(line_buffer="d['")
737 _, matches = complete(line_buffer="d['")
745 nt.assert_in("hello", matches)
738 nt.assert_in("hello", matches)
746 nt.assert_in("world", matches)
739 nt.assert_in("world", matches)
747
740
748
741
749 def test_dict_key_completion_invalids():
742 def test_dict_key_completion_invalids():
750 """Smoke test cases dict key completion can't handle"""
743 """Smoke test cases dict key completion can't handle"""
751 ip = get_ipython()
744 ip = get_ipython()
752 complete = ip.Completer.complete
745 complete = ip.Completer.complete
753
746
754 ip.user_ns['no_getitem'] = None
747 ip.user_ns['no_getitem'] = None
755 ip.user_ns['no_keys'] = []
748 ip.user_ns['no_keys'] = []
756 ip.user_ns['cant_call_keys'] = dict
749 ip.user_ns['cant_call_keys'] = dict
757 ip.user_ns['empty'] = {}
750 ip.user_ns['empty'] = {}
758 ip.user_ns['d'] = {'abc': 5}
751 ip.user_ns['d'] = {'abc': 5}
759
752
760 _, matches = complete(line_buffer="no_getitem['")
753 _, matches = complete(line_buffer="no_getitem['")
761 _, matches = complete(line_buffer="no_keys['")
754 _, matches = complete(line_buffer="no_keys['")
762 _, matches = complete(line_buffer="cant_call_keys['")
755 _, matches = complete(line_buffer="cant_call_keys['")
763 _, matches = complete(line_buffer="empty['")
756 _, matches = complete(line_buffer="empty['")
764 _, matches = complete(line_buffer="name_error['")
757 _, matches = complete(line_buffer="name_error['")
765 _, matches = complete(line_buffer="d['\\") # incomplete escape
758 _, matches = complete(line_buffer="d['\\") # incomplete escape
766
759
767 class KeyCompletable(object):
760 class KeyCompletable(object):
768 def __init__(self, things=()):
761 def __init__(self, things=()):
769 self.things = things
762 self.things = things
770
763
771 def _ipython_key_completions_(self):
764 def _ipython_key_completions_(self):
772 return list(self.things)
765 return list(self.things)
773
766
774 def test_object_key_completion():
767 def test_object_key_completion():
775 ip = get_ipython()
768 ip = get_ipython()
776 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
769 ip.user_ns['key_completable'] = KeyCompletable(['qwerty', 'qwick'])
777
770
778 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
771 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
779 nt.assert_in('qwerty', matches)
772 nt.assert_in('qwerty', matches)
780 nt.assert_in('qwick', matches)
773 nt.assert_in('qwick', matches)
781
774
782
775
783 def test_aimport_module_completer():
776 def test_aimport_module_completer():
784 ip = get_ipython()
777 ip = get_ipython()
785 _, matches = ip.complete('i', '%aimport i')
778 _, matches = ip.complete('i', '%aimport i')
786 nt.assert_in('io', matches)
779 nt.assert_in('io', matches)
787 nt.assert_not_in('int', matches)
780 nt.assert_not_in('int', matches)
788
781
789 def test_nested_import_module_completer():
782 def test_nested_import_module_completer():
790 ip = get_ipython()
783 ip = get_ipython()
791 _, matches = ip.complete(None, 'import IPython.co', 17)
784 _, matches = ip.complete(None, 'import IPython.co', 17)
792 nt.assert_in('IPython.core', matches)
785 nt.assert_in('IPython.core', matches)
793 nt.assert_not_in('import IPython.core', matches)
786 nt.assert_not_in('import IPython.core', matches)
794 nt.assert_not_in('IPython.display', matches)
787 nt.assert_not_in('IPython.display', matches)
795
788
796 def test_import_module_completer():
789 def test_import_module_completer():
797 ip = get_ipython()
790 ip = get_ipython()
798 _, matches = ip.complete('i', 'import i')
791 _, matches = ip.complete('i', 'import i')
799 nt.assert_in('io', matches)
792 nt.assert_in('io', matches)
800 nt.assert_not_in('int', matches)
793 nt.assert_not_in('int', matches)
801
794
802 def test_from_module_completer():
795 def test_from_module_completer():
803 ip = get_ipython()
796 ip = get_ipython()
804 _, matches = ip.complete('B', 'from io import B', 16)
797 _, matches = ip.complete('B', 'from io import B', 16)
805 nt.assert_in('BytesIO', matches)
798 nt.assert_in('BytesIO', matches)
806 nt.assert_not_in('BaseException', matches)
799 nt.assert_not_in('BaseException', matches)
@@ -1,950 +1,918 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 import ast
12 import ast
13 import os
13 import os
14 import signal
14 import signal
15 import shutil
15 import shutil
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import unittest
18 import unittest
19 try:
19 try:
20 from unittest import mock
20 from unittest import mock
21 except ImportError:
21 except ImportError:
22 import mock
22 import mock
23 from os.path import join
23 from os.path import join
24
24
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 from IPython.core.error import InputRejected
27 from IPython.core.error import InputRejected
28 from IPython.core.inputtransformer import InputTransformer
28 from IPython.core.inputtransformer import InputTransformer
29 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 )
31 )
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils.process import find_cmd
33 from IPython.utils.process import find_cmd
34 from IPython.utils import py3compat
34 from IPython.utils import py3compat
35 from IPython.utils.py3compat import unicode_type, PY3
35 from IPython.utils.py3compat import unicode_type, PY3
36
36
37 if PY3:
37 if PY3:
38 from io import StringIO
38 from io import StringIO
39 else:
39 else:
40 from StringIO import StringIO
40 from StringIO import StringIO
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals
43 # Globals
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # This is used by every single test, no point repeating it ad nauseam
45 # This is used by every single test, no point repeating it ad nauseam
46 ip = get_ipython()
46 ip = get_ipython()
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Tests
49 # Tests
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 class DerivedInterrupt(KeyboardInterrupt):
52 class DerivedInterrupt(KeyboardInterrupt):
53 pass
53 pass
54
54
55 class InteractiveShellTestCase(unittest.TestCase):
55 class InteractiveShellTestCase(unittest.TestCase):
56 def test_naked_string_cells(self):
56 def test_naked_string_cells(self):
57 """Test that cells with only naked strings are fully executed"""
57 """Test that cells with only naked strings are fully executed"""
58 # First, single-line inputs
58 # First, single-line inputs
59 ip.run_cell('"a"\n')
59 ip.run_cell('"a"\n')
60 self.assertEqual(ip.user_ns['_'], 'a')
60 self.assertEqual(ip.user_ns['_'], 'a')
61 # And also multi-line cells
61 # And also multi-line cells
62 ip.run_cell('"""a\nb"""\n')
62 ip.run_cell('"""a\nb"""\n')
63 self.assertEqual(ip.user_ns['_'], 'a\nb')
63 self.assertEqual(ip.user_ns['_'], 'a\nb')
64
64
65 def test_run_empty_cell(self):
65 def test_run_empty_cell(self):
66 """Just make sure we don't get a horrible error with a blank
66 """Just make sure we don't get a horrible error with a blank
67 cell of input. Yes, I did overlook that."""
67 cell of input. Yes, I did overlook that."""
68 old_xc = ip.execution_count
68 old_xc = ip.execution_count
69 res = ip.run_cell('')
69 res = ip.run_cell('')
70 self.assertEqual(ip.execution_count, old_xc)
70 self.assertEqual(ip.execution_count, old_xc)
71 self.assertEqual(res.execution_count, None)
71 self.assertEqual(res.execution_count, None)
72
72
73 def test_run_cell_multiline(self):
73 def test_run_cell_multiline(self):
74 """Multi-block, multi-line cells must execute correctly.
74 """Multi-block, multi-line cells must execute correctly.
75 """
75 """
76 src = '\n'.join(["x=1",
76 src = '\n'.join(["x=1",
77 "y=2",
77 "y=2",
78 "if 1:",
78 "if 1:",
79 " x += 1",
79 " x += 1",
80 " y += 1",])
80 " y += 1",])
81 res = ip.run_cell(src)
81 res = ip.run_cell(src)
82 self.assertEqual(ip.user_ns['x'], 2)
82 self.assertEqual(ip.user_ns['x'], 2)
83 self.assertEqual(ip.user_ns['y'], 3)
83 self.assertEqual(ip.user_ns['y'], 3)
84 self.assertEqual(res.success, True)
84 self.assertEqual(res.success, True)
85 self.assertEqual(res.result, None)
85 self.assertEqual(res.result, None)
86
86
87 def test_multiline_string_cells(self):
87 def test_multiline_string_cells(self):
88 "Code sprinkled with multiline strings should execute (GH-306)"
88 "Code sprinkled with multiline strings should execute (GH-306)"
89 ip.run_cell('tmp=0')
89 ip.run_cell('tmp=0')
90 self.assertEqual(ip.user_ns['tmp'], 0)
90 self.assertEqual(ip.user_ns['tmp'], 0)
91 res = ip.run_cell('tmp=1;"""a\nb"""\n')
91 res = ip.run_cell('tmp=1;"""a\nb"""\n')
92 self.assertEqual(ip.user_ns['tmp'], 1)
92 self.assertEqual(ip.user_ns['tmp'], 1)
93 self.assertEqual(res.success, True)
93 self.assertEqual(res.success, True)
94 self.assertEqual(res.result, "a\nb")
94 self.assertEqual(res.result, "a\nb")
95
95
96 def test_dont_cache_with_semicolon(self):
96 def test_dont_cache_with_semicolon(self):
97 "Ending a line with semicolon should not cache the returned object (GH-307)"
97 "Ending a line with semicolon should not cache the returned object (GH-307)"
98 oldlen = len(ip.user_ns['Out'])
98 oldlen = len(ip.user_ns['Out'])
99 for cell in ['1;', '1;1;']:
99 for cell in ['1;', '1;1;']:
100 res = ip.run_cell(cell, store_history=True)
100 res = ip.run_cell(cell, store_history=True)
101 newlen = len(ip.user_ns['Out'])
101 newlen = len(ip.user_ns['Out'])
102 self.assertEqual(oldlen, newlen)
102 self.assertEqual(oldlen, newlen)
103 self.assertIsNone(res.result)
103 self.assertIsNone(res.result)
104 i = 0
104 i = 0
105 #also test the default caching behavior
105 #also test the default caching behavior
106 for cell in ['1', '1;1']:
106 for cell in ['1', '1;1']:
107 ip.run_cell(cell, store_history=True)
107 ip.run_cell(cell, store_history=True)
108 newlen = len(ip.user_ns['Out'])
108 newlen = len(ip.user_ns['Out'])
109 i += 1
109 i += 1
110 self.assertEqual(oldlen+i, newlen)
110 self.assertEqual(oldlen+i, newlen)
111
111
112 def test_syntax_error(self):
112 def test_syntax_error(self):
113 res = ip.run_cell("raise = 3")
113 res = ip.run_cell("raise = 3")
114 self.assertIsInstance(res.error_before_exec, SyntaxError)
114 self.assertIsInstance(res.error_before_exec, SyntaxError)
115
115
116 def test_In_variable(self):
116 def test_In_variable(self):
117 "Verify that In variable grows with user input (GH-284)"
117 "Verify that In variable grows with user input (GH-284)"
118 oldlen = len(ip.user_ns['In'])
118 oldlen = len(ip.user_ns['In'])
119 ip.run_cell('1;', store_history=True)
119 ip.run_cell('1;', store_history=True)
120 newlen = len(ip.user_ns['In'])
120 newlen = len(ip.user_ns['In'])
121 self.assertEqual(oldlen+1, newlen)
121 self.assertEqual(oldlen+1, newlen)
122 self.assertEqual(ip.user_ns['In'][-1],'1;')
122 self.assertEqual(ip.user_ns['In'][-1],'1;')
123
123
124 def test_magic_names_in_string(self):
124 def test_magic_names_in_string(self):
125 ip.run_cell('a = """\n%exit\n"""')
125 ip.run_cell('a = """\n%exit\n"""')
126 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
126 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
127
127
128 def test_trailing_newline(self):
128 def test_trailing_newline(self):
129 """test that running !(command) does not raise a SyntaxError"""
129 """test that running !(command) does not raise a SyntaxError"""
130 ip.run_cell('!(true)\n', False)
130 ip.run_cell('!(true)\n', False)
131 ip.run_cell('!(true)\n\n\n', False)
131 ip.run_cell('!(true)\n\n\n', False)
132
132
133 def test_gh_597(self):
133 def test_gh_597(self):
134 """Pretty-printing lists of objects with non-ascii reprs may cause
134 """Pretty-printing lists of objects with non-ascii reprs may cause
135 problems."""
135 problems."""
136 class Spam(object):
136 class Spam(object):
137 def __repr__(self):
137 def __repr__(self):
138 return "\xe9"*50
138 return "\xe9"*50
139 import IPython.core.formatters
139 import IPython.core.formatters
140 f = IPython.core.formatters.PlainTextFormatter()
140 f = IPython.core.formatters.PlainTextFormatter()
141 f([Spam(),Spam()])
141 f([Spam(),Spam()])
142
142
143
143
144 def test_future_flags(self):
144 def test_future_flags(self):
145 """Check that future flags are used for parsing code (gh-777)"""
145 """Check that future flags are used for parsing code (gh-777)"""
146 ip.run_cell('from __future__ import print_function')
146 ip.run_cell('from __future__ import print_function')
147 try:
147 try:
148 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
148 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
149 assert 'prfunc_return_val' in ip.user_ns
149 assert 'prfunc_return_val' in ip.user_ns
150 finally:
150 finally:
151 # Reset compiler flags so we don't mess up other tests.
151 # Reset compiler flags so we don't mess up other tests.
152 ip.compile.reset_compiler_flags()
152 ip.compile.reset_compiler_flags()
153
153
154 def test_future_unicode(self):
154 def test_future_unicode(self):
155 """Check that unicode_literals is imported from __future__ (gh #786)"""
155 """Check that unicode_literals is imported from __future__ (gh #786)"""
156 try:
156 try:
157 ip.run_cell(u'byte_str = "a"')
157 ip.run_cell(u'byte_str = "a"')
158 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
158 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
159 ip.run_cell('from __future__ import unicode_literals')
159 ip.run_cell('from __future__ import unicode_literals')
160 ip.run_cell(u'unicode_str = "a"')
160 ip.run_cell(u'unicode_str = "a"')
161 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
161 assert isinstance(ip.user_ns['unicode_str'], unicode_type) # strings literals are now unicode
162 finally:
162 finally:
163 # Reset compiler flags so we don't mess up other tests.
163 # Reset compiler flags so we don't mess up other tests.
164 ip.compile.reset_compiler_flags()
164 ip.compile.reset_compiler_flags()
165
165
166 def test_can_pickle(self):
166 def test_can_pickle(self):
167 "Can we pickle objects defined interactively (GH-29)"
167 "Can we pickle objects defined interactively (GH-29)"
168 ip = get_ipython()
168 ip = get_ipython()
169 ip.reset()
169 ip.reset()
170 ip.run_cell(("class Mylist(list):\n"
170 ip.run_cell(("class Mylist(list):\n"
171 " def __init__(self,x=[]):\n"
171 " def __init__(self,x=[]):\n"
172 " list.__init__(self,x)"))
172 " list.__init__(self,x)"))
173 ip.run_cell("w=Mylist([1,2,3])")
173 ip.run_cell("w=Mylist([1,2,3])")
174
174
175 from pickle import dumps
175 from pickle import dumps
176
176
177 # We need to swap in our main module - this is only necessary
177 # We need to swap in our main module - this is only necessary
178 # inside the test framework, because IPython puts the interactive module
178 # inside the test framework, because IPython puts the interactive module
179 # in place (but the test framework undoes this).
179 # in place (but the test framework undoes this).
180 _main = sys.modules['__main__']
180 _main = sys.modules['__main__']
181 sys.modules['__main__'] = ip.user_module
181 sys.modules['__main__'] = ip.user_module
182 try:
182 try:
183 res = dumps(ip.user_ns["w"])
183 res = dumps(ip.user_ns["w"])
184 finally:
184 finally:
185 sys.modules['__main__'] = _main
185 sys.modules['__main__'] = _main
186 self.assertTrue(isinstance(res, bytes))
186 self.assertTrue(isinstance(res, bytes))
187
187
188 def test_global_ns(self):
188 def test_global_ns(self):
189 "Code in functions must be able to access variables outside them."
189 "Code in functions must be able to access variables outside them."
190 ip = get_ipython()
190 ip = get_ipython()
191 ip.run_cell("a = 10")
191 ip.run_cell("a = 10")
192 ip.run_cell(("def f(x):\n"
192 ip.run_cell(("def f(x):\n"
193 " return x + a"))
193 " return x + a"))
194 ip.run_cell("b = f(12)")
194 ip.run_cell("b = f(12)")
195 self.assertEqual(ip.user_ns["b"], 22)
195 self.assertEqual(ip.user_ns["b"], 22)
196
196
197 def test_bad_custom_tb(self):
197 def test_bad_custom_tb(self):
198 """Check that InteractiveShell is protected from bad custom exception handlers"""
198 """Check that InteractiveShell is protected from bad custom exception handlers"""
199 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
199 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
200 self.assertEqual(ip.custom_exceptions, (IOError,))
200 self.assertEqual(ip.custom_exceptions, (IOError,))
201 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
201 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
202 ip.run_cell(u'raise IOError("foo")')
202 ip.run_cell(u'raise IOError("foo")')
203 self.assertEqual(ip.custom_exceptions, ())
203 self.assertEqual(ip.custom_exceptions, ())
204
204
205 def test_bad_custom_tb_return(self):
205 def test_bad_custom_tb_return(self):
206 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
206 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
207 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
207 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
208 self.assertEqual(ip.custom_exceptions, (NameError,))
208 self.assertEqual(ip.custom_exceptions, (NameError,))
209 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
209 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
210 ip.run_cell(u'a=abracadabra')
210 ip.run_cell(u'a=abracadabra')
211 self.assertEqual(ip.custom_exceptions, ())
211 self.assertEqual(ip.custom_exceptions, ())
212
212
213 def test_drop_by_id(self):
213 def test_drop_by_id(self):
214 myvars = {"a":object(), "b":object(), "c": object()}
214 myvars = {"a":object(), "b":object(), "c": object()}
215 ip.push(myvars, interactive=False)
215 ip.push(myvars, interactive=False)
216 for name in myvars:
216 for name in myvars:
217 assert name in ip.user_ns, name
217 assert name in ip.user_ns, name
218 assert name in ip.user_ns_hidden, name
218 assert name in ip.user_ns_hidden, name
219 ip.user_ns['b'] = 12
219 ip.user_ns['b'] = 12
220 ip.drop_by_id(myvars)
220 ip.drop_by_id(myvars)
221 for name in ["a", "c"]:
221 for name in ["a", "c"]:
222 assert name not in ip.user_ns, name
222 assert name not in ip.user_ns, name
223 assert name not in ip.user_ns_hidden, name
223 assert name not in ip.user_ns_hidden, name
224 assert ip.user_ns['b'] == 12
224 assert ip.user_ns['b'] == 12
225 ip.reset()
225 ip.reset()
226
226
227 def test_var_expand(self):
227 def test_var_expand(self):
228 ip.user_ns['f'] = u'Ca\xf1o'
228 ip.user_ns['f'] = u'Ca\xf1o'
229 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
229 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
230 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
230 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
231 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
231 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
232 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
232 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
233
233
234 ip.user_ns['f'] = b'Ca\xc3\xb1o'
234 ip.user_ns['f'] = b'Ca\xc3\xb1o'
235 # This should not raise any exception:
235 # This should not raise any exception:
236 ip.var_expand(u'echo $f')
236 ip.var_expand(u'echo $f')
237
237
238 def test_var_expand_local(self):
238 def test_var_expand_local(self):
239 """Test local variable expansion in !system and %magic calls"""
239 """Test local variable expansion in !system and %magic calls"""
240 # !system
240 # !system
241 ip.run_cell('def test():\n'
241 ip.run_cell('def test():\n'
242 ' lvar = "ttt"\n'
242 ' lvar = "ttt"\n'
243 ' ret = !echo {lvar}\n'
243 ' ret = !echo {lvar}\n'
244 ' return ret[0]\n')
244 ' return ret[0]\n')
245 res = ip.user_ns['test']()
245 res = ip.user_ns['test']()
246 nt.assert_in('ttt', res)
246 nt.assert_in('ttt', res)
247
247
248 # %magic
248 # %magic
249 ip.run_cell('def makemacro():\n'
249 ip.run_cell('def makemacro():\n'
250 ' macroname = "macro_var_expand_locals"\n'
250 ' macroname = "macro_var_expand_locals"\n'
251 ' %macro {macroname} codestr\n')
251 ' %macro {macroname} codestr\n')
252 ip.user_ns['codestr'] = "str(12)"
252 ip.user_ns['codestr'] = "str(12)"
253 ip.run_cell('makemacro()')
253 ip.run_cell('makemacro()')
254 nt.assert_in('macro_var_expand_locals', ip.user_ns)
254 nt.assert_in('macro_var_expand_locals', ip.user_ns)
255
255
256 def test_var_expand_self(self):
256 def test_var_expand_self(self):
257 """Test variable expansion with the name 'self', which was failing.
257 """Test variable expansion with the name 'self', which was failing.
258
258
259 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
259 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
260 """
260 """
261 ip.run_cell('class cTest:\n'
261 ip.run_cell('class cTest:\n'
262 ' classvar="see me"\n'
262 ' classvar="see me"\n'
263 ' def test(self):\n'
263 ' def test(self):\n'
264 ' res = !echo Variable: {self.classvar}\n'
264 ' res = !echo Variable: {self.classvar}\n'
265 ' return res[0]\n')
265 ' return res[0]\n')
266 nt.assert_in('see me', ip.user_ns['cTest']().test())
266 nt.assert_in('see me', ip.user_ns['cTest']().test())
267
267
268 def test_bad_var_expand(self):
268 def test_bad_var_expand(self):
269 """var_expand on invalid formats shouldn't raise"""
269 """var_expand on invalid formats shouldn't raise"""
270 # SyntaxError
270 # SyntaxError
271 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
271 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
272 # NameError
272 # NameError
273 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
273 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
274 # ZeroDivisionError
274 # ZeroDivisionError
275 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
275 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
276
276
277 def test_silent_postexec(self):
277 def test_silent_postexec(self):
278 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
278 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
279 pre_explicit = mock.Mock()
279 pre_explicit = mock.Mock()
280 pre_always = mock.Mock()
280 pre_always = mock.Mock()
281 post_explicit = mock.Mock()
281 post_explicit = mock.Mock()
282 post_always = mock.Mock()
282 post_always = mock.Mock()
283
283
284 ip.events.register('pre_run_cell', pre_explicit)
284 ip.events.register('pre_run_cell', pre_explicit)
285 ip.events.register('pre_execute', pre_always)
285 ip.events.register('pre_execute', pre_always)
286 ip.events.register('post_run_cell', post_explicit)
286 ip.events.register('post_run_cell', post_explicit)
287 ip.events.register('post_execute', post_always)
287 ip.events.register('post_execute', post_always)
288
288
289 try:
289 try:
290 ip.run_cell("1", silent=True)
290 ip.run_cell("1", silent=True)
291 assert pre_always.called
291 assert pre_always.called
292 assert not pre_explicit.called
292 assert not pre_explicit.called
293 assert post_always.called
293 assert post_always.called
294 assert not post_explicit.called
294 assert not post_explicit.called
295 # double-check that non-silent exec did what we expected
295 # double-check that non-silent exec did what we expected
296 # silent to avoid
296 # silent to avoid
297 ip.run_cell("1")
297 ip.run_cell("1")
298 assert pre_explicit.called
298 assert pre_explicit.called
299 assert post_explicit.called
299 assert post_explicit.called
300 finally:
300 finally:
301 # remove post-exec
301 # remove post-exec
302 ip.events.unregister('pre_run_cell', pre_explicit)
302 ip.events.unregister('pre_run_cell', pre_explicit)
303 ip.events.unregister('pre_execute', pre_always)
303 ip.events.unregister('pre_execute', pre_always)
304 ip.events.unregister('post_run_cell', post_explicit)
304 ip.events.unregister('post_run_cell', post_explicit)
305 ip.events.unregister('post_execute', post_always)
305 ip.events.unregister('post_execute', post_always)
306
306
307 def test_silent_noadvance(self):
307 def test_silent_noadvance(self):
308 """run_cell(silent=True) doesn't advance execution_count"""
308 """run_cell(silent=True) doesn't advance execution_count"""
309 ec = ip.execution_count
309 ec = ip.execution_count
310 # silent should force store_history=False
310 # silent should force store_history=False
311 ip.run_cell("1", store_history=True, silent=True)
311 ip.run_cell("1", store_history=True, silent=True)
312
312
313 self.assertEqual(ec, ip.execution_count)
313 self.assertEqual(ec, ip.execution_count)
314 # double-check that non-silent exec did what we expected
314 # double-check that non-silent exec did what we expected
315 # silent to avoid
315 # silent to avoid
316 ip.run_cell("1", store_history=True)
316 ip.run_cell("1", store_history=True)
317 self.assertEqual(ec+1, ip.execution_count)
317 self.assertEqual(ec+1, ip.execution_count)
318
318
319 def test_silent_nodisplayhook(self):
319 def test_silent_nodisplayhook(self):
320 """run_cell(silent=True) doesn't trigger displayhook"""
320 """run_cell(silent=True) doesn't trigger displayhook"""
321 d = dict(called=False)
321 d = dict(called=False)
322
322
323 trap = ip.display_trap
323 trap = ip.display_trap
324 save_hook = trap.hook
324 save_hook = trap.hook
325
325
326 def failing_hook(*args, **kwargs):
326 def failing_hook(*args, **kwargs):
327 d['called'] = True
327 d['called'] = True
328
328
329 try:
329 try:
330 trap.hook = failing_hook
330 trap.hook = failing_hook
331 res = ip.run_cell("1", silent=True)
331 res = ip.run_cell("1", silent=True)
332 self.assertFalse(d['called'])
332 self.assertFalse(d['called'])
333 self.assertIsNone(res.result)
333 self.assertIsNone(res.result)
334 # double-check that non-silent exec did what we expected
334 # double-check that non-silent exec did what we expected
335 # silent to avoid
335 # silent to avoid
336 ip.run_cell("1")
336 ip.run_cell("1")
337 self.assertTrue(d['called'])
337 self.assertTrue(d['called'])
338 finally:
338 finally:
339 trap.hook = save_hook
339 trap.hook = save_hook
340
340
341 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
342 def test_print_softspace(self):
343 """Verify that softspace is handled correctly when executing multiple
344 statements.
345
346 In [1]: print 1; print 2
347 1
348 2
349
350 In [2]: print 1,; print 2
351 1 2
352 """
353
354 def test_ofind_line_magic(self):
341 def test_ofind_line_magic(self):
355 from IPython.core.magic import register_line_magic
342 from IPython.core.magic import register_line_magic
356
343
357 @register_line_magic
344 @register_line_magic
358 def lmagic(line):
345 def lmagic(line):
359 "A line magic"
346 "A line magic"
360
347
361 # Get info on line magic
348 # Get info on line magic
362 lfind = ip._ofind('lmagic')
349 lfind = ip._ofind('lmagic')
363 info = dict(found=True, isalias=False, ismagic=True,
350 info = dict(found=True, isalias=False, ismagic=True,
364 namespace = 'IPython internal', obj= lmagic.__wrapped__,
351 namespace = 'IPython internal', obj= lmagic.__wrapped__,
365 parent = None)
352 parent = None)
366 nt.assert_equal(lfind, info)
353 nt.assert_equal(lfind, info)
367
354
368 def test_ofind_cell_magic(self):
355 def test_ofind_cell_magic(self):
369 from IPython.core.magic import register_cell_magic
356 from IPython.core.magic import register_cell_magic
370
357
371 @register_cell_magic
358 @register_cell_magic
372 def cmagic(line, cell):
359 def cmagic(line, cell):
373 "A cell magic"
360 "A cell magic"
374
361
375 # Get info on cell magic
362 # Get info on cell magic
376 find = ip._ofind('cmagic')
363 find = ip._ofind('cmagic')
377 info = dict(found=True, isalias=False, ismagic=True,
364 info = dict(found=True, isalias=False, ismagic=True,
378 namespace = 'IPython internal', obj= cmagic.__wrapped__,
365 namespace = 'IPython internal', obj= cmagic.__wrapped__,
379 parent = None)
366 parent = None)
380 nt.assert_equal(find, info)
367 nt.assert_equal(find, info)
381
368
382 def test_ofind_property_with_error(self):
369 def test_ofind_property_with_error(self):
383 class A(object):
370 class A(object):
384 @property
371 @property
385 def foo(self):
372 def foo(self):
386 raise NotImplementedError()
373 raise NotImplementedError()
387 a = A()
374 a = A()
388
375
389 found = ip._ofind('a.foo', [('locals', locals())])
376 found = ip._ofind('a.foo', [('locals', locals())])
390 info = dict(found=True, isalias=False, ismagic=False,
377 info = dict(found=True, isalias=False, ismagic=False,
391 namespace='locals', obj=A.foo, parent=a)
378 namespace='locals', obj=A.foo, parent=a)
392 nt.assert_equal(found, info)
379 nt.assert_equal(found, info)
393
380
394 def test_ofind_multiple_attribute_lookups(self):
381 def test_ofind_multiple_attribute_lookups(self):
395 class A(object):
382 class A(object):
396 @property
383 @property
397 def foo(self):
384 def foo(self):
398 raise NotImplementedError()
385 raise NotImplementedError()
399
386
400 a = A()
387 a = A()
401 a.a = A()
388 a.a = A()
402 a.a.a = A()
389 a.a.a = A()
403
390
404 found = ip._ofind('a.a.a.foo', [('locals', locals())])
391 found = ip._ofind('a.a.a.foo', [('locals', locals())])
405 info = dict(found=True, isalias=False, ismagic=False,
392 info = dict(found=True, isalias=False, ismagic=False,
406 namespace='locals', obj=A.foo, parent=a.a.a)
393 namespace='locals', obj=A.foo, parent=a.a.a)
407 nt.assert_equal(found, info)
394 nt.assert_equal(found, info)
408
395
409 def test_ofind_slotted_attributes(self):
396 def test_ofind_slotted_attributes(self):
410 class A(object):
397 class A(object):
411 __slots__ = ['foo']
398 __slots__ = ['foo']
412 def __init__(self):
399 def __init__(self):
413 self.foo = 'bar'
400 self.foo = 'bar'
414
401
415 a = A()
402 a = A()
416 found = ip._ofind('a.foo', [('locals', locals())])
403 found = ip._ofind('a.foo', [('locals', locals())])
417 info = dict(found=True, isalias=False, ismagic=False,
404 info = dict(found=True, isalias=False, ismagic=False,
418 namespace='locals', obj=a.foo, parent=a)
405 namespace='locals', obj=a.foo, parent=a)
419 nt.assert_equal(found, info)
406 nt.assert_equal(found, info)
420
407
421 found = ip._ofind('a.bar', [('locals', locals())])
408 found = ip._ofind('a.bar', [('locals', locals())])
422 info = dict(found=False, isalias=False, ismagic=False,
409 info = dict(found=False, isalias=False, ismagic=False,
423 namespace=None, obj=None, parent=a)
410 namespace=None, obj=None, parent=a)
424 nt.assert_equal(found, info)
411 nt.assert_equal(found, info)
425
412
426 def test_ofind_prefers_property_to_instance_level_attribute(self):
413 def test_ofind_prefers_property_to_instance_level_attribute(self):
427 class A(object):
414 class A(object):
428 @property
415 @property
429 def foo(self):
416 def foo(self):
430 return 'bar'
417 return 'bar'
431 a = A()
418 a = A()
432 a.__dict__['foo'] = 'baz'
419 a.__dict__['foo'] = 'baz'
433 nt.assert_equal(a.foo, 'bar')
420 nt.assert_equal(a.foo, 'bar')
434 found = ip._ofind('a.foo', [('locals', locals())])
421 found = ip._ofind('a.foo', [('locals', locals())])
435 nt.assert_is(found['obj'], A.foo)
422 nt.assert_is(found['obj'], A.foo)
436
423
437 def test_custom_syntaxerror_exception(self):
424 def test_custom_syntaxerror_exception(self):
438 called = []
425 called = []
439 def my_handler(shell, etype, value, tb, tb_offset=None):
426 def my_handler(shell, etype, value, tb, tb_offset=None):
440 called.append(etype)
427 called.append(etype)
441 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
428 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
442
429
443 ip.set_custom_exc((SyntaxError,), my_handler)
430 ip.set_custom_exc((SyntaxError,), my_handler)
444 try:
431 try:
445 ip.run_cell("1f")
432 ip.run_cell("1f")
446 # Check that this was called, and only once.
433 # Check that this was called, and only once.
447 self.assertEqual(called, [SyntaxError])
434 self.assertEqual(called, [SyntaxError])
448 finally:
435 finally:
449 # Reset the custom exception hook
436 # Reset the custom exception hook
450 ip.set_custom_exc((), None)
437 ip.set_custom_exc((), None)
451
438
452 def test_custom_exception(self):
439 def test_custom_exception(self):
453 called = []
440 called = []
454 def my_handler(shell, etype, value, tb, tb_offset=None):
441 def my_handler(shell, etype, value, tb, tb_offset=None):
455 called.append(etype)
442 called.append(etype)
456 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
443 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
457
444
458 ip.set_custom_exc((ValueError,), my_handler)
445 ip.set_custom_exc((ValueError,), my_handler)
459 try:
446 try:
460 res = ip.run_cell("raise ValueError('test')")
447 res = ip.run_cell("raise ValueError('test')")
461 # Check that this was called, and only once.
448 # Check that this was called, and only once.
462 self.assertEqual(called, [ValueError])
449 self.assertEqual(called, [ValueError])
463 # Check that the error is on the result object
450 # Check that the error is on the result object
464 self.assertIsInstance(res.error_in_exec, ValueError)
451 self.assertIsInstance(res.error_in_exec, ValueError)
465 finally:
452 finally:
466 # Reset the custom exception hook
453 # Reset the custom exception hook
467 ip.set_custom_exc((), None)
454 ip.set_custom_exc((), None)
468
455
469 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
470 def test_future_environment(self):
471 "Can we run code with & without the shell's __future__ imports?"
472 ip.run_cell("from __future__ import division")
473 ip.run_cell("a = 1/2", shell_futures=True)
474 self.assertEqual(ip.user_ns['a'], 0.5)
475 ip.run_cell("b = 1/2", shell_futures=False)
476 self.assertEqual(ip.user_ns['b'], 0)
477
478 ip.compile.reset_compiler_flags()
479 # This shouldn't leak to the shell's compiler
480 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
481 self.assertEqual(ip.user_ns['c'], 0.5)
482 ip.run_cell("d = 1/2", shell_futures=True)
483 self.assertEqual(ip.user_ns['d'], 0)
484
485 def test_mktempfile(self):
456 def test_mktempfile(self):
486 filename = ip.mktempfile()
457 filename = ip.mktempfile()
487 # Check that we can open the file again on Windows
458 # Check that we can open the file again on Windows
488 with open(filename, 'w') as f:
459 with open(filename, 'w') as f:
489 f.write('abc')
460 f.write('abc')
490
461
491 filename = ip.mktempfile(data='blah')
462 filename = ip.mktempfile(data='blah')
492 with open(filename, 'r') as f:
463 with open(filename, 'r') as f:
493 self.assertEqual(f.read(), 'blah')
464 self.assertEqual(f.read(), 'blah')
494
465
495 def test_new_main_mod(self):
466 def test_new_main_mod(self):
496 # Smoketest to check that this accepts a unicode module name
467 # Smoketest to check that this accepts a unicode module name
497 name = u'jiefmw'
468 name = u'jiefmw'
498 mod = ip.new_main_mod(u'%s.py' % name, name)
469 mod = ip.new_main_mod(u'%s.py' % name, name)
499 self.assertEqual(mod.__name__, name)
470 self.assertEqual(mod.__name__, name)
500
471
501 def test_get_exception_only(self):
472 def test_get_exception_only(self):
502 try:
473 try:
503 raise KeyboardInterrupt
474 raise KeyboardInterrupt
504 except KeyboardInterrupt:
475 except KeyboardInterrupt:
505 msg = ip.get_exception_only()
476 msg = ip.get_exception_only()
506 self.assertEqual(msg, 'KeyboardInterrupt\n')
477 self.assertEqual(msg, 'KeyboardInterrupt\n')
507
478
508 try:
479 try:
509 raise DerivedInterrupt("foo")
480 raise DerivedInterrupt("foo")
510 except KeyboardInterrupt:
481 except KeyboardInterrupt:
511 msg = ip.get_exception_only()
482 msg = ip.get_exception_only()
512 if sys.version_info[0] <= 2:
513 self.assertEqual(msg, 'DerivedInterrupt: foo\n')
514 else:
515 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
483 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
516
484
517 def test_inspect_text(self):
485 def test_inspect_text(self):
518 ip.run_cell('a = 5')
486 ip.run_cell('a = 5')
519 text = ip.object_inspect_text('a')
487 text = ip.object_inspect_text('a')
520 self.assertIsInstance(text, unicode_type)
488 self.assertIsInstance(text, unicode_type)
521
489
522
490
523 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
491 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
524
492
525 @onlyif_unicode_paths
493 @onlyif_unicode_paths
526 def setUp(self):
494 def setUp(self):
527 self.BASETESTDIR = tempfile.mkdtemp()
495 self.BASETESTDIR = tempfile.mkdtemp()
528 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
496 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
529 os.mkdir(self.TESTDIR)
497 os.mkdir(self.TESTDIR)
530 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
498 with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile:
531 sfile.write("pass\n")
499 sfile.write("pass\n")
532 self.oldpath = py3compat.getcwd()
500 self.oldpath = py3compat.getcwd()
533 os.chdir(self.TESTDIR)
501 os.chdir(self.TESTDIR)
534 self.fname = u"åäötestscript.py"
502 self.fname = u"åäötestscript.py"
535
503
536 def tearDown(self):
504 def tearDown(self):
537 os.chdir(self.oldpath)
505 os.chdir(self.oldpath)
538 shutil.rmtree(self.BASETESTDIR)
506 shutil.rmtree(self.BASETESTDIR)
539
507
540 @onlyif_unicode_paths
508 @onlyif_unicode_paths
541 def test_1(self):
509 def test_1(self):
542 """Test safe_execfile with non-ascii path
510 """Test safe_execfile with non-ascii path
543 """
511 """
544 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
512 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
545
513
546 class ExitCodeChecks(tt.TempFileMixin):
514 class ExitCodeChecks(tt.TempFileMixin):
547 def test_exit_code_ok(self):
515 def test_exit_code_ok(self):
548 self.system('exit 0')
516 self.system('exit 0')
549 self.assertEqual(ip.user_ns['_exit_code'], 0)
517 self.assertEqual(ip.user_ns['_exit_code'], 0)
550
518
551 def test_exit_code_error(self):
519 def test_exit_code_error(self):
552 self.system('exit 1')
520 self.system('exit 1')
553 self.assertEqual(ip.user_ns['_exit_code'], 1)
521 self.assertEqual(ip.user_ns['_exit_code'], 1)
554
522
555 @skipif(not hasattr(signal, 'SIGALRM'))
523 @skipif(not hasattr(signal, 'SIGALRM'))
556 def test_exit_code_signal(self):
524 def test_exit_code_signal(self):
557 self.mktmp("import signal, time\n"
525 self.mktmp("import signal, time\n"
558 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
526 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
559 "time.sleep(1)\n")
527 "time.sleep(1)\n")
560 self.system("%s %s" % (sys.executable, self.fname))
528 self.system("%s %s" % (sys.executable, self.fname))
561 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
529 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
562
530
563 @onlyif_cmds_exist("csh")
531 @onlyif_cmds_exist("csh")
564 def test_exit_code_signal_csh(self):
532 def test_exit_code_signal_csh(self):
565 SHELL = os.environ.get('SHELL', None)
533 SHELL = os.environ.get('SHELL', None)
566 os.environ['SHELL'] = find_cmd("csh")
534 os.environ['SHELL'] = find_cmd("csh")
567 try:
535 try:
568 self.test_exit_code_signal()
536 self.test_exit_code_signal()
569 finally:
537 finally:
570 if SHELL is not None:
538 if SHELL is not None:
571 os.environ['SHELL'] = SHELL
539 os.environ['SHELL'] = SHELL
572 else:
540 else:
573 del os.environ['SHELL']
541 del os.environ['SHELL']
574
542
575 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
543 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
576 system = ip.system_raw
544 system = ip.system_raw
577
545
578 @onlyif_unicode_paths
546 @onlyif_unicode_paths
579 def test_1(self):
547 def test_1(self):
580 """Test system_raw with non-ascii cmd
548 """Test system_raw with non-ascii cmd
581 """
549 """
582 cmd = u'''python -c "'åäö'" '''
550 cmd = u'''python -c "'åäö'" '''
583 ip.system_raw(cmd)
551 ip.system_raw(cmd)
584
552
585 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
553 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
586 @mock.patch('os.system', side_effect=KeyboardInterrupt)
554 @mock.patch('os.system', side_effect=KeyboardInterrupt)
587 def test_control_c(self, *mocks):
555 def test_control_c(self, *mocks):
588 try:
556 try:
589 self.system("sleep 1 # wont happen")
557 self.system("sleep 1 # wont happen")
590 except KeyboardInterrupt:
558 except KeyboardInterrupt:
591 self.fail("system call should intercept "
559 self.fail("system call should intercept "
592 "keyboard interrupt from subprocess.call")
560 "keyboard interrupt from subprocess.call")
593 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
561 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT)
594
562
595 # TODO: Exit codes are currently ignored on Windows.
563 # TODO: Exit codes are currently ignored on Windows.
596 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
564 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
597 system = ip.system_piped
565 system = ip.system_piped
598
566
599 @skip_win32
567 @skip_win32
600 def test_exit_code_ok(self):
568 def test_exit_code_ok(self):
601 ExitCodeChecks.test_exit_code_ok(self)
569 ExitCodeChecks.test_exit_code_ok(self)
602
570
603 @skip_win32
571 @skip_win32
604 def test_exit_code_error(self):
572 def test_exit_code_error(self):
605 ExitCodeChecks.test_exit_code_error(self)
573 ExitCodeChecks.test_exit_code_error(self)
606
574
607 @skip_win32
575 @skip_win32
608 def test_exit_code_signal(self):
576 def test_exit_code_signal(self):
609 ExitCodeChecks.test_exit_code_signal(self)
577 ExitCodeChecks.test_exit_code_signal(self)
610
578
611 class TestModules(unittest.TestCase, tt.TempFileMixin):
579 class TestModules(unittest.TestCase, tt.TempFileMixin):
612 def test_extraneous_loads(self):
580 def test_extraneous_loads(self):
613 """Test we're not loading modules on startup that we shouldn't.
581 """Test we're not loading modules on startup that we shouldn't.
614 """
582 """
615 self.mktmp("import sys\n"
583 self.mktmp("import sys\n"
616 "print('numpy' in sys.modules)\n"
584 "print('numpy' in sys.modules)\n"
617 "print('ipyparallel' in sys.modules)\n"
585 "print('ipyparallel' in sys.modules)\n"
618 "print('ipykernel' in sys.modules)\n"
586 "print('ipykernel' in sys.modules)\n"
619 )
587 )
620 out = "False\nFalse\nFalse\n"
588 out = "False\nFalse\nFalse\n"
621 tt.ipexec_validate(self.fname, out)
589 tt.ipexec_validate(self.fname, out)
622
590
623 class Negator(ast.NodeTransformer):
591 class Negator(ast.NodeTransformer):
624 """Negates all number literals in an AST."""
592 """Negates all number literals in an AST."""
625 def visit_Num(self, node):
593 def visit_Num(self, node):
626 node.n = -node.n
594 node.n = -node.n
627 return node
595 return node
628
596
629 class TestAstTransform(unittest.TestCase):
597 class TestAstTransform(unittest.TestCase):
630 def setUp(self):
598 def setUp(self):
631 self.negator = Negator()
599 self.negator = Negator()
632 ip.ast_transformers.append(self.negator)
600 ip.ast_transformers.append(self.negator)
633
601
634 def tearDown(self):
602 def tearDown(self):
635 ip.ast_transformers.remove(self.negator)
603 ip.ast_transformers.remove(self.negator)
636
604
637 def test_run_cell(self):
605 def test_run_cell(self):
638 with tt.AssertPrints('-34'):
606 with tt.AssertPrints('-34'):
639 ip.run_cell('print (12 + 22)')
607 ip.run_cell('print (12 + 22)')
640
608
641 # A named reference to a number shouldn't be transformed.
609 # A named reference to a number shouldn't be transformed.
642 ip.user_ns['n'] = 55
610 ip.user_ns['n'] = 55
643 with tt.AssertNotPrints('-55'):
611 with tt.AssertNotPrints('-55'):
644 ip.run_cell('print (n)')
612 ip.run_cell('print (n)')
645
613
646 def test_timeit(self):
614 def test_timeit(self):
647 called = set()
615 called = set()
648 def f(x):
616 def f(x):
649 called.add(x)
617 called.add(x)
650 ip.push({'f':f})
618 ip.push({'f':f})
651
619
652 with tt.AssertPrints("average of "):
620 with tt.AssertPrints("average of "):
653 ip.run_line_magic("timeit", "-n1 f(1)")
621 ip.run_line_magic("timeit", "-n1 f(1)")
654 self.assertEqual(called, {-1})
622 self.assertEqual(called, {-1})
655 called.clear()
623 called.clear()
656
624
657 with tt.AssertPrints("average of "):
625 with tt.AssertPrints("average of "):
658 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
626 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
659 self.assertEqual(called, {-2, -3})
627 self.assertEqual(called, {-2, -3})
660
628
661 def test_time(self):
629 def test_time(self):
662 called = []
630 called = []
663 def f(x):
631 def f(x):
664 called.append(x)
632 called.append(x)
665 ip.push({'f':f})
633 ip.push({'f':f})
666
634
667 # Test with an expression
635 # Test with an expression
668 with tt.AssertPrints("Wall time: "):
636 with tt.AssertPrints("Wall time: "):
669 ip.run_line_magic("time", "f(5+9)")
637 ip.run_line_magic("time", "f(5+9)")
670 self.assertEqual(called, [-14])
638 self.assertEqual(called, [-14])
671 called[:] = []
639 called[:] = []
672
640
673 # Test with a statement (different code path)
641 # Test with a statement (different code path)
674 with tt.AssertPrints("Wall time: "):
642 with tt.AssertPrints("Wall time: "):
675 ip.run_line_magic("time", "a = f(-3 + -2)")
643 ip.run_line_magic("time", "a = f(-3 + -2)")
676 self.assertEqual(called, [5])
644 self.assertEqual(called, [5])
677
645
678 def test_macro(self):
646 def test_macro(self):
679 ip.push({'a':10})
647 ip.push({'a':10})
680 # The AST transformation makes this do a+=-1
648 # The AST transformation makes this do a+=-1
681 ip.define_macro("amacro", "a+=1\nprint(a)")
649 ip.define_macro("amacro", "a+=1\nprint(a)")
682
650
683 with tt.AssertPrints("9"):
651 with tt.AssertPrints("9"):
684 ip.run_cell("amacro")
652 ip.run_cell("amacro")
685 with tt.AssertPrints("8"):
653 with tt.AssertPrints("8"):
686 ip.run_cell("amacro")
654 ip.run_cell("amacro")
687
655
688 class IntegerWrapper(ast.NodeTransformer):
656 class IntegerWrapper(ast.NodeTransformer):
689 """Wraps all integers in a call to Integer()"""
657 """Wraps all integers in a call to Integer()"""
690 def visit_Num(self, node):
658 def visit_Num(self, node):
691 if isinstance(node.n, int):
659 if isinstance(node.n, int):
692 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
660 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
693 args=[node], keywords=[])
661 args=[node], keywords=[])
694 return node
662 return node
695
663
696 class TestAstTransform2(unittest.TestCase):
664 class TestAstTransform2(unittest.TestCase):
697 def setUp(self):
665 def setUp(self):
698 self.intwrapper = IntegerWrapper()
666 self.intwrapper = IntegerWrapper()
699 ip.ast_transformers.append(self.intwrapper)
667 ip.ast_transformers.append(self.intwrapper)
700
668
701 self.calls = []
669 self.calls = []
702 def Integer(*args):
670 def Integer(*args):
703 self.calls.append(args)
671 self.calls.append(args)
704 return args
672 return args
705 ip.push({"Integer": Integer})
673 ip.push({"Integer": Integer})
706
674
707 def tearDown(self):
675 def tearDown(self):
708 ip.ast_transformers.remove(self.intwrapper)
676 ip.ast_transformers.remove(self.intwrapper)
709 del ip.user_ns['Integer']
677 del ip.user_ns['Integer']
710
678
711 def test_run_cell(self):
679 def test_run_cell(self):
712 ip.run_cell("n = 2")
680 ip.run_cell("n = 2")
713 self.assertEqual(self.calls, [(2,)])
681 self.assertEqual(self.calls, [(2,)])
714
682
715 # This shouldn't throw an error
683 # This shouldn't throw an error
716 ip.run_cell("o = 2.0")
684 ip.run_cell("o = 2.0")
717 self.assertEqual(ip.user_ns['o'], 2.0)
685 self.assertEqual(ip.user_ns['o'], 2.0)
718
686
719 def test_timeit(self):
687 def test_timeit(self):
720 called = set()
688 called = set()
721 def f(x):
689 def f(x):
722 called.add(x)
690 called.add(x)
723 ip.push({'f':f})
691 ip.push({'f':f})
724
692
725 with tt.AssertPrints("average of "):
693 with tt.AssertPrints("average of "):
726 ip.run_line_magic("timeit", "-n1 f(1)")
694 ip.run_line_magic("timeit", "-n1 f(1)")
727 self.assertEqual(called, {(1,)})
695 self.assertEqual(called, {(1,)})
728 called.clear()
696 called.clear()
729
697
730 with tt.AssertPrints("average of "):
698 with tt.AssertPrints("average of "):
731 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
699 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
732 self.assertEqual(called, {(2,), (3,)})
700 self.assertEqual(called, {(2,), (3,)})
733
701
734 class ErrorTransformer(ast.NodeTransformer):
702 class ErrorTransformer(ast.NodeTransformer):
735 """Throws an error when it sees a number."""
703 """Throws an error when it sees a number."""
736 def visit_Num(self, node):
704 def visit_Num(self, node):
737 raise ValueError("test")
705 raise ValueError("test")
738
706
739 class TestAstTransformError(unittest.TestCase):
707 class TestAstTransformError(unittest.TestCase):
740 def test_unregistering(self):
708 def test_unregistering(self):
741 err_transformer = ErrorTransformer()
709 err_transformer = ErrorTransformer()
742 ip.ast_transformers.append(err_transformer)
710 ip.ast_transformers.append(err_transformer)
743
711
744 with tt.AssertPrints("unregister", channel='stderr'):
712 with tt.AssertPrints("unregister", channel='stderr'):
745 ip.run_cell("1 + 2")
713 ip.run_cell("1 + 2")
746
714
747 # This should have been removed.
715 # This should have been removed.
748 nt.assert_not_in(err_transformer, ip.ast_transformers)
716 nt.assert_not_in(err_transformer, ip.ast_transformers)
749
717
750
718
751 class StringRejector(ast.NodeTransformer):
719 class StringRejector(ast.NodeTransformer):
752 """Throws an InputRejected when it sees a string literal.
720 """Throws an InputRejected when it sees a string literal.
753
721
754 Used to verify that NodeTransformers can signal that a piece of code should
722 Used to verify that NodeTransformers can signal that a piece of code should
755 not be executed by throwing an InputRejected.
723 not be executed by throwing an InputRejected.
756 """
724 """
757
725
758 def visit_Str(self, node):
726 def visit_Str(self, node):
759 raise InputRejected("test")
727 raise InputRejected("test")
760
728
761
729
762 class TestAstTransformInputRejection(unittest.TestCase):
730 class TestAstTransformInputRejection(unittest.TestCase):
763
731
764 def setUp(self):
732 def setUp(self):
765 self.transformer = StringRejector()
733 self.transformer = StringRejector()
766 ip.ast_transformers.append(self.transformer)
734 ip.ast_transformers.append(self.transformer)
767
735
768 def tearDown(self):
736 def tearDown(self):
769 ip.ast_transformers.remove(self.transformer)
737 ip.ast_transformers.remove(self.transformer)
770
738
771 def test_input_rejection(self):
739 def test_input_rejection(self):
772 """Check that NodeTransformers can reject input."""
740 """Check that NodeTransformers can reject input."""
773
741
774 expect_exception_tb = tt.AssertPrints("InputRejected: test")
742 expect_exception_tb = tt.AssertPrints("InputRejected: test")
775 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
743 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
776
744
777 # Run the same check twice to verify that the transformer is not
745 # Run the same check twice to verify that the transformer is not
778 # disabled after raising.
746 # disabled after raising.
779 with expect_exception_tb, expect_no_cell_output:
747 with expect_exception_tb, expect_no_cell_output:
780 ip.run_cell("'unsafe'")
748 ip.run_cell("'unsafe'")
781
749
782 with expect_exception_tb, expect_no_cell_output:
750 with expect_exception_tb, expect_no_cell_output:
783 res = ip.run_cell("'unsafe'")
751 res = ip.run_cell("'unsafe'")
784
752
785 self.assertIsInstance(res.error_before_exec, InputRejected)
753 self.assertIsInstance(res.error_before_exec, InputRejected)
786
754
787 def test__IPYTHON__():
755 def test__IPYTHON__():
788 # This shouldn't raise a NameError, that's all
756 # This shouldn't raise a NameError, that's all
789 __IPYTHON__
757 __IPYTHON__
790
758
791
759
792 class DummyRepr(object):
760 class DummyRepr(object):
793 def __repr__(self):
761 def __repr__(self):
794 return "DummyRepr"
762 return "DummyRepr"
795
763
796 def _repr_html_(self):
764 def _repr_html_(self):
797 return "<b>dummy</b>"
765 return "<b>dummy</b>"
798
766
799 def _repr_javascript_(self):
767 def _repr_javascript_(self):
800 return "console.log('hi');", {'key': 'value'}
768 return "console.log('hi');", {'key': 'value'}
801
769
802
770
803 def test_user_variables():
771 def test_user_variables():
804 # enable all formatters
772 # enable all formatters
805 ip.display_formatter.active_types = ip.display_formatter.format_types
773 ip.display_formatter.active_types = ip.display_formatter.format_types
806
774
807 ip.user_ns['dummy'] = d = DummyRepr()
775 ip.user_ns['dummy'] = d = DummyRepr()
808 keys = {'dummy', 'doesnotexist'}
776 keys = {'dummy', 'doesnotexist'}
809 r = ip.user_expressions({ key:key for key in keys})
777 r = ip.user_expressions({ key:key for key in keys})
810
778
811 nt.assert_equal(keys, set(r.keys()))
779 nt.assert_equal(keys, set(r.keys()))
812 dummy = r['dummy']
780 dummy = r['dummy']
813 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
781 nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys()))
814 nt.assert_equal(dummy['status'], 'ok')
782 nt.assert_equal(dummy['status'], 'ok')
815 data = dummy['data']
783 data = dummy['data']
816 metadata = dummy['metadata']
784 metadata = dummy['metadata']
817 nt.assert_equal(data.get('text/html'), d._repr_html_())
785 nt.assert_equal(data.get('text/html'), d._repr_html_())
818 js, jsmd = d._repr_javascript_()
786 js, jsmd = d._repr_javascript_()
819 nt.assert_equal(data.get('application/javascript'), js)
787 nt.assert_equal(data.get('application/javascript'), js)
820 nt.assert_equal(metadata.get('application/javascript'), jsmd)
788 nt.assert_equal(metadata.get('application/javascript'), jsmd)
821
789
822 dne = r['doesnotexist']
790 dne = r['doesnotexist']
823 nt.assert_equal(dne['status'], 'error')
791 nt.assert_equal(dne['status'], 'error')
824 nt.assert_equal(dne['ename'], 'NameError')
792 nt.assert_equal(dne['ename'], 'NameError')
825
793
826 # back to text only
794 # back to text only
827 ip.display_formatter.active_types = ['text/plain']
795 ip.display_formatter.active_types = ['text/plain']
828
796
829 def test_user_expression():
797 def test_user_expression():
830 # enable all formatters
798 # enable all formatters
831 ip.display_formatter.active_types = ip.display_formatter.format_types
799 ip.display_formatter.active_types = ip.display_formatter.format_types
832 query = {
800 query = {
833 'a' : '1 + 2',
801 'a' : '1 + 2',
834 'b' : '1/0',
802 'b' : '1/0',
835 }
803 }
836 r = ip.user_expressions(query)
804 r = ip.user_expressions(query)
837 import pprint
805 import pprint
838 pprint.pprint(r)
806 pprint.pprint(r)
839 nt.assert_equal(set(r.keys()), set(query.keys()))
807 nt.assert_equal(set(r.keys()), set(query.keys()))
840 a = r['a']
808 a = r['a']
841 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
809 nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys()))
842 nt.assert_equal(a['status'], 'ok')
810 nt.assert_equal(a['status'], 'ok')
843 data = a['data']
811 data = a['data']
844 metadata = a['metadata']
812 metadata = a['metadata']
845 nt.assert_equal(data.get('text/plain'), '3')
813 nt.assert_equal(data.get('text/plain'), '3')
846
814
847 b = r['b']
815 b = r['b']
848 nt.assert_equal(b['status'], 'error')
816 nt.assert_equal(b['status'], 'error')
849 nt.assert_equal(b['ename'], 'ZeroDivisionError')
817 nt.assert_equal(b['ename'], 'ZeroDivisionError')
850
818
851 # back to text only
819 # back to text only
852 ip.display_formatter.active_types = ['text/plain']
820 ip.display_formatter.active_types = ['text/plain']
853
821
854
822
855
823
856
824
857
825
858 class TestSyntaxErrorTransformer(unittest.TestCase):
826 class TestSyntaxErrorTransformer(unittest.TestCase):
859 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
827 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
860
828
861 class SyntaxErrorTransformer(InputTransformer):
829 class SyntaxErrorTransformer(InputTransformer):
862
830
863 def push(self, line):
831 def push(self, line):
864 pos = line.find('syntaxerror')
832 pos = line.find('syntaxerror')
865 if pos >= 0:
833 if pos >= 0:
866 e = SyntaxError('input contains "syntaxerror"')
834 e = SyntaxError('input contains "syntaxerror"')
867 e.text = line
835 e.text = line
868 e.offset = pos + 1
836 e.offset = pos + 1
869 raise e
837 raise e
870 return line
838 return line
871
839
872 def reset(self):
840 def reset(self):
873 pass
841 pass
874
842
875 def setUp(self):
843 def setUp(self):
876 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
844 self.transformer = TestSyntaxErrorTransformer.SyntaxErrorTransformer()
877 ip.input_splitter.python_line_transforms.append(self.transformer)
845 ip.input_splitter.python_line_transforms.append(self.transformer)
878 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
846 ip.input_transformer_manager.python_line_transforms.append(self.transformer)
879
847
880 def tearDown(self):
848 def tearDown(self):
881 ip.input_splitter.python_line_transforms.remove(self.transformer)
849 ip.input_splitter.python_line_transforms.remove(self.transformer)
882 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
850 ip.input_transformer_manager.python_line_transforms.remove(self.transformer)
883
851
884 def test_syntaxerror_input_transformer(self):
852 def test_syntaxerror_input_transformer(self):
885 with tt.AssertPrints('1234'):
853 with tt.AssertPrints('1234'):
886 ip.run_cell('1234')
854 ip.run_cell('1234')
887 with tt.AssertPrints('SyntaxError: invalid syntax'):
855 with tt.AssertPrints('SyntaxError: invalid syntax'):
888 ip.run_cell('1 2 3') # plain python syntax error
856 ip.run_cell('1 2 3') # plain python syntax error
889 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
857 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
890 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
858 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
891 with tt.AssertPrints('3456'):
859 with tt.AssertPrints('3456'):
892 ip.run_cell('3456')
860 ip.run_cell('3456')
893
861
894
862
895
863
896 def test_warning_suppression():
864 def test_warning_suppression():
897 ip.run_cell("import warnings")
865 ip.run_cell("import warnings")
898 try:
866 try:
899 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
867 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
900 ip.run_cell("warnings.warn('asdf')")
868 ip.run_cell("warnings.warn('asdf')")
901 # Here's the real test -- if we run that again, we should get the
869 # Here's the real test -- if we run that again, we should get the
902 # warning again. Traditionally, each warning was only issued once per
870 # warning again. Traditionally, each warning was only issued once per
903 # IPython session (approximately), even if the user typed in new and
871 # IPython session (approximately), even if the user typed in new and
904 # different code that should have also triggered the warning, leading
872 # different code that should have also triggered the warning, leading
905 # to much confusion.
873 # to much confusion.
906 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
874 with tt.AssertPrints("UserWarning: asdf", channel="stderr"):
907 ip.run_cell("warnings.warn('asdf')")
875 ip.run_cell("warnings.warn('asdf')")
908 finally:
876 finally:
909 ip.run_cell("del warnings")
877 ip.run_cell("del warnings")
910
878
911
879
912 def test_deprecation_warning():
880 def test_deprecation_warning():
913 ip.run_cell("""
881 ip.run_cell("""
914 import warnings
882 import warnings
915 def wrn():
883 def wrn():
916 warnings.warn(
884 warnings.warn(
917 "I AM A WARNING",
885 "I AM A WARNING",
918 DeprecationWarning
886 DeprecationWarning
919 )
887 )
920 """)
888 """)
921 try:
889 try:
922 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
890 with tt.AssertPrints("I AM A WARNING", channel="stderr"):
923 ip.run_cell("wrn()")
891 ip.run_cell("wrn()")
924 finally:
892 finally:
925 ip.run_cell("del warnings")
893 ip.run_cell("del warnings")
926 ip.run_cell("del wrn")
894 ip.run_cell("del wrn")
927
895
928
896
929 class TestImportNoDeprecate(tt.TempFileMixin):
897 class TestImportNoDeprecate(tt.TempFileMixin):
930
898
931 def setup(self):
899 def setup(self):
932 """Make a valid python temp file."""
900 """Make a valid python temp file."""
933 self.mktmp("""
901 self.mktmp("""
934 import warnings
902 import warnings
935 def wrn():
903 def wrn():
936 warnings.warn(
904 warnings.warn(
937 "I AM A WARNING",
905 "I AM A WARNING",
938 DeprecationWarning
906 DeprecationWarning
939 )
907 )
940 """)
908 """)
941
909
942 def test_no_dep(self):
910 def test_no_dep(self):
943 """
911 """
944 No deprecation warning should be raised from imported functions
912 No deprecation warning should be raised from imported functions
945 """
913 """
946 ip.run_cell("from {} import wrn".format(self.fname))
914 ip.run_cell("from {} import wrn".format(self.fname))
947
915
948 with tt.AssertNotPrints("I AM A WARNING"):
916 with tt.AssertNotPrints("I AM A WARNING"):
949 ip.run_cell("wrn()")
917 ip.run_cell("wrn()")
950 ip.run_cell("del wrn")
918 ip.run_cell("del wrn")
@@ -1,1011 +1,988 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 from __future__ import absolute_import
6 from __future__ import absolute_import
7
7
8 import io
8 import io
9 import os
9 import os
10 import sys
10 import sys
11 import warnings
11 import warnings
12 from unittest import TestCase
12 from unittest import TestCase
13
13
14 try:
14 try:
15 from importlib import invalidate_caches # Required from Python 3.3
15 from importlib import invalidate_caches # Required from Python 3.3
16 except ImportError:
16 except ImportError:
17 def invalidate_caches():
17 def invalidate_caches():
18 pass
18 pass
19
19
20 import nose.tools as nt
20 import nose.tools as nt
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
28 from IPython.core.magics import execution, script, code
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 import py3compat
31 from IPython.utils import py3compat
32 from IPython.utils.io import capture_output
32 from IPython.utils.io import capture_output
33 from IPython.utils.tempdir import TemporaryDirectory
33 from IPython.utils.tempdir import TemporaryDirectory
34 from IPython.utils.process import find_cmd
34 from IPython.utils.process import find_cmd
35
35
36 if py3compat.PY3:
36 if py3compat.PY3:
37 from io import StringIO
37 from io import StringIO
38 else:
38 else:
39 from StringIO import StringIO
39 from StringIO import StringIO
40
40
41
41
42 _ip = get_ipython()
42 _ip = get_ipython()
43
43
44 @magic.magics_class
44 @magic.magics_class
45 class DummyMagics(magic.Magics): pass
45 class DummyMagics(magic.Magics): pass
46
46
47 def test_extract_code_ranges():
47 def test_extract_code_ranges():
48 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
48 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
49 expected = [(0, 1),
49 expected = [(0, 1),
50 (2, 3),
50 (2, 3),
51 (4, 6),
51 (4, 6),
52 (6, 9),
52 (6, 9),
53 (9, 14),
53 (9, 14),
54 (16, None),
54 (16, None),
55 (None, 9),
55 (None, 9),
56 (9, None),
56 (9, None),
57 (None, 13),
57 (None, 13),
58 (None, None)]
58 (None, None)]
59 actual = list(code.extract_code_ranges(instr))
59 actual = list(code.extract_code_ranges(instr))
60 nt.assert_equal(actual, expected)
60 nt.assert_equal(actual, expected)
61
61
62 def test_extract_symbols():
62 def test_extract_symbols():
63 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
63 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
64 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
64 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
65 expected = [([], ['a']),
65 expected = [([], ['a']),
66 (["def b():\n return 42\n"], []),
66 (["def b():\n return 42\n"], []),
67 (["class A: pass\n"], []),
67 (["class A: pass\n"], []),
68 (["class A: pass\n", "def b():\n return 42\n"], []),
68 (["class A: pass\n", "def b():\n return 42\n"], []),
69 (["class A: pass\n"], ['a']),
69 (["class A: pass\n"], ['a']),
70 ([], ['z'])]
70 ([], ['z'])]
71 for symbols, exp in zip(symbols_args, expected):
71 for symbols, exp in zip(symbols_args, expected):
72 nt.assert_equal(code.extract_symbols(source, symbols), exp)
72 nt.assert_equal(code.extract_symbols(source, symbols), exp)
73
73
74
74
75 def test_extract_symbols_raises_exception_with_non_python_code():
75 def test_extract_symbols_raises_exception_with_non_python_code():
76 source = ("=begin A Ruby program :)=end\n"
76 source = ("=begin A Ruby program :)=end\n"
77 "def hello\n"
77 "def hello\n"
78 "puts 'Hello world'\n"
78 "puts 'Hello world'\n"
79 "end")
79 "end")
80 with nt.assert_raises(SyntaxError):
80 with nt.assert_raises(SyntaxError):
81 code.extract_symbols(source, "hello")
81 code.extract_symbols(source, "hello")
82
82
83 def test_config():
83 def test_config():
84 """ test that config magic does not raise
84 """ test that config magic does not raise
85 can happen if Configurable init is moved too early into
85 can happen if Configurable init is moved too early into
86 Magics.__init__ as then a Config object will be registerd as a
86 Magics.__init__ as then a Config object will be registerd as a
87 magic.
87 magic.
88 """
88 """
89 ## should not raise.
89 ## should not raise.
90 _ip.magic('config')
90 _ip.magic('config')
91
91
92 def test_rehashx():
92 def test_rehashx():
93 # clear up everything
93 # clear up everything
94 _ip.alias_manager.clear_aliases()
94 _ip.alias_manager.clear_aliases()
95 del _ip.db['syscmdlist']
95 del _ip.db['syscmdlist']
96
96
97 _ip.magic('rehashx')
97 _ip.magic('rehashx')
98 # Practically ALL ipython development systems will have more than 10 aliases
98 # Practically ALL ipython development systems will have more than 10 aliases
99
99
100 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
100 nt.assert_true(len(_ip.alias_manager.aliases) > 10)
101 for name, cmd in _ip.alias_manager.aliases:
101 for name, cmd in _ip.alias_manager.aliases:
102 # we must strip dots from alias names
102 # we must strip dots from alias names
103 nt.assert_not_in('.', name)
103 nt.assert_not_in('.', name)
104
104
105 # rehashx must fill up syscmdlist
105 # rehashx must fill up syscmdlist
106 scoms = _ip.db['syscmdlist']
106 scoms = _ip.db['syscmdlist']
107 nt.assert_true(len(scoms) > 10)
107 nt.assert_true(len(scoms) > 10)
108
108
109
109
110 def test_magic_parse_options():
110 def test_magic_parse_options():
111 """Test that we don't mangle paths when parsing magic options."""
111 """Test that we don't mangle paths when parsing magic options."""
112 ip = get_ipython()
112 ip = get_ipython()
113 path = 'c:\\x'
113 path = 'c:\\x'
114 m = DummyMagics(ip)
114 m = DummyMagics(ip)
115 opts = m.parse_options('-f %s' % path,'f:')[0]
115 opts = m.parse_options('-f %s' % path,'f:')[0]
116 # argv splitting is os-dependent
116 # argv splitting is os-dependent
117 if os.name == 'posix':
117 if os.name == 'posix':
118 expected = 'c:x'
118 expected = 'c:x'
119 else:
119 else:
120 expected = path
120 expected = path
121 nt.assert_equal(opts['f'], expected)
121 nt.assert_equal(opts['f'], expected)
122
122
123 def test_magic_parse_long_options():
123 def test_magic_parse_long_options():
124 """Magic.parse_options can handle --foo=bar long options"""
124 """Magic.parse_options can handle --foo=bar long options"""
125 ip = get_ipython()
125 ip = get_ipython()
126 m = DummyMagics(ip)
126 m = DummyMagics(ip)
127 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
127 opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=')
128 nt.assert_in('foo', opts)
128 nt.assert_in('foo', opts)
129 nt.assert_in('bar', opts)
129 nt.assert_in('bar', opts)
130 nt.assert_equal(opts['bar'], "bubble")
130 nt.assert_equal(opts['bar'], "bubble")
131
131
132
132
133 @dec.skip_without('sqlite3')
133 @dec.skip_without('sqlite3')
134 def doctest_hist_f():
134 def doctest_hist_f():
135 """Test %hist -f with temporary filename.
135 """Test %hist -f with temporary filename.
136
136
137 In [9]: import tempfile
137 In [9]: import tempfile
138
138
139 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
139 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
140
140
141 In [11]: %hist -nl -f $tfile 3
141 In [11]: %hist -nl -f $tfile 3
142
142
143 In [13]: import os; os.unlink(tfile)
143 In [13]: import os; os.unlink(tfile)
144 """
144 """
145
145
146
146
147 @dec.skip_without('sqlite3')
147 @dec.skip_without('sqlite3')
148 def doctest_hist_r():
148 def doctest_hist_r():
149 """Test %hist -r
149 """Test %hist -r
150
150
151 XXX - This test is not recording the output correctly. For some reason, in
151 XXX - This test is not recording the output correctly. For some reason, in
152 testing mode the raw history isn't getting populated. No idea why.
152 testing mode the raw history isn't getting populated. No idea why.
153 Disabling the output checking for now, though at least we do run it.
153 Disabling the output checking for now, though at least we do run it.
154
154
155 In [1]: 'hist' in _ip.lsmagic()
155 In [1]: 'hist' in _ip.lsmagic()
156 Out[1]: True
156 Out[1]: True
157
157
158 In [2]: x=1
158 In [2]: x=1
159
159
160 In [3]: %hist -rl 2
160 In [3]: %hist -rl 2
161 x=1 # random
161 x=1 # random
162 %hist -r 2
162 %hist -r 2
163 """
163 """
164
164
165
165
166 @dec.skip_without('sqlite3')
166 @dec.skip_without('sqlite3')
167 def doctest_hist_op():
167 def doctest_hist_op():
168 """Test %hist -op
168 """Test %hist -op
169
169
170 In [1]: class b(float):
170 In [1]: class b(float):
171 ...: pass
171 ...: pass
172 ...:
172 ...:
173
173
174 In [2]: class s(object):
174 In [2]: class s(object):
175 ...: def __str__(self):
175 ...: def __str__(self):
176 ...: return 's'
176 ...: return 's'
177 ...:
177 ...:
178
178
179 In [3]:
179 In [3]:
180
180
181 In [4]: class r(b):
181 In [4]: class r(b):
182 ...: def __repr__(self):
182 ...: def __repr__(self):
183 ...: return 'r'
183 ...: return 'r'
184 ...:
184 ...:
185
185
186 In [5]: class sr(s,r): pass
186 In [5]: class sr(s,r): pass
187 ...:
187 ...:
188
188
189 In [6]:
189 In [6]:
190
190
191 In [7]: bb=b()
191 In [7]: bb=b()
192
192
193 In [8]: ss=s()
193 In [8]: ss=s()
194
194
195 In [9]: rr=r()
195 In [9]: rr=r()
196
196
197 In [10]: ssrr=sr()
197 In [10]: ssrr=sr()
198
198
199 In [11]: 4.5
199 In [11]: 4.5
200 Out[11]: 4.5
200 Out[11]: 4.5
201
201
202 In [12]: str(ss)
202 In [12]: str(ss)
203 Out[12]: 's'
203 Out[12]: 's'
204
204
205 In [13]:
205 In [13]:
206
206
207 In [14]: %hist -op
207 In [14]: %hist -op
208 >>> class b:
208 >>> class b:
209 ... pass
209 ... pass
210 ...
210 ...
211 >>> class s(b):
211 >>> class s(b):
212 ... def __str__(self):
212 ... def __str__(self):
213 ... return 's'
213 ... return 's'
214 ...
214 ...
215 >>>
215 >>>
216 >>> class r(b):
216 >>> class r(b):
217 ... def __repr__(self):
217 ... def __repr__(self):
218 ... return 'r'
218 ... return 'r'
219 ...
219 ...
220 >>> class sr(s,r): pass
220 >>> class sr(s,r): pass
221 >>>
221 >>>
222 >>> bb=b()
222 >>> bb=b()
223 >>> ss=s()
223 >>> ss=s()
224 >>> rr=r()
224 >>> rr=r()
225 >>> ssrr=sr()
225 >>> ssrr=sr()
226 >>> 4.5
226 >>> 4.5
227 4.5
227 4.5
228 >>> str(ss)
228 >>> str(ss)
229 's'
229 's'
230 >>>
230 >>>
231 """
231 """
232
232
233 def test_hist_pof():
233 def test_hist_pof():
234 ip = get_ipython()
234 ip = get_ipython()
235 ip.run_cell(u"1+2", store_history=True)
235 ip.run_cell(u"1+2", store_history=True)
236 #raise Exception(ip.history_manager.session_number)
236 #raise Exception(ip.history_manager.session_number)
237 #raise Exception(list(ip.history_manager._get_range_session()))
237 #raise Exception(list(ip.history_manager._get_range_session()))
238 with TemporaryDirectory() as td:
238 with TemporaryDirectory() as td:
239 tf = os.path.join(td, 'hist.py')
239 tf = os.path.join(td, 'hist.py')
240 ip.run_line_magic('history', '-pof %s' % tf)
240 ip.run_line_magic('history', '-pof %s' % tf)
241 assert os.path.isfile(tf)
241 assert os.path.isfile(tf)
242
242
243
243
244 @dec.skip_without('sqlite3')
244 @dec.skip_without('sqlite3')
245 def test_macro():
245 def test_macro():
246 ip = get_ipython()
246 ip = get_ipython()
247 ip.history_manager.reset() # Clear any existing history.
247 ip.history_manager.reset() # Clear any existing history.
248 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
248 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
249 for i, cmd in enumerate(cmds, start=1):
249 for i, cmd in enumerate(cmds, start=1):
250 ip.history_manager.store_inputs(i, cmd)
250 ip.history_manager.store_inputs(i, cmd)
251 ip.magic("macro test 1-3")
251 ip.magic("macro test 1-3")
252 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
252 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
253
253
254 # List macros
254 # List macros
255 nt.assert_in("test", ip.magic("macro"))
255 nt.assert_in("test", ip.magic("macro"))
256
256
257
257
258 @dec.skip_without('sqlite3')
258 @dec.skip_without('sqlite3')
259 def test_macro_run():
259 def test_macro_run():
260 """Test that we can run a multi-line macro successfully."""
260 """Test that we can run a multi-line macro successfully."""
261 ip = get_ipython()
261 ip = get_ipython()
262 ip.history_manager.reset()
262 ip.history_manager.reset()
263 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
263 cmds = ["a=10", "a+=1", py3compat.doctest_refactor_print("print a"),
264 "%macro test 2-3"]
264 "%macro test 2-3"]
265 for cmd in cmds:
265 for cmd in cmds:
266 ip.run_cell(cmd, store_history=True)
266 ip.run_cell(cmd, store_history=True)
267 nt.assert_equal(ip.user_ns["test"].value,
267 nt.assert_equal(ip.user_ns["test"].value,
268 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
268 py3compat.doctest_refactor_print("a+=1\nprint a\n"))
269 with tt.AssertPrints("12"):
269 with tt.AssertPrints("12"):
270 ip.run_cell("test")
270 ip.run_cell("test")
271 with tt.AssertPrints("13"):
271 with tt.AssertPrints("13"):
272 ip.run_cell("test")
272 ip.run_cell("test")
273
273
274
274
275 def test_magic_magic():
275 def test_magic_magic():
276 """Test %magic"""
276 """Test %magic"""
277 ip = get_ipython()
277 ip = get_ipython()
278 with capture_output() as captured:
278 with capture_output() as captured:
279 ip.magic("magic")
279 ip.magic("magic")
280
280
281 stdout = captured.stdout
281 stdout = captured.stdout
282 nt.assert_in('%magic', stdout)
282 nt.assert_in('%magic', stdout)
283 nt.assert_in('IPython', stdout)
283 nt.assert_in('IPython', stdout)
284 nt.assert_in('Available', stdout)
284 nt.assert_in('Available', stdout)
285
285
286
286
287 @dec.skipif_not_numpy
287 @dec.skipif_not_numpy
288 def test_numpy_reset_array_undec():
288 def test_numpy_reset_array_undec():
289 "Test '%reset array' functionality"
289 "Test '%reset array' functionality"
290 _ip.ex('import numpy as np')
290 _ip.ex('import numpy as np')
291 _ip.ex('a = np.empty(2)')
291 _ip.ex('a = np.empty(2)')
292 nt.assert_in('a', _ip.user_ns)
292 nt.assert_in('a', _ip.user_ns)
293 _ip.magic('reset -f array')
293 _ip.magic('reset -f array')
294 nt.assert_not_in('a', _ip.user_ns)
294 nt.assert_not_in('a', _ip.user_ns)
295
295
296 def test_reset_out():
296 def test_reset_out():
297 "Test '%reset out' magic"
297 "Test '%reset out' magic"
298 _ip.run_cell("parrot = 'dead'", store_history=True)
298 _ip.run_cell("parrot = 'dead'", store_history=True)
299 # test '%reset -f out', make an Out prompt
299 # test '%reset -f out', make an Out prompt
300 _ip.run_cell("parrot", store_history=True)
300 _ip.run_cell("parrot", store_history=True)
301 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
301 nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
302 _ip.magic('reset -f out')
302 _ip.magic('reset -f out')
303 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
303 nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')])
304 nt.assert_equal(len(_ip.user_ns['Out']), 0)
304 nt.assert_equal(len(_ip.user_ns['Out']), 0)
305
305
306 def test_reset_in():
306 def test_reset_in():
307 "Test '%reset in' magic"
307 "Test '%reset in' magic"
308 # test '%reset -f in'
308 # test '%reset -f in'
309 _ip.run_cell("parrot", store_history=True)
309 _ip.run_cell("parrot", store_history=True)
310 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
310 nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
311 _ip.magic('%reset -f in')
311 _ip.magic('%reset -f in')
312 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
312 nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')])
313 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
313 nt.assert_equal(len(set(_ip.user_ns['In'])), 1)
314
314
315 def test_reset_dhist():
315 def test_reset_dhist():
316 "Test '%reset dhist' magic"
316 "Test '%reset dhist' magic"
317 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
317 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
318 _ip.magic('cd ' + os.path.dirname(nt.__file__))
318 _ip.magic('cd ' + os.path.dirname(nt.__file__))
319 _ip.magic('cd -')
319 _ip.magic('cd -')
320 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
320 nt.assert_true(len(_ip.user_ns['_dh']) > 0)
321 _ip.magic('reset -f dhist')
321 _ip.magic('reset -f dhist')
322 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
322 nt.assert_equal(len(_ip.user_ns['_dh']), 0)
323 _ip.run_cell("_dh = [d for d in tmp]") #restore
323 _ip.run_cell("_dh = [d for d in tmp]") #restore
324
324
325 def test_reset_in_length():
325 def test_reset_in_length():
326 "Test that '%reset in' preserves In[] length"
326 "Test that '%reset in' preserves In[] length"
327 _ip.run_cell("print 'foo'")
327 _ip.run_cell("print 'foo'")
328 _ip.run_cell("reset -f in")
328 _ip.run_cell("reset -f in")
329 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
329 nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1)
330
330
331 def test_tb_syntaxerror():
331 def test_tb_syntaxerror():
332 """test %tb after a SyntaxError"""
332 """test %tb after a SyntaxError"""
333 ip = get_ipython()
333 ip = get_ipython()
334 ip.run_cell("for")
334 ip.run_cell("for")
335
335
336 # trap and validate stdout
336 # trap and validate stdout
337 save_stdout = sys.stdout
337 save_stdout = sys.stdout
338 try:
338 try:
339 sys.stdout = StringIO()
339 sys.stdout = StringIO()
340 ip.run_cell("%tb")
340 ip.run_cell("%tb")
341 out = sys.stdout.getvalue()
341 out = sys.stdout.getvalue()
342 finally:
342 finally:
343 sys.stdout = save_stdout
343 sys.stdout = save_stdout
344 # trim output, and only check the last line
344 # trim output, and only check the last line
345 last_line = out.rstrip().splitlines()[-1].strip()
345 last_line = out.rstrip().splitlines()[-1].strip()
346 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
346 nt.assert_equal(last_line, "SyntaxError: invalid syntax")
347
347
348
348
349 def test_time():
349 def test_time():
350 ip = get_ipython()
350 ip = get_ipython()
351
351
352 with tt.AssertPrints("Wall time: "):
352 with tt.AssertPrints("Wall time: "):
353 ip.run_cell("%time None")
353 ip.run_cell("%time None")
354
354
355 ip.run_cell("def f(kmjy):\n"
355 ip.run_cell("def f(kmjy):\n"
356 " %time print (2*kmjy)")
356 " %time print (2*kmjy)")
357
357
358 with tt.AssertPrints("Wall time: "):
358 with tt.AssertPrints("Wall time: "):
359 with tt.AssertPrints("hihi", suppress=False):
359 with tt.AssertPrints("hihi", suppress=False):
360 ip.run_cell("f('hi')")
360 ip.run_cell("f('hi')")
361
361
362
362
363 @dec.skip_win32
363 @dec.skip_win32
364 def test_time2():
364 def test_time2():
365 ip = get_ipython()
365 ip = get_ipython()
366
366
367 with tt.AssertPrints("CPU times: user "):
367 with tt.AssertPrints("CPU times: user "):
368 ip.run_cell("%time None")
368 ip.run_cell("%time None")
369
369
370 def test_time3():
370 def test_time3():
371 """Erroneous magic function calls, issue gh-3334"""
371 """Erroneous magic function calls, issue gh-3334"""
372 ip = get_ipython()
372 ip = get_ipython()
373 ip.user_ns.pop('run', None)
373 ip.user_ns.pop('run', None)
374
374
375 with tt.AssertNotPrints("not found", channel='stderr'):
375 with tt.AssertNotPrints("not found", channel='stderr'):
376 ip.run_cell("%%time\n"
376 ip.run_cell("%%time\n"
377 "run = 0\n"
377 "run = 0\n"
378 "run += 1")
378 "run += 1")
379
379
380 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
381 def test_time_futures():
382 "Test %time with __future__ environments"
383 ip = get_ipython()
384 ip.autocall = 0
385 ip.run_cell("from __future__ import division")
386 with tt.AssertPrints('0.25'):
387 ip.run_line_magic('time', 'print(1/4)')
388 ip.compile.reset_compiler_flags()
389 with tt.AssertNotPrints('0.25'):
390 ip.run_line_magic('time', 'print(1/4)')
391
392 def test_doctest_mode():
380 def test_doctest_mode():
393 "Toggle doctest_mode twice, it should be a no-op and run without error"
381 "Toggle doctest_mode twice, it should be a no-op and run without error"
394 _ip.magic('doctest_mode')
382 _ip.magic('doctest_mode')
395 _ip.magic('doctest_mode')
383 _ip.magic('doctest_mode')
396
384
397
385
398 def test_parse_options():
386 def test_parse_options():
399 """Tests for basic options parsing in magics."""
387 """Tests for basic options parsing in magics."""
400 # These are only the most minimal of tests, more should be added later. At
388 # These are only the most minimal of tests, more should be added later. At
401 # the very least we check that basic text/unicode calls work OK.
389 # the very least we check that basic text/unicode calls work OK.
402 m = DummyMagics(_ip)
390 m = DummyMagics(_ip)
403 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
391 nt.assert_equal(m.parse_options('foo', '')[1], 'foo')
404 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
392 nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo')
405
393
406
394
407 def test_dirops():
395 def test_dirops():
408 """Test various directory handling operations."""
396 """Test various directory handling operations."""
409 # curpath = lambda :os.path.splitdrive(py3compat.getcwd())[1].replace('\\','/')
397 # curpath = lambda :os.path.splitdrive(py3compat.getcwd())[1].replace('\\','/')
410 curpath = py3compat.getcwd
398 curpath = py3compat.getcwd
411 startdir = py3compat.getcwd()
399 startdir = py3compat.getcwd()
412 ipdir = os.path.realpath(_ip.ipython_dir)
400 ipdir = os.path.realpath(_ip.ipython_dir)
413 try:
401 try:
414 _ip.magic('cd "%s"' % ipdir)
402 _ip.magic('cd "%s"' % ipdir)
415 nt.assert_equal(curpath(), ipdir)
403 nt.assert_equal(curpath(), ipdir)
416 _ip.magic('cd -')
404 _ip.magic('cd -')
417 nt.assert_equal(curpath(), startdir)
405 nt.assert_equal(curpath(), startdir)
418 _ip.magic('pushd "%s"' % ipdir)
406 _ip.magic('pushd "%s"' % ipdir)
419 nt.assert_equal(curpath(), ipdir)
407 nt.assert_equal(curpath(), ipdir)
420 _ip.magic('popd')
408 _ip.magic('popd')
421 nt.assert_equal(curpath(), startdir)
409 nt.assert_equal(curpath(), startdir)
422 finally:
410 finally:
423 os.chdir(startdir)
411 os.chdir(startdir)
424
412
425
413
426 def test_xmode():
414 def test_xmode():
427 # Calling xmode three times should be a no-op
415 # Calling xmode three times should be a no-op
428 xmode = _ip.InteractiveTB.mode
416 xmode = _ip.InteractiveTB.mode
429 for i in range(3):
417 for i in range(3):
430 _ip.magic("xmode")
418 _ip.magic("xmode")
431 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
419 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
432
420
433 def test_reset_hard():
421 def test_reset_hard():
434 monitor = []
422 monitor = []
435 class A(object):
423 class A(object):
436 def __del__(self):
424 def __del__(self):
437 monitor.append(1)
425 monitor.append(1)
438 def __repr__(self):
426 def __repr__(self):
439 return "<A instance>"
427 return "<A instance>"
440
428
441 _ip.user_ns["a"] = A()
429 _ip.user_ns["a"] = A()
442 _ip.run_cell("a")
430 _ip.run_cell("a")
443
431
444 nt.assert_equal(monitor, [])
432 nt.assert_equal(monitor, [])
445 _ip.magic("reset -f")
433 _ip.magic("reset -f")
446 nt.assert_equal(monitor, [1])
434 nt.assert_equal(monitor, [1])
447
435
448 class TestXdel(tt.TempFileMixin):
436 class TestXdel(tt.TempFileMixin):
449 def test_xdel(self):
437 def test_xdel(self):
450 """Test that references from %run are cleared by xdel."""
438 """Test that references from %run are cleared by xdel."""
451 src = ("class A(object):\n"
439 src = ("class A(object):\n"
452 " monitor = []\n"
440 " monitor = []\n"
453 " def __del__(self):\n"
441 " def __del__(self):\n"
454 " self.monitor.append(1)\n"
442 " self.monitor.append(1)\n"
455 "a = A()\n")
443 "a = A()\n")
456 self.mktmp(src)
444 self.mktmp(src)
457 # %run creates some hidden references...
445 # %run creates some hidden references...
458 _ip.magic("run %s" % self.fname)
446 _ip.magic("run %s" % self.fname)
459 # ... as does the displayhook.
447 # ... as does the displayhook.
460 _ip.run_cell("a")
448 _ip.run_cell("a")
461
449
462 monitor = _ip.user_ns["A"].monitor
450 monitor = _ip.user_ns["A"].monitor
463 nt.assert_equal(monitor, [])
451 nt.assert_equal(monitor, [])
464
452
465 _ip.magic("xdel a")
453 _ip.magic("xdel a")
466
454
467 # Check that a's __del__ method has been called.
455 # Check that a's __del__ method has been called.
468 nt.assert_equal(monitor, [1])
456 nt.assert_equal(monitor, [1])
469
457
470 def doctest_who():
458 def doctest_who():
471 """doctest for %who
459 """doctest for %who
472
460
473 In [1]: %reset -f
461 In [1]: %reset -f
474
462
475 In [2]: alpha = 123
463 In [2]: alpha = 123
476
464
477 In [3]: beta = 'beta'
465 In [3]: beta = 'beta'
478
466
479 In [4]: %who int
467 In [4]: %who int
480 alpha
468 alpha
481
469
482 In [5]: %who str
470 In [5]: %who str
483 beta
471 beta
484
472
485 In [6]: %whos
473 In [6]: %whos
486 Variable Type Data/Info
474 Variable Type Data/Info
487 ----------------------------
475 ----------------------------
488 alpha int 123
476 alpha int 123
489 beta str beta
477 beta str beta
490
478
491 In [7]: %who_ls
479 In [7]: %who_ls
492 Out[7]: ['alpha', 'beta']
480 Out[7]: ['alpha', 'beta']
493 """
481 """
494
482
495 def test_whos():
483 def test_whos():
496 """Check that whos is protected against objects where repr() fails."""
484 """Check that whos is protected against objects where repr() fails."""
497 class A(object):
485 class A(object):
498 def __repr__(self):
486 def __repr__(self):
499 raise Exception()
487 raise Exception()
500 _ip.user_ns['a'] = A()
488 _ip.user_ns['a'] = A()
501 _ip.magic("whos")
489 _ip.magic("whos")
502
490
503 @py3compat.u_format
491 @py3compat.u_format
504 def doctest_precision():
492 def doctest_precision():
505 """doctest for %precision
493 """doctest for %precision
506
494
507 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
495 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
508
496
509 In [2]: %precision 5
497 In [2]: %precision 5
510 Out[2]: {u}'%.5f'
498 Out[2]: {u}'%.5f'
511
499
512 In [3]: f.float_format
500 In [3]: f.float_format
513 Out[3]: {u}'%.5f'
501 Out[3]: {u}'%.5f'
514
502
515 In [4]: %precision %e
503 In [4]: %precision %e
516 Out[4]: {u}'%e'
504 Out[4]: {u}'%e'
517
505
518 In [5]: f(3.1415927)
506 In [5]: f(3.1415927)
519 Out[5]: {u}'3.141593e+00'
507 Out[5]: {u}'3.141593e+00'
520 """
508 """
521
509
522 def test_psearch():
510 def test_psearch():
523 with tt.AssertPrints("dict.fromkeys"):
511 with tt.AssertPrints("dict.fromkeys"):
524 _ip.run_cell("dict.fr*?")
512 _ip.run_cell("dict.fr*?")
525
513
526 def test_timeit_shlex():
514 def test_timeit_shlex():
527 """test shlex issues with timeit (#1109)"""
515 """test shlex issues with timeit (#1109)"""
528 _ip.ex("def f(*a,**kw): pass")
516 _ip.ex("def f(*a,**kw): pass")
529 _ip.magic('timeit -n1 "this is a bug".count(" ")')
517 _ip.magic('timeit -n1 "this is a bug".count(" ")')
530 _ip.magic('timeit -r1 -n1 f(" ", 1)')
518 _ip.magic('timeit -r1 -n1 f(" ", 1)')
531 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
519 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
532 _ip.magic('timeit -r1 -n1 ("a " + "b")')
520 _ip.magic('timeit -r1 -n1 ("a " + "b")')
533 _ip.magic('timeit -r1 -n1 f("a " + "b")')
521 _ip.magic('timeit -r1 -n1 f("a " + "b")')
534 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
522 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
535
523
536
524
537 def test_timeit_arguments():
525 def test_timeit_arguments():
538 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
526 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
539 _ip.magic("timeit ('#')")
527 _ip.magic("timeit ('#')")
540
528
541
529
542 def test_timeit_special_syntax():
530 def test_timeit_special_syntax():
543 "Test %%timeit with IPython special syntax"
531 "Test %%timeit with IPython special syntax"
544 @register_line_magic
532 @register_line_magic
545 def lmagic(line):
533 def lmagic(line):
546 ip = get_ipython()
534 ip = get_ipython()
547 ip.user_ns['lmagic_out'] = line
535 ip.user_ns['lmagic_out'] = line
548
536
549 # line mode test
537 # line mode test
550 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
538 _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line')
551 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
539 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
552 # cell mode test
540 # cell mode test
553 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
541 _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2')
554 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
542 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
555
543
556 def test_timeit_return():
544 def test_timeit_return():
557 """
545 """
558 test wether timeit -o return object
546 test wether timeit -o return object
559 """
547 """
560
548
561 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
549 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
562 assert(res is not None)
550 assert(res is not None)
563
551
564 def test_timeit_quiet():
552 def test_timeit_quiet():
565 """
553 """
566 test quiet option of timeit magic
554 test quiet option of timeit magic
567 """
555 """
568 with tt.AssertNotPrints("loops"):
556 with tt.AssertNotPrints("loops"):
569 _ip.run_cell("%timeit -n1 -r1 -q 1")
557 _ip.run_cell("%timeit -n1 -r1 -q 1")
570
558
571 def test_timeit_return_quiet():
559 def test_timeit_return_quiet():
572 with tt.AssertNotPrints("loops"):
560 with tt.AssertNotPrints("loops"):
573 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
561 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
574 assert (res is not None)
562 assert (res is not None)
575
563
576 @dec.skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
577 def test_timeit_futures():
578 "Test %timeit with __future__ environments"
579 ip = get_ipython()
580 ip.run_cell("from __future__ import division")
581 with tt.AssertPrints('0.25'):
582 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
583 ip.compile.reset_compiler_flags()
584 with tt.AssertNotPrints('0.25'):
585 ip.run_line_magic('timeit', '-n1 -r1 print(1/4)')
586
587 @dec.skipif(execution.profile is None)
564 @dec.skipif(execution.profile is None)
588 def test_prun_special_syntax():
565 def test_prun_special_syntax():
589 "Test %%prun with IPython special syntax"
566 "Test %%prun with IPython special syntax"
590 @register_line_magic
567 @register_line_magic
591 def lmagic(line):
568 def lmagic(line):
592 ip = get_ipython()
569 ip = get_ipython()
593 ip.user_ns['lmagic_out'] = line
570 ip.user_ns['lmagic_out'] = line
594
571
595 # line mode test
572 # line mode test
596 _ip.run_line_magic('prun', '-q %lmagic my line')
573 _ip.run_line_magic('prun', '-q %lmagic my line')
597 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
574 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line')
598 # cell mode test
575 # cell mode test
599 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
576 _ip.run_cell_magic('prun', '-q', '%lmagic my line2')
600 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
577 nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2')
601
578
602 @dec.skipif(execution.profile is None)
579 @dec.skipif(execution.profile is None)
603 def test_prun_quotes():
580 def test_prun_quotes():
604 "Test that prun does not clobber string escapes (GH #1302)"
581 "Test that prun does not clobber string escapes (GH #1302)"
605 _ip.magic(r"prun -q x = '\t'")
582 _ip.magic(r"prun -q x = '\t'")
606 nt.assert_equal(_ip.user_ns['x'], '\t')
583 nt.assert_equal(_ip.user_ns['x'], '\t')
607
584
608 def test_extension():
585 def test_extension():
609 # Debugging information for failures of this test
586 # Debugging information for failures of this test
610 print('sys.path:')
587 print('sys.path:')
611 for p in sys.path:
588 for p in sys.path:
612 print(' ', p)
589 print(' ', p)
613 print('CWD', os.getcwd())
590 print('CWD', os.getcwd())
614
591
615 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
592 nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension")
616 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
593 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
617 sys.path.insert(0, daft_path)
594 sys.path.insert(0, daft_path)
618 try:
595 try:
619 _ip.user_ns.pop('arq', None)
596 _ip.user_ns.pop('arq', None)
620 invalidate_caches() # Clear import caches
597 invalidate_caches() # Clear import caches
621 _ip.magic("load_ext daft_extension")
598 _ip.magic("load_ext daft_extension")
622 nt.assert_equal(_ip.user_ns['arq'], 185)
599 nt.assert_equal(_ip.user_ns['arq'], 185)
623 _ip.magic("unload_ext daft_extension")
600 _ip.magic("unload_ext daft_extension")
624 assert 'arq' not in _ip.user_ns
601 assert 'arq' not in _ip.user_ns
625 finally:
602 finally:
626 sys.path.remove(daft_path)
603 sys.path.remove(daft_path)
627
604
628
605
629 def test_notebook_export_json():
606 def test_notebook_export_json():
630 _ip = get_ipython()
607 _ip = get_ipython()
631 _ip.history_manager.reset() # Clear any existing history.
608 _ip.history_manager.reset() # Clear any existing history.
632 cmds = [u"a=1", u"def b():\n return a**2", u"print('noël, été', b())"]
609 cmds = [u"a=1", u"def b():\n return a**2", u"print('noël, été', b())"]
633 for i, cmd in enumerate(cmds, start=1):
610 for i, cmd in enumerate(cmds, start=1):
634 _ip.history_manager.store_inputs(i, cmd)
611 _ip.history_manager.store_inputs(i, cmd)
635 with TemporaryDirectory() as td:
612 with TemporaryDirectory() as td:
636 outfile = os.path.join(td, "nb.ipynb")
613 outfile = os.path.join(td, "nb.ipynb")
637 _ip.magic("notebook -e %s" % outfile)
614 _ip.magic("notebook -e %s" % outfile)
638
615
639
616
640 class TestEnv(TestCase):
617 class TestEnv(TestCase):
641
618
642 def test_env(self):
619 def test_env(self):
643 env = _ip.magic("env")
620 env = _ip.magic("env")
644 self.assertTrue(isinstance(env, dict))
621 self.assertTrue(isinstance(env, dict))
645
622
646 def test_env_get_set_simple(self):
623 def test_env_get_set_simple(self):
647 env = _ip.magic("env var val1")
624 env = _ip.magic("env var val1")
648 self.assertEqual(env, None)
625 self.assertEqual(env, None)
649 self.assertEqual(os.environ['var'], 'val1')
626 self.assertEqual(os.environ['var'], 'val1')
650 self.assertEqual(_ip.magic("env var"), 'val1')
627 self.assertEqual(_ip.magic("env var"), 'val1')
651 env = _ip.magic("env var=val2")
628 env = _ip.magic("env var=val2")
652 self.assertEqual(env, None)
629 self.assertEqual(env, None)
653 self.assertEqual(os.environ['var'], 'val2')
630 self.assertEqual(os.environ['var'], 'val2')
654
631
655 def test_env_get_set_complex(self):
632 def test_env_get_set_complex(self):
656 env = _ip.magic("env var 'val1 '' 'val2")
633 env = _ip.magic("env var 'val1 '' 'val2")
657 self.assertEqual(env, None)
634 self.assertEqual(env, None)
658 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
635 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
659 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
636 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
660 env = _ip.magic('env var=val2 val3="val4')
637 env = _ip.magic('env var=val2 val3="val4')
661 self.assertEqual(env, None)
638 self.assertEqual(env, None)
662 self.assertEqual(os.environ['var'], 'val2 val3="val4')
639 self.assertEqual(os.environ['var'], 'val2 val3="val4')
663
640
664 def test_env_set_bad_input(self):
641 def test_env_set_bad_input(self):
665 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
642 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
666
643
667 def test_env_set_whitespace(self):
644 def test_env_set_whitespace(self):
668 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
645 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
669
646
670
647
671 class CellMagicTestCase(TestCase):
648 class CellMagicTestCase(TestCase):
672
649
673 def check_ident(self, magic):
650 def check_ident(self, magic):
674 # Manually called, we get the result
651 # Manually called, we get the result
675 out = _ip.run_cell_magic(magic, 'a', 'b')
652 out = _ip.run_cell_magic(magic, 'a', 'b')
676 nt.assert_equal(out, ('a','b'))
653 nt.assert_equal(out, ('a','b'))
677 # Via run_cell, it goes into the user's namespace via displayhook
654 # Via run_cell, it goes into the user's namespace via displayhook
678 _ip.run_cell('%%' + magic +' c\nd')
655 _ip.run_cell('%%' + magic +' c\nd')
679 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
656 nt.assert_equal(_ip.user_ns['_'], ('c','d'))
680
657
681 def test_cell_magic_func_deco(self):
658 def test_cell_magic_func_deco(self):
682 "Cell magic using simple decorator"
659 "Cell magic using simple decorator"
683 @register_cell_magic
660 @register_cell_magic
684 def cellm(line, cell):
661 def cellm(line, cell):
685 return line, cell
662 return line, cell
686
663
687 self.check_ident('cellm')
664 self.check_ident('cellm')
688
665
689 def test_cell_magic_reg(self):
666 def test_cell_magic_reg(self):
690 "Cell magic manually registered"
667 "Cell magic manually registered"
691 def cellm(line, cell):
668 def cellm(line, cell):
692 return line, cell
669 return line, cell
693
670
694 _ip.register_magic_function(cellm, 'cell', 'cellm2')
671 _ip.register_magic_function(cellm, 'cell', 'cellm2')
695 self.check_ident('cellm2')
672 self.check_ident('cellm2')
696
673
697 def test_cell_magic_class(self):
674 def test_cell_magic_class(self):
698 "Cell magics declared via a class"
675 "Cell magics declared via a class"
699 @magics_class
676 @magics_class
700 class MyMagics(Magics):
677 class MyMagics(Magics):
701
678
702 @cell_magic
679 @cell_magic
703 def cellm3(self, line, cell):
680 def cellm3(self, line, cell):
704 return line, cell
681 return line, cell
705
682
706 _ip.register_magics(MyMagics)
683 _ip.register_magics(MyMagics)
707 self.check_ident('cellm3')
684 self.check_ident('cellm3')
708
685
709 def test_cell_magic_class2(self):
686 def test_cell_magic_class2(self):
710 "Cell magics declared via a class, #2"
687 "Cell magics declared via a class, #2"
711 @magics_class
688 @magics_class
712 class MyMagics2(Magics):
689 class MyMagics2(Magics):
713
690
714 @cell_magic('cellm4')
691 @cell_magic('cellm4')
715 def cellm33(self, line, cell):
692 def cellm33(self, line, cell):
716 return line, cell
693 return line, cell
717
694
718 _ip.register_magics(MyMagics2)
695 _ip.register_magics(MyMagics2)
719 self.check_ident('cellm4')
696 self.check_ident('cellm4')
720 # Check that nothing is registered as 'cellm33'
697 # Check that nothing is registered as 'cellm33'
721 c33 = _ip.find_cell_magic('cellm33')
698 c33 = _ip.find_cell_magic('cellm33')
722 nt.assert_equal(c33, None)
699 nt.assert_equal(c33, None)
723
700
724 def test_file():
701 def test_file():
725 """Basic %%file"""
702 """Basic %%file"""
726 ip = get_ipython()
703 ip = get_ipython()
727 with TemporaryDirectory() as td:
704 with TemporaryDirectory() as td:
728 fname = os.path.join(td, 'file1')
705 fname = os.path.join(td, 'file1')
729 ip.run_cell_magic("file", fname, u'\n'.join([
706 ip.run_cell_magic("file", fname, u'\n'.join([
730 'line1',
707 'line1',
731 'line2',
708 'line2',
732 ]))
709 ]))
733 with open(fname) as f:
710 with open(fname) as f:
734 s = f.read()
711 s = f.read()
735 nt.assert_in('line1\n', s)
712 nt.assert_in('line1\n', s)
736 nt.assert_in('line2', s)
713 nt.assert_in('line2', s)
737
714
738 def test_file_var_expand():
715 def test_file_var_expand():
739 """%%file $filename"""
716 """%%file $filename"""
740 ip = get_ipython()
717 ip = get_ipython()
741 with TemporaryDirectory() as td:
718 with TemporaryDirectory() as td:
742 fname = os.path.join(td, 'file1')
719 fname = os.path.join(td, 'file1')
743 ip.user_ns['filename'] = fname
720 ip.user_ns['filename'] = fname
744 ip.run_cell_magic("file", '$filename', u'\n'.join([
721 ip.run_cell_magic("file", '$filename', u'\n'.join([
745 'line1',
722 'line1',
746 'line2',
723 'line2',
747 ]))
724 ]))
748 with open(fname) as f:
725 with open(fname) as f:
749 s = f.read()
726 s = f.read()
750 nt.assert_in('line1\n', s)
727 nt.assert_in('line1\n', s)
751 nt.assert_in('line2', s)
728 nt.assert_in('line2', s)
752
729
753 def test_file_unicode():
730 def test_file_unicode():
754 """%%file with unicode cell"""
731 """%%file with unicode cell"""
755 ip = get_ipython()
732 ip = get_ipython()
756 with TemporaryDirectory() as td:
733 with TemporaryDirectory() as td:
757 fname = os.path.join(td, 'file1')
734 fname = os.path.join(td, 'file1')
758 ip.run_cell_magic("file", fname, u'\n'.join([
735 ip.run_cell_magic("file", fname, u'\n'.join([
759 u'liné1',
736 u'liné1',
760 u'liné2',
737 u'liné2',
761 ]))
738 ]))
762 with io.open(fname, encoding='utf-8') as f:
739 with io.open(fname, encoding='utf-8') as f:
763 s = f.read()
740 s = f.read()
764 nt.assert_in(u'liné1\n', s)
741 nt.assert_in(u'liné1\n', s)
765 nt.assert_in(u'liné2', s)
742 nt.assert_in(u'liné2', s)
766
743
767 def test_file_amend():
744 def test_file_amend():
768 """%%file -a amends files"""
745 """%%file -a amends files"""
769 ip = get_ipython()
746 ip = get_ipython()
770 with TemporaryDirectory() as td:
747 with TemporaryDirectory() as td:
771 fname = os.path.join(td, 'file2')
748 fname = os.path.join(td, 'file2')
772 ip.run_cell_magic("file", fname, u'\n'.join([
749 ip.run_cell_magic("file", fname, u'\n'.join([
773 'line1',
750 'line1',
774 'line2',
751 'line2',
775 ]))
752 ]))
776 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
753 ip.run_cell_magic("file", "-a %s" % fname, u'\n'.join([
777 'line3',
754 'line3',
778 'line4',
755 'line4',
779 ]))
756 ]))
780 with open(fname) as f:
757 with open(fname) as f:
781 s = f.read()
758 s = f.read()
782 nt.assert_in('line1\n', s)
759 nt.assert_in('line1\n', s)
783 nt.assert_in('line3\n', s)
760 nt.assert_in('line3\n', s)
784
761
785
762
786 def test_script_config():
763 def test_script_config():
787 ip = get_ipython()
764 ip = get_ipython()
788 ip.config.ScriptMagics.script_magics = ['whoda']
765 ip.config.ScriptMagics.script_magics = ['whoda']
789 sm = script.ScriptMagics(shell=ip)
766 sm = script.ScriptMagics(shell=ip)
790 nt.assert_in('whoda', sm.magics['cell'])
767 nt.assert_in('whoda', sm.magics['cell'])
791
768
792 @dec.skip_win32
769 @dec.skip_win32
793 def test_script_out():
770 def test_script_out():
794 ip = get_ipython()
771 ip = get_ipython()
795 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
772 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
796 nt.assert_equal(ip.user_ns['output'], 'hi\n')
773 nt.assert_equal(ip.user_ns['output'], 'hi\n')
797
774
798 @dec.skip_win32
775 @dec.skip_win32
799 def test_script_err():
776 def test_script_err():
800 ip = get_ipython()
777 ip = get_ipython()
801 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
778 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
802 nt.assert_equal(ip.user_ns['error'], 'hello\n')
779 nt.assert_equal(ip.user_ns['error'], 'hello\n')
803
780
804 @dec.skip_win32
781 @dec.skip_win32
805 def test_script_out_err():
782 def test_script_out_err():
806 ip = get_ipython()
783 ip = get_ipython()
807 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
784 ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2")
808 nt.assert_equal(ip.user_ns['output'], 'hi\n')
785 nt.assert_equal(ip.user_ns['output'], 'hi\n')
809 nt.assert_equal(ip.user_ns['error'], 'hello\n')
786 nt.assert_equal(ip.user_ns['error'], 'hello\n')
810
787
811 @dec.skip_win32
788 @dec.skip_win32
812 def test_script_bg_out():
789 def test_script_bg_out():
813 ip = get_ipython()
790 ip = get_ipython()
814 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
791 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
815 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
792 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
816
793
817 @dec.skip_win32
794 @dec.skip_win32
818 def test_script_bg_err():
795 def test_script_bg_err():
819 ip = get_ipython()
796 ip = get_ipython()
820 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
797 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
821 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
798 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
822
799
823 @dec.skip_win32
800 @dec.skip_win32
824 def test_script_bg_out_err():
801 def test_script_bg_out_err():
825 ip = get_ipython()
802 ip = get_ipython()
826 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
803 ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2")
827 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
804 nt.assert_equal(ip.user_ns['output'].read(), b'hi\n')
828 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
805 nt.assert_equal(ip.user_ns['error'].read(), b'hello\n')
829
806
830 def test_script_defaults():
807 def test_script_defaults():
831 ip = get_ipython()
808 ip = get_ipython()
832 for cmd in ['sh', 'bash', 'perl', 'ruby']:
809 for cmd in ['sh', 'bash', 'perl', 'ruby']:
833 try:
810 try:
834 find_cmd(cmd)
811 find_cmd(cmd)
835 except Exception:
812 except Exception:
836 pass
813 pass
837 else:
814 else:
838 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
815 nt.assert_in(cmd, ip.magics_manager.magics['cell'])
839
816
840
817
841 @magics_class
818 @magics_class
842 class FooFoo(Magics):
819 class FooFoo(Magics):
843 """class with both %foo and %%foo magics"""
820 """class with both %foo and %%foo magics"""
844 @line_magic('foo')
821 @line_magic('foo')
845 def line_foo(self, line):
822 def line_foo(self, line):
846 "I am line foo"
823 "I am line foo"
847 pass
824 pass
848
825
849 @cell_magic("foo")
826 @cell_magic("foo")
850 def cell_foo(self, line, cell):
827 def cell_foo(self, line, cell):
851 "I am cell foo, not line foo"
828 "I am cell foo, not line foo"
852 pass
829 pass
853
830
854 def test_line_cell_info():
831 def test_line_cell_info():
855 """%%foo and %foo magics are distinguishable to inspect"""
832 """%%foo and %foo magics are distinguishable to inspect"""
856 ip = get_ipython()
833 ip = get_ipython()
857 ip.magics_manager.register(FooFoo)
834 ip.magics_manager.register(FooFoo)
858 oinfo = ip.object_inspect('foo')
835 oinfo = ip.object_inspect('foo')
859 nt.assert_true(oinfo['found'])
836 nt.assert_true(oinfo['found'])
860 nt.assert_true(oinfo['ismagic'])
837 nt.assert_true(oinfo['ismagic'])
861
838
862 oinfo = ip.object_inspect('%%foo')
839 oinfo = ip.object_inspect('%%foo')
863 nt.assert_true(oinfo['found'])
840 nt.assert_true(oinfo['found'])
864 nt.assert_true(oinfo['ismagic'])
841 nt.assert_true(oinfo['ismagic'])
865 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
842 nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__)
866
843
867 oinfo = ip.object_inspect('%foo')
844 oinfo = ip.object_inspect('%foo')
868 nt.assert_true(oinfo['found'])
845 nt.assert_true(oinfo['found'])
869 nt.assert_true(oinfo['ismagic'])
846 nt.assert_true(oinfo['ismagic'])
870 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
847 nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__)
871
848
872 def test_multiple_magics():
849 def test_multiple_magics():
873 ip = get_ipython()
850 ip = get_ipython()
874 foo1 = FooFoo(ip)
851 foo1 = FooFoo(ip)
875 foo2 = FooFoo(ip)
852 foo2 = FooFoo(ip)
876 mm = ip.magics_manager
853 mm = ip.magics_manager
877 mm.register(foo1)
854 mm.register(foo1)
878 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
855 nt.assert_true(mm.magics['line']['foo'].__self__ is foo1)
879 mm.register(foo2)
856 mm.register(foo2)
880 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
857 nt.assert_true(mm.magics['line']['foo'].__self__ is foo2)
881
858
882 def test_alias_magic():
859 def test_alias_magic():
883 """Test %alias_magic."""
860 """Test %alias_magic."""
884 ip = get_ipython()
861 ip = get_ipython()
885 mm = ip.magics_manager
862 mm = ip.magics_manager
886
863
887 # Basic operation: both cell and line magics are created, if possible.
864 # Basic operation: both cell and line magics are created, if possible.
888 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
865 ip.run_line_magic('alias_magic', 'timeit_alias timeit')
889 nt.assert_in('timeit_alias', mm.magics['line'])
866 nt.assert_in('timeit_alias', mm.magics['line'])
890 nt.assert_in('timeit_alias', mm.magics['cell'])
867 nt.assert_in('timeit_alias', mm.magics['cell'])
891
868
892 # --cell is specified, line magic not created.
869 # --cell is specified, line magic not created.
893 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
870 ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit')
894 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
871 nt.assert_not_in('timeit_cell_alias', mm.magics['line'])
895 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
872 nt.assert_in('timeit_cell_alias', mm.magics['cell'])
896
873
897 # Test that line alias is created successfully.
874 # Test that line alias is created successfully.
898 ip.run_line_magic('alias_magic', '--line env_alias env')
875 ip.run_line_magic('alias_magic', '--line env_alias env')
899 nt.assert_equal(ip.run_line_magic('env', ''),
876 nt.assert_equal(ip.run_line_magic('env', ''),
900 ip.run_line_magic('env_alias', ''))
877 ip.run_line_magic('env_alias', ''))
901
878
902 def test_save():
879 def test_save():
903 """Test %save."""
880 """Test %save."""
904 ip = get_ipython()
881 ip = get_ipython()
905 ip.history_manager.reset() # Clear any existing history.
882 ip.history_manager.reset() # Clear any existing history.
906 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
883 cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"]
907 for i, cmd in enumerate(cmds, start=1):
884 for i, cmd in enumerate(cmds, start=1):
908 ip.history_manager.store_inputs(i, cmd)
885 ip.history_manager.store_inputs(i, cmd)
909 with TemporaryDirectory() as tmpdir:
886 with TemporaryDirectory() as tmpdir:
910 file = os.path.join(tmpdir, "testsave.py")
887 file = os.path.join(tmpdir, "testsave.py")
911 ip.run_line_magic("save", "%s 1-10" % file)
888 ip.run_line_magic("save", "%s 1-10" % file)
912 with open(file) as f:
889 with open(file) as f:
913 content = f.read()
890 content = f.read()
914 nt.assert_equal(content.count(cmds[0]), 1)
891 nt.assert_equal(content.count(cmds[0]), 1)
915 nt.assert_in('coding: utf-8', content)
892 nt.assert_in('coding: utf-8', content)
916 ip.run_line_magic("save", "-a %s 1-10" % file)
893 ip.run_line_magic("save", "-a %s 1-10" % file)
917 with open(file) as f:
894 with open(file) as f:
918 content = f.read()
895 content = f.read()
919 nt.assert_equal(content.count(cmds[0]), 2)
896 nt.assert_equal(content.count(cmds[0]), 2)
920 nt.assert_in('coding: utf-8', content)
897 nt.assert_in('coding: utf-8', content)
921
898
922
899
923 def test_store():
900 def test_store():
924 """Test %store."""
901 """Test %store."""
925 ip = get_ipython()
902 ip = get_ipython()
926 ip.run_line_magic('load_ext', 'storemagic')
903 ip.run_line_magic('load_ext', 'storemagic')
927
904
928 # make sure the storage is empty
905 # make sure the storage is empty
929 ip.run_line_magic('store', '-z')
906 ip.run_line_magic('store', '-z')
930 ip.user_ns['var'] = 42
907 ip.user_ns['var'] = 42
931 ip.run_line_magic('store', 'var')
908 ip.run_line_magic('store', 'var')
932 ip.user_ns['var'] = 39
909 ip.user_ns['var'] = 39
933 ip.run_line_magic('store', '-r')
910 ip.run_line_magic('store', '-r')
934 nt.assert_equal(ip.user_ns['var'], 42)
911 nt.assert_equal(ip.user_ns['var'], 42)
935
912
936 ip.run_line_magic('store', '-d var')
913 ip.run_line_magic('store', '-d var')
937 ip.user_ns['var'] = 39
914 ip.user_ns['var'] = 39
938 ip.run_line_magic('store' , '-r')
915 ip.run_line_magic('store' , '-r')
939 nt.assert_equal(ip.user_ns['var'], 39)
916 nt.assert_equal(ip.user_ns['var'], 39)
940
917
941
918
942 def _run_edit_test(arg_s, exp_filename=None,
919 def _run_edit_test(arg_s, exp_filename=None,
943 exp_lineno=-1,
920 exp_lineno=-1,
944 exp_contents=None,
921 exp_contents=None,
945 exp_is_temp=None):
922 exp_is_temp=None):
946 ip = get_ipython()
923 ip = get_ipython()
947 M = code.CodeMagics(ip)
924 M = code.CodeMagics(ip)
948 last_call = ['','']
925 last_call = ['','']
949 opts,args = M.parse_options(arg_s,'prxn:')
926 opts,args = M.parse_options(arg_s,'prxn:')
950 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
927 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
951
928
952 if exp_filename is not None:
929 if exp_filename is not None:
953 nt.assert_equal(exp_filename, filename)
930 nt.assert_equal(exp_filename, filename)
954 if exp_contents is not None:
931 if exp_contents is not None:
955 with io.open(filename, 'r', encoding='utf-8') as f:
932 with io.open(filename, 'r', encoding='utf-8') as f:
956 contents = f.read()
933 contents = f.read()
957 nt.assert_equal(exp_contents, contents)
934 nt.assert_equal(exp_contents, contents)
958 if exp_lineno != -1:
935 if exp_lineno != -1:
959 nt.assert_equal(exp_lineno, lineno)
936 nt.assert_equal(exp_lineno, lineno)
960 if exp_is_temp is not None:
937 if exp_is_temp is not None:
961 nt.assert_equal(exp_is_temp, is_temp)
938 nt.assert_equal(exp_is_temp, is_temp)
962
939
963
940
964 def test_edit_interactive():
941 def test_edit_interactive():
965 """%edit on interactively defined objects"""
942 """%edit on interactively defined objects"""
966 ip = get_ipython()
943 ip = get_ipython()
967 n = ip.execution_count
944 n = ip.execution_count
968 ip.run_cell(u"def foo(): return 1", store_history=True)
945 ip.run_cell(u"def foo(): return 1", store_history=True)
969
946
970 try:
947 try:
971 _run_edit_test("foo")
948 _run_edit_test("foo")
972 except code.InteractivelyDefined as e:
949 except code.InteractivelyDefined as e:
973 nt.assert_equal(e.index, n)
950 nt.assert_equal(e.index, n)
974 else:
951 else:
975 raise AssertionError("Should have raised InteractivelyDefined")
952 raise AssertionError("Should have raised InteractivelyDefined")
976
953
977
954
978 def test_edit_cell():
955 def test_edit_cell():
979 """%edit [cell id]"""
956 """%edit [cell id]"""
980 ip = get_ipython()
957 ip = get_ipython()
981
958
982 ip.run_cell(u"def foo(): return 1", store_history=True)
959 ip.run_cell(u"def foo(): return 1", store_history=True)
983
960
984 # test
961 # test
985 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
962 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
986
963
987 def test_bookmark():
964 def test_bookmark():
988 ip = get_ipython()
965 ip = get_ipython()
989 ip.run_line_magic('bookmark', 'bmname')
966 ip.run_line_magic('bookmark', 'bmname')
990 with tt.AssertPrints('bmname'):
967 with tt.AssertPrints('bmname'):
991 ip.run_line_magic('bookmark', '-l')
968 ip.run_line_magic('bookmark', '-l')
992 ip.run_line_magic('bookmark', '-d bmname')
969 ip.run_line_magic('bookmark', '-d bmname')
993
970
994 def test_ls_magic():
971 def test_ls_magic():
995 ip = get_ipython()
972 ip = get_ipython()
996 json_formatter = ip.display_formatter.formatters['application/json']
973 json_formatter = ip.display_formatter.formatters['application/json']
997 json_formatter.enabled = True
974 json_formatter.enabled = True
998 lsmagic = ip.magic('lsmagic')
975 lsmagic = ip.magic('lsmagic')
999 with warnings.catch_warnings(record=True) as w:
976 with warnings.catch_warnings(record=True) as w:
1000 j = json_formatter(lsmagic)
977 j = json_formatter(lsmagic)
1001 nt.assert_equal(sorted(j), ['cell', 'line'])
978 nt.assert_equal(sorted(j), ['cell', 'line'])
1002 nt.assert_equal(w, []) # no warnings
979 nt.assert_equal(w, []) # no warnings
1003
980
1004 def test_strip_initial_indent():
981 def test_strip_initial_indent():
1005 def sii(s):
982 def sii(s):
1006 lines = s.splitlines()
983 lines = s.splitlines()
1007 return '\n'.join(code.strip_initial_indent(lines))
984 return '\n'.join(code.strip_initial_indent(lines))
1008
985
1009 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
986 nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2")
1010 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
987 nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc")
1011 nt.assert_equal(sii("a\n b"), "a\n b")
988 nt.assert_equal(sii("a\n b"), "a\n b")
@@ -1,210 +1,203 b''
1 """Tests for various magic functions specific to the terminal frontend.
1 """Tests for various magic functions specific to the terminal frontend.
2
2
3 Needs to be run by nose (to make ipython session available).
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5 from __future__ import absolute_import
5 from __future__ import absolute_import
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Imports
8 # Imports
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 import sys
11 import sys
12 from unittest import TestCase
12 from unittest import TestCase
13
13
14 import nose.tools as nt
14 import nose.tools as nt
15
15
16 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
17 from IPython.utils.py3compat import PY3
17 from IPython.utils.py3compat import PY3
18
18
19 if PY3:
19 if PY3:
20 from io import StringIO
20 from io import StringIO
21 else:
21 else:
22 from StringIO import StringIO
22 from StringIO import StringIO
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Globals
25 # Globals
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 ip = get_ipython()
27 ip = get_ipython()
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Test functions begin
30 # Test functions begin
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 def check_cpaste(code, should_fail=False):
33 def check_cpaste(code, should_fail=False):
34 """Execute code via 'cpaste' and ensure it was executed, unless
34 """Execute code via 'cpaste' and ensure it was executed, unless
35 should_fail is set.
35 should_fail is set.
36 """
36 """
37 ip.user_ns['code_ran'] = False
37 ip.user_ns['code_ran'] = False
38
38
39 src = StringIO()
39 src = StringIO()
40 if not hasattr(src, 'encoding'):
40 if not hasattr(src, 'encoding'):
41 # IPython expects stdin to have an encoding attribute
41 # IPython expects stdin to have an encoding attribute
42 src.encoding = None
42 src.encoding = None
43 src.write(code)
43 src.write(code)
44 src.write('\n--\n')
44 src.write('\n--\n')
45 src.seek(0)
45 src.seek(0)
46
46
47 stdin_save = sys.stdin
47 stdin_save = sys.stdin
48 sys.stdin = src
48 sys.stdin = src
49
49
50 try:
50 try:
51 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
51 context = tt.AssertPrints if should_fail else tt.AssertNotPrints
52 with context("Traceback (most recent call last)"):
52 with context("Traceback (most recent call last)"):
53 ip.magic('cpaste')
53 ip.magic('cpaste')
54
54
55 if not should_fail:
55 if not should_fail:
56 assert ip.user_ns['code_ran'], "%r failed" % code
56 assert ip.user_ns['code_ran'], "%r failed" % code
57 finally:
57 finally:
58 sys.stdin = stdin_save
58 sys.stdin = stdin_save
59
59
60 PY31 = sys.version_info[:2] == (3,1)
61
62 def test_cpaste():
60 def test_cpaste():
63 """Test cpaste magic"""
61 """Test cpaste magic"""
64
62
65 def runf():
63 def runf():
66 """Marker function: sets a flag when executed.
64 """Marker function: sets a flag when executed.
67 """
65 """
68 ip.user_ns['code_ran'] = True
66 ip.user_ns['code_ran'] = True
69 return 'runf' # return string so '+ runf()' doesn't result in success
67 return 'runf' # return string so '+ runf()' doesn't result in success
70
68
71 tests = {'pass': ["runf()",
69 tests = {'pass': ["runf()",
72 "In [1]: runf()",
70 "In [1]: runf()",
73 "In [1]: if 1:\n ...: runf()",
71 "In [1]: if 1:\n ...: runf()",
74 "> > > runf()",
72 "> > > runf()",
75 ">>> runf()",
73 ">>> runf()",
76 " >>> runf()",
74 " >>> runf()",
77 ],
75 ],
78
76
79 'fail': ["1 + runf()",
77 'fail': ["1 + runf()",
78 "++ runf()",
80 ]}
79 ]}
81
80
82 # I don't know why this is failing specifically on Python 3.1. I've
83 # checked it manually interactively, but we don't care enough about 3.1
84 # to spend time fiddling with the tests, so we just skip it.
85 if not PY31:
86 tests['fail'].append("++ runf()")
87
88 ip.user_ns['runf'] = runf
81 ip.user_ns['runf'] = runf
89
82
90 for code in tests['pass']:
83 for code in tests['pass']:
91 check_cpaste(code)
84 check_cpaste(code)
92
85
93 for code in tests['fail']:
86 for code in tests['fail']:
94 check_cpaste(code, should_fail=True)
87 check_cpaste(code, should_fail=True)
95
88
96
89
97 class PasteTestCase(TestCase):
90 class PasteTestCase(TestCase):
98 """Multiple tests for clipboard pasting"""
91 """Multiple tests for clipboard pasting"""
99
92
100 def paste(self, txt, flags='-q'):
93 def paste(self, txt, flags='-q'):
101 """Paste input text, by default in quiet mode"""
94 """Paste input text, by default in quiet mode"""
102 ip.hooks.clipboard_get = lambda : txt
95 ip.hooks.clipboard_get = lambda : txt
103 ip.magic('paste '+flags)
96 ip.magic('paste '+flags)
104
97
105 def setUp(self):
98 def setUp(self):
106 # Inject fake clipboard hook but save original so we can restore it later
99 # Inject fake clipboard hook but save original so we can restore it later
107 self.original_clip = ip.hooks.clipboard_get
100 self.original_clip = ip.hooks.clipboard_get
108
101
109 def tearDown(self):
102 def tearDown(self):
110 # Restore original hook
103 # Restore original hook
111 ip.hooks.clipboard_get = self.original_clip
104 ip.hooks.clipboard_get = self.original_clip
112
105
113 def test_paste(self):
106 def test_paste(self):
114 ip.user_ns.pop('x', None)
107 ip.user_ns.pop('x', None)
115 self.paste('x = 1')
108 self.paste('x = 1')
116 nt.assert_equal(ip.user_ns['x'], 1)
109 nt.assert_equal(ip.user_ns['x'], 1)
117 ip.user_ns.pop('x')
110 ip.user_ns.pop('x')
118
111
119 def test_paste_pyprompt(self):
112 def test_paste_pyprompt(self):
120 ip.user_ns.pop('x', None)
113 ip.user_ns.pop('x', None)
121 self.paste('>>> x=2')
114 self.paste('>>> x=2')
122 nt.assert_equal(ip.user_ns['x'], 2)
115 nt.assert_equal(ip.user_ns['x'], 2)
123 ip.user_ns.pop('x')
116 ip.user_ns.pop('x')
124
117
125 def test_paste_py_multi(self):
118 def test_paste_py_multi(self):
126 self.paste("""
119 self.paste("""
127 >>> x = [1,2,3]
120 >>> x = [1,2,3]
128 >>> y = []
121 >>> y = []
129 >>> for i in x:
122 >>> for i in x:
130 ... y.append(i**2)
123 ... y.append(i**2)
131 ...
124 ...
132 """)
125 """)
133 nt.assert_equal(ip.user_ns['x'], [1,2,3])
126 nt.assert_equal(ip.user_ns['x'], [1,2,3])
134 nt.assert_equal(ip.user_ns['y'], [1,4,9])
127 nt.assert_equal(ip.user_ns['y'], [1,4,9])
135
128
136 def test_paste_py_multi_r(self):
129 def test_paste_py_multi_r(self):
137 "Now, test that self.paste -r works"
130 "Now, test that self.paste -r works"
138 self.test_paste_py_multi()
131 self.test_paste_py_multi()
139 nt.assert_equal(ip.user_ns.pop('x'), [1,2,3])
132 nt.assert_equal(ip.user_ns.pop('x'), [1,2,3])
140 nt.assert_equal(ip.user_ns.pop('y'), [1,4,9])
133 nt.assert_equal(ip.user_ns.pop('y'), [1,4,9])
141 nt.assert_false('x' in ip.user_ns)
134 nt.assert_false('x' in ip.user_ns)
142 ip.magic('paste -r')
135 ip.magic('paste -r')
143 nt.assert_equal(ip.user_ns['x'], [1,2,3])
136 nt.assert_equal(ip.user_ns['x'], [1,2,3])
144 nt.assert_equal(ip.user_ns['y'], [1,4,9])
137 nt.assert_equal(ip.user_ns['y'], [1,4,9])
145
138
146 def test_paste_email(self):
139 def test_paste_email(self):
147 "Test pasting of email-quoted contents"
140 "Test pasting of email-quoted contents"
148 self.paste("""\
141 self.paste("""\
149 >> def foo(x):
142 >> def foo(x):
150 >> return x + 1
143 >> return x + 1
151 >> xx = foo(1.1)""")
144 >> xx = foo(1.1)""")
152 nt.assert_equal(ip.user_ns['xx'], 2.1)
145 nt.assert_equal(ip.user_ns['xx'], 2.1)
153
146
154 def test_paste_email2(self):
147 def test_paste_email2(self):
155 "Email again; some programs add a space also at each quoting level"
148 "Email again; some programs add a space also at each quoting level"
156 self.paste("""\
149 self.paste("""\
157 > > def foo(x):
150 > > def foo(x):
158 > > return x + 1
151 > > return x + 1
159 > > yy = foo(2.1) """)
152 > > yy = foo(2.1) """)
160 nt.assert_equal(ip.user_ns['yy'], 3.1)
153 nt.assert_equal(ip.user_ns['yy'], 3.1)
161
154
162 def test_paste_email_py(self):
155 def test_paste_email_py(self):
163 "Email quoting of interactive input"
156 "Email quoting of interactive input"
164 self.paste("""\
157 self.paste("""\
165 >> >>> def f(x):
158 >> >>> def f(x):
166 >> ... return x+1
159 >> ... return x+1
167 >> ...
160 >> ...
168 >> >>> zz = f(2.5) """)
161 >> >>> zz = f(2.5) """)
169 nt.assert_equal(ip.user_ns['zz'], 3.5)
162 nt.assert_equal(ip.user_ns['zz'], 3.5)
170
163
171 def test_paste_echo(self):
164 def test_paste_echo(self):
172 "Also test self.paste echoing, by temporarily faking the writer"
165 "Also test self.paste echoing, by temporarily faking the writer"
173 w = StringIO()
166 w = StringIO()
174 writer = ip.write
167 writer = ip.write
175 ip.write = w.write
168 ip.write = w.write
176 code = """
169 code = """
177 a = 100
170 a = 100
178 b = 200"""
171 b = 200"""
179 try:
172 try:
180 self.paste(code,'')
173 self.paste(code,'')
181 out = w.getvalue()
174 out = w.getvalue()
182 finally:
175 finally:
183 ip.write = writer
176 ip.write = writer
184 nt.assert_equal(ip.user_ns['a'], 100)
177 nt.assert_equal(ip.user_ns['a'], 100)
185 nt.assert_equal(ip.user_ns['b'], 200)
178 nt.assert_equal(ip.user_ns['b'], 200)
186 nt.assert_equal(out, code+"\n## -- End pasted text --\n")
179 nt.assert_equal(out, code+"\n## -- End pasted text --\n")
187
180
188 def test_paste_leading_commas(self):
181 def test_paste_leading_commas(self):
189 "Test multiline strings with leading commas"
182 "Test multiline strings with leading commas"
190 tm = ip.magics_manager.registry['TerminalMagics']
183 tm = ip.magics_manager.registry['TerminalMagics']
191 s = '''\
184 s = '''\
192 a = """
185 a = """
193 ,1,2,3
186 ,1,2,3
194 """'''
187 """'''
195 ip.user_ns.pop('foo', None)
188 ip.user_ns.pop('foo', None)
196 tm.store_or_execute(s, 'foo')
189 tm.store_or_execute(s, 'foo')
197 nt.assert_in('foo', ip.user_ns)
190 nt.assert_in('foo', ip.user_ns)
198
191
199
192
200 def test_paste_trailing_question(self):
193 def test_paste_trailing_question(self):
201 "Test pasting sources with trailing question marks"
194 "Test pasting sources with trailing question marks"
202 tm = ip.magics_manager.registry['TerminalMagics']
195 tm = ip.magics_manager.registry['TerminalMagics']
203 s = '''\
196 s = '''\
204 def funcfoo():
197 def funcfoo():
205 if True: #am i true?
198 if True: #am i true?
206 return 'fooresult'
199 return 'fooresult'
207 '''
200 '''
208 ip.user_ns.pop('funcfoo', None)
201 ip.user_ns.pop('funcfoo', None)
209 self.paste(s)
202 self.paste(s)
210 nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult')
203 nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult')
@@ -1,456 +1,455 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import os
9 import os
10 import re
10 import re
11 import sys
11 import sys
12
12
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 from .. import oinspect
15 from .. import oinspect
16 from IPython.core.magic import (Magics, magics_class, line_magic,
16 from IPython.core.magic import (Magics, magics_class, line_magic,
17 cell_magic, line_cell_magic,
17 cell_magic, line_cell_magic,
18 register_line_magic, register_cell_magic,
18 register_line_magic, register_cell_magic,
19 register_line_cell_magic)
19 register_line_cell_magic)
20 from decorator import decorator
20 from decorator import decorator
21 from IPython.testing.decorators import skipif
21 from IPython.testing.decorators import skipif
22 from IPython.testing.tools import AssertPrints
22 from IPython.testing.tools import AssertPrints
23 from IPython.utils.path import compress_user
23 from IPython.utils.path import compress_user
24 from IPython.utils import py3compat
24 from IPython.utils import py3compat
25 from IPython.utils.signatures import Signature, Parameter
25 from IPython.utils.signatures import Signature, Parameter
26
26
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Globals and constants
29 # Globals and constants
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 inspector = oinspect.Inspector()
32 inspector = oinspect.Inspector()
33 ip = get_ipython()
33 ip = get_ipython()
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Local utilities
36 # Local utilities
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 # WARNING: since this test checks the line number where a function is
39 # WARNING: since this test checks the line number where a function is
40 # defined, if any code is inserted above, the following line will need to be
40 # defined, if any code is inserted above, the following line will need to be
41 # updated. Do NOT insert any whitespace between the next line and the function
41 # updated. Do NOT insert any whitespace between the next line and the function
42 # definition below.
42 # definition below.
43 THIS_LINE_NUMBER = 43 # Put here the actual number of this line
43 THIS_LINE_NUMBER = 43 # Put here the actual number of this line
44
44
45 from unittest import TestCase
45 from unittest import TestCase
46
46
47 class Test(TestCase):
47 class Test(TestCase):
48
48
49 def test_find_source_lines(self):
49 def test_find_source_lines(self):
50 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
50 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
51 THIS_LINE_NUMBER+6)
51 THIS_LINE_NUMBER+6)
52
52
53
53
54 # A couple of utilities to ensure these tests work the same from a source or a
54 # A couple of utilities to ensure these tests work the same from a source or a
55 # binary install
55 # binary install
56 def pyfile(fname):
56 def pyfile(fname):
57 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
57 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
58
58
59
59
60 def match_pyfiles(f1, f2):
60 def match_pyfiles(f1, f2):
61 nt.assert_equal(pyfile(f1), pyfile(f2))
61 nt.assert_equal(pyfile(f1), pyfile(f2))
62
62
63
63
64 def test_find_file():
64 def test_find_file():
65 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
65 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
66
66
67
67
68 def test_find_file_decorated1():
68 def test_find_file_decorated1():
69
69
70 @decorator
70 @decorator
71 def noop1(f):
71 def noop1(f):
72 def wrapper():
72 def wrapper():
73 return f(*a, **kw)
73 return f(*a, **kw)
74 return wrapper
74 return wrapper
75
75
76 @noop1
76 @noop1
77 def f(x):
77 def f(x):
78 "My docstring"
78 "My docstring"
79
79
80 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
80 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
81 nt.assert_equal(f.__doc__, "My docstring")
81 nt.assert_equal(f.__doc__, "My docstring")
82
82
83
83
84 def test_find_file_decorated2():
84 def test_find_file_decorated2():
85
85
86 @decorator
86 @decorator
87 def noop2(f, *a, **kw):
87 def noop2(f, *a, **kw):
88 return f(*a, **kw)
88 return f(*a, **kw)
89
89
90 @noop2
90 @noop2
91 @noop2
91 @noop2
92 @noop2
92 @noop2
93 def f(x):
93 def f(x):
94 "My docstring 2"
94 "My docstring 2"
95
95
96 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
96 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
97 nt.assert_equal(f.__doc__, "My docstring 2")
97 nt.assert_equal(f.__doc__, "My docstring 2")
98
98
99
99
100 def test_find_file_magic():
100 def test_find_file_magic():
101 run = ip.find_line_magic('run')
101 run = ip.find_line_magic('run')
102 nt.assert_not_equal(oinspect.find_file(run), None)
102 nt.assert_not_equal(oinspect.find_file(run), None)
103
103
104
104
105 # A few generic objects we can then inspect in the tests below
105 # A few generic objects we can then inspect in the tests below
106
106
107 class Call(object):
107 class Call(object):
108 """This is the class docstring."""
108 """This is the class docstring."""
109
109
110 def __init__(self, x, y=1):
110 def __init__(self, x, y=1):
111 """This is the constructor docstring."""
111 """This is the constructor docstring."""
112
112
113 def __call__(self, *a, **kw):
113 def __call__(self, *a, **kw):
114 """This is the call docstring."""
114 """This is the call docstring."""
115
115
116 def method(self, x, z=2):
116 def method(self, x, z=2):
117 """Some method's docstring"""
117 """Some method's docstring"""
118
118
119 class HasSignature(object):
119 class HasSignature(object):
120 """This is the class docstring."""
120 """This is the class docstring."""
121 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
121 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
122
122
123 def __init__(self, *args):
123 def __init__(self, *args):
124 """This is the init docstring"""
124 """This is the init docstring"""
125
125
126
126
127 class SimpleClass(object):
127 class SimpleClass(object):
128 def method(self, x, z=2):
128 def method(self, x, z=2):
129 """Some method's docstring"""
129 """Some method's docstring"""
130
130
131
131
132 class OldStyle:
132 class OldStyle:
133 """An old-style class for testing."""
133 """An old-style class for testing."""
134 pass
134 pass
135
135
136
136
137 def f(x, y=2, *a, **kw):
137 def f(x, y=2, *a, **kw):
138 """A simple function."""
138 """A simple function."""
139
139
140
140
141 def g(y, z=3, *a, **kw):
141 def g(y, z=3, *a, **kw):
142 pass # no docstring
142 pass # no docstring
143
143
144
144
145 @register_line_magic
145 @register_line_magic
146 def lmagic(line):
146 def lmagic(line):
147 "A line magic"
147 "A line magic"
148
148
149
149
150 @register_cell_magic
150 @register_cell_magic
151 def cmagic(line, cell):
151 def cmagic(line, cell):
152 "A cell magic"
152 "A cell magic"
153
153
154
154
155 @register_line_cell_magic
155 @register_line_cell_magic
156 def lcmagic(line, cell=None):
156 def lcmagic(line, cell=None):
157 "A line/cell magic"
157 "A line/cell magic"
158
158
159
159
160 @magics_class
160 @magics_class
161 class SimpleMagics(Magics):
161 class SimpleMagics(Magics):
162 @line_magic
162 @line_magic
163 def Clmagic(self, cline):
163 def Clmagic(self, cline):
164 "A class-based line magic"
164 "A class-based line magic"
165
165
166 @cell_magic
166 @cell_magic
167 def Ccmagic(self, cline, ccell):
167 def Ccmagic(self, cline, ccell):
168 "A class-based cell magic"
168 "A class-based cell magic"
169
169
170 @line_cell_magic
170 @line_cell_magic
171 def Clcmagic(self, cline, ccell=None):
171 def Clcmagic(self, cline, ccell=None):
172 "A class-based line/cell magic"
172 "A class-based line/cell magic"
173
173
174
174
175 class Awkward(object):
175 class Awkward(object):
176 def __getattr__(self, name):
176 def __getattr__(self, name):
177 raise Exception(name)
177 raise Exception(name)
178
178
179 class NoBoolCall:
179 class NoBoolCall:
180 """
180 """
181 callable with `__bool__` raising should still be inspect-able.
181 callable with `__bool__` raising should still be inspect-able.
182 """
182 """
183
183
184 def __call__(self):
184 def __call__(self):
185 """does nothing"""
185 """does nothing"""
186 pass
186 pass
187
187
188 def __bool__(self):
188 def __bool__(self):
189 """just raise NotImplemented"""
189 """just raise NotImplemented"""
190 raise NotImplementedError('Must be implemented')
190 raise NotImplementedError('Must be implemented')
191
191
192
192
193 class SerialLiar(object):
193 class SerialLiar(object):
194 """Attribute accesses always get another copy of the same class.
194 """Attribute accesses always get another copy of the same class.
195
195
196 unittest.mock.call does something similar, but it's not ideal for testing
196 unittest.mock.call does something similar, but it's not ideal for testing
197 as the failure mode is to eat all your RAM. This gives up after 10k levels.
197 as the failure mode is to eat all your RAM. This gives up after 10k levels.
198 """
198 """
199 def __init__(self, max_fibbing_twig, lies_told=0):
199 def __init__(self, max_fibbing_twig, lies_told=0):
200 if lies_told > 10000:
200 if lies_told > 10000:
201 raise RuntimeError('Nose too long, honesty is the best policy')
201 raise RuntimeError('Nose too long, honesty is the best policy')
202 self.max_fibbing_twig = max_fibbing_twig
202 self.max_fibbing_twig = max_fibbing_twig
203 self.lies_told = lies_told
203 self.lies_told = lies_told
204 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
204 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
205
205
206 def __getattr__(self, item):
206 def __getattr__(self, item):
207 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
207 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
208
208
209
209
210 def check_calltip(obj, name, call, docstring):
210 def check_calltip(obj, name, call, docstring):
211 """Generic check pattern all calltip tests will use"""
211 """Generic check pattern all calltip tests will use"""
212 info = inspector.info(obj, name)
212 info = inspector.info(obj, name)
213 call_line, ds = oinspect.call_tip(info)
213 call_line, ds = oinspect.call_tip(info)
214 nt.assert_equal(call_line, call)
214 nt.assert_equal(call_line, call)
215 nt.assert_equal(ds, docstring)
215 nt.assert_equal(ds, docstring)
216
216
217 #-----------------------------------------------------------------------------
217 #-----------------------------------------------------------------------------
218 # Tests
218 # Tests
219 #-----------------------------------------------------------------------------
219 #-----------------------------------------------------------------------------
220
220
221 def test_calltip_class():
221 def test_calltip_class():
222 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
222 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
223
223
224
224
225 def test_calltip_instance():
225 def test_calltip_instance():
226 c = Call(1)
226 c = Call(1)
227 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
227 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
228
228
229
229
230 def test_calltip_method():
230 def test_calltip_method():
231 c = Call(1)
231 c = Call(1)
232 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
232 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
233
233
234
234
235 def test_calltip_function():
235 def test_calltip_function():
236 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
236 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
237
237
238
238
239 def test_calltip_function2():
239 def test_calltip_function2():
240 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
240 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
241
241
242
242
243 @skipif(sys.version_info >= (3, 5))
243 @skipif(sys.version_info >= (3, 5))
244 def test_calltip_builtin():
244 def test_calltip_builtin():
245 check_calltip(sum, 'sum', None, sum.__doc__)
245 check_calltip(sum, 'sum', None, sum.__doc__)
246
246
247
247
248 def test_calltip_line_magic():
248 def test_calltip_line_magic():
249 check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic")
249 check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic")
250
250
251
251
252 def test_calltip_cell_magic():
252 def test_calltip_cell_magic():
253 check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic")
253 check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic")
254
254
255
255
256 def test_calltip_line_cell_magic():
256 def test_calltip_line_cell_magic():
257 check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)',
257 check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)',
258 "A line/cell magic")
258 "A line/cell magic")
259
259
260
260
261 def test_class_magics():
261 def test_class_magics():
262 cm = SimpleMagics(ip)
262 cm = SimpleMagics(ip)
263 ip.register_magics(cm)
263 ip.register_magics(cm)
264 check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)',
264 check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)',
265 "A class-based line magic")
265 "A class-based line magic")
266 check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)',
266 check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)',
267 "A class-based cell magic")
267 "A class-based cell magic")
268 check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)',
268 check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)',
269 "A class-based line/cell magic")
269 "A class-based line/cell magic")
270
270
271
271
272 def test_info():
272 def test_info():
273 "Check that Inspector.info fills out various fields as expected."
273 "Check that Inspector.info fills out various fields as expected."
274 i = inspector.info(Call, oname='Call')
274 i = inspector.info(Call, oname='Call')
275 nt.assert_equal(i['type_name'], 'type')
275 nt.assert_equal(i['type_name'], 'type')
276 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
276 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
277 nt.assert_equal(i['base_class'], expted_class)
277 nt.assert_equal(i['base_class'], expted_class)
278 if sys.version_info > (3,):
279 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
278 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
280 fname = __file__
279 fname = __file__
281 if fname.endswith(".pyc"):
280 if fname.endswith(".pyc"):
282 fname = fname[:-1]
281 fname = fname[:-1]
283 # case-insensitive comparison needed on some filesystems
282 # case-insensitive comparison needed on some filesystems
284 # e.g. Windows:
283 # e.g. Windows:
285 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
284 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
286 nt.assert_equal(i['definition'], None)
285 nt.assert_equal(i['definition'], None)
287 nt.assert_equal(i['docstring'], Call.__doc__)
286 nt.assert_equal(i['docstring'], Call.__doc__)
288 nt.assert_equal(i['source'], None)
287 nt.assert_equal(i['source'], None)
289 nt.assert_true(i['isclass'])
288 nt.assert_true(i['isclass'])
290 _self_py2 = '' if py3compat.PY3 else 'self, '
289 _self_py2 = '' if py3compat.PY3 else 'self, '
291 nt.assert_equal(i['init_definition'], "Call(%sx, y=1)" % _self_py2)
290 nt.assert_equal(i['init_definition'], "Call(%sx, y=1)" % _self_py2)
292 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
291 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
293
292
294 i = inspector.info(Call, detail_level=1)
293 i = inspector.info(Call, detail_level=1)
295 nt.assert_not_equal(i['source'], None)
294 nt.assert_not_equal(i['source'], None)
296 nt.assert_equal(i['docstring'], None)
295 nt.assert_equal(i['docstring'], None)
297
296
298 c = Call(1)
297 c = Call(1)
299 c.__doc__ = "Modified instance docstring"
298 c.__doc__ = "Modified instance docstring"
300 i = inspector.info(c)
299 i = inspector.info(c)
301 nt.assert_equal(i['type_name'], 'Call')
300 nt.assert_equal(i['type_name'], 'Call')
302 nt.assert_equal(i['docstring'], "Modified instance docstring")
301 nt.assert_equal(i['docstring'], "Modified instance docstring")
303 nt.assert_equal(i['class_docstring'], Call.__doc__)
302 nt.assert_equal(i['class_docstring'], Call.__doc__)
304 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
303 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
305 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
304 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
306
305
307 # Test old-style classes, which for example may not have an __init__ method.
306 # Test old-style classes, which for example may not have an __init__ method.
308 if not py3compat.PY3:
307 if not py3compat.PY3:
309 i = inspector.info(OldStyle)
308 i = inspector.info(OldStyle)
310 nt.assert_equal(i['type_name'], 'classobj')
309 nt.assert_equal(i['type_name'], 'classobj')
311
310
312 i = inspector.info(OldStyle())
311 i = inspector.info(OldStyle())
313 nt.assert_equal(i['type_name'], 'instance')
312 nt.assert_equal(i['type_name'], 'instance')
314 nt.assert_equal(i['docstring'], OldStyle.__doc__)
313 nt.assert_equal(i['docstring'], OldStyle.__doc__)
315
314
316 def test_class_signature():
315 def test_class_signature():
317 info = inspector.info(HasSignature, 'HasSignature')
316 info = inspector.info(HasSignature, 'HasSignature')
318 nt.assert_equal(info['init_definition'], "HasSignature(test)")
317 nt.assert_equal(info['init_definition'], "HasSignature(test)")
319 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
318 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
320
319
321 def test_info_awkward():
320 def test_info_awkward():
322 # Just test that this doesn't throw an error.
321 # Just test that this doesn't throw an error.
323 inspector.info(Awkward())
322 inspector.info(Awkward())
324
323
325 def test_bool_raise():
324 def test_bool_raise():
326 inspector.info(NoBoolCall())
325 inspector.info(NoBoolCall())
327
326
328 def test_info_serialliar():
327 def test_info_serialliar():
329 fib_tracker = [0]
328 fib_tracker = [0]
330 inspector.info(SerialLiar(fib_tracker))
329 inspector.info(SerialLiar(fib_tracker))
331
330
332 # Nested attribute access should be cut off at 100 levels deep to avoid
331 # Nested attribute access should be cut off at 100 levels deep to avoid
333 # infinite loops: https://github.com/ipython/ipython/issues/9122
332 # infinite loops: https://github.com/ipython/ipython/issues/9122
334 nt.assert_less(fib_tracker[0], 9000)
333 nt.assert_less(fib_tracker[0], 9000)
335
334
336 def test_calldef_none():
335 def test_calldef_none():
337 # We should ignore __call__ for all of these.
336 # We should ignore __call__ for all of these.
338 for obj in [f, SimpleClass().method, any, str.upper]:
337 for obj in [f, SimpleClass().method, any, str.upper]:
339 print(obj)
338 print(obj)
340 i = inspector.info(obj)
339 i = inspector.info(obj)
341 nt.assert_is(i['call_def'], None)
340 nt.assert_is(i['call_def'], None)
342
341
343 def f_kwarg(pos, *, kwonly):
342 def f_kwarg(pos, *, kwonly):
344 pass
343 pass
345
344
346 def test_definition_kwonlyargs():
345 def test_definition_kwonlyargs():
347 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
346 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
348 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
347 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
349
348
350 def test_getdoc():
349 def test_getdoc():
351 class A(object):
350 class A(object):
352 """standard docstring"""
351 """standard docstring"""
353 pass
352 pass
354
353
355 class B(object):
354 class B(object):
356 """standard docstring"""
355 """standard docstring"""
357 def getdoc(self):
356 def getdoc(self):
358 return "custom docstring"
357 return "custom docstring"
359
358
360 class C(object):
359 class C(object):
361 """standard docstring"""
360 """standard docstring"""
362 def getdoc(self):
361 def getdoc(self):
363 return None
362 return None
364
363
365 a = A()
364 a = A()
366 b = B()
365 b = B()
367 c = C()
366 c = C()
368
367
369 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
368 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
370 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
369 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
371 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
370 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
372
371
373
372
374 def test_empty_property_has_no_source():
373 def test_empty_property_has_no_source():
375 i = inspector.info(property(), detail_level=1)
374 i = inspector.info(property(), detail_level=1)
376 nt.assert_is(i['source'], None)
375 nt.assert_is(i['source'], None)
377
376
378
377
379 def test_property_sources():
378 def test_property_sources():
380 import zlib
379 import zlib
381
380
382 class A(object):
381 class A(object):
383 @property
382 @property
384 def foo(self):
383 def foo(self):
385 return 'bar'
384 return 'bar'
386
385
387 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
386 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
388
387
389 id = property(id)
388 id = property(id)
390 compress = property(zlib.compress)
389 compress = property(zlib.compress)
391
390
392 i = inspector.info(A.foo, detail_level=1)
391 i = inspector.info(A.foo, detail_level=1)
393 nt.assert_in('def foo(self):', i['source'])
392 nt.assert_in('def foo(self):', i['source'])
394 nt.assert_in('lambda self, v:', i['source'])
393 nt.assert_in('lambda self, v:', i['source'])
395
394
396 i = inspector.info(A.id, detail_level=1)
395 i = inspector.info(A.id, detail_level=1)
397 nt.assert_in('fget = <function id>', i['source'])
396 nt.assert_in('fget = <function id>', i['source'])
398
397
399 i = inspector.info(A.compress, detail_level=1)
398 i = inspector.info(A.compress, detail_level=1)
400 nt.assert_in('fget = <function zlib.compress>', i['source'])
399 nt.assert_in('fget = <function zlib.compress>', i['source'])
401
400
402
401
403 def test_property_docstring_is_in_info_for_detail_level_0():
402 def test_property_docstring_is_in_info_for_detail_level_0():
404 class A(object):
403 class A(object):
405 @property
404 @property
406 def foobar(self):
405 def foobar(self):
407 """This is `foobar` property."""
406 """This is `foobar` property."""
408 pass
407 pass
409
408
410 ip.user_ns['a_obj'] = A()
409 ip.user_ns['a_obj'] = A()
411 nt.assert_equals(
410 nt.assert_equals(
412 'This is `foobar` property.',
411 'This is `foobar` property.',
413 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
412 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
414
413
415 ip.user_ns['a_cls'] = A
414 ip.user_ns['a_cls'] = A
416 nt.assert_equals(
415 nt.assert_equals(
417 'This is `foobar` property.',
416 'This is `foobar` property.',
418 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
417 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
419
418
420
419
421 def test_pdef():
420 def test_pdef():
422 # See gh-1914
421 # See gh-1914
423 def foo(): pass
422 def foo(): pass
424 inspector.pdef(foo, 'foo')
423 inspector.pdef(foo, 'foo')
425
424
426
425
427 def test_pinfo_nonascii():
426 def test_pinfo_nonascii():
428 # See gh-1177
427 # See gh-1177
429 from . import nonascii2
428 from . import nonascii2
430 ip.user_ns['nonascii2'] = nonascii2
429 ip.user_ns['nonascii2'] = nonascii2
431 ip._inspect('pinfo', 'nonascii2', detail_level=1)
430 ip._inspect('pinfo', 'nonascii2', detail_level=1)
432
431
433
432
434 def test_pinfo_magic():
433 def test_pinfo_magic():
435 with AssertPrints('Docstring:'):
434 with AssertPrints('Docstring:'):
436 ip._inspect('pinfo', 'lsmagic', detail_level=0)
435 ip._inspect('pinfo', 'lsmagic', detail_level=0)
437
436
438 with AssertPrints('Source:'):
437 with AssertPrints('Source:'):
439 ip._inspect('pinfo', 'lsmagic', detail_level=1)
438 ip._inspect('pinfo', 'lsmagic', detail_level=1)
440
439
441
440
442 def test_init_colors():
441 def test_init_colors():
443 # ensure colors are not present in signature info
442 # ensure colors are not present in signature info
444 info = inspector.info(HasSignature)
443 info = inspector.info(HasSignature)
445 init_def = info['init_definition']
444 init_def = info['init_definition']
446 nt.assert_not_in('[0m', init_def)
445 nt.assert_not_in('[0m', init_def)
447
446
448
447
449 def test_builtin_init():
448 def test_builtin_init():
450 info = inspector.info(list)
449 info = inspector.info(list)
451 init_def = info['init_definition']
450 init_def = info['init_definition']
452 # Python < 3.4 can't get init definition from builtins,
451 # Python < 3.4 can't get init definition from builtins,
453 # but still exercise the inspection in case of error-raising bugs.
452 # but still exercise the inspection in case of error-raising bugs.
454 if sys.version_info >= (3,4):
453 if sys.version_info >= (3,4):
455 nt.assert_is_not_none(init_def)
454 nt.assert_is_not_none(init_def)
456
455
@@ -1,356 +1,355 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import sys
5 import sys
6 import os.path
6 import os.path
7 from textwrap import dedent
7 from textwrap import dedent
8 import traceback
8 import traceback
9 import unittest
9 import unittest
10
10
11 try:
11 try:
12 from unittest import mock
12 from unittest import mock
13 except ImportError:
13 except ImportError:
14 import mock # Python 2
14 import mock # Python 2
15
15
16 from ..ultratb import ColorTB, VerboseTB, find_recursion
16 from ..ultratb import ColorTB, VerboseTB, find_recursion
17
17
18
18
19 from IPython.testing import tools as tt
19 from IPython.testing import tools as tt
20 from IPython.testing.decorators import onlyif_unicode_paths
20 from IPython.testing.decorators import onlyif_unicode_paths
21 from IPython.utils.syspathcontext import prepended_to_syspath
21 from IPython.utils.syspathcontext import prepended_to_syspath
22 from IPython.utils.tempdir import TemporaryDirectory
22 from IPython.utils.tempdir import TemporaryDirectory
23 from IPython.utils.py3compat import PY3
23 from IPython.utils.py3compat import PY3
24
24
25 ip = get_ipython()
25 ip = get_ipython()
26
26
27 file_1 = """1
27 file_1 = """1
28 2
28 2
29 3
29 3
30 def f():
30 def f():
31 1/0
31 1/0
32 """
32 """
33
33
34 file_2 = """def f():
34 file_2 = """def f():
35 1/0
35 1/0
36 """
36 """
37
37
38 class ChangedPyFileTest(unittest.TestCase):
38 class ChangedPyFileTest(unittest.TestCase):
39 def test_changing_py_file(self):
39 def test_changing_py_file(self):
40 """Traceback produced if the line where the error occurred is missing?
40 """Traceback produced if the line where the error occurred is missing?
41
41
42 https://github.com/ipython/ipython/issues/1456
42 https://github.com/ipython/ipython/issues/1456
43 """
43 """
44 with TemporaryDirectory() as td:
44 with TemporaryDirectory() as td:
45 fname = os.path.join(td, "foo.py")
45 fname = os.path.join(td, "foo.py")
46 with open(fname, "w") as f:
46 with open(fname, "w") as f:
47 f.write(file_1)
47 f.write(file_1)
48
48
49 with prepended_to_syspath(td):
49 with prepended_to_syspath(td):
50 ip.run_cell("import foo")
50 ip.run_cell("import foo")
51
51
52 with tt.AssertPrints("ZeroDivisionError"):
52 with tt.AssertPrints("ZeroDivisionError"):
53 ip.run_cell("foo.f()")
53 ip.run_cell("foo.f()")
54
54
55 # Make the file shorter, so the line of the error is missing.
55 # Make the file shorter, so the line of the error is missing.
56 with open(fname, "w") as f:
56 with open(fname, "w") as f:
57 f.write(file_2)
57 f.write(file_2)
58
58
59 # For some reason, this was failing on the *second* call after
59 # For some reason, this was failing on the *second* call after
60 # changing the file, so we call f() twice.
60 # changing the file, so we call f() twice.
61 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
61 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
62 with tt.AssertPrints("ZeroDivisionError"):
62 with tt.AssertPrints("ZeroDivisionError"):
63 ip.run_cell("foo.f()")
63 ip.run_cell("foo.f()")
64 with tt.AssertPrints("ZeroDivisionError"):
64 with tt.AssertPrints("ZeroDivisionError"):
65 ip.run_cell("foo.f()")
65 ip.run_cell("foo.f()")
66
66
67 iso_8859_5_file = u'''# coding: iso-8859-5
67 iso_8859_5_file = u'''# coding: iso-8859-5
68
68
69 def fail():
69 def fail():
70 """дбИЖ"""
70 """дбИЖ"""
71 1/0 # дбИЖ
71 1/0 # дбИЖ
72 '''
72 '''
73
73
74 class NonAsciiTest(unittest.TestCase):
74 class NonAsciiTest(unittest.TestCase):
75 @onlyif_unicode_paths
75 @onlyif_unicode_paths
76 def test_nonascii_path(self):
76 def test_nonascii_path(self):
77 # Non-ascii directory name as well.
77 # Non-ascii directory name as well.
78 with TemporaryDirectory(suffix=u'é') as td:
78 with TemporaryDirectory(suffix=u'é') as td:
79 fname = os.path.join(td, u"fooé.py")
79 fname = os.path.join(td, u"fooé.py")
80 with open(fname, "w") as f:
80 with open(fname, "w") as f:
81 f.write(file_1)
81 f.write(file_1)
82
82
83 with prepended_to_syspath(td):
83 with prepended_to_syspath(td):
84 ip.run_cell("import foo")
84 ip.run_cell("import foo")
85
85
86 with tt.AssertPrints("ZeroDivisionError"):
86 with tt.AssertPrints("ZeroDivisionError"):
87 ip.run_cell("foo.f()")
87 ip.run_cell("foo.f()")
88
88
89 def test_iso8859_5(self):
89 def test_iso8859_5(self):
90 with TemporaryDirectory() as td:
90 with TemporaryDirectory() as td:
91 fname = os.path.join(td, 'dfghjkl.py')
91 fname = os.path.join(td, 'dfghjkl.py')
92
92
93 with io.open(fname, 'w', encoding='iso-8859-5') as f:
93 with io.open(fname, 'w', encoding='iso-8859-5') as f:
94 f.write(iso_8859_5_file)
94 f.write(iso_8859_5_file)
95
95
96 with prepended_to_syspath(td):
96 with prepended_to_syspath(td):
97 ip.run_cell("from dfghjkl import fail")
97 ip.run_cell("from dfghjkl import fail")
98
98
99 with tt.AssertPrints("ZeroDivisionError"):
99 with tt.AssertPrints("ZeroDivisionError"):
100 with tt.AssertPrints(u'дбИЖ', suppress=False):
100 with tt.AssertPrints(u'дбИЖ', suppress=False):
101 ip.run_cell('fail()')
101 ip.run_cell('fail()')
102
102
103 def test_nonascii_msg(self):
103 def test_nonascii_msg(self):
104 cell = u"raise Exception('é')"
104 cell = u"raise Exception('é')"
105 expected = u"Exception('é')"
105 expected = u"Exception('é')"
106 ip.run_cell("%xmode plain")
106 ip.run_cell("%xmode plain")
107 with tt.AssertPrints(expected):
107 with tt.AssertPrints(expected):
108 ip.run_cell(cell)
108 ip.run_cell(cell)
109
109
110 ip.run_cell("%xmode verbose")
110 ip.run_cell("%xmode verbose")
111 with tt.AssertPrints(expected):
111 with tt.AssertPrints(expected):
112 ip.run_cell(cell)
112 ip.run_cell(cell)
113
113
114 ip.run_cell("%xmode context")
114 ip.run_cell("%xmode context")
115 with tt.AssertPrints(expected):
115 with tt.AssertPrints(expected):
116 ip.run_cell(cell)
116 ip.run_cell(cell)
117
117
118
118
119 class NestedGenExprTestCase(unittest.TestCase):
119 class NestedGenExprTestCase(unittest.TestCase):
120 """
120 """
121 Regression test for the following issues:
121 Regression test for the following issues:
122 https://github.com/ipython/ipython/issues/8293
122 https://github.com/ipython/ipython/issues/8293
123 https://github.com/ipython/ipython/issues/8205
123 https://github.com/ipython/ipython/issues/8205
124 """
124 """
125 def test_nested_genexpr(self):
125 def test_nested_genexpr(self):
126 code = dedent(
126 code = dedent(
127 """\
127 """\
128 class SpecificException(Exception):
128 class SpecificException(Exception):
129 pass
129 pass
130
130
131 def foo(x):
131 def foo(x):
132 raise SpecificException("Success!")
132 raise SpecificException("Success!")
133
133
134 sum(sum(foo(x) for _ in [0]) for x in [0])
134 sum(sum(foo(x) for _ in [0]) for x in [0])
135 """
135 """
136 )
136 )
137 with tt.AssertPrints('SpecificException: Success!', suppress=False):
137 with tt.AssertPrints('SpecificException: Success!', suppress=False):
138 ip.run_cell(code)
138 ip.run_cell(code)
139
139
140
140
141 indentationerror_file = """if True:
141 indentationerror_file = """if True:
142 zoon()
142 zoon()
143 """
143 """
144
144
145 class IndentationErrorTest(unittest.TestCase):
145 class IndentationErrorTest(unittest.TestCase):
146 def test_indentationerror_shows_line(self):
146 def test_indentationerror_shows_line(self):
147 # See issue gh-2398
147 # See issue gh-2398
148 with tt.AssertPrints("IndentationError"):
148 with tt.AssertPrints("IndentationError"):
149 with tt.AssertPrints("zoon()", suppress=False):
149 with tt.AssertPrints("zoon()", suppress=False):
150 ip.run_cell(indentationerror_file)
150 ip.run_cell(indentationerror_file)
151
151
152 with TemporaryDirectory() as td:
152 with TemporaryDirectory() as td:
153 fname = os.path.join(td, "foo.py")
153 fname = os.path.join(td, "foo.py")
154 with open(fname, "w") as f:
154 with open(fname, "w") as f:
155 f.write(indentationerror_file)
155 f.write(indentationerror_file)
156
156
157 with tt.AssertPrints("IndentationError"):
157 with tt.AssertPrints("IndentationError"):
158 with tt.AssertPrints("zoon()", suppress=False):
158 with tt.AssertPrints("zoon()", suppress=False):
159 ip.magic('run %s' % fname)
159 ip.magic('run %s' % fname)
160
160
161 se_file_1 = """1
161 se_file_1 = """1
162 2
162 2
163 7/
163 7/
164 """
164 """
165
165
166 se_file_2 = """7/
166 se_file_2 = """7/
167 """
167 """
168
168
169 class SyntaxErrorTest(unittest.TestCase):
169 class SyntaxErrorTest(unittest.TestCase):
170 def test_syntaxerror_without_lineno(self):
170 def test_syntaxerror_without_lineno(self):
171 with tt.AssertNotPrints("TypeError"):
171 with tt.AssertNotPrints("TypeError"):
172 with tt.AssertPrints("line unknown"):
172 with tt.AssertPrints("line unknown"):
173 ip.run_cell("raise SyntaxError()")
173 ip.run_cell("raise SyntaxError()")
174
174
175 def test_changing_py_file(self):
175 def test_changing_py_file(self):
176 with TemporaryDirectory() as td:
176 with TemporaryDirectory() as td:
177 fname = os.path.join(td, "foo.py")
177 fname = os.path.join(td, "foo.py")
178 with open(fname, 'w') as f:
178 with open(fname, 'w') as f:
179 f.write(se_file_1)
179 f.write(se_file_1)
180
180
181 with tt.AssertPrints(["7/", "SyntaxError"]):
181 with tt.AssertPrints(["7/", "SyntaxError"]):
182 ip.magic("run " + fname)
182 ip.magic("run " + fname)
183
183
184 # Modify the file
184 # Modify the file
185 with open(fname, 'w') as f:
185 with open(fname, 'w') as f:
186 f.write(se_file_2)
186 f.write(se_file_2)
187
187
188 # The SyntaxError should point to the correct line
188 # The SyntaxError should point to the correct line
189 with tt.AssertPrints(["7/", "SyntaxError"]):
189 with tt.AssertPrints(["7/", "SyntaxError"]):
190 ip.magic("run " + fname)
190 ip.magic("run " + fname)
191
191
192 def test_non_syntaxerror(self):
192 def test_non_syntaxerror(self):
193 # SyntaxTB may be called with an error other than a SyntaxError
193 # SyntaxTB may be called with an error other than a SyntaxError
194 # See e.g. gh-4361
194 # See e.g. gh-4361
195 try:
195 try:
196 raise ValueError('QWERTY')
196 raise ValueError('QWERTY')
197 except ValueError:
197 except ValueError:
198 with tt.AssertPrints('QWERTY'):
198 with tt.AssertPrints('QWERTY'):
199 ip.showsyntaxerror()
199 ip.showsyntaxerror()
200
200
201
201
202 class Python3ChainedExceptionsTest(unittest.TestCase):
202 class Python3ChainedExceptionsTest(unittest.TestCase):
203 DIRECT_CAUSE_ERROR_CODE = """
203 DIRECT_CAUSE_ERROR_CODE = """
204 try:
204 try:
205 x = 1 + 2
205 x = 1 + 2
206 print(not_defined_here)
206 print(not_defined_here)
207 except Exception as e:
207 except Exception as e:
208 x += 55
208 x += 55
209 x - 1
209 x - 1
210 y = {}
210 y = {}
211 raise KeyError('uh') from e
211 raise KeyError('uh') from e
212 """
212 """
213
213
214 EXCEPTION_DURING_HANDLING_CODE = """
214 EXCEPTION_DURING_HANDLING_CODE = """
215 try:
215 try:
216 x = 1 + 2
216 x = 1 + 2
217 print(not_defined_here)
217 print(not_defined_here)
218 except Exception as e:
218 except Exception as e:
219 x += 55
219 x += 55
220 x - 1
220 x - 1
221 y = {}
221 y = {}
222 raise KeyError('uh')
222 raise KeyError('uh')
223 """
223 """
224
224
225 SUPPRESS_CHAINING_CODE = """
225 SUPPRESS_CHAINING_CODE = """
226 try:
226 try:
227 1/0
227 1/0
228 except Exception:
228 except Exception:
229 raise ValueError("Yikes") from None
229 raise ValueError("Yikes") from None
230 """
230 """
231
231
232 def test_direct_cause_error(self):
232 def test_direct_cause_error(self):
233 if PY3:
233 if PY3:
234 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
234 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
235 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
235 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
236
236
237 def test_exception_during_handling_error(self):
237 def test_exception_during_handling_error(self):
238 if PY3:
238 if PY3:
239 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
239 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
240 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
240 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
241
241
242 def test_suppress_exception_chaining(self):
242 def test_suppress_exception_chaining(self):
243 if PY3:
243 if PY3:
244 with tt.AssertNotPrints("ZeroDivisionError"), \
244 with tt.AssertNotPrints("ZeroDivisionError"), \
245 tt.AssertPrints("ValueError", suppress=False):
245 tt.AssertPrints("ValueError", suppress=False):
246 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
246 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
247
247
248
248
249 class RecursionTest(unittest.TestCase):
249 class RecursionTest(unittest.TestCase):
250 DEFINITIONS = """
250 DEFINITIONS = """
251 def non_recurs():
251 def non_recurs():
252 1/0
252 1/0
253
253
254 def r1():
254 def r1():
255 r1()
255 r1()
256
256
257 def r3a():
257 def r3a():
258 r3b()
258 r3b()
259
259
260 def r3b():
260 def r3b():
261 r3c()
261 r3c()
262
262
263 def r3c():
263 def r3c():
264 r3a()
264 r3a()
265
265
266 def r3o1():
266 def r3o1():
267 r3a()
267 r3a()
268
268
269 def r3o2():
269 def r3o2():
270 r3o1()
270 r3o1()
271 """
271 """
272 def setUp(self):
272 def setUp(self):
273 ip.run_cell(self.DEFINITIONS)
273 ip.run_cell(self.DEFINITIONS)
274
274
275 def test_no_recursion(self):
275 def test_no_recursion(self):
276 with tt.AssertNotPrints("frames repeated"):
276 with tt.AssertNotPrints("frames repeated"):
277 ip.run_cell("non_recurs()")
277 ip.run_cell("non_recurs()")
278
278
279 def test_recursion_one_frame(self):
279 def test_recursion_one_frame(self):
280 with tt.AssertPrints("1 frames repeated"):
280 with tt.AssertPrints("1 frames repeated"):
281 ip.run_cell("r1()")
281 ip.run_cell("r1()")
282
282
283 def test_recursion_three_frames(self):
283 def test_recursion_three_frames(self):
284 with tt.AssertPrints("3 frames repeated"):
284 with tt.AssertPrints("3 frames repeated"):
285 ip.run_cell("r3o2()")
285 ip.run_cell("r3o2()")
286
286
287 def test_find_recursion(self):
287 def test_find_recursion(self):
288 captured = []
288 captured = []
289 def capture_exc(*args, **kwargs):
289 def capture_exc(*args, **kwargs):
290 captured.append(sys.exc_info())
290 captured.append(sys.exc_info())
291 with mock.patch.object(ip, 'showtraceback', capture_exc):
291 with mock.patch.object(ip, 'showtraceback', capture_exc):
292 ip.run_cell("r3o2()")
292 ip.run_cell("r3o2()")
293
293
294 self.assertEqual(len(captured), 1)
294 self.assertEqual(len(captured), 1)
295 etype, evalue, tb = captured[0]
295 etype, evalue, tb = captured[0]
296 self.assertIn("recursion", str(evalue))
296 self.assertIn("recursion", str(evalue))
297
297
298 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
298 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
299 for r in records[:10]:
299 for r in records[:10]:
300 print(r[1:4])
300 print(r[1:4])
301
301
302 # The outermost frames should be:
302 # The outermost frames should be:
303 # 0: the 'cell' that was running when the exception came up
303 # 0: the 'cell' that was running when the exception came up
304 # 1: r3o2()
304 # 1: r3o2()
305 # 2: r3o1()
305 # 2: r3o1()
306 # 3: r3a()
306 # 3: r3a()
307 # Then repeating r3b, r3c, r3a
307 # Then repeating r3b, r3c, r3a
308 last_unique, repeat_length = find_recursion(etype, evalue, records)
308 last_unique, repeat_length = find_recursion(etype, evalue, records)
309 self.assertEqual(last_unique, 2)
309 self.assertEqual(last_unique, 2)
310 self.assertEqual(repeat_length, 3)
310 self.assertEqual(repeat_length, 3)
311
311
312
312
313 #----------------------------------------------------------------------------
313 #----------------------------------------------------------------------------
314
314
315 # module testing (minimal)
315 # module testing (minimal)
316 if sys.version_info > (3,):
317 def test_handlers():
316 def test_handlers():
318 def spam(c, d_e):
317 def spam(c, d_e):
319 (d, e) = d_e
318 (d, e) = d_e
320 x = c + d
319 x = c + d
321 y = c * d
320 y = c * d
322 foo(x, y)
321 foo(x, y)
323
322
324 def foo(a, b, bar=1):
323 def foo(a, b, bar=1):
325 eggs(a, b + bar)
324 eggs(a, b + bar)
326
325
327 def eggs(f, g, z=globals()):
326 def eggs(f, g, z=globals()):
328 h = f + g
327 h = f + g
329 i = f - g
328 i = f - g
330 return h / i
329 return h / i
331
330
332 buff = io.StringIO()
331 buff = io.StringIO()
333
332
334 buff.write('')
333 buff.write('')
335 buff.write('*** Before ***')
334 buff.write('*** Before ***')
336 try:
335 try:
337 buff.write(spam(1, (2, 3)))
336 buff.write(spam(1, (2, 3)))
338 except:
337 except:
339 traceback.print_exc(file=buff)
338 traceback.print_exc(file=buff)
340
339
341 handler = ColorTB(ostream=buff)
340 handler = ColorTB(ostream=buff)
342 buff.write('*** ColorTB ***')
341 buff.write('*** ColorTB ***')
343 try:
342 try:
344 buff.write(spam(1, (2, 3)))
343 buff.write(spam(1, (2, 3)))
345 except:
344 except:
346 handler(*sys.exc_info())
345 handler(*sys.exc_info())
347 buff.write('')
346 buff.write('')
348
347
349 handler = VerboseTB(ostream=buff)
348 handler = VerboseTB(ostream=buff)
350 buff.write('*** VerboseTB ***')
349 buff.write('*** VerboseTB ***')
351 try:
350 try:
352 buff.write(spam(1, (2, 3)))
351 buff.write(spam(1, (2, 3)))
353 except:
352 except:
354 handler(*sys.exc_info())
353 handler(*sys.exc_info())
355 buff.write('')
354 buff.write('')
356
355
@@ -1,433 +1,432 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) recursively. This
8 calling this script (with different arguments) recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 """
15 """
16
16
17 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import glob
22 import glob
23 from io import BytesIO
23 from io import BytesIO
24 import os
24 import os
25 import os.path as path
25 import os.path as path
26 import sys
26 import sys
27 from threading import Thread, Lock, Event
27 from threading import Thread, Lock, Event
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.plugins.xunit import Xunit
31 from nose.plugins.xunit import Xunit
32 from nose import SkipTest
32 from nose import SkipTest
33 from nose.core import TestProgram
33 from nose.core import TestProgram
34 from nose.plugins import Plugin
34 from nose.plugins import Plugin
35 from nose.util import safe_str
35 from nose.util import safe_str
36
36
37 from IPython import version_info
37 from IPython import version_info
38 from IPython.utils.py3compat import bytes_to_str
38 from IPython.utils.py3compat import bytes_to_str
39 from IPython.utils.importstring import import_item
39 from IPython.utils.importstring import import_item
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 from IPython.external.decorators import KnownFailure, knownfailureif
41 from IPython.external.decorators import KnownFailure, knownfailureif
42
42
43 pjoin = path.join
43 pjoin = path.join
44
44
45
45
46 # Enable printing all warnings raise by IPython's modules
46 # Enable printing all warnings raise by IPython's modules
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
48 if sys.version_info > (3,0):
49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
48 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
50 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
49 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
51 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
50 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
52
51
53 if version_info < (6,):
52 if version_info < (6,):
54 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
53 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
55 # warning with the runner they also import from standard import library. (as of Dec 2015)
54 # warning with the runner they also import from standard import library. (as of Dec 2015)
56 # Ignore, let's revisit that in a couple of years for IPython 6.
55 # Ignore, let's revisit that in a couple of years for IPython 6.
57 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
56 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
58
57
59
58
60 # ------------------------------------------------------------------------------
59 # ------------------------------------------------------------------------------
61 # Monkeypatch Xunit to count known failures as skipped.
60 # Monkeypatch Xunit to count known failures as skipped.
62 # ------------------------------------------------------------------------------
61 # ------------------------------------------------------------------------------
63 def monkeypatch_xunit():
62 def monkeypatch_xunit():
64 try:
63 try:
65 knownfailureif(True)(lambda: None)()
64 knownfailureif(True)(lambda: None)()
66 except Exception as e:
65 except Exception as e:
67 KnownFailureTest = type(e)
66 KnownFailureTest = type(e)
68
67
69 def addError(self, test, err, capt=None):
68 def addError(self, test, err, capt=None):
70 if issubclass(err[0], KnownFailureTest):
69 if issubclass(err[0], KnownFailureTest):
71 err = (SkipTest,) + err[1:]
70 err = (SkipTest,) + err[1:]
72 return self.orig_addError(test, err, capt)
71 return self.orig_addError(test, err, capt)
73
72
74 Xunit.orig_addError = Xunit.addError
73 Xunit.orig_addError = Xunit.addError
75 Xunit.addError = addError
74 Xunit.addError = addError
76
75
77 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
78 # Check which dependencies are installed and greater than minimum version.
77 # Check which dependencies are installed and greater than minimum version.
79 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
80 def extract_version(mod):
79 def extract_version(mod):
81 return mod.__version__
80 return mod.__version__
82
81
83 def test_for(item, min_version=None, callback=extract_version):
82 def test_for(item, min_version=None, callback=extract_version):
84 """Test to see if item is importable, and optionally check against a minimum
83 """Test to see if item is importable, and optionally check against a minimum
85 version.
84 version.
86
85
87 If min_version is given, the default behavior is to check against the
86 If min_version is given, the default behavior is to check against the
88 `__version__` attribute of the item, but specifying `callback` allows you to
87 `__version__` attribute of the item, but specifying `callback` allows you to
89 extract the value you are interested in. e.g::
88 extract the value you are interested in. e.g::
90
89
91 In [1]: import sys
90 In [1]: import sys
92
91
93 In [2]: from IPython.testing.iptest import test_for
92 In [2]: from IPython.testing.iptest import test_for
94
93
95 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
94 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
96 Out[3]: True
95 Out[3]: True
97
96
98 """
97 """
99 try:
98 try:
100 check = import_item(item)
99 check = import_item(item)
101 except (ImportError, RuntimeError):
100 except (ImportError, RuntimeError):
102 # GTK reports Runtime error if it can't be initialized even if it's
101 # GTK reports Runtime error if it can't be initialized even if it's
103 # importable.
102 # importable.
104 return False
103 return False
105 else:
104 else:
106 if min_version:
105 if min_version:
107 if callback:
106 if callback:
108 # extra processing step to get version to compare
107 # extra processing step to get version to compare
109 check = callback(check)
108 check = callback(check)
110
109
111 return check >= min_version
110 return check >= min_version
112 else:
111 else:
113 return True
112 return True
114
113
115 # Global dict where we can store information on what we have and what we don't
114 # Global dict where we can store information on what we have and what we don't
116 # have available at test run time
115 # have available at test run time
117 have = {'matplotlib': test_for('matplotlib'),
116 have = {'matplotlib': test_for('matplotlib'),
118 'pygments': test_for('pygments'),
117 'pygments': test_for('pygments'),
119 'sqlite3': test_for('sqlite3')}
118 'sqlite3': test_for('sqlite3')}
120
119
121 #-----------------------------------------------------------------------------
120 #-----------------------------------------------------------------------------
122 # Test suite definitions
121 # Test suite definitions
123 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
124
123
125 test_group_names = ['core',
124 test_group_names = ['core',
126 'extensions', 'lib', 'terminal', 'testing', 'utils',
125 'extensions', 'lib', 'terminal', 'testing', 'utils',
127 ]
126 ]
128
127
129 class TestSection(object):
128 class TestSection(object):
130 def __init__(self, name, includes):
129 def __init__(self, name, includes):
131 self.name = name
130 self.name = name
132 self.includes = includes
131 self.includes = includes
133 self.excludes = []
132 self.excludes = []
134 self.dependencies = []
133 self.dependencies = []
135 self.enabled = True
134 self.enabled = True
136
135
137 def exclude(self, module):
136 def exclude(self, module):
138 if not module.startswith('IPython'):
137 if not module.startswith('IPython'):
139 module = self.includes[0] + "." + module
138 module = self.includes[0] + "." + module
140 self.excludes.append(module.replace('.', os.sep))
139 self.excludes.append(module.replace('.', os.sep))
141
140
142 def requires(self, *packages):
141 def requires(self, *packages):
143 self.dependencies.extend(packages)
142 self.dependencies.extend(packages)
144
143
145 @property
144 @property
146 def will_run(self):
145 def will_run(self):
147 return self.enabled and all(have[p] for p in self.dependencies)
146 return self.enabled and all(have[p] for p in self.dependencies)
148
147
149 # Name -> (include, exclude, dependencies_met)
148 # Name -> (include, exclude, dependencies_met)
150 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
149 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
151
150
152
151
153 # Exclusions and dependencies
152 # Exclusions and dependencies
154 # ---------------------------
153 # ---------------------------
155
154
156 # core:
155 # core:
157 sec = test_sections['core']
156 sec = test_sections['core']
158 if not have['sqlite3']:
157 if not have['sqlite3']:
159 sec.exclude('tests.test_history')
158 sec.exclude('tests.test_history')
160 sec.exclude('history')
159 sec.exclude('history')
161 if not have['matplotlib']:
160 if not have['matplotlib']:
162 sec.exclude('pylabtools'),
161 sec.exclude('pylabtools'),
163 sec.exclude('tests.test_pylabtools')
162 sec.exclude('tests.test_pylabtools')
164
163
165 # lib:
164 # lib:
166 sec = test_sections['lib']
165 sec = test_sections['lib']
167 sec.exclude('kernel')
166 sec.exclude('kernel')
168 if not have['pygments']:
167 if not have['pygments']:
169 sec.exclude('tests.test_lexers')
168 sec.exclude('tests.test_lexers')
170 # We do this unconditionally, so that the test suite doesn't import
169 # We do this unconditionally, so that the test suite doesn't import
171 # gtk, changing the default encoding and masking some unicode bugs.
170 # gtk, changing the default encoding and masking some unicode bugs.
172 sec.exclude('inputhookgtk')
171 sec.exclude('inputhookgtk')
173 # We also do this unconditionally, because wx can interfere with Unix signals.
172 # We also do this unconditionally, because wx can interfere with Unix signals.
174 # There are currently no tests for it anyway.
173 # There are currently no tests for it anyway.
175 sec.exclude('inputhookwx')
174 sec.exclude('inputhookwx')
176 # Testing inputhook will need a lot of thought, to figure out
175 # Testing inputhook will need a lot of thought, to figure out
177 # how to have tests that don't lock up with the gui event
176 # how to have tests that don't lock up with the gui event
178 # loops in the picture
177 # loops in the picture
179 sec.exclude('inputhook')
178 sec.exclude('inputhook')
180
179
181 # testing:
180 # testing:
182 sec = test_sections['testing']
181 sec = test_sections['testing']
183 # These have to be skipped on win32 because they use echo, rm, cd, etc.
182 # These have to be skipped on win32 because they use echo, rm, cd, etc.
184 # See ticket https://github.com/ipython/ipython/issues/87
183 # See ticket https://github.com/ipython/ipython/issues/87
185 if sys.platform == 'win32':
184 if sys.platform == 'win32':
186 sec.exclude('plugin.test_exampleip')
185 sec.exclude('plugin.test_exampleip')
187 sec.exclude('plugin.dtexample')
186 sec.exclude('plugin.dtexample')
188
187
189 # don't run jupyter_console tests found via shim
188 # don't run jupyter_console tests found via shim
190 test_sections['terminal'].exclude('console')
189 test_sections['terminal'].exclude('console')
191
190
192 # extensions:
191 # extensions:
193 sec = test_sections['extensions']
192 sec = test_sections['extensions']
194 # This is deprecated in favour of rpy2
193 # This is deprecated in favour of rpy2
195 sec.exclude('rmagic')
194 sec.exclude('rmagic')
196 # autoreload does some strange stuff, so move it to its own test section
195 # autoreload does some strange stuff, so move it to its own test section
197 sec.exclude('autoreload')
196 sec.exclude('autoreload')
198 sec.exclude('tests.test_autoreload')
197 sec.exclude('tests.test_autoreload')
199 test_sections['autoreload'] = TestSection('autoreload',
198 test_sections['autoreload'] = TestSection('autoreload',
200 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
199 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
201 test_group_names.append('autoreload')
200 test_group_names.append('autoreload')
202
201
203
202
204 #-----------------------------------------------------------------------------
203 #-----------------------------------------------------------------------------
205 # Functions and classes
204 # Functions and classes
206 #-----------------------------------------------------------------------------
205 #-----------------------------------------------------------------------------
207
206
208 def check_exclusions_exist():
207 def check_exclusions_exist():
209 from IPython.paths import get_ipython_package_dir
208 from IPython.paths import get_ipython_package_dir
210 from warnings import warn
209 from warnings import warn
211 parent = os.path.dirname(get_ipython_package_dir())
210 parent = os.path.dirname(get_ipython_package_dir())
212 for sec in test_sections:
211 for sec in test_sections:
213 for pattern in sec.exclusions:
212 for pattern in sec.exclusions:
214 fullpath = pjoin(parent, pattern)
213 fullpath = pjoin(parent, pattern)
215 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
214 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
216 warn("Excluding nonexistent file: %r" % pattern)
215 warn("Excluding nonexistent file: %r" % pattern)
217
216
218
217
219 class ExclusionPlugin(Plugin):
218 class ExclusionPlugin(Plugin):
220 """A nose plugin to effect our exclusions of files and directories.
219 """A nose plugin to effect our exclusions of files and directories.
221 """
220 """
222 name = 'exclusions'
221 name = 'exclusions'
223 score = 3000 # Should come before any other plugins
222 score = 3000 # Should come before any other plugins
224
223
225 def __init__(self, exclude_patterns=None):
224 def __init__(self, exclude_patterns=None):
226 """
225 """
227 Parameters
226 Parameters
228 ----------
227 ----------
229
228
230 exclude_patterns : sequence of strings, optional
229 exclude_patterns : sequence of strings, optional
231 Filenames containing these patterns (as raw strings, not as regular
230 Filenames containing these patterns (as raw strings, not as regular
232 expressions) are excluded from the tests.
231 expressions) are excluded from the tests.
233 """
232 """
234 self.exclude_patterns = exclude_patterns or []
233 self.exclude_patterns = exclude_patterns or []
235 super(ExclusionPlugin, self).__init__()
234 super(ExclusionPlugin, self).__init__()
236
235
237 def options(self, parser, env=os.environ):
236 def options(self, parser, env=os.environ):
238 Plugin.options(self, parser, env)
237 Plugin.options(self, parser, env)
239
238
240 def configure(self, options, config):
239 def configure(self, options, config):
241 Plugin.configure(self, options, config)
240 Plugin.configure(self, options, config)
242 # Override nose trying to disable plugin.
241 # Override nose trying to disable plugin.
243 self.enabled = True
242 self.enabled = True
244
243
245 def wantFile(self, filename):
244 def wantFile(self, filename):
246 """Return whether the given filename should be scanned for tests.
245 """Return whether the given filename should be scanned for tests.
247 """
246 """
248 if any(pat in filename for pat in self.exclude_patterns):
247 if any(pat in filename for pat in self.exclude_patterns):
249 return False
248 return False
250 return None
249 return None
251
250
252 def wantDirectory(self, directory):
251 def wantDirectory(self, directory):
253 """Return whether the given directory should be scanned for tests.
252 """Return whether the given directory should be scanned for tests.
254 """
253 """
255 if any(pat in directory for pat in self.exclude_patterns):
254 if any(pat in directory for pat in self.exclude_patterns):
256 return False
255 return False
257 return None
256 return None
258
257
259
258
260 class StreamCapturer(Thread):
259 class StreamCapturer(Thread):
261 daemon = True # Don't hang if main thread crashes
260 daemon = True # Don't hang if main thread crashes
262 started = False
261 started = False
263 def __init__(self, echo=False):
262 def __init__(self, echo=False):
264 super(StreamCapturer, self).__init__()
263 super(StreamCapturer, self).__init__()
265 self.echo = echo
264 self.echo = echo
266 self.streams = []
265 self.streams = []
267 self.buffer = BytesIO()
266 self.buffer = BytesIO()
268 self.readfd, self.writefd = os.pipe()
267 self.readfd, self.writefd = os.pipe()
269 self.buffer_lock = Lock()
268 self.buffer_lock = Lock()
270 self.stop = Event()
269 self.stop = Event()
271
270
272 def run(self):
271 def run(self):
273 self.started = True
272 self.started = True
274
273
275 while not self.stop.is_set():
274 while not self.stop.is_set():
276 chunk = os.read(self.readfd, 1024)
275 chunk = os.read(self.readfd, 1024)
277
276
278 with self.buffer_lock:
277 with self.buffer_lock:
279 self.buffer.write(chunk)
278 self.buffer.write(chunk)
280 if self.echo:
279 if self.echo:
281 sys.stdout.write(bytes_to_str(chunk))
280 sys.stdout.write(bytes_to_str(chunk))
282
281
283 os.close(self.readfd)
282 os.close(self.readfd)
284 os.close(self.writefd)
283 os.close(self.writefd)
285
284
286 def reset_buffer(self):
285 def reset_buffer(self):
287 with self.buffer_lock:
286 with self.buffer_lock:
288 self.buffer.truncate(0)
287 self.buffer.truncate(0)
289 self.buffer.seek(0)
288 self.buffer.seek(0)
290
289
291 def get_buffer(self):
290 def get_buffer(self):
292 with self.buffer_lock:
291 with self.buffer_lock:
293 return self.buffer.getvalue()
292 return self.buffer.getvalue()
294
293
295 def ensure_started(self):
294 def ensure_started(self):
296 if not self.started:
295 if not self.started:
297 self.start()
296 self.start()
298
297
299 def halt(self):
298 def halt(self):
300 """Safely stop the thread."""
299 """Safely stop the thread."""
301 if not self.started:
300 if not self.started:
302 return
301 return
303
302
304 self.stop.set()
303 self.stop.set()
305 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
304 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
306 self.join()
305 self.join()
307
306
308 class SubprocessStreamCapturePlugin(Plugin):
307 class SubprocessStreamCapturePlugin(Plugin):
309 name='subprocstreams'
308 name='subprocstreams'
310 def __init__(self):
309 def __init__(self):
311 Plugin.__init__(self)
310 Plugin.__init__(self)
312 self.stream_capturer = StreamCapturer()
311 self.stream_capturer = StreamCapturer()
313 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
312 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
314 # This is ugly, but distant parts of the test machinery need to be able
313 # This is ugly, but distant parts of the test machinery need to be able
315 # to redirect streams, so we make the object globally accessible.
314 # to redirect streams, so we make the object globally accessible.
316 nose.iptest_stdstreams_fileno = self.get_write_fileno
315 nose.iptest_stdstreams_fileno = self.get_write_fileno
317
316
318 def get_write_fileno(self):
317 def get_write_fileno(self):
319 if self.destination == 'capture':
318 if self.destination == 'capture':
320 self.stream_capturer.ensure_started()
319 self.stream_capturer.ensure_started()
321 return self.stream_capturer.writefd
320 return self.stream_capturer.writefd
322 elif self.destination == 'discard':
321 elif self.destination == 'discard':
323 return os.open(os.devnull, os.O_WRONLY)
322 return os.open(os.devnull, os.O_WRONLY)
324 else:
323 else:
325 return sys.__stdout__.fileno()
324 return sys.__stdout__.fileno()
326
325
327 def configure(self, options, config):
326 def configure(self, options, config):
328 Plugin.configure(self, options, config)
327 Plugin.configure(self, options, config)
329 # Override nose trying to disable plugin.
328 # Override nose trying to disable plugin.
330 if self.destination == 'capture':
329 if self.destination == 'capture':
331 self.enabled = True
330 self.enabled = True
332
331
333 def startTest(self, test):
332 def startTest(self, test):
334 # Reset log capture
333 # Reset log capture
335 self.stream_capturer.reset_buffer()
334 self.stream_capturer.reset_buffer()
336
335
337 def formatFailure(self, test, err):
336 def formatFailure(self, test, err):
338 # Show output
337 # Show output
339 ec, ev, tb = err
338 ec, ev, tb = err
340 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
339 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
341 if captured.strip():
340 if captured.strip():
342 ev = safe_str(ev)
341 ev = safe_str(ev)
343 out = [ev, '>> begin captured subprocess output <<',
342 out = [ev, '>> begin captured subprocess output <<',
344 captured,
343 captured,
345 '>> end captured subprocess output <<']
344 '>> end captured subprocess output <<']
346 return ec, '\n'.join(out), tb
345 return ec, '\n'.join(out), tb
347
346
348 return err
347 return err
349
348
350 formatError = formatFailure
349 formatError = formatFailure
351
350
352 def finalize(self, result):
351 def finalize(self, result):
353 self.stream_capturer.halt()
352 self.stream_capturer.halt()
354
353
355
354
356 def run_iptest():
355 def run_iptest():
357 """Run the IPython test suite using nose.
356 """Run the IPython test suite using nose.
358
357
359 This function is called when this script is **not** called with the form
358 This function is called when this script is **not** called with the form
360 `iptest all`. It simply calls nose with appropriate command line flags
359 `iptest all`. It simply calls nose with appropriate command line flags
361 and accepts all of the standard nose arguments.
360 and accepts all of the standard nose arguments.
362 """
361 """
363 # Apply our monkeypatch to Xunit
362 # Apply our monkeypatch to Xunit
364 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
363 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
365 monkeypatch_xunit()
364 monkeypatch_xunit()
366
365
367 arg1 = sys.argv[1]
366 arg1 = sys.argv[1]
368 if arg1 in test_sections:
367 if arg1 in test_sections:
369 section = test_sections[arg1]
368 section = test_sections[arg1]
370 sys.argv[1:2] = section.includes
369 sys.argv[1:2] = section.includes
371 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
370 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
372 section = test_sections[arg1[8:]]
371 section = test_sections[arg1[8:]]
373 sys.argv[1:2] = section.includes
372 sys.argv[1:2] = section.includes
374 else:
373 else:
375 section = TestSection(arg1, includes=[arg1])
374 section = TestSection(arg1, includes=[arg1])
376
375
377
376
378 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
377 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
379 # We add --exe because of setuptools' imbecility (it
378 # We add --exe because of setuptools' imbecility (it
380 # blindly does chmod +x on ALL files). Nose does the
379 # blindly does chmod +x on ALL files). Nose does the
381 # right thing and it tries to avoid executables,
380 # right thing and it tries to avoid executables,
382 # setuptools unfortunately forces our hand here. This
381 # setuptools unfortunately forces our hand here. This
383 # has been discussed on the distutils list and the
382 # has been discussed on the distutils list and the
384 # setuptools devs refuse to fix this problem!
383 # setuptools devs refuse to fix this problem!
385 '--exe',
384 '--exe',
386 ]
385 ]
387 if '-a' not in argv and '-A' not in argv:
386 if '-a' not in argv and '-A' not in argv:
388 argv = argv + ['-a', '!crash']
387 argv = argv + ['-a', '!crash']
389
388
390 if nose.__version__ >= '0.11':
389 if nose.__version__ >= '0.11':
391 # I don't fully understand why we need this one, but depending on what
390 # I don't fully understand why we need this one, but depending on what
392 # directory the test suite is run from, if we don't give it, 0 tests
391 # directory the test suite is run from, if we don't give it, 0 tests
393 # get run. Specifically, if the test suite is run from the source dir
392 # get run. Specifically, if the test suite is run from the source dir
394 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
393 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
395 # even if the same call done in this directory works fine). It appears
394 # even if the same call done in this directory works fine). It appears
396 # that if the requested package is in the current dir, nose bails early
395 # that if the requested package is in the current dir, nose bails early
397 # by default. Since it's otherwise harmless, leave it in by default
396 # by default. Since it's otherwise harmless, leave it in by default
398 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
397 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
399 argv.append('--traverse-namespace')
398 argv.append('--traverse-namespace')
400
399
401 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
400 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
402 SubprocessStreamCapturePlugin() ]
401 SubprocessStreamCapturePlugin() ]
403
402
404 # we still have some vestigial doctests in core
403 # we still have some vestigial doctests in core
405 if (section.name.startswith(('core', 'IPython.core'))):
404 if (section.name.startswith(('core', 'IPython.core'))):
406 plugins.append(IPythonDoctest())
405 plugins.append(IPythonDoctest())
407 argv.extend([
406 argv.extend([
408 '--with-ipdoctest',
407 '--with-ipdoctest',
409 '--ipdoctest-tests',
408 '--ipdoctest-tests',
410 '--ipdoctest-extension=txt',
409 '--ipdoctest-extension=txt',
411 ])
410 ])
412
411
413
412
414 # Use working directory set by parent process (see iptestcontroller)
413 # Use working directory set by parent process (see iptestcontroller)
415 if 'IPTEST_WORKING_DIR' in os.environ:
414 if 'IPTEST_WORKING_DIR' in os.environ:
416 os.chdir(os.environ['IPTEST_WORKING_DIR'])
415 os.chdir(os.environ['IPTEST_WORKING_DIR'])
417
416
418 # We need a global ipython running in this process, but the special
417 # We need a global ipython running in this process, but the special
419 # in-process group spawns its own IPython kernels, so for *that* group we
418 # in-process group spawns its own IPython kernels, so for *that* group we
420 # must avoid also opening the global one (otherwise there's a conflict of
419 # must avoid also opening the global one (otherwise there's a conflict of
421 # singletons). Ultimately the solution to this problem is to refactor our
420 # singletons). Ultimately the solution to this problem is to refactor our
422 # assumptions about what needs to be a singleton and what doesn't (app
421 # assumptions about what needs to be a singleton and what doesn't (app
423 # objects should, individual shells shouldn't). But for now, this
422 # objects should, individual shells shouldn't). But for now, this
424 # workaround allows the test suite for the inprocess module to complete.
423 # workaround allows the test suite for the inprocess module to complete.
425 if 'kernel.inprocess' not in section.name:
424 if 'kernel.inprocess' not in section.name:
426 from IPython.testing import globalipapp
425 from IPython.testing import globalipapp
427 globalipapp.start_ipython()
426 globalipapp.start_ipython()
428
427
429 # Now nose can run
428 # Now nose can run
430 TestProgram(argv=argv, addplugins=plugins)
429 TestProgram(argv=argv, addplugins=plugins)
431
430
432 if __name__ == '__main__':
431 if __name__ == '__main__':
433 run_iptest()
432 run_iptest()
@@ -1,43 +1,30 b''
1 """Decorators marks that a doctest should be skipped, for both python 2 and 3.
1 """Decorators marks that a doctest should be skipped.
2
2
3 The IPython.testing.decorators module triggers various extra imports, including
3 The IPython.testing.decorators module triggers various extra imports, including
4 numpy and sympy if they're present. Since this decorator is used in core parts
4 numpy and sympy if they're present. Since this decorator is used in core parts
5 of IPython, it's in a separate module so that running IPython doesn't trigger
5 of IPython, it's in a separate module so that running IPython doesn't trigger
6 those imports."""
6 those imports."""
7
7
8 #-----------------------------------------------------------------------------
8 # Copyright (C) IPython Development Team
9 # Copyright (C) 2009-2011 The IPython Development Team
9 # Distributed under the terms of the Modified BSD License.
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
10
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import sys
20
21 #-----------------------------------------------------------------------------
22 # Decorators
23 #-----------------------------------------------------------------------------
24
11
25 def skip_doctest(f):
12 def skip_doctest(f):
26 """Decorator - mark a function or method for skipping its doctest.
13 """Decorator - mark a function or method for skipping its doctest.
27
14
28 This decorator allows you to mark a function whose docstring you wish to
15 This decorator allows you to mark a function whose docstring you wish to
29 omit from testing, while preserving the docstring for introspection, help,
16 omit from testing, while preserving the docstring for introspection, help,
30 etc."""
17 etc."""
31 f.skip_doctest = True
18 f.skip_doctest = True
32 return f
19 return f
33
20
34
21
35 def skip_doctest_py3(f):
22 def skip_doctest_py3(f):
36 """Decorator - skip the doctest under Python 3."""
23 """Decorator - skip the doctest under Python 3."""
37 f.skip_doctest = (sys.version_info[0] >= 3)
24 f.skip_doctest = (sys.version_info[0] >= 3)
38 return f
25 return f
39
26
40 def skip_doctest_py2(f):
27 def skip_doctest_py2(f):
41 """Decorator - skip the doctest under Python 3."""
28 """Decorator - skip the doctest under Python 3."""
42 f.skip_doctest = (sys.version_info[0] < 3)
29 f.skip_doctest = (sys.version_info[0] < 3)
43 return f
30 return f
This diff has been collapsed as it changes many lines, (594 lines changed) Show them Hide them
@@ -1,9 +1,595 b''
1 """Load our patched versions of tokenize.
1 """Patched version of standard library tokenize, to deal with various bugs.
2
3 Based on Python 3.2 code.
4
5 Patches:
6
7 - Gareth Rees' patch for Python issue #12691 (untokenizing)
8 - Except we don't encode the output of untokenize
9 - Python 2 compatible syntax, so that it can be byte-compiled at installation
10 - Newlines in comments and blank lines should be either NL or NEWLINE, depending
11 on whether they are in a multi-line statement. Filed as Python issue #17061.
12 - Export generate_tokens & TokenError
13 - u and rb literals are allowed under Python 3.3 and above.
14
15 ------------------------------------------------------------------------------
16 Tokenization help for Python programs.
17
18 tokenize(readline) is a generator that breaks a stream of bytes into
19 Python tokens. It decodes the bytes according to PEP-0263 for
20 determining source file encoding.
21
22 It accepts a readline-like method which is called repeatedly to get the
23 next line of input (or b"" for EOF). It generates 5-tuples with these
24 members:
25
26 the token type (see token.py)
27 the token (a string)
28 the starting (row, column) indices of the token (a 2-tuple of ints)
29 the ending (row, column) indices of the token (a 2-tuple of ints)
30 the original line (string)
31
32 It is designed to match the working of the Python tokenizer exactly, except
33 that it produces COMMENT tokens for comments and gives type OP for all
34 operators. Additionally, all token lists start with an ENCODING token
35 which tells you which encoding was used to decode the bytes stream.
2 """
36 """
37 from __future__ import absolute_import
3
38
39 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
40 __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, '
41 'Skip Montanaro, Raymond Hettinger, Trent Nelson, '
42 'Michael Foord')
43 import builtins
44 import re
4 import sys
45 import sys
46 from token import *
47 from codecs import lookup, BOM_UTF8
48 import collections
49 from io import TextIOWrapper
50 cookie_re = re.compile("coding[:=]\s*([-\w.]+)")
51
52 import token
53 __all__ = token.__all__ + ["COMMENT", "tokenize", "detect_encoding",
54 "NL", "untokenize", "ENCODING", "TokenInfo"]
55 del token
56
57 __all__ += ["generate_tokens", "TokenError"]
58
59 COMMENT = N_TOKENS
60 tok_name[COMMENT] = 'COMMENT'
61 NL = N_TOKENS + 1
62 tok_name[NL] = 'NL'
63 ENCODING = N_TOKENS + 2
64 tok_name[ENCODING] = 'ENCODING'
65 N_TOKENS += 3
66
67 class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')):
68 def __repr__(self):
69 annotated_type = '%d (%s)' % (self.type, tok_name[self.type])
70 return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' %
71 self._replace(type=annotated_type))
72
73 def group(*choices): return '(' + '|'.join(choices) + ')'
74 def any(*choices): return group(*choices) + '*'
75 def maybe(*choices): return group(*choices) + '?'
76
77 # Note: we use unicode matching for names ("\w") but ascii matching for
78 # number literals.
79 Whitespace = r'[ \f\t]*'
80 Comment = r'#[^\r\n]*'
81 Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
82 Name = r'\w+'
83
84 Hexnumber = r'0[xX][0-9a-fA-F]+'
85 Binnumber = r'0[bB][01]+'
86 Octnumber = r'0[oO][0-7]+'
87 Decnumber = r'(?:0+|[1-9][0-9]*)'
88 Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber)
89 Exponent = r'[eE][-+]?[0-9]+'
90 Pointfloat = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(Exponent)
91 Expfloat = r'[0-9]+' + Exponent
92 Floatnumber = group(Pointfloat, Expfloat)
93 Imagnumber = group(r'[0-9]+[jJ]', Floatnumber + r'[jJ]')
94 Number = group(Imagnumber, Floatnumber, Intnumber)
95
96 if sys.version_info.minor >= 3:
97 StringPrefix = r'(?:[bB][rR]?|[rR][bB]?|[uU])?'
98 else:
99 StringPrefix = r'(?:[bB]?[rR]?)?'
100
101 # Tail end of ' string.
102 Single = r"[^'\\]*(?:\\.[^'\\]*)*'"
103 # Tail end of " string.
104 Double = r'[^"\\]*(?:\\.[^"\\]*)*"'
105 # Tail end of ''' string.
106 Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
107 # Tail end of """ string.
108 Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
109 Triple = group(StringPrefix + "'''", StringPrefix + '"""')
110 # Single-line ' or " string.
111 String = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
112 StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
113
114 # Because of leftmost-then-longest match semantics, be sure to put the
115 # longest operators first (e.g., if = came before ==, == would get
116 # recognized as two instances of =).
117 Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"!=",
118 r"//=?", r"->",
119 r"[+\-*/%&|^=<>]=?",
120 r"~")
121
122 Bracket = '[][(){}]'
123 Special = group(r'\r?\n', r'\.\.\.', r'[:;.,@]')
124 Funny = group(Operator, Bracket, Special)
125
126 PlainToken = group(Number, Funny, String, Name)
127 Token = Ignore + PlainToken
128
129 # First (or only) line of ' or " string.
130 ContStr = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
131 group("'", r'\\\r?\n'),
132 StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
133 group('"', r'\\\r?\n'))
134 PseudoExtras = group(r'\\\r?\n', Comment, Triple)
135 PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
136
137 def _compile(expr):
138 return re.compile(expr, re.UNICODE)
139
140 tokenprog, pseudoprog, single3prog, double3prog = map(
141 _compile, (Token, PseudoToken, Single3, Double3))
142 endprogs = {"'": _compile(Single), '"': _compile(Double),
143 "'''": single3prog, '"""': double3prog,
144 "r'''": single3prog, 'r"""': double3prog,
145 "b'''": single3prog, 'b"""': double3prog,
146 "R'''": single3prog, 'R"""': double3prog,
147 "B'''": single3prog, 'B"""': double3prog,
148 "br'''": single3prog, 'br"""': double3prog,
149 "bR'''": single3prog, 'bR"""': double3prog,
150 "Br'''": single3prog, 'Br"""': double3prog,
151 "BR'''": single3prog, 'BR"""': double3prog,
152 'r': None, 'R': None, 'b': None, 'B': None}
153
154 triple_quoted = {}
155 for t in ("'''", '"""',
156 "r'''", 'r"""', "R'''", 'R"""',
157 "b'''", 'b"""', "B'''", 'B"""',
158 "br'''", 'br"""', "Br'''", 'Br"""',
159 "bR'''", 'bR"""', "BR'''", 'BR"""'):
160 triple_quoted[t] = t
161 single_quoted = {}
162 for t in ("'", '"',
163 "r'", 'r"', "R'", 'R"',
164 "b'", 'b"', "B'", 'B"',
165 "br'", 'br"', "Br'", 'Br"',
166 "bR'", 'bR"', "BR'", 'BR"' ):
167 single_quoted[t] = t
168
169 if sys.version_info.minor >= 3:
170 # Python 3.3
171 for _prefix in ['rb', 'rB', 'Rb', 'RB', 'u', 'U']:
172 _t2 = _prefix+'"""'
173 endprogs[_t2] = double3prog
174 triple_quoted[_t2] = _t2
175 _t1 = _prefix + "'''"
176 endprogs[_t1] = single3prog
177 triple_quoted[_t1] = _t1
178 single_quoted[_prefix+'"'] = _prefix+'"'
179 single_quoted[_prefix+"'"] = _prefix+"'"
180 del _prefix, _t2, _t1
181 endprogs['u'] = None
182 endprogs['U'] = None
183
184 del _compile
185
186 tabsize = 8
187
188 class TokenError(Exception): pass
189
190 class StopTokenizing(Exception): pass
191
192
193 class Untokenizer:
194
195 def __init__(self):
196 self.tokens = []
197 self.prev_row = 1
198 self.prev_col = 0
199 self.encoding = 'utf-8'
200
201 def add_whitespace(self, tok_type, start):
202 row, col = start
203 assert row >= self.prev_row
204 col_offset = col - self.prev_col
205 if col_offset > 0:
206 self.tokens.append(" " * col_offset)
207 elif row > self.prev_row and tok_type not in (NEWLINE, NL, ENDMARKER):
208 # Line was backslash-continued.
209 self.tokens.append(" ")
210
211 def untokenize(self, tokens):
212 iterable = iter(tokens)
213 for t in iterable:
214 if len(t) == 2:
215 self.compat(t, iterable)
216 break
217 tok_type, token, start, end = t[:4]
218 if tok_type == ENCODING:
219 self.encoding = token
220 continue
221 self.add_whitespace(tok_type, start)
222 self.tokens.append(token)
223 self.prev_row, self.prev_col = end
224 if tok_type in (NEWLINE, NL):
225 self.prev_row += 1
226 self.prev_col = 0
227 return "".join(self.tokens)
228
229 def compat(self, token, iterable):
230 # This import is here to avoid problems when the itertools
231 # module is not built yet and tokenize is imported.
232 from itertools import chain
233 startline = False
234 prevstring = False
235 indents = []
236 toks_append = self.tokens.append
237
238 for tok in chain([token], iterable):
239 toknum, tokval = tok[:2]
240 if toknum == ENCODING:
241 self.encoding = tokval
242 continue
243
244 if toknum in (NAME, NUMBER):
245 tokval += ' '
246
247 # Insert a space between two consecutive strings
248 if toknum == STRING:
249 if prevstring:
250 tokval = ' ' + tokval
251 prevstring = True
252 else:
253 prevstring = False
254
255 if toknum == INDENT:
256 indents.append(tokval)
257 continue
258 elif toknum == DEDENT:
259 indents.pop()
260 continue
261 elif toknum in (NEWLINE, NL):
262 startline = True
263 elif startline and indents:
264 toks_append(indents[-1])
265 startline = False
266 toks_append(tokval)
267
268
269 def untokenize(tokens):
270 """
271 Convert ``tokens`` (an iterable) back into Python source code. Return
272 a bytes object, encoded using the encoding specified by the last
273 ENCODING token in ``tokens``, or UTF-8 if no ENCODING token is found.
274
275 The result is guaranteed to tokenize back to match the input so that
276 the conversion is lossless and round-trips are assured. The
277 guarantee applies only to the token type and token string as the
278 spacing between tokens (column positions) may change.
279
280 :func:`untokenize` has two modes. If the input tokens are sequences
281 of length 2 (``type``, ``string``) then spaces are added as necessary to
282 preserve the round-trip property.
283
284 If the input tokens are sequences of length 4 or more (``type``,
285 ``string``, ``start``, ``end``), as returned by :func:`tokenize`, then
286 spaces are added so that each token appears in the result at the
287 position indicated by ``start`` and ``end``, if possible.
288 """
289 return Untokenizer().untokenize(tokens)
290
291
292 def _get_normal_name(orig_enc):
293 """Imitates get_normal_name in tokenizer.c."""
294 # Only care about the first 12 characters.
295 enc = orig_enc[:12].lower().replace("_", "-")
296 if enc == "utf-8" or enc.startswith("utf-8-"):
297 return "utf-8"
298 if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \
299 enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")):
300 return "iso-8859-1"
301 return orig_enc
302
303 def detect_encoding(readline):
304 """
305 The detect_encoding() function is used to detect the encoding that should
306 be used to decode a Python source file. It requires one argment, readline,
307 in the same way as the tokenize() generator.
308
309 It will call readline a maximum of twice, and return the encoding used
310 (as a string) and a list of any lines (left as bytes) it has read in.
311
312 It detects the encoding from the presence of a utf-8 bom or an encoding
313 cookie as specified in pep-0263. If both a bom and a cookie are present,
314 but disagree, a SyntaxError will be raised. If the encoding cookie is an
315 invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found,
316 'utf-8-sig' is returned.
317
318 If no encoding is specified, then the default of 'utf-8' will be returned.
319 """
320 bom_found = False
321 encoding = None
322 default = 'utf-8'
323 def read_or_stop():
324 try:
325 return readline()
326 except StopIteration:
327 return b''
328
329 def find_cookie(line):
330 try:
331 # Decode as UTF-8. Either the line is an encoding declaration,
332 # in which case it should be pure ASCII, or it must be UTF-8
333 # per default encoding.
334 line_string = line.decode('utf-8')
335 except UnicodeDecodeError:
336 raise SyntaxError("invalid or missing encoding declaration")
337
338 matches = cookie_re.findall(line_string)
339 if not matches:
340 return None
341 encoding = _get_normal_name(matches[0])
342 try:
343 codec = lookup(encoding)
344 except LookupError:
345 # This behaviour mimics the Python interpreter
346 raise SyntaxError("unknown encoding: " + encoding)
347
348 if bom_found:
349 if encoding != 'utf-8':
350 # This behaviour mimics the Python interpreter
351 raise SyntaxError('encoding problem: utf-8')
352 encoding += '-sig'
353 return encoding
354
355 first = read_or_stop()
356 if first.startswith(BOM_UTF8):
357 bom_found = True
358 first = first[3:]
359 default = 'utf-8-sig'
360 if not first:
361 return default, []
362
363 encoding = find_cookie(first)
364 if encoding:
365 return encoding, [first]
366
367 second = read_or_stop()
368 if not second:
369 return default, [first]
370
371 encoding = find_cookie(second)
372 if encoding:
373 return encoding, [first, second]
374
375 return default, [first, second]
376
377
378 def open(filename):
379 """Open a file in read only mode using the encoding detected by
380 detect_encoding().
381 """
382 buffer = builtins.open(filename, 'rb')
383 encoding, lines = detect_encoding(buffer.readline)
384 buffer.seek(0)
385 text = TextIOWrapper(buffer, encoding, line_buffering=True)
386 text.mode = 'r'
387 return text
388
389
390 def tokenize(readline):
391 """
392 The tokenize() generator requires one argment, readline, which
393 must be a callable object which provides the same interface as the
394 readline() method of built-in file objects. Each call to the function
395 should return one line of input as bytes. Alternately, readline
396 can be a callable function terminating with StopIteration:
397 readline = open(myfile, 'rb').__next__ # Example of alternate readline
398
399 The generator produces 5-tuples with these members: the token type; the
400 token string; a 2-tuple (srow, scol) of ints specifying the row and
401 column where the token begins in the source; a 2-tuple (erow, ecol) of
402 ints specifying the row and column where the token ends in the source;
403 and the line on which the token was found. The line passed is the
404 logical line; continuation lines are included.
5
405
6 if sys.version_info[0] >= 3:
406 The first token sequence will always be an ENCODING token
7 from ._tokenize_py3 import *
407 which tells you which encoding was used to decode the bytes stream.
408 """
409 # This import is here to avoid problems when the itertools module is not
410 # built yet and tokenize is imported.
411 from itertools import chain, repeat
412 encoding, consumed = detect_encoding(readline)
413 rl_gen = iter(readline, b"")
414 empty = repeat(b"")
415 return _tokenize(chain(consumed, rl_gen, empty).__next__, encoding)
416
417
418 def _tokenize(readline, encoding):
419 lnum = parenlev = continued = 0
420 numchars = '0123456789'
421 contstr, needcont = '', 0
422 contline = None
423 indents = [0]
424
425 if encoding is not None:
426 if encoding == "utf-8-sig":
427 # BOM will already have been stripped.
428 encoding = "utf-8"
429 yield TokenInfo(ENCODING, encoding, (0, 0), (0, 0), '')
430 while True: # loop over lines in stream
431 try:
432 line = readline()
433 except StopIteration:
434 line = b''
435
436 if encoding is not None:
437 line = line.decode(encoding)
438 lnum += 1
439 pos, max = 0, len(line)
440
441 if contstr: # continued string
442 if not line:
443 raise TokenError("EOF in multi-line string", strstart)
444 endmatch = endprog.match(line)
445 if endmatch:
446 pos = end = endmatch.end(0)
447 yield TokenInfo(STRING, contstr + line[:end],
448 strstart, (lnum, end), contline + line)
449 contstr, needcont = '', 0
450 contline = None
451 elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
452 yield TokenInfo(ERRORTOKEN, contstr + line,
453 strstart, (lnum, len(line)), contline)
454 contstr = ''
455 contline = None
456 continue
457 else:
458 contstr = contstr + line
459 contline = contline + line
460 continue
461
462 elif parenlev == 0 and not continued: # new statement
463 if not line: break
464 column = 0
465 while pos < max: # measure leading whitespace
466 if line[pos] == ' ':
467 column += 1
468 elif line[pos] == '\t':
469 column = (column//tabsize + 1)*tabsize
470 elif line[pos] == '\f':
471 column = 0
472 else:
473 break
474 pos += 1
475 if pos == max:
476 break
477
478 if line[pos] in '#\r\n': # skip comments or blank lines
479 if line[pos] == '#':
480 comment_token = line[pos:].rstrip('\r\n')
481 nl_pos = pos + len(comment_token)
482 yield TokenInfo(COMMENT, comment_token,
483 (lnum, pos), (lnum, pos + len(comment_token)), line)
484 yield TokenInfo(NEWLINE, line[nl_pos:],
485 (lnum, nl_pos), (lnum, len(line)), line)
486 else:
487 yield TokenInfo(NEWLINE, line[pos:],
488 (lnum, pos), (lnum, len(line)), line)
489 continue
490
491 if column > indents[-1]: # count indents or dedents
492 indents.append(column)
493 yield TokenInfo(INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
494 while column < indents[-1]:
495 if column not in indents:
496 raise IndentationError(
497 "unindent does not match any outer indentation level",
498 ("<tokenize>", lnum, pos, line))
499 indents = indents[:-1]
500 yield TokenInfo(DEDENT, '', (lnum, pos), (lnum, pos), line)
501
502 else: # continued statement
503 if not line:
504 raise TokenError("EOF in multi-line statement", (lnum, 0))
505 continued = 0
506
507 while pos < max:
508 pseudomatch = pseudoprog.match(line, pos)
509 if pseudomatch: # scan for tokens
510 start, end = pseudomatch.span(1)
511 spos, epos, pos = (lnum, start), (lnum, end), end
512 token, initial = line[start:end], line[start]
513
514 if (initial in numchars or # ordinary number
515 (initial == '.' and token != '.' and token != '...')):
516 yield TokenInfo(NUMBER, token, spos, epos, line)
517 elif initial in '\r\n':
518 yield TokenInfo(NL if parenlev > 0 else NEWLINE,
519 token, spos, epos, line)
520 elif initial == '#':
521 assert not token.endswith("\n")
522 yield TokenInfo(COMMENT, token, spos, epos, line)
523 elif token in triple_quoted:
524 endprog = endprogs[token]
525 endmatch = endprog.match(line, pos)
526 if endmatch: # all on one line
527 pos = endmatch.end(0)
528 token = line[start:pos]
529 yield TokenInfo(STRING, token, spos, (lnum, pos), line)
530 else:
531 strstart = (lnum, start) # multiple lines
532 contstr = line[start:]
533 contline = line
534 break
535 elif initial in single_quoted or \
536 token[:2] in single_quoted or \
537 token[:3] in single_quoted:
538 if token[-1] == '\n': # continued string
539 strstart = (lnum, start)
540 endprog = (endprogs[initial] or endprogs[token[1]] or
541 endprogs[token[2]])
542 contstr, needcont = line[start:], 1
543 contline = line
544 break
545 else: # ordinary string
546 yield TokenInfo(STRING, token, spos, epos, line)
547 elif initial.isidentifier(): # ordinary name
548 yield TokenInfo(NAME, token, spos, epos, line)
549 elif initial == '\\': # continued stmt
550 continued = 1
551 else:
552 if initial in '([{':
553 parenlev += 1
554 elif initial in ')]}':
555 parenlev -= 1
556 yield TokenInfo(OP, token, spos, epos, line)
557 else:
558 yield TokenInfo(ERRORTOKEN, line[pos],
559 (lnum, pos), (lnum, pos+1), line)
560 pos += 1
561
562 for indent in indents[1:]: # pop remaining indent levels
563 yield TokenInfo(DEDENT, '', (lnum, 0), (lnum, 0), '')
564 yield TokenInfo(ENDMARKER, '', (lnum, 0), (lnum, 0), '')
565
566
567 # An undocumented, backwards compatible, API for all the places in the standard
568 # library that expect to be able to use tokenize with strings
569 def generate_tokens(readline):
570 return _tokenize(readline, None)
571
572 if __name__ == "__main__":
573 # Quick sanity check
574 s = b'''def parseline(self, line):
575 """Parse the line into a command name and a string containing
576 the arguments. Returns a tuple containing (command, args, line).
577 'command' and 'args' may be None if the line couldn't be parsed.
578 """
579 line = line.strip()
580 if not line:
581 return None, None, line
582 elif line[0] == '?':
583 line = 'help ' + line[1:]
584 elif line[0] == '!':
585 if hasattr(self, 'do_shell'):
586 line = 'shell ' + line[1:]
8 else:
587 else:
9 from ._tokenize_py2 import *
588 return None, None, line
589 i, n = 0, len(line)
590 while i < n and line[i] in self.identchars: i = i+1
591 cmd, arg = line[:i], line[i:].strip()
592 return cmd, arg, line
593 '''
594 for tok in tokenize(iter(s.splitlines()).__next__):
595 print(tok)
@@ -1,299 +1,297 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2008-2011, IPython Development Team.
10 # Copyright (c) 2008-2011, IPython Development Team.
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.rst, distributed with this software.
17 # The full license is in the file COPYING.rst, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Minimal Python version sanity check
21 # Minimal Python version sanity check
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 from __future__ import print_function
23 from __future__ import print_function
24
24
25 import sys
25 import sys
26
26
27 # This check is also made in IPython/__init__, don't forget to update both when
27 # This check is also made in IPython/__init__, don't forget to update both when
28 # changing Python version requirements.
28 # changing Python version requirements.
29 if sys.version_info < (3,3):
29 if sys.version_info < (3,3):
30 error = """
30 error = """
31 IPython 6.0+ does not support Python 2.6, 2.7, 3.0, 3.1, or 3.2.
31 IPython 6.0+ does not support Python 2.6, 2.7, 3.0, 3.1, or 3.2.
32 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
32 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
33 Beginning with IPython 6.0, Python 3.3 and above is required.
33 Beginning with IPython 6.0, Python 3.3 and above is required.
34
34
35 See IPython `README.rst` file for more information:
35 See IPython `README.rst` file for more information:
36
36
37 https://github.com/ipython/ipython/blob/master/README.rst
37 https://github.com/ipython/ipython/blob/master/README.rst
38
38
39 """
39 """
40
40
41 print(error, file=sys.stderr)
41 print(error, file=sys.stderr)
42 sys.exit(1)
42 sys.exit(1)
43
43
44 PY3 = (sys.version_info[0] >= 3)
45
46 # At least we're on the python version we need, move on.
44 # At least we're on the python version we need, move on.
47
45
48 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
49 # Imports
47 # Imports
50 #-------------------------------------------------------------------------------
48 #-------------------------------------------------------------------------------
51
49
52 # Stdlib imports
50 # Stdlib imports
53 import os
51 import os
54
52
55 from glob import glob
53 from glob import glob
56
54
57 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
55 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
58 # update it when the contents of directories change.
56 # update it when the contents of directories change.
59 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
57 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
60
58
61 from distutils.core import setup
59 from distutils.core import setup
62
60
63 # Our own imports
61 # Our own imports
64 from setupbase import target_update
62 from setupbase import target_update
65
63
66 from setupbase import (
64 from setupbase import (
67 setup_args,
65 setup_args,
68 find_packages,
66 find_packages,
69 find_package_data,
67 find_package_data,
70 check_package_data_first,
68 check_package_data_first,
71 find_entry_points,
69 find_entry_points,
72 build_scripts_entrypt,
70 build_scripts_entrypt,
73 find_data_files,
71 find_data_files,
74 git_prebuild,
72 git_prebuild,
75 install_symlinked,
73 install_symlinked,
76 install_lib_symlink,
74 install_lib_symlink,
77 install_scripts_for_symlink,
75 install_scripts_for_symlink,
78 unsymlink,
76 unsymlink,
79 )
77 )
80
78
81 isfile = os.path.isfile
79 isfile = os.path.isfile
82 pjoin = os.path.join
80 pjoin = os.path.join
83
81
84 #-------------------------------------------------------------------------------
82 #-------------------------------------------------------------------------------
85 # Handle OS specific things
83 # Handle OS specific things
86 #-------------------------------------------------------------------------------
84 #-------------------------------------------------------------------------------
87
85
88 if os.name in ('nt','dos'):
86 if os.name in ('nt','dos'):
89 os_name = 'windows'
87 os_name = 'windows'
90 else:
88 else:
91 os_name = os.name
89 os_name = os.name
92
90
93 # Under Windows, 'sdist' has not been supported. Now that the docs build with
91 # Under Windows, 'sdist' has not been supported. Now that the docs build with
94 # Sphinx it might work, but let's not turn it on until someone confirms that it
92 # Sphinx it might work, but let's not turn it on until someone confirms that it
95 # actually works.
93 # actually works.
96 if os_name == 'windows' and 'sdist' in sys.argv:
94 if os_name == 'windows' and 'sdist' in sys.argv:
97 print('The sdist command is not available under Windows. Exiting.')
95 print('The sdist command is not available under Windows. Exiting.')
98 sys.exit(1)
96 sys.exit(1)
99
97
100
98
101 #-------------------------------------------------------------------------------
99 #-------------------------------------------------------------------------------
102 # Things related to the IPython documentation
100 # Things related to the IPython documentation
103 #-------------------------------------------------------------------------------
101 #-------------------------------------------------------------------------------
104
102
105 # update the manuals when building a source dist
103 # update the manuals when building a source dist
106 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
104 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
107
105
108 # List of things to be updated. Each entry is a triplet of args for
106 # List of things to be updated. Each entry is a triplet of args for
109 # target_update()
107 # target_update()
110 to_update = [
108 to_update = [
111 ('docs/man/ipython.1.gz',
109 ('docs/man/ipython.1.gz',
112 ['docs/man/ipython.1'],
110 ['docs/man/ipython.1'],
113 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
111 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
114 ]
112 ]
115
113
116
114
117 [ target_update(*t) for t in to_update ]
115 [ target_update(*t) for t in to_update ]
118
116
119 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
120 # Find all the packages, package data, and data_files
118 # Find all the packages, package data, and data_files
121 #---------------------------------------------------------------------------
119 #---------------------------------------------------------------------------
122
120
123 packages = find_packages()
121 packages = find_packages()
124 package_data = find_package_data()
122 package_data = find_package_data()
125
123
126 data_files = find_data_files()
124 data_files = find_data_files()
127
125
128 setup_args['packages'] = packages
126 setup_args['packages'] = packages
129 setup_args['package_data'] = package_data
127 setup_args['package_data'] = package_data
130 setup_args['data_files'] = data_files
128 setup_args['data_files'] = data_files
131
129
132 #---------------------------------------------------------------------------
130 #---------------------------------------------------------------------------
133 # custom distutils commands
131 # custom distutils commands
134 #---------------------------------------------------------------------------
132 #---------------------------------------------------------------------------
135 # imports here, so they are after setuptools import if there was one
133 # imports here, so they are after setuptools import if there was one
136 from distutils.command.sdist import sdist
134 from distutils.command.sdist import sdist
137 from distutils.command.upload import upload
135 from distutils.command.upload import upload
138
136
139 class UploadWindowsInstallers(upload):
137 class UploadWindowsInstallers(upload):
140
138
141 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
139 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
142 user_options = upload.user_options + [
140 user_options = upload.user_options + [
143 ('files=', 'f', 'exe file (or glob) to upload')
141 ('files=', 'f', 'exe file (or glob) to upload')
144 ]
142 ]
145 def initialize_options(self):
143 def initialize_options(self):
146 upload.initialize_options(self)
144 upload.initialize_options(self)
147 meta = self.distribution.metadata
145 meta = self.distribution.metadata
148 base = '{name}-{version}'.format(
146 base = '{name}-{version}'.format(
149 name=meta.get_name(),
147 name=meta.get_name(),
150 version=meta.get_version()
148 version=meta.get_version()
151 )
149 )
152 self.files = os.path.join('dist', '%s.*.exe' % base)
150 self.files = os.path.join('dist', '%s.*.exe' % base)
153
151
154 def run(self):
152 def run(self):
155 for dist_file in glob(self.files):
153 for dist_file in glob(self.files):
156 self.upload_file('bdist_wininst', 'any', dist_file)
154 self.upload_file('bdist_wininst', 'any', dist_file)
157
155
158 setup_args['cmdclass'] = {
156 setup_args['cmdclass'] = {
159 'build_py': \
157 'build_py': \
160 check_package_data_first(git_prebuild('IPython')),
158 check_package_data_first(git_prebuild('IPython')),
161 'sdist' : git_prebuild('IPython', sdist),
159 'sdist' : git_prebuild('IPython', sdist),
162 'upload_wininst' : UploadWindowsInstallers,
160 'upload_wininst' : UploadWindowsInstallers,
163 'symlink': install_symlinked,
161 'symlink': install_symlinked,
164 'install_lib_symlink': install_lib_symlink,
162 'install_lib_symlink': install_lib_symlink,
165 'install_scripts_sym': install_scripts_for_symlink,
163 'install_scripts_sym': install_scripts_for_symlink,
166 'unsymlink': unsymlink,
164 'unsymlink': unsymlink,
167 }
165 }
168
166
169
167
170 #---------------------------------------------------------------------------
168 #---------------------------------------------------------------------------
171 # Handle scripts, dependencies, and setuptools specific things
169 # Handle scripts, dependencies, and setuptools specific things
172 #---------------------------------------------------------------------------
170 #---------------------------------------------------------------------------
173
171
174 # For some commands, use setuptools. Note that we do NOT list install here!
172 # For some commands, use setuptools. Note that we do NOT list install here!
175 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
173 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
176 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
174 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
177 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
175 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
178 'egg_info', 'easy_install', 'upload', 'install_egg_info',
176 'egg_info', 'easy_install', 'upload', 'install_egg_info',
179 ))
177 ))
180
178
181 if len(needs_setuptools.intersection(sys.argv)) > 0:
179 if len(needs_setuptools.intersection(sys.argv)) > 0:
182 import setuptools
180 import setuptools
183
181
184 # This dict is used for passing extra arguments that are setuptools
182 # This dict is used for passing extra arguments that are setuptools
185 # specific to setup
183 # specific to setup
186 setuptools_extra_args = {}
184 setuptools_extra_args = {}
187
185
188 # setuptools requirements
186 # setuptools requirements
189
187
190 extras_require = dict(
188 extras_require = dict(
191 parallel = ['ipyparallel'],
189 parallel = ['ipyparallel'],
192 qtconsole = ['qtconsole'],
190 qtconsole = ['qtconsole'],
193 doc = ['Sphinx>=1.3'],
191 doc = ['Sphinx>=1.3'],
194 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
192 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'nbformat', 'ipykernel', 'numpy'],
195 terminal = [],
193 terminal = [],
196 kernel = ['ipykernel'],
194 kernel = ['ipykernel'],
197 nbformat = ['nbformat'],
195 nbformat = ['nbformat'],
198 notebook = ['notebook', 'ipywidgets'],
196 notebook = ['notebook', 'ipywidgets'],
199 nbconvert = ['nbconvert'],
197 nbconvert = ['nbconvert'],
200 )
198 )
201
199
202 install_requires = [
200 install_requires = [
203 'setuptools>=18.5',
201 'setuptools>=18.5',
204 'decorator',
202 'decorator',
205 'pickleshare',
203 'pickleshare',
206 'simplegeneric>0.8',
204 'simplegeneric>0.8',
207 'traitlets>=4.2',
205 'traitlets>=4.2',
208 'prompt_toolkit>=1.0.3,<2.0.0',
206 'prompt_toolkit>=1.0.3,<2.0.0',
209 'pygments',
207 'pygments',
210 ]
208 ]
211
209
212 # Platform-specific dependencies:
210 # Platform-specific dependencies:
213 # This is the correct way to specify these,
211 # This is the correct way to specify these,
214 # but requires pip >= 6. pip < 6 ignores these.
212 # but requires pip >= 6. pip < 6 ignores these.
215
213
216 extras_require.update({
214 extras_require.update({
217 ':python_version == "2.7"': ['backports.shutil_get_terminal_size'],
215 ':python_version == "2.7"': ['backports.shutil_get_terminal_size'],
218 ':python_version == "2.7" or python_version == "3.3"': ['pathlib2'],
216 ':python_version == "2.7" or python_version == "3.3"': ['pathlib2'],
219 ':sys_platform != "win32"': ['pexpect'],
217 ':sys_platform != "win32"': ['pexpect'],
220 ':sys_platform == "darwin"': ['appnope'],
218 ':sys_platform == "darwin"': ['appnope'],
221 ':sys_platform == "win32"': ['colorama'],
219 ':sys_platform == "win32"': ['colorama'],
222 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
220 ':sys_platform == "win32" and python_version < "3.6"': ['win_unicode_console>=0.5'],
223 'test:python_version == "2.7"': ['mock'],
221 'test:python_version == "2.7"': ['mock'],
224 })
222 })
225 # FIXME: re-specify above platform dependencies for pip < 6
223 # FIXME: re-specify above platform dependencies for pip < 6
226 # These would result in non-portable bdists.
224 # These would result in non-portable bdists.
227 if not any(arg.startswith('bdist') for arg in sys.argv):
225 if not any(arg.startswith('bdist') for arg in sys.argv):
228 if sys.version_info < (3, 3):
226 if sys.version_info < (3, 3):
229 extras_require['test'].append('mock')
227 extras_require['test'].append('mock')
230
228
231 if sys.platform == 'darwin':
229 if sys.platform == 'darwin':
232 install_requires.extend(['appnope'])
230 install_requires.extend(['appnope'])
233
231
234 if not sys.platform.startswith('win'):
232 if not sys.platform.startswith('win'):
235 install_requires.append('pexpect')
233 install_requires.append('pexpect')
236
234
237 # workaround pypa/setuptools#147, where setuptools misspells
235 # workaround pypa/setuptools#147, where setuptools misspells
238 # platform_python_implementation as python_implementation
236 # platform_python_implementation as python_implementation
239 if 'setuptools' in sys.modules:
237 if 'setuptools' in sys.modules:
240 for key in list(extras_require):
238 for key in list(extras_require):
241 if 'platform_python_implementation' in key:
239 if 'platform_python_implementation' in key:
242 new_key = key.replace('platform_python_implementation', 'python_implementation')
240 new_key = key.replace('platform_python_implementation', 'python_implementation')
243 extras_require[new_key] = extras_require.pop(key)
241 extras_require[new_key] = extras_require.pop(key)
244
242
245 everything = set()
243 everything = set()
246 for key, deps in extras_require.items():
244 for key, deps in extras_require.items():
247 if ':' not in key:
245 if ':' not in key:
248 everything.update(deps)
246 everything.update(deps)
249 extras_require['all'] = everything
247 extras_require['all'] = everything
250
248
251 if 'setuptools' in sys.modules:
249 if 'setuptools' in sys.modules:
252 setuptools_extra_args['python_requires'] = '>=3.3'
250 setuptools_extra_args['python_requires'] = '>=3.3'
253 setuptools_extra_args['zip_safe'] = False
251 setuptools_extra_args['zip_safe'] = False
254 setuptools_extra_args['entry_points'] = {
252 setuptools_extra_args['entry_points'] = {
255 'console_scripts': find_entry_points(),
253 'console_scripts': find_entry_points(),
256 'pygments.lexers': [
254 'pygments.lexers': [
257 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
255 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
258 'ipython = IPython.lib.lexers:IPythonLexer',
256 'ipython = IPython.lib.lexers:IPythonLexer',
259 'ipython3 = IPython.lib.lexers:IPython3Lexer',
257 'ipython3 = IPython.lib.lexers:IPython3Lexer',
260 ],
258 ],
261 }
259 }
262 setup_args['extras_require'] = extras_require
260 setup_args['extras_require'] = extras_require
263 requires = setup_args['install_requires'] = install_requires
261 requires = setup_args['install_requires'] = install_requires
264
262
265 # Script to be run by the windows binary installer after the default setup
263 # Script to be run by the windows binary installer after the default setup
266 # routine, to add shortcuts and similar windows-only things. Windows
264 # routine, to add shortcuts and similar windows-only things. Windows
267 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
265 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
268 # doesn't find them.
266 # doesn't find them.
269 if 'bdist_wininst' in sys.argv:
267 if 'bdist_wininst' in sys.argv:
270 if len(sys.argv) > 2 and \
268 if len(sys.argv) > 2 and \
271 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
269 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
272 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
270 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
273 sys.exit(1)
271 sys.exit(1)
274 setup_args['data_files'].append(
272 setup_args['data_files'].append(
275 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
273 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
276 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
274 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
277 setup_args['options'] = {"bdist_wininst":
275 setup_args['options'] = {"bdist_wininst":
278 {"install_script":
276 {"install_script":
279 "ipython_win_post_install.py"}}
277 "ipython_win_post_install.py"}}
280
278
281 else:
279 else:
282 # scripts has to be a non-empty list, or install_scripts isn't called
280 # scripts has to be a non-empty list, or install_scripts isn't called
283 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
281 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
284
282
285 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
283 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
286
284
287 #---------------------------------------------------------------------------
285 #---------------------------------------------------------------------------
288 # Do the actual setup now
286 # Do the actual setup now
289 #---------------------------------------------------------------------------
287 #---------------------------------------------------------------------------
290
288
291 setup_args.update(setuptools_extra_args)
289 setup_args.update(setuptools_extra_args)
292
290
293
291
294
292
295 def main():
293 def main():
296 setup(**setup_args)
294 setup(**setup_args)
297
295
298 if __name__ == '__main__':
296 if __name__ == '__main__':
299 main()
297 main()
@@ -1,235 +1,232 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Simple tools to query github.com and gather stats about issues.
2 """Simple tools to query github.com and gather stats about issues.
3
3
4 To generate a report for IPython 2.0, run:
4 To generate a report for IPython 2.0, run:
5
5
6 python github_stats.py --milestone 2.0 --since-tag rel-1.0.0
6 python github_stats.py --milestone 2.0 --since-tag rel-1.0.0
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Imports
9 # Imports
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 from __future__ import print_function
12 from __future__ import print_function
13
13
14 import codecs
14 import codecs
15 import sys
15 import sys
16
16
17 from argparse import ArgumentParser
17 from argparse import ArgumentParser
18 from datetime import datetime, timedelta
18 from datetime import datetime, timedelta
19 from subprocess import check_output
19 from subprocess import check_output
20
20
21 from gh_api import (
21 from gh_api import (
22 get_paged_request, make_auth_header, get_pull_request, is_pull_request,
22 get_paged_request, make_auth_header, get_pull_request, is_pull_request,
23 get_milestone_id, get_issues_list, get_authors,
23 get_milestone_id, get_issues_list, get_authors,
24 )
24 )
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Globals
26 # Globals
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
29 ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
30 PER_PAGE = 100
30 PER_PAGE = 100
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Functions
33 # Functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 def round_hour(dt):
36 def round_hour(dt):
37 return dt.replace(minute=0,second=0,microsecond=0)
37 return dt.replace(minute=0,second=0,microsecond=0)
38
38
39 def _parse_datetime(s):
39 def _parse_datetime(s):
40 """Parse dates in the format returned by the Github API."""
40 """Parse dates in the format returned by the Github API."""
41 if s:
41 if s:
42 return datetime.strptime(s, ISO8601)
42 return datetime.strptime(s, ISO8601)
43 else:
43 else:
44 return datetime.fromtimestamp(0)
44 return datetime.fromtimestamp(0)
45
45
46 def issues2dict(issues):
46 def issues2dict(issues):
47 """Convert a list of issues to a dict, keyed by issue number."""
47 """Convert a list of issues to a dict, keyed by issue number."""
48 idict = {}
48 idict = {}
49 for i in issues:
49 for i in issues:
50 idict[i['number']] = i
50 idict[i['number']] = i
51 return idict
51 return idict
52
52
53 def split_pulls(all_issues, project="ipython/ipython"):
53 def split_pulls(all_issues, project="ipython/ipython"):
54 """split a list of closed issues into non-PR Issues and Pull Requests"""
54 """split a list of closed issues into non-PR Issues and Pull Requests"""
55 pulls = []
55 pulls = []
56 issues = []
56 issues = []
57 for i in all_issues:
57 for i in all_issues:
58 if is_pull_request(i):
58 if is_pull_request(i):
59 pull = get_pull_request(project, i['number'], auth=True)
59 pull = get_pull_request(project, i['number'], auth=True)
60 pulls.append(pull)
60 pulls.append(pull)
61 else:
61 else:
62 issues.append(i)
62 issues.append(i)
63 return issues, pulls
63 return issues, pulls
64
64
65
65
66 def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", pulls=False):
66 def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", pulls=False):
67 """Get all issues closed since a particular point in time. period
67 """Get all issues closed since a particular point in time. period
68 can either be a datetime object, or a timedelta object. In the
68 can either be a datetime object, or a timedelta object. In the
69 latter case, it is used as a time before the present.
69 latter case, it is used as a time before the present.
70 """
70 """
71
71
72 which = 'pulls' if pulls else 'issues'
72 which = 'pulls' if pulls else 'issues'
73
73
74 if isinstance(period, timedelta):
74 if isinstance(period, timedelta):
75 since = round_hour(datetime.utcnow() - period)
75 since = round_hour(datetime.utcnow() - period)
76 else:
76 else:
77 since = period
77 since = period
78 url = "https://api.github.com/repos/%s/%s?state=closed&sort=updated&since=%s&per_page=%i" % (project, which, since.strftime(ISO8601), PER_PAGE)
78 url = "https://api.github.com/repos/%s/%s?state=closed&sort=updated&since=%s&per_page=%i" % (project, which, since.strftime(ISO8601), PER_PAGE)
79 allclosed = get_paged_request(url, headers=make_auth_header())
79 allclosed = get_paged_request(url, headers=make_auth_header())
80
80
81 filtered = [ i for i in allclosed if _parse_datetime(i['closed_at']) > since ]
81 filtered = [ i for i in allclosed if _parse_datetime(i['closed_at']) > since ]
82 if pulls:
82 if pulls:
83 filtered = [ i for i in filtered if _parse_datetime(i['merged_at']) > since ]
83 filtered = [ i for i in filtered if _parse_datetime(i['merged_at']) > since ]
84 # filter out PRs not against master (backports)
84 # filter out PRs not against master (backports)
85 filtered = [ i for i in filtered if i['base']['ref'] == 'master' ]
85 filtered = [ i for i in filtered if i['base']['ref'] == 'master' ]
86 else:
86 else:
87 filtered = [ i for i in filtered if not is_pull_request(i) ]
87 filtered = [ i for i in filtered if not is_pull_request(i) ]
88
88
89 return filtered
89 return filtered
90
90
91
91
92 def sorted_by_field(issues, field='closed_at', reverse=False):
92 def sorted_by_field(issues, field='closed_at', reverse=False):
93 """Return a list of issues sorted by closing date date."""
93 """Return a list of issues sorted by closing date date."""
94 return sorted(issues, key = lambda i:i[field], reverse=reverse)
94 return sorted(issues, key = lambda i:i[field], reverse=reverse)
95
95
96
96
97 def report(issues, show_urls=False):
97 def report(issues, show_urls=False):
98 """Summary report about a list of issues, printing number and title."""
98 """Summary report about a list of issues, printing number and title."""
99 if show_urls:
99 if show_urls:
100 for i in issues:
100 for i in issues:
101 role = 'ghpull' if 'merged_at' in i else 'ghissue'
101 role = 'ghpull' if 'merged_at' in i else 'ghissue'
102 print(u'* :%s:`%d`: %s' % (role, i['number'],
102 print(u'* :%s:`%d`: %s' % (role, i['number'],
103 i['title'].replace(u'`', u'``')))
103 i['title'].replace(u'`', u'``')))
104 else:
104 else:
105 for i in issues:
105 for i in issues:
106 print(u'* %d: %s' % (i['number'], i['title'].replace(u'`', u'``')))
106 print(u'* %d: %s' % (i['number'], i['title'].replace(u'`', u'``')))
107
107
108 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
109 # Main script
109 # Main script
110 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
111
111
112 if __name__ == "__main__":
112 if __name__ == "__main__":
113
113
114 print("DEPRECATE: backport_pr.py is deprecated and is is now recommended"
114 print("DEPRECATE: backport_pr.py is deprecated and is is now recommended"
115 "to install `ghpro` from PyPI.", file=sys.stderr)
115 "to install `ghpro` from PyPI.", file=sys.stderr)
116
116
117 # deal with unicode
118 if sys.version_info < (3,):
119 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
120
117
121 # Whether to add reST urls for all issues in printout.
118 # Whether to add reST urls for all issues in printout.
122 show_urls = True
119 show_urls = True
123
120
124 parser = ArgumentParser()
121 parser = ArgumentParser()
125 parser.add_argument('--since-tag', type=str,
122 parser.add_argument('--since-tag', type=str,
126 help="The git tag to use for the starting point (typically the last major release)."
123 help="The git tag to use for the starting point (typically the last major release)."
127 )
124 )
128 parser.add_argument('--milestone', type=str,
125 parser.add_argument('--milestone', type=str,
129 help="The GitHub milestone to use for filtering issues [optional]."
126 help="The GitHub milestone to use for filtering issues [optional]."
130 )
127 )
131 parser.add_argument('--days', type=int,
128 parser.add_argument('--days', type=int,
132 help="The number of days of data to summarize (use this or --since-tag)."
129 help="The number of days of data to summarize (use this or --since-tag)."
133 )
130 )
134 parser.add_argument('--project', type=str, default="ipython/ipython",
131 parser.add_argument('--project', type=str, default="ipython/ipython",
135 help="The project to summarize."
132 help="The project to summarize."
136 )
133 )
137 parser.add_argument('--links', action='store_true', default=False,
134 parser.add_argument('--links', action='store_true', default=False,
138 help="Include links to all closed Issues and PRs in the output."
135 help="Include links to all closed Issues and PRs in the output."
139 )
136 )
140
137
141 opts = parser.parse_args()
138 opts = parser.parse_args()
142 tag = opts.since_tag
139 tag = opts.since_tag
143
140
144 # set `since` from days or git tag
141 # set `since` from days or git tag
145 if opts.days:
142 if opts.days:
146 since = datetime.utcnow() - timedelta(days=opts.days)
143 since = datetime.utcnow() - timedelta(days=opts.days)
147 else:
144 else:
148 if not tag:
145 if not tag:
149 tag = check_output(['git', 'describe', '--abbrev=0']).strip().decode('utf8')
146 tag = check_output(['git', 'describe', '--abbrev=0']).strip().decode('utf8')
150 cmd = ['git', 'log', '-1', '--format=%ai', tag]
147 cmd = ['git', 'log', '-1', '--format=%ai', tag]
151 tagday, tz = check_output(cmd).strip().decode('utf8').rsplit(' ', 1)
148 tagday, tz = check_output(cmd).strip().decode('utf8').rsplit(' ', 1)
152 since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
149 since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
153 h = int(tz[1:3])
150 h = int(tz[1:3])
154 m = int(tz[3:])
151 m = int(tz[3:])
155 td = timedelta(hours=h, minutes=m)
152 td = timedelta(hours=h, minutes=m)
156 if tz[0] == '-':
153 if tz[0] == '-':
157 since += td
154 since += td
158 else:
155 else:
159 since -= td
156 since -= td
160
157
161 since = round_hour(since)
158 since = round_hour(since)
162
159
163 milestone = opts.milestone
160 milestone = opts.milestone
164 project = opts.project
161 project = opts.project
165
162
166 print("fetching GitHub stats since %s (tag: %s, milestone: %s)" % (since, tag, milestone), file=sys.stderr)
163 print("fetching GitHub stats since %s (tag: %s, milestone: %s)" % (since, tag, milestone), file=sys.stderr)
167 if milestone:
164 if milestone:
168 milestone_id = get_milestone_id(project=project, milestone=milestone,
165 milestone_id = get_milestone_id(project=project, milestone=milestone,
169 auth=True)
166 auth=True)
170 issues_and_pulls = get_issues_list(project=project,
167 issues_and_pulls = get_issues_list(project=project,
171 milestone=milestone_id,
168 milestone=milestone_id,
172 state='closed',
169 state='closed',
173 auth=True,
170 auth=True,
174 )
171 )
175 issues, pulls = split_pulls(issues_and_pulls, project=project)
172 issues, pulls = split_pulls(issues_and_pulls, project=project)
176 else:
173 else:
177 issues = issues_closed_since(since, project=project, pulls=False)
174 issues = issues_closed_since(since, project=project, pulls=False)
178 pulls = issues_closed_since(since, project=project, pulls=True)
175 pulls = issues_closed_since(since, project=project, pulls=True)
179
176
180 # For regular reports, it's nice to show them in reverse chronological order
177 # For regular reports, it's nice to show them in reverse chronological order
181 issues = sorted_by_field(issues, reverse=True)
178 issues = sorted_by_field(issues, reverse=True)
182 pulls = sorted_by_field(pulls, reverse=True)
179 pulls = sorted_by_field(pulls, reverse=True)
183
180
184 n_issues, n_pulls = map(len, (issues, pulls))
181 n_issues, n_pulls = map(len, (issues, pulls))
185 n_total = n_issues + n_pulls
182 n_total = n_issues + n_pulls
186
183
187 # Print summary report we can directly include into release notes.
184 # Print summary report we can directly include into release notes.
188
185
189 print()
186 print()
190 since_day = since.strftime("%Y/%m/%d")
187 since_day = since.strftime("%Y/%m/%d")
191 today = datetime.today().strftime("%Y/%m/%d")
188 today = datetime.today().strftime("%Y/%m/%d")
192 print("GitHub stats for %s - %s (tag: %s)" % (since_day, today, tag))
189 print("GitHub stats for %s - %s (tag: %s)" % (since_day, today, tag))
193 print()
190 print()
194 print("These lists are automatically generated, and may be incomplete or contain duplicates.")
191 print("These lists are automatically generated, and may be incomplete or contain duplicates.")
195 print()
192 print()
196
193
197 ncommits = 0
194 ncommits = 0
198 all_authors = []
195 all_authors = []
199 if tag:
196 if tag:
200 # print git info, in addition to GitHub info:
197 # print git info, in addition to GitHub info:
201 since_tag = tag+'..'
198 since_tag = tag+'..'
202 cmd = ['git', 'log', '--oneline', since_tag]
199 cmd = ['git', 'log', '--oneline', since_tag]
203 ncommits += len(check_output(cmd).splitlines())
200 ncommits += len(check_output(cmd).splitlines())
204
201
205 author_cmd = ['git', 'log', '--use-mailmap', "--format=* %aN", since_tag]
202 author_cmd = ['git', 'log', '--use-mailmap', "--format=* %aN", since_tag]
206 all_authors.extend(check_output(author_cmd).decode('utf-8', 'replace').splitlines())
203 all_authors.extend(check_output(author_cmd).decode('utf-8', 'replace').splitlines())
207
204
208 pr_authors = []
205 pr_authors = []
209 for pr in pulls:
206 for pr in pulls:
210 pr_authors.extend(get_authors(pr))
207 pr_authors.extend(get_authors(pr))
211 ncommits = len(pr_authors) + ncommits - len(pulls)
208 ncommits = len(pr_authors) + ncommits - len(pulls)
212 author_cmd = ['git', 'check-mailmap'] + pr_authors
209 author_cmd = ['git', 'check-mailmap'] + pr_authors
213 with_email = check_output(author_cmd).decode('utf-8', 'replace').splitlines()
210 with_email = check_output(author_cmd).decode('utf-8', 'replace').splitlines()
214 all_authors.extend([ u'* ' + a.split(' <')[0] for a in with_email ])
211 all_authors.extend([ u'* ' + a.split(' <')[0] for a in with_email ])
215 unique_authors = sorted(set(all_authors), key=lambda s: s.lower())
212 unique_authors = sorted(set(all_authors), key=lambda s: s.lower())
216
213
217 print("We closed %d issues and merged %d pull requests." % (n_issues, n_pulls))
214 print("We closed %d issues and merged %d pull requests." % (n_issues, n_pulls))
218 if milestone:
215 if milestone:
219 print("The full list can be seen `on GitHub <https://github.com/{project}/issues?q=milestone%3A{milestone}+>`__".format(project=project,milestone=milestone)
216 print("The full list can be seen `on GitHub <https://github.com/{project}/issues?q=milestone%3A{milestone}+>`__".format(project=project,milestone=milestone)
220 )
217 )
221
218
222 print()
219 print()
223 print("The following %i authors contributed %i commits." % (len(unique_authors), ncommits))
220 print("The following %i authors contributed %i commits." % (len(unique_authors), ncommits))
224 print()
221 print()
225 print('\n'.join(unique_authors))
222 print('\n'.join(unique_authors))
226
223
227 if opts.links:
224 if opts.links:
228 print()
225 print()
229 print("GitHub issues and pull requests:")
226 print("GitHub issues and pull requests:")
230 print()
227 print()
231 print('Pull Requests (%d):\n' % n_pulls)
228 print('Pull Requests (%d):\n' % n_pulls)
232 report(pulls, show_urls)
229 report(pulls, show_urls)
233 print()
230 print()
234 print('Issues (%d):\n' % n_issues)
231 print('Issues (%d):\n' % n_issues)
235 report(issues, show_urls)
232 report(issues, show_urls)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (595 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now