##// END OF EJS Templates
Remove clearly marked python 2 imports.
Terry Davis -
Show More
@@ -1,639 +1,637 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 https://docs.python.org/2/license.html
16 https://docs.python.org/2/license.html
17 """
17 """
18
18
19 #*****************************************************************************
19 #*****************************************************************************
20 #
20 #
21 # This file is licensed under the PSF license.
21 # This file is licensed under the PSF license.
22 #
22 #
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
25 #
25 #
26 #
26 #
27 #*****************************************************************************
27 #*****************************************************************************
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import linecache
32 import linecache
33 import sys
33 import sys
34 import warnings
34 import warnings
35 import re
35 import re
36
36
37 from IPython import get_ipython
37 from IPython import get_ipython
38 from IPython.utils import PyColorize
38 from IPython.utils import PyColorize
39 from IPython.utils import coloransi, py3compat
39 from IPython.utils import coloransi, py3compat
40 from IPython.core.excolors import exception_colors
40 from IPython.core.excolors import exception_colors
41 from IPython.testing.skipdoctest import skip_doctest
41 from IPython.testing.skipdoctest import skip_doctest
42
42
43
43
44 prompt = 'ipdb> '
44 prompt = 'ipdb> '
45
45
46 #We have to check this directly from sys.argv, config struct not yet available
46 #We have to check this directly from sys.argv, config struct not yet available
47 from pdb import Pdb as OldPdb
47 from pdb import Pdb as OldPdb
48
48
49 # Allow the set_trace code to operate outside of an ipython instance, even if
49 # Allow the set_trace code to operate outside of an ipython instance, even if
50 # it does so with some limitations. The rest of this support is implemented in
50 # it does so with some limitations. The rest of this support is implemented in
51 # the Tracer constructor.
51 # the Tracer constructor.
52
52
53 def make_arrow(pad):
53 def make_arrow(pad):
54 """generate the leading arrow in front of traceback or debugger"""
54 """generate the leading arrow in front of traceback or debugger"""
55 if pad >= 2:
55 if pad >= 2:
56 return '-'*(pad-2) + '> '
56 return '-'*(pad-2) + '> '
57 elif pad == 1:
57 elif pad == 1:
58 return '>'
58 return '>'
59 return ''
59 return ''
60
60
61
61
62 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
62 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
63 """Exception hook which handles `BdbQuit` exceptions.
63 """Exception hook which handles `BdbQuit` exceptions.
64
64
65 All other exceptions are processed using the `excepthook`
65 All other exceptions are processed using the `excepthook`
66 parameter.
66 parameter.
67 """
67 """
68 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
68 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
69 DeprecationWarning, stacklevel=2)
69 DeprecationWarning, stacklevel=2)
70 if et==bdb.BdbQuit:
70 if et==bdb.BdbQuit:
71 print('Exiting Debugger.')
71 print('Exiting Debugger.')
72 elif excepthook is not None:
72 elif excepthook is not None:
73 excepthook(et, ev, tb)
73 excepthook(et, ev, tb)
74 else:
74 else:
75 # Backwards compatibility. Raise deprecation warning?
75 # Backwards compatibility. Raise deprecation warning?
76 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
76 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
77
77
78
78
79 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
79 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
80 warnings.warn(
80 warnings.warn(
81 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
81 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
82 DeprecationWarning, stacklevel=2)
82 DeprecationWarning, stacklevel=2)
83 print('Exiting Debugger.')
83 print('Exiting Debugger.')
84
84
85
85
86 class Tracer(object):
86 class Tracer(object):
87 """
87 """
88 DEPRECATED
88 DEPRECATED
89
89
90 Class for local debugging, similar to pdb.set_trace.
90 Class for local debugging, similar to pdb.set_trace.
91
91
92 Instances of this class, when called, behave like pdb.set_trace, but
92 Instances of this class, when called, behave like pdb.set_trace, but
93 providing IPython's enhanced capabilities.
93 providing IPython's enhanced capabilities.
94
94
95 This is implemented as a class which must be initialized in your own code
95 This is implemented as a class which must be initialized in your own code
96 and not as a standalone function because we need to detect at runtime
96 and not as a standalone function because we need to detect at runtime
97 whether IPython is already active or not. That detection is done in the
97 whether IPython is already active or not. That detection is done in the
98 constructor, ensuring that this code plays nicely with a running IPython,
98 constructor, ensuring that this code plays nicely with a running IPython,
99 while functioning acceptably (though with limitations) if outside of it.
99 while functioning acceptably (though with limitations) if outside of it.
100 """
100 """
101
101
102 @skip_doctest
102 @skip_doctest
103 def __init__(self, colors=None):
103 def __init__(self, colors=None):
104 """
104 """
105 DEPRECATED
105 DEPRECATED
106
106
107 Create a local debugger instance.
107 Create a local debugger instance.
108
108
109 Parameters
109 Parameters
110 ----------
110 ----------
111
111
112 colors : str, optional
112 colors : str, optional
113 The name of the color scheme to use, it must be one of IPython's
113 The name of the color scheme to use, it must be one of IPython's
114 valid color schemes. If not given, the function will default to
114 valid color schemes. If not given, the function will default to
115 the current IPython scheme when running inside IPython, and to
115 the current IPython scheme when running inside IPython, and to
116 'NoColor' otherwise.
116 'NoColor' otherwise.
117
117
118 Examples
118 Examples
119 --------
119 --------
120 ::
120 ::
121
121
122 from IPython.core.debugger import Tracer; debug_here = Tracer()
122 from IPython.core.debugger import Tracer; debug_here = Tracer()
123
123
124 Later in your code::
124 Later in your code::
125
125
126 debug_here() # -> will open up the debugger at that point.
126 debug_here() # -> will open up the debugger at that point.
127
127
128 Once the debugger activates, you can use all of its regular commands to
128 Once the debugger activates, you can use all of its regular commands to
129 step through code, set breakpoints, etc. See the pdb documentation
129 step through code, set breakpoints, etc. See the pdb documentation
130 from the Python standard library for usage details.
130 from the Python standard library for usage details.
131 """
131 """
132 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
132 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
133 "`IPython.core.debugger.Pdb.set_trace()`",
133 "`IPython.core.debugger.Pdb.set_trace()`",
134 DeprecationWarning, stacklevel=2)
134 DeprecationWarning, stacklevel=2)
135
135
136 ip = get_ipython()
136 ip = get_ipython()
137 if ip is None:
137 if ip is None:
138 # Outside of ipython, we set our own exception hook manually
138 # Outside of ipython, we set our own exception hook manually
139 sys.excepthook = functools.partial(BdbQuit_excepthook,
139 sys.excepthook = functools.partial(BdbQuit_excepthook,
140 excepthook=sys.excepthook)
140 excepthook=sys.excepthook)
141 def_colors = 'NoColor'
141 def_colors = 'NoColor'
142 else:
142 else:
143 # In ipython, we use its custom exception handler mechanism
143 # In ipython, we use its custom exception handler mechanism
144 def_colors = ip.colors
144 def_colors = ip.colors
145 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
145 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
146
146
147 if colors is None:
147 if colors is None:
148 colors = def_colors
148 colors = def_colors
149
149
150 # The stdlib debugger internally uses a modified repr from the `repr`
150 # The stdlib debugger internally uses a modified repr from the `repr`
151 # module, that limits the length of printed strings to a hardcoded
151 # module, that limits the length of printed strings to a hardcoded
152 # limit of 30 characters. That much trimming is too aggressive, let's
152 # limit of 30 characters. That much trimming is too aggressive, let's
153 # at least raise that limit to 80 chars, which should be enough for
153 # at least raise that limit to 80 chars, which should be enough for
154 # most interactive uses.
154 # most interactive uses.
155 try:
155 try:
156 from reprlib import aRepr
156 from reprlib import aRepr
157 aRepr.maxstring = 80
157 aRepr.maxstring = 80
158 except:
158 except:
159 # This is only a user-facing convenience, so any error we encounter
159 # This is only a user-facing convenience, so any error we encounter
160 # here can be warned about but can be otherwise ignored. These
160 # here can be warned about but can be otherwise ignored. These
161 # printouts will tell us about problems if this API changes
161 # printouts will tell us about problems if this API changes
162 import traceback
162 import traceback
163 traceback.print_exc()
163 traceback.print_exc()
164
164
165 self.debugger = Pdb(colors)
165 self.debugger = Pdb(colors)
166
166
167 def __call__(self):
167 def __call__(self):
168 """Starts an interactive debugger at the point where called.
168 """Starts an interactive debugger at the point where called.
169
169
170 This is similar to the pdb.set_trace() function from the std lib, but
170 This is similar to the pdb.set_trace() function from the std lib, but
171 using IPython's enhanced debugger."""
171 using IPython's enhanced debugger."""
172
172
173 self.debugger.set_trace(sys._getframe().f_back)
173 self.debugger.set_trace(sys._getframe().f_back)
174
174
175
175
176 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
176 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
177
177
178
178
179 def strip_indentation(multiline_string):
179 def strip_indentation(multiline_string):
180 return RGX_EXTRA_INDENT.sub('', multiline_string)
180 return RGX_EXTRA_INDENT.sub('', multiline_string)
181
181
182
182
183 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
183 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
184 """Make new_fn have old_fn's doc string. This is particularly useful
184 """Make new_fn have old_fn's doc string. This is particularly useful
185 for the ``do_...`` commands that hook into the help system.
185 for the ``do_...`` commands that hook into the help system.
186 Adapted from from a comp.lang.python posting
186 Adapted from from a comp.lang.python posting
187 by Duncan Booth."""
187 by Duncan Booth."""
188 def wrapper(*args, **kw):
188 def wrapper(*args, **kw):
189 return new_fn(*args, **kw)
189 return new_fn(*args, **kw)
190 if old_fn.__doc__:
190 if old_fn.__doc__:
191 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
191 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
192 return wrapper
192 return wrapper
193
193
194
194
195 class Pdb(OldPdb):
195 class Pdb(OldPdb):
196 """Modified Pdb class, does not load readline.
196 """Modified Pdb class, does not load readline.
197
197
198 for a standalone version that uses prompt_toolkit, see
198 for a standalone version that uses prompt_toolkit, see
199 `IPython.terminal.debugger.TerminalPdb` and
199 `IPython.terminal.debugger.TerminalPdb` and
200 `IPython.terminal.debugger.set_trace()`
200 `IPython.terminal.debugger.set_trace()`
201 """
201 """
202
202
203 def __init__(self, color_scheme=None, completekey=None,
203 def __init__(self, color_scheme=None, completekey=None,
204 stdin=None, stdout=None, context=5, **kwargs):
204 stdin=None, stdout=None, context=5, **kwargs):
205 """Create a new IPython debugger.
205 """Create a new IPython debugger.
206
206
207 :param color_scheme: Deprecated, do not use.
207 :param color_scheme: Deprecated, do not use.
208 :param completekey: Passed to pdb.Pdb.
208 :param completekey: Passed to pdb.Pdb.
209 :param stdin: Passed to pdb.Pdb.
209 :param stdin: Passed to pdb.Pdb.
210 :param stdout: Passed to pdb.Pdb.
210 :param stdout: Passed to pdb.Pdb.
211 :param context: Number of lines of source code context to show when
211 :param context: Number of lines of source code context to show when
212 displaying stacktrace information.
212 displaying stacktrace information.
213 :param kwargs: Passed to pdb.Pdb.
213 :param kwargs: Passed to pdb.Pdb.
214 The possibilities are python version dependent, see the python
214 The possibilities are python version dependent, see the python
215 docs for more info.
215 docs for more info.
216 """
216 """
217
217
218 # Parent constructor:
218 # Parent constructor:
219 try:
219 try:
220 self.context = int(context)
220 self.context = int(context)
221 if self.context <= 0:
221 if self.context <= 0:
222 raise ValueError("Context must be a positive integer")
222 raise ValueError("Context must be a positive integer")
223 except (TypeError, ValueError):
223 except (TypeError, ValueError):
224 raise ValueError("Context must be a positive integer")
224 raise ValueError("Context must be a positive integer")
225
225
226 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
226 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
227 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
227 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
228
228
229 # IPython changes...
229 # IPython changes...
230 self.shell = get_ipython()
230 self.shell = get_ipython()
231
231
232 if self.shell is None:
232 if self.shell is None:
233 save_main = sys.modules['__main__']
233 save_main = sys.modules['__main__']
234 # No IPython instance running, we must create one
234 # No IPython instance running, we must create one
235 from IPython.terminal.interactiveshell import \
235 from IPython.terminal.interactiveshell import \
236 TerminalInteractiveShell
236 TerminalInteractiveShell
237 self.shell = TerminalInteractiveShell.instance()
237 self.shell = TerminalInteractiveShell.instance()
238 # needed by any code which calls __import__("__main__") after
238 # needed by any code which calls __import__("__main__") after
239 # the debugger was entered. See also #9941.
239 # the debugger was entered. See also #9941.
240 sys.modules['__main__'] = save_main
240 sys.modules['__main__'] = save_main
241
241
242 if color_scheme is not None:
242 if color_scheme is not None:
243 warnings.warn(
243 warnings.warn(
244 "The `color_scheme` argument is deprecated since version 5.1",
244 "The `color_scheme` argument is deprecated since version 5.1",
245 DeprecationWarning, stacklevel=2)
245 DeprecationWarning, stacklevel=2)
246 else:
246 else:
247 color_scheme = self.shell.colors
247 color_scheme = self.shell.colors
248
248
249 self.aliases = {}
249 self.aliases = {}
250
250
251 # Create color table: we copy the default one from the traceback
251 # Create color table: we copy the default one from the traceback
252 # module and add a few attributes needed for debugging
252 # module and add a few attributes needed for debugging
253 self.color_scheme_table = exception_colors()
253 self.color_scheme_table = exception_colors()
254
254
255 # shorthands
255 # shorthands
256 C = coloransi.TermColors
256 C = coloransi.TermColors
257 cst = self.color_scheme_table
257 cst = self.color_scheme_table
258
258
259 cst['NoColor'].colors.prompt = C.NoColor
259 cst['NoColor'].colors.prompt = C.NoColor
260 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
260 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
261 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
261 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
262
262
263 cst['Linux'].colors.prompt = C.Green
263 cst['Linux'].colors.prompt = C.Green
264 cst['Linux'].colors.breakpoint_enabled = C.LightRed
264 cst['Linux'].colors.breakpoint_enabled = C.LightRed
265 cst['Linux'].colors.breakpoint_disabled = C.Red
265 cst['Linux'].colors.breakpoint_disabled = C.Red
266
266
267 cst['LightBG'].colors.prompt = C.Blue
267 cst['LightBG'].colors.prompt = C.Blue
268 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
268 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
269 cst['LightBG'].colors.breakpoint_disabled = C.Red
269 cst['LightBG'].colors.breakpoint_disabled = C.Red
270
270
271 cst['Neutral'].colors.prompt = C.Blue
271 cst['Neutral'].colors.prompt = C.Blue
272 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
272 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
273 cst['Neutral'].colors.breakpoint_disabled = C.Red
273 cst['Neutral'].colors.breakpoint_disabled = C.Red
274
274
275
275
276 # Add a python parser so we can syntax highlight source while
276 # Add a python parser so we can syntax highlight source while
277 # debugging.
277 # debugging.
278 self.parser = PyColorize.Parser(style=color_scheme)
278 self.parser = PyColorize.Parser(style=color_scheme)
279 self.set_colors(color_scheme)
279 self.set_colors(color_scheme)
280
280
281 # Set the prompt - the default prompt is '(Pdb)'
281 # Set the prompt - the default prompt is '(Pdb)'
282 self.prompt = prompt
282 self.prompt = prompt
283
283
284 def set_colors(self, scheme):
284 def set_colors(self, scheme):
285 """Shorthand access to the color table scheme selector method."""
285 """Shorthand access to the color table scheme selector method."""
286 self.color_scheme_table.set_active_scheme(scheme)
286 self.color_scheme_table.set_active_scheme(scheme)
287 self.parser.style = scheme
287 self.parser.style = scheme
288
288
289 def interaction(self, frame, traceback):
289 def interaction(self, frame, traceback):
290 try:
290 try:
291 OldPdb.interaction(self, frame, traceback)
291 OldPdb.interaction(self, frame, traceback)
292 except KeyboardInterrupt:
292 except KeyboardInterrupt:
293 self.stdout.write('\n' + self.shell.get_exception_only())
293 self.stdout.write('\n' + self.shell.get_exception_only())
294
294
295 def new_do_up(self, arg):
295 def new_do_up(self, arg):
296 OldPdb.do_up(self, arg)
296 OldPdb.do_up(self, arg)
297 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
297 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
298
298
299 def new_do_down(self, arg):
299 def new_do_down(self, arg):
300 OldPdb.do_down(self, arg)
300 OldPdb.do_down(self, arg)
301
301
302 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
302 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
303
303
304 def new_do_frame(self, arg):
304 def new_do_frame(self, arg):
305 OldPdb.do_frame(self, arg)
305 OldPdb.do_frame(self, arg)
306
306
307 def new_do_quit(self, arg):
307 def new_do_quit(self, arg):
308
308
309 if hasattr(self, 'old_all_completions'):
309 if hasattr(self, 'old_all_completions'):
310 self.shell.Completer.all_completions=self.old_all_completions
310 self.shell.Completer.all_completions=self.old_all_completions
311
311
312 return OldPdb.do_quit(self, arg)
312 return OldPdb.do_quit(self, arg)
313
313
314 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
314 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
315
315
316 def new_do_restart(self, arg):
316 def new_do_restart(self, arg):
317 """Restart command. In the context of ipython this is exactly the same
317 """Restart command. In the context of ipython this is exactly the same
318 thing as 'quit'."""
318 thing as 'quit'."""
319 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
319 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
320 return self.do_quit(arg)
320 return self.do_quit(arg)
321
321
322 def print_stack_trace(self, context=None):
322 def print_stack_trace(self, context=None):
323 if context is None:
323 if context is None:
324 context = self.context
324 context = self.context
325 try:
325 try:
326 context=int(context)
326 context=int(context)
327 if context <= 0:
327 if context <= 0:
328 raise ValueError("Context must be a positive integer")
328 raise ValueError("Context must be a positive integer")
329 except (TypeError, ValueError):
329 except (TypeError, ValueError):
330 raise ValueError("Context must be a positive integer")
330 raise ValueError("Context must be a positive integer")
331 try:
331 try:
332 for frame_lineno in self.stack:
332 for frame_lineno in self.stack:
333 self.print_stack_entry(frame_lineno, context=context)
333 self.print_stack_entry(frame_lineno, context=context)
334 except KeyboardInterrupt:
334 except KeyboardInterrupt:
335 pass
335 pass
336
336
337 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
337 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
338 context=None):
338 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 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
347 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
348
348
349 # vds: >>
349 # vds: >>
350 frame, lineno = frame_lineno
350 frame, lineno = frame_lineno
351 filename = frame.f_code.co_filename
351 filename = frame.f_code.co_filename
352 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
352 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
353 # vds: <<
353 # vds: <<
354
354
355 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
355 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
356 if context is None:
356 if context is None:
357 context = self.context
357 context = self.context
358 try:
358 try:
359 context=int(context)
359 context=int(context)
360 if context <= 0:
360 if context <= 0:
361 print("Context must be a positive integer", file=self.stdout)
361 print("Context must be a positive integer", file=self.stdout)
362 except (TypeError, ValueError):
362 except (TypeError, ValueError):
363 print("Context must be a positive integer", file=self.stdout)
363 print("Context must be a positive integer", file=self.stdout)
364 try:
364
365 import reprlib # Py 3
365 import reprlib
366 except ImportError:
367 import repr as reprlib # Py 2
368
366
369 ret = []
367 ret = []
370
368
371 Colors = self.color_scheme_table.active_colors
369 Colors = self.color_scheme_table.active_colors
372 ColorsNormal = Colors.Normal
370 ColorsNormal = Colors.Normal
373 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
371 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
374 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
372 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
375 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
373 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
376 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
374 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
377 ColorsNormal)
375 ColorsNormal)
378
376
379 frame, lineno = frame_lineno
377 frame, lineno = frame_lineno
380
378
381 return_value = ''
379 return_value = ''
382 if '__return__' in frame.f_locals:
380 if '__return__' in frame.f_locals:
383 rv = frame.f_locals['__return__']
381 rv = frame.f_locals['__return__']
384 #return_value += '->'
382 #return_value += '->'
385 return_value += reprlib.repr(rv) + '\n'
383 return_value += reprlib.repr(rv) + '\n'
386 ret.append(return_value)
384 ret.append(return_value)
387
385
388 #s = filename + '(' + `lineno` + ')'
386 #s = filename + '(' + `lineno` + ')'
389 filename = self.canonic(frame.f_code.co_filename)
387 filename = self.canonic(frame.f_code.co_filename)
390 link = tpl_link % py3compat.cast_unicode(filename)
388 link = tpl_link % py3compat.cast_unicode(filename)
391
389
392 if frame.f_code.co_name:
390 if frame.f_code.co_name:
393 func = frame.f_code.co_name
391 func = frame.f_code.co_name
394 else:
392 else:
395 func = "<lambda>"
393 func = "<lambda>"
396
394
397 call = ''
395 call = ''
398 if func != '?':
396 if func != '?':
399 if '__args__' in frame.f_locals:
397 if '__args__' in frame.f_locals:
400 args = reprlib.repr(frame.f_locals['__args__'])
398 args = reprlib.repr(frame.f_locals['__args__'])
401 else:
399 else:
402 args = '()'
400 args = '()'
403 call = tpl_call % (func, args)
401 call = tpl_call % (func, args)
404
402
405 # The level info should be generated in the same format pdb uses, to
403 # The level info should be generated in the same format pdb uses, to
406 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
404 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
407 if frame is self.curframe:
405 if frame is self.curframe:
408 ret.append('> ')
406 ret.append('> ')
409 else:
407 else:
410 ret.append(' ')
408 ret.append(' ')
411 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
409 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
412
410
413 start = lineno - 1 - context//2
411 start = lineno - 1 - context//2
414 lines = linecache.getlines(filename)
412 lines = linecache.getlines(filename)
415 start = min(start, len(lines) - context)
413 start = min(start, len(lines) - context)
416 start = max(start, 0)
414 start = max(start, 0)
417 lines = lines[start : start + context]
415 lines = lines[start : start + context]
418
416
419 for i,line in enumerate(lines):
417 for i,line in enumerate(lines):
420 show_arrow = (start + 1 + i == lineno)
418 show_arrow = (start + 1 + i == lineno)
421 linetpl = (frame is self.curframe or show_arrow) \
419 linetpl = (frame is self.curframe or show_arrow) \
422 and tpl_line_em \
420 and tpl_line_em \
423 or tpl_line
421 or tpl_line
424 ret.append(self.__format_line(linetpl, filename,
422 ret.append(self.__format_line(linetpl, filename,
425 start + 1 + i, line,
423 start + 1 + i, line,
426 arrow = show_arrow) )
424 arrow = show_arrow) )
427 return ''.join(ret)
425 return ''.join(ret)
428
426
429 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
427 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
430 bp_mark = ""
428 bp_mark = ""
431 bp_mark_color = ""
429 bp_mark_color = ""
432
430
433 new_line, err = self.parser.format2(line, 'str')
431 new_line, err = self.parser.format2(line, 'str')
434 if not err:
432 if not err:
435 line = new_line
433 line = new_line
436
434
437 bp = None
435 bp = None
438 if lineno in self.get_file_breaks(filename):
436 if lineno in self.get_file_breaks(filename):
439 bps = self.get_breaks(filename, lineno)
437 bps = self.get_breaks(filename, lineno)
440 bp = bps[-1]
438 bp = bps[-1]
441
439
442 if bp:
440 if bp:
443 Colors = self.color_scheme_table.active_colors
441 Colors = self.color_scheme_table.active_colors
444 bp_mark = str(bp.number)
442 bp_mark = str(bp.number)
445 bp_mark_color = Colors.breakpoint_enabled
443 bp_mark_color = Colors.breakpoint_enabled
446 if not bp.enabled:
444 if not bp.enabled:
447 bp_mark_color = Colors.breakpoint_disabled
445 bp_mark_color = Colors.breakpoint_disabled
448
446
449 numbers_width = 7
447 numbers_width = 7
450 if arrow:
448 if arrow:
451 # This is the line with the error
449 # This is the line with the error
452 pad = numbers_width - len(str(lineno)) - len(bp_mark)
450 pad = numbers_width - len(str(lineno)) - len(bp_mark)
453 num = '%s%s' % (make_arrow(pad), str(lineno))
451 num = '%s%s' % (make_arrow(pad), str(lineno))
454 else:
452 else:
455 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
453 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
456
454
457 return tpl_line % (bp_mark_color + bp_mark, num, line)
455 return tpl_line % (bp_mark_color + bp_mark, num, line)
458
456
459
457
460 def print_list_lines(self, filename, first, last):
458 def print_list_lines(self, filename, first, last):
461 """The printing (as opposed to the parsing part of a 'list'
459 """The printing (as opposed to the parsing part of a 'list'
462 command."""
460 command."""
463 try:
461 try:
464 Colors = self.color_scheme_table.active_colors
462 Colors = self.color_scheme_table.active_colors
465 ColorsNormal = Colors.Normal
463 ColorsNormal = Colors.Normal
466 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
464 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
467 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
465 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
468 src = []
466 src = []
469 if filename == "<string>" and hasattr(self, "_exec_filename"):
467 if filename == "<string>" and hasattr(self, "_exec_filename"):
470 filename = self._exec_filename
468 filename = self._exec_filename
471
469
472 for lineno in range(first, last+1):
470 for lineno in range(first, last+1):
473 line = linecache.getline(filename, lineno)
471 line = linecache.getline(filename, lineno)
474 if not line:
472 if not line:
475 break
473 break
476
474
477 if lineno == self.curframe.f_lineno:
475 if lineno == self.curframe.f_lineno:
478 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
476 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
479 else:
477 else:
480 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
478 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
481
479
482 src.append(line)
480 src.append(line)
483 self.lineno = lineno
481 self.lineno = lineno
484
482
485 print(''.join(src), file=self.stdout)
483 print(''.join(src), file=self.stdout)
486
484
487 except KeyboardInterrupt:
485 except KeyboardInterrupt:
488 pass
486 pass
489
487
490 def do_list(self, arg):
488 def do_list(self, arg):
491 """Print lines of code from the current stack frame
489 """Print lines of code from the current stack frame
492 """
490 """
493 self.lastcmd = 'list'
491 self.lastcmd = 'list'
494 last = None
492 last = None
495 if arg:
493 if arg:
496 try:
494 try:
497 x = eval(arg, {}, {})
495 x = eval(arg, {}, {})
498 if type(x) == type(()):
496 if type(x) == type(()):
499 first, last = x
497 first, last = x
500 first = int(first)
498 first = int(first)
501 last = int(last)
499 last = int(last)
502 if last < first:
500 if last < first:
503 # Assume it's a count
501 # Assume it's a count
504 last = first + last
502 last = first + last
505 else:
503 else:
506 first = max(1, int(x) - 5)
504 first = max(1, int(x) - 5)
507 except:
505 except:
508 print('*** Error in argument:', repr(arg), file=self.stdout)
506 print('*** Error in argument:', repr(arg), file=self.stdout)
509 return
507 return
510 elif self.lineno is None:
508 elif self.lineno is None:
511 first = max(1, self.curframe.f_lineno - 5)
509 first = max(1, self.curframe.f_lineno - 5)
512 else:
510 else:
513 first = self.lineno + 1
511 first = self.lineno + 1
514 if last is None:
512 if last is None:
515 last = first + 10
513 last = first + 10
516 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
514 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
517
515
518 # vds: >>
516 # vds: >>
519 lineno = first
517 lineno = first
520 filename = self.curframe.f_code.co_filename
518 filename = self.curframe.f_code.co_filename
521 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
519 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
522 # vds: <<
520 # vds: <<
523
521
524 do_l = do_list
522 do_l = do_list
525
523
526 def getsourcelines(self, obj):
524 def getsourcelines(self, obj):
527 lines, lineno = inspect.findsource(obj)
525 lines, lineno = inspect.findsource(obj)
528 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
526 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
529 # must be a module frame: do not try to cut a block out of it
527 # must be a module frame: do not try to cut a block out of it
530 return lines, 1
528 return lines, 1
531 elif inspect.ismodule(obj):
529 elif inspect.ismodule(obj):
532 return lines, 1
530 return lines, 1
533 return inspect.getblock(lines[lineno:]), lineno+1
531 return inspect.getblock(lines[lineno:]), lineno+1
534
532
535 def do_longlist(self, arg):
533 def do_longlist(self, arg):
536 """Print lines of code from the current stack frame.
534 """Print lines of code from the current stack frame.
537
535
538 Shows more lines than 'list' does.
536 Shows more lines than 'list' does.
539 """
537 """
540 self.lastcmd = 'longlist'
538 self.lastcmd = 'longlist'
541 try:
539 try:
542 lines, lineno = self.getsourcelines(self.curframe)
540 lines, lineno = self.getsourcelines(self.curframe)
543 except OSError as err:
541 except OSError as err:
544 self.error(err)
542 self.error(err)
545 return
543 return
546 last = lineno + len(lines)
544 last = lineno + len(lines)
547 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
545 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
548 do_ll = do_longlist
546 do_ll = do_longlist
549
547
550 def do_debug(self, arg):
548 def do_debug(self, arg):
551 """debug code
549 """debug code
552 Enter a recursive debugger that steps through the code
550 Enter a recursive debugger that steps through the code
553 argument (which is an arbitrary expression or statement to be
551 argument (which is an arbitrary expression or statement to be
554 executed in the current environment).
552 executed in the current environment).
555 """
553 """
556 sys.settrace(None)
554 sys.settrace(None)
557 globals = self.curframe.f_globals
555 globals = self.curframe.f_globals
558 locals = self.curframe_locals
556 locals = self.curframe_locals
559 p = self.__class__(completekey=self.completekey,
557 p = self.__class__(completekey=self.completekey,
560 stdin=self.stdin, stdout=self.stdout)
558 stdin=self.stdin, stdout=self.stdout)
561 p.use_rawinput = self.use_rawinput
559 p.use_rawinput = self.use_rawinput
562 p.prompt = "(%s) " % self.prompt.strip()
560 p.prompt = "(%s) " % self.prompt.strip()
563 self.message("ENTERING RECURSIVE DEBUGGER")
561 self.message("ENTERING RECURSIVE DEBUGGER")
564 sys.call_tracing(p.run, (arg, globals, locals))
562 sys.call_tracing(p.run, (arg, globals, locals))
565 self.message("LEAVING RECURSIVE DEBUGGER")
563 self.message("LEAVING RECURSIVE DEBUGGER")
566 sys.settrace(self.trace_dispatch)
564 sys.settrace(self.trace_dispatch)
567 self.lastcmd = p.lastcmd
565 self.lastcmd = p.lastcmd
568
566
569 def do_pdef(self, arg):
567 def do_pdef(self, arg):
570 """Print the call signature for any callable object.
568 """Print the call signature for any callable object.
571
569
572 The debugger interface to %pdef"""
570 The debugger interface to %pdef"""
573 namespaces = [('Locals', self.curframe.f_locals),
571 namespaces = [('Locals', self.curframe.f_locals),
574 ('Globals', self.curframe.f_globals)]
572 ('Globals', self.curframe.f_globals)]
575 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
573 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
576
574
577 def do_pdoc(self, arg):
575 def do_pdoc(self, arg):
578 """Print the docstring for an object.
576 """Print the docstring for an object.
579
577
580 The debugger interface to %pdoc."""
578 The debugger interface to %pdoc."""
581 namespaces = [('Locals', self.curframe.f_locals),
579 namespaces = [('Locals', self.curframe.f_locals),
582 ('Globals', self.curframe.f_globals)]
580 ('Globals', self.curframe.f_globals)]
583 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
581 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
584
582
585 def do_pfile(self, arg):
583 def do_pfile(self, arg):
586 """Print (or run through pager) the file where an object is defined.
584 """Print (or run through pager) the file where an object is defined.
587
585
588 The debugger interface to %pfile.
586 The debugger interface to %pfile.
589 """
587 """
590 namespaces = [('Locals', self.curframe.f_locals),
588 namespaces = [('Locals', self.curframe.f_locals),
591 ('Globals', self.curframe.f_globals)]
589 ('Globals', self.curframe.f_globals)]
592 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
590 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
593
591
594 def do_pinfo(self, arg):
592 def do_pinfo(self, arg):
595 """Provide detailed information about an object.
593 """Provide detailed information about an object.
596
594
597 The debugger interface to %pinfo, i.e., obj?."""
595 The debugger interface to %pinfo, i.e., obj?."""
598 namespaces = [('Locals', self.curframe.f_locals),
596 namespaces = [('Locals', self.curframe.f_locals),
599 ('Globals', self.curframe.f_globals)]
597 ('Globals', self.curframe.f_globals)]
600 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
598 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
601
599
602 def do_pinfo2(self, arg):
600 def do_pinfo2(self, arg):
603 """Provide extra detailed information about an object.
601 """Provide extra detailed information about an object.
604
602
605 The debugger interface to %pinfo2, i.e., obj??."""
603 The debugger interface to %pinfo2, i.e., obj??."""
606 namespaces = [('Locals', self.curframe.f_locals),
604 namespaces = [('Locals', self.curframe.f_locals),
607 ('Globals', self.curframe.f_globals)]
605 ('Globals', self.curframe.f_globals)]
608 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
606 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
609
607
610 def do_psource(self, arg):
608 def do_psource(self, arg):
611 """Print (or run through pager) the source code for an object."""
609 """Print (or run through pager) the source code for an object."""
612 namespaces = [('Locals', self.curframe.f_locals),
610 namespaces = [('Locals', self.curframe.f_locals),
613 ('Globals', self.curframe.f_globals)]
611 ('Globals', self.curframe.f_globals)]
614 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
612 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
615
613
616 def do_where(self, arg):
614 def do_where(self, arg):
617 """w(here)
615 """w(here)
618 Print a stack trace, with the most recent frame at the bottom.
616 Print a stack trace, with the most recent frame at the bottom.
619 An arrow indicates the "current frame", which determines the
617 An arrow indicates the "current frame", which determines the
620 context of most commands. 'bt' is an alias for this command.
618 context of most commands. 'bt' is an alias for this command.
621
619
622 Take a number as argument as an (optional) number of context line to
620 Take a number as argument as an (optional) number of context line to
623 print"""
621 print"""
624 if arg:
622 if arg:
625 context = int(arg)
623 context = int(arg)
626 self.print_stack_trace(context)
624 self.print_stack_trace(context)
627 else:
625 else:
628 self.print_stack_trace()
626 self.print_stack_trace()
629
627
630 do_w = do_where
628 do_w = do_where
631
629
632
630
633 def set_trace(frame=None):
631 def set_trace(frame=None):
634 """
632 """
635 Start debugging from `frame`.
633 Start debugging from `frame`.
636
634
637 If frame is not specified, debugging starts from caller's frame.
635 If frame is not specified, debugging starts from caller's frame.
638 """
636 """
639 Pdb().set_trace(frame or sys._getframe().f_back)
637 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,225 +1,222 b''
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
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 import sys
7 import sys
8 import warnings
8 import warnings
9
9
10 import nose.tools as nt
10 import nose.tools as nt
11
11
12 from IPython.core import debugger
12 from IPython.core import debugger
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Helper classes, from CPython's Pdb test suite
15 # Helper classes, from CPython's Pdb test suite
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 class _FakeInput(object):
18 class _FakeInput(object):
19 """
19 """
20 A fake input stream for pdb's interactive debugger. Whenever a
20 A fake input stream for pdb's interactive debugger. Whenever a
21 line is read, print it (to simulate the user typing it), and then
21 line is read, print it (to simulate the user typing it), and then
22 return it. The set of lines to return is specified in the
22 return it. The set of lines to return is specified in the
23 constructor; they should not have trailing newlines.
23 constructor; they should not have trailing newlines.
24 """
24 """
25 def __init__(self, lines):
25 def __init__(self, lines):
26 self.lines = iter(lines)
26 self.lines = iter(lines)
27
27
28 def readline(self):
28 def readline(self):
29 line = next(self.lines)
29 line = next(self.lines)
30 print(line)
30 print(line)
31 return line+'\n'
31 return line+'\n'
32
32
33 class PdbTestInput(object):
33 class PdbTestInput(object):
34 """Context manager that makes testing Pdb in doctests easier."""
34 """Context manager that makes testing Pdb in doctests easier."""
35
35
36 def __init__(self, input):
36 def __init__(self, input):
37 self.input = input
37 self.input = input
38
38
39 def __enter__(self):
39 def __enter__(self):
40 self.real_stdin = sys.stdin
40 self.real_stdin = sys.stdin
41 sys.stdin = _FakeInput(self.input)
41 sys.stdin = _FakeInput(self.input)
42
42
43 def __exit__(self, *exc):
43 def __exit__(self, *exc):
44 sys.stdin = self.real_stdin
44 sys.stdin = self.real_stdin
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Tests
47 # Tests
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 def test_longer_repr():
50 def test_longer_repr():
51 try:
51 from reprlib import repr as trepr
52 from reprlib import repr as trepr # Py 3
53 except ImportError:
54 from repr import repr as trepr # Py 2
55
52
56 a = '1234567890'* 7
53 a = '1234567890'* 7
57 ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'"
54 ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'"
58 a_trunc = "'123456789012...8901234567890'"
55 a_trunc = "'123456789012...8901234567890'"
59 nt.assert_equal(trepr(a), a_trunc)
56 nt.assert_equal(trepr(a), a_trunc)
60 # The creation of our tracer modifies the repr module's repr function
57 # The creation of our tracer modifies the repr module's repr function
61 # in-place, since that global is used directly by the stdlib's pdb module.
58 # in-place, since that global is used directly by the stdlib's pdb module.
62 with warnings.catch_warnings():
59 with warnings.catch_warnings():
63 warnings.simplefilter('ignore', DeprecationWarning)
60 warnings.simplefilter('ignore', DeprecationWarning)
64 debugger.Tracer()
61 debugger.Tracer()
65 nt.assert_equal(trepr(a), ar)
62 nt.assert_equal(trepr(a), ar)
66
63
67 def test_ipdb_magics():
64 def test_ipdb_magics():
68 '''Test calling some IPython magics from ipdb.
65 '''Test calling some IPython magics from ipdb.
69
66
70 First, set up some test functions and classes which we can inspect.
67 First, set up some test functions and classes which we can inspect.
71
68
72 >>> class ExampleClass(object):
69 >>> class ExampleClass(object):
73 ... """Docstring for ExampleClass."""
70 ... """Docstring for ExampleClass."""
74 ... def __init__(self):
71 ... def __init__(self):
75 ... """Docstring for ExampleClass.__init__"""
72 ... """Docstring for ExampleClass.__init__"""
76 ... pass
73 ... pass
77 ... def __str__(self):
74 ... def __str__(self):
78 ... return "ExampleClass()"
75 ... return "ExampleClass()"
79
76
80 >>> def example_function(x, y, z="hello"):
77 >>> def example_function(x, y, z="hello"):
81 ... """Docstring for example_function."""
78 ... """Docstring for example_function."""
82 ... pass
79 ... pass
83
80
84 >>> old_trace = sys.gettrace()
81 >>> old_trace = sys.gettrace()
85
82
86 Create a function which triggers ipdb.
83 Create a function which triggers ipdb.
87
84
88 >>> def trigger_ipdb():
85 >>> def trigger_ipdb():
89 ... a = ExampleClass()
86 ... a = ExampleClass()
90 ... debugger.Pdb().set_trace()
87 ... debugger.Pdb().set_trace()
91
88
92 >>> with PdbTestInput([
89 >>> with PdbTestInput([
93 ... 'pdef example_function',
90 ... 'pdef example_function',
94 ... 'pdoc ExampleClass',
91 ... 'pdoc ExampleClass',
95 ... 'up',
92 ... 'up',
96 ... 'down',
93 ... 'down',
97 ... 'list',
94 ... 'list',
98 ... 'pinfo a',
95 ... 'pinfo a',
99 ... 'll',
96 ... 'll',
100 ... 'continue',
97 ... 'continue',
101 ... ]):
98 ... ]):
102 ... trigger_ipdb()
99 ... trigger_ipdb()
103 --Return--
100 --Return--
104 None
101 None
105 > <doctest ...>(3)trigger_ipdb()
102 > <doctest ...>(3)trigger_ipdb()
106 1 def trigger_ipdb():
103 1 def trigger_ipdb():
107 2 a = ExampleClass()
104 2 a = ExampleClass()
108 ----> 3 debugger.Pdb().set_trace()
105 ----> 3 debugger.Pdb().set_trace()
109 <BLANKLINE>
106 <BLANKLINE>
110 ipdb> pdef example_function
107 ipdb> pdef example_function
111 example_function(x, y, z='hello')
108 example_function(x, y, z='hello')
112 ipdb> pdoc ExampleClass
109 ipdb> pdoc ExampleClass
113 Class docstring:
110 Class docstring:
114 Docstring for ExampleClass.
111 Docstring for ExampleClass.
115 Init docstring:
112 Init docstring:
116 Docstring for ExampleClass.__init__
113 Docstring for ExampleClass.__init__
117 ipdb> up
114 ipdb> up
118 > <doctest ...>(11)<module>()
115 > <doctest ...>(11)<module>()
119 7 'pinfo a',
116 7 'pinfo a',
120 8 'll',
117 8 'll',
121 9 'continue',
118 9 'continue',
122 10 ]):
119 10 ]):
123 ---> 11 trigger_ipdb()
120 ---> 11 trigger_ipdb()
124 <BLANKLINE>
121 <BLANKLINE>
125 ipdb> down
122 ipdb> down
126 None
123 None
127 > <doctest ...>(3)trigger_ipdb()
124 > <doctest ...>(3)trigger_ipdb()
128 1 def trigger_ipdb():
125 1 def trigger_ipdb():
129 2 a = ExampleClass()
126 2 a = ExampleClass()
130 ----> 3 debugger.Pdb().set_trace()
127 ----> 3 debugger.Pdb().set_trace()
131 <BLANKLINE>
128 <BLANKLINE>
132 ipdb> list
129 ipdb> list
133 1 def trigger_ipdb():
130 1 def trigger_ipdb():
134 2 a = ExampleClass()
131 2 a = ExampleClass()
135 ----> 3 debugger.Pdb().set_trace()
132 ----> 3 debugger.Pdb().set_trace()
136 <BLANKLINE>
133 <BLANKLINE>
137 ipdb> pinfo a
134 ipdb> pinfo a
138 Type: ExampleClass
135 Type: ExampleClass
139 String form: ExampleClass()
136 String form: ExampleClass()
140 Namespace: Local...
137 Namespace: Local...
141 Docstring: Docstring for ExampleClass.
138 Docstring: Docstring for ExampleClass.
142 Init docstring: Docstring for ExampleClass.__init__
139 Init docstring: Docstring for ExampleClass.__init__
143 ipdb> ll
140 ipdb> ll
144 1 def trigger_ipdb():
141 1 def trigger_ipdb():
145 2 a = ExampleClass()
142 2 a = ExampleClass()
146 ----> 3 debugger.Pdb().set_trace()
143 ----> 3 debugger.Pdb().set_trace()
147 <BLANKLINE>
144 <BLANKLINE>
148 ipdb> continue
145 ipdb> continue
149
146
150 Restore previous trace function, e.g. for coverage.py
147 Restore previous trace function, e.g. for coverage.py
151
148
152 >>> sys.settrace(old_trace)
149 >>> sys.settrace(old_trace)
153 '''
150 '''
154
151
155 def test_ipdb_magics2():
152 def test_ipdb_magics2():
156 '''Test ipdb with a very short function.
153 '''Test ipdb with a very short function.
157
154
158 >>> old_trace = sys.gettrace()
155 >>> old_trace = sys.gettrace()
159
156
160 >>> def bar():
157 >>> def bar():
161 ... pass
158 ... pass
162
159
163 Run ipdb.
160 Run ipdb.
164
161
165 >>> with PdbTestInput([
162 >>> with PdbTestInput([
166 ... 'continue',
163 ... 'continue',
167 ... ]):
164 ... ]):
168 ... debugger.Pdb().runcall(bar)
165 ... debugger.Pdb().runcall(bar)
169 > <doctest ...>(2)bar()
166 > <doctest ...>(2)bar()
170 1 def bar():
167 1 def bar():
171 ----> 2 pass
168 ----> 2 pass
172 <BLANKLINE>
169 <BLANKLINE>
173 ipdb> continue
170 ipdb> continue
174
171
175 Restore previous trace function, e.g. for coverage.py
172 Restore previous trace function, e.g. for coverage.py
176
173
177 >>> sys.settrace(old_trace)
174 >>> sys.settrace(old_trace)
178 '''
175 '''
179
176
180 def can_quit():
177 def can_quit():
181 '''Test that quit work in ipydb
178 '''Test that quit work in ipydb
182
179
183 >>> old_trace = sys.gettrace()
180 >>> old_trace = sys.gettrace()
184
181
185 >>> def bar():
182 >>> def bar():
186 ... pass
183 ... pass
187
184
188 >>> with PdbTestInput([
185 >>> with PdbTestInput([
189 ... 'quit',
186 ... 'quit',
190 ... ]):
187 ... ]):
191 ... debugger.Pdb().runcall(bar)
188 ... debugger.Pdb().runcall(bar)
192 > <doctest ...>(2)bar()
189 > <doctest ...>(2)bar()
193 1 def bar():
190 1 def bar():
194 ----> 2 pass
191 ----> 2 pass
195 <BLANKLINE>
192 <BLANKLINE>
196 ipdb> quit
193 ipdb> quit
197
194
198 Restore previous trace function, e.g. for coverage.py
195 Restore previous trace function, e.g. for coverage.py
199
196
200 >>> sys.settrace(old_trace)
197 >>> sys.settrace(old_trace)
201 '''
198 '''
202
199
203
200
204 def can_exit():
201 def can_exit():
205 '''Test that quit work in ipydb
202 '''Test that quit work in ipydb
206
203
207 >>> old_trace = sys.gettrace()
204 >>> old_trace = sys.gettrace()
208
205
209 >>> def bar():
206 >>> def bar():
210 ... pass
207 ... pass
211
208
212 >>> with PdbTestInput([
209 >>> with PdbTestInput([
213 ... 'exit',
210 ... 'exit',
214 ... ]):
211 ... ]):
215 ... debugger.Pdb().runcall(bar)
212 ... debugger.Pdb().runcall(bar)
216 > <doctest ...>(2)bar()
213 > <doctest ...>(2)bar()
217 1 def bar():
214 1 def bar():
218 ----> 2 pass
215 ----> 2 pass
219 <BLANKLINE>
216 <BLANKLINE>
220 ipdb> exit
217 ipdb> exit
221
218
222 Restore previous trace function, e.g. for coverage.py
219 Restore previous trace function, e.g. for coverage.py
223
220
224 >>> sys.settrace(old_trace)
221 >>> sys.settrace(old_trace)
225 '''
222 '''
@@ -1,654 +1,651 b''
1 """Various display related classes.
1 """Various display related classes.
2
2
3 Authors : MinRK, gregcaporaso, dannystaple
3 Authors : MinRK, gregcaporaso, dannystaple
4 """
4 """
5 from html import escape as html_escape
5 from html import escape as html_escape
6 from os.path import exists, isfile, splitext, abspath, join, isdir
6 from os.path import exists, isfile, splitext, abspath, join, isdir
7 from os import walk, sep, fsdecode
7 from os import walk, sep, fsdecode
8
8
9 from IPython.core.display import DisplayObject, TextDisplayObject
9 from IPython.core.display import DisplayObject, TextDisplayObject
10
10
11 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
11 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
12 'FileLink', 'FileLinks', 'Code']
12 'FileLink', 'FileLinks', 'Code']
13
13
14
14
15 class Audio(DisplayObject):
15 class Audio(DisplayObject):
16 """Create an audio object.
16 """Create an audio object.
17
17
18 When this object is returned by an input cell or passed to the
18 When this object is returned by an input cell or passed to the
19 display function, it will result in Audio controls being displayed
19 display function, it will result in Audio controls being displayed
20 in the frontend (only works in the notebook).
20 in the frontend (only works in the notebook).
21
21
22 Parameters
22 Parameters
23 ----------
23 ----------
24 data : numpy array, list, unicode, str or bytes
24 data : numpy array, list, unicode, str or bytes
25 Can be one of
25 Can be one of
26
26
27 * Numpy 1d array containing the desired waveform (mono)
27 * Numpy 1d array containing the desired waveform (mono)
28 * Numpy 2d array containing waveforms for each channel.
28 * Numpy 2d array containing waveforms for each channel.
29 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
29 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
30 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
30 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
31 * List of float or integer representing the waveform (mono)
31 * List of float or integer representing the waveform (mono)
32 * String containing the filename
32 * String containing the filename
33 * Bytestring containing raw PCM data or
33 * Bytestring containing raw PCM data or
34 * URL pointing to a file on the web.
34 * URL pointing to a file on the web.
35
35
36 If the array option is used, the waveform will be normalized.
36 If the array option is used, the waveform will be normalized.
37
37
38 If a filename or url is used, the format support will be browser
38 If a filename or url is used, the format support will be browser
39 dependent.
39 dependent.
40 url : unicode
40 url : unicode
41 A URL to download the data from.
41 A URL to download the data from.
42 filename : unicode
42 filename : unicode
43 Path to a local file to load the data from.
43 Path to a local file to load the data from.
44 embed : boolean
44 embed : boolean
45 Should the audio data be embedded using a data URI (True) or should
45 Should the audio data be embedded using a data URI (True) or should
46 the original source be referenced. Set this to True if you want the
46 the original source be referenced. Set this to True if you want the
47 audio to playable later with no internet connection in the notebook.
47 audio to playable later with no internet connection in the notebook.
48
48
49 Default is `True`, unless the keyword argument `url` is set, then
49 Default is `True`, unless the keyword argument `url` is set, then
50 default value is `False`.
50 default value is `False`.
51 rate : integer
51 rate : integer
52 The sampling rate of the raw data.
52 The sampling rate of the raw data.
53 Only required when data parameter is being used as an array
53 Only required when data parameter is being used as an array
54 autoplay : bool
54 autoplay : bool
55 Set to True if the audio should immediately start playing.
55 Set to True if the audio should immediately start playing.
56 Default is `False`.
56 Default is `False`.
57 normalize : bool
57 normalize : bool
58 Whether audio should be normalized (rescaled) to the maximum possible
58 Whether audio should be normalized (rescaled) to the maximum possible
59 range. Default is `True`. When set to `False`, `data` must be between
59 range. Default is `True`. When set to `False`, `data` must be between
60 -1 and 1 (inclusive), otherwise an error is raised.
60 -1 and 1 (inclusive), otherwise an error is raised.
61 Applies only when `data` is a list or array of samples; other types of
61 Applies only when `data` is a list or array of samples; other types of
62 audio are never normalized.
62 audio are never normalized.
63
63
64 Examples
64 Examples
65 --------
65 --------
66 ::
66 ::
67
67
68 # Generate a sound
68 # Generate a sound
69 import numpy as np
69 import numpy as np
70 framerate = 44100
70 framerate = 44100
71 t = np.linspace(0,5,framerate*5)
71 t = np.linspace(0,5,framerate*5)
72 data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
72 data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
73 Audio(data,rate=framerate)
73 Audio(data,rate=framerate)
74
74
75 # Can also do stereo or more channels
75 # Can also do stereo or more channels
76 dataleft = np.sin(2*np.pi*220*t)
76 dataleft = np.sin(2*np.pi*220*t)
77 dataright = np.sin(2*np.pi*224*t)
77 dataright = np.sin(2*np.pi*224*t)
78 Audio([dataleft, dataright],rate=framerate)
78 Audio([dataleft, dataright],rate=framerate)
79
79
80 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
80 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
81 Audio(url="http://www.w3schools.com/html/horse.ogg")
81 Audio(url="http://www.w3schools.com/html/horse.ogg")
82
82
83 Audio('/path/to/sound.wav') # From file
83 Audio('/path/to/sound.wav') # From file
84 Audio(filename='/path/to/sound.ogg')
84 Audio(filename='/path/to/sound.ogg')
85
85
86 Audio(b'RAW_WAV_DATA..) # From bytes
86 Audio(b'RAW_WAV_DATA..) # From bytes
87 Audio(data=b'RAW_WAV_DATA..)
87 Audio(data=b'RAW_WAV_DATA..)
88
88
89 See Also
89 See Also
90 --------
90 --------
91
91
92 See also the ``Audio`` widgets form the ``ipywidget`` package for more flexibility and options.
92 See also the ``Audio`` widgets form the ``ipywidget`` package for more flexibility and options.
93
93
94 """
94 """
95 _read_flags = 'rb'
95 _read_flags = 'rb'
96
96
97 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
97 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
98 element_id=None):
98 element_id=None):
99 if filename is None and url is None and data is None:
99 if filename is None and url is None and data is None:
100 raise ValueError("No audio data found. Expecting filename, url, or data.")
100 raise ValueError("No audio data found. Expecting filename, url, or data.")
101 if embed is False and url is None:
101 if embed is False and url is None:
102 raise ValueError("No url found. Expecting url when embed=False")
102 raise ValueError("No url found. Expecting url when embed=False")
103
103
104 if url is not None and embed is not True:
104 if url is not None and embed is not True:
105 self.embed = False
105 self.embed = False
106 else:
106 else:
107 self.embed = True
107 self.embed = True
108 self.autoplay = autoplay
108 self.autoplay = autoplay
109 self.element_id = element_id
109 self.element_id = element_id
110 super(Audio, self).__init__(data=data, url=url, filename=filename)
110 super(Audio, self).__init__(data=data, url=url, filename=filename)
111
111
112 if self.data is not None and not isinstance(self.data, bytes):
112 if self.data is not None and not isinstance(self.data, bytes):
113 if rate is None:
113 if rate is None:
114 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
114 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
115 self.data = Audio._make_wav(data, rate, normalize)
115 self.data = Audio._make_wav(data, rate, normalize)
116
116
117 def reload(self):
117 def reload(self):
118 """Reload the raw data from file or URL."""
118 """Reload the raw data from file or URL."""
119 import mimetypes
119 import mimetypes
120 if self.embed:
120 if self.embed:
121 super(Audio, self).reload()
121 super(Audio, self).reload()
122
122
123 if self.filename is not None:
123 if self.filename is not None:
124 self.mimetype = mimetypes.guess_type(self.filename)[0]
124 self.mimetype = mimetypes.guess_type(self.filename)[0]
125 elif self.url is not None:
125 elif self.url is not None:
126 self.mimetype = mimetypes.guess_type(self.url)[0]
126 self.mimetype = mimetypes.guess_type(self.url)[0]
127 else:
127 else:
128 self.mimetype = "audio/wav"
128 self.mimetype = "audio/wav"
129
129
130 @staticmethod
130 @staticmethod
131 def _make_wav(data, rate, normalize):
131 def _make_wav(data, rate, normalize):
132 """ Transform a numpy array to a PCM bytestring """
132 """ Transform a numpy array to a PCM bytestring """
133 from io import BytesIO
133 from io import BytesIO
134 import wave
134 import wave
135
135
136 try:
136 try:
137 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
137 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
138 except ImportError:
138 except ImportError:
139 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
139 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
140
140
141 fp = BytesIO()
141 fp = BytesIO()
142 waveobj = wave.open(fp,mode='wb')
142 waveobj = wave.open(fp,mode='wb')
143 waveobj.setnchannels(nchan)
143 waveobj.setnchannels(nchan)
144 waveobj.setframerate(rate)
144 waveobj.setframerate(rate)
145 waveobj.setsampwidth(2)
145 waveobj.setsampwidth(2)
146 waveobj.setcomptype('NONE','NONE')
146 waveobj.setcomptype('NONE','NONE')
147 waveobj.writeframes(scaled)
147 waveobj.writeframes(scaled)
148 val = fp.getvalue()
148 val = fp.getvalue()
149 waveobj.close()
149 waveobj.close()
150
150
151 return val
151 return val
152
152
153 @staticmethod
153 @staticmethod
154 def _validate_and_normalize_with_numpy(data, normalize):
154 def _validate_and_normalize_with_numpy(data, normalize):
155 import numpy as np
155 import numpy as np
156
156
157 data = np.array(data, dtype=float)
157 data = np.array(data, dtype=float)
158 if len(data.shape) == 1:
158 if len(data.shape) == 1:
159 nchan = 1
159 nchan = 1
160 elif len(data.shape) == 2:
160 elif len(data.shape) == 2:
161 # In wave files,channels are interleaved. E.g.,
161 # In wave files,channels are interleaved. E.g.,
162 # "L1R1L2R2..." for stereo. See
162 # "L1R1L2R2..." for stereo. See
163 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
163 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
164 # for channel ordering
164 # for channel ordering
165 nchan = data.shape[0]
165 nchan = data.shape[0]
166 data = data.T.ravel()
166 data = data.T.ravel()
167 else:
167 else:
168 raise ValueError('Array audio input must be a 1D or 2D array')
168 raise ValueError('Array audio input must be a 1D or 2D array')
169
169
170 max_abs_value = np.max(np.abs(data))
170 max_abs_value = np.max(np.abs(data))
171 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
171 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
172 scaled = data / normalization_factor * 32767
172 scaled = data / normalization_factor * 32767
173 return scaled.astype('<h').tostring(), nchan
173 return scaled.astype('<h').tostring(), nchan
174
174
175
175
176 @staticmethod
176 @staticmethod
177 def _validate_and_normalize_without_numpy(data, normalize):
177 def _validate_and_normalize_without_numpy(data, normalize):
178 import array
178 import array
179 import sys
179 import sys
180
180
181 data = array.array('f', data)
181 data = array.array('f', data)
182
182
183 try:
183 try:
184 max_abs_value = float(max([abs(x) for x in data]))
184 max_abs_value = float(max([abs(x) for x in data]))
185 except TypeError:
185 except TypeError:
186 raise TypeError('Only lists of mono audio are '
186 raise TypeError('Only lists of mono audio are '
187 'supported if numpy is not installed')
187 'supported if numpy is not installed')
188
188
189 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
189 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
190 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
190 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
191 if sys.byteorder == 'big':
191 if sys.byteorder == 'big':
192 scaled.byteswap()
192 scaled.byteswap()
193 nchan = 1
193 nchan = 1
194 return scaled.tobytes(), nchan
194 return scaled.tobytes(), nchan
195
195
196 @staticmethod
196 @staticmethod
197 def _get_normalization_factor(max_abs_value, normalize):
197 def _get_normalization_factor(max_abs_value, normalize):
198 if not normalize and max_abs_value > 1:
198 if not normalize and max_abs_value > 1:
199 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
199 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
200 return max_abs_value if normalize else 1
200 return max_abs_value if normalize else 1
201
201
202 def _data_and_metadata(self):
202 def _data_and_metadata(self):
203 """shortcut for returning metadata with url information, if defined"""
203 """shortcut for returning metadata with url information, if defined"""
204 md = {}
204 md = {}
205 if self.url:
205 if self.url:
206 md['url'] = self.url
206 md['url'] = self.url
207 if md:
207 if md:
208 return self.data, md
208 return self.data, md
209 else:
209 else:
210 return self.data
210 return self.data
211
211
212 def _repr_html_(self):
212 def _repr_html_(self):
213 src = """
213 src = """
214 <audio {element_id} controls="controls" {autoplay}>
214 <audio {element_id} controls="controls" {autoplay}>
215 <source src="{src}" type="{type}" />
215 <source src="{src}" type="{type}" />
216 Your browser does not support the audio element.
216 Your browser does not support the audio element.
217 </audio>
217 </audio>
218 """
218 """
219 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
219 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
220 element_id=self.element_id_attr())
220 element_id=self.element_id_attr())
221
221
222 def src_attr(self):
222 def src_attr(self):
223 import base64
223 import base64
224 if self.embed and (self.data is not None):
224 if self.embed and (self.data is not None):
225 data = base64=base64.b64encode(self.data).decode('ascii')
225 data = base64=base64.b64encode(self.data).decode('ascii')
226 return """data:{type};base64,{base64}""".format(type=self.mimetype,
226 return """data:{type};base64,{base64}""".format(type=self.mimetype,
227 base64=data)
227 base64=data)
228 elif self.url is not None:
228 elif self.url is not None:
229 return self.url
229 return self.url
230 else:
230 else:
231 return ""
231 return ""
232
232
233 def autoplay_attr(self):
233 def autoplay_attr(self):
234 if(self.autoplay):
234 if(self.autoplay):
235 return 'autoplay="autoplay"'
235 return 'autoplay="autoplay"'
236 else:
236 else:
237 return ''
237 return ''
238
238
239 def element_id_attr(self):
239 def element_id_attr(self):
240 if (self.element_id):
240 if (self.element_id):
241 return 'id="{element_id}"'.format(element_id=self.element_id)
241 return 'id="{element_id}"'.format(element_id=self.element_id)
242 else:
242 else:
243 return ''
243 return ''
244
244
245 class IFrame(object):
245 class IFrame(object):
246 """
246 """
247 Generic class to embed an iframe in an IPython notebook
247 Generic class to embed an iframe in an IPython notebook
248 """
248 """
249
249
250 iframe = """
250 iframe = """
251 <iframe
251 <iframe
252 width="{width}"
252 width="{width}"
253 height="{height}"
253 height="{height}"
254 src="{src}{params}"
254 src="{src}{params}"
255 frameborder="0"
255 frameborder="0"
256 allowfullscreen
256 allowfullscreen
257 ></iframe>
257 ></iframe>
258 """
258 """
259
259
260 def __init__(self, src, width, height, **kwargs):
260 def __init__(self, src, width, height, **kwargs):
261 self.src = src
261 self.src = src
262 self.width = width
262 self.width = width
263 self.height = height
263 self.height = height
264 self.params = kwargs
264 self.params = kwargs
265
265
266 def _repr_html_(self):
266 def _repr_html_(self):
267 """return the embed iframe"""
267 """return the embed iframe"""
268 if self.params:
268 if self.params:
269 try:
269 from urllib.parse import urlencode
270 from urllib.parse import urlencode # Py 3
271 except ImportError:
272 from urllib import urlencode
273 params = "?" + urlencode(self.params)
270 params = "?" + urlencode(self.params)
274 else:
271 else:
275 params = ""
272 params = ""
276 return self.iframe.format(src=self.src,
273 return self.iframe.format(src=self.src,
277 width=self.width,
274 width=self.width,
278 height=self.height,
275 height=self.height,
279 params=params)
276 params=params)
280
277
281 class YouTubeVideo(IFrame):
278 class YouTubeVideo(IFrame):
282 """Class for embedding a YouTube Video in an IPython session, based on its video id.
279 """Class for embedding a YouTube Video in an IPython session, based on its video id.
283
280
284 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
281 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
285 do::
282 do::
286
283
287 vid = YouTubeVideo("foo")
284 vid = YouTubeVideo("foo")
288 display(vid)
285 display(vid)
289
286
290 To start from 30 seconds::
287 To start from 30 seconds::
291
288
292 vid = YouTubeVideo("abc", start=30)
289 vid = YouTubeVideo("abc", start=30)
293 display(vid)
290 display(vid)
294
291
295 To calculate seconds from time as hours, minutes, seconds use
292 To calculate seconds from time as hours, minutes, seconds use
296 :class:`datetime.timedelta`::
293 :class:`datetime.timedelta`::
297
294
298 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
295 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
299
296
300 Other parameters can be provided as documented at
297 Other parameters can be provided as documented at
301 https://developers.google.com/youtube/player_parameters#Parameters
298 https://developers.google.com/youtube/player_parameters#Parameters
302
299
303 When converting the notebook using nbconvert, a jpeg representation of the video
300 When converting the notebook using nbconvert, a jpeg representation of the video
304 will be inserted in the document.
301 will be inserted in the document.
305 """
302 """
306
303
307 def __init__(self, id, width=400, height=300, **kwargs):
304 def __init__(self, id, width=400, height=300, **kwargs):
308 self.id=id
305 self.id=id
309 src = "https://www.youtube.com/embed/{0}".format(id)
306 src = "https://www.youtube.com/embed/{0}".format(id)
310 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
307 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
311
308
312 def _repr_jpeg_(self):
309 def _repr_jpeg_(self):
313 # Deferred import
310 # Deferred import
314 from urllib.request import urlopen
311 from urllib.request import urlopen
315
312
316 try:
313 try:
317 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
314 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
318 except IOError:
315 except IOError:
319 return None
316 return None
320
317
321 class VimeoVideo(IFrame):
318 class VimeoVideo(IFrame):
322 """
319 """
323 Class for embedding a Vimeo video in an IPython session, based on its video id.
320 Class for embedding a Vimeo video in an IPython session, based on its video id.
324 """
321 """
325
322
326 def __init__(self, id, width=400, height=300, **kwargs):
323 def __init__(self, id, width=400, height=300, **kwargs):
327 src="https://player.vimeo.com/video/{0}".format(id)
324 src="https://player.vimeo.com/video/{0}".format(id)
328 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
325 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
329
326
330 class ScribdDocument(IFrame):
327 class ScribdDocument(IFrame):
331 """
328 """
332 Class for embedding a Scribd document in an IPython session
329 Class for embedding a Scribd document in an IPython session
333
330
334 Use the start_page params to specify a starting point in the document
331 Use the start_page params to specify a starting point in the document
335 Use the view_mode params to specify display type one off scroll | slideshow | book
332 Use the view_mode params to specify display type one off scroll | slideshow | book
336
333
337 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
334 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
338
335
339 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
336 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
340 """
337 """
341
338
342 def __init__(self, id, width=400, height=300, **kwargs):
339 def __init__(self, id, width=400, height=300, **kwargs):
343 src="https://www.scribd.com/embeds/{0}/content".format(id)
340 src="https://www.scribd.com/embeds/{0}/content".format(id)
344 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
341 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
345
342
346 class FileLink(object):
343 class FileLink(object):
347 """Class for embedding a local file link in an IPython session, based on path
344 """Class for embedding a local file link in an IPython session, based on path
348
345
349 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
346 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
350
347
351 you would do::
348 you would do::
352
349
353 local_file = FileLink("my/data.txt")
350 local_file = FileLink("my/data.txt")
354 display(local_file)
351 display(local_file)
355
352
356 or in the HTML notebook, just::
353 or in the HTML notebook, just::
357
354
358 FileLink("my/data.txt")
355 FileLink("my/data.txt")
359 """
356 """
360
357
361 html_link_str = "<a href='%s' target='_blank'>%s</a>"
358 html_link_str = "<a href='%s' target='_blank'>%s</a>"
362
359
363 def __init__(self,
360 def __init__(self,
364 path,
361 path,
365 url_prefix='',
362 url_prefix='',
366 result_html_prefix='',
363 result_html_prefix='',
367 result_html_suffix='<br>'):
364 result_html_suffix='<br>'):
368 """
365 """
369 Parameters
366 Parameters
370 ----------
367 ----------
371 path : str
368 path : str
372 path to the file or directory that should be formatted
369 path to the file or directory that should be formatted
373 url_prefix : str
370 url_prefix : str
374 prefix to be prepended to all files to form a working link [default:
371 prefix to be prepended to all files to form a working link [default:
375 '']
372 '']
376 result_html_prefix : str
373 result_html_prefix : str
377 text to append to beginning to link [default: '']
374 text to append to beginning to link [default: '']
378 result_html_suffix : str
375 result_html_suffix : str
379 text to append at the end of link [default: '<br>']
376 text to append at the end of link [default: '<br>']
380 """
377 """
381 if isdir(path):
378 if isdir(path):
382 raise ValueError("Cannot display a directory using FileLink. "
379 raise ValueError("Cannot display a directory using FileLink. "
383 "Use FileLinks to display '%s'." % path)
380 "Use FileLinks to display '%s'." % path)
384 self.path = fsdecode(path)
381 self.path = fsdecode(path)
385 self.url_prefix = url_prefix
382 self.url_prefix = url_prefix
386 self.result_html_prefix = result_html_prefix
383 self.result_html_prefix = result_html_prefix
387 self.result_html_suffix = result_html_suffix
384 self.result_html_suffix = result_html_suffix
388
385
389 def _format_path(self):
386 def _format_path(self):
390 fp = ''.join([self.url_prefix, html_escape(self.path)])
387 fp = ''.join([self.url_prefix, html_escape(self.path)])
391 return ''.join([self.result_html_prefix,
388 return ''.join([self.result_html_prefix,
392 self.html_link_str % \
389 self.html_link_str % \
393 (fp, html_escape(self.path, quote=False)),
390 (fp, html_escape(self.path, quote=False)),
394 self.result_html_suffix])
391 self.result_html_suffix])
395
392
396 def _repr_html_(self):
393 def _repr_html_(self):
397 """return html link to file
394 """return html link to file
398 """
395 """
399 if not exists(self.path):
396 if not exists(self.path):
400 return ("Path (<tt>%s</tt>) doesn't exist. "
397 return ("Path (<tt>%s</tt>) doesn't exist. "
401 "It may still be in the process of "
398 "It may still be in the process of "
402 "being generated, or you may have the "
399 "being generated, or you may have the "
403 "incorrect path." % self.path)
400 "incorrect path." % self.path)
404
401
405 return self._format_path()
402 return self._format_path()
406
403
407 def __repr__(self):
404 def __repr__(self):
408 """return absolute path to file
405 """return absolute path to file
409 """
406 """
410 return abspath(self.path)
407 return abspath(self.path)
411
408
412 class FileLinks(FileLink):
409 class FileLinks(FileLink):
413 """Class for embedding local file links in an IPython session, based on path
410 """Class for embedding local file links in an IPython session, based on path
414
411
415 e.g. to embed links to files that were generated in the IPython notebook
412 e.g. to embed links to files that were generated in the IPython notebook
416 under ``my/data``, you would do::
413 under ``my/data``, you would do::
417
414
418 local_files = FileLinks("my/data")
415 local_files = FileLinks("my/data")
419 display(local_files)
416 display(local_files)
420
417
421 or in the HTML notebook, just::
418 or in the HTML notebook, just::
422
419
423 FileLinks("my/data")
420 FileLinks("my/data")
424 """
421 """
425 def __init__(self,
422 def __init__(self,
426 path,
423 path,
427 url_prefix='',
424 url_prefix='',
428 included_suffixes=None,
425 included_suffixes=None,
429 result_html_prefix='',
426 result_html_prefix='',
430 result_html_suffix='<br>',
427 result_html_suffix='<br>',
431 notebook_display_formatter=None,
428 notebook_display_formatter=None,
432 terminal_display_formatter=None,
429 terminal_display_formatter=None,
433 recursive=True):
430 recursive=True):
434 """
431 """
435 See :class:`FileLink` for the ``path``, ``url_prefix``,
432 See :class:`FileLink` for the ``path``, ``url_prefix``,
436 ``result_html_prefix`` and ``result_html_suffix`` parameters.
433 ``result_html_prefix`` and ``result_html_suffix`` parameters.
437
434
438 included_suffixes : list
435 included_suffixes : list
439 Filename suffixes to include when formatting output [default: include
436 Filename suffixes to include when formatting output [default: include
440 all files]
437 all files]
441
438
442 notebook_display_formatter : function
439 notebook_display_formatter : function
443 Used to format links for display in the notebook. See discussion of
440 Used to format links for display in the notebook. See discussion of
444 formatter functions below.
441 formatter functions below.
445
442
446 terminal_display_formatter : function
443 terminal_display_formatter : function
447 Used to format links for display in the terminal. See discussion of
444 Used to format links for display in the terminal. See discussion of
448 formatter functions below.
445 formatter functions below.
449
446
450 Formatter functions must be of the form::
447 Formatter functions must be of the form::
451
448
452 f(dirname, fnames, included_suffixes)
449 f(dirname, fnames, included_suffixes)
453
450
454 dirname : str
451 dirname : str
455 The name of a directory
452 The name of a directory
456 fnames : list
453 fnames : list
457 The files in that directory
454 The files in that directory
458 included_suffixes : list
455 included_suffixes : list
459 The file suffixes that should be included in the output (passing None
456 The file suffixes that should be included in the output (passing None
460 meansto include all suffixes in the output in the built-in formatters)
457 meansto include all suffixes in the output in the built-in formatters)
461 recursive : boolean
458 recursive : boolean
462 Whether to recurse into subdirectories. Default is True.
459 Whether to recurse into subdirectories. Default is True.
463
460
464 The function should return a list of lines that will be printed in the
461 The function should return a list of lines that will be printed in the
465 notebook (if passing notebook_display_formatter) or the terminal (if
462 notebook (if passing notebook_display_formatter) or the terminal (if
466 passing terminal_display_formatter). This function is iterated over for
463 passing terminal_display_formatter). This function is iterated over for
467 each directory in self.path. Default formatters are in place, can be
464 each directory in self.path. Default formatters are in place, can be
468 passed here to support alternative formatting.
465 passed here to support alternative formatting.
469
466
470 """
467 """
471 if isfile(path):
468 if isfile(path):
472 raise ValueError("Cannot display a file using FileLinks. "
469 raise ValueError("Cannot display a file using FileLinks. "
473 "Use FileLink to display '%s'." % path)
470 "Use FileLink to display '%s'." % path)
474 self.included_suffixes = included_suffixes
471 self.included_suffixes = included_suffixes
475 # remove trailing slashes for more consistent output formatting
472 # remove trailing slashes for more consistent output formatting
476 path = path.rstrip('/')
473 path = path.rstrip('/')
477
474
478 self.path = path
475 self.path = path
479 self.url_prefix = url_prefix
476 self.url_prefix = url_prefix
480 self.result_html_prefix = result_html_prefix
477 self.result_html_prefix = result_html_prefix
481 self.result_html_suffix = result_html_suffix
478 self.result_html_suffix = result_html_suffix
482
479
483 self.notebook_display_formatter = \
480 self.notebook_display_formatter = \
484 notebook_display_formatter or self._get_notebook_display_formatter()
481 notebook_display_formatter or self._get_notebook_display_formatter()
485 self.terminal_display_formatter = \
482 self.terminal_display_formatter = \
486 terminal_display_formatter or self._get_terminal_display_formatter()
483 terminal_display_formatter or self._get_terminal_display_formatter()
487
484
488 self.recursive = recursive
485 self.recursive = recursive
489
486
490 def _get_display_formatter(self,
487 def _get_display_formatter(self,
491 dirname_output_format,
488 dirname_output_format,
492 fname_output_format,
489 fname_output_format,
493 fp_format,
490 fp_format,
494 fp_cleaner=None):
491 fp_cleaner=None):
495 """ generate built-in formatter function
492 """ generate built-in formatter function
496
493
497 this is used to define both the notebook and terminal built-in
494 this is used to define both the notebook and terminal built-in
498 formatters as they only differ by some wrapper text for each entry
495 formatters as they only differ by some wrapper text for each entry
499
496
500 dirname_output_format: string to use for formatting directory
497 dirname_output_format: string to use for formatting directory
501 names, dirname will be substituted for a single "%s" which
498 names, dirname will be substituted for a single "%s" which
502 must appear in this string
499 must appear in this string
503 fname_output_format: string to use for formatting file names,
500 fname_output_format: string to use for formatting file names,
504 if a single "%s" appears in the string, fname will be substituted
501 if a single "%s" appears in the string, fname will be substituted
505 if two "%s" appear in the string, the path to fname will be
502 if two "%s" appear in the string, the path to fname will be
506 substituted for the first and fname will be substituted for the
503 substituted for the first and fname will be substituted for the
507 second
504 second
508 fp_format: string to use for formatting filepaths, must contain
505 fp_format: string to use for formatting filepaths, must contain
509 exactly two "%s" and the dirname will be substituted for the first
506 exactly two "%s" and the dirname will be substituted for the first
510 and fname will be substituted for the second
507 and fname will be substituted for the second
511 """
508 """
512 def f(dirname, fnames, included_suffixes=None):
509 def f(dirname, fnames, included_suffixes=None):
513 result = []
510 result = []
514 # begin by figuring out which filenames, if any,
511 # begin by figuring out which filenames, if any,
515 # are going to be displayed
512 # are going to be displayed
516 display_fnames = []
513 display_fnames = []
517 for fname in fnames:
514 for fname in fnames:
518 if (isfile(join(dirname,fname)) and
515 if (isfile(join(dirname,fname)) and
519 (included_suffixes is None or
516 (included_suffixes is None or
520 splitext(fname)[1] in included_suffixes)):
517 splitext(fname)[1] in included_suffixes)):
521 display_fnames.append(fname)
518 display_fnames.append(fname)
522
519
523 if len(display_fnames) == 0:
520 if len(display_fnames) == 0:
524 # if there are no filenames to display, don't print anything
521 # if there are no filenames to display, don't print anything
525 # (not even the directory name)
522 # (not even the directory name)
526 pass
523 pass
527 else:
524 else:
528 # otherwise print the formatted directory name followed by
525 # otherwise print the formatted directory name followed by
529 # the formatted filenames
526 # the formatted filenames
530 dirname_output_line = dirname_output_format % dirname
527 dirname_output_line = dirname_output_format % dirname
531 result.append(dirname_output_line)
528 result.append(dirname_output_line)
532 for fname in display_fnames:
529 for fname in display_fnames:
533 fp = fp_format % (dirname,fname)
530 fp = fp_format % (dirname,fname)
534 if fp_cleaner is not None:
531 if fp_cleaner is not None:
535 fp = fp_cleaner(fp)
532 fp = fp_cleaner(fp)
536 try:
533 try:
537 # output can include both a filepath and a filename...
534 # output can include both a filepath and a filename...
538 fname_output_line = fname_output_format % (fp, fname)
535 fname_output_line = fname_output_format % (fp, fname)
539 except TypeError:
536 except TypeError:
540 # ... or just a single filepath
537 # ... or just a single filepath
541 fname_output_line = fname_output_format % fname
538 fname_output_line = fname_output_format % fname
542 result.append(fname_output_line)
539 result.append(fname_output_line)
543 return result
540 return result
544 return f
541 return f
545
542
546 def _get_notebook_display_formatter(self,
543 def _get_notebook_display_formatter(self,
547 spacer="&nbsp;&nbsp;"):
544 spacer="&nbsp;&nbsp;"):
548 """ generate function to use for notebook formatting
545 """ generate function to use for notebook formatting
549 """
546 """
550 dirname_output_format = \
547 dirname_output_format = \
551 self.result_html_prefix + "%s/" + self.result_html_suffix
548 self.result_html_prefix + "%s/" + self.result_html_suffix
552 fname_output_format = \
549 fname_output_format = \
553 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
550 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
554 fp_format = self.url_prefix + '%s/%s'
551 fp_format = self.url_prefix + '%s/%s'
555 if sep == "\\":
552 if sep == "\\":
556 # Working on a platform where the path separator is "\", so
553 # Working on a platform where the path separator is "\", so
557 # must convert these to "/" for generating a URI
554 # must convert these to "/" for generating a URI
558 def fp_cleaner(fp):
555 def fp_cleaner(fp):
559 # Replace all occurrences of backslash ("\") with a forward
556 # Replace all occurrences of backslash ("\") with a forward
560 # slash ("/") - this is necessary on windows when a path is
557 # slash ("/") - this is necessary on windows when a path is
561 # provided as input, but we must link to a URI
558 # provided as input, but we must link to a URI
562 return fp.replace('\\','/')
559 return fp.replace('\\','/')
563 else:
560 else:
564 fp_cleaner = None
561 fp_cleaner = None
565
562
566 return self._get_display_formatter(dirname_output_format,
563 return self._get_display_formatter(dirname_output_format,
567 fname_output_format,
564 fname_output_format,
568 fp_format,
565 fp_format,
569 fp_cleaner)
566 fp_cleaner)
570
567
571 def _get_terminal_display_formatter(self,
568 def _get_terminal_display_formatter(self,
572 spacer=" "):
569 spacer=" "):
573 """ generate function to use for terminal formatting
570 """ generate function to use for terminal formatting
574 """
571 """
575 dirname_output_format = "%s/"
572 dirname_output_format = "%s/"
576 fname_output_format = spacer + "%s"
573 fname_output_format = spacer + "%s"
577 fp_format = '%s/%s'
574 fp_format = '%s/%s'
578
575
579 return self._get_display_formatter(dirname_output_format,
576 return self._get_display_formatter(dirname_output_format,
580 fname_output_format,
577 fname_output_format,
581 fp_format)
578 fp_format)
582
579
583 def _format_path(self):
580 def _format_path(self):
584 result_lines = []
581 result_lines = []
585 if self.recursive:
582 if self.recursive:
586 walked_dir = list(walk(self.path))
583 walked_dir = list(walk(self.path))
587 else:
584 else:
588 walked_dir = [next(walk(self.path))]
585 walked_dir = [next(walk(self.path))]
589 walked_dir.sort()
586 walked_dir.sort()
590 for dirname, subdirs, fnames in walked_dir:
587 for dirname, subdirs, fnames in walked_dir:
591 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
588 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
592 return '\n'.join(result_lines)
589 return '\n'.join(result_lines)
593
590
594 def __repr__(self):
591 def __repr__(self):
595 """return newline-separated absolute paths
592 """return newline-separated absolute paths
596 """
593 """
597 result_lines = []
594 result_lines = []
598 if self.recursive:
595 if self.recursive:
599 walked_dir = list(walk(self.path))
596 walked_dir = list(walk(self.path))
600 else:
597 else:
601 walked_dir = [next(walk(self.path))]
598 walked_dir = [next(walk(self.path))]
602 walked_dir.sort()
599 walked_dir.sort()
603 for dirname, subdirs, fnames in walked_dir:
600 for dirname, subdirs, fnames in walked_dir:
604 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
601 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
605 return '\n'.join(result_lines)
602 return '\n'.join(result_lines)
606
603
607
604
608 class Code(TextDisplayObject):
605 class Code(TextDisplayObject):
609 """Display syntax-highlighted source code.
606 """Display syntax-highlighted source code.
610
607
611 This uses Pygments to highlight the code for HTML and Latex output.
608 This uses Pygments to highlight the code for HTML and Latex output.
612
609
613 Parameters
610 Parameters
614 ----------
611 ----------
615 data : str
612 data : str
616 The code as a string
613 The code as a string
617 url : str
614 url : str
618 A URL to fetch the code from
615 A URL to fetch the code from
619 filename : str
616 filename : str
620 A local filename to load the code from
617 A local filename to load the code from
621 language : str
618 language : str
622 The short name of a Pygments lexer to use for highlighting.
619 The short name of a Pygments lexer to use for highlighting.
623 If not specified, it will guess the lexer based on the filename
620 If not specified, it will guess the lexer based on the filename
624 or the code. Available lexers: http://pygments.org/docs/lexers/
621 or the code. Available lexers: http://pygments.org/docs/lexers/
625 """
622 """
626 def __init__(self, data=None, url=None, filename=None, language=None):
623 def __init__(self, data=None, url=None, filename=None, language=None):
627 self.language = language
624 self.language = language
628 super().__init__(data=data, url=url, filename=filename)
625 super().__init__(data=data, url=url, filename=filename)
629
626
630 def _get_lexer(self):
627 def _get_lexer(self):
631 if self.language:
628 if self.language:
632 from pygments.lexers import get_lexer_by_name
629 from pygments.lexers import get_lexer_by_name
633 return get_lexer_by_name(self.language)
630 return get_lexer_by_name(self.language)
634 elif self.filename:
631 elif self.filename:
635 from pygments.lexers import get_lexer_for_filename
632 from pygments.lexers import get_lexer_for_filename
636 return get_lexer_for_filename(self.filename)
633 return get_lexer_for_filename(self.filename)
637 else:
634 else:
638 from pygments.lexers import guess_lexer
635 from pygments.lexers import guess_lexer
639 return guess_lexer(self.data)
636 return guess_lexer(self.data)
640
637
641 def __repr__(self):
638 def __repr__(self):
642 return self.data
639 return self.data
643
640
644 def _repr_html_(self):
641 def _repr_html_(self):
645 from pygments import highlight
642 from pygments import highlight
646 from pygments.formatters import HtmlFormatter
643 from pygments.formatters import HtmlFormatter
647 fmt = HtmlFormatter()
644 fmt = HtmlFormatter()
648 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
645 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
649 return style + highlight(self.data, self._get_lexer(), fmt)
646 return style + highlight(self.data, self._get_lexer(), fmt)
650
647
651 def _repr_latex_(self):
648 def _repr_latex_(self):
652 from pygments import highlight
649 from pygments import highlight
653 from pygments.formatters import LatexFormatter
650 from pygments.formatters import LatexFormatter
654 return highlight(self.data, self._get_lexer(), LatexFormatter())
651 return highlight(self.data, self._get_lexer(), LatexFormatter())
@@ -1,666 +1,663 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 Deprecated since IPython 5.0
3 Deprecated since IPython 5.0
4
4
5 Inputhook management for GUI event loop integration.
5 Inputhook management for GUI event loop integration.
6 """
6 """
7
7
8 # Copyright (c) IPython Development Team.
8 # Copyright (c) IPython Development Team.
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10
10
11 try:
11 try:
12 import ctypes
12 import ctypes
13 except ImportError:
13 except ImportError:
14 ctypes = None
14 ctypes = None
15 except SystemError: # IronPython issue, 2/8/2014
15 except SystemError: # IronPython issue, 2/8/2014
16 ctypes = None
16 ctypes = None
17 import os
17 import os
18 import platform
18 import platform
19 import sys
19 import sys
20 from distutils.version import LooseVersion as V
20 from distutils.version import LooseVersion as V
21
21
22 from warnings import warn
22 from warnings import warn
23
23
24
24
25 warn("`IPython.lib.inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
25 warn("`IPython.lib.inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
26 DeprecationWarning, stacklevel=2)
26 DeprecationWarning, stacklevel=2)
27
27
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Constants
30 # Constants
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Constants for identifying the GUI toolkits.
33 # Constants for identifying the GUI toolkits.
34 GUI_WX = 'wx'
34 GUI_WX = 'wx'
35 GUI_QT = 'qt'
35 GUI_QT = 'qt'
36 GUI_QT4 = 'qt4'
36 GUI_QT4 = 'qt4'
37 GUI_GTK = 'gtk'
37 GUI_GTK = 'gtk'
38 GUI_TK = 'tk'
38 GUI_TK = 'tk'
39 GUI_OSX = 'osx'
39 GUI_OSX = 'osx'
40 GUI_GLUT = 'glut'
40 GUI_GLUT = 'glut'
41 GUI_PYGLET = 'pyglet'
41 GUI_PYGLET = 'pyglet'
42 GUI_GTK3 = 'gtk3'
42 GUI_GTK3 = 'gtk3'
43 GUI_NONE = 'none' # i.e. disable
43 GUI_NONE = 'none' # i.e. disable
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Utilities
46 # Utilities
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 def _stdin_ready_posix():
49 def _stdin_ready_posix():
50 """Return True if there's something to read on stdin (posix version)."""
50 """Return True if there's something to read on stdin (posix version)."""
51 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
51 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
52 return bool(infds)
52 return bool(infds)
53
53
54 def _stdin_ready_nt():
54 def _stdin_ready_nt():
55 """Return True if there's something to read on stdin (nt version)."""
55 """Return True if there's something to read on stdin (nt version)."""
56 return msvcrt.kbhit()
56 return msvcrt.kbhit()
57
57
58 def _stdin_ready_other():
58 def _stdin_ready_other():
59 """Return True, assuming there's something to read on stdin."""
59 """Return True, assuming there's something to read on stdin."""
60 return True
60 return True
61
61
62 def _use_appnope():
62 def _use_appnope():
63 """Should we use appnope for dealing with OS X app nap?
63 """Should we use appnope for dealing with OS X app nap?
64
64
65 Checks if we are on OS X 10.9 or greater.
65 Checks if we are on OS X 10.9 or greater.
66 """
66 """
67 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
67 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
68
68
69 def _ignore_CTRL_C_posix():
69 def _ignore_CTRL_C_posix():
70 """Ignore CTRL+C (SIGINT)."""
70 """Ignore CTRL+C (SIGINT)."""
71 signal.signal(signal.SIGINT, signal.SIG_IGN)
71 signal.signal(signal.SIGINT, signal.SIG_IGN)
72
72
73 def _allow_CTRL_C_posix():
73 def _allow_CTRL_C_posix():
74 """Take CTRL+C into account (SIGINT)."""
74 """Take CTRL+C into account (SIGINT)."""
75 signal.signal(signal.SIGINT, signal.default_int_handler)
75 signal.signal(signal.SIGINT, signal.default_int_handler)
76
76
77 def _ignore_CTRL_C_other():
77 def _ignore_CTRL_C_other():
78 """Ignore CTRL+C (not implemented)."""
78 """Ignore CTRL+C (not implemented)."""
79 pass
79 pass
80
80
81 def _allow_CTRL_C_other():
81 def _allow_CTRL_C_other():
82 """Take CTRL+C into account (not implemented)."""
82 """Take CTRL+C into account (not implemented)."""
83 pass
83 pass
84
84
85 if os.name == 'posix':
85 if os.name == 'posix':
86 import select
86 import select
87 import signal
87 import signal
88 stdin_ready = _stdin_ready_posix
88 stdin_ready = _stdin_ready_posix
89 ignore_CTRL_C = _ignore_CTRL_C_posix
89 ignore_CTRL_C = _ignore_CTRL_C_posix
90 allow_CTRL_C = _allow_CTRL_C_posix
90 allow_CTRL_C = _allow_CTRL_C_posix
91 elif os.name == 'nt':
91 elif os.name == 'nt':
92 import msvcrt
92 import msvcrt
93 stdin_ready = _stdin_ready_nt
93 stdin_ready = _stdin_ready_nt
94 ignore_CTRL_C = _ignore_CTRL_C_other
94 ignore_CTRL_C = _ignore_CTRL_C_other
95 allow_CTRL_C = _allow_CTRL_C_other
95 allow_CTRL_C = _allow_CTRL_C_other
96 else:
96 else:
97 stdin_ready = _stdin_ready_other
97 stdin_ready = _stdin_ready_other
98 ignore_CTRL_C = _ignore_CTRL_C_other
98 ignore_CTRL_C = _ignore_CTRL_C_other
99 allow_CTRL_C = _allow_CTRL_C_other
99 allow_CTRL_C = _allow_CTRL_C_other
100
100
101
101
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103 # Main InputHookManager class
103 # Main InputHookManager class
104 #-----------------------------------------------------------------------------
104 #-----------------------------------------------------------------------------
105
105
106
106
107 class InputHookManager(object):
107 class InputHookManager(object):
108 """DEPRECATED since IPython 5.0
108 """DEPRECATED since IPython 5.0
109
109
110 Manage PyOS_InputHook for different GUI toolkits.
110 Manage PyOS_InputHook for different GUI toolkits.
111
111
112 This class installs various hooks under ``PyOSInputHook`` to handle
112 This class installs various hooks under ``PyOSInputHook`` to handle
113 GUI event loop integration.
113 GUI event loop integration.
114 """
114 """
115
115
116 def __init__(self):
116 def __init__(self):
117 if ctypes is None:
117 if ctypes is None:
118 warn("IPython GUI event loop requires ctypes, %gui will not be available")
118 warn("IPython GUI event loop requires ctypes, %gui will not be available")
119 else:
119 else:
120 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
120 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
121 self.guihooks = {}
121 self.guihooks = {}
122 self.aliases = {}
122 self.aliases = {}
123 self.apps = {}
123 self.apps = {}
124 self._reset()
124 self._reset()
125
125
126 def _reset(self):
126 def _reset(self):
127 self._callback_pyfunctype = None
127 self._callback_pyfunctype = None
128 self._callback = None
128 self._callback = None
129 self._installed = False
129 self._installed = False
130 self._current_gui = None
130 self._current_gui = None
131
131
132 def get_pyos_inputhook(self):
132 def get_pyos_inputhook(self):
133 """DEPRECATED since IPython 5.0
133 """DEPRECATED since IPython 5.0
134
134
135 Return the current PyOS_InputHook as a ctypes.c_void_p."""
135 Return the current PyOS_InputHook as a ctypes.c_void_p."""
136 warn("`get_pyos_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
136 warn("`get_pyos_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
137 DeprecationWarning, stacklevel=2)
137 DeprecationWarning, stacklevel=2)
138 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
138 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
139
139
140 def get_pyos_inputhook_as_func(self):
140 def get_pyos_inputhook_as_func(self):
141 """DEPRECATED since IPython 5.0
141 """DEPRECATED since IPython 5.0
142
142
143 Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
143 Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
144 warn("`get_pyos_inputhook_as_func` is deprecated since IPython 5.0 and will be removed in future versions.",
144 warn("`get_pyos_inputhook_as_func` is deprecated since IPython 5.0 and will be removed in future versions.",
145 DeprecationWarning, stacklevel=2)
145 DeprecationWarning, stacklevel=2)
146 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
146 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
147
147
148 def set_inputhook(self, callback):
148 def set_inputhook(self, callback):
149 """DEPRECATED since IPython 5.0
149 """DEPRECATED since IPython 5.0
150
150
151 Set PyOS_InputHook to callback and return the previous one."""
151 Set PyOS_InputHook to callback and return the previous one."""
152 # On platforms with 'readline' support, it's all too likely to
152 # On platforms with 'readline' support, it's all too likely to
153 # have a KeyboardInterrupt signal delivered *even before* an
153 # have a KeyboardInterrupt signal delivered *even before* an
154 # initial ``try:`` clause in the callback can be executed, so
154 # initial ``try:`` clause in the callback can be executed, so
155 # we need to disable CTRL+C in this situation.
155 # we need to disable CTRL+C in this situation.
156 ignore_CTRL_C()
156 ignore_CTRL_C()
157 self._callback = callback
157 self._callback = callback
158 self._callback_pyfunctype = self.PYFUNC(callback)
158 self._callback_pyfunctype = self.PYFUNC(callback)
159 pyos_inputhook_ptr = self.get_pyos_inputhook()
159 pyos_inputhook_ptr = self.get_pyos_inputhook()
160 original = self.get_pyos_inputhook_as_func()
160 original = self.get_pyos_inputhook_as_func()
161 pyos_inputhook_ptr.value = \
161 pyos_inputhook_ptr.value = \
162 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
162 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
163 self._installed = True
163 self._installed = True
164 return original
164 return original
165
165
166 def clear_inputhook(self, app=None):
166 def clear_inputhook(self, app=None):
167 """DEPRECATED since IPython 5.0
167 """DEPRECATED since IPython 5.0
168
168
169 Set PyOS_InputHook to NULL and return the previous one.
169 Set PyOS_InputHook to NULL and return the previous one.
170
170
171 Parameters
171 Parameters
172 ----------
172 ----------
173 app : optional, ignored
173 app : optional, ignored
174 This parameter is allowed only so that clear_inputhook() can be
174 This parameter is allowed only so that clear_inputhook() can be
175 called with a similar interface as all the ``enable_*`` methods. But
175 called with a similar interface as all the ``enable_*`` methods. But
176 the actual value of the parameter is ignored. This uniform interface
176 the actual value of the parameter is ignored. This uniform interface
177 makes it easier to have user-level entry points in the main IPython
177 makes it easier to have user-level entry points in the main IPython
178 app like :meth:`enable_gui`."""
178 app like :meth:`enable_gui`."""
179 warn("`clear_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
179 warn("`clear_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
180 DeprecationWarning, stacklevel=2)
180 DeprecationWarning, stacklevel=2)
181 pyos_inputhook_ptr = self.get_pyos_inputhook()
181 pyos_inputhook_ptr = self.get_pyos_inputhook()
182 original = self.get_pyos_inputhook_as_func()
182 original = self.get_pyos_inputhook_as_func()
183 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
183 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
184 allow_CTRL_C()
184 allow_CTRL_C()
185 self._reset()
185 self._reset()
186 return original
186 return original
187
187
188 def clear_app_refs(self, gui=None):
188 def clear_app_refs(self, gui=None):
189 """DEPRECATED since IPython 5.0
189 """DEPRECATED since IPython 5.0
190
190
191 Clear IPython's internal reference to an application instance.
191 Clear IPython's internal reference to an application instance.
192
192
193 Whenever we create an app for a user on qt4 or wx, we hold a
193 Whenever we create an app for a user on qt4 or wx, we hold a
194 reference to the app. This is needed because in some cases bad things
194 reference to the app. This is needed because in some cases bad things
195 can happen if a user doesn't hold a reference themselves. This
195 can happen if a user doesn't hold a reference themselves. This
196 method is provided to clear the references we are holding.
196 method is provided to clear the references we are holding.
197
197
198 Parameters
198 Parameters
199 ----------
199 ----------
200 gui : None or str
200 gui : None or str
201 If None, clear all app references. If ('wx', 'qt4') clear
201 If None, clear all app references. If ('wx', 'qt4') clear
202 the app for that toolkit. References are not held for gtk or tk
202 the app for that toolkit. References are not held for gtk or tk
203 as those toolkits don't have the notion of an app.
203 as those toolkits don't have the notion of an app.
204 """
204 """
205 warn("`clear_app_refs` is deprecated since IPython 5.0 and will be removed in future versions.",
205 warn("`clear_app_refs` is deprecated since IPython 5.0 and will be removed in future versions.",
206 DeprecationWarning, stacklevel=2)
206 DeprecationWarning, stacklevel=2)
207 if gui is None:
207 if gui is None:
208 self.apps = {}
208 self.apps = {}
209 elif gui in self.apps:
209 elif gui in self.apps:
210 del self.apps[gui]
210 del self.apps[gui]
211
211
212 def register(self, toolkitname, *aliases):
212 def register(self, toolkitname, *aliases):
213 """DEPRECATED since IPython 5.0
213 """DEPRECATED since IPython 5.0
214
214
215 Register a class to provide the event loop for a given GUI.
215 Register a class to provide the event loop for a given GUI.
216
216
217 This is intended to be used as a class decorator. It should be passed
217 This is intended to be used as a class decorator. It should be passed
218 the names with which to register this GUI integration. The classes
218 the names with which to register this GUI integration. The classes
219 themselves should subclass :class:`InputHookBase`.
219 themselves should subclass :class:`InputHookBase`.
220
220
221 ::
221 ::
222
222
223 @inputhook_manager.register('qt')
223 @inputhook_manager.register('qt')
224 class QtInputHook(InputHookBase):
224 class QtInputHook(InputHookBase):
225 def enable(self, app=None):
225 def enable(self, app=None):
226 ...
226 ...
227 """
227 """
228 warn("`register` is deprecated since IPython 5.0 and will be removed in future versions.",
228 warn("`register` is deprecated since IPython 5.0 and will be removed in future versions.",
229 DeprecationWarning, stacklevel=2)
229 DeprecationWarning, stacklevel=2)
230 def decorator(cls):
230 def decorator(cls):
231 if ctypes is not None:
231 if ctypes is not None:
232 inst = cls(self)
232 inst = cls(self)
233 self.guihooks[toolkitname] = inst
233 self.guihooks[toolkitname] = inst
234 for a in aliases:
234 for a in aliases:
235 self.aliases[a] = toolkitname
235 self.aliases[a] = toolkitname
236 return cls
236 return cls
237 return decorator
237 return decorator
238
238
239 def current_gui(self):
239 def current_gui(self):
240 """DEPRECATED since IPython 5.0
240 """DEPRECATED since IPython 5.0
241
241
242 Return a string indicating the currently active GUI or None."""
242 Return a string indicating the currently active GUI or None."""
243 warn("`current_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
243 warn("`current_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
244 DeprecationWarning, stacklevel=2)
244 DeprecationWarning, stacklevel=2)
245 return self._current_gui
245 return self._current_gui
246
246
247 def enable_gui(self, gui=None, app=None):
247 def enable_gui(self, gui=None, app=None):
248 """DEPRECATED since IPython 5.0
248 """DEPRECATED since IPython 5.0
249
249
250 Switch amongst GUI input hooks by name.
250 Switch amongst GUI input hooks by name.
251
251
252 This is a higher level method than :meth:`set_inputhook` - it uses the
252 This is a higher level method than :meth:`set_inputhook` - it uses the
253 GUI name to look up a registered object which enables the input hook
253 GUI name to look up a registered object which enables the input hook
254 for that GUI.
254 for that GUI.
255
255
256 Parameters
256 Parameters
257 ----------
257 ----------
258 gui : optional, string or None
258 gui : optional, string or None
259 If None (or 'none'), clears input hook, otherwise it must be one
259 If None (or 'none'), clears input hook, otherwise it must be one
260 of the recognized GUI names (see ``GUI_*`` constants in module).
260 of the recognized GUI names (see ``GUI_*`` constants in module).
261
261
262 app : optional, existing application object.
262 app : optional, existing application object.
263 For toolkits that have the concept of a global app, you can supply an
263 For toolkits that have the concept of a global app, you can supply an
264 existing one. If not given, the toolkit will be probed for one, and if
264 existing one. If not given, the toolkit will be probed for one, and if
265 none is found, a new one will be created. Note that GTK does not have
265 none is found, a new one will be created. Note that GTK does not have
266 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
266 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
267
267
268 Returns
268 Returns
269 -------
269 -------
270 The output of the underlying gui switch routine, typically the actual
270 The output of the underlying gui switch routine, typically the actual
271 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
271 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
272 one.
272 one.
273 """
273 """
274 warn("`enable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
274 warn("`enable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
275 DeprecationWarning, stacklevel=2)
275 DeprecationWarning, stacklevel=2)
276 if gui in (None, GUI_NONE):
276 if gui in (None, GUI_NONE):
277 return self.disable_gui()
277 return self.disable_gui()
278
278
279 if gui in self.aliases:
279 if gui in self.aliases:
280 return self.enable_gui(self.aliases[gui], app)
280 return self.enable_gui(self.aliases[gui], app)
281
281
282 try:
282 try:
283 gui_hook = self.guihooks[gui]
283 gui_hook = self.guihooks[gui]
284 except KeyError:
284 except KeyError:
285 e = "Invalid GUI request {!r}, valid ones are: {}"
285 e = "Invalid GUI request {!r}, valid ones are: {}"
286 raise ValueError(e.format(gui, ', '.join(self.guihooks)))
286 raise ValueError(e.format(gui, ', '.join(self.guihooks)))
287 self._current_gui = gui
287 self._current_gui = gui
288
288
289 app = gui_hook.enable(app)
289 app = gui_hook.enable(app)
290 if app is not None:
290 if app is not None:
291 app._in_event_loop = True
291 app._in_event_loop = True
292 self.apps[gui] = app
292 self.apps[gui] = app
293 return app
293 return app
294
294
295 def disable_gui(self):
295 def disable_gui(self):
296 """DEPRECATED since IPython 5.0
296 """DEPRECATED since IPython 5.0
297
297
298 Disable GUI event loop integration.
298 Disable GUI event loop integration.
299
299
300 If an application was registered, this sets its ``_in_event_loop``
300 If an application was registered, this sets its ``_in_event_loop``
301 attribute to False. It then calls :meth:`clear_inputhook`.
301 attribute to False. It then calls :meth:`clear_inputhook`.
302 """
302 """
303 warn("`disable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
303 warn("`disable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
304 DeprecationWarning, stacklevel=2)
304 DeprecationWarning, stacklevel=2)
305 gui = self._current_gui
305 gui = self._current_gui
306 if gui in self.apps:
306 if gui in self.apps:
307 self.apps[gui]._in_event_loop = False
307 self.apps[gui]._in_event_loop = False
308 return self.clear_inputhook()
308 return self.clear_inputhook()
309
309
310 class InputHookBase(object):
310 class InputHookBase(object):
311 """DEPRECATED since IPython 5.0
311 """DEPRECATED since IPython 5.0
312
312
313 Base class for input hooks for specific toolkits.
313 Base class for input hooks for specific toolkits.
314
314
315 Subclasses should define an :meth:`enable` method with one argument, ``app``,
315 Subclasses should define an :meth:`enable` method with one argument, ``app``,
316 which will either be an instance of the toolkit's application class, or None.
316 which will either be an instance of the toolkit's application class, or None.
317 They may also define a :meth:`disable` method with no arguments.
317 They may also define a :meth:`disable` method with no arguments.
318 """
318 """
319 def __init__(self, manager):
319 def __init__(self, manager):
320 self.manager = manager
320 self.manager = manager
321
321
322 def disable(self):
322 def disable(self):
323 pass
323 pass
324
324
325 inputhook_manager = InputHookManager()
325 inputhook_manager = InputHookManager()
326
326
327 @inputhook_manager.register('osx')
327 @inputhook_manager.register('osx')
328 class NullInputHook(InputHookBase):
328 class NullInputHook(InputHookBase):
329 """DEPRECATED since IPython 5.0
329 """DEPRECATED since IPython 5.0
330
330
331 A null inputhook that doesn't need to do anything"""
331 A null inputhook that doesn't need to do anything"""
332 def enable(self, app=None):
332 def enable(self, app=None):
333 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
333 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
334 DeprecationWarning, stacklevel=2)
334 DeprecationWarning, stacklevel=2)
335
335
336 @inputhook_manager.register('wx')
336 @inputhook_manager.register('wx')
337 class WxInputHook(InputHookBase):
337 class WxInputHook(InputHookBase):
338 def enable(self, app=None):
338 def enable(self, app=None):
339 """DEPRECATED since IPython 5.0
339 """DEPRECATED since IPython 5.0
340
340
341 Enable event loop integration with wxPython.
341 Enable event loop integration with wxPython.
342
342
343 Parameters
343 Parameters
344 ----------
344 ----------
345 app : WX Application, optional.
345 app : WX Application, optional.
346 Running application to use. If not given, we probe WX for an
346 Running application to use. If not given, we probe WX for an
347 existing application object, and create a new one if none is found.
347 existing application object, and create a new one if none is found.
348
348
349 Notes
349 Notes
350 -----
350 -----
351 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
351 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
352 the wxPython to integrate with terminal based applications like
352 the wxPython to integrate with terminal based applications like
353 IPython.
353 IPython.
354
354
355 If ``app`` is not given we probe for an existing one, and return it if
355 If ``app`` is not given we probe for an existing one, and return it if
356 found. If no existing app is found, we create an :class:`wx.App` as
356 found. If no existing app is found, we create an :class:`wx.App` as
357 follows::
357 follows::
358
358
359 import wx
359 import wx
360 app = wx.App(redirect=False, clearSigInt=False)
360 app = wx.App(redirect=False, clearSigInt=False)
361 """
361 """
362 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
362 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
363 DeprecationWarning, stacklevel=2)
363 DeprecationWarning, stacklevel=2)
364 import wx
364 import wx
365
365
366 wx_version = V(wx.__version__).version
366 wx_version = V(wx.__version__).version
367
367
368 if wx_version < [2, 8]:
368 if wx_version < [2, 8]:
369 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
369 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
370
370
371 from IPython.lib.inputhookwx import inputhook_wx
371 from IPython.lib.inputhookwx import inputhook_wx
372 self.manager.set_inputhook(inputhook_wx)
372 self.manager.set_inputhook(inputhook_wx)
373 if _use_appnope():
373 if _use_appnope():
374 from appnope import nope
374 from appnope import nope
375 nope()
375 nope()
376
376
377 import wx
377 import wx
378 if app is None:
378 if app is None:
379 app = wx.GetApp()
379 app = wx.GetApp()
380 if app is None:
380 if app is None:
381 app = wx.App(redirect=False, clearSigInt=False)
381 app = wx.App(redirect=False, clearSigInt=False)
382
382
383 return app
383 return app
384
384
385 def disable(self):
385 def disable(self):
386 """DEPRECATED since IPython 5.0
386 """DEPRECATED since IPython 5.0
387
387
388 Disable event loop integration with wxPython.
388 Disable event loop integration with wxPython.
389
389
390 This restores appnapp on OS X
390 This restores appnapp on OS X
391 """
391 """
392 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
392 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
393 DeprecationWarning, stacklevel=2)
393 DeprecationWarning, stacklevel=2)
394 if _use_appnope():
394 if _use_appnope():
395 from appnope import nap
395 from appnope import nap
396 nap()
396 nap()
397
397
398 @inputhook_manager.register('qt', 'qt4')
398 @inputhook_manager.register('qt', 'qt4')
399 class Qt4InputHook(InputHookBase):
399 class Qt4InputHook(InputHookBase):
400 def enable(self, app=None):
400 def enable(self, app=None):
401 """DEPRECATED since IPython 5.0
401 """DEPRECATED since IPython 5.0
402
402
403 Enable event loop integration with PyQt4.
403 Enable event loop integration with PyQt4.
404
404
405 Parameters
405 Parameters
406 ----------
406 ----------
407 app : Qt Application, optional.
407 app : Qt Application, optional.
408 Running application to use. If not given, we probe Qt for an
408 Running application to use. If not given, we probe Qt for an
409 existing application object, and create a new one if none is found.
409 existing application object, and create a new one if none is found.
410
410
411 Notes
411 Notes
412 -----
412 -----
413 This methods sets the PyOS_InputHook for PyQt4, which allows
413 This methods sets the PyOS_InputHook for PyQt4, which allows
414 the PyQt4 to integrate with terminal based applications like
414 the PyQt4 to integrate with terminal based applications like
415 IPython.
415 IPython.
416
416
417 If ``app`` is not given we probe for an existing one, and return it if
417 If ``app`` is not given we probe for an existing one, and return it if
418 found. If no existing app is found, we create an :class:`QApplication`
418 found. If no existing app is found, we create an :class:`QApplication`
419 as follows::
419 as follows::
420
420
421 from PyQt4 import QtCore
421 from PyQt4 import QtCore
422 app = QtGui.QApplication(sys.argv)
422 app = QtGui.QApplication(sys.argv)
423 """
423 """
424 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
424 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
425 DeprecationWarning, stacklevel=2)
425 DeprecationWarning, stacklevel=2)
426 from IPython.lib.inputhookqt4 import create_inputhook_qt4
426 from IPython.lib.inputhookqt4 import create_inputhook_qt4
427 app, inputhook_qt4 = create_inputhook_qt4(self.manager, app)
427 app, inputhook_qt4 = create_inputhook_qt4(self.manager, app)
428 self.manager.set_inputhook(inputhook_qt4)
428 self.manager.set_inputhook(inputhook_qt4)
429 if _use_appnope():
429 if _use_appnope():
430 from appnope import nope
430 from appnope import nope
431 nope()
431 nope()
432
432
433 return app
433 return app
434
434
435 def disable_qt4(self):
435 def disable_qt4(self):
436 """DEPRECATED since IPython 5.0
436 """DEPRECATED since IPython 5.0
437
437
438 Disable event loop integration with PyQt4.
438 Disable event loop integration with PyQt4.
439
439
440 This restores appnapp on OS X
440 This restores appnapp on OS X
441 """
441 """
442 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
442 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
443 DeprecationWarning, stacklevel=2)
443 DeprecationWarning, stacklevel=2)
444 if _use_appnope():
444 if _use_appnope():
445 from appnope import nap
445 from appnope import nap
446 nap()
446 nap()
447
447
448
448
449 @inputhook_manager.register('qt5')
449 @inputhook_manager.register('qt5')
450 class Qt5InputHook(Qt4InputHook):
450 class Qt5InputHook(Qt4InputHook):
451 def enable(self, app=None):
451 def enable(self, app=None):
452 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
452 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
453 DeprecationWarning, stacklevel=2)
453 DeprecationWarning, stacklevel=2)
454 os.environ['QT_API'] = 'pyqt5'
454 os.environ['QT_API'] = 'pyqt5'
455 return Qt4InputHook.enable(self, app)
455 return Qt4InputHook.enable(self, app)
456
456
457
457
458 @inputhook_manager.register('gtk')
458 @inputhook_manager.register('gtk')
459 class GtkInputHook(InputHookBase):
459 class GtkInputHook(InputHookBase):
460 def enable(self, app=None):
460 def enable(self, app=None):
461 """DEPRECATED since IPython 5.0
461 """DEPRECATED since IPython 5.0
462
462
463 Enable event loop integration with PyGTK.
463 Enable event loop integration with PyGTK.
464
464
465 Parameters
465 Parameters
466 ----------
466 ----------
467 app : ignored
467 app : ignored
468 Ignored, it's only a placeholder to keep the call signature of all
468 Ignored, it's only a placeholder to keep the call signature of all
469 gui activation methods consistent, which simplifies the logic of
469 gui activation methods consistent, which simplifies the logic of
470 supporting magics.
470 supporting magics.
471
471
472 Notes
472 Notes
473 -----
473 -----
474 This methods sets the PyOS_InputHook for PyGTK, which allows
474 This methods sets the PyOS_InputHook for PyGTK, which allows
475 the PyGTK to integrate with terminal based applications like
475 the PyGTK to integrate with terminal based applications like
476 IPython.
476 IPython.
477 """
477 """
478 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
478 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
479 DeprecationWarning, stacklevel=2)
479 DeprecationWarning, stacklevel=2)
480 import gtk
480 import gtk
481 try:
481 try:
482 gtk.set_interactive(True)
482 gtk.set_interactive(True)
483 except AttributeError:
483 except AttributeError:
484 # For older versions of gtk, use our own ctypes version
484 # For older versions of gtk, use our own ctypes version
485 from IPython.lib.inputhookgtk import inputhook_gtk
485 from IPython.lib.inputhookgtk import inputhook_gtk
486 self.manager.set_inputhook(inputhook_gtk)
486 self.manager.set_inputhook(inputhook_gtk)
487
487
488
488
489 @inputhook_manager.register('tk')
489 @inputhook_manager.register('tk')
490 class TkInputHook(InputHookBase):
490 class TkInputHook(InputHookBase):
491 def enable(self, app=None):
491 def enable(self, app=None):
492 """DEPRECATED since IPython 5.0
492 """DEPRECATED since IPython 5.0
493
493
494 Enable event loop integration with Tk.
494 Enable event loop integration with Tk.
495
495
496 Parameters
496 Parameters
497 ----------
497 ----------
498 app : toplevel :class:`Tkinter.Tk` widget, optional.
498 app : toplevel :class:`Tkinter.Tk` widget, optional.
499 Running toplevel widget to use. If not given, we probe Tk for an
499 Running toplevel widget to use. If not given, we probe Tk for an
500 existing one, and create a new one if none is found.
500 existing one, and create a new one if none is found.
501
501
502 Notes
502 Notes
503 -----
503 -----
504 If you have already created a :class:`Tkinter.Tk` object, the only
504 If you have already created a :class:`Tkinter.Tk` object, the only
505 thing done by this method is to register with the
505 thing done by this method is to register with the
506 :class:`InputHookManager`, since creating that object automatically
506 :class:`InputHookManager`, since creating that object automatically
507 sets ``PyOS_InputHook``.
507 sets ``PyOS_InputHook``.
508 """
508 """
509 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
509 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
510 DeprecationWarning, stacklevel=2)
510 DeprecationWarning, stacklevel=2)
511 if app is None:
511 if app is None:
512 try:
512 from tkinter import Tk
513 from tkinter import Tk # Py 3
514 except ImportError:
515 from Tkinter import Tk # Py 2
516 app = Tk()
513 app = Tk()
517 app.withdraw()
514 app.withdraw()
518 self.manager.apps[GUI_TK] = app
515 self.manager.apps[GUI_TK] = app
519 return app
516 return app
520
517
521
518
522 @inputhook_manager.register('glut')
519 @inputhook_manager.register('glut')
523 class GlutInputHook(InputHookBase):
520 class GlutInputHook(InputHookBase):
524 def enable(self, app=None):
521 def enable(self, app=None):
525 """DEPRECATED since IPython 5.0
522 """DEPRECATED since IPython 5.0
526
523
527 Enable event loop integration with GLUT.
524 Enable event loop integration with GLUT.
528
525
529 Parameters
526 Parameters
530 ----------
527 ----------
531
528
532 app : ignored
529 app : ignored
533 Ignored, it's only a placeholder to keep the call signature of all
530 Ignored, it's only a placeholder to keep the call signature of all
534 gui activation methods consistent, which simplifies the logic of
531 gui activation methods consistent, which simplifies the logic of
535 supporting magics.
532 supporting magics.
536
533
537 Notes
534 Notes
538 -----
535 -----
539
536
540 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
537 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
541 integrate with terminal based applications like IPython. Due to GLUT
538 integrate with terminal based applications like IPython. Due to GLUT
542 limitations, it is currently not possible to start the event loop
539 limitations, it is currently not possible to start the event loop
543 without first creating a window. You should thus not create another
540 without first creating a window. You should thus not create another
544 window but use instead the created one. See 'gui-glut.py' in the
541 window but use instead the created one. See 'gui-glut.py' in the
545 docs/examples/lib directory.
542 docs/examples/lib directory.
546
543
547 The default screen mode is set to:
544 The default screen mode is set to:
548 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
545 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
549 """
546 """
550 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
547 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
551 DeprecationWarning, stacklevel=2)
548 DeprecationWarning, stacklevel=2)
552
549
553 import OpenGL.GLUT as glut
550 import OpenGL.GLUT as glut
554 from IPython.lib.inputhookglut import glut_display_mode, \
551 from IPython.lib.inputhookglut import glut_display_mode, \
555 glut_close, glut_display, \
552 glut_close, glut_display, \
556 glut_idle, inputhook_glut
553 glut_idle, inputhook_glut
557
554
558 if GUI_GLUT not in self.manager.apps:
555 if GUI_GLUT not in self.manager.apps:
559 glut.glutInit( sys.argv )
556 glut.glutInit( sys.argv )
560 glut.glutInitDisplayMode( glut_display_mode )
557 glut.glutInitDisplayMode( glut_display_mode )
561 # This is specific to freeglut
558 # This is specific to freeglut
562 if bool(glut.glutSetOption):
559 if bool(glut.glutSetOption):
563 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
560 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
564 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
561 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
565 glut.glutCreateWindow( sys.argv[0] )
562 glut.glutCreateWindow( sys.argv[0] )
566 glut.glutReshapeWindow( 1, 1 )
563 glut.glutReshapeWindow( 1, 1 )
567 glut.glutHideWindow( )
564 glut.glutHideWindow( )
568 glut.glutWMCloseFunc( glut_close )
565 glut.glutWMCloseFunc( glut_close )
569 glut.glutDisplayFunc( glut_display )
566 glut.glutDisplayFunc( glut_display )
570 glut.glutIdleFunc( glut_idle )
567 glut.glutIdleFunc( glut_idle )
571 else:
568 else:
572 glut.glutWMCloseFunc( glut_close )
569 glut.glutWMCloseFunc( glut_close )
573 glut.glutDisplayFunc( glut_display )
570 glut.glutDisplayFunc( glut_display )
574 glut.glutIdleFunc( glut_idle)
571 glut.glutIdleFunc( glut_idle)
575 self.manager.set_inputhook( inputhook_glut )
572 self.manager.set_inputhook( inputhook_glut )
576
573
577
574
578 def disable(self):
575 def disable(self):
579 """DEPRECATED since IPython 5.0
576 """DEPRECATED since IPython 5.0
580
577
581 Disable event loop integration with glut.
578 Disable event loop integration with glut.
582
579
583 This sets PyOS_InputHook to NULL and set the display function to a
580 This sets PyOS_InputHook to NULL and set the display function to a
584 dummy one and set the timer to a dummy timer that will be triggered
581 dummy one and set the timer to a dummy timer that will be triggered
585 very far in the future.
582 very far in the future.
586 """
583 """
587 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
584 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
588 DeprecationWarning, stacklevel=2)
585 DeprecationWarning, stacklevel=2)
589 import OpenGL.GLUT as glut
586 import OpenGL.GLUT as glut
590 from glut_support import glutMainLoopEvent
587 from glut_support import glutMainLoopEvent
591
588
592 glut.glutHideWindow() # This is an event to be processed below
589 glut.glutHideWindow() # This is an event to be processed below
593 glutMainLoopEvent()
590 glutMainLoopEvent()
594 super(GlutInputHook, self).disable()
591 super(GlutInputHook, self).disable()
595
592
596 @inputhook_manager.register('pyglet')
593 @inputhook_manager.register('pyglet')
597 class PygletInputHook(InputHookBase):
594 class PygletInputHook(InputHookBase):
598 def enable(self, app=None):
595 def enable(self, app=None):
599 """DEPRECATED since IPython 5.0
596 """DEPRECATED since IPython 5.0
600
597
601 Enable event loop integration with pyglet.
598 Enable event loop integration with pyglet.
602
599
603 Parameters
600 Parameters
604 ----------
601 ----------
605 app : ignored
602 app : ignored
606 Ignored, it's only a placeholder to keep the call signature of all
603 Ignored, it's only a placeholder to keep the call signature of all
607 gui activation methods consistent, which simplifies the logic of
604 gui activation methods consistent, which simplifies the logic of
608 supporting magics.
605 supporting magics.
609
606
610 Notes
607 Notes
611 -----
608 -----
612 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
609 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
613 pyglet to integrate with terminal based applications like
610 pyglet to integrate with terminal based applications like
614 IPython.
611 IPython.
615
612
616 """
613 """
617 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
614 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
618 DeprecationWarning, stacklevel=2)
615 DeprecationWarning, stacklevel=2)
619 from IPython.lib.inputhookpyglet import inputhook_pyglet
616 from IPython.lib.inputhookpyglet import inputhook_pyglet
620 self.manager.set_inputhook(inputhook_pyglet)
617 self.manager.set_inputhook(inputhook_pyglet)
621 return app
618 return app
622
619
623
620
624 @inputhook_manager.register('gtk3')
621 @inputhook_manager.register('gtk3')
625 class Gtk3InputHook(InputHookBase):
622 class Gtk3InputHook(InputHookBase):
626 def enable(self, app=None):
623 def enable(self, app=None):
627 """DEPRECATED since IPython 5.0
624 """DEPRECATED since IPython 5.0
628
625
629 Enable event loop integration with Gtk3 (gir bindings).
626 Enable event loop integration with Gtk3 (gir bindings).
630
627
631 Parameters
628 Parameters
632 ----------
629 ----------
633 app : ignored
630 app : ignored
634 Ignored, it's only a placeholder to keep the call signature of all
631 Ignored, it's only a placeholder to keep the call signature of all
635 gui activation methods consistent, which simplifies the logic of
632 gui activation methods consistent, which simplifies the logic of
636 supporting magics.
633 supporting magics.
637
634
638 Notes
635 Notes
639 -----
636 -----
640 This methods sets the PyOS_InputHook for Gtk3, which allows
637 This methods sets the PyOS_InputHook for Gtk3, which allows
641 the Gtk3 to integrate with terminal based applications like
638 the Gtk3 to integrate with terminal based applications like
642 IPython.
639 IPython.
643 """
640 """
644 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
641 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
645 DeprecationWarning, stacklevel=2)
642 DeprecationWarning, stacklevel=2)
646 from IPython.lib.inputhookgtk3 import inputhook_gtk3
643 from IPython.lib.inputhookgtk3 import inputhook_gtk3
647 self.manager.set_inputhook(inputhook_gtk3)
644 self.manager.set_inputhook(inputhook_gtk3)
648
645
649
646
650 clear_inputhook = inputhook_manager.clear_inputhook
647 clear_inputhook = inputhook_manager.clear_inputhook
651 set_inputhook = inputhook_manager.set_inputhook
648 set_inputhook = inputhook_manager.set_inputhook
652 current_gui = inputhook_manager.current_gui
649 current_gui = inputhook_manager.current_gui
653 clear_app_refs = inputhook_manager.clear_app_refs
650 clear_app_refs = inputhook_manager.clear_app_refs
654 enable_gui = inputhook_manager.enable_gui
651 enable_gui = inputhook_manager.enable_gui
655 disable_gui = inputhook_manager.disable_gui
652 disable_gui = inputhook_manager.disable_gui
656 register = inputhook_manager.register
653 register = inputhook_manager.register
657 guis = inputhook_manager.guihooks
654 guis = inputhook_manager.guihooks
658
655
659
656
660 def _deprecated_disable():
657 def _deprecated_disable():
661 warn("This function is deprecated since IPython 4.0 use disable_gui() instead",
658 warn("This function is deprecated since IPython 4.0 use disable_gui() instead",
662 DeprecationWarning, stacklevel=2)
659 DeprecationWarning, stacklevel=2)
663 inputhook_manager.disable_gui()
660 inputhook_manager.disable_gui()
664
661
665 disable_wx = disable_qt4 = disable_gtk = disable_gtk3 = disable_glut = \
662 disable_wx = disable_qt4 = disable_gtk = disable_gtk3 = disable_glut = \
666 disable_pyglet = disable_osx = _deprecated_disable
663 disable_pyglet = disable_osx = _deprecated_disable
General Comments 0
You need to be logged in to leave comments. Login now