##// END OF EJS Templates
Merge with upstream
Gael Varoquaux -
r1620:ed1093e0 merge
parent child Browse files
Show More
@@ -0,0 +1,53 b''
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """IPython Test Suite Runner.
4 """
5
6 import sys
7 import warnings
8
9 from nose.core import TestProgram
10 import nose.plugins.builtin
11
12 from IPython.testing.plugin.ipdoctest import IPythonDoctest
13
14 def main():
15 """Run the IPython test suite.
16 """
17
18 warnings.filterwarnings('ignore',
19 'This will be removed soon. Use IPython.testing.util instead')
20
21
22 # construct list of plugins, omitting the existing doctest plugin
23 plugins = [IPythonDoctest()]
24 for p in nose.plugins.builtin.plugins:
25 plug = p()
26 if plug.name == 'doctest':
27 continue
28
29 #print 'adding plugin:',plug.name # dbg
30 plugins.append(plug)
31
32 argv = sys.argv + ['--doctest-tests','--doctest-extension=txt',
33 '--detailed-errors',
34
35 # We add --exe because of setuptools' imbecility (it
36 # blindly does chmod +x on ALL files). Nose does the
37 # right thing and it tries to avoid executables,
38 # setuptools unfortunately forces our hand here. This
39 # has been discussed on the distutils list and the
40 # setuptools devs refuse to fix this problem!
41 '--exe',
42 ]
43
44 has_ip = False
45 for arg in sys.argv:
46 if 'IPython' in arg:
47 has_ip = True
48 break
49
50 if not has_ip:
51 argv.append('IPython')
52
53 TestProgram(argv=argv,plugins=plugins)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,8 b''
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """IPython Test Suite Runner.
4 """
5
6 from IPython.testing import iptest
7
8 iptest.main()
@@ -1,1236 +1,1238 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Shell classes.
2 """IPython Shell classes.
3
3
4 All the matplotlib support code was co-developed with John Hunter,
4 All the matplotlib support code was co-developed with John Hunter,
5 matplotlib's author.
5 matplotlib's author.
6
6
7 $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $"""
7 $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $"""
8
8
9 #*****************************************************************************
9 #*****************************************************************************
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #*****************************************************************************
14 #*****************************************************************************
15
15
16 from IPython import Release
16 from IPython import Release
17 __author__ = '%s <%s>' % Release.authors['Fernando']
17 __author__ = '%s <%s>' % Release.authors['Fernando']
18 __license__ = Release.license
18 __license__ = Release.license
19
19
20 # Code begins
20 # Code begins
21 # Stdlib imports
21 # Stdlib imports
22 import __builtin__
22 import __builtin__
23 import __main__
23 import __main__
24 import Queue
24 import Queue
25 import inspect
25 import inspect
26 import os
26 import os
27 import sys
27 import sys
28 import thread
28 import thread
29 import threading
29 import threading
30 import time
30 import time
31
31
32 from signal import signal, SIGINT
32 from signal import signal, SIGINT
33
33
34 try:
34 try:
35 import ctypes
35 import ctypes
36 HAS_CTYPES = True
36 HAS_CTYPES = True
37 except ImportError:
37 except ImportError:
38 HAS_CTYPES = False
38 HAS_CTYPES = False
39
39
40 # IPython imports
40 # IPython imports
41 import IPython
41 import IPython
42 from IPython import ultraTB, ipapi
42 from IPython import ultraTB, ipapi
43 from IPython.Magic import Magic
43 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 from IPython.iplib import InteractiveShell
45 from IPython.iplib import InteractiveShell
45 from IPython.ipmaker import make_IPython
46 from IPython.ipmaker import make_IPython
46 from IPython.Magic import Magic
47 from IPython.ipstruct import Struct
47 from IPython.ipstruct import Struct
48 from IPython.testing import decorators as testdec
48
49
49 # Globals
50 # Globals
50 # global flag to pass around information about Ctrl-C without exceptions
51 # global flag to pass around information about Ctrl-C without exceptions
51 KBINT = False
52 KBINT = False
52
53
53 # global flag to turn on/off Tk support.
54 # global flag to turn on/off Tk support.
54 USE_TK = False
55 USE_TK = False
55
56
56 # ID for the main thread, used for cross-thread exceptions
57 # ID for the main thread, used for cross-thread exceptions
57 MAIN_THREAD_ID = thread.get_ident()
58 MAIN_THREAD_ID = thread.get_ident()
58
59
59 # Tag when runcode() is active, for exception handling
60 # Tag when runcode() is active, for exception handling
60 CODE_RUN = None
61 CODE_RUN = None
61
62
62 # Default timeout for waiting for multithreaded shells (in seconds)
63 # Default timeout for waiting for multithreaded shells (in seconds)
63 GUI_TIMEOUT = 10
64 GUI_TIMEOUT = 10
64
65
65 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
66 # This class is trivial now, but I want to have it in to publish a clean
67 # This class is trivial now, but I want to have it in to publish a clean
67 # interface. Later when the internals are reorganized, code that uses this
68 # interface. Later when the internals are reorganized, code that uses this
68 # shouldn't have to change.
69 # shouldn't have to change.
69
70
70 class IPShell:
71 class IPShell:
71 """Create an IPython instance."""
72 """Create an IPython instance."""
72
73
73 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
74 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
74 debug=1,shell_class=InteractiveShell):
75 debug=1,shell_class=InteractiveShell):
75 self.IP = make_IPython(argv,user_ns=user_ns,
76 self.IP = make_IPython(argv,user_ns=user_ns,
76 user_global_ns=user_global_ns,
77 user_global_ns=user_global_ns,
77 debug=debug,shell_class=shell_class)
78 debug=debug,shell_class=shell_class)
78
79
79 def mainloop(self,sys_exit=0,banner=None):
80 def mainloop(self,sys_exit=0,banner=None):
80 self.IP.mainloop(banner)
81 self.IP.mainloop(banner)
81 if sys_exit:
82 if sys_exit:
82 sys.exit()
83 sys.exit()
83
84
84 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
85 def kill_embedded(self,parameter_s=''):
86 def kill_embedded(self,parameter_s=''):
86 """%kill_embedded : deactivate for good the current embedded IPython.
87 """%kill_embedded : deactivate for good the current embedded IPython.
87
88
88 This function (after asking for confirmation) sets an internal flag so that
89 This function (after asking for confirmation) sets an internal flag so that
89 an embedded IPython will never activate again. This is useful to
90 an embedded IPython will never activate again. This is useful to
90 permanently disable a shell that is being called inside a loop: once you've
91 permanently disable a shell that is being called inside a loop: once you've
91 figured out what you needed from it, you may then kill it and the program
92 figured out what you needed from it, you may then kill it and the program
92 will then continue to run without the interactive shell interfering again.
93 will then continue to run without the interactive shell interfering again.
93 """
94 """
94
95
95 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
96 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
96 "(y/n)? [y/N] ",'n')
97 "(y/n)? [y/N] ",'n')
97 if kill:
98 if kill:
98 self.shell.embedded_active = False
99 self.shell.embedded_active = False
99 print "This embedded IPython will not reactivate anymore once you exit."
100 print "This embedded IPython will not reactivate anymore once you exit."
100
101
101 class IPShellEmbed:
102 class IPShellEmbed:
102 """Allow embedding an IPython shell into a running program.
103 """Allow embedding an IPython shell into a running program.
103
104
104 Instances of this class are callable, with the __call__ method being an
105 Instances of this class are callable, with the __call__ method being an
105 alias to the embed() method of an InteractiveShell instance.
106 alias to the embed() method of an InteractiveShell instance.
106
107
107 Usage (see also the example-embed.py file for a running example):
108 Usage (see also the example-embed.py file for a running example):
108
109
109 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
110 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
110
111
111 - argv: list containing valid command-line options for IPython, as they
112 - argv: list containing valid command-line options for IPython, as they
112 would appear in sys.argv[1:].
113 would appear in sys.argv[1:].
113
114
114 For example, the following command-line options:
115 For example, the following command-line options:
115
116
116 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
117 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
117
118
118 would be passed in the argv list as:
119 would be passed in the argv list as:
119
120
120 ['-prompt_in1','Input <\\#>','-colors','LightBG']
121 ['-prompt_in1','Input <\\#>','-colors','LightBG']
121
122
122 - banner: string which gets printed every time the interpreter starts.
123 - banner: string which gets printed every time the interpreter starts.
123
124
124 - exit_msg: string which gets printed every time the interpreter exits.
125 - exit_msg: string which gets printed every time the interpreter exits.
125
126
126 - rc_override: a dict or Struct of configuration options such as those
127 - rc_override: a dict or Struct of configuration options such as those
127 used by IPython. These options are read from your ~/.ipython/ipythonrc
128 used by IPython. These options are read from your ~/.ipython/ipythonrc
128 file when the Shell object is created. Passing an explicit rc_override
129 file when the Shell object is created. Passing an explicit rc_override
129 dict with any options you want allows you to override those values at
130 dict with any options you want allows you to override those values at
130 creation time without having to modify the file. This way you can create
131 creation time without having to modify the file. This way you can create
131 embeddable instances configured in any way you want without editing any
132 embeddable instances configured in any way you want without editing any
132 global files (thus keeping your interactive IPython configuration
133 global files (thus keeping your interactive IPython configuration
133 unchanged).
134 unchanged).
134
135
135 Then the ipshell instance can be called anywhere inside your code:
136 Then the ipshell instance can be called anywhere inside your code:
136
137
137 ipshell(header='') -> Opens up an IPython shell.
138 ipshell(header='') -> Opens up an IPython shell.
138
139
139 - header: string printed by the IPython shell upon startup. This can let
140 - header: string printed by the IPython shell upon startup. This can let
140 you know where in your code you are when dropping into the shell. Note
141 you know where in your code you are when dropping into the shell. Note
141 that 'banner' gets prepended to all calls, so header is used for
142 that 'banner' gets prepended to all calls, so header is used for
142 location-specific information.
143 location-specific information.
143
144
144 For more details, see the __call__ method below.
145 For more details, see the __call__ method below.
145
146
146 When the IPython shell is exited with Ctrl-D, normal program execution
147 When the IPython shell is exited with Ctrl-D, normal program execution
147 resumes.
148 resumes.
148
149
149 This functionality was inspired by a posting on comp.lang.python by cmkl
150 This functionality was inspired by a posting on comp.lang.python by cmkl
150 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
151 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
151 by the IDL stop/continue commands."""
152 by the IDL stop/continue commands."""
152
153
153 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
154 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
154 user_ns=None):
155 user_ns=None):
155 """Note that argv here is a string, NOT a list."""
156 """Note that argv here is a string, NOT a list."""
156 self.set_banner(banner)
157 self.set_banner(banner)
157 self.set_exit_msg(exit_msg)
158 self.set_exit_msg(exit_msg)
158 self.set_dummy_mode(0)
159 self.set_dummy_mode(0)
159
160
160 # sys.displayhook is a global, we need to save the user's original
161 # sys.displayhook is a global, we need to save the user's original
161 # Don't rely on __displayhook__, as the user may have changed that.
162 # Don't rely on __displayhook__, as the user may have changed that.
162 self.sys_displayhook_ori = sys.displayhook
163 self.sys_displayhook_ori = sys.displayhook
163
164
164 # save readline completer status
165 # save readline completer status
165 try:
166 try:
166 #print 'Save completer',sys.ipcompleter # dbg
167 #print 'Save completer',sys.ipcompleter # dbg
167 self.sys_ipcompleter_ori = sys.ipcompleter
168 self.sys_ipcompleter_ori = sys.ipcompleter
168 except:
169 except:
169 pass # not nested with IPython
170 pass # not nested with IPython
170
171
171 self.IP = make_IPython(argv,rc_override=rc_override,
172 self.IP = make_IPython(argv,rc_override=rc_override,
172 embedded=True,
173 embedded=True,
173 user_ns=user_ns)
174 user_ns=user_ns)
174
175
175 ip = ipapi.IPApi(self.IP)
176 ip = ipapi.IPApi(self.IP)
176 ip.expose_magic("kill_embedded",kill_embedded)
177 ip.expose_magic("kill_embedded",kill_embedded)
177
178
178 # copy our own displayhook also
179 # copy our own displayhook also
179 self.sys_displayhook_embed = sys.displayhook
180 self.sys_displayhook_embed = sys.displayhook
180 # and leave the system's display hook clean
181 # and leave the system's display hook clean
181 sys.displayhook = self.sys_displayhook_ori
182 sys.displayhook = self.sys_displayhook_ori
182 # don't use the ipython crash handler so that user exceptions aren't
183 # don't use the ipython crash handler so that user exceptions aren't
183 # trapped
184 # trapped
184 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
185 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
185 mode = self.IP.rc.xmode,
186 mode = self.IP.rc.xmode,
186 call_pdb = self.IP.rc.pdb)
187 call_pdb = self.IP.rc.pdb)
187 self.restore_system_completer()
188 self.restore_system_completer()
188
189
189 def restore_system_completer(self):
190 def restore_system_completer(self):
190 """Restores the readline completer which was in place.
191 """Restores the readline completer which was in place.
191
192
192 This allows embedded IPython within IPython not to disrupt the
193 This allows embedded IPython within IPython not to disrupt the
193 parent's completion.
194 parent's completion.
194 """
195 """
195
196
196 try:
197 try:
197 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
198 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
198 sys.ipcompleter = self.sys_ipcompleter_ori
199 sys.ipcompleter = self.sys_ipcompleter_ori
199 except:
200 except:
200 pass
201 pass
201
202
202 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
203 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
203 """Activate the interactive interpreter.
204 """Activate the interactive interpreter.
204
205
205 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
206 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
206 the interpreter shell with the given local and global namespaces, and
207 the interpreter shell with the given local and global namespaces, and
207 optionally print a header string at startup.
208 optionally print a header string at startup.
208
209
209 The shell can be globally activated/deactivated using the
210 The shell can be globally activated/deactivated using the
210 set/get_dummy_mode methods. This allows you to turn off a shell used
211 set/get_dummy_mode methods. This allows you to turn off a shell used
211 for debugging globally.
212 for debugging globally.
212
213
213 However, *each* time you call the shell you can override the current
214 However, *each* time you call the shell you can override the current
214 state of dummy_mode with the optional keyword parameter 'dummy'. For
215 state of dummy_mode with the optional keyword parameter 'dummy'. For
215 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
216 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
216 can still have a specific call work by making it as IPShell(dummy=0).
217 can still have a specific call work by making it as IPShell(dummy=0).
217
218
218 The optional keyword parameter dummy controls whether the call
219 The optional keyword parameter dummy controls whether the call
219 actually does anything. """
220 actually does anything. """
220
221
221 # If the user has turned it off, go away
222 # If the user has turned it off, go away
222 if not self.IP.embedded_active:
223 if not self.IP.embedded_active:
223 return
224 return
224
225
225 # Normal exits from interactive mode set this flag, so the shell can't
226 # Normal exits from interactive mode set this flag, so the shell can't
226 # re-enter (it checks this variable at the start of interactive mode).
227 # re-enter (it checks this variable at the start of interactive mode).
227 self.IP.exit_now = False
228 self.IP.exit_now = False
228
229
229 # Allow the dummy parameter to override the global __dummy_mode
230 # Allow the dummy parameter to override the global __dummy_mode
230 if dummy or (dummy != 0 and self.__dummy_mode):
231 if dummy or (dummy != 0 and self.__dummy_mode):
231 return
232 return
232
233
233 # Set global subsystems (display,completions) to our values
234 # Set global subsystems (display,completions) to our values
234 sys.displayhook = self.sys_displayhook_embed
235 sys.displayhook = self.sys_displayhook_embed
235 if self.IP.has_readline:
236 if self.IP.has_readline:
236 self.IP.set_completer()
237 self.IP.set_completer()
237
238
238 if self.banner and header:
239 if self.banner and header:
239 format = '%s\n%s\n'
240 format = '%s\n%s\n'
240 else:
241 else:
241 format = '%s%s\n'
242 format = '%s%s\n'
242 banner = format % (self.banner,header)
243 banner = format % (self.banner,header)
243
244
244 # Call the embedding code with a stack depth of 1 so it can skip over
245 # Call the embedding code with a stack depth of 1 so it can skip over
245 # our call and get the original caller's namespaces.
246 # our call and get the original caller's namespaces.
246 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
247 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
247
248
248 if self.exit_msg:
249 if self.exit_msg:
249 print self.exit_msg
250 print self.exit_msg
250
251
251 # Restore global systems (display, completion)
252 # Restore global systems (display, completion)
252 sys.displayhook = self.sys_displayhook_ori
253 sys.displayhook = self.sys_displayhook_ori
253 self.restore_system_completer()
254 self.restore_system_completer()
254
255
255 def set_dummy_mode(self,dummy):
256 def set_dummy_mode(self,dummy):
256 """Sets the embeddable shell's dummy mode parameter.
257 """Sets the embeddable shell's dummy mode parameter.
257
258
258 set_dummy_mode(dummy): dummy = 0 or 1.
259 set_dummy_mode(dummy): dummy = 0 or 1.
259
260
260 This parameter is persistent and makes calls to the embeddable shell
261 This parameter is persistent and makes calls to the embeddable shell
261 silently return without performing any action. This allows you to
262 silently return without performing any action. This allows you to
262 globally activate or deactivate a shell you're using with a single call.
263 globally activate or deactivate a shell you're using with a single call.
263
264
264 If you need to manually"""
265 If you need to manually"""
265
266
266 if dummy not in [0,1,False,True]:
267 if dummy not in [0,1,False,True]:
267 raise ValueError,'dummy parameter must be boolean'
268 raise ValueError,'dummy parameter must be boolean'
268 self.__dummy_mode = dummy
269 self.__dummy_mode = dummy
269
270
270 def get_dummy_mode(self):
271 def get_dummy_mode(self):
271 """Return the current value of the dummy mode parameter.
272 """Return the current value of the dummy mode parameter.
272 """
273 """
273 return self.__dummy_mode
274 return self.__dummy_mode
274
275
275 def set_banner(self,banner):
276 def set_banner(self,banner):
276 """Sets the global banner.
277 """Sets the global banner.
277
278
278 This banner gets prepended to every header printed when the shell
279 This banner gets prepended to every header printed when the shell
279 instance is called."""
280 instance is called."""
280
281
281 self.banner = banner
282 self.banner = banner
282
283
283 def set_exit_msg(self,exit_msg):
284 def set_exit_msg(self,exit_msg):
284 """Sets the global exit_msg.
285 """Sets the global exit_msg.
285
286
286 This exit message gets printed upon exiting every time the embedded
287 This exit message gets printed upon exiting every time the embedded
287 shell is called. It is None by default. """
288 shell is called. It is None by default. """
288
289
289 self.exit_msg = exit_msg
290 self.exit_msg = exit_msg
290
291
291 #-----------------------------------------------------------------------------
292 #-----------------------------------------------------------------------------
292 if HAS_CTYPES:
293 if HAS_CTYPES:
293 # Add async exception support. Trick taken from:
294 # Add async exception support. Trick taken from:
294 # http://sebulba.wikispaces.com/recipe+thread2
295 # http://sebulba.wikispaces.com/recipe+thread2
295 def _async_raise(tid, exctype):
296 def _async_raise(tid, exctype):
296 """raises the exception, performs cleanup if needed"""
297 """raises the exception, performs cleanup if needed"""
297 if not inspect.isclass(exctype):
298 if not inspect.isclass(exctype):
298 raise TypeError("Only types can be raised (not instances)")
299 raise TypeError("Only types can be raised (not instances)")
299 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
300 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
300 ctypes.py_object(exctype))
301 ctypes.py_object(exctype))
301 if res == 0:
302 if res == 0:
302 raise ValueError("invalid thread id")
303 raise ValueError("invalid thread id")
303 elif res != 1:
304 elif res != 1:
304 # """if it returns a number greater than one, you're in trouble,
305 # """if it returns a number greater than one, you're in trouble,
305 # and you should call it again with exc=NULL to revert the effect"""
306 # and you should call it again with exc=NULL to revert the effect"""
306 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
307 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
307 raise SystemError("PyThreadState_SetAsyncExc failed")
308 raise SystemError("PyThreadState_SetAsyncExc failed")
308
309
309 def sigint_handler (signum,stack_frame):
310 def sigint_handler (signum,stack_frame):
310 """Sigint handler for threaded apps.
311 """Sigint handler for threaded apps.
311
312
312 This is a horrible hack to pass information about SIGINT _without_
313 This is a horrible hack to pass information about SIGINT _without_
313 using exceptions, since I haven't been able to properly manage
314 using exceptions, since I haven't been able to properly manage
314 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
315 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
315 done (or at least that's my understanding from a c.l.py thread where
316 done (or at least that's my understanding from a c.l.py thread where
316 this was discussed)."""
317 this was discussed)."""
317
318
318 global KBINT
319 global KBINT
319
320
320 if CODE_RUN:
321 if CODE_RUN:
321 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
322 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
322 else:
323 else:
323 KBINT = True
324 KBINT = True
324 print '\nKeyboardInterrupt - Press <Enter> to continue.',
325 print '\nKeyboardInterrupt - Press <Enter> to continue.',
325 Term.cout.flush()
326 Term.cout.flush()
326
327
327 else:
328 else:
328 def sigint_handler (signum,stack_frame):
329 def sigint_handler (signum,stack_frame):
329 """Sigint handler for threaded apps.
330 """Sigint handler for threaded apps.
330
331
331 This is a horrible hack to pass information about SIGINT _without_
332 This is a horrible hack to pass information about SIGINT _without_
332 using exceptions, since I haven't been able to properly manage
333 using exceptions, since I haven't been able to properly manage
333 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
334 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
334 done (or at least that's my understanding from a c.l.py thread where
335 done (or at least that's my understanding from a c.l.py thread where
335 this was discussed)."""
336 this was discussed)."""
336
337
337 global KBINT
338 global KBINT
338
339
339 print '\nKeyboardInterrupt - Press <Enter> to continue.',
340 print '\nKeyboardInterrupt - Press <Enter> to continue.',
340 Term.cout.flush()
341 Term.cout.flush()
341 # Set global flag so that runsource can know that Ctrl-C was hit
342 # Set global flag so that runsource can know that Ctrl-C was hit
342 KBINT = True
343 KBINT = True
343
344
344
345
345 class MTInteractiveShell(InteractiveShell):
346 class MTInteractiveShell(InteractiveShell):
346 """Simple multi-threaded shell."""
347 """Simple multi-threaded shell."""
347
348
348 # Threading strategy taken from:
349 # Threading strategy taken from:
349 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
350 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
350 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
351 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
351 # from the pygtk mailing list, to avoid lockups with system calls.
352 # from the pygtk mailing list, to avoid lockups with system calls.
352
353
353 # class attribute to indicate whether the class supports threads or not.
354 # class attribute to indicate whether the class supports threads or not.
354 # Subclasses with thread support should override this as needed.
355 # Subclasses with thread support should override this as needed.
355 isthreaded = True
356 isthreaded = True
356
357
357 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
358 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
358 user_ns=None,user_global_ns=None,banner2='',
359 user_ns=None,user_global_ns=None,banner2='',
359 gui_timeout=GUI_TIMEOUT,**kw):
360 gui_timeout=GUI_TIMEOUT,**kw):
360 """Similar to the normal InteractiveShell, but with threading control"""
361 """Similar to the normal InteractiveShell, but with threading control"""
361
362
362 InteractiveShell.__init__(self,name,usage,rc,user_ns,
363 InteractiveShell.__init__(self,name,usage,rc,user_ns,
363 user_global_ns,banner2)
364 user_global_ns,banner2)
364
365
365 # Timeout we wait for GUI thread
366 # Timeout we wait for GUI thread
366 self.gui_timeout = gui_timeout
367 self.gui_timeout = gui_timeout
367
368
368 # A queue to hold the code to be executed.
369 # A queue to hold the code to be executed.
369 self.code_queue = Queue.Queue()
370 self.code_queue = Queue.Queue()
370
371
371 # Stuff to do at closing time
372 # Stuff to do at closing time
372 self._kill = None
373 self._kill = None
373 on_kill = kw.get('on_kill', [])
374 on_kill = kw.get('on_kill', [])
374 # Check that all things to kill are callable:
375 # Check that all things to kill are callable:
375 for t in on_kill:
376 for t in on_kill:
376 if not callable(t):
377 if not callable(t):
377 raise TypeError,'on_kill must be a list of callables'
378 raise TypeError,'on_kill must be a list of callables'
378 self.on_kill = on_kill
379 self.on_kill = on_kill
379 # thread identity of the "worker thread" (that may execute code directly)
380 # thread identity of the "worker thread" (that may execute code directly)
380 self.worker_ident = None
381 self.worker_ident = None
381
382
382 def runsource(self, source, filename="<input>", symbol="single"):
383 def runsource(self, source, filename="<input>", symbol="single"):
383 """Compile and run some source in the interpreter.
384 """Compile and run some source in the interpreter.
384
385
385 Modified version of code.py's runsource(), to handle threading issues.
386 Modified version of code.py's runsource(), to handle threading issues.
386 See the original for full docstring details."""
387 See the original for full docstring details."""
387
388
388 global KBINT
389 global KBINT
389
390
390 # If Ctrl-C was typed, we reset the flag and return right away
391 # If Ctrl-C was typed, we reset the flag and return right away
391 if KBINT:
392 if KBINT:
392 KBINT = False
393 KBINT = False
393 return False
394 return False
394
395
395 if self._kill:
396 if self._kill:
396 # can't queue new code if we are being killed
397 # can't queue new code if we are being killed
397 return True
398 return True
398
399
399 try:
400 try:
400 code = self.compile(source, filename, symbol)
401 code = self.compile(source, filename, symbol)
401 except (OverflowError, SyntaxError, ValueError):
402 except (OverflowError, SyntaxError, ValueError):
402 # Case 1
403 # Case 1
403 self.showsyntaxerror(filename)
404 self.showsyntaxerror(filename)
404 return False
405 return False
405
406
406 if code is None:
407 if code is None:
407 # Case 2
408 # Case 2
408 return True
409 return True
409
410
410 # shortcut - if we are in worker thread, or the worker thread is not
411 # shortcut - if we are in worker thread, or the worker thread is not
411 # running, execute directly (to allow recursion and prevent deadlock if
412 # running, execute directly (to allow recursion and prevent deadlock if
412 # code is run early in IPython construction)
413 # code is run early in IPython construction)
413
414
414 if (self.worker_ident is None
415 if (self.worker_ident is None
415 or self.worker_ident == thread.get_ident() ):
416 or self.worker_ident == thread.get_ident() ):
416 InteractiveShell.runcode(self,code)
417 InteractiveShell.runcode(self,code)
417 return
418 return
418
419
419 # Case 3
420 # Case 3
420 # Store code in queue, so the execution thread can handle it.
421 # Store code in queue, so the execution thread can handle it.
421
422
422 completed_ev, received_ev = threading.Event(), threading.Event()
423 completed_ev, received_ev = threading.Event(), threading.Event()
423
424
424 self.code_queue.put((code,completed_ev, received_ev))
425 self.code_queue.put((code,completed_ev, received_ev))
425 # first make sure the message was received, with timeout
426 # first make sure the message was received, with timeout
426 received_ev.wait(self.gui_timeout)
427 received_ev.wait(self.gui_timeout)
427 if not received_ev.isSet():
428 if not received_ev.isSet():
428 # the mainloop is dead, start executing code directly
429 # the mainloop is dead, start executing code directly
429 print "Warning: Timeout for mainloop thread exceeded"
430 print "Warning: Timeout for mainloop thread exceeded"
430 print "switching to nonthreaded mode (until mainloop wakes up again)"
431 print "switching to nonthreaded mode (until mainloop wakes up again)"
431 self.worker_ident = None
432 self.worker_ident = None
432 else:
433 else:
433 completed_ev.wait()
434 completed_ev.wait()
434 return False
435 return False
435
436
436 def runcode(self):
437 def runcode(self):
437 """Execute a code object.
438 """Execute a code object.
438
439
439 Multithreaded wrapper around IPython's runcode()."""
440 Multithreaded wrapper around IPython's runcode()."""
440
441
441 global CODE_RUN
442 global CODE_RUN
442
443
443 # we are in worker thread, stash out the id for runsource()
444 # we are in worker thread, stash out the id for runsource()
444 self.worker_ident = thread.get_ident()
445 self.worker_ident = thread.get_ident()
445
446
446 if self._kill:
447 if self._kill:
447 print >>Term.cout, 'Closing threads...',
448 print >>Term.cout, 'Closing threads...',
448 Term.cout.flush()
449 Term.cout.flush()
449 for tokill in self.on_kill:
450 for tokill in self.on_kill:
450 tokill()
451 tokill()
451 print >>Term.cout, 'Done.'
452 print >>Term.cout, 'Done.'
452 # allow kill() to return
453 # allow kill() to return
453 self._kill.set()
454 self._kill.set()
454 return True
455 return True
455
456
456 # Install sigint handler. We do it every time to ensure that if user
457 # Install sigint handler. We do it every time to ensure that if user
457 # code modifies it, we restore our own handling.
458 # code modifies it, we restore our own handling.
458 try:
459 try:
459 signal(SIGINT,sigint_handler)
460 signal(SIGINT,sigint_handler)
460 except SystemError:
461 except SystemError:
461 # This happens under Windows, which seems to have all sorts
462 # This happens under Windows, which seems to have all sorts
462 # of problems with signal handling. Oh well...
463 # of problems with signal handling. Oh well...
463 pass
464 pass
464
465
465 # Flush queue of pending code by calling the run methood of the parent
466 # Flush queue of pending code by calling the run methood of the parent
466 # class with all items which may be in the queue.
467 # class with all items which may be in the queue.
467 code_to_run = None
468 code_to_run = None
468 while 1:
469 while 1:
469 try:
470 try:
470 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
471 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
471 except Queue.Empty:
472 except Queue.Empty:
472 break
473 break
473 received_ev.set()
474 received_ev.set()
474
475
475 # Exceptions need to be raised differently depending on which
476 # Exceptions need to be raised differently depending on which
476 # thread is active. This convoluted try/except is only there to
477 # thread is active. This convoluted try/except is only there to
477 # protect against asynchronous exceptions, to ensure that a KBINT
478 # protect against asynchronous exceptions, to ensure that a KBINT
478 # at the wrong time doesn't deadlock everything. The global
479 # at the wrong time doesn't deadlock everything. The global
479 # CODE_TO_RUN is set to true/false as close as possible to the
480 # CODE_TO_RUN is set to true/false as close as possible to the
480 # runcode() call, so that the KBINT handler is correctly informed.
481 # runcode() call, so that the KBINT handler is correctly informed.
481 try:
482 try:
482 try:
483 try:
483 CODE_RUN = True
484 CODE_RUN = True
484 InteractiveShell.runcode(self,code_to_run)
485 InteractiveShell.runcode(self,code_to_run)
485 except KeyboardInterrupt:
486 except KeyboardInterrupt:
486 print "Keyboard interrupted in mainloop"
487 print "Keyboard interrupted in mainloop"
487 while not self.code_queue.empty():
488 while not self.code_queue.empty():
488 code, ev1,ev2 = self.code_queue.get_nowait()
489 code, ev1,ev2 = self.code_queue.get_nowait()
489 ev1.set()
490 ev1.set()
490 ev2.set()
491 ev2.set()
491 break
492 break
492 finally:
493 finally:
493 CODE_RUN = False
494 CODE_RUN = False
494 # allow runsource() return from wait
495 # allow runsource() return from wait
495 completed_ev.set()
496 completed_ev.set()
496
497
497
498
498 # This MUST return true for gtk threading to work
499 # This MUST return true for gtk threading to work
499 return True
500 return True
500
501
501 def kill(self):
502 def kill(self):
502 """Kill the thread, returning when it has been shut down."""
503 """Kill the thread, returning when it has been shut down."""
503 self._kill = threading.Event()
504 self._kill = threading.Event()
504 self._kill.wait()
505 self._kill.wait()
505
506
506 class MatplotlibShellBase:
507 class MatplotlibShellBase:
507 """Mixin class to provide the necessary modifications to regular IPython
508 """Mixin class to provide the necessary modifications to regular IPython
508 shell classes for matplotlib support.
509 shell classes for matplotlib support.
509
510
510 Given Python's MRO, this should be used as the FIRST class in the
511 Given Python's MRO, this should be used as the FIRST class in the
511 inheritance hierarchy, so that it overrides the relevant methods."""
512 inheritance hierarchy, so that it overrides the relevant methods."""
512
513
513 def _matplotlib_config(self,name,user_ns,user_global_ns=None):
514 def _matplotlib_config(self,name,user_ns,user_global_ns=None):
514 """Return items needed to setup the user's shell with matplotlib"""
515 """Return items needed to setup the user's shell with matplotlib"""
515
516
516 # Initialize matplotlib to interactive mode always
517 # Initialize matplotlib to interactive mode always
517 import matplotlib
518 import matplotlib
518 from matplotlib import backends
519 from matplotlib import backends
519 matplotlib.interactive(True)
520 matplotlib.interactive(True)
520
521
521 def use(arg):
522 def use(arg):
522 """IPython wrapper for matplotlib's backend switcher.
523 """IPython wrapper for matplotlib's backend switcher.
523
524
524 In interactive use, we can not allow switching to a different
525 In interactive use, we can not allow switching to a different
525 interactive backend, since thread conflicts will most likely crash
526 interactive backend, since thread conflicts will most likely crash
526 the python interpreter. This routine does a safety check first,
527 the python interpreter. This routine does a safety check first,
527 and refuses to perform a dangerous switch. It still allows
528 and refuses to perform a dangerous switch. It still allows
528 switching to non-interactive backends."""
529 switching to non-interactive backends."""
529
530
530 if arg in backends.interactive_bk and arg != self.mpl_backend:
531 if arg in backends.interactive_bk and arg != self.mpl_backend:
531 m=('invalid matplotlib backend switch.\n'
532 m=('invalid matplotlib backend switch.\n'
532 'This script attempted to switch to the interactive '
533 'This script attempted to switch to the interactive '
533 'backend: `%s`\n'
534 'backend: `%s`\n'
534 'Your current choice of interactive backend is: `%s`\n\n'
535 'Your current choice of interactive backend is: `%s`\n\n'
535 'Switching interactive matplotlib backends at runtime\n'
536 'Switching interactive matplotlib backends at runtime\n'
536 'would crash the python interpreter, '
537 'would crash the python interpreter, '
537 'and IPython has blocked it.\n\n'
538 'and IPython has blocked it.\n\n'
538 'You need to either change your choice of matplotlib backend\n'
539 'You need to either change your choice of matplotlib backend\n'
539 'by editing your .matplotlibrc file, or run this script as a \n'
540 'by editing your .matplotlibrc file, or run this script as a \n'
540 'standalone file from the command line, not using IPython.\n' %
541 'standalone file from the command line, not using IPython.\n' %
541 (arg,self.mpl_backend) )
542 (arg,self.mpl_backend) )
542 raise RuntimeError, m
543 raise RuntimeError, m
543 else:
544 else:
544 self.mpl_use(arg)
545 self.mpl_use(arg)
545 self.mpl_use._called = True
546 self.mpl_use._called = True
546
547
547 self.matplotlib = matplotlib
548 self.matplotlib = matplotlib
548 self.mpl_backend = matplotlib.rcParams['backend']
549 self.mpl_backend = matplotlib.rcParams['backend']
549
550
550 # we also need to block switching of interactive backends by use()
551 # we also need to block switching of interactive backends by use()
551 self.mpl_use = matplotlib.use
552 self.mpl_use = matplotlib.use
552 self.mpl_use._called = False
553 self.mpl_use._called = False
553 # overwrite the original matplotlib.use with our wrapper
554 # overwrite the original matplotlib.use with our wrapper
554 matplotlib.use = use
555 matplotlib.use = use
555
556
556 # This must be imported last in the matplotlib series, after
557 # This must be imported last in the matplotlib series, after
557 # backend/interactivity choices have been made
558 # backend/interactivity choices have been made
558 import matplotlib.pylab as pylab
559 import matplotlib.pylab as pylab
559 self.pylab = pylab
560 self.pylab = pylab
560
561
561 self.pylab.show._needmain = False
562 self.pylab.show._needmain = False
562 # We need to detect at runtime whether show() is called by the user.
563 # We need to detect at runtime whether show() is called by the user.
563 # For this, we wrap it into a decorator which adds a 'called' flag.
564 # For this, we wrap it into a decorator which adds a 'called' flag.
564 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
565 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
565
566
566 # Build a user namespace initialized with matplotlib/matlab features.
567 # Build a user namespace initialized with matplotlib/matlab features.
567 user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns,
568 user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns,
568 user_global_ns)
569 user_global_ns)
569
570
570 # Import numpy as np/pyplot as plt are conventions we're trying to
571 # Import numpy as np/pyplot as plt are conventions we're trying to
571 # somewhat standardize on. Making them available to users by default
572 # somewhat standardize on. Making them available to users by default
572 # will greatly help this.
573 # will greatly help this.
573 exec ("import numpy\n"
574 exec ("import numpy\n"
574 "import numpy as np\n"
575 "import numpy as np\n"
575 "import matplotlib\n"
576 "import matplotlib\n"
576 "import matplotlib.pylab as pylab\n"
577 "import matplotlib.pylab as pylab\n"
577 "try:\n"
578 "try:\n"
578 " import matplotlib.pyplot as plt\n"
579 " import matplotlib.pyplot as plt\n"
579 "except ImportError:\n"
580 "except ImportError:\n"
580 " pass\n"
581 " pass\n"
581 ) in user_ns
582 ) in user_ns
582
583
583 # Build matplotlib info banner
584 # Build matplotlib info banner
584 b="""
585 b="""
585 Welcome to pylab, a matplotlib-based Python environment.
586 Welcome to pylab, a matplotlib-based Python environment.
586 For more information, type 'help(pylab)'.
587 For more information, type 'help(pylab)'.
587 """
588 """
588 return user_ns,user_global_ns,b
589 return user_ns,user_global_ns,b
589
590
590 def mplot_exec(self,fname,*where,**kw):
591 def mplot_exec(self,fname,*where,**kw):
591 """Execute a matplotlib script.
592 """Execute a matplotlib script.
592
593
593 This is a call to execfile(), but wrapped in safeties to properly
594 This is a call to execfile(), but wrapped in safeties to properly
594 handle interactive rendering and backend switching."""
595 handle interactive rendering and backend switching."""
595
596
596 #print '*** Matplotlib runner ***' # dbg
597 #print '*** Matplotlib runner ***' # dbg
597 # turn off rendering until end of script
598 # turn off rendering until end of script
598 isInteractive = self.matplotlib.rcParams['interactive']
599 isInteractive = self.matplotlib.rcParams['interactive']
599 self.matplotlib.interactive(False)
600 self.matplotlib.interactive(False)
600 self.safe_execfile(fname,*where,**kw)
601 self.safe_execfile(fname,*where,**kw)
601 self.matplotlib.interactive(isInteractive)
602 self.matplotlib.interactive(isInteractive)
602 # make rendering call now, if the user tried to do it
603 # make rendering call now, if the user tried to do it
603 if self.pylab.draw_if_interactive.called:
604 if self.pylab.draw_if_interactive.called:
604 self.pylab.draw()
605 self.pylab.draw()
605 self.pylab.draw_if_interactive.called = False
606 self.pylab.draw_if_interactive.called = False
606
607
607 # if a backend switch was performed, reverse it now
608 # if a backend switch was performed, reverse it now
608 if self.mpl_use._called:
609 if self.mpl_use._called:
609 self.matplotlib.rcParams['backend'] = self.mpl_backend
610 self.matplotlib.rcParams['backend'] = self.mpl_backend
610
611
612 @testdec.skip_doctest
611 def magic_run(self,parameter_s=''):
613 def magic_run(self,parameter_s=''):
612 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
614 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
613
615
614 # Fix the docstring so users see the original as well
616 # Fix the docstring so users see the original as well
615 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
617 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
616 "\n *** Modified %run for Matplotlib,"
618 "\n *** Modified %run for Matplotlib,"
617 " with proper interactive handling ***")
619 " with proper interactive handling ***")
618
620
619 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
621 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
620 # and multithreaded. Note that these are meant for internal use, the IPShell*
622 # and multithreaded. Note that these are meant for internal use, the IPShell*
621 # classes below are the ones meant for public consumption.
623 # classes below are the ones meant for public consumption.
622
624
623 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
625 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
624 """Single-threaded shell with matplotlib support."""
626 """Single-threaded shell with matplotlib support."""
625
627
626 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
628 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
627 user_ns=None,user_global_ns=None,**kw):
629 user_ns=None,user_global_ns=None,**kw):
628 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
630 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
629 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
631 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
630 banner2=b2,**kw)
632 banner2=b2,**kw)
631
633
632 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
634 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
633 """Multi-threaded shell with matplotlib support."""
635 """Multi-threaded shell with matplotlib support."""
634
636
635 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
637 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
636 user_ns=None,user_global_ns=None, **kw):
638 user_ns=None,user_global_ns=None, **kw):
637 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
639 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
638 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
640 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
639 banner2=b2,**kw)
641 banner2=b2,**kw)
640
642
641 #-----------------------------------------------------------------------------
643 #-----------------------------------------------------------------------------
642 # Utility functions for the different GUI enabled IPShell* classes.
644 # Utility functions for the different GUI enabled IPShell* classes.
643
645
644 def get_tk():
646 def get_tk():
645 """Tries to import Tkinter and returns a withdrawn Tkinter root
647 """Tries to import Tkinter and returns a withdrawn Tkinter root
646 window. If Tkinter is already imported or not available, this
648 window. If Tkinter is already imported or not available, this
647 returns None. This function calls `hijack_tk` underneath.
649 returns None. This function calls `hijack_tk` underneath.
648 """
650 """
649 if not USE_TK or sys.modules.has_key('Tkinter'):
651 if not USE_TK or sys.modules.has_key('Tkinter'):
650 return None
652 return None
651 else:
653 else:
652 try:
654 try:
653 import Tkinter
655 import Tkinter
654 except ImportError:
656 except ImportError:
655 return None
657 return None
656 else:
658 else:
657 hijack_tk()
659 hijack_tk()
658 r = Tkinter.Tk()
660 r = Tkinter.Tk()
659 r.withdraw()
661 r.withdraw()
660 return r
662 return r
661
663
662 def hijack_tk():
664 def hijack_tk():
663 """Modifies Tkinter's mainloop with a dummy so when a module calls
665 """Modifies Tkinter's mainloop with a dummy so when a module calls
664 mainloop, it does not block.
666 mainloop, it does not block.
665
667
666 """
668 """
667 def misc_mainloop(self, n=0):
669 def misc_mainloop(self, n=0):
668 pass
670 pass
669 def tkinter_mainloop(n=0):
671 def tkinter_mainloop(n=0):
670 pass
672 pass
671
673
672 import Tkinter
674 import Tkinter
673 Tkinter.Misc.mainloop = misc_mainloop
675 Tkinter.Misc.mainloop = misc_mainloop
674 Tkinter.mainloop = tkinter_mainloop
676 Tkinter.mainloop = tkinter_mainloop
675
677
676 def update_tk(tk):
678 def update_tk(tk):
677 """Updates the Tkinter event loop. This is typically called from
679 """Updates the Tkinter event loop. This is typically called from
678 the respective WX or GTK mainloops.
680 the respective WX or GTK mainloops.
679 """
681 """
680 if tk:
682 if tk:
681 tk.update()
683 tk.update()
682
684
683 def hijack_wx():
685 def hijack_wx():
684 """Modifies wxPython's MainLoop with a dummy so user code does not
686 """Modifies wxPython's MainLoop with a dummy so user code does not
685 block IPython. The hijacked mainloop function is returned.
687 block IPython. The hijacked mainloop function is returned.
686 """
688 """
687 def dummy_mainloop(*args, **kw):
689 def dummy_mainloop(*args, **kw):
688 pass
690 pass
689
691
690 try:
692 try:
691 import wx
693 import wx
692 except ImportError:
694 except ImportError:
693 # For very old versions of WX
695 # For very old versions of WX
694 import wxPython as wx
696 import wxPython as wx
695
697
696 ver = wx.__version__
698 ver = wx.__version__
697 orig_mainloop = None
699 orig_mainloop = None
698 if ver[:3] >= '2.5':
700 if ver[:3] >= '2.5':
699 import wx
701 import wx
700 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
702 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
701 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
703 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
702 else: raise AttributeError('Could not find wx core module')
704 else: raise AttributeError('Could not find wx core module')
703 orig_mainloop = core.PyApp_MainLoop
705 orig_mainloop = core.PyApp_MainLoop
704 core.PyApp_MainLoop = dummy_mainloop
706 core.PyApp_MainLoop = dummy_mainloop
705 elif ver[:3] == '2.4':
707 elif ver[:3] == '2.4':
706 orig_mainloop = wx.wxc.wxPyApp_MainLoop
708 orig_mainloop = wx.wxc.wxPyApp_MainLoop
707 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
709 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
708 else:
710 else:
709 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
711 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
710 return orig_mainloop
712 return orig_mainloop
711
713
712 def hijack_gtk():
714 def hijack_gtk():
713 """Modifies pyGTK's mainloop with a dummy so user code does not
715 """Modifies pyGTK's mainloop with a dummy so user code does not
714 block IPython. This function returns the original `gtk.mainloop`
716 block IPython. This function returns the original `gtk.mainloop`
715 function that has been hijacked.
717 function that has been hijacked.
716 """
718 """
717 def dummy_mainloop(*args, **kw):
719 def dummy_mainloop(*args, **kw):
718 pass
720 pass
719 import gtk
721 import gtk
720 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
722 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
721 else: orig_mainloop = gtk.mainloop
723 else: orig_mainloop = gtk.mainloop
722 gtk.mainloop = dummy_mainloop
724 gtk.mainloop = dummy_mainloop
723 gtk.main = dummy_mainloop
725 gtk.main = dummy_mainloop
724 return orig_mainloop
726 return orig_mainloop
725
727
726 def hijack_qt():
728 def hijack_qt():
727 """Modifies PyQt's mainloop with a dummy so user code does not
729 """Modifies PyQt's mainloop with a dummy so user code does not
728 block IPython. This function returns the original
730 block IPython. This function returns the original
729 `qt.qApp.exec_loop` function that has been hijacked.
731 `qt.qApp.exec_loop` function that has been hijacked.
730 """
732 """
731 def dummy_mainloop(*args, **kw):
733 def dummy_mainloop(*args, **kw):
732 pass
734 pass
733 import qt
735 import qt
734 orig_mainloop = qt.qApp.exec_loop
736 orig_mainloop = qt.qApp.exec_loop
735 qt.qApp.exec_loop = dummy_mainloop
737 qt.qApp.exec_loop = dummy_mainloop
736 qt.QApplication.exec_loop = dummy_mainloop
738 qt.QApplication.exec_loop = dummy_mainloop
737 return orig_mainloop
739 return orig_mainloop
738
740
739 def hijack_qt4():
741 def hijack_qt4():
740 """Modifies PyQt4's mainloop with a dummy so user code does not
742 """Modifies PyQt4's mainloop with a dummy so user code does not
741 block IPython. This function returns the original
743 block IPython. This function returns the original
742 `QtGui.qApp.exec_` function that has been hijacked.
744 `QtGui.qApp.exec_` function that has been hijacked.
743 """
745 """
744 def dummy_mainloop(*args, **kw):
746 def dummy_mainloop(*args, **kw):
745 pass
747 pass
746 from PyQt4 import QtGui, QtCore
748 from PyQt4 import QtGui, QtCore
747 orig_mainloop = QtGui.qApp.exec_
749 orig_mainloop = QtGui.qApp.exec_
748 QtGui.qApp.exec_ = dummy_mainloop
750 QtGui.qApp.exec_ = dummy_mainloop
749 QtGui.QApplication.exec_ = dummy_mainloop
751 QtGui.QApplication.exec_ = dummy_mainloop
750 QtCore.QCoreApplication.exec_ = dummy_mainloop
752 QtCore.QCoreApplication.exec_ = dummy_mainloop
751 return orig_mainloop
753 return orig_mainloop
752
754
753 #-----------------------------------------------------------------------------
755 #-----------------------------------------------------------------------------
754 # The IPShell* classes below are the ones meant to be run by external code as
756 # The IPShell* classes below are the ones meant to be run by external code as
755 # IPython instances. Note that unless a specific threading strategy is
757 # IPython instances. Note that unless a specific threading strategy is
756 # desired, the factory function start() below should be used instead (it
758 # desired, the factory function start() below should be used instead (it
757 # selects the proper threaded class).
759 # selects the proper threaded class).
758
760
759 class IPThread(threading.Thread):
761 class IPThread(threading.Thread):
760 def run(self):
762 def run(self):
761 self.IP.mainloop(self._banner)
763 self.IP.mainloop(self._banner)
762 self.IP.kill()
764 self.IP.kill()
763
765
764 class IPShellGTK(IPThread):
766 class IPShellGTK(IPThread):
765 """Run a gtk mainloop() in a separate thread.
767 """Run a gtk mainloop() in a separate thread.
766
768
767 Python commands can be passed to the thread where they will be executed.
769 Python commands can be passed to the thread where they will be executed.
768 This is implemented by periodically checking for passed code using a
770 This is implemented by periodically checking for passed code using a
769 GTK timeout callback."""
771 GTK timeout callback."""
770
772
771 TIMEOUT = 100 # Millisecond interval between timeouts.
773 TIMEOUT = 100 # Millisecond interval between timeouts.
772
774
773 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
775 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
774 debug=1,shell_class=MTInteractiveShell):
776 debug=1,shell_class=MTInteractiveShell):
775
777
776 import gtk
778 import gtk
777
779
778 self.gtk = gtk
780 self.gtk = gtk
779 self.gtk_mainloop = hijack_gtk()
781 self.gtk_mainloop = hijack_gtk()
780
782
781 # Allows us to use both Tk and GTK.
783 # Allows us to use both Tk and GTK.
782 self.tk = get_tk()
784 self.tk = get_tk()
783
785
784 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
786 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
785 else: mainquit = self.gtk.mainquit
787 else: mainquit = self.gtk.mainquit
786
788
787 self.IP = make_IPython(argv,user_ns=user_ns,
789 self.IP = make_IPython(argv,user_ns=user_ns,
788 user_global_ns=user_global_ns,
790 user_global_ns=user_global_ns,
789 debug=debug,
791 debug=debug,
790 shell_class=shell_class,
792 shell_class=shell_class,
791 on_kill=[mainquit])
793 on_kill=[mainquit])
792
794
793 # HACK: slot for banner in self; it will be passed to the mainloop
795 # HACK: slot for banner in self; it will be passed to the mainloop
794 # method only and .run() needs it. The actual value will be set by
796 # method only and .run() needs it. The actual value will be set by
795 # .mainloop().
797 # .mainloop().
796 self._banner = None
798 self._banner = None
797
799
798 threading.Thread.__init__(self)
800 threading.Thread.__init__(self)
799
801
800 def mainloop(self,sys_exit=0,banner=None):
802 def mainloop(self,sys_exit=0,banner=None):
801
803
802 self._banner = banner
804 self._banner = banner
803
805
804 if self.gtk.pygtk_version >= (2,4,0):
806 if self.gtk.pygtk_version >= (2,4,0):
805 import gobject
807 import gobject
806 gobject.idle_add(self.on_timer)
808 gobject.idle_add(self.on_timer)
807 else:
809 else:
808 self.gtk.idle_add(self.on_timer)
810 self.gtk.idle_add(self.on_timer)
809
811
810 if sys.platform != 'win32':
812 if sys.platform != 'win32':
811 try:
813 try:
812 if self.gtk.gtk_version[0] >= 2:
814 if self.gtk.gtk_version[0] >= 2:
813 self.gtk.gdk.threads_init()
815 self.gtk.gdk.threads_init()
814 except AttributeError:
816 except AttributeError:
815 pass
817 pass
816 except RuntimeError:
818 except RuntimeError:
817 error('Your pyGTK likely has not been compiled with '
819 error('Your pyGTK likely has not been compiled with '
818 'threading support.\n'
820 'threading support.\n'
819 'The exception printout is below.\n'
821 'The exception printout is below.\n'
820 'You can either rebuild pyGTK with threads, or '
822 'You can either rebuild pyGTK with threads, or '
821 'try using \n'
823 'try using \n'
822 'matplotlib with a different backend (like Tk or WX).\n'
824 'matplotlib with a different backend (like Tk or WX).\n'
823 'Note that matplotlib will most likely not work in its '
825 'Note that matplotlib will most likely not work in its '
824 'current state!')
826 'current state!')
825 self.IP.InteractiveTB()
827 self.IP.InteractiveTB()
826
828
827 self.start()
829 self.start()
828 self.gtk.gdk.threads_enter()
830 self.gtk.gdk.threads_enter()
829 self.gtk_mainloop()
831 self.gtk_mainloop()
830 self.gtk.gdk.threads_leave()
832 self.gtk.gdk.threads_leave()
831 self.join()
833 self.join()
832
834
833 def on_timer(self):
835 def on_timer(self):
834 """Called when GTK is idle.
836 """Called when GTK is idle.
835
837
836 Must return True always, otherwise GTK stops calling it"""
838 Must return True always, otherwise GTK stops calling it"""
837
839
838 update_tk(self.tk)
840 update_tk(self.tk)
839 self.IP.runcode()
841 self.IP.runcode()
840 time.sleep(0.01)
842 time.sleep(0.01)
841 return True
843 return True
842
844
843
845
844 class IPShellWX(IPThread):
846 class IPShellWX(IPThread):
845 """Run a wx mainloop() in a separate thread.
847 """Run a wx mainloop() in a separate thread.
846
848
847 Python commands can be passed to the thread where they will be executed.
849 Python commands can be passed to the thread where they will be executed.
848 This is implemented by periodically checking for passed code using a
850 This is implemented by periodically checking for passed code using a
849 GTK timeout callback."""
851 GTK timeout callback."""
850
852
851 TIMEOUT = 100 # Millisecond interval between timeouts.
853 TIMEOUT = 100 # Millisecond interval between timeouts.
852
854
853 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
855 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
854 debug=1,shell_class=MTInteractiveShell):
856 debug=1,shell_class=MTInteractiveShell):
855
857
856 self.IP = make_IPython(argv,user_ns=user_ns,
858 self.IP = make_IPython(argv,user_ns=user_ns,
857 user_global_ns=user_global_ns,
859 user_global_ns=user_global_ns,
858 debug=debug,
860 debug=debug,
859 shell_class=shell_class,
861 shell_class=shell_class,
860 on_kill=[self.wxexit])
862 on_kill=[self.wxexit])
861
863
862 wantedwxversion=self.IP.rc.wxversion
864 wantedwxversion=self.IP.rc.wxversion
863 if wantedwxversion!="0":
865 if wantedwxversion!="0":
864 try:
866 try:
865 import wxversion
867 import wxversion
866 except ImportError:
868 except ImportError:
867 error('The wxversion module is needed for WX version selection')
869 error('The wxversion module is needed for WX version selection')
868 else:
870 else:
869 try:
871 try:
870 wxversion.select(wantedwxversion)
872 wxversion.select(wantedwxversion)
871 except:
873 except:
872 self.IP.InteractiveTB()
874 self.IP.InteractiveTB()
873 error('Requested wxPython version %s could not be loaded' %
875 error('Requested wxPython version %s could not be loaded' %
874 wantedwxversion)
876 wantedwxversion)
875
877
876 import wx
878 import wx
877
879
878 threading.Thread.__init__(self)
880 threading.Thread.__init__(self)
879 self.wx = wx
881 self.wx = wx
880 self.wx_mainloop = hijack_wx()
882 self.wx_mainloop = hijack_wx()
881
883
882 # Allows us to use both Tk and GTK.
884 # Allows us to use both Tk and GTK.
883 self.tk = get_tk()
885 self.tk = get_tk()
884
886
885 # HACK: slot for banner in self; it will be passed to the mainloop
887 # HACK: slot for banner in self; it will be passed to the mainloop
886 # method only and .run() needs it. The actual value will be set by
888 # method only and .run() needs it. The actual value will be set by
887 # .mainloop().
889 # .mainloop().
888 self._banner = None
890 self._banner = None
889
891
890 self.app = None
892 self.app = None
891
893
892 def wxexit(self, *args):
894 def wxexit(self, *args):
893 if self.app is not None:
895 if self.app is not None:
894 self.app.agent.timer.Stop()
896 self.app.agent.timer.Stop()
895 self.app.ExitMainLoop()
897 self.app.ExitMainLoop()
896
898
897 def mainloop(self,sys_exit=0,banner=None):
899 def mainloop(self,sys_exit=0,banner=None):
898
900
899 self._banner = banner
901 self._banner = banner
900
902
901 self.start()
903 self.start()
902
904
903 class TimerAgent(self.wx.MiniFrame):
905 class TimerAgent(self.wx.MiniFrame):
904 wx = self.wx
906 wx = self.wx
905 IP = self.IP
907 IP = self.IP
906 tk = self.tk
908 tk = self.tk
907 def __init__(self, parent, interval):
909 def __init__(self, parent, interval):
908 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
910 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
909 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
911 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
910 size=(100, 100),style=style)
912 size=(100, 100),style=style)
911 self.Show(False)
913 self.Show(False)
912 self.interval = interval
914 self.interval = interval
913 self.timerId = self.wx.NewId()
915 self.timerId = self.wx.NewId()
914
916
915 def StartWork(self):
917 def StartWork(self):
916 self.timer = self.wx.Timer(self, self.timerId)
918 self.timer = self.wx.Timer(self, self.timerId)
917 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
919 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
918 self.timer.Start(self.interval)
920 self.timer.Start(self.interval)
919
921
920 def OnTimer(self, event):
922 def OnTimer(self, event):
921 update_tk(self.tk)
923 update_tk(self.tk)
922 self.IP.runcode()
924 self.IP.runcode()
923
925
924 class App(self.wx.App):
926 class App(self.wx.App):
925 wx = self.wx
927 wx = self.wx
926 TIMEOUT = self.TIMEOUT
928 TIMEOUT = self.TIMEOUT
927 def OnInit(self):
929 def OnInit(self):
928 'Create the main window and insert the custom frame'
930 'Create the main window and insert the custom frame'
929 self.agent = TimerAgent(None, self.TIMEOUT)
931 self.agent = TimerAgent(None, self.TIMEOUT)
930 self.agent.Show(False)
932 self.agent.Show(False)
931 self.agent.StartWork()
933 self.agent.StartWork()
932 return True
934 return True
933
935
934 self.app = App(redirect=False)
936 self.app = App(redirect=False)
935 self.wx_mainloop(self.app)
937 self.wx_mainloop(self.app)
936 self.join()
938 self.join()
937
939
938
940
939 class IPShellQt(IPThread):
941 class IPShellQt(IPThread):
940 """Run a Qt event loop in a separate thread.
942 """Run a Qt event loop in a separate thread.
941
943
942 Python commands can be passed to the thread where they will be executed.
944 Python commands can be passed to the thread where they will be executed.
943 This is implemented by periodically checking for passed code using a
945 This is implemented by periodically checking for passed code using a
944 Qt timer / slot."""
946 Qt timer / slot."""
945
947
946 TIMEOUT = 100 # Millisecond interval between timeouts.
948 TIMEOUT = 100 # Millisecond interval between timeouts.
947
949
948 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
950 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
949 debug=0, shell_class=MTInteractiveShell):
951 debug=0, shell_class=MTInteractiveShell):
950
952
951 import qt
953 import qt
952
954
953 self.exec_loop = hijack_qt()
955 self.exec_loop = hijack_qt()
954
956
955 # Allows us to use both Tk and QT.
957 # Allows us to use both Tk and QT.
956 self.tk = get_tk()
958 self.tk = get_tk()
957
959
958 self.IP = make_IPython(argv,
960 self.IP = make_IPython(argv,
959 user_ns=user_ns,
961 user_ns=user_ns,
960 user_global_ns=user_global_ns,
962 user_global_ns=user_global_ns,
961 debug=debug,
963 debug=debug,
962 shell_class=shell_class,
964 shell_class=shell_class,
963 on_kill=[qt.qApp.exit])
965 on_kill=[qt.qApp.exit])
964
966
965 # HACK: slot for banner in self; it will be passed to the mainloop
967 # HACK: slot for banner in self; it will be passed to the mainloop
966 # method only and .run() needs it. The actual value will be set by
968 # method only and .run() needs it. The actual value will be set by
967 # .mainloop().
969 # .mainloop().
968 self._banner = None
970 self._banner = None
969
971
970 threading.Thread.__init__(self)
972 threading.Thread.__init__(self)
971
973
972 def mainloop(self, sys_exit=0, banner=None):
974 def mainloop(self, sys_exit=0, banner=None):
973
975
974 import qt
976 import qt
975
977
976 self._banner = banner
978 self._banner = banner
977
979
978 if qt.QApplication.startingUp():
980 if qt.QApplication.startingUp():
979 a = qt.QApplication(sys.argv)
981 a = qt.QApplication(sys.argv)
980
982
981 self.timer = qt.QTimer()
983 self.timer = qt.QTimer()
982 qt.QObject.connect(self.timer,
984 qt.QObject.connect(self.timer,
983 qt.SIGNAL('timeout()'),
985 qt.SIGNAL('timeout()'),
984 self.on_timer)
986 self.on_timer)
985
987
986 self.start()
988 self.start()
987 self.timer.start(self.TIMEOUT, True)
989 self.timer.start(self.TIMEOUT, True)
988 while True:
990 while True:
989 if self.IP._kill: break
991 if self.IP._kill: break
990 self.exec_loop()
992 self.exec_loop()
991 self.join()
993 self.join()
992
994
993 def on_timer(self):
995 def on_timer(self):
994 update_tk(self.tk)
996 update_tk(self.tk)
995 result = self.IP.runcode()
997 result = self.IP.runcode()
996 self.timer.start(self.TIMEOUT, True)
998 self.timer.start(self.TIMEOUT, True)
997 return result
999 return result
998
1000
999
1001
1000 class IPShellQt4(IPThread):
1002 class IPShellQt4(IPThread):
1001 """Run a Qt event loop in a separate thread.
1003 """Run a Qt event loop in a separate thread.
1002
1004
1003 Python commands can be passed to the thread where they will be executed.
1005 Python commands can be passed to the thread where they will be executed.
1004 This is implemented by periodically checking for passed code using a
1006 This is implemented by periodically checking for passed code using a
1005 Qt timer / slot."""
1007 Qt timer / slot."""
1006
1008
1007 TIMEOUT = 100 # Millisecond interval between timeouts.
1009 TIMEOUT = 100 # Millisecond interval between timeouts.
1008
1010
1009 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1011 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1010 debug=0, shell_class=MTInteractiveShell):
1012 debug=0, shell_class=MTInteractiveShell):
1011
1013
1012 from PyQt4 import QtCore, QtGui
1014 from PyQt4 import QtCore, QtGui
1013
1015
1014 try:
1016 try:
1015 # present in PyQt4-4.2.1 or later
1017 # present in PyQt4-4.2.1 or later
1016 QtCore.pyqtRemoveInputHook()
1018 QtCore.pyqtRemoveInputHook()
1017 except AttributeError:
1019 except AttributeError:
1018 pass
1020 pass
1019
1021
1020 if QtCore.PYQT_VERSION_STR == '4.3':
1022 if QtCore.PYQT_VERSION_STR == '4.3':
1021 warn('''PyQt4 version 4.3 detected.
1023 warn('''PyQt4 version 4.3 detected.
1022 If you experience repeated threading warnings, please update PyQt4.
1024 If you experience repeated threading warnings, please update PyQt4.
1023 ''')
1025 ''')
1024
1026
1025 self.exec_ = hijack_qt4()
1027 self.exec_ = hijack_qt4()
1026
1028
1027 # Allows us to use both Tk and QT.
1029 # Allows us to use both Tk and QT.
1028 self.tk = get_tk()
1030 self.tk = get_tk()
1029
1031
1030 self.IP = make_IPython(argv,
1032 self.IP = make_IPython(argv,
1031 user_ns=user_ns,
1033 user_ns=user_ns,
1032 user_global_ns=user_global_ns,
1034 user_global_ns=user_global_ns,
1033 debug=debug,
1035 debug=debug,
1034 shell_class=shell_class,
1036 shell_class=shell_class,
1035 on_kill=[QtGui.qApp.exit])
1037 on_kill=[QtGui.qApp.exit])
1036
1038
1037 # HACK: slot for banner in self; it will be passed to the mainloop
1039 # HACK: slot for banner in self; it will be passed to the mainloop
1038 # method only and .run() needs it. The actual value will be set by
1040 # method only and .run() needs it. The actual value will be set by
1039 # .mainloop().
1041 # .mainloop().
1040 self._banner = None
1042 self._banner = None
1041
1043
1042 threading.Thread.__init__(self)
1044 threading.Thread.__init__(self)
1043
1045
1044 def mainloop(self, sys_exit=0, banner=None):
1046 def mainloop(self, sys_exit=0, banner=None):
1045
1047
1046 from PyQt4 import QtCore, QtGui
1048 from PyQt4 import QtCore, QtGui
1047
1049
1048 self._banner = banner
1050 self._banner = banner
1049
1051
1050 if QtGui.QApplication.startingUp():
1052 if QtGui.QApplication.startingUp():
1051 a = QtGui.QApplication(sys.argv)
1053 a = QtGui.QApplication(sys.argv)
1052
1054
1053 self.timer = QtCore.QTimer()
1055 self.timer = QtCore.QTimer()
1054 QtCore.QObject.connect(self.timer,
1056 QtCore.QObject.connect(self.timer,
1055 QtCore.SIGNAL('timeout()'),
1057 QtCore.SIGNAL('timeout()'),
1056 self.on_timer)
1058 self.on_timer)
1057
1059
1058 self.start()
1060 self.start()
1059 self.timer.start(self.TIMEOUT)
1061 self.timer.start(self.TIMEOUT)
1060 while True:
1062 while True:
1061 if self.IP._kill: break
1063 if self.IP._kill: break
1062 self.exec_()
1064 self.exec_()
1063 self.join()
1065 self.join()
1064
1066
1065 def on_timer(self):
1067 def on_timer(self):
1066 update_tk(self.tk)
1068 update_tk(self.tk)
1067 result = self.IP.runcode()
1069 result = self.IP.runcode()
1068 self.timer.start(self.TIMEOUT)
1070 self.timer.start(self.TIMEOUT)
1069 return result
1071 return result
1070
1072
1071
1073
1072 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1074 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1073 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1075 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1074 def _load_pylab(user_ns):
1076 def _load_pylab(user_ns):
1075 """Allow users to disable pulling all of pylab into the top-level
1077 """Allow users to disable pulling all of pylab into the top-level
1076 namespace.
1078 namespace.
1077
1079
1078 This little utility must be called AFTER the actual ipython instance is
1080 This little utility must be called AFTER the actual ipython instance is
1079 running, since only then will the options file have been fully parsed."""
1081 running, since only then will the options file have been fully parsed."""
1080
1082
1081 ip = IPython.ipapi.get()
1083 ip = IPython.ipapi.get()
1082 if ip.options.pylab_import_all:
1084 if ip.options.pylab_import_all:
1083 ip.ex("from matplotlib.pylab import *")
1085 ip.ex("from matplotlib.pylab import *")
1084 ip.IP.user_config_ns.update(ip.user_ns)
1086 ip.IP.user_config_ns.update(ip.user_ns)
1085
1087
1086
1088
1087 class IPShellMatplotlib(IPShell):
1089 class IPShellMatplotlib(IPShell):
1088 """Subclass IPShell with MatplotlibShell as the internal shell.
1090 """Subclass IPShell with MatplotlibShell as the internal shell.
1089
1091
1090 Single-threaded class, meant for the Tk* and FLTK* backends.
1092 Single-threaded class, meant for the Tk* and FLTK* backends.
1091
1093
1092 Having this on a separate class simplifies the external driver code."""
1094 Having this on a separate class simplifies the external driver code."""
1093
1095
1094 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1096 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1095 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1097 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1096 shell_class=MatplotlibShell)
1098 shell_class=MatplotlibShell)
1097 _load_pylab(self.IP.user_ns)
1099 _load_pylab(self.IP.user_ns)
1098
1100
1099 class IPShellMatplotlibGTK(IPShellGTK):
1101 class IPShellMatplotlibGTK(IPShellGTK):
1100 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1102 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1101
1103
1102 Multi-threaded class, meant for the GTK* backends."""
1104 Multi-threaded class, meant for the GTK* backends."""
1103
1105
1104 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1106 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1105 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1107 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1106 shell_class=MatplotlibMTShell)
1108 shell_class=MatplotlibMTShell)
1107 _load_pylab(self.IP.user_ns)
1109 _load_pylab(self.IP.user_ns)
1108
1110
1109 class IPShellMatplotlibWX(IPShellWX):
1111 class IPShellMatplotlibWX(IPShellWX):
1110 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1112 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1111
1113
1112 Multi-threaded class, meant for the WX* backends."""
1114 Multi-threaded class, meant for the WX* backends."""
1113
1115
1114 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1116 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1115 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1117 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1116 shell_class=MatplotlibMTShell)
1118 shell_class=MatplotlibMTShell)
1117 _load_pylab(self.IP.user_ns)
1119 _load_pylab(self.IP.user_ns)
1118
1120
1119 class IPShellMatplotlibQt(IPShellQt):
1121 class IPShellMatplotlibQt(IPShellQt):
1120 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1122 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1121
1123
1122 Multi-threaded class, meant for the Qt* backends."""
1124 Multi-threaded class, meant for the Qt* backends."""
1123
1125
1124 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1126 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1125 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1127 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1126 shell_class=MatplotlibMTShell)
1128 shell_class=MatplotlibMTShell)
1127 _load_pylab(self.IP.user_ns)
1129 _load_pylab(self.IP.user_ns)
1128
1130
1129 class IPShellMatplotlibQt4(IPShellQt4):
1131 class IPShellMatplotlibQt4(IPShellQt4):
1130 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1132 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1131
1133
1132 Multi-threaded class, meant for the Qt4* backends."""
1134 Multi-threaded class, meant for the Qt4* backends."""
1133
1135
1134 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1136 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1135 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1137 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1136 shell_class=MatplotlibMTShell)
1138 shell_class=MatplotlibMTShell)
1137 _load_pylab(self.IP.user_ns)
1139 _load_pylab(self.IP.user_ns)
1138
1140
1139 #-----------------------------------------------------------------------------
1141 #-----------------------------------------------------------------------------
1140 # Factory functions to actually start the proper thread-aware shell
1142 # Factory functions to actually start the proper thread-aware shell
1141
1143
1142 def _select_shell(argv):
1144 def _select_shell(argv):
1143 """Select a shell from the given argv vector.
1145 """Select a shell from the given argv vector.
1144
1146
1145 This function implements the threading selection policy, allowing runtime
1147 This function implements the threading selection policy, allowing runtime
1146 control of the threading mode, both for general users and for matplotlib.
1148 control of the threading mode, both for general users and for matplotlib.
1147
1149
1148 Return:
1150 Return:
1149 Shell class to be instantiated for runtime operation.
1151 Shell class to be instantiated for runtime operation.
1150 """
1152 """
1151
1153
1152 global USE_TK
1154 global USE_TK
1153
1155
1154 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1156 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1155 'wthread' : IPShellMatplotlibWX,
1157 'wthread' : IPShellMatplotlibWX,
1156 'qthread' : IPShellMatplotlibQt,
1158 'qthread' : IPShellMatplotlibQt,
1157 'q4thread' : IPShellMatplotlibQt4,
1159 'q4thread' : IPShellMatplotlibQt4,
1158 'tkthread' : IPShellMatplotlib, # Tk is built-in
1160 'tkthread' : IPShellMatplotlib, # Tk is built-in
1159 }
1161 }
1160
1162
1161 th_shell = {'gthread' : IPShellGTK,
1163 th_shell = {'gthread' : IPShellGTK,
1162 'wthread' : IPShellWX,
1164 'wthread' : IPShellWX,
1163 'qthread' : IPShellQt,
1165 'qthread' : IPShellQt,
1164 'q4thread' : IPShellQt4,
1166 'q4thread' : IPShellQt4,
1165 'tkthread' : IPShell, # Tk is built-in
1167 'tkthread' : IPShell, # Tk is built-in
1166 }
1168 }
1167
1169
1168 backends = {'gthread' : 'GTKAgg',
1170 backends = {'gthread' : 'GTKAgg',
1169 'wthread' : 'WXAgg',
1171 'wthread' : 'WXAgg',
1170 'qthread' : 'QtAgg',
1172 'qthread' : 'QtAgg',
1171 'q4thread' :'Qt4Agg',
1173 'q4thread' :'Qt4Agg',
1172 'tkthread' :'TkAgg',
1174 'tkthread' :'TkAgg',
1173 }
1175 }
1174
1176
1175 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1177 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1176 'tkthread'])
1178 'tkthread'])
1177 user_opts = set([s.replace('-','') for s in argv[:3]])
1179 user_opts = set([s.replace('-','') for s in argv[:3]])
1178 special_opts = user_opts & all_opts
1180 special_opts = user_opts & all_opts
1179
1181
1180 if 'tk' in special_opts:
1182 if 'tk' in special_opts:
1181 USE_TK = True
1183 USE_TK = True
1182 special_opts.remove('tk')
1184 special_opts.remove('tk')
1183
1185
1184 if 'pylab' in special_opts:
1186 if 'pylab' in special_opts:
1185
1187
1186 try:
1188 try:
1187 import matplotlib
1189 import matplotlib
1188 except ImportError:
1190 except ImportError:
1189 error('matplotlib could NOT be imported! Starting normal IPython.')
1191 error('matplotlib could NOT be imported! Starting normal IPython.')
1190 return IPShell
1192 return IPShell
1191
1193
1192 special_opts.remove('pylab')
1194 special_opts.remove('pylab')
1193 # If there's any option left, it means the user wants to force the
1195 # If there's any option left, it means the user wants to force the
1194 # threading backend, else it's auto-selected from the rc file
1196 # threading backend, else it's auto-selected from the rc file
1195 if special_opts:
1197 if special_opts:
1196 th_mode = special_opts.pop()
1198 th_mode = special_opts.pop()
1197 matplotlib.rcParams['backend'] = backends[th_mode]
1199 matplotlib.rcParams['backend'] = backends[th_mode]
1198 else:
1200 else:
1199 backend = matplotlib.rcParams['backend']
1201 backend = matplotlib.rcParams['backend']
1200 if backend.startswith('GTK'):
1202 if backend.startswith('GTK'):
1201 th_mode = 'gthread'
1203 th_mode = 'gthread'
1202 elif backend.startswith('WX'):
1204 elif backend.startswith('WX'):
1203 th_mode = 'wthread'
1205 th_mode = 'wthread'
1204 elif backend.startswith('Qt4'):
1206 elif backend.startswith('Qt4'):
1205 th_mode = 'q4thread'
1207 th_mode = 'q4thread'
1206 elif backend.startswith('Qt'):
1208 elif backend.startswith('Qt'):
1207 th_mode = 'qthread'
1209 th_mode = 'qthread'
1208 else:
1210 else:
1209 # Any other backend, use plain Tk
1211 # Any other backend, use plain Tk
1210 th_mode = 'tkthread'
1212 th_mode = 'tkthread'
1211
1213
1212 return mpl_shell[th_mode]
1214 return mpl_shell[th_mode]
1213 else:
1215 else:
1214 # No pylab requested, just plain threads
1216 # No pylab requested, just plain threads
1215 try:
1217 try:
1216 th_mode = special_opts.pop()
1218 th_mode = special_opts.pop()
1217 except KeyError:
1219 except KeyError:
1218 th_mode = 'tkthread'
1220 th_mode = 'tkthread'
1219 return th_shell[th_mode]
1221 return th_shell[th_mode]
1220
1222
1221
1223
1222 # This is the one which should be called by external code.
1224 # This is the one which should be called by external code.
1223 def start(user_ns = None):
1225 def start(user_ns = None):
1224 """Return a running shell instance, dealing with threading options.
1226 """Return a running shell instance, dealing with threading options.
1225
1227
1226 This is a factory function which will instantiate the proper IPython shell
1228 This is a factory function which will instantiate the proper IPython shell
1227 based on the user's threading choice. Such a selector is needed because
1229 based on the user's threading choice. Such a selector is needed because
1228 different GUI toolkits require different thread handling details."""
1230 different GUI toolkits require different thread handling details."""
1229
1231
1230 shell = _select_shell(sys.argv)
1232 shell = _select_shell(sys.argv)
1231 return shell(user_ns = user_ns)
1233 return shell(user_ns = user_ns)
1232
1234
1233 # Some aliases for backwards compatibility
1235 # Some aliases for backwards compatibility
1234 IPythonShell = IPShell
1236 IPythonShell = IPShell
1235 IPythonShellEmbed = IPShellEmbed
1237 IPythonShellEmbed = IPShellEmbed
1236 #************************ End of file <Shell.py> ***************************
1238 #************************ End of file <Shell.py> ***************************
@@ -1,63 +1,67 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test process execution and IO redirection.
3 Test process execution and IO redirection.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 from cStringIO import StringIO
15 from cStringIO import StringIO
16 from time import sleep
16 from time import sleep
17 import sys
17 import sys
18
18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend._process import PipedProcess
20 from IPython.testing import decorators as testdec
20
21
21 def test_capture_out():
22 def test_capture_out():
22 """ A simple test to see if we can execute a process and get the output.
23 """ A simple test to see if we can execute a process and get the output.
23 """
24 """
24 s = StringIO()
25 s = StringIO()
25 p = PipedProcess('echo 1', out_callback=s.write, )
26 p = PipedProcess('echo 1', out_callback=s.write, )
26 p.start()
27 p.start()
27 p.join()
28 p.join()
28 assert s.getvalue() == '1\n'
29 result = s.getvalue().rstrip()
29
30 assert result == '1'
30
31
32 # FIXME
33 @testdec.skip("This doesn't work under Windows")
31 def test_io():
34 def test_io():
32 """ Checks that we can send characters on stdin to the process.
35 """ Checks that we can send characters on stdin to the process.
33 """
36 """
34 s = StringIO()
37 s = StringIO()
35 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
38 p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"',
36 out_callback=s.write, )
39 out_callback=s.write, )
37 p.start()
40 p.start()
38 test_string = '12345\n'
41 test_string = '12345\n'
39 while not hasattr(p, 'process'):
42 while not hasattr(p, 'process'):
40 sleep(0.1)
43 sleep(0.1)
41 p.process.stdin.write(test_string)
44 p.process.stdin.write(test_string)
42 p.join()
45 p.join()
43 assert s.getvalue() == test_string
46 result = s.getvalue()
47 assert result == test_string
44
48
45
49
46 def test_kill():
50 def test_kill():
47 """ Check that we can kill a process, and its subprocess.
51 """ Check that we can kill a process, and its subprocess.
48 """
52 """
49 s = StringIO()
53 s = StringIO()
50 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
54 p = PipedProcess(sys.executable + ' -c "a = raw_input();"',
51 out_callback=s.write, )
55 out_callback=s.write, )
52 p.start()
56 p.start()
53 while not hasattr(p, 'process'):
57 while not hasattr(p, 'process'):
54 sleep(0.1)
58 sleep(0.1)
55 p.process.kill()
59 p.process.kill()
56 assert p.process.poll() is not None
60 assert p.process.poll() is not None
57
61
58
62
59 if __name__ == '__main__':
63 if __name__ == '__main__':
60 test_capture_out()
64 test_capture_out()
61 test_io()
65 test_io()
62 test_kill()
66 test_kill()
63
67
@@ -1,61 +1,68 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test the output capture at the OS level, using file descriptors.
3 Test the output capture at the OS level, using file descriptors.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15
15
16 import os
16 import os
17 from cStringIO import StringIO
17 from cStringIO import StringIO
18
18
19 from IPython.testing import decorators as testdec
19
20
21 # FIXME
22 @testdec.skip("This doesn't work under Windows")
20 def test_redirector():
23 def test_redirector():
21 """ Checks that the redirector can be used to do synchronous capture.
24 """ Checks that the redirector can be used to do synchronous capture.
22 """
25 """
23 from IPython.kernel.core.fd_redirector import FDRedirector
26 from IPython.kernel.core.fd_redirector import FDRedirector
24 r = FDRedirector()
27 r = FDRedirector()
25 out = StringIO()
28 out = StringIO()
26 try:
29 try:
27 r.start()
30 r.start()
28 for i in range(10):
31 for i in range(10):
29 os.system('echo %ic' % i)
32 os.system('echo %ic' % i)
30 print >>out, r.getvalue(),
33 print >>out, r.getvalue(),
31 print >>out, i
34 print >>out, i
32 except:
35 except:
33 r.stop()
36 r.stop()
34 raise
37 raise
35 r.stop()
38 r.stop()
36 assert out.getvalue() == "".join("%ic\n%i\n" %(i, i) for i in range(10))
39 result1 = out.getvalue()
37
40 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
41 assert result1 == result2
38
42
43 # FIXME
44 @testdec.skip("This doesn't work under Windows")
39 def test_redirector_output_trap():
45 def test_redirector_output_trap():
40 """ This test check not only that the redirector_output_trap does
46 """ This test check not only that the redirector_output_trap does
41 trap the output, but also that it does it in a gready way, that
47 trap the output, but also that it does it in a gready way, that
42 is by calling the callback ASAP.
48 is by calling the callback ASAP.
43 """
49 """
44 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
50 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
45 out = StringIO()
51 out = StringIO()
46 trap = RedirectorOutputTrap(out.write, out.write)
52 trap = RedirectorOutputTrap(out.write, out.write)
47 try:
53 try:
48 trap.set()
54 trap.set()
49 for i in range(10):
55 for i in range(10):
50 os.system('echo %ic' % i)
56 os.system('echo %ic' % i)
51 print "%ip" % i
57 print "%ip" % i
52 print >>out, i
58 print >>out, i
53 except:
59 except:
54 trap.unset()
60 trap.unset()
55 raise
61 raise
56 trap.unset()
62 trap.unset()
57 assert out.getvalue() == "".join("%ic\n%ip\n%i\n" %(i, i, i)
63 result1 = out.getvalue()
58 for i in range(10))
64 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
65 assert result1 == result2
59
66
60
67
61
68
@@ -1,135 +1,147 b''
1 """Decorators for labeling test objects.
1 """Decorators for labeling test objects.
2
2
3 Decorators that merely return a modified version of the original
3 Decorators that merely return a modified version of the original
4 function object are straightforward. Decorators that return a new
4 function object are straightforward. Decorators that return a new
5 function object need to use
5 function object need to use
6 nose.tools.make_decorator(original_function)(decorator) in returning
6 nose.tools.make_decorator(original_function)(decorator) in returning
7 the decorator, in order to preserve metadata such as function name,
7 the decorator, in order to preserve metadata such as function name,
8 setup and teardown functions and so on - see nose.tools for more
8 setup and teardown functions and so on - see nose.tools for more
9 information.
9 information.
10
10
11 NOTE: This file contains IPython-specific decorators and imports the
11 NOTE: This file contains IPython-specific decorators and imports the
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
13 code will be added at the bottom if we end up extending this.
13 code will be added at the bottom if we end up extending this.
14 """
14 """
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import inspect
17 import inspect
18
18
19 # Third-party imports
19 # Third-party imports
20
20
21 # This is Michele Simionato's decorator module, also kept verbatim.
21 # This is Michele Simionato's decorator module, also kept verbatim.
22 from decorator_msim import decorator, update_wrapper
22 from decorator_msim import decorator, update_wrapper
23
23
24 # Grab the numpy-specific decorators which we keep in a file that we
24 # Grab the numpy-specific decorators which we keep in a file that we
25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
26 # of numpy.testing.decorators.
26 # of numpy.testing.decorators.
27 from decorators_numpy import *
27 from decorators_numpy import *
28
28
29 ##############################################################################
29 ##############################################################################
30 # Local code begins
30 # Local code begins
31
31
32 # Utility functions
32 # Utility functions
33
33
34 def apply_wrapper(wrapper,func):
34 def apply_wrapper(wrapper,func):
35 """Apply a wrapper to a function for decoration.
35 """Apply a wrapper to a function for decoration.
36
36
37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
38 to apply a wrapper in a decorator so that all nose attributes, as well as
38 to apply a wrapper in a decorator so that all nose attributes, as well as
39 function signature and other properties, survive the decoration cleanly.
39 function signature and other properties, survive the decoration cleanly.
40 This will ensure that wrapped functions can still be well introspected via
40 This will ensure that wrapped functions can still be well introspected via
41 IPython, for example.
41 IPython, for example.
42 """
42 """
43 import nose.tools
43 import nose.tools
44
44
45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
46
46
47
47
48 def make_label_dec(label,ds=None):
48 def make_label_dec(label,ds=None):
49 """Factory function to create a decorator that applies one or more labels.
49 """Factory function to create a decorator that applies one or more labels.
50
50
51 :Parameters:
51 :Parameters:
52 label : string or sequence
52 label : string or sequence
53 One or more labels that will be applied by the decorator to the functions
53 One or more labels that will be applied by the decorator to the functions
54 it decorates. Labels are attributes of the decorated function with their
54 it decorates. Labels are attributes of the decorated function with their
55 value set to True.
55 value set to True.
56
56
57 :Keywords:
57 :Keywords:
58 ds : string
58 ds : string
59 An optional docstring for the resulting decorator. If not given, a
59 An optional docstring for the resulting decorator. If not given, a
60 default docstring is auto-generated.
60 default docstring is auto-generated.
61
61
62 :Returns:
62 :Returns:
63 A decorator.
63 A decorator.
64
64
65 :Examples:
65 :Examples:
66
66
67 A simple labeling decorator:
67 A simple labeling decorator:
68 >>> slow = make_label_dec('slow')
68 >>> slow = make_label_dec('slow')
69 >>> print slow.__doc__
69 >>> print slow.__doc__
70 Labels a test as 'slow'.
70 Labels a test as 'slow'.
71
71
72 And one that uses multiple labels and a custom docstring:
72 And one that uses multiple labels and a custom docstring:
73 >>> rare = make_label_dec(['slow','hard'],
73 >>> rare = make_label_dec(['slow','hard'],
74 ... "Mix labels 'slow' and 'hard' for rare tests.")
74 ... "Mix labels 'slow' and 'hard' for rare tests.")
75 >>> print rare.__doc__
75 >>> print rare.__doc__
76 Mix labels 'slow' and 'hard' for rare tests.
76 Mix labels 'slow' and 'hard' for rare tests.
77
77
78 Now, let's test using this one:
78 Now, let's test using this one:
79 >>> @rare
79 >>> @rare
80 ... def f(): pass
80 ... def f(): pass
81 ...
81 ...
82 >>>
82 >>>
83 >>> f.slow
83 >>> f.slow
84 True
84 True
85 >>> f.hard
85 >>> f.hard
86 True
86 True
87 """
87 """
88
88
89 if isinstance(label,basestring):
89 if isinstance(label,basestring):
90 labels = [label]
90 labels = [label]
91 else:
91 else:
92 labels = label
92 labels = label
93
93
94 # Validate that the given label(s) are OK for use in setattr() by doing a
94 # Validate that the given label(s) are OK for use in setattr() by doing a
95 # dry run on a dummy function.
95 # dry run on a dummy function.
96 tmp = lambda : None
96 tmp = lambda : None
97 for label in labels:
97 for label in labels:
98 setattr(tmp,label,True)
98 setattr(tmp,label,True)
99
99
100 # This is the actual decorator we'll return
100 # This is the actual decorator we'll return
101 def decor(f):
101 def decor(f):
102 for label in labels:
102 for label in labels:
103 setattr(f,label,True)
103 setattr(f,label,True)
104 return f
104 return f
105
105
106 # Apply the user's docstring, or autogenerate a basic one
106 # Apply the user's docstring, or autogenerate a basic one
107 if ds is None:
107 if ds is None:
108 ds = "Labels a test as %r." % label
108 ds = "Labels a test as %r." % label
109 decor.__doc__ = ds
109 decor.__doc__ = ds
110
110
111 return decor
111 return decor
112
112
113 #-----------------------------------------------------------------------------
113 #-----------------------------------------------------------------------------
114 # Decorators for public use
114 # Decorators for public use
115
115
116 skip_doctest = make_label_dec('skip_doctest',
116 skip_doctest = make_label_dec('skip_doctest',
117 """Decorator - mark a function or method for skipping its doctest.
117 """Decorator - mark a function or method for skipping its doctest.
118
118
119 This decorator allows you to mark a function whose docstring you wish to
119 This decorator allows you to mark a function whose docstring you wish to
120 omit from testing, while preserving the docstring for introspection, help,
120 omit from testing, while preserving the docstring for introspection, help,
121 etc.""")
121 etc.""")
122
122
123 def skip(msg=''):
124 """Decorator - mark a test function for skipping from test suite.
123
125
124 def skip(func):
126 :Parameters:
125 """Decorator - mark a test function for skipping from test suite."""
127
128 func : function
129 Test function to be skipped
130
131 msg : string
132 Optional message to be added.
133 """
126
134
127 import nose
135 import nose
128
129 def wrapper(*a,**k):
130 raise nose.SkipTest("Skipping test for function: %s" %
131 func.__name__)
132
133 return apply_wrapper(wrapper,func)
134
136
137 def inner(func):
138
139 def wrapper(*a,**k):
140 if msg: out = '\n'+msg
141 else: out = ''
142 raise nose.SkipTest("Skipping test for function: %s%s" %
143 (func.__name__,out))
144
145 return apply_wrapper(wrapper,func)
135
146
147 return inner
@@ -1,784 +1,806 b''
1 """Nose Plugin that supports IPython doctests.
1 """Nose Plugin that supports IPython doctests.
2
2
3 Limitations:
3 Limitations:
4
4
5 - When generating examples for use as doctests, make sure that you have
5 - When generating examples for use as doctests, make sure that you have
6 pretty-printing OFF. This can be done either by starting ipython with the
6 pretty-printing OFF. This can be done either by starting ipython with the
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
8 interactively disabling it with %Pprint. This is required so that IPython
8 interactively disabling it with %Pprint. This is required so that IPython
9 output matches that of normal Python, which is used by doctest for internal
9 output matches that of normal Python, which is used by doctest for internal
10 execution.
10 execution.
11
11
12 - Do not rely on specific prompt numbers for results (such as using
12 - Do not rely on specific prompt numbers for results (such as using
13 '_34==True', for example). For IPython tests run via an external process the
13 '_34==True', for example). For IPython tests run via an external process the
14 prompt numbers may be different, and IPython tests run as normal python code
14 prompt numbers may be different, and IPython tests run as normal python code
15 won't even have these special _NN variables set at all.
15 won't even have these special _NN variables set at all.
16 """
16 """
17
17
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Module imports
21
21
22 # From the standard library
22 # From the standard library
23 import __builtin__
23 import __builtin__
24 import commands
24 import commands
25 import doctest
25 import doctest
26 import inspect
26 import inspect
27 import logging
27 import logging
28 import os
28 import os
29 import re
29 import re
30 import sys
30 import sys
31 import traceback
31 import traceback
32 import unittest
32 import unittest
33
33
34 from inspect import getmodule
34 from inspect import getmodule
35 from StringIO import StringIO
35 from StringIO import StringIO
36
36
37 # We are overriding the default doctest runner, so we need to import a few
37 # We are overriding the default doctest runner, so we need to import a few
38 # things from doctest directly
38 # things from doctest directly
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
40 _unittest_reportflags, DocTestRunner,
40 _unittest_reportflags, DocTestRunner,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
42 _exception_traceback,
42 _exception_traceback,
43 linecache)
43 linecache)
44
44
45 # Third-party modules
45 # Third-party modules
46 import nose.core
46 import nose.core
47
47
48 from nose.plugins import doctests, Plugin
48 from nose.plugins import doctests, Plugin
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
50
50
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52 # Module globals and other constants
52 # Module globals and other constants
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 ###########################################################################
56 ###########################################################################
57 # *** HACK ***
57 # *** HACK ***
58 # We must start our own ipython object and heavily muck with it so that all the
58 # We must start our own ipython object and heavily muck with it so that all the
59 # modifications IPython makes to system behavior don't send the doctest
59 # modifications IPython makes to system behavior don't send the doctest
60 # machinery into a fit. This code should be considered a gross hack, but it
60 # machinery into a fit. This code should be considered a gross hack, but it
61 # gets the job done.
61 # gets the job done.
62
62
63
63
64 # Hack to modify the %run command so we can sync the user's namespace with the
64 # Hack to modify the %run command so we can sync the user's namespace with the
65 # test globals. Once we move over to a clean magic system, this will be done
65 # test globals. Once we move over to a clean magic system, this will be done
66 # with much less ugliness.
66 # with much less ugliness.
67
67
68 def _run_ns_sync(self,arg_s,runner=None):
68 def _run_ns_sync(self,arg_s,runner=None):
69 """Modified version of %run that syncs testing namespaces.
69 """Modified version of %run that syncs testing namespaces.
70
70
71 This is strictly needed for running doctests that call %run.
71 This is strictly needed for running doctests that call %run.
72 """
72 """
73
73
74 out = _ip.IP.magic_run_ori(arg_s,runner)
74 out = _ip.IP.magic_run_ori(arg_s,runner)
75 _run_ns_sync.test_globs.update(_ip.user_ns)
75 _run_ns_sync.test_globs.update(_ip.user_ns)
76 return out
76 return out
77
77
78
78
79 class ipnsdict(dict):
79 class ipnsdict(dict):
80 """A special subclass of dict for use as an IPython namespace in doctests.
80 """A special subclass of dict for use as an IPython namespace in doctests.
81
81
82 This subclass adds a simple checkpointing capability so that when testing
82 This subclass adds a simple checkpointing capability so that when testing
83 machinery clears it (we use it as the test execution context), it doesn't
83 machinery clears it (we use it as the test execution context), it doesn't
84 get completely destroyed.
84 get completely destroyed.
85 """
85 """
86
86
87 def __init__(self,*a):
87 def __init__(self,*a):
88 dict.__init__(self,*a)
88 dict.__init__(self,*a)
89 self._savedict = {}
89 self._savedict = {}
90
90
91 def clear(self):
91 def clear(self):
92 dict.clear(self)
92 dict.clear(self)
93 self.update(self._savedict)
93 self.update(self._savedict)
94
94
95 def _checkpoint(self):
95 def _checkpoint(self):
96 self._savedict.clear()
96 self._savedict.clear()
97 self._savedict.update(self)
97 self._savedict.update(self)
98
98
99 def update(self,other):
99 def update(self,other):
100 self._checkpoint()
100 self._checkpoint()
101 dict.update(self,other)
101 dict.update(self,other)
102 # If '_' is in the namespace, python won't set it when executing code,
102 # If '_' is in the namespace, python won't set it when executing code,
103 # and we have examples that test it. So we ensure that the namespace
103 # and we have examples that test it. So we ensure that the namespace
104 # is always 'clean' of it before it's used for test code execution.
104 # is always 'clean' of it before it's used for test code execution.
105 self.pop('_',None)
105 self.pop('_',None)
106
106
107
107
108 def start_ipython():
108 def start_ipython():
109 """Start a global IPython shell, which we need for IPython-specific syntax.
109 """Start a global IPython shell, which we need for IPython-specific syntax.
110 """
110 """
111 import new
111 import new
112
112
113 import IPython
113 import IPython
114
114
115 def xsys(cmd):
115 def xsys(cmd):
116 """Execute a command and print its output.
116 """Execute a command and print its output.
117
117
118 This is just a convenience function to replace the IPython system call
118 This is just a convenience function to replace the IPython system call
119 with one that is more doctest-friendly.
119 with one that is more doctest-friendly.
120 """
120 """
121 cmd = _ip.IP.var_expand(cmd,depth=1)
121 cmd = _ip.IP.var_expand(cmd,depth=1)
122 sys.stdout.write(commands.getoutput(cmd))
122 sys.stdout.write(commands.getoutput(cmd))
123 sys.stdout.flush()
123 sys.stdout.flush()
124
124
125 # Store certain global objects that IPython modifies
125 # Store certain global objects that IPython modifies
126 _displayhook = sys.displayhook
126 _displayhook = sys.displayhook
127 _excepthook = sys.excepthook
127 _excepthook = sys.excepthook
128 _main = sys.modules.get('__main__')
128 _main = sys.modules.get('__main__')
129
129
130 # Start IPython instance. We customize it to start with minimal frills.
130 # Start IPython instance. We customize it to start with minimal frills.
131 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
131 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
132
132
133 IPython.Shell.IPShell(['--classic','--noterm_title'],
133 IPython.Shell.IPShell(['--classic','--noterm_title'],
134 user_ns,global_ns)
134 user_ns,global_ns)
135
135
136 # Deactivate the various python system hooks added by ipython for
136 # Deactivate the various python system hooks added by ipython for
137 # interactive convenience so we don't confuse the doctest system
137 # interactive convenience so we don't confuse the doctest system
138 sys.modules['__main__'] = _main
138 sys.modules['__main__'] = _main
139 sys.displayhook = _displayhook
139 sys.displayhook = _displayhook
140 sys.excepthook = _excepthook
140 sys.excepthook = _excepthook
141
141
142 # So that ipython magics and aliases can be doctested (they work by making
142 # So that ipython magics and aliases can be doctested (they work by making
143 # a call into a global _ip object)
143 # a call into a global _ip object)
144 _ip = IPython.ipapi.get()
144 _ip = IPython.ipapi.get()
145 __builtin__._ip = _ip
145 __builtin__._ip = _ip
146
146
147 # Modify the IPython system call with one that uses getoutput, so that we
147 # Modify the IPython system call with one that uses getoutput, so that we
148 # can capture subcommands and print them to Python's stdout, otherwise the
148 # can capture subcommands and print them to Python's stdout, otherwise the
149 # doctest machinery would miss them.
149 # doctest machinery would miss them.
150 _ip.system = xsys
150 _ip.system = xsys
151
151
152 # Also patch our %run function in.
152 # Also patch our %run function in.
153 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
153 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
154 _ip.IP.magic_run_ori = _ip.IP.magic_run
154 _ip.IP.magic_run_ori = _ip.IP.magic_run
155 _ip.IP.magic_run = im
155 _ip.IP.magic_run = im
156
156
157 # The start call MUST be made here. I'm not sure yet why it doesn't work if
157 # The start call MUST be made here. I'm not sure yet why it doesn't work if
158 # it is made later, at plugin initialization time, but in all my tests, that's
158 # it is made later, at plugin initialization time, but in all my tests, that's
159 # the case.
159 # the case.
160 start_ipython()
160 start_ipython()
161
161
162 # *** END HACK ***
162 # *** END HACK ***
163 ###########################################################################
163 ###########################################################################
164
164
165 # Classes and functions
165 # Classes and functions
166
166
167 def is_extension_module(filename):
167 def is_extension_module(filename):
168 """Return whether the given filename is an extension module.
168 """Return whether the given filename is an extension module.
169
169
170 This simply checks that the extension is either .so or .pyd.
170 This simply checks that the extension is either .so or .pyd.
171 """
171 """
172 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
172 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
173
173
174
174
175 class nodoc(object):
175 class nodoc(object):
176 def __init__(self,obj):
176 def __init__(self,obj):
177 self.obj = obj
177 self.obj = obj
178
178
179 def __getattribute__(self,key):
179 def __getattribute__(self,key):
180 if key == '__doc__':
180 if key == '__doc__':
181 return None
181 return None
182 else:
182 else:
183 return getattr(object.__getattribute__(self,'obj'),key)
183 return getattr(object.__getattribute__(self,'obj'),key)
184
184
185 # Modified version of the one in the stdlib, that fixes a python bug (doctests
185 # Modified version of the one in the stdlib, that fixes a python bug (doctests
186 # not found in extension modules, http://bugs.python.org/issue3158)
186 # not found in extension modules, http://bugs.python.org/issue3158)
187 class DocTestFinder(doctest.DocTestFinder):
187 class DocTestFinder(doctest.DocTestFinder):
188
188
189 def _from_module(self, module, object):
189 def _from_module(self, module, object):
190 """
190 """
191 Return true if the given object is defined in the given
191 Return true if the given object is defined in the given
192 module.
192 module.
193 """
193 """
194 if module is None:
194 if module is None:
195 return True
195 return True
196 elif inspect.isfunction(object):
196 elif inspect.isfunction(object):
197 return module.__dict__ is object.func_globals
197 return module.__dict__ is object.func_globals
198 elif inspect.isbuiltin(object):
198 elif inspect.isbuiltin(object):
199 return module.__name__ == object.__module__
199 return module.__name__ == object.__module__
200 elif inspect.isclass(object):
200 elif inspect.isclass(object):
201 return module.__name__ == object.__module__
201 return module.__name__ == object.__module__
202 elif inspect.ismethod(object):
202 elif inspect.ismethod(object):
203 # This one may be a bug in cython that fails to correctly set the
203 # This one may be a bug in cython that fails to correctly set the
204 # __module__ attribute of methods, but since the same error is easy
204 # __module__ attribute of methods, but since the same error is easy
205 # to make by extension code writers, having this safety in place
205 # to make by extension code writers, having this safety in place
206 # isn't such a bad idea
206 # isn't such a bad idea
207 return module.__name__ == object.im_class.__module__
207 return module.__name__ == object.im_class.__module__
208 elif inspect.getmodule(object) is not None:
208 elif inspect.getmodule(object) is not None:
209 return module is inspect.getmodule(object)
209 return module is inspect.getmodule(object)
210 elif hasattr(object, '__module__'):
210 elif hasattr(object, '__module__'):
211 return module.__name__ == object.__module__
211 return module.__name__ == object.__module__
212 elif isinstance(object, property):
212 elif isinstance(object, property):
213 return True # [XX] no way not be sure.
213 return True # [XX] no way not be sure.
214 else:
214 else:
215 raise ValueError("object must be a class or function")
215 raise ValueError("object must be a class or function")
216
216
217 def _find(self, tests, obj, name, module, source_lines, globs, seen):
217 def _find(self, tests, obj, name, module, source_lines, globs, seen):
218 """
218 """
219 Find tests for the given object and any contained objects, and
219 Find tests for the given object and any contained objects, and
220 add them to `tests`.
220 add them to `tests`.
221 """
221 """
222
222
223 if hasattr(obj,"skip_doctest"):
223 if hasattr(obj,"skip_doctest"):
224 #print 'SKIPPING DOCTEST FOR:',obj # dbg
224 #print 'SKIPPING DOCTEST FOR:',obj # dbg
225 obj = nodoc(obj)
225 obj = nodoc(obj)
226
226
227 doctest.DocTestFinder._find(self,tests, obj, name, module,
227 doctest.DocTestFinder._find(self,tests, obj, name, module,
228 source_lines, globs, seen)
228 source_lines, globs, seen)
229
229
230 # Below we re-run pieces of the above method with manual modifications,
230 # Below we re-run pieces of the above method with manual modifications,
231 # because the original code is buggy and fails to correctly identify
231 # because the original code is buggy and fails to correctly identify
232 # doctests in extension modules.
232 # doctests in extension modules.
233
233
234 # Local shorthands
234 # Local shorthands
235 from inspect import isroutine, isclass, ismodule
235 from inspect import isroutine, isclass, ismodule
236
236
237 # Look for tests in a module's contained objects.
237 # Look for tests in a module's contained objects.
238 if inspect.ismodule(obj) and self._recurse:
238 if inspect.ismodule(obj) and self._recurse:
239 for valname, val in obj.__dict__.items():
239 for valname, val in obj.__dict__.items():
240 valname1 = '%s.%s' % (name, valname)
240 valname1 = '%s.%s' % (name, valname)
241 if ( (isroutine(val) or isclass(val))
241 if ( (isroutine(val) or isclass(val))
242 and self._from_module(module, val) ):
242 and self._from_module(module, val) ):
243
243
244 self._find(tests, val, valname1, module, source_lines,
244 self._find(tests, val, valname1, module, source_lines,
245 globs, seen)
245 globs, seen)
246
246
247 # Look for tests in a class's contained objects.
247 # Look for tests in a class's contained objects.
248 if inspect.isclass(obj) and self._recurse:
248 if inspect.isclass(obj) and self._recurse:
249 #print 'RECURSE into class:',obj # dbg
249 #print 'RECURSE into class:',obj # dbg
250 for valname, val in obj.__dict__.items():
250 for valname, val in obj.__dict__.items():
251 # Special handling for staticmethod/classmethod.
251 # Special handling for staticmethod/classmethod.
252 if isinstance(val, staticmethod):
252 if isinstance(val, staticmethod):
253 val = getattr(obj, valname)
253 val = getattr(obj, valname)
254 if isinstance(val, classmethod):
254 if isinstance(val, classmethod):
255 val = getattr(obj, valname).im_func
255 val = getattr(obj, valname).im_func
256
256
257 # Recurse to methods, properties, and nested classes.
257 # Recurse to methods, properties, and nested classes.
258 if ((inspect.isfunction(val) or inspect.isclass(val) or
258 if ((inspect.isfunction(val) or inspect.isclass(val) or
259 inspect.ismethod(val) or
259 inspect.ismethod(val) or
260 isinstance(val, property)) and
260 isinstance(val, property)) and
261 self._from_module(module, val)):
261 self._from_module(module, val)):
262 valname = '%s.%s' % (name, valname)
262 valname = '%s.%s' % (name, valname)
263 self._find(tests, val, valname, module, source_lines,
263 self._find(tests, val, valname, module, source_lines,
264 globs, seen)
264 globs, seen)
265
265
266
266
267 class IPDoctestOutputChecker(doctest.OutputChecker):
267 class IPDoctestOutputChecker(doctest.OutputChecker):
268 """Second-chance checker with support for random tests.
268 """Second-chance checker with support for random tests.
269
269
270 If the default comparison doesn't pass, this checker looks in the expected
270 If the default comparison doesn't pass, this checker looks in the expected
271 output string for flags that tell us to ignore the output.
271 output string for flags that tell us to ignore the output.
272 """
272 """
273
273
274 random_re = re.compile(r'#\s*random\s+')
274 random_re = re.compile(r'#\s*random\s+')
275
275
276 def check_output(self, want, got, optionflags):
276 def check_output(self, want, got, optionflags):
277 """Check output, accepting special markers embedded in the output.
277 """Check output, accepting special markers embedded in the output.
278
278
279 If the output didn't pass the default validation but the special string
279 If the output didn't pass the default validation but the special string
280 '#random' is included, we accept it."""
280 '#random' is included, we accept it."""
281
281
282 # Let the original tester verify first, in case people have valid tests
282 # Let the original tester verify first, in case people have valid tests
283 # that happen to have a comment saying '#random' embedded in.
283 # that happen to have a comment saying '#random' embedded in.
284 ret = doctest.OutputChecker.check_output(self, want, got,
284 ret = doctest.OutputChecker.check_output(self, want, got,
285 optionflags)
285 optionflags)
286 if not ret and self.random_re.search(want):
286 if not ret and self.random_re.search(want):
287 #print >> sys.stderr, 'RANDOM OK:',want # dbg
287 #print >> sys.stderr, 'RANDOM OK:',want # dbg
288 return True
288 return True
289
289
290 return ret
290 return ret
291
291
292
292
293 class DocTestCase(doctests.DocTestCase):
293 class DocTestCase(doctests.DocTestCase):
294 """Proxy for DocTestCase: provides an address() method that
294 """Proxy for DocTestCase: provides an address() method that
295 returns the correct address for the doctest case. Otherwise
295 returns the correct address for the doctest case. Otherwise
296 acts as a proxy to the test case. To provide hints for address(),
296 acts as a proxy to the test case. To provide hints for address(),
297 an obj may also be passed -- this will be used as the test object
297 an obj may also be passed -- this will be used as the test object
298 for purposes of determining the test address, if it is provided.
298 for purposes of determining the test address, if it is provided.
299 """
299 """
300
300
301 # Note: this method was taken from numpy's nosetester module.
301 # Note: this method was taken from numpy's nosetester module.
302
302
303 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
303 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
304 # its constructor that blocks non-default arguments from being passed
304 # its constructor that blocks non-default arguments from being passed
305 # down into doctest.DocTestCase
305 # down into doctest.DocTestCase
306
306
307 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
307 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
308 checker=None, obj=None, result_var='_'):
308 checker=None, obj=None, result_var='_'):
309 self._result_var = result_var
309 self._result_var = result_var
310 doctests.DocTestCase.__init__(self, test,
310 doctests.DocTestCase.__init__(self, test,
311 optionflags=optionflags,
311 optionflags=optionflags,
312 setUp=setUp, tearDown=tearDown,
312 setUp=setUp, tearDown=tearDown,
313 checker=checker)
313 checker=checker)
314 # Now we must actually copy the original constructor from the stdlib
314 # Now we must actually copy the original constructor from the stdlib
315 # doctest class, because we can't call it directly and a bug in nose
315 # doctest class, because we can't call it directly and a bug in nose
316 # means it never gets passed the right arguments.
316 # means it never gets passed the right arguments.
317
317
318 self._dt_optionflags = optionflags
318 self._dt_optionflags = optionflags
319 self._dt_checker = checker
319 self._dt_checker = checker
320 self._dt_test = test
320 self._dt_test = test
321 self._dt_setUp = setUp
321 self._dt_setUp = setUp
322 self._dt_tearDown = tearDown
322 self._dt_tearDown = tearDown
323
323
324 # XXX - store this runner once in the object!
324 # XXX - store this runner once in the object!
325 runner = IPDocTestRunner(optionflags=optionflags,
325 runner = IPDocTestRunner(optionflags=optionflags,
326 checker=checker, verbose=False)
326 checker=checker, verbose=False)
327 self._dt_runner = runner
327 self._dt_runner = runner
328
328
329
329
330 # Each doctest should remember what directory it was loaded from...
330 # Each doctest should remember what directory it was loaded from...
331 self._ori_dir = os.getcwd()
331 self._ori_dir = os.getcwd()
332
332
333 # Modified runTest from the default stdlib
333 # Modified runTest from the default stdlib
334 def runTest(self):
334 def runTest(self):
335 test = self._dt_test
335 test = self._dt_test
336 runner = self._dt_runner
336 runner = self._dt_runner
337
337
338 old = sys.stdout
338 old = sys.stdout
339 new = StringIO()
339 new = StringIO()
340 optionflags = self._dt_optionflags
340 optionflags = self._dt_optionflags
341
341
342 if not (optionflags & REPORTING_FLAGS):
342 if not (optionflags & REPORTING_FLAGS):
343 # The option flags don't include any reporting flags,
343 # The option flags don't include any reporting flags,
344 # so add the default reporting flags
344 # so add the default reporting flags
345 optionflags |= _unittest_reportflags
345 optionflags |= _unittest_reportflags
346
346
347 try:
347 try:
348 # Save our current directory and switch out to the one where the
348 # Save our current directory and switch out to the one where the
349 # test was originally created, in case another doctest did a
349 # test was originally created, in case another doctest did a
350 # directory change. We'll restore this in the finally clause.
350 # directory change. We'll restore this in the finally clause.
351 curdir = os.getcwd()
351 curdir = os.getcwd()
352 os.chdir(self._ori_dir)
352 os.chdir(self._ori_dir)
353
353
354 runner.DIVIDER = "-"*70
354 runner.DIVIDER = "-"*70
355 failures, tries = runner.run(test,out=new.write,
355 failures, tries = runner.run(test,out=new.write,
356 clear_globs=False)
356 clear_globs=False)
357 finally:
357 finally:
358 sys.stdout = old
358 sys.stdout = old
359 os.chdir(curdir)
359 os.chdir(curdir)
360
360
361 if failures:
361 if failures:
362 raise self.failureException(self.format_failure(new.getvalue()))
362 raise self.failureException(self.format_failure(new.getvalue()))
363
363
364 def setUp(self):
364 def setUp(self):
365 """Modified test setup that syncs with ipython namespace"""
365 """Modified test setup that syncs with ipython namespace"""
366
366
367 if isinstance(self._dt_test.examples[0],IPExample):
367 if isinstance(self._dt_test.examples[0],IPExample):
368 # for IPython examples *only*, we swap the globals with the ipython
368 # for IPython examples *only*, we swap the globals with the ipython
369 # namespace, after updating it with the globals (which doctest
369 # namespace, after updating it with the globals (which doctest
370 # fills with the necessary info from the module being tested).
370 # fills with the necessary info from the module being tested).
371 _ip.IP.user_ns.update(self._dt_test.globs)
371 _ip.IP.user_ns.update(self._dt_test.globs)
372 self._dt_test.globs = _ip.IP.user_ns
372 self._dt_test.globs = _ip.IP.user_ns
373
373
374 doctests.DocTestCase.setUp(self)
374 doctests.DocTestCase.setUp(self)
375
375
376
376
377
377
378 # A simple subclassing of the original with a different class name, so we can
378 # A simple subclassing of the original with a different class name, so we can
379 # distinguish and treat differently IPython examples from pure python ones.
379 # distinguish and treat differently IPython examples from pure python ones.
380 class IPExample(doctest.Example): pass
380 class IPExample(doctest.Example): pass
381
381
382
382
383 class IPExternalExample(doctest.Example):
383 class IPExternalExample(doctest.Example):
384 """Doctest examples to be run in an external process."""
384 """Doctest examples to be run in an external process."""
385
385
386 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
386 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
387 options=None):
387 options=None):
388 # Parent constructor
388 # Parent constructor
389 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
389 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
390
390
391 # An EXTRA newline is needed to prevent pexpect hangs
391 # An EXTRA newline is needed to prevent pexpect hangs
392 self.source += '\n'
392 self.source += '\n'
393
393
394
394
395 class IPDocTestParser(doctest.DocTestParser):
395 class IPDocTestParser(doctest.DocTestParser):
396 """
396 """
397 A class used to parse strings containing doctest examples.
397 A class used to parse strings containing doctest examples.
398
398
399 Note: This is a version modified to properly recognize IPython input and
399 Note: This is a version modified to properly recognize IPython input and
400 convert any IPython examples into valid Python ones.
400 convert any IPython examples into valid Python ones.
401 """
401 """
402 # This regular expression is used to find doctest examples in a
402 # This regular expression is used to find doctest examples in a
403 # string. It defines three groups: `source` is the source code
403 # string. It defines three groups: `source` is the source code
404 # (including leading indentation and prompts); `indent` is the
404 # (including leading indentation and prompts); `indent` is the
405 # indentation of the first (PS1) line of the source code; and
405 # indentation of the first (PS1) line of the source code; and
406 # `want` is the expected output (including leading indentation).
406 # `want` is the expected output (including leading indentation).
407
407
408 # Classic Python prompts or default IPython ones
408 # Classic Python prompts or default IPython ones
409 _PS1_PY = r'>>>'
409 _PS1_PY = r'>>>'
410 _PS2_PY = r'\.\.\.'
410 _PS2_PY = r'\.\.\.'
411
411
412 _PS1_IP = r'In\ \[\d+\]:'
412 _PS1_IP = r'In\ \[\d+\]:'
413 _PS2_IP = r'\ \ \ \.\.\.+:'
413 _PS2_IP = r'\ \ \ \.\.\.+:'
414
414
415 _RE_TPL = r'''
415 _RE_TPL = r'''
416 # Source consists of a PS1 line followed by zero or more PS2 lines.
416 # Source consists of a PS1 line followed by zero or more PS2 lines.
417 (?P<source>
417 (?P<source>
418 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
418 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
419 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
419 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
420 \n? # a newline
420 \n? # a newline
421 # Want consists of any non-blank lines that do not start with PS1.
421 # Want consists of any non-blank lines that do not start with PS1.
422 (?P<want> (?:(?![ ]*$) # Not a blank line
422 (?P<want> (?:(?![ ]*$) # Not a blank line
423 (?![ ]*%s) # Not a line starting with PS1
423 (?![ ]*%s) # Not a line starting with PS1
424 (?![ ]*%s) # Not a line starting with PS2
424 (?![ ]*%s) # Not a line starting with PS2
425 .*$\n? # But any other line
425 .*$\n? # But any other line
426 )*)
426 )*)
427 '''
427 '''
428
428
429 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
429 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
430 re.MULTILINE | re.VERBOSE)
430 re.MULTILINE | re.VERBOSE)
431
431
432 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
432 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
433 re.MULTILINE | re.VERBOSE)
433 re.MULTILINE | re.VERBOSE)
434
434
435 # Mark a test as being fully random. In this case, we simply append the
435 # Mark a test as being fully random. In this case, we simply append the
436 # random marker ('#random') to each individual example's output. This way
436 # random marker ('#random') to each individual example's output. This way
437 # we don't need to modify any other code.
437 # we don't need to modify any other code.
438 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
438 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
439
439
440 # Mark tests to be executed in an external process - currently unsupported.
440 # Mark tests to be executed in an external process - currently unsupported.
441 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
441 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
442
442
443 def ip2py(self,source):
443 def ip2py(self,source):
444 """Convert input IPython source into valid Python."""
444 """Convert input IPython source into valid Python."""
445 out = []
445 out = []
446 newline = out.append
446 newline = out.append
447 for lnum,line in enumerate(source.splitlines()):
447 for lnum,line in enumerate(source.splitlines()):
448 newline(_ip.IP.prefilter(line,lnum>0))
448 newline(_ip.IP.prefilter(line,lnum>0))
449 newline('') # ensure a closing newline, needed by doctest
449 newline('') # ensure a closing newline, needed by doctest
450 #print "PYSRC:", '\n'.join(out) # dbg
450 #print "PYSRC:", '\n'.join(out) # dbg
451 return '\n'.join(out)
451 return '\n'.join(out)
452
452
453 def parse(self, string, name='<string>'):
453 def parse(self, string, name='<string>'):
454 """
454 """
455 Divide the given string into examples and intervening text,
455 Divide the given string into examples and intervening text,
456 and return them as a list of alternating Examples and strings.
456 and return them as a list of alternating Examples and strings.
457 Line numbers for the Examples are 0-based. The optional
457 Line numbers for the Examples are 0-based. The optional
458 argument `name` is a name identifying this string, and is only
458 argument `name` is a name identifying this string, and is only
459 used for error messages.
459 used for error messages.
460 """
460 """
461
461
462 #print 'Parse string:\n',string # dbg
462 #print 'Parse string:\n',string # dbg
463
463
464 string = string.expandtabs()
464 string = string.expandtabs()
465 # If all lines begin with the same indentation, then strip it.
465 # If all lines begin with the same indentation, then strip it.
466 min_indent = self._min_indent(string)
466 min_indent = self._min_indent(string)
467 if min_indent > 0:
467 if min_indent > 0:
468 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
468 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
469
469
470 output = []
470 output = []
471 charno, lineno = 0, 0
471 charno, lineno = 0, 0
472
472
473 # We make 'all random' tests by adding the '# random' mark to every
473 # We make 'all random' tests by adding the '# random' mark to every
474 # block of output in the test.
474 # block of output in the test.
475 if self._RANDOM_TEST.search(string):
475 if self._RANDOM_TEST.search(string):
476 random_marker = '\n# random'
476 random_marker = '\n# random'
477 else:
477 else:
478 random_marker = ''
478 random_marker = ''
479
479
480 # Whether to convert the input from ipython to python syntax
480 # Whether to convert the input from ipython to python syntax
481 ip2py = False
481 ip2py = False
482 # Find all doctest examples in the string. First, try them as Python
482 # Find all doctest examples in the string. First, try them as Python
483 # examples, then as IPython ones
483 # examples, then as IPython ones
484 terms = list(self._EXAMPLE_RE_PY.finditer(string))
484 terms = list(self._EXAMPLE_RE_PY.finditer(string))
485 if terms:
485 if terms:
486 # Normal Python example
486 # Normal Python example
487 #print '-'*70 # dbg
487 #print '-'*70 # dbg
488 #print 'PyExample, Source:\n',string # dbg
488 #print 'PyExample, Source:\n',string # dbg
489 #print '-'*70 # dbg
489 #print '-'*70 # dbg
490 Example = doctest.Example
490 Example = doctest.Example
491 else:
491 else:
492 # It's an ipython example. Note that IPExamples are run
492 # It's an ipython example. Note that IPExamples are run
493 # in-process, so their syntax must be turned into valid python.
493 # in-process, so their syntax must be turned into valid python.
494 # IPExternalExamples are run out-of-process (via pexpect) so they
494 # IPExternalExamples are run out-of-process (via pexpect) so they
495 # don't need any filtering (a real ipython will be executing them).
495 # don't need any filtering (a real ipython will be executing them).
496 terms = list(self._EXAMPLE_RE_IP.finditer(string))
496 terms = list(self._EXAMPLE_RE_IP.finditer(string))
497 if self._EXTERNAL_IP.search(string):
497 if self._EXTERNAL_IP.search(string):
498 #print '-'*70 # dbg
498 #print '-'*70 # dbg
499 #print 'IPExternalExample, Source:\n',string # dbg
499 #print 'IPExternalExample, Source:\n',string # dbg
500 #print '-'*70 # dbg
500 #print '-'*70 # dbg
501 Example = IPExternalExample
501 Example = IPExternalExample
502 else:
502 else:
503 #print '-'*70 # dbg
503 #print '-'*70 # dbg
504 #print 'IPExample, Source:\n',string # dbg
504 #print 'IPExample, Source:\n',string # dbg
505 #print '-'*70 # dbg
505 #print '-'*70 # dbg
506 Example = IPExample
506 Example = IPExample
507 ip2py = True
507 ip2py = True
508
508
509 for m in terms:
509 for m in terms:
510 # Add the pre-example text to `output`.
510 # Add the pre-example text to `output`.
511 output.append(string[charno:m.start()])
511 output.append(string[charno:m.start()])
512 # Update lineno (lines before this example)
512 # Update lineno (lines before this example)
513 lineno += string.count('\n', charno, m.start())
513 lineno += string.count('\n', charno, m.start())
514 # Extract info from the regexp match.
514 # Extract info from the regexp match.
515 (source, options, want, exc_msg) = \
515 (source, options, want, exc_msg) = \
516 self._parse_example(m, name, lineno,ip2py)
516 self._parse_example(m, name, lineno,ip2py)
517
517
518 # Append the random-output marker (it defaults to empty in most
518 # Append the random-output marker (it defaults to empty in most
519 # cases, it's only non-empty for 'all-random' tests):
519 # cases, it's only non-empty for 'all-random' tests):
520 want += random_marker
520 want += random_marker
521
521
522 if Example is IPExternalExample:
522 if Example is IPExternalExample:
523 options[doctest.NORMALIZE_WHITESPACE] = True
523 options[doctest.NORMALIZE_WHITESPACE] = True
524 want += '\n'
524 want += '\n'
525
525
526 # Create an Example, and add it to the list.
526 # Create an Example, and add it to the list.
527 if not self._IS_BLANK_OR_COMMENT(source):
527 if not self._IS_BLANK_OR_COMMENT(source):
528 output.append(Example(source, want, exc_msg,
528 output.append(Example(source, want, exc_msg,
529 lineno=lineno,
529 lineno=lineno,
530 indent=min_indent+len(m.group('indent')),
530 indent=min_indent+len(m.group('indent')),
531 options=options))
531 options=options))
532 # Update lineno (lines inside this example)
532 # Update lineno (lines inside this example)
533 lineno += string.count('\n', m.start(), m.end())
533 lineno += string.count('\n', m.start(), m.end())
534 # Update charno.
534 # Update charno.
535 charno = m.end()
535 charno = m.end()
536 # Add any remaining post-example text to `output`.
536 # Add any remaining post-example text to `output`.
537 output.append(string[charno:])
537 output.append(string[charno:])
538 return output
538 return output
539
539
540 def _parse_example(self, m, name, lineno,ip2py=False):
540 def _parse_example(self, m, name, lineno,ip2py=False):
541 """
541 """
542 Given a regular expression match from `_EXAMPLE_RE` (`m`),
542 Given a regular expression match from `_EXAMPLE_RE` (`m`),
543 return a pair `(source, want)`, where `source` is the matched
543 return a pair `(source, want)`, where `source` is the matched
544 example's source code (with prompts and indentation stripped);
544 example's source code (with prompts and indentation stripped);
545 and `want` is the example's expected output (with indentation
545 and `want` is the example's expected output (with indentation
546 stripped).
546 stripped).
547
547
548 `name` is the string's name, and `lineno` is the line number
548 `name` is the string's name, and `lineno` is the line number
549 where the example starts; both are used for error messages.
549 where the example starts; both are used for error messages.
550
550
551 Optional:
551 Optional:
552 `ip2py`: if true, filter the input via IPython to convert the syntax
552 `ip2py`: if true, filter the input via IPython to convert the syntax
553 into valid python.
553 into valid python.
554 """
554 """
555
555
556 # Get the example's indentation level.
556 # Get the example's indentation level.
557 indent = len(m.group('indent'))
557 indent = len(m.group('indent'))
558
558
559 # Divide source into lines; check that they're properly
559 # Divide source into lines; check that they're properly
560 # indented; and then strip their indentation & prompts.
560 # indented; and then strip their indentation & prompts.
561 source_lines = m.group('source').split('\n')
561 source_lines = m.group('source').split('\n')
562
562
563 # We're using variable-length input prompts
563 # We're using variable-length input prompts
564 ps1 = m.group('ps1')
564 ps1 = m.group('ps1')
565 ps2 = m.group('ps2')
565 ps2 = m.group('ps2')
566 ps1_len = len(ps1)
566 ps1_len = len(ps1)
567
567
568 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
568 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
569 if ps2:
569 if ps2:
570 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
570 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
571
571
572 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
572 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
573
573
574 if ip2py:
574 if ip2py:
575 # Convert source input from IPython into valid Python syntax
575 # Convert source input from IPython into valid Python syntax
576 source = self.ip2py(source)
576 source = self.ip2py(source)
577
577
578 # Divide want into lines; check that it's properly indented; and
578 # Divide want into lines; check that it's properly indented; and
579 # then strip the indentation. Spaces before the last newline should
579 # then strip the indentation. Spaces before the last newline should
580 # be preserved, so plain rstrip() isn't good enough.
580 # be preserved, so plain rstrip() isn't good enough.
581 want = m.group('want')
581 want = m.group('want')
582 want_lines = want.split('\n')
582 want_lines = want.split('\n')
583 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
583 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
584 del want_lines[-1] # forget final newline & spaces after it
584 del want_lines[-1] # forget final newline & spaces after it
585 self._check_prefix(want_lines, ' '*indent, name,
585 self._check_prefix(want_lines, ' '*indent, name,
586 lineno + len(source_lines))
586 lineno + len(source_lines))
587
587
588 # Remove ipython output prompt that might be present in the first line
588 # Remove ipython output prompt that might be present in the first line
589 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
589 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
590
590
591 want = '\n'.join([wl[indent:] for wl in want_lines])
591 want = '\n'.join([wl[indent:] for wl in want_lines])
592
592
593 # If `want` contains a traceback message, then extract it.
593 # If `want` contains a traceback message, then extract it.
594 m = self._EXCEPTION_RE.match(want)
594 m = self._EXCEPTION_RE.match(want)
595 if m:
595 if m:
596 exc_msg = m.group('msg')
596 exc_msg = m.group('msg')
597 else:
597 else:
598 exc_msg = None
598 exc_msg = None
599
599
600 # Extract options from the source.
600 # Extract options from the source.
601 options = self._find_options(source, name, lineno)
601 options = self._find_options(source, name, lineno)
602
602
603 return source, options, want, exc_msg
603 return source, options, want, exc_msg
604
604
605 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
605 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
606 """
606 """
607 Given the lines of a source string (including prompts and
607 Given the lines of a source string (including prompts and
608 leading indentation), check to make sure that every prompt is
608 leading indentation), check to make sure that every prompt is
609 followed by a space character. If any line is not followed by
609 followed by a space character. If any line is not followed by
610 a space character, then raise ValueError.
610 a space character, then raise ValueError.
611
611
612 Note: IPython-modified version which takes the input prompt length as a
612 Note: IPython-modified version which takes the input prompt length as a
613 parameter, so that prompts of variable length can be dealt with.
613 parameter, so that prompts of variable length can be dealt with.
614 """
614 """
615 space_idx = indent+ps1_len
615 space_idx = indent+ps1_len
616 min_len = space_idx+1
616 min_len = space_idx+1
617 for i, line in enumerate(lines):
617 for i, line in enumerate(lines):
618 if len(line) >= min_len and line[space_idx] != ' ':
618 if len(line) >= min_len and line[space_idx] != ' ':
619 raise ValueError('line %r of the docstring for %s '
619 raise ValueError('line %r of the docstring for %s '
620 'lacks blank after %s: %r' %
620 'lacks blank after %s: %r' %
621 (lineno+i+1, name,
621 (lineno+i+1, name,
622 line[indent:space_idx], line))
622 line[indent:space_idx], line))
623
623
624
624
625 SKIP = doctest.register_optionflag('SKIP')
625 SKIP = doctest.register_optionflag('SKIP')
626
626
627
627
628 class IPDocTestRunner(doctest.DocTestRunner,object):
628 class IPDocTestRunner(doctest.DocTestRunner,object):
629 """Test runner that synchronizes the IPython namespace with test globals.
629 """Test runner that synchronizes the IPython namespace with test globals.
630 """
630 """
631
631
632 def run(self, test, compileflags=None, out=None, clear_globs=True):
632 def run(self, test, compileflags=None, out=None, clear_globs=True):
633
633
634 # Hack: ipython needs access to the execution context of the example,
634 # Hack: ipython needs access to the execution context of the example,
635 # so that it can propagate user variables loaded by %run into
635 # so that it can propagate user variables loaded by %run into
636 # test.globs. We put them here into our modified %run as a function
636 # test.globs. We put them here into our modified %run as a function
637 # attribute. Our new %run will then only make the namespace update
637 # attribute. Our new %run will then only make the namespace update
638 # when called (rather than unconconditionally updating test.globs here
638 # when called (rather than unconconditionally updating test.globs here
639 # for all examples, most of which won't be calling %run anyway).
639 # for all examples, most of which won't be calling %run anyway).
640 _run_ns_sync.test_globs = test.globs
640 _run_ns_sync.test_globs = test.globs
641
641
642 return super(IPDocTestRunner,self).run(test,
642 return super(IPDocTestRunner,self).run(test,
643 compileflags,out,clear_globs)
643 compileflags,out,clear_globs)
644
644
645
645
646 class DocFileCase(doctest.DocFileCase):
646 class DocFileCase(doctest.DocFileCase):
647 """Overrides to provide filename
647 """Overrides to provide filename
648 """
648 """
649 def address(self):
649 def address(self):
650 return (self._dt_test.filename, None, None)
650 return (self._dt_test.filename, None, None)
651
651
652
652
653 class ExtensionDoctest(doctests.Doctest):
653 class ExtensionDoctest(doctests.Doctest):
654 """Nose Plugin that supports doctests in extension modules.
654 """Nose Plugin that supports doctests in extension modules.
655 """
655 """
656 name = 'extdoctest' # call nosetests with --with-extdoctest
656 name = 'extdoctest' # call nosetests with --with-extdoctest
657 enabled = True
657 enabled = True
658
658
659 def options(self, parser, env=os.environ):
659 def options(self, parser, env=os.environ):
660 Plugin.options(self, parser, env)
660 Plugin.options(self, parser, env)
661 parser.add_option('--doctest-tests', action='store_true',
662 dest='doctest_tests',
663 default=env.get('NOSE_DOCTEST_TESTS',True),
664 help="Also look for doctests in test modules. "
665 "Note that classes, methods and functions should "
666 "have either doctests or non-doctest tests, "
667 "not both. [NOSE_DOCTEST_TESTS]")
668 parser.add_option('--doctest-extension', action="append",
669 dest="doctestExtension",
670 help="Also look for doctests in files with "
671 "this extension [NOSE_DOCTEST_EXTENSION]")
672 # Set the default as a list, if given in env; otherwise
673 # an additional value set on the command line will cause
674 # an error.
675 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
676 if env_setting is not None:
677 parser.set_defaults(doctestExtension=tolist(env_setting))
678
661
679
662 def configure(self, options, config):
680 def configure(self, options, config):
663 Plugin.configure(self, options, config)
681 Plugin.configure(self, options, config)
664 self.doctest_tests = options.doctest_tests
682 self.doctest_tests = options.doctest_tests
665 self.extension = tolist(options.doctestExtension)
683 self.extension = tolist(options.doctestExtension)
666
684
667 self.parser = doctest.DocTestParser()
685 self.parser = doctest.DocTestParser()
668 self.finder = DocTestFinder()
686 self.finder = DocTestFinder()
669 self.checker = IPDoctestOutputChecker()
687 self.checker = IPDoctestOutputChecker()
670 self.globs = None
688 self.globs = None
671 self.extraglobs = None
689 self.extraglobs = None
672
690
673 def loadTestsFromExtensionModule(self,filename):
691 def loadTestsFromExtensionModule(self,filename):
674 bpath,mod = os.path.split(filename)
692 bpath,mod = os.path.split(filename)
675 modname = os.path.splitext(mod)[0]
693 modname = os.path.splitext(mod)[0]
676 try:
694 try:
677 sys.path.append(bpath)
695 sys.path.append(bpath)
678 module = __import__(modname)
696 module = __import__(modname)
679 tests = list(self.loadTestsFromModule(module))
697 tests = list(self.loadTestsFromModule(module))
680 finally:
698 finally:
681 sys.path.pop()
699 sys.path.pop()
682 return tests
700 return tests
683
701
684 # NOTE: the method below is almost a copy of the original one in nose, with
702 # NOTE: the method below is almost a copy of the original one in nose, with
685 # a few modifications to control output checking.
703 # a few modifications to control output checking.
686
704
687 def loadTestsFromModule(self, module):
705 def loadTestsFromModule(self, module):
688 #print 'lTM',module # dbg
706 #print 'lTM',module # dbg
689
707
690 if not self.matches(module.__name__):
708 if not self.matches(module.__name__):
691 log.debug("Doctest doesn't want module %s", module)
709 log.debug("Doctest doesn't want module %s", module)
692 return
710 return
693
711
694 tests = self.finder.find(module,globs=self.globs,
712 tests = self.finder.find(module,globs=self.globs,
695 extraglobs=self.extraglobs)
713 extraglobs=self.extraglobs)
696 if not tests:
714 if not tests:
697 return
715 return
698
716
699 # always use whitespace and ellipsis options
717 # always use whitespace and ellipsis options
700 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
718 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
701
719
702 tests.sort()
720 tests.sort()
703 module_file = module.__file__
721 module_file = module.__file__
704 if module_file[-4:] in ('.pyc', '.pyo'):
722 if module_file[-4:] in ('.pyc', '.pyo'):
705 module_file = module_file[:-1]
723 module_file = module_file[:-1]
706 for test in tests:
724 for test in tests:
707 if not test.examples:
725 if not test.examples:
708 continue
726 continue
709 if not test.filename:
727 if not test.filename:
710 test.filename = module_file
728 test.filename = module_file
711
729
712 yield DocTestCase(test,
730 yield DocTestCase(test,
713 optionflags=optionflags,
731 optionflags=optionflags,
714 checker=self.checker)
732 checker=self.checker)
715
733
716
734
717 def loadTestsFromFile(self, filename):
735 def loadTestsFromFile(self, filename):
718 #print 'lTF',filename # dbg
736 #print 'lTF',filename # dbg
719
737
720 if is_extension_module(filename):
738 if is_extension_module(filename):
721 for t in self.loadTestsFromExtensionModule(filename):
739 for t in self.loadTestsFromExtensionModule(filename):
722 yield t
740 yield t
723 else:
741 else:
724 if self.extension and anyp(filename.endswith, self.extension):
742 if self.extension and anyp(filename.endswith, self.extension):
725 name = os.path.basename(filename)
743 name = os.path.basename(filename)
726 dh = open(filename)
744 dh = open(filename)
727 try:
745 try:
728 doc = dh.read()
746 doc = dh.read()
729 finally:
747 finally:
730 dh.close()
748 dh.close()
731 test = self.parser.get_doctest(
749 test = self.parser.get_doctest(
732 doc, globs={'__file__': filename}, name=name,
750 doc, globs={'__file__': filename}, name=name,
733 filename=filename, lineno=0)
751 filename=filename, lineno=0)
734 if test.examples:
752 if test.examples:
735 #print 'FileCase:',test.examples # dbg
753 #print 'FileCase:',test.examples # dbg
736 yield DocFileCase(test)
754 yield DocFileCase(test)
737 else:
755 else:
738 yield False # no tests to load
756 yield False # no tests to load
739
757
740 def wantFile(self,filename):
758 def wantFile(self,filename):
741 """Return whether the given filename should be scanned for tests.
759 """Return whether the given filename should be scanned for tests.
742
760
743 Modified version that accepts extension modules as valid containers for
761 Modified version that accepts extension modules as valid containers for
744 doctests.
762 doctests.
745 """
763 """
746 #print 'Filename:',filename # dbg
764 print 'Filename:',filename # dbg
747
765
748 # XXX - temporarily hardcoded list, will move to driver later
766 # XXX - temporarily hardcoded list, will move to driver later
749 exclude = ['IPython/external/',
767 exclude = ['IPython/external/',
750 'IPython/Extensions/ipy_',
751 'IPython/platutils_win32',
768 'IPython/platutils_win32',
752 'IPython/frontend/cocoa',
769 'IPython/frontend/cocoa',
753 'IPython_doctest_plugin',
770 'IPython_doctest_plugin',
754 'IPython/Gnuplot',
771 'IPython/Gnuplot',
755 'IPython/Extensions/PhysicalQIn']
772 'IPython/Extensions/ipy_',
773 'IPython/Extensions/PhysicalQIn',
774 'IPython/Extensions/scitedirector',
775 'IPython/testing/plugin',
776 ]
756
777
757 for fex in exclude:
778 for fex in exclude:
758 if fex in filename: # substring
779 if fex in filename: # substring
759 #print '###>>> SKIP:',filename # dbg
780 #print '###>>> SKIP:',filename # dbg
760 return False
781 return False
761
782
762 if is_extension_module(filename):
783 if is_extension_module(filename):
763 return True
784 return True
764 else:
785 else:
765 return doctests.Doctest.wantFile(self,filename)
786 return doctests.Doctest.wantFile(self,filename)
766
787
767
788
768 class IPythonDoctest(ExtensionDoctest):
789 class IPythonDoctest(ExtensionDoctest):
769 """Nose Plugin that supports doctests in extension modules.
790 """Nose Plugin that supports doctests in extension modules.
770 """
791 """
771 name = 'ipdoctest' # call nosetests with --with-ipdoctest
792 name = 'ipdoctest' # call nosetests with --with-ipdoctest
772 enabled = True
793 enabled = True
773
794
774 def configure(self, options, config):
795 def configure(self, options, config):
775
796
776 Plugin.configure(self, options, config)
797 Plugin.configure(self, options, config)
777 self.doctest_tests = options.doctest_tests
798 self.doctest_tests = options.doctest_tests
778 self.extension = tolist(options.doctestExtension)
799 self.extension = tolist(options.doctestExtension)
779
800
780 self.parser = IPDocTestParser()
801 self.parser = IPDocTestParser()
781 self.finder = DocTestFinder(parser=self.parser)
802 self.finder = DocTestFinder(parser=self.parser)
782 self.checker = IPDoctestOutputChecker()
803 self.checker = IPDoctestOutputChecker()
783 self.globs = None
804 self.globs = None
784 self.extraglobs = None
805 self.extraglobs = None
806
@@ -1,180 +1,185 b''
1 # Module imports
1 # Module imports
2 # Std lib
2 # Std lib
3 import inspect
3 import inspect
4
4
5 # Third party
5 # Third party
6
6
7 # Our own
7 # Our own
8 from IPython.testing import decorators as dec
8 from IPython.testing import decorators as dec
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Utilities
11 # Utilities
12
12
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
14 # circular dependencies and is easier to reuse.
14 # circular dependencies and is easier to reuse.
15 def getargspec(obj):
15 def getargspec(obj):
16 """Get the names and default values of a function's arguments.
16 """Get the names and default values of a function's arguments.
17
17
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
19 'args' is a list of the argument names (it may contain nested lists).
19 'args' is a list of the argument names (it may contain nested lists).
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
21 'defaults' is an n-tuple of the default values of the last n arguments.
21 'defaults' is an n-tuple of the default values of the last n arguments.
22
22
23 Modified version of inspect.getargspec from the Python Standard
23 Modified version of inspect.getargspec from the Python Standard
24 Library."""
24 Library."""
25
25
26 if inspect.isfunction(obj):
26 if inspect.isfunction(obj):
27 func_obj = obj
27 func_obj = obj
28 elif inspect.ismethod(obj):
28 elif inspect.ismethod(obj):
29 func_obj = obj.im_func
29 func_obj = obj.im_func
30 else:
30 else:
31 raise TypeError, 'arg is not a Python function'
31 raise TypeError, 'arg is not a Python function'
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
33 return args, varargs, varkw, func_obj.func_defaults
33 return args, varargs, varkw, func_obj.func_defaults
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Testing functions
36 # Testing functions
37
37
38 def test_trivial():
38 def test_trivial():
39 """A trivial passing test."""
39 """A trivial passing test."""
40 pass
40 pass
41
41
42
42
43 @dec.skip
43 @dec.skip
44 def test_deliberately_broken():
44 def test_deliberately_broken():
45 """A deliberately broken test - we want to skip this one."""
45 """A deliberately broken test - we want to skip this one."""
46 1/0
46 1/0
47
47
48 @dec.skip('foo')
49 def test_deliberately_broken2():
50 """Another deliberately broken test - we want to skip this one."""
51 1/0
52
48
53
49 # Verify that we can correctly skip the doctest for a function at will, but
54 # Verify that we can correctly skip the doctest for a function at will, but
50 # that the docstring itself is NOT destroyed by the decorator.
55 # that the docstring itself is NOT destroyed by the decorator.
51 @dec.skip_doctest
56 @dec.skip_doctest
52 def doctest_bad(x,y=1,**k):
57 def doctest_bad(x,y=1,**k):
53 """A function whose doctest we need to skip.
58 """A function whose doctest we need to skip.
54
59
55 >>> 1+1
60 >>> 1+1
56 3
61 3
57 """
62 """
58 print 'x:',x
63 print 'x:',x
59 print 'y:',y
64 print 'y:',y
60 print 'k:',k
65 print 'k:',k
61
66
62
67
63 def call_doctest_bad():
68 def call_doctest_bad():
64 """Check that we can still call the decorated functions.
69 """Check that we can still call the decorated functions.
65
70
66 >>> doctest_bad(3,y=4)
71 >>> doctest_bad(3,y=4)
67 x: 3
72 x: 3
68 y: 4
73 y: 4
69 k: {}
74 k: {}
70 """
75 """
71 pass
76 pass
72
77
73
78
74 # Doctest skipping should work for class methods too
79 # Doctest skipping should work for class methods too
75 class foo(object):
80 class foo(object):
76 """Foo
81 """Foo
77
82
78 Example:
83 Example:
79
84
80 >>> 1+1
85 >>> 1+1
81 2
86 2
82 """
87 """
83
88
84 @dec.skip_doctest
89 @dec.skip_doctest
85 def __init__(self,x):
90 def __init__(self,x):
86 """Make a foo.
91 """Make a foo.
87
92
88 Example:
93 Example:
89
94
90 >>> f = foo(3)
95 >>> f = foo(3)
91 junk
96 junk
92 """
97 """
93 print 'Making a foo.'
98 print 'Making a foo.'
94 self.x = x
99 self.x = x
95
100
96 @dec.skip_doctest
101 @dec.skip_doctest
97 def bar(self,y):
102 def bar(self,y):
98 """Example:
103 """Example:
99
104
100 >>> f = foo(3)
105 >>> f = foo(3)
101 >>> f.bar(0)
106 >>> f.bar(0)
102 boom!
107 boom!
103 >>> 1/0
108 >>> 1/0
104 bam!
109 bam!
105 """
110 """
106 return 1/y
111 return 1/y
107
112
108 def baz(self,y):
113 def baz(self,y):
109 """Example:
114 """Example:
110
115
111 >>> f = foo(3)
116 >>> f = foo(3)
112 Making a foo.
117 Making a foo.
113 >>> f.baz(3)
118 >>> f.baz(3)
114 True
119 True
115 """
120 """
116 return self.x==y
121 return self.x==y
117
122
118
123
119 def test_skip_dt_decorator():
124 def test_skip_dt_decorator():
120 """Doctest-skipping decorator should preserve the docstring.
125 """Doctest-skipping decorator should preserve the docstring.
121 """
126 """
122 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
127 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
123 check = """A function whose doctest we need to skip.
128 check = """A function whose doctest we need to skip.
124
129
125 >>> 1+1
130 >>> 1+1
126 3
131 3
127 """
132 """
128 # Fetch the docstring from doctest_bad after decoration.
133 # Fetch the docstring from doctest_bad after decoration.
129 val = doctest_bad.__doc__
134 val = doctest_bad.__doc__
130
135
131 assert check==val,"doctest_bad docstrings don't match"
136 assert check==val,"doctest_bad docstrings don't match"
132
137
133
138
134 def test_skip_dt_decorator2():
139 def test_skip_dt_decorator2():
135 """Doctest-skipping decorator should preserve function signature.
140 """Doctest-skipping decorator should preserve function signature.
136 """
141 """
137 # Hardcoded correct answer
142 # Hardcoded correct answer
138 dtargs = (['x', 'y'], None, 'k', (1,))
143 dtargs = (['x', 'y'], None, 'k', (1,))
139 # Introspect out the value
144 # Introspect out the value
140 dtargsr = getargspec(doctest_bad)
145 dtargsr = getargspec(doctest_bad)
141 assert dtargsr==dtargs, \
146 assert dtargsr==dtargs, \
142 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
147 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
143
148
144
149
145 def doctest_run():
150 def doctest_run():
146 """Test running a trivial script.
151 """Test running a trivial script.
147
152
148 In [13]: run simplevars.py
153 In [13]: run simplevars.py
149 x is: 1
154 x is: 1
150 """
155 """
151
156
152 #@dec.skip_doctest
157 #@dec.skip_doctest
153 def doctest_runvars():
158 def doctest_runvars():
154 """Test that variables defined in scripts get loaded correcly via %run.
159 """Test that variables defined in scripts get loaded correcly via %run.
155
160
156 In [13]: run simplevars.py
161 In [13]: run simplevars.py
157 x is: 1
162 x is: 1
158
163
159 In [14]: x
164 In [14]: x
160 Out[14]: 1
165 Out[14]: 1
161 """
166 """
162
167
163 def doctest_ivars():
168 def doctest_ivars():
164 """Test that variables defined interactively are picked up.
169 """Test that variables defined interactively are picked up.
165 In [5]: zz=1
170 In [5]: zz=1
166
171
167 In [6]: zz
172 In [6]: zz
168 Out[6]: 1
173 Out[6]: 1
169 """
174 """
170
175
171 @dec.skip_doctest
176 @dec.skip_doctest
172 def doctest_refs():
177 def doctest_refs():
173 """DocTest reference holding issues when running scripts.
178 """DocTest reference holding issues when running scripts.
174
179
175 In [32]: run show_refs.py
180 In [32]: run show_refs.py
176 c referrers: [<type 'dict'>]
181 c referrers: [<type 'dict'>]
177
182
178 In [33]: map(type,gc.get_referrers(c))
183 In [33]: map(type,gc.get_referrers(c))
179 Out[33]: [<type 'dict'>]
184 Out[33]: [<type 'dict'>]
180 """
185 """
@@ -1,86 +1,87 b''
1 """DEPRECATED - use IPython.testing.util instead.
1 """DEPRECATED - use IPython.testing.util instead.
2
2
3 Utilities for testing code.
3 Utilities for testing code.
4 """
4 """
5
5
6 #############################################################################
6 #############################################################################
7
7
8 # This was old testing code we never really used in IPython. The pieces of
8 # This was old testing code we never really used in IPython. The pieces of
9 # testing machinery from snakeoil that were good have already been merged into
9 # testing machinery from snakeoil that were good have already been merged into
10 # the nose plugin, so this can be taken away soon. Leave a warning for now,
10 # the nose plugin, so this can be taken away soon. Leave a warning for now,
11 # we'll remove it in a later release (around 0.10 or so).
11 # we'll remove it in a later release (around 0.10 or so).
12
12 from warnings import warn
13 from warnings import warn
13 warn('This will be removed soon. Use IPython.testing.util instead',
14 warn('This will be removed soon. Use IPython.testing.util instead',
14 DeprecationWarning)
15 DeprecationWarning)
15
16
16 #############################################################################
17 #############################################################################
17
18
18 # Required modules and packages
19 # Required modules and packages
19
20
20 # Standard Python lib
21 # Standard Python lib
21 import os
22 import os
22 import sys
23 import sys
23
24
24 # From this project
25 # From this project
25 from IPython.tools import utils
26 from IPython.tools import utils
26
27
27 # path to our own installation, so we can find source files under this.
28 # path to our own installation, so we can find source files under this.
28 TEST_PATH = os.path.dirname(os.path.abspath(__file__))
29 TEST_PATH = os.path.dirname(os.path.abspath(__file__))
29
30
30 # Global flag, used by vprint
31 # Global flag, used by vprint
31 VERBOSE = '-v' in sys.argv or '--verbose' in sys.argv
32 VERBOSE = '-v' in sys.argv or '--verbose' in sys.argv
32
33
33 ##########################################################################
34 ##########################################################################
34 # Code begins
35 # Code begins
35
36
36 # Some utility functions
37 # Some utility functions
37 def vprint(*args):
38 def vprint(*args):
38 """Print-like function which relies on a global VERBOSE flag."""
39 """Print-like function which relies on a global VERBOSE flag."""
39 if not VERBOSE:
40 if not VERBOSE:
40 return
41 return
41
42
42 write = sys.stdout.write
43 write = sys.stdout.write
43 for item in args:
44 for item in args:
44 write(str(item))
45 write(str(item))
45 write('\n')
46 write('\n')
46 sys.stdout.flush()
47 sys.stdout.flush()
47
48
48 def test_path(path):
49 def test_path(path):
49 """Return a path as a subdir of the test package.
50 """Return a path as a subdir of the test package.
50
51
51 This finds the correct path of the test package on disk, and prepends it
52 This finds the correct path of the test package on disk, and prepends it
52 to the input path."""
53 to the input path."""
53
54
54 return os.path.join(TEST_PATH,path)
55 return os.path.join(TEST_PATH,path)
55
56
56 def fullPath(startPath,files):
57 def fullPath(startPath,files):
57 """Make full paths for all the listed files, based on startPath.
58 """Make full paths for all the listed files, based on startPath.
58
59
59 Only the base part of startPath is kept, since this routine is typically
60 Only the base part of startPath is kept, since this routine is typically
60 used with a script's __file__ variable as startPath. The base of startPath
61 used with a script's __file__ variable as startPath. The base of startPath
61 is then prepended to all the listed files, forming the output list.
62 is then prepended to all the listed files, forming the output list.
62
63
63 :Parameters:
64 :Parameters:
64 startPath : string
65 startPath : string
65 Initial path to use as the base for the results. This path is split
66 Initial path to use as the base for the results. This path is split
66 using os.path.split() and only its first component is kept.
67 using os.path.split() and only its first component is kept.
67
68
68 files : string or list
69 files : string or list
69 One or more files.
70 One or more files.
70
71
71 :Examples:
72 :Examples:
72
73
73 >>> fullPath('/foo/bar.py',['a.txt','b.txt'])
74 >>> fullPath('/foo/bar.py',['a.txt','b.txt'])
74 ['/foo/a.txt', '/foo/b.txt']
75 ['/foo/a.txt', '/foo/b.txt']
75
76
76 >>> fullPath('/foo',['a.txt','b.txt'])
77 >>> fullPath('/foo',['a.txt','b.txt'])
77 ['/a.txt', '/b.txt']
78 ['/a.txt', '/b.txt']
78
79
79 If a single file is given, the output is still a list:
80 If a single file is given, the output is still a list:
80 >>> fullPath('/foo','a.txt')
81 >>> fullPath('/foo','a.txt')
81 ['/a.txt']
82 ['/a.txt']
82 """
83 """
83
84
84 files = utils.list_strings(files)
85 files = utils.list_strings(files)
85 base = os.path.split(startPath)[0]
86 base = os.path.split(startPath)[0]
86 return [ os.path.join(base,f) for f in files ]
87 return [ os.path.join(base,f) for f in files ]
@@ -1,1061 +1,1062 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 ultraTB.py -- Spice up your tracebacks!
3 ultraTB.py -- Spice up your tracebacks!
4
4
5 * ColorTB
5 * ColorTB
6 I've always found it a bit hard to visually parse tracebacks in Python. The
6 I've always found it a bit hard to visually parse tracebacks in Python. The
7 ColorTB class is a solution to that problem. It colors the different parts of a
7 ColorTB class is a solution to that problem. It colors the different parts of a
8 traceback in a manner similar to what you would expect from a syntax-highlighting
8 traceback in a manner similar to what you would expect from a syntax-highlighting
9 text editor.
9 text editor.
10
10
11 Installation instructions for ColorTB:
11 Installation instructions for ColorTB:
12 import sys,ultraTB
12 import sys,ultraTB
13 sys.excepthook = ultraTB.ColorTB()
13 sys.excepthook = ultraTB.ColorTB()
14
14
15 * VerboseTB
15 * VerboseTB
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
16 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
17 of useful info when a traceback occurs. Ping originally had it spit out HTML
18 and intended it for CGI programmers, but why should they have all the fun? I
18 and intended it for CGI programmers, but why should they have all the fun? I
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
19 altered it to spit out colored text to the terminal. It's a bit overwhelming,
20 but kind of neat, and maybe useful for long-running programs that you believe
20 but kind of neat, and maybe useful for long-running programs that you believe
21 are bug-free. If a crash *does* occur in that type of program you want details.
21 are bug-free. If a crash *does* occur in that type of program you want details.
22 Give it a shot--you'll love it or you'll hate it.
22 Give it a shot--you'll love it or you'll hate it.
23
23
24 Note:
24 Note:
25
25
26 The Verbose mode prints the variables currently visible where the exception
26 The Verbose mode prints the variables currently visible where the exception
27 happened (shortening their strings if too long). This can potentially be
27 happened (shortening their strings if too long). This can potentially be
28 very slow, if you happen to have a huge data structure whose string
28 very slow, if you happen to have a huge data structure whose string
29 representation is complex to compute. Your computer may appear to freeze for
29 representation is complex to compute. Your computer may appear to freeze for
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
30 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
31 with Ctrl-C (maybe hitting it more than once).
31 with Ctrl-C (maybe hitting it more than once).
32
32
33 If you encounter this kind of situation often, you may want to use the
33 If you encounter this kind of situation often, you may want to use the
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
34 Verbose_novars mode instead of the regular Verbose, which avoids formatting
35 variables (but otherwise includes the information and context given by
35 variables (but otherwise includes the information and context given by
36 Verbose).
36 Verbose).
37
37
38
38
39 Installation instructions for ColorTB:
39 Installation instructions for ColorTB:
40 import sys,ultraTB
40 import sys,ultraTB
41 sys.excepthook = ultraTB.VerboseTB()
41 sys.excepthook = ultraTB.VerboseTB()
42
42
43 Note: Much of the code in this module was lifted verbatim from the standard
43 Note: Much of the code in this module was lifted verbatim from the standard
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
44 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
45
45
46 * Color schemes
46 * Color schemes
47 The colors are defined in the class TBTools through the use of the
47 The colors are defined in the class TBTools through the use of the
48 ColorSchemeTable class. Currently the following exist:
48 ColorSchemeTable class. Currently the following exist:
49
49
50 - NoColor: allows all of this module to be used in any terminal (the color
50 - NoColor: allows all of this module to be used in any terminal (the color
51 escapes are just dummy blank strings).
51 escapes are just dummy blank strings).
52
52
53 - Linux: is meant to look good in a terminal like the Linux console (black
53 - Linux: is meant to look good in a terminal like the Linux console (black
54 or very dark background).
54 or very dark background).
55
55
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
56 - LightBG: similar to Linux but swaps dark/light colors to be more readable
57 in light background terminals.
57 in light background terminals.
58
58
59 You can implement other color schemes easily, the syntax is fairly
59 You can implement other color schemes easily, the syntax is fairly
60 self-explanatory. Please send back new schemes you develop to the author for
60 self-explanatory. Please send back new schemes you develop to the author for
61 possible inclusion in future releases.
61 possible inclusion in future releases.
62
62
63 $Id: ultraTB.py 2908 2007-12-30 21:07:46Z vivainio $"""
63 $Id: ultraTB.py 2908 2007-12-30 21:07:46Z vivainio $"""
64
64
65 #*****************************************************************************
65 #*****************************************************************************
66 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
66 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
67 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
67 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
68 #
68 #
69 # Distributed under the terms of the BSD License. The full license is in
69 # Distributed under the terms of the BSD License. The full license is in
70 # the file COPYING, distributed as part of this software.
70 # the file COPYING, distributed as part of this software.
71 #*****************************************************************************
71 #*****************************************************************************
72
72
73 from IPython import Release
73 from IPython import Release
74 __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+
74 __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+
75 Release.authors['Fernando'])
75 Release.authors['Fernando'])
76 __license__ = Release.license
76 __license__ = Release.license
77
77
78 # Required modules
78 # Required modules
79 import inspect
79 import inspect
80 import keyword
80 import keyword
81 import linecache
81 import linecache
82 import os
82 import os
83 import pydoc
83 import pydoc
84 import re
84 import re
85 import string
85 import string
86 import sys
86 import sys
87 import time
87 import time
88 import tokenize
88 import tokenize
89 import traceback
89 import traceback
90 import types
90 import types
91
91
92 # For purposes of monkeypatching inspect to fix a bug in it.
92 # For purposes of monkeypatching inspect to fix a bug in it.
93 from inspect import getsourcefile, getfile, getmodule,\
93 from inspect import getsourcefile, getfile, getmodule,\
94 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
94 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
95
95
96
96
97 # IPython's own modules
97 # IPython's own modules
98 # Modified pdb which doesn't damage IPython's readline handling
98 # Modified pdb which doesn't damage IPython's readline handling
99 from IPython import Debugger, PyColorize
99 from IPython import Debugger, PyColorize
100 from IPython.ipstruct import Struct
100 from IPython.ipstruct import Struct
101 from IPython.excolors import ExceptionColors
101 from IPython.excolors import ExceptionColors
102 from IPython.genutils import Term,uniq_stable,error,info
102 from IPython.genutils import Term,uniq_stable,error,info
103
103
104 # Globals
104 # Globals
105 # amount of space to put line numbers before verbose tracebacks
105 # amount of space to put line numbers before verbose tracebacks
106 INDENT_SIZE = 8
106 INDENT_SIZE = 8
107
107
108 # Default color scheme. This is used, for example, by the traceback
108 # Default color scheme. This is used, for example, by the traceback
109 # formatter. When running in an actual IPython instance, the user's rc.colors
109 # formatter. When running in an actual IPython instance, the user's rc.colors
110 # value is used, but havinga module global makes this functionality available
110 # value is used, but havinga module global makes this functionality available
111 # to users of ultraTB who are NOT running inside ipython.
111 # to users of ultraTB who are NOT running inside ipython.
112 DEFAULT_SCHEME = 'NoColor'
112 DEFAULT_SCHEME = 'NoColor'
113
113
114 #---------------------------------------------------------------------------
114 #---------------------------------------------------------------------------
115 # Code begins
115 # Code begins
116
116
117 # Utility functions
117 # Utility functions
118 def inspect_error():
118 def inspect_error():
119 """Print a message about internal inspect errors.
119 """Print a message about internal inspect errors.
120
120
121 These are unfortunately quite common."""
121 These are unfortunately quite common."""
122
122
123 error('Internal Python error in the inspect module.\n'
123 error('Internal Python error in the inspect module.\n'
124 'Below is the traceback from this internal error.\n')
124 'Below is the traceback from this internal error.\n')
125
125
126
126
127 def findsource(object):
127 def findsource(object):
128 """Return the entire source file and starting line number for an object.
128 """Return the entire source file and starting line number for an object.
129
129
130 The argument may be a module, class, method, function, traceback, frame,
130 The argument may be a module, class, method, function, traceback, frame,
131 or code object. The source code is returned as a list of all the lines
131 or code object. The source code is returned as a list of all the lines
132 in the file and the line number indexes a line in that list. An IOError
132 in the file and the line number indexes a line in that list. An IOError
133 is raised if the source code cannot be retrieved.
133 is raised if the source code cannot be retrieved.
134
134
135 FIXED version with which we monkeypatch the stdlib to work around a bug."""
135 FIXED version with which we monkeypatch the stdlib to work around a bug."""
136
136
137 file = getsourcefile(object) or getfile(object)
137 file = getsourcefile(object) or getfile(object)
138 # If the object is a frame, then trying to get the globals dict from its
138 # If the object is a frame, then trying to get the globals dict from its
139 # module won't work. Instead, the frame object itself has the globals
139 # module won't work. Instead, the frame object itself has the globals
140 # dictionary.
140 # dictionary.
141 globals_dict = None
141 globals_dict = None
142 if inspect.isframe(object):
142 if inspect.isframe(object):
143 # XXX: can this ever be false?
143 # XXX: can this ever be false?
144 globals_dict = object.f_globals
144 globals_dict = object.f_globals
145 else:
145 else:
146 module = getmodule(object, file)
146 module = getmodule(object, file)
147 if module:
147 if module:
148 globals_dict = module.__dict__
148 globals_dict = module.__dict__
149 lines = linecache.getlines(file, globals_dict)
149 lines = linecache.getlines(file, globals_dict)
150 if not lines:
150 if not lines:
151 raise IOError('could not get source code')
151 raise IOError('could not get source code')
152
152
153 if ismodule(object):
153 if ismodule(object):
154 return lines, 0
154 return lines, 0
155
155
156 if isclass(object):
156 if isclass(object):
157 name = object.__name__
157 name = object.__name__
158 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
158 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
159 # make some effort to find the best matching class definition:
159 # make some effort to find the best matching class definition:
160 # use the one with the least indentation, which is the one
160 # use the one with the least indentation, which is the one
161 # that's most probably not inside a function definition.
161 # that's most probably not inside a function definition.
162 candidates = []
162 candidates = []
163 for i in range(len(lines)):
163 for i in range(len(lines)):
164 match = pat.match(lines[i])
164 match = pat.match(lines[i])
165 if match:
165 if match:
166 # if it's at toplevel, it's already the best one
166 # if it's at toplevel, it's already the best one
167 if lines[i][0] == 'c':
167 if lines[i][0] == 'c':
168 return lines, i
168 return lines, i
169 # else add whitespace to candidate list
169 # else add whitespace to candidate list
170 candidates.append((match.group(1), i))
170 candidates.append((match.group(1), i))
171 if candidates:
171 if candidates:
172 # this will sort by whitespace, and by line number,
172 # this will sort by whitespace, and by line number,
173 # less whitespace first
173 # less whitespace first
174 candidates.sort()
174 candidates.sort()
175 return lines, candidates[0][1]
175 return lines, candidates[0][1]
176 else:
176 else:
177 raise IOError('could not find class definition')
177 raise IOError('could not find class definition')
178
178
179 if ismethod(object):
179 if ismethod(object):
180 object = object.im_func
180 object = object.im_func
181 if isfunction(object):
181 if isfunction(object):
182 object = object.func_code
182 object = object.func_code
183 if istraceback(object):
183 if istraceback(object):
184 object = object.tb_frame
184 object = object.tb_frame
185 if isframe(object):
185 if isframe(object):
186 object = object.f_code
186 object = object.f_code
187 if iscode(object):
187 if iscode(object):
188 if not hasattr(object, 'co_firstlineno'):
188 if not hasattr(object, 'co_firstlineno'):
189 raise IOError('could not find function definition')
189 raise IOError('could not find function definition')
190 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
190 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
191 pmatch = pat.match
191 pmatch = pat.match
192 # fperez - fix: sometimes, co_firstlineno can give a number larger than
192 # fperez - fix: sometimes, co_firstlineno can give a number larger than
193 # the length of lines, which causes an error. Safeguard against that.
193 # the length of lines, which causes an error. Safeguard against that.
194 lnum = min(object.co_firstlineno,len(lines))-1
194 lnum = min(object.co_firstlineno,len(lines))-1
195 while lnum > 0:
195 while lnum > 0:
196 if pmatch(lines[lnum]): break
196 if pmatch(lines[lnum]): break
197 lnum -= 1
197 lnum -= 1
198
198
199 return lines, lnum
199 return lines, lnum
200 raise IOError('could not find code object')
200 raise IOError('could not find code object')
201
201
202 # Monkeypatch inspect to apply our bugfix. This code only works with py25
202 # Monkeypatch inspect to apply our bugfix. This code only works with py25
203 if sys.version_info[:2] >= (2,5):
203 if sys.version_info[:2] >= (2,5):
204 inspect.findsource = findsource
204 inspect.findsource = findsource
205
205
206 def fix_frame_records_filenames(records):
206 def fix_frame_records_filenames(records):
207 """Try to fix the filenames in each record from inspect.getinnerframes().
207 """Try to fix the filenames in each record from inspect.getinnerframes().
208
208
209 Particularly, modules loaded from within zip files have useless filenames
209 Particularly, modules loaded from within zip files have useless filenames
210 attached to their code object, and inspect.getinnerframes() just uses it.
210 attached to their code object, and inspect.getinnerframes() just uses it.
211 """
211 """
212 fixed_records = []
212 fixed_records = []
213 for frame, filename, line_no, func_name, lines, index in records:
213 for frame, filename, line_no, func_name, lines, index in records:
214 # Look inside the frame's globals dictionary for __file__, which should
214 # Look inside the frame's globals dictionary for __file__, which should
215 # be better.
215 # be better.
216 better_fn = frame.f_globals.get('__file__', None)
216 better_fn = frame.f_globals.get('__file__', None)
217 if isinstance(better_fn, str):
217 if isinstance(better_fn, str):
218 # Check the type just in case someone did something weird with
218 # Check the type just in case someone did something weird with
219 # __file__. It might also be None if the error occurred during
219 # __file__. It might also be None if the error occurred during
220 # import.
220 # import.
221 filename = better_fn
221 filename = better_fn
222 fixed_records.append((frame, filename, line_no, func_name, lines, index))
222 fixed_records.append((frame, filename, line_no, func_name, lines, index))
223 return fixed_records
223 return fixed_records
224
224
225
225
226 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
226 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
227 import linecache
227 import linecache
228 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
228 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
229
229
230 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
230 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
231
231
232 # If the error is at the console, don't build any context, since it would
232 # If the error is at the console, don't build any context, since it would
233 # otherwise produce 5 blank lines printed out (there is no file at the
233 # otherwise produce 5 blank lines printed out (there is no file at the
234 # console)
234 # console)
235 rec_check = records[tb_offset:]
235 rec_check = records[tb_offset:]
236 try:
236 try:
237 rname = rec_check[0][1]
237 rname = rec_check[0][1]
238 if rname == '<ipython console>' or rname.endswith('<string>'):
238 if rname == '<ipython console>' or rname.endswith('<string>'):
239 return rec_check
239 return rec_check
240 except IndexError:
240 except IndexError:
241 pass
241 pass
242
242
243 aux = traceback.extract_tb(etb)
243 aux = traceback.extract_tb(etb)
244 assert len(records) == len(aux)
244 assert len(records) == len(aux)
245 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
245 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
246 maybeStart = lnum-1 - context//2
246 maybeStart = lnum-1 - context//2
247 start = max(maybeStart, 0)
247 start = max(maybeStart, 0)
248 end = start + context
248 end = start + context
249 lines = linecache.getlines(file)[start:end]
249 lines = linecache.getlines(file)[start:end]
250 # pad with empty lines if necessary
250 # pad with empty lines if necessary
251 if maybeStart < 0:
251 if maybeStart < 0:
252 lines = (['\n'] * -maybeStart) + lines
252 lines = (['\n'] * -maybeStart) + lines
253 if len(lines) < context:
253 if len(lines) < context:
254 lines += ['\n'] * (context - len(lines))
254 lines += ['\n'] * (context - len(lines))
255 buf = list(records[i])
255 buf = list(records[i])
256 buf[LNUM_POS] = lnum
256 buf[LNUM_POS] = lnum
257 buf[INDEX_POS] = lnum - 1 - start
257 buf[INDEX_POS] = lnum - 1 - start
258 buf[LINES_POS] = lines
258 buf[LINES_POS] = lines
259 records[i] = tuple(buf)
259 records[i] = tuple(buf)
260 return records[tb_offset:]
260 return records[tb_offset:]
261
261
262 # Helper function -- largely belongs to VerboseTB, but we need the same
262 # Helper function -- largely belongs to VerboseTB, but we need the same
263 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
263 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
264 # can be recognized properly by ipython.el's py-traceback-line-re
264 # can be recognized properly by ipython.el's py-traceback-line-re
265 # (SyntaxErrors have to be treated specially because they have no traceback)
265 # (SyntaxErrors have to be treated specially because they have no traceback)
266
266
267 _parser = PyColorize.Parser()
267 _parser = PyColorize.Parser()
268
268
269 def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):
269 def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):
270 numbers_width = INDENT_SIZE - 1
270 numbers_width = INDENT_SIZE - 1
271 res = []
271 res = []
272 i = lnum - index
272 i = lnum - index
273
273
274 # This lets us get fully syntax-highlighted tracebacks.
274 # This lets us get fully syntax-highlighted tracebacks.
275 if scheme is None:
275 if scheme is None:
276 try:
276 try:
277 scheme = __IPYTHON__.rc.colors
277 scheme = __IPYTHON__.rc.colors
278 except:
278 except:
279 scheme = DEFAULT_SCHEME
279 scheme = DEFAULT_SCHEME
280 _line_format = _parser.format2
280 _line_format = _parser.format2
281
281
282 for line in lines:
282 for line in lines:
283 new_line, err = _line_format(line,'str',scheme)
283 new_line, err = _line_format(line,'str',scheme)
284 if not err: line = new_line
284 if not err: line = new_line
285
285
286 if i == lnum:
286 if i == lnum:
287 # This is the line with the error
287 # This is the line with the error
288 pad = numbers_width - len(str(i))
288 pad = numbers_width - len(str(i))
289 if pad >= 3:
289 if pad >= 3:
290 marker = '-'*(pad-3) + '-> '
290 marker = '-'*(pad-3) + '-> '
291 elif pad == 2:
291 elif pad == 2:
292 marker = '> '
292 marker = '> '
293 elif pad == 1:
293 elif pad == 1:
294 marker = '>'
294 marker = '>'
295 else:
295 else:
296 marker = ''
296 marker = ''
297 num = marker + str(i)
297 num = marker + str(i)
298 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
298 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
299 Colors.line, line, Colors.Normal)
299 Colors.line, line, Colors.Normal)
300 else:
300 else:
301 num = '%*s' % (numbers_width,i)
301 num = '%*s' % (numbers_width,i)
302 line = '%s%s%s %s' %(Colors.lineno, num,
302 line = '%s%s%s %s' %(Colors.lineno, num,
303 Colors.Normal, line)
303 Colors.Normal, line)
304
304
305 res.append(line)
305 res.append(line)
306 if lvals and i == lnum:
306 if lvals and i == lnum:
307 res.append(lvals + '\n')
307 res.append(lvals + '\n')
308 i = i + 1
308 i = i + 1
309 return res
309 return res
310
310
311
311
312 #---------------------------------------------------------------------------
312 #---------------------------------------------------------------------------
313 # Module classes
313 # Module classes
314 class TBTools:
314 class TBTools:
315 """Basic tools used by all traceback printer classes."""
315 """Basic tools used by all traceback printer classes."""
316
316
317 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
317 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
318 # Whether to call the interactive pdb debugger after printing
318 # Whether to call the interactive pdb debugger after printing
319 # tracebacks or not
319 # tracebacks or not
320 self.call_pdb = call_pdb
320 self.call_pdb = call_pdb
321
321
322 # Create color table
322 # Create color table
323 self.color_scheme_table = ExceptionColors
323 self.color_scheme_table = ExceptionColors
324
324
325 self.set_colors(color_scheme)
325 self.set_colors(color_scheme)
326 self.old_scheme = color_scheme # save initial value for toggles
326 self.old_scheme = color_scheme # save initial value for toggles
327
327
328 if call_pdb:
328 if call_pdb:
329 self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name)
329 self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name)
330 else:
330 else:
331 self.pdb = None
331 self.pdb = None
332
332
333 def set_colors(self,*args,**kw):
333 def set_colors(self,*args,**kw):
334 """Shorthand access to the color table scheme selector method."""
334 """Shorthand access to the color table scheme selector method."""
335
335
336 # Set own color table
336 # Set own color table
337 self.color_scheme_table.set_active_scheme(*args,**kw)
337 self.color_scheme_table.set_active_scheme(*args,**kw)
338 # for convenience, set Colors to the active scheme
338 # for convenience, set Colors to the active scheme
339 self.Colors = self.color_scheme_table.active_colors
339 self.Colors = self.color_scheme_table.active_colors
340 # Also set colors of debugger
340 # Also set colors of debugger
341 if hasattr(self,'pdb') and self.pdb is not None:
341 if hasattr(self,'pdb') and self.pdb is not None:
342 self.pdb.set_colors(*args,**kw)
342 self.pdb.set_colors(*args,**kw)
343
343
344 def color_toggle(self):
344 def color_toggle(self):
345 """Toggle between the currently active color scheme and NoColor."""
345 """Toggle between the currently active color scheme and NoColor."""
346
346
347 if self.color_scheme_table.active_scheme_name == 'NoColor':
347 if self.color_scheme_table.active_scheme_name == 'NoColor':
348 self.color_scheme_table.set_active_scheme(self.old_scheme)
348 self.color_scheme_table.set_active_scheme(self.old_scheme)
349 self.Colors = self.color_scheme_table.active_colors
349 self.Colors = self.color_scheme_table.active_colors
350 else:
350 else:
351 self.old_scheme = self.color_scheme_table.active_scheme_name
351 self.old_scheme = self.color_scheme_table.active_scheme_name
352 self.color_scheme_table.set_active_scheme('NoColor')
352 self.color_scheme_table.set_active_scheme('NoColor')
353 self.Colors = self.color_scheme_table.active_colors
353 self.Colors = self.color_scheme_table.active_colors
354
354
355 #---------------------------------------------------------------------------
355 #---------------------------------------------------------------------------
356 class ListTB(TBTools):
356 class ListTB(TBTools):
357 """Print traceback information from a traceback list, with optional color.
357 """Print traceback information from a traceback list, with optional color.
358
358
359 Calling: requires 3 arguments:
359 Calling: requires 3 arguments:
360 (etype, evalue, elist)
360 (etype, evalue, elist)
361 as would be obtained by:
361 as would be obtained by:
362 etype, evalue, tb = sys.exc_info()
362 etype, evalue, tb = sys.exc_info()
363 if tb:
363 if tb:
364 elist = traceback.extract_tb(tb)
364 elist = traceback.extract_tb(tb)
365 else:
365 else:
366 elist = None
366 elist = None
367
367
368 It can thus be used by programs which need to process the traceback before
368 It can thus be used by programs which need to process the traceback before
369 printing (such as console replacements based on the code module from the
369 printing (such as console replacements based on the code module from the
370 standard library).
370 standard library).
371
371
372 Because they are meant to be called without a full traceback (only a
372 Because they are meant to be called without a full traceback (only a
373 list), instances of this class can't call the interactive pdb debugger."""
373 list), instances of this class can't call the interactive pdb debugger."""
374
374
375 def __init__(self,color_scheme = 'NoColor'):
375 def __init__(self,color_scheme = 'NoColor'):
376 TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
376 TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0)
377
377
378 def __call__(self, etype, value, elist):
378 def __call__(self, etype, value, elist):
379 Term.cout.flush()
379 Term.cout.flush()
380 print >> Term.cerr, self.text(etype,value,elist)
380 print >> Term.cerr, self.text(etype,value,elist)
381 Term.cerr.flush()
381 Term.cerr.flush()
382
382
383 def text(self,etype, value, elist,context=5):
383 def text(self,etype, value, elist,context=5):
384 """Return a color formatted string with the traceback info."""
384 """Return a color formatted string with the traceback info."""
385
385
386 Colors = self.Colors
386 Colors = self.Colors
387 out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
387 out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
388 if elist:
388 if elist:
389 out_string.append('Traceback %s(most recent call last)%s:' % \
389 out_string.append('Traceback %s(most recent call last)%s:' % \
390 (Colors.normalEm, Colors.Normal) + '\n')
390 (Colors.normalEm, Colors.Normal) + '\n')
391 out_string.extend(self._format_list(elist))
391 out_string.extend(self._format_list(elist))
392 lines = self._format_exception_only(etype, value)
392 lines = self._format_exception_only(etype, value)
393 for line in lines[:-1]:
393 for line in lines[:-1]:
394 out_string.append(" "+line)
394 out_string.append(" "+line)
395 out_string.append(lines[-1])
395 out_string.append(lines[-1])
396 return ''.join(out_string)
396 return ''.join(out_string)
397
397
398 def _format_list(self, extracted_list):
398 def _format_list(self, extracted_list):
399 """Format a list of traceback entry tuples for printing.
399 """Format a list of traceback entry tuples for printing.
400
400
401 Given a list of tuples as returned by extract_tb() or
401 Given a list of tuples as returned by extract_tb() or
402 extract_stack(), return a list of strings ready for printing.
402 extract_stack(), return a list of strings ready for printing.
403 Each string in the resulting list corresponds to the item with the
403 Each string in the resulting list corresponds to the item with the
404 same index in the argument list. Each string ends in a newline;
404 same index in the argument list. Each string ends in a newline;
405 the strings may contain internal newlines as well, for those items
405 the strings may contain internal newlines as well, for those items
406 whose source text line is not None.
406 whose source text line is not None.
407
407
408 Lifted almost verbatim from traceback.py
408 Lifted almost verbatim from traceback.py
409 """
409 """
410
410
411 Colors = self.Colors
411 Colors = self.Colors
412 list = []
412 list = []
413 for filename, lineno, name, line in extracted_list[:-1]:
413 for filename, lineno, name, line in extracted_list[:-1]:
414 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
414 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
415 (Colors.filename, filename, Colors.Normal,
415 (Colors.filename, filename, Colors.Normal,
416 Colors.lineno, lineno, Colors.Normal,
416 Colors.lineno, lineno, Colors.Normal,
417 Colors.name, name, Colors.Normal)
417 Colors.name, name, Colors.Normal)
418 if line:
418 if line:
419 item = item + ' %s\n' % line.strip()
419 item = item + ' %s\n' % line.strip()
420 list.append(item)
420 list.append(item)
421 # Emphasize the last entry
421 # Emphasize the last entry
422 filename, lineno, name, line = extracted_list[-1]
422 filename, lineno, name, line = extracted_list[-1]
423 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
423 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
424 (Colors.normalEm,
424 (Colors.normalEm,
425 Colors.filenameEm, filename, Colors.normalEm,
425 Colors.filenameEm, filename, Colors.normalEm,
426 Colors.linenoEm, lineno, Colors.normalEm,
426 Colors.linenoEm, lineno, Colors.normalEm,
427 Colors.nameEm, name, Colors.normalEm,
427 Colors.nameEm, name, Colors.normalEm,
428 Colors.Normal)
428 Colors.Normal)
429 if line:
429 if line:
430 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
430 item = item + '%s %s%s\n' % (Colors.line, line.strip(),
431 Colors.Normal)
431 Colors.Normal)
432 list.append(item)
432 list.append(item)
433 return list
433 return list
434
434
435 def _format_exception_only(self, etype, value):
435 def _format_exception_only(self, etype, value):
436 """Format the exception part of a traceback.
436 """Format the exception part of a traceback.
437
437
438 The arguments are the exception type and value such as given by
438 The arguments are the exception type and value such as given by
439 sys.exc_info()[:2]. The return value is a list of strings, each ending
439 sys.exc_info()[:2]. The return value is a list of strings, each ending
440 in a newline. Normally, the list contains a single string; however,
440 in a newline. Normally, the list contains a single string; however,
441 for SyntaxError exceptions, it contains several lines that (when
441 for SyntaxError exceptions, it contains several lines that (when
442 printed) display detailed information about where the syntax error
442 printed) display detailed information about where the syntax error
443 occurred. The message indicating which exception occurred is the
443 occurred. The message indicating which exception occurred is the
444 always last string in the list.
444 always last string in the list.
445
445
446 Also lifted nearly verbatim from traceback.py
446 Also lifted nearly verbatim from traceback.py
447 """
447 """
448
448
449 have_filedata = False
449 Colors = self.Colors
450 Colors = self.Colors
450 list = []
451 list = []
451 try:
452 try:
452 stype = Colors.excName + etype.__name__ + Colors.Normal
453 stype = Colors.excName + etype.__name__ + Colors.Normal
453 except AttributeError:
454 except AttributeError:
454 stype = etype # String exceptions don't get special coloring
455 stype = etype # String exceptions don't get special coloring
455 if value is None:
456 if value is None:
456 list.append( str(stype) + '\n')
457 list.append( str(stype) + '\n')
457 else:
458 else:
458 if etype is SyntaxError:
459 if etype is SyntaxError:
459 try:
460 try:
460 msg, (filename, lineno, offset, line) = value
461 msg, (filename, lineno, offset, line) = value
461 except:
462 except:
462 have_filedata = False
463 have_filedata = False
463 else:
464 else:
464 have_filedata = True
465 have_filedata = True
465 #print 'filename is',filename # dbg
466 #print 'filename is',filename # dbg
466 if not filename: filename = "<string>"
467 if not filename: filename = "<string>"
467 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
468 list.append('%s File %s"%s"%s, line %s%d%s\n' % \
468 (Colors.normalEm,
469 (Colors.normalEm,
469 Colors.filenameEm, filename, Colors.normalEm,
470 Colors.filenameEm, filename, Colors.normalEm,
470 Colors.linenoEm, lineno, Colors.Normal ))
471 Colors.linenoEm, lineno, Colors.Normal ))
471 if line is not None:
472 if line is not None:
472 i = 0
473 i = 0
473 while i < len(line) and line[i].isspace():
474 while i < len(line) and line[i].isspace():
474 i = i+1
475 i = i+1
475 list.append('%s %s%s\n' % (Colors.line,
476 list.append('%s %s%s\n' % (Colors.line,
476 line.strip(),
477 line.strip(),
477 Colors.Normal))
478 Colors.Normal))
478 if offset is not None:
479 if offset is not None:
479 s = ' '
480 s = ' '
480 for c in line[i:offset-1]:
481 for c in line[i:offset-1]:
481 if c.isspace():
482 if c.isspace():
482 s = s + c
483 s = s + c
483 else:
484 else:
484 s = s + ' '
485 s = s + ' '
485 list.append('%s%s^%s\n' % (Colors.caret, s,
486 list.append('%s%s^%s\n' % (Colors.caret, s,
486 Colors.Normal) )
487 Colors.Normal) )
487 value = msg
488 value = msg
488 s = self._some_str(value)
489 s = self._some_str(value)
489 if s:
490 if s:
490 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
491 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
491 Colors.Normal, s))
492 Colors.Normal, s))
492 else:
493 else:
493 list.append('%s\n' % str(stype))
494 list.append('%s\n' % str(stype))
494
495
495 # vds:>>
496 # vds:>>
496 if have_filedata:
497 if have_filedata:
497 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
498 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
498 # vds:<<
499 # vds:<<
499
500
500 return list
501 return list
501
502
502 def _some_str(self, value):
503 def _some_str(self, value):
503 # Lifted from traceback.py
504 # Lifted from traceback.py
504 try:
505 try:
505 return str(value)
506 return str(value)
506 except:
507 except:
507 return '<unprintable %s object>' % type(value).__name__
508 return '<unprintable %s object>' % type(value).__name__
508
509
509 #----------------------------------------------------------------------------
510 #----------------------------------------------------------------------------
510 class VerboseTB(TBTools):
511 class VerboseTB(TBTools):
511 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
512 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
512 of HTML. Requires inspect and pydoc. Crazy, man.
513 of HTML. Requires inspect and pydoc. Crazy, man.
513
514
514 Modified version which optionally strips the topmost entries from the
515 Modified version which optionally strips the topmost entries from the
515 traceback, to be used with alternate interpreters (because their own code
516 traceback, to be used with alternate interpreters (because their own code
516 would appear in the traceback)."""
517 would appear in the traceback)."""
517
518
518 def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0,
519 def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0,
519 call_pdb = 0, include_vars=1):
520 call_pdb = 0, include_vars=1):
520 """Specify traceback offset, headers and color scheme.
521 """Specify traceback offset, headers and color scheme.
521
522
522 Define how many frames to drop from the tracebacks. Calling it with
523 Define how many frames to drop from the tracebacks. Calling it with
523 tb_offset=1 allows use of this handler in interpreters which will have
524 tb_offset=1 allows use of this handler in interpreters which will have
524 their own code at the top of the traceback (VerboseTB will first
525 their own code at the top of the traceback (VerboseTB will first
525 remove that frame before printing the traceback info)."""
526 remove that frame before printing the traceback info)."""
526 TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb)
527 TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb)
527 self.tb_offset = tb_offset
528 self.tb_offset = tb_offset
528 self.long_header = long_header
529 self.long_header = long_header
529 self.include_vars = include_vars
530 self.include_vars = include_vars
530
531
531 def text(self, etype, evalue, etb, context=5):
532 def text(self, etype, evalue, etb, context=5):
532 """Return a nice text document describing the traceback."""
533 """Return a nice text document describing the traceback."""
533
534
534 # some locals
535 # some locals
535 try:
536 try:
536 etype = etype.__name__
537 etype = etype.__name__
537 except AttributeError:
538 except AttributeError:
538 pass
539 pass
539 Colors = self.Colors # just a shorthand + quicker name lookup
540 Colors = self.Colors # just a shorthand + quicker name lookup
540 ColorsNormal = Colors.Normal # used a lot
541 ColorsNormal = Colors.Normal # used a lot
541 col_scheme = self.color_scheme_table.active_scheme_name
542 col_scheme = self.color_scheme_table.active_scheme_name
542 indent = ' '*INDENT_SIZE
543 indent = ' '*INDENT_SIZE
543 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
544 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
544 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
545 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
545 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
546 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
546
547
547 # some internal-use functions
548 # some internal-use functions
548 def text_repr(value):
549 def text_repr(value):
549 """Hopefully pretty robust repr equivalent."""
550 """Hopefully pretty robust repr equivalent."""
550 # this is pretty horrible but should always return *something*
551 # this is pretty horrible but should always return *something*
551 try:
552 try:
552 return pydoc.text.repr(value)
553 return pydoc.text.repr(value)
553 except KeyboardInterrupt:
554 except KeyboardInterrupt:
554 raise
555 raise
555 except:
556 except:
556 try:
557 try:
557 return repr(value)
558 return repr(value)
558 except KeyboardInterrupt:
559 except KeyboardInterrupt:
559 raise
560 raise
560 except:
561 except:
561 try:
562 try:
562 # all still in an except block so we catch
563 # all still in an except block so we catch
563 # getattr raising
564 # getattr raising
564 name = getattr(value, '__name__', None)
565 name = getattr(value, '__name__', None)
565 if name:
566 if name:
566 # ick, recursion
567 # ick, recursion
567 return text_repr(name)
568 return text_repr(name)
568 klass = getattr(value, '__class__', None)
569 klass = getattr(value, '__class__', None)
569 if klass:
570 if klass:
570 return '%s instance' % text_repr(klass)
571 return '%s instance' % text_repr(klass)
571 except KeyboardInterrupt:
572 except KeyboardInterrupt:
572 raise
573 raise
573 except:
574 except:
574 return 'UNRECOVERABLE REPR FAILURE'
575 return 'UNRECOVERABLE REPR FAILURE'
575 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
576 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
576 def nullrepr(value, repr=text_repr): return ''
577 def nullrepr(value, repr=text_repr): return ''
577
578
578 # meat of the code begins
579 # meat of the code begins
579 try:
580 try:
580 etype = etype.__name__
581 etype = etype.__name__
581 except AttributeError:
582 except AttributeError:
582 pass
583 pass
583
584
584 if self.long_header:
585 if self.long_header:
585 # Header with the exception type, python version, and date
586 # Header with the exception type, python version, and date
586 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
587 pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable
587 date = time.ctime(time.time())
588 date = time.ctime(time.time())
588
589
589 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
590 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
590 exc, ' '*(75-len(str(etype))-len(pyver)),
591 exc, ' '*(75-len(str(etype))-len(pyver)),
591 pyver, string.rjust(date, 75) )
592 pyver, string.rjust(date, 75) )
592 head += "\nA problem occured executing Python code. Here is the sequence of function"\
593 head += "\nA problem occured executing Python code. Here is the sequence of function"\
593 "\ncalls leading up to the error, with the most recent (innermost) call last."
594 "\ncalls leading up to the error, with the most recent (innermost) call last."
594 else:
595 else:
595 # Simplified header
596 # Simplified header
596 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
597 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
597 string.rjust('Traceback (most recent call last)',
598 string.rjust('Traceback (most recent call last)',
598 75 - len(str(etype)) ) )
599 75 - len(str(etype)) ) )
599 frames = []
600 frames = []
600 # Flush cache before calling inspect. This helps alleviate some of the
601 # Flush cache before calling inspect. This helps alleviate some of the
601 # problems with python 2.3's inspect.py.
602 # problems with python 2.3's inspect.py.
602 linecache.checkcache()
603 linecache.checkcache()
603 # Drop topmost frames if requested
604 # Drop topmost frames if requested
604 try:
605 try:
605 # Try the default getinnerframes and Alex's: Alex's fixes some
606 # Try the default getinnerframes and Alex's: Alex's fixes some
606 # problems, but it generates empty tracebacks for console errors
607 # problems, but it generates empty tracebacks for console errors
607 # (5 blanks lines) where none should be returned.
608 # (5 blanks lines) where none should be returned.
608 #records = inspect.getinnerframes(etb, context)[self.tb_offset:]
609 #records = inspect.getinnerframes(etb, context)[self.tb_offset:]
609 #print 'python records:', records # dbg
610 #print 'python records:', records # dbg
610 records = _fixed_getinnerframes(etb, context,self.tb_offset)
611 records = _fixed_getinnerframes(etb, context,self.tb_offset)
611 #print 'alex records:', records # dbg
612 #print 'alex records:', records # dbg
612 except:
613 except:
613
614
614 # FIXME: I've been getting many crash reports from python 2.3
615 # FIXME: I've been getting many crash reports from python 2.3
615 # users, traceable to inspect.py. If I can find a small test-case
616 # users, traceable to inspect.py. If I can find a small test-case
616 # to reproduce this, I should either write a better workaround or
617 # to reproduce this, I should either write a better workaround or
617 # file a bug report against inspect (if that's the real problem).
618 # file a bug report against inspect (if that's the real problem).
618 # So far, I haven't been able to find an isolated example to
619 # So far, I haven't been able to find an isolated example to
619 # reproduce the problem.
620 # reproduce the problem.
620 inspect_error()
621 inspect_error()
621 traceback.print_exc(file=Term.cerr)
622 traceback.print_exc(file=Term.cerr)
622 info('\nUnfortunately, your original traceback can not be constructed.\n')
623 info('\nUnfortunately, your original traceback can not be constructed.\n')
623 return ''
624 return ''
624
625
625 # build some color string templates outside these nested loops
626 # build some color string templates outside these nested loops
626 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
627 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
627 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
628 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
628 ColorsNormal)
629 ColorsNormal)
629 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
630 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
630 (Colors.vName, Colors.valEm, ColorsNormal)
631 (Colors.vName, Colors.valEm, ColorsNormal)
631 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
632 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
632 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
633 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
633 Colors.vName, ColorsNormal)
634 Colors.vName, ColorsNormal)
634 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
635 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
635 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
636 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
636 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
637 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
637 ColorsNormal)
638 ColorsNormal)
638
639
639 # now, loop over all records printing context and info
640 # now, loop over all records printing context and info
640 abspath = os.path.abspath
641 abspath = os.path.abspath
641 for frame, file, lnum, func, lines, index in records:
642 for frame, file, lnum, func, lines, index in records:
642 #print '*** record:',file,lnum,func,lines,index # dbg
643 #print '*** record:',file,lnum,func,lines,index # dbg
643 try:
644 try:
644 file = file and abspath(file) or '?'
645 file = file and abspath(file) or '?'
645 except OSError:
646 except OSError:
646 # if file is '<console>' or something not in the filesystem,
647 # if file is '<console>' or something not in the filesystem,
647 # the abspath call will throw an OSError. Just ignore it and
648 # the abspath call will throw an OSError. Just ignore it and
648 # keep the original file string.
649 # keep the original file string.
649 pass
650 pass
650 link = tpl_link % file
651 link = tpl_link % file
651 try:
652 try:
652 args, varargs, varkw, locals = inspect.getargvalues(frame)
653 args, varargs, varkw, locals = inspect.getargvalues(frame)
653 except:
654 except:
654 # This can happen due to a bug in python2.3. We should be
655 # This can happen due to a bug in python2.3. We should be
655 # able to remove this try/except when 2.4 becomes a
656 # able to remove this try/except when 2.4 becomes a
656 # requirement. Bug details at http://python.org/sf/1005466
657 # requirement. Bug details at http://python.org/sf/1005466
657 inspect_error()
658 inspect_error()
658 traceback.print_exc(file=Term.cerr)
659 traceback.print_exc(file=Term.cerr)
659 info("\nIPython's exception reporting continues...\n")
660 info("\nIPython's exception reporting continues...\n")
660
661
661 if func == '?':
662 if func == '?':
662 call = ''
663 call = ''
663 else:
664 else:
664 # Decide whether to include variable details or not
665 # Decide whether to include variable details or not
665 var_repr = self.include_vars and eqrepr or nullrepr
666 var_repr = self.include_vars and eqrepr or nullrepr
666 try:
667 try:
667 call = tpl_call % (func,inspect.formatargvalues(args,
668 call = tpl_call % (func,inspect.formatargvalues(args,
668 varargs, varkw,
669 varargs, varkw,
669 locals,formatvalue=var_repr))
670 locals,formatvalue=var_repr))
670 except KeyError:
671 except KeyError:
671 # Very odd crash from inspect.formatargvalues(). The
672 # Very odd crash from inspect.formatargvalues(). The
672 # scenario under which it appeared was a call to
673 # scenario under which it appeared was a call to
673 # view(array,scale) in NumTut.view.view(), where scale had
674 # view(array,scale) in NumTut.view.view(), where scale had
674 # been defined as a scalar (it should be a tuple). Somehow
675 # been defined as a scalar (it should be a tuple). Somehow
675 # inspect messes up resolving the argument list of view()
676 # inspect messes up resolving the argument list of view()
676 # and barfs out. At some point I should dig into this one
677 # and barfs out. At some point I should dig into this one
677 # and file a bug report about it.
678 # and file a bug report about it.
678 inspect_error()
679 inspect_error()
679 traceback.print_exc(file=Term.cerr)
680 traceback.print_exc(file=Term.cerr)
680 info("\nIPython's exception reporting continues...\n")
681 info("\nIPython's exception reporting continues...\n")
681 call = tpl_call_fail % func
682 call = tpl_call_fail % func
682
683
683 # Initialize a list of names on the current line, which the
684 # Initialize a list of names on the current line, which the
684 # tokenizer below will populate.
685 # tokenizer below will populate.
685 names = []
686 names = []
686
687
687 def tokeneater(token_type, token, start, end, line):
688 def tokeneater(token_type, token, start, end, line):
688 """Stateful tokeneater which builds dotted names.
689 """Stateful tokeneater which builds dotted names.
689
690
690 The list of names it appends to (from the enclosing scope) can
691 The list of names it appends to (from the enclosing scope) can
691 contain repeated composite names. This is unavoidable, since
692 contain repeated composite names. This is unavoidable, since
692 there is no way to disambguate partial dotted structures until
693 there is no way to disambguate partial dotted structures until
693 the full list is known. The caller is responsible for pruning
694 the full list is known. The caller is responsible for pruning
694 the final list of duplicates before using it."""
695 the final list of duplicates before using it."""
695
696
696 # build composite names
697 # build composite names
697 if token == '.':
698 if token == '.':
698 try:
699 try:
699 names[-1] += '.'
700 names[-1] += '.'
700 # store state so the next token is added for x.y.z names
701 # store state so the next token is added for x.y.z names
701 tokeneater.name_cont = True
702 tokeneater.name_cont = True
702 return
703 return
703 except IndexError:
704 except IndexError:
704 pass
705 pass
705 if token_type == tokenize.NAME and token not in keyword.kwlist:
706 if token_type == tokenize.NAME and token not in keyword.kwlist:
706 if tokeneater.name_cont:
707 if tokeneater.name_cont:
707 # Dotted names
708 # Dotted names
708 names[-1] += token
709 names[-1] += token
709 tokeneater.name_cont = False
710 tokeneater.name_cont = False
710 else:
711 else:
711 # Regular new names. We append everything, the caller
712 # Regular new names. We append everything, the caller
712 # will be responsible for pruning the list later. It's
713 # will be responsible for pruning the list later. It's
713 # very tricky to try to prune as we go, b/c composite
714 # very tricky to try to prune as we go, b/c composite
714 # names can fool us. The pruning at the end is easy
715 # names can fool us. The pruning at the end is easy
715 # to do (or the caller can print a list with repeated
716 # to do (or the caller can print a list with repeated
716 # names if so desired.
717 # names if so desired.
717 names.append(token)
718 names.append(token)
718 elif token_type == tokenize.NEWLINE:
719 elif token_type == tokenize.NEWLINE:
719 raise IndexError
720 raise IndexError
720 # we need to store a bit of state in the tokenizer to build
721 # we need to store a bit of state in the tokenizer to build
721 # dotted names
722 # dotted names
722 tokeneater.name_cont = False
723 tokeneater.name_cont = False
723
724
724 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
725 def linereader(file=file, lnum=[lnum], getline=linecache.getline):
725 line = getline(file, lnum[0])
726 line = getline(file, lnum[0])
726 lnum[0] += 1
727 lnum[0] += 1
727 return line
728 return line
728
729
729 # Build the list of names on this line of code where the exception
730 # Build the list of names on this line of code where the exception
730 # occurred.
731 # occurred.
731 try:
732 try:
732 # This builds the names list in-place by capturing it from the
733 # This builds the names list in-place by capturing it from the
733 # enclosing scope.
734 # enclosing scope.
734 tokenize.tokenize(linereader, tokeneater)
735 tokenize.tokenize(linereader, tokeneater)
735 except IndexError:
736 except IndexError:
736 # signals exit of tokenizer
737 # signals exit of tokenizer
737 pass
738 pass
738 except tokenize.TokenError,msg:
739 except tokenize.TokenError,msg:
739 _m = ("An unexpected error occurred while tokenizing input\n"
740 _m = ("An unexpected error occurred while tokenizing input\n"
740 "The following traceback may be corrupted or invalid\n"
741 "The following traceback may be corrupted or invalid\n"
741 "The error message is: %s\n" % msg)
742 "The error message is: %s\n" % msg)
742 error(_m)
743 error(_m)
743
744
744 # prune names list of duplicates, but keep the right order
745 # prune names list of duplicates, but keep the right order
745 unique_names = uniq_stable(names)
746 unique_names = uniq_stable(names)
746
747
747 # Start loop over vars
748 # Start loop over vars
748 lvals = []
749 lvals = []
749 if self.include_vars:
750 if self.include_vars:
750 for name_full in unique_names:
751 for name_full in unique_names:
751 name_base = name_full.split('.',1)[0]
752 name_base = name_full.split('.',1)[0]
752 if name_base in frame.f_code.co_varnames:
753 if name_base in frame.f_code.co_varnames:
753 if locals.has_key(name_base):
754 if locals.has_key(name_base):
754 try:
755 try:
755 value = repr(eval(name_full,locals))
756 value = repr(eval(name_full,locals))
756 except:
757 except:
757 value = undefined
758 value = undefined
758 else:
759 else:
759 value = undefined
760 value = undefined
760 name = tpl_local_var % name_full
761 name = tpl_local_var % name_full
761 else:
762 else:
762 if frame.f_globals.has_key(name_base):
763 if frame.f_globals.has_key(name_base):
763 try:
764 try:
764 value = repr(eval(name_full,frame.f_globals))
765 value = repr(eval(name_full,frame.f_globals))
765 except:
766 except:
766 value = undefined
767 value = undefined
767 else:
768 else:
768 value = undefined
769 value = undefined
769 name = tpl_global_var % name_full
770 name = tpl_global_var % name_full
770 lvals.append(tpl_name_val % (name,value))
771 lvals.append(tpl_name_val % (name,value))
771 if lvals:
772 if lvals:
772 lvals = '%s%s' % (indent,em_normal.join(lvals))
773 lvals = '%s%s' % (indent,em_normal.join(lvals))
773 else:
774 else:
774 lvals = ''
775 lvals = ''
775
776
776 level = '%s %s\n' % (link,call)
777 level = '%s %s\n' % (link,call)
777
778
778 if index is None:
779 if index is None:
779 frames.append(level)
780 frames.append(level)
780 else:
781 else:
781 frames.append('%s%s' % (level,''.join(
782 frames.append('%s%s' % (level,''.join(
782 _formatTracebackLines(lnum,index,lines,Colors,lvals,
783 _formatTracebackLines(lnum,index,lines,Colors,lvals,
783 col_scheme))))
784 col_scheme))))
784
785
785 # Get (safely) a string form of the exception info
786 # Get (safely) a string form of the exception info
786 try:
787 try:
787 etype_str,evalue_str = map(str,(etype,evalue))
788 etype_str,evalue_str = map(str,(etype,evalue))
788 except:
789 except:
789 # User exception is improperly defined.
790 # User exception is improperly defined.
790 etype,evalue = str,sys.exc_info()[:2]
791 etype,evalue = str,sys.exc_info()[:2]
791 etype_str,evalue_str = map(str,(etype,evalue))
792 etype_str,evalue_str = map(str,(etype,evalue))
792 # ... and format it
793 # ... and format it
793 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
794 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
794 ColorsNormal, evalue_str)]
795 ColorsNormal, evalue_str)]
795 if type(evalue) is types.InstanceType:
796 if type(evalue) is types.InstanceType:
796 try:
797 try:
797 names = [w for w in dir(evalue) if isinstance(w, basestring)]
798 names = [w for w in dir(evalue) if isinstance(w, basestring)]
798 except:
799 except:
799 # Every now and then, an object with funny inernals blows up
800 # Every now and then, an object with funny inernals blows up
800 # when dir() is called on it. We do the best we can to report
801 # when dir() is called on it. We do the best we can to report
801 # the problem and continue
802 # the problem and continue
802 _m = '%sException reporting error (object with broken dir())%s:'
803 _m = '%sException reporting error (object with broken dir())%s:'
803 exception.append(_m % (Colors.excName,ColorsNormal))
804 exception.append(_m % (Colors.excName,ColorsNormal))
804 etype_str,evalue_str = map(str,sys.exc_info()[:2])
805 etype_str,evalue_str = map(str,sys.exc_info()[:2])
805 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
806 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
806 ColorsNormal, evalue_str))
807 ColorsNormal, evalue_str))
807 names = []
808 names = []
808 for name in names:
809 for name in names:
809 value = text_repr(getattr(evalue, name))
810 value = text_repr(getattr(evalue, name))
810 exception.append('\n%s%s = %s' % (indent, name, value))
811 exception.append('\n%s%s = %s' % (indent, name, value))
811
812
812 # vds: >>
813 # vds: >>
813 if records:
814 if records:
814 filepath, lnum = records[-1][1:3]
815 filepath, lnum = records[-1][1:3]
815 #print "file:", str(file), "linenb", str(lnum) # dbg
816 #print "file:", str(file), "linenb", str(lnum) # dbg
816 filepath = os.path.abspath(filepath)
817 filepath = os.path.abspath(filepath)
817 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
818 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
818 # vds: <<
819 # vds: <<
819
820
820 # return all our info assembled as a single string
821 # return all our info assembled as a single string
821 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
822 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
822
823
823 def debugger(self,force=False):
824 def debugger(self,force=False):
824 """Call up the pdb debugger if desired, always clean up the tb
825 """Call up the pdb debugger if desired, always clean up the tb
825 reference.
826 reference.
826
827
827 Keywords:
828 Keywords:
828
829
829 - force(False): by default, this routine checks the instance call_pdb
830 - force(False): by default, this routine checks the instance call_pdb
830 flag and does not actually invoke the debugger if the flag is false.
831 flag and does not actually invoke the debugger if the flag is false.
831 The 'force' option forces the debugger to activate even if the flag
832 The 'force' option forces the debugger to activate even if the flag
832 is false.
833 is false.
833
834
834 If the call_pdb flag is set, the pdb interactive debugger is
835 If the call_pdb flag is set, the pdb interactive debugger is
835 invoked. In all cases, the self.tb reference to the current traceback
836 invoked. In all cases, the self.tb reference to the current traceback
836 is deleted to prevent lingering references which hamper memory
837 is deleted to prevent lingering references which hamper memory
837 management.
838 management.
838
839
839 Note that each call to pdb() does an 'import readline', so if your app
840 Note that each call to pdb() does an 'import readline', so if your app
840 requires a special setup for the readline completers, you'll have to
841 requires a special setup for the readline completers, you'll have to
841 fix that by hand after invoking the exception handler."""
842 fix that by hand after invoking the exception handler."""
842
843
843 if force or self.call_pdb:
844 if force or self.call_pdb:
844 if self.pdb is None:
845 if self.pdb is None:
845 self.pdb = Debugger.Pdb(
846 self.pdb = Debugger.Pdb(
846 self.color_scheme_table.active_scheme_name)
847 self.color_scheme_table.active_scheme_name)
847 # the system displayhook may have changed, restore the original
848 # the system displayhook may have changed, restore the original
848 # for pdb
849 # for pdb
849 dhook = sys.displayhook
850 dhook = sys.displayhook
850 sys.displayhook = sys.__displayhook__
851 sys.displayhook = sys.__displayhook__
851 self.pdb.reset()
852 self.pdb.reset()
852 # Find the right frame so we don't pop up inside ipython itself
853 # Find the right frame so we don't pop up inside ipython itself
853 if hasattr(self,'tb'):
854 if hasattr(self,'tb'):
854 etb = self.tb
855 etb = self.tb
855 else:
856 else:
856 etb = self.tb = sys.last_traceback
857 etb = self.tb = sys.last_traceback
857 while self.tb.tb_next is not None:
858 while self.tb.tb_next is not None:
858 self.tb = self.tb.tb_next
859 self.tb = self.tb.tb_next
859 try:
860 try:
860 if etb and etb.tb_next:
861 if etb and etb.tb_next:
861 etb = etb.tb_next
862 etb = etb.tb_next
862 self.pdb.botframe = etb.tb_frame
863 self.pdb.botframe = etb.tb_frame
863 self.pdb.interaction(self.tb.tb_frame, self.tb)
864 self.pdb.interaction(self.tb.tb_frame, self.tb)
864 finally:
865 finally:
865 sys.displayhook = dhook
866 sys.displayhook = dhook
866
867
867 if hasattr(self,'tb'):
868 if hasattr(self,'tb'):
868 del self.tb
869 del self.tb
869
870
870 def handler(self, info=None):
871 def handler(self, info=None):
871 (etype, evalue, etb) = info or sys.exc_info()
872 (etype, evalue, etb) = info or sys.exc_info()
872 self.tb = etb
873 self.tb = etb
873 Term.cout.flush()
874 Term.cout.flush()
874 print >> Term.cerr, self.text(etype, evalue, etb)
875 print >> Term.cerr, self.text(etype, evalue, etb)
875 Term.cerr.flush()
876 Term.cerr.flush()
876
877
877 # Changed so an instance can just be called as VerboseTB_inst() and print
878 # Changed so an instance can just be called as VerboseTB_inst() and print
878 # out the right info on its own.
879 # out the right info on its own.
879 def __call__(self, etype=None, evalue=None, etb=None):
880 def __call__(self, etype=None, evalue=None, etb=None):
880 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
881 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
881 if etb is None:
882 if etb is None:
882 self.handler()
883 self.handler()
883 else:
884 else:
884 self.handler((etype, evalue, etb))
885 self.handler((etype, evalue, etb))
885 try:
886 try:
886 self.debugger()
887 self.debugger()
887 except KeyboardInterrupt:
888 except KeyboardInterrupt:
888 print "\nKeyboardInterrupt"
889 print "\nKeyboardInterrupt"
889
890
890 #----------------------------------------------------------------------------
891 #----------------------------------------------------------------------------
891 class FormattedTB(VerboseTB,ListTB):
892 class FormattedTB(VerboseTB,ListTB):
892 """Subclass ListTB but allow calling with a traceback.
893 """Subclass ListTB but allow calling with a traceback.
893
894
894 It can thus be used as a sys.excepthook for Python > 2.1.
895 It can thus be used as a sys.excepthook for Python > 2.1.
895
896
896 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
897 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
897
898
898 Allows a tb_offset to be specified. This is useful for situations where
899 Allows a tb_offset to be specified. This is useful for situations where
899 one needs to remove a number of topmost frames from the traceback (such as
900 one needs to remove a number of topmost frames from the traceback (such as
900 occurs with python programs that themselves execute other python code,
901 occurs with python programs that themselves execute other python code,
901 like Python shells). """
902 like Python shells). """
902
903
903 def __init__(self, mode = 'Plain', color_scheme='Linux',
904 def __init__(self, mode = 'Plain', color_scheme='Linux',
904 tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
905 tb_offset = 0,long_header=0,call_pdb=0,include_vars=0):
905
906
906 # NEVER change the order of this list. Put new modes at the end:
907 # NEVER change the order of this list. Put new modes at the end:
907 self.valid_modes = ['Plain','Context','Verbose']
908 self.valid_modes = ['Plain','Context','Verbose']
908 self.verbose_modes = self.valid_modes[1:3]
909 self.verbose_modes = self.valid_modes[1:3]
909
910
910 VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
911 VerboseTB.__init__(self,color_scheme,tb_offset,long_header,
911 call_pdb=call_pdb,include_vars=include_vars)
912 call_pdb=call_pdb,include_vars=include_vars)
912 self.set_mode(mode)
913 self.set_mode(mode)
913
914
914 def _extract_tb(self,tb):
915 def _extract_tb(self,tb):
915 if tb:
916 if tb:
916 return traceback.extract_tb(tb)
917 return traceback.extract_tb(tb)
917 else:
918 else:
918 return None
919 return None
919
920
920 def text(self, etype, value, tb,context=5,mode=None):
921 def text(self, etype, value, tb,context=5,mode=None):
921 """Return formatted traceback.
922 """Return formatted traceback.
922
923
923 If the optional mode parameter is given, it overrides the current
924 If the optional mode parameter is given, it overrides the current
924 mode."""
925 mode."""
925
926
926 if mode is None:
927 if mode is None:
927 mode = self.mode
928 mode = self.mode
928 if mode in self.verbose_modes:
929 if mode in self.verbose_modes:
929 # verbose modes need a full traceback
930 # verbose modes need a full traceback
930 return VerboseTB.text(self,etype, value, tb,context=5)
931 return VerboseTB.text(self,etype, value, tb,context=5)
931 else:
932 else:
932 # We must check the source cache because otherwise we can print
933 # We must check the source cache because otherwise we can print
933 # out-of-date source code.
934 # out-of-date source code.
934 linecache.checkcache()
935 linecache.checkcache()
935 # Now we can extract and format the exception
936 # Now we can extract and format the exception
936 elist = self._extract_tb(tb)
937 elist = self._extract_tb(tb)
937 if len(elist) > self.tb_offset:
938 if len(elist) > self.tb_offset:
938 del elist[:self.tb_offset]
939 del elist[:self.tb_offset]
939 return ListTB.text(self,etype,value,elist)
940 return ListTB.text(self,etype,value,elist)
940
941
941 def set_mode(self,mode=None):
942 def set_mode(self,mode=None):
942 """Switch to the desired mode.
943 """Switch to the desired mode.
943
944
944 If mode is not specified, cycles through the available modes."""
945 If mode is not specified, cycles through the available modes."""
945
946
946 if not mode:
947 if not mode:
947 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
948 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
948 len(self.valid_modes)
949 len(self.valid_modes)
949 self.mode = self.valid_modes[new_idx]
950 self.mode = self.valid_modes[new_idx]
950 elif mode not in self.valid_modes:
951 elif mode not in self.valid_modes:
951 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
952 raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\
952 'Valid modes: '+str(self.valid_modes)
953 'Valid modes: '+str(self.valid_modes)
953 else:
954 else:
954 self.mode = mode
955 self.mode = mode
955 # include variable details only in 'Verbose' mode
956 # include variable details only in 'Verbose' mode
956 self.include_vars = (self.mode == self.valid_modes[2])
957 self.include_vars = (self.mode == self.valid_modes[2])
957
958
958 # some convenient shorcuts
959 # some convenient shorcuts
959 def plain(self):
960 def plain(self):
960 self.set_mode(self.valid_modes[0])
961 self.set_mode(self.valid_modes[0])
961
962
962 def context(self):
963 def context(self):
963 self.set_mode(self.valid_modes[1])
964 self.set_mode(self.valid_modes[1])
964
965
965 def verbose(self):
966 def verbose(self):
966 self.set_mode(self.valid_modes[2])
967 self.set_mode(self.valid_modes[2])
967
968
968 #----------------------------------------------------------------------------
969 #----------------------------------------------------------------------------
969 class AutoFormattedTB(FormattedTB):
970 class AutoFormattedTB(FormattedTB):
970 """A traceback printer which can be called on the fly.
971 """A traceback printer which can be called on the fly.
971
972
972 It will find out about exceptions by itself.
973 It will find out about exceptions by itself.
973
974
974 A brief example:
975 A brief example:
975
976
976 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
977 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
977 try:
978 try:
978 ...
979 ...
979 except:
980 except:
980 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
981 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
981 """
982 """
982 def __call__(self,etype=None,evalue=None,etb=None,
983 def __call__(self,etype=None,evalue=None,etb=None,
983 out=None,tb_offset=None):
984 out=None,tb_offset=None):
984 """Print out a formatted exception traceback.
985 """Print out a formatted exception traceback.
985
986
986 Optional arguments:
987 Optional arguments:
987 - out: an open file-like object to direct output to.
988 - out: an open file-like object to direct output to.
988
989
989 - tb_offset: the number of frames to skip over in the stack, on a
990 - tb_offset: the number of frames to skip over in the stack, on a
990 per-call basis (this overrides temporarily the instance's tb_offset
991 per-call basis (this overrides temporarily the instance's tb_offset
991 given at initialization time. """
992 given at initialization time. """
992
993
993 if out is None:
994 if out is None:
994 out = Term.cerr
995 out = Term.cerr
995 Term.cout.flush()
996 Term.cout.flush()
996 if tb_offset is not None:
997 if tb_offset is not None:
997 tb_offset, self.tb_offset = self.tb_offset, tb_offset
998 tb_offset, self.tb_offset = self.tb_offset, tb_offset
998 print >> out, self.text(etype, evalue, etb)
999 print >> out, self.text(etype, evalue, etb)
999 self.tb_offset = tb_offset
1000 self.tb_offset = tb_offset
1000 else:
1001 else:
1001 print >> out, self.text(etype, evalue, etb)
1002 print >> out, self.text(etype, evalue, etb)
1002 out.flush()
1003 out.flush()
1003 try:
1004 try:
1004 self.debugger()
1005 self.debugger()
1005 except KeyboardInterrupt:
1006 except KeyboardInterrupt:
1006 print "\nKeyboardInterrupt"
1007 print "\nKeyboardInterrupt"
1007
1008
1008 def text(self,etype=None,value=None,tb=None,context=5,mode=None):
1009 def text(self,etype=None,value=None,tb=None,context=5,mode=None):
1009 if etype is None:
1010 if etype is None:
1010 etype,value,tb = sys.exc_info()
1011 etype,value,tb = sys.exc_info()
1011 self.tb = tb
1012 self.tb = tb
1012 return FormattedTB.text(self,etype,value,tb,context=5,mode=mode)
1013 return FormattedTB.text(self,etype,value,tb,context=5,mode=mode)
1013
1014
1014 #---------------------------------------------------------------------------
1015 #---------------------------------------------------------------------------
1015 # A simple class to preserve Nathan's original functionality.
1016 # A simple class to preserve Nathan's original functionality.
1016 class ColorTB(FormattedTB):
1017 class ColorTB(FormattedTB):
1017 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1018 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1018 def __init__(self,color_scheme='Linux',call_pdb=0):
1019 def __init__(self,color_scheme='Linux',call_pdb=0):
1019 FormattedTB.__init__(self,color_scheme=color_scheme,
1020 FormattedTB.__init__(self,color_scheme=color_scheme,
1020 call_pdb=call_pdb)
1021 call_pdb=call_pdb)
1021
1022
1022 #----------------------------------------------------------------------------
1023 #----------------------------------------------------------------------------
1023 # module testing (minimal)
1024 # module testing (minimal)
1024 if __name__ == "__main__":
1025 if __name__ == "__main__":
1025 def spam(c, (d, e)):
1026 def spam(c, (d, e)):
1026 x = c + d
1027 x = c + d
1027 y = c * d
1028 y = c * d
1028 foo(x, y)
1029 foo(x, y)
1029
1030
1030 def foo(a, b, bar=1):
1031 def foo(a, b, bar=1):
1031 eggs(a, b + bar)
1032 eggs(a, b + bar)
1032
1033
1033 def eggs(f, g, z=globals()):
1034 def eggs(f, g, z=globals()):
1034 h = f + g
1035 h = f + g
1035 i = f - g
1036 i = f - g
1036 return h / i
1037 return h / i
1037
1038
1038 print ''
1039 print ''
1039 print '*** Before ***'
1040 print '*** Before ***'
1040 try:
1041 try:
1041 print spam(1, (2, 3))
1042 print spam(1, (2, 3))
1042 except:
1043 except:
1043 traceback.print_exc()
1044 traceback.print_exc()
1044 print ''
1045 print ''
1045
1046
1046 handler = ColorTB()
1047 handler = ColorTB()
1047 print '*** ColorTB ***'
1048 print '*** ColorTB ***'
1048 try:
1049 try:
1049 print spam(1, (2, 3))
1050 print spam(1, (2, 3))
1050 except:
1051 except:
1051 apply(handler, sys.exc_info() )
1052 apply(handler, sys.exc_info() )
1052 print ''
1053 print ''
1053
1054
1054 handler = VerboseTB()
1055 handler = VerboseTB()
1055 print '*** VerboseTB ***'
1056 print '*** VerboseTB ***'
1056 try:
1057 try:
1057 print spam(1, (2, 3))
1058 print spam(1, (2, 3))
1058 except:
1059 except:
1059 apply(handler, sys.exc_info() )
1060 apply(handler, sys.exc_info() )
1060 print ''
1061 print ''
1061
1062
@@ -1,391 +1,397 b''
1 .. _development:
1 .. _development:
2
2
3 ==================================
3 ==================================
4 IPython development guidelines
4 IPython development guidelines
5 ==================================
5 ==================================
6
6
7 .. contents::
7 .. contents::
8
8
9
9
10 Overview
10 Overview
11 ========
11 ========
12
12
13 IPython is the next generation of IPython. It is named such for two reasons:
13 IPython is the next generation of IPython. It is named such for two reasons:
14
14
15 - Eventually, IPython will become IPython version 1.0.
15 - Eventually, IPython will become IPython version 1.0.
16 - This new code base needs to be able to co-exist with the existing IPython until
16 - This new code base needs to be able to co-exist with the existing IPython until
17 it is a full replacement for it. Thus we needed a different name. We couldn't
17 it is a full replacement for it. Thus we needed a different name. We couldn't
18 use ``ipython`` (lowercase) as some files systems are case insensitive.
18 use ``ipython`` (lowercase) as some files systems are case insensitive.
19
19
20 There are two, no three, main goals of the IPython effort:
20 There are two, no three, main goals of the IPython effort:
21
21
22 1. Clean up the existing codebase and write lots of tests.
22 1. Clean up the existing codebase and write lots of tests.
23 2. Separate the core functionality of IPython from the terminal to enable IPython
23 2. Separate the core functionality of IPython from the terminal to enable IPython
24 to be used from within a variety of GUI applications.
24 to be used from within a variety of GUI applications.
25 3. Implement a system for interactive parallel computing.
25 3. Implement a system for interactive parallel computing.
26
26
27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
28 out that the technologies required for this goal are nearly identical with those
28 out that the technologies required for this goal are nearly identical with those
29 required for goal two. This is the main reason the interactive parallel computing
29 required for goal two. This is the main reason the interactive parallel computing
30 capabilities are being put into IPython proper. Currently the third of these goals is
30 capabilities are being put into IPython proper. Currently the third of these goals is
31 furthest along.
31 furthest along.
32
32
33 This document describes IPython from the perspective of developers.
33 This document describes IPython from the perspective of developers.
34
34
35
35
36 Project organization
36 Project organization
37 ====================
37 ====================
38
38
39 Subpackages
39 Subpackages
40 -----------
40 -----------
41
41
42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
43
43
44 - **Dependencies**. One of the most important things to keep in mind in
44 - **Dependencies**. One of the most important things to keep in mind in
45 partitioning code amongst subpackages, is that they should be used to cleanly
45 partitioning code amongst subpackages, is that they should be used to cleanly
46 encapsulate dependencies.
46 encapsulate dependencies.
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
48 contains all of the tests for that package. For information about writing tests
48 contains all of the tests for that package. For information about writing tests
49 for IPython, see the `Testing System`_ section of this document.
49 for IPython, see the `Testing System`_ section of this document.
50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
51 that contains the configuration information for the components of the
51 that contains the configuration information for the components of the
52 subpackage. For information about how the IPython configuration system
52 subpackage. For information about how the IPython configuration system
53 works, see the `Configuration System`_ section of this document.
53 works, see the `Configuration System`_ section of this document.
54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
55 contains all of the command line scripts associated with the subpackage.
55 contains all of the command line scripts associated with the subpackage.
56
56
57 Installation and dependencies
57 Installation and dependencies
58 -----------------------------
58 -----------------------------
59
59
60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
62 features that `setuptools`_ has (like namespace packages), the current implementation
62 features that `setuptools`_ has (like namespace packages), the current implementation
63 of `setuptools`_ has performance problems, particularly on shared file systems. In
63 of `setuptools`_ has performance problems, particularly on shared file systems. In
64 particular, when Python packages are installed on NSF file systems, import times
64 particular, when Python packages are installed on NSF file systems, import times
65 become much too long (up towards 10 seconds).
65 become much too long (up towards 10 seconds).
66
66
67 Because IPython is being used extensively in the context of high performance
67 Because IPython is being used extensively in the context of high performance
68 computing, where performance is critical but shared file systems are common, we feel
68 computing, where performance is critical but shared file systems are common, we feel
69 these performance hits are not acceptable. Thus, until the performance problems
69 these performance hits are not acceptable. Thus, until the performance problems
70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
71 are hopeful that these problems will be addressed and that we will eventually begin
71 are hopeful that these problems will be addressed and that we will eventually begin
72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
73 will make the eventual transition to `setuptools`_ as painless as possible.
73 will make the eventual transition to `setuptools`_ as painless as possible.
74
74
75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
76
76
77 - Distinguish between required and optional dependencies. However, the required
77 - Distinguish between required and optional dependencies. However, the required
78 dependencies for IPython should be only the Python standard library.
78 dependencies for IPython should be only the Python standard library.
79 - Upon installation check to see which optional dependencies are present and tell
79 - Upon installation check to see which optional dependencies are present and tell
80 the user which parts of IPython need which optional dependencies.
80 the user which parts of IPython need which optional dependencies.
81
81
82 It is absolutely critical that each subpackage of IPython has a clearly specified set
82 It is absolutely critical that each subpackage of IPython has a clearly specified set
83 of dependencies and that dependencies are not carelessly inherited from other IPython
83 of dependencies and that dependencies are not carelessly inherited from other IPython
84 subpackages. Furthermore, tests that have certain dependencies should not fail if
84 subpackages. Furthermore, tests that have certain dependencies should not fail if
85 those dependencies are not present. Instead they should be skipped and print a
85 those dependencies are not present. Instead they should be skipped and print a
86 message.
86 message.
87
87
88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
89 .. _distutils: http://docs.python.org/lib/module-distutils.html
89 .. _distutils: http://docs.python.org/lib/module-distutils.html
90 .. _Matplotlib: http://matplotlib.sourceforge.net/
90 .. _Matplotlib: http://matplotlib.sourceforge.net/
91
91
92 Specific subpackages
92 Specific subpackages
93 --------------------
93 --------------------
94
94
95 ``core``
95 ``core``
96 This is the core functionality of IPython that is independent of the
96 This is the core functionality of IPython that is independent of the
97 terminal, network and GUIs. Most of the code that is in the current
97 terminal, network and GUIs. Most of the code that is in the current
98 IPython trunk will be refactored, cleaned up and moved here.
98 IPython trunk will be refactored, cleaned up and moved here.
99
99
100 ``kernel``
100 ``kernel``
101 The enables the IPython core to be expose to a the network. This is
101 The enables the IPython core to be expose to a the network. This is
102 also where all of the parallel computing capabilities are to be found.
102 also where all of the parallel computing capabilities are to be found.
103
103
104 ``config``
104 ``config``
105 The configuration package used by IPython.
105 The configuration package used by IPython.
106
106
107 ``frontends``
107 ``frontends``
108 The various frontends for IPython. A frontend is the end-user application
108 The various frontends for IPython. A frontend is the end-user application
109 that exposes the capabilities of IPython to the user. The most basic frontend
109 that exposes the capabilities of IPython to the user. The most basic frontend
110 will simply be a terminal based application that looks just like today 's
110 will simply be a terminal based application that looks just like today 's
111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
112
112
113 ``notebook``
113 ``notebook``
114 An application that allows users to work with IPython notebooks.
114 An application that allows users to work with IPython notebooks.
115
115
116 ``tools``
116 ``tools``
117 This is where general utilities go.
117 This is where general utilities go.
118
118
119
119
120 Version control
120 Version control
121 ===============
121 ===============
122
122
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 development. First, you should install Bazaar. After you have done that, make
125 development. First, you should install Bazaar. After you have done that, make
126 sure that it is working by getting the latest main branch of IPython::
126 sure that it is working by getting the latest main branch of IPython::
127
127
128 $ bzr branch lp:ipython
128 $ bzr branch lp:ipython
129
129
130 Now you can create a new branch for you to do your work in::
130 Now you can create a new branch for you to do your work in::
131
131
132 $ bzr branch ipython ipython-mybranch
132 $ bzr branch ipython ipython-mybranch
133
133
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
135 and then commit those changes using the commit command::
135 and then commit those changes using the commit command::
136
136
137 $ ...do work in ipython-mybranch...
137 $ ...do work in ipython-mybranch...
138 $ bzr ci -m "the commit message goes here"
138 $ bzr ci -m "the commit message goes here"
139
139
140 Please note that since we now don't use an old-style linear ChangeLog
140 Please note that since we now don't use an old-style linear ChangeLog
141 (that tends to cause problems with distributed version control
141 (that tends to cause problems with distributed version control
142 systems), you should ensure that your log messages are reasonably
142 systems), you should ensure that your log messages are reasonably
143 detailed. Use a docstring-like approach in the commit messages
143 detailed. Use a docstring-like approach in the commit messages
144 (including the second line being left *blank*)::
144 (including the second line being left *blank*)::
145
145
146 Single line summary of changes being committed.
146 Single line summary of changes being committed.
147
147
148 - more details when warranted ...
148 - more details when warranted ...
149 - including crediting outside contributors if they sent the
149 - including crediting outside contributors if they sent the
150 code/bug/idea!
150 code/bug/idea!
151
151
152 If we couple this with a policy of making single commits for each
152 If we couple this with a policy of making single commits for each
153 reasonably atomic change, the bzr log should give an excellent view of
153 reasonably atomic change, the bzr log should give an excellent view of
154 the project, and the `--short` log option becomes a nice summary.
154 the project, and the `--short` log option becomes a nice summary.
155
155
156 While working with this branch, it is a good idea to merge in changes that have been
156 While working with this branch, it is a good idea to merge in changes that have been
157 made upstream in the parent branch. This can be done by doing::
157 made upstream in the parent branch. This can be done by doing::
158
158
159 $ bzr pull
159 $ bzr pull
160
160
161 If this command shows that the branches have diverged, then you should do a merge
161 If this command shows that the branches have diverged, then you should do a merge
162 instead::
162 instead::
163
163
164 $ bzr merge lp:ipython
164 $ bzr merge lp:ipython
165
165
166 If you want others to be able to see your branch, you can create an account with
166 If you want others to be able to see your branch, you can create an account with
167 launchpad and push the branch to your own workspace::
167 launchpad and push the branch to your own workspace::
168
168
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
170
170
171 Finally, once the work in your branch is done, you can merge your changes back into
171 Finally, once the work in your branch is done, you can merge your changes back into
172 the `ipython` branch by using merge::
172 the `ipython` branch by using merge::
173
173
174 $ cd ipython
174 $ cd ipython
175 $ merge ../ipython-mybranch
175 $ merge ../ipython-mybranch
176 [resolve any conflicts]
176 [resolve any conflicts]
177 $ bzr ci -m "Fixing that bug"
177 $ bzr ci -m "Fixing that bug"
178 $ bzr push
178 $ bzr push
179
179
180 But this will require you to have write permissions to the `ipython` branch. It you don't
180 But this will require you to have write permissions to the `ipython` branch. It you don't
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
181 you can tell one of the IPython devs about your branch and they can do the merge for you.
182
182
183 More information about Bazaar workflows can be found `here`__.
183 More information about Bazaar workflows can be found `here`__.
184
184
185 .. __: http://subversion.tigris.org/
185 .. __: http://subversion.tigris.org/
186 .. __: http://bazaar-vcs.org/
186 .. __: http://bazaar-vcs.org/
187 .. __: http://www.launchpad.net/ipython
187 .. __: http://www.launchpad.net/ipython
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
189
189
190 Documentation
190 Documentation
191 =============
191 =============
192
192
193 Standalone documentation
193 Standalone documentation
194 ------------------------
194 ------------------------
195
195
196 All standalone documentation should be written in plain text (``.txt``) files using
196 All standalone documentation should be written in plain text (``.txt``) files using
197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
199 a suitably named subdirectory should be used. The documentation in this location will
199 a suitably named subdirectory should be used. The documentation in this location will
200 serve as the main source for IPython documentation and all existing documentation
200 serve as the main source for IPython documentation and all existing documentation
201 should be converted to this format.
201 should be converted to this format.
202
202
203 In the future, the text files in the ``docs`` directory will be used to generate all
203 In the future, the text files in the ``docs`` directory will be used to generate all
204 forms of documentation for IPython. This include documentation on the IPython website
204 forms of documentation for IPython. This include documentation on the IPython website
205 as well as *pdf* documentation.
205 as well as *pdf* documentation.
206
206
207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
208
208
209 Docstring format
209 Docstring format
210 ----------------
210 ----------------
211
211
212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
215 variety of tools. This means that if in the future we have any reason to change from
215 variety of tools. This means that if in the future we have any reason to change from
216 `Epydoc`_ to something else, we'll have fewer transition pains.
216 `Epydoc`_ to something else, we'll have fewer transition pains.
217
217
218 Details about using `reStructuredText`_ for docstrings can be found `here
218 Details about using `reStructuredText`_ for docstrings can be found `here
219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
220
220
221 .. _Epydoc: http://epydoc.sourceforge.net/
221 .. _Epydoc: http://epydoc.sourceforge.net/
222
222
223 Additional PEPs of interest regarding documentation of code:
223 Additional PEPs of interest regarding documentation of code:
224
224
225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
228
228
229
229
230 Coding conventions
230 Coding conventions
231 ==================
231 ==================
232
232
233 General
233 General
234 -------
234 -------
235
235
236 In general, we'll try to follow the standard Python style conventions as described here:
236 In general, we'll try to follow the standard Python style conventions as described here:
237
237
238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
239
239
240
240
241 Other comments:
241 Other comments:
242
242
243 - In a large file, top level classes and functions should be
243 - In a large file, top level classes and functions should be
244 separated by 2-3 lines to make it easier to separate them visually.
244 separated by 2-3 lines to make it easier to separate them visually.
245 - Use 4 spaces for indentation.
245 - Use 4 spaces for indentation.
246 - Keep the ordering of methods the same in classes that have the same
246 - Keep the ordering of methods the same in classes that have the same
247 methods. This is particularly true for classes that implement
247 methods. This is particularly true for classes that implement
248 similar interfaces and for interfaces that are similar.
248 similar interfaces and for interfaces that are similar.
249
249
250 Naming conventions
250 Naming conventions
251 ------------------
251 ------------------
252
252
253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
254 Python Code`_.
254 Python Code`_.
255
255
256 For all new IPython code (and much existing code is being refactored), we'll use:
256 For all new IPython code (and much existing code is being refactored), we'll use:
257
257
258 - All ``lowercase`` module names.
258 - All ``lowercase`` module names.
259
259
260 - ``CamelCase`` for class names.
260 - ``CamelCase`` for class names.
261
261
262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
263
263
264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
265 convention, providing shadow names for backward compatibility in public interfaces.
265 convention, providing shadow names for backward compatibility in public interfaces.
266
266
267 There are, however, some important exceptions to these rules. In some cases, IPython
267 There are, however, some important exceptions to these rules. In some cases, IPython
268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
269
269
270 - If you are subclassing a class that uses different conventions, use its
270 - If you are subclassing a class that uses different conventions, use its
271 naming conventions throughout your subclass. Thus, if you are creating a
271 naming conventions throughout your subclass. Thus, if you are creating a
272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
273
273
274 - All IPython's official interfaces should use our conventions. In some cases
274 - All IPython's official interfaces should use our conventions. In some cases
275 this will mean that you need to provide shadow names (first implement ``fooBar``
275 this will mean that you need to provide shadow names (first implement ``fooBar``
276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
277 will probably be necessary at times. But, please use this sparingly!
277 will probably be necessary at times. But, please use this sparingly!
278
278
279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
280 Names with a leading double underscore will *only* be used in special cases, as they
280 Names with a leading double underscore will *only* be used in special cases, as they
281 makes subclassing difficult (such names are not easily seen by child classes).
281 makes subclassing difficult (such names are not easily seen by child classes).
282
282
283 Occasionally some run-in lowercase names are used, but mostly for very short names or
283 Occasionally some run-in lowercase names are used, but mostly for very short names or
284 where we are implementing methods very similar to existing ones in a base class (like
284 where we are implementing methods very similar to existing ones in a base class (like
285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
286
286
287 The old IPython codebase has a big mix of classes and modules prefixed with an
287 The old IPython codebase has a big mix of classes and modules prefixed with an
288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
289 namespaces offer cleaner prefixing. The only case where this approach is justified is
289 namespaces offer cleaner prefixing. The only case where this approach is justified is
290 for classes which are expected to be imported into external namespaces and a very
290 for classes which are expected to be imported into external namespaces and a very
291 generic name (like Shell) is too likely to clash with something else. We'll need to
291 generic name (like Shell) is too likely to clash with something else. We'll need to
292 revisit this issue as we clean up and refactor the code, but in general we should
292 revisit this issue as we clean up and refactor the code, but in general we should
293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
295
295
296 .. _devel_testing:
296 .. _devel_testing:
297
297
298 Testing system
298 Testing system
299 ==============
299 ==============
300
300
301 It is extremely important that all code contributed to IPython has tests. Tests should
301 It is extremely important that all code contributed to IPython has tests. Tests should
302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
305 not be required to simply use IPython.
305 not be required to simply use IPython.
306
306
307 .. _Nose: http://code.google.com/p/python-nose/
307 .. _Nose: http://code.google.com/p/python-nose/
308
308
309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
311 run the tests and the twisted reactor will be handled correctly.
311 run the tests and the twisted reactor will be handled correctly.
312
312
313 .. __: http://www.twistedmatrix.com
313 .. __: http://www.twistedmatrix.com
314
314
315 Each subpackage in IPython should have its own ``tests`` directory that contains all
315 Each subpackage in IPython should have its own ``tests`` directory that contains all
316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
317 a subpackage has any dependencies beyond the Python standard library, the tests for
317 a subpackage has any dependencies beyond the Python standard library, the tests for
318 that subpackage should be skipped if the dependencies are not found. This is very
318 that subpackage should be skipped if the dependencies are not found. This is very
319 important so users don't get tests failing simply because they don't have dependencies.
319 important so users don't get tests failing simply because they don't have dependencies.
320
320
321 We also need to look into use Noses ability to tag tests to allow a more modular
321 We also need to look into use Noses ability to tag tests to allow a more modular
322 approach of running tests.
322 approach of running tests.
323
323
324 .. _devel_config:
324 .. _devel_config:
325
325
326 Configuration system
326 Configuration system
327 ====================
327 ====================
328
328
329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
330 improvement over the configuration system used in IPython. IPython works with these
330 improvement over the configuration system used in IPython. IPython works with these
331 files using the `ConfigObj`_ package, which IPython includes as
331 files using the `ConfigObj`_ package, which IPython includes as
332 ``ipython1/external/configobj.py``.
332 ``ipython1/external/configobj.py``.
333
333
334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
335 should contain a ``config`` subdirectory that contains all of the configuration
335 should contain a ``config`` subdirectory that contains all of the configuration
336 information for the subpackage. To see how configuration information is defined (along
336 information for the subpackage. To see how configuration information is defined (along
337 with defaults) see at the examples in ``ipython1/kernel/config`` and
337 with defaults) see at the examples in ``ipython1/kernel/config`` and
338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
340
340
341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
343 We won't actually use `Traits`_, but will implement something similar in pure Python.
343 We won't actually use `Traits`_, but will implement something similar in pure Python.
344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
345 underneath the hood. Talk to Fernando if you are interested in working on this part of
345 underneath the hood. Talk to Fernando if you are interested in working on this part of
346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
347
347
348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
350 .. _Traits: http://code.enthought.com/traits/
350 .. _Traits: http://code.enthought.com/traits/
351
351
352 Installation and testing scenarios
352 Installation and testing scenarios
353 ==================================
353 ==================================
354
354
355 This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies.
355 This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies.
356
356
357 Installation scenarios
357 Installation scenarios under Linux and OS X
358 ----------------------
358 -------------------------------------------
359
359
360 1. Install from tarball using `python setup.py install`.
360 1. Install from tarball using `python setup.py install`.
361 a. With only readline+nose dependencies installed.
361 a. With only readline+nose dependencies installed.
362 b. With all dependencies installed (readline, zope.interface,
362 b. With all dependencies installed (readline, zope.interface,
363 Twisted, foolscap, Sphinx, nose, pyOpenSSL).
363 Twisted, foolscap, Sphinx, nose, pyOpenSSL).
364 2. Install using easy_install.
364 2. Install using easy_install.
365 a. With only readline+nose dependencies installed.
365 a. With only readline+nose dependencies installed.
366 i. Default dependencies: `easy_install ipython-0.9.beta3-py2.5.egg`
366 i. Default dependencies: `easy_install ipython-0.9.beta3-py2.5.egg`
367 ii. Optional dependency sets: `easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]`
367 ii. Optional dependency sets: `easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]`
368 b. With all dependencies already installed.
368 b. With all dependencies already installed.
369
369
370 Installation scenarios under Win32
371 ----------------------------------
372
373 1. Install everything from .exe installers
374 2. easy_install?
375
370
376
371 Tests to run for these scenarios
377 Tests to run for these scenarios
372 --------------------------------
378 --------------------------------
373
379
374 1. Run the full test suite.
380 1. Run the full test suite.
375 2. Start a controller and engines and try a few things by hand.
381 2. Start a controller and engines and try a few things by hand.
376 a. Using ipcluster.
382 a. Using ipcluster.
377 b. Using ipcontroller/ipengine by hand.
383 b. Using ipcontroller/ipengine by hand.
378 3. Run a few of the parallel examples.
384 3. Run a few of the parallel examples.
379 4. Try the kernel with and without security with and without PyOpenSSL
385 4. Try the kernel with and without security with and without PyOpenSSL
380 installed.
386 installed.
381 5. Beat on the IPython terminal a bunch.
387 5. Beat on the IPython terminal a bunch.
382 6. Make sure that furl files are being put in proper locations.
388 6. Make sure that furl files are being put in proper locations.
383
389
384
390
385
391
386
392
387
393
388
394
389
395
390
396
391
397
@@ -1,179 +1,178 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 """Setup script for IPython.
3 """Setup script for IPython.
4
4
5 Under Posix environments it works like a typical setup.py script.
5 Under Posix environments it works like a typical setup.py script.
6 Under Windows, the command sdist is not supported, since IPython
6 Under Windows, the command sdist is not supported, since IPython
7 requires utilities which are not available under Windows."""
7 requires utilities which are not available under Windows."""
8
8
9 #-------------------------------------------------------------------------------
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
10 # Copyright (C) 2008 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15
15
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 # Stdlib imports
20 # Stdlib imports
21 import os
21 import os
22 import sys
22 import sys
23
23
24 from glob import glob
24 from glob import glob
25
25
26 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
26 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
27 # update it when the contents of directories change.
27 # update it when the contents of directories change.
28 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
28 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
29
29
30 from distutils.core import setup
30 from distutils.core import setup
31
31
32 # Local imports
32 # Local imports
33 from IPython.genutils import target_update
33 from IPython.genutils import target_update
34
34
35 from setupbase import (
35 from setupbase import (
36 setup_args,
36 setup_args,
37 find_packages,
37 find_packages,
38 find_package_data,
38 find_package_data,
39 find_scripts,
39 find_scripts,
40 find_data_files,
40 find_data_files,
41 check_for_dependencies
41 check_for_dependencies
42 )
42 )
43
43
44 isfile = os.path.isfile
44 isfile = os.path.isfile
45
45
46 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
47 # Handle OS specific things
47 # Handle OS specific things
48 #-------------------------------------------------------------------------------
48 #-------------------------------------------------------------------------------
49
49
50 if os.name == 'posix':
50 if os.name == 'posix':
51 os_name = 'posix'
51 os_name = 'posix'
52 elif os.name in ['nt','dos']:
52 elif os.name in ['nt','dos']:
53 os_name = 'windows'
53 os_name = 'windows'
54 else:
54 else:
55 print 'Unsupported operating system:',os.name
55 print 'Unsupported operating system:',os.name
56 sys.exit(1)
56 sys.exit(1)
57
57
58 # Under Windows, 'sdist' has not been supported. Now that the docs build with
58 # Under Windows, 'sdist' has not been supported. Now that the docs build with
59 # Sphinx it might work, but let's not turn it on until someone confirms that it
59 # Sphinx it might work, but let's not turn it on until someone confirms that it
60 # actually works.
60 # actually works.
61 if os_name == 'windows' and 'sdist' in sys.argv:
61 if os_name == 'windows' and 'sdist' in sys.argv:
62 print 'The sdist command is not available under Windows. Exiting.'
62 print 'The sdist command is not available under Windows. Exiting.'
63 sys.exit(1)
63 sys.exit(1)
64
64
65 #-------------------------------------------------------------------------------
65 #-------------------------------------------------------------------------------
66 # Things related to the IPython documentation
66 # Things related to the IPython documentation
67 #-------------------------------------------------------------------------------
67 #-------------------------------------------------------------------------------
68
68
69 # update the manuals when building a source dist
69 # update the manuals when building a source dist
70 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
70 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
71 import textwrap
71 import textwrap
72
72
73 # List of things to be updated. Each entry is a triplet of args for
73 # List of things to be updated. Each entry is a triplet of args for
74 # target_update()
74 # target_update()
75 to_update = [
75 to_update = [
76 # FIXME - Disabled for now: we need to redo an automatic way
76 # FIXME - Disabled for now: we need to redo an automatic way
77 # of generating the magic info inside the rst.
77 # of generating the magic info inside the rst.
78 #('docs/magic.tex',
78 #('docs/magic.tex',
79 #['IPython/Magic.py'],
79 #['IPython/Magic.py'],
80 #"cd doc && ./update_magic.sh" ),
80 #"cd doc && ./update_magic.sh" ),
81
81
82 ('docs/man/ipython.1.gz',
82 ('docs/man/ipython.1.gz',
83 ['docs/man/ipython.1'],
83 ['docs/man/ipython.1'],
84 "cd docs/man && gzip -9c ipython.1 > ipython.1.gz"),
84 "cd docs/man && gzip -9c ipython.1 > ipython.1.gz"),
85
85
86 ('docs/man/pycolor.1.gz',
86 ('docs/man/pycolor.1.gz',
87 ['docs/man/pycolor.1'],
87 ['docs/man/pycolor.1'],
88 "cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz"),
88 "cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz"),
89 ]
89 ]
90
90
91 # Only build the docs is sphinx is present
91 # Only build the docs is sphinx is present
92 try:
92 try:
93 import sphinx
93 import sphinx
94 except ImportError:
94 except ImportError:
95 pass
95 pass
96 else:
96 else:
97 pass
97 pass
98 # BEG: This is disabled as I am not sure what to depend on.
98 # BEG: This is disabled as I am not sure what to depend on.
99 # I actually don't think we should be automatically building
99 # I actually don't think we should be automatically building
100 # the docs for people.
100 # the docs for people.
101 # The do_sphinx scripts builds html and pdf, so just one
101 # The do_sphinx scripts builds html and pdf, so just one
102 # target is enough to cover all manual generation
102 # target is enough to cover all manual generation
103 # to_update.append(
103 # to_update.append(
104 # ('docs/manual/ipython.pdf',
104 # ('docs/manual/ipython.pdf',
105 # ['IPython/Release.py','docs/source/ipython.rst'],
105 # ['IPython/Release.py','docs/source/ipython.rst'],
106 # "cd docs && python do_sphinx.py")
106 # "cd docs && python do_sphinx.py")
107 # )
107 # )
108
108
109 [ target_update(*t) for t in to_update ]
109 [ target_update(*t) for t in to_update ]
110
110
111 # Build the docs
111 # Build the docs
112 os.system('cd docs && make dist')
112 os.system('cd docs && make dist')
113
113
114 #---------------------------------------------------------------------------
114 #---------------------------------------------------------------------------
115 # Find all the packages, package data, scripts and data_files
115 # Find all the packages, package data, scripts and data_files
116 #---------------------------------------------------------------------------
116 #---------------------------------------------------------------------------
117
117
118 packages = find_packages()
118 packages = find_packages()
119 package_data = find_package_data()
119 package_data = find_package_data()
120 scripts = find_scripts()
120 scripts = find_scripts()
121 data_files = find_data_files()
121 data_files = find_data_files()
122
122
123 #---------------------------------------------------------------------------
123 #---------------------------------------------------------------------------
124 # Handle dependencies and setuptools specific things
124 # Handle dependencies and setuptools specific things
125 #---------------------------------------------------------------------------
125 #---------------------------------------------------------------------------
126
126
127 # This dict is used for passing extra arguments that are setuptools
127 # This dict is used for passing extra arguments that are setuptools
128 # specific to setup
128 # specific to setup
129 setuptools_extra_args = {}
129 setuptools_extra_args = {}
130
130
131 if 'setuptools' in sys.modules:
131 if 'setuptools' in sys.modules:
132 setuptools_extra_args['zip_safe'] = False
132 setuptools_extra_args['zip_safe'] = False
133 setuptools_extra_args['entry_points'] = {
133 setuptools_extra_args['entry_points'] = {
134 'console_scripts': [
134 'console_scripts': [
135 'ipython = IPython.ipapi:launch_new_instance',
135 'ipython = IPython.ipapi:launch_new_instance',
136 'pycolor = IPython.PyColorize:main',
136 'pycolor = IPython.PyColorize:main',
137 'ipcontroller = IPython.kernel.scripts.ipcontroller:main',
137 'ipcontroller = IPython.kernel.scripts.ipcontroller:main',
138 'ipengine = IPython.kernel.scripts.ipengine:main',
138 'ipengine = IPython.kernel.scripts.ipengine:main',
139 'ipcluster = IPython.kernel.scripts.ipcluster:main',
139 'ipcluster = IPython.kernel.scripts.ipcluster:main',
140 'ipythonx = IPython.frontend.wx.ipythonx:main'
140 'ipythonx = IPython.frontend.wx.ipythonx:main',
141 'iptest = IPython.testing.iptest:main',
141 ]
142 ]
142 }
143 }
143 setup_args["extras_require"] = dict(
144 setup_args['extras_require'] = dict(
144 kernel = [
145 kernel = [
145 "zope.interface>=3.4.1",
146 'zope.interface>=3.4.1',
146 "Twisted>=8.0.1",
147 'Twisted>=8.0.1',
147 "foolscap>=0.2.6"
148 'foolscap>=0.2.6'
148 ],
149 ],
149 doc=['Sphinx>=0.3','pygments'],
150 doc='Sphinx>=0.3',
150 test='nose>=0.10.1',
151 test='nose>=0.10.1',
151 security=["pyOpenSSL>=0.6"]
152 security='pyOpenSSL>=0.6'
152 )
153 )
153 # Allow setuptools to handle the scripts
154 # Allow setuptools to handle the scripts
154 scripts = []
155 scripts = []
155 # eggs will lack docs, examples
156 data_files = []
157 else:
156 else:
158 # package_data of setuptools was introduced to distutils in 2.4
157 # package_data of setuptools was introduced to distutils in 2.4
159 cfgfiles = filter(isfile, glob('IPython/UserConfig/*'))
158 cfgfiles = filter(isfile, glob('IPython/UserConfig/*'))
160 if sys.version_info < (2,4):
159 if sys.version_info < (2,4):
161 data_files.append(('lib', 'IPython/UserConfig', cfgfiles))
160 data_files.append(('lib', 'IPython/UserConfig', cfgfiles))
162 # If we are running without setuptools, call this function which will
161 # If we are running without setuptools, call this function which will
163 # check for dependencies an inform the user what is needed. This is
162 # check for dependencies an inform the user what is needed. This is
164 # just to make life easy for users.
163 # just to make life easy for users.
165 check_for_dependencies()
164 check_for_dependencies()
166
165
167
166
168 #---------------------------------------------------------------------------
167 #---------------------------------------------------------------------------
169 # Do the actual setup now
168 # Do the actual setup now
170 #---------------------------------------------------------------------------
169 #---------------------------------------------------------------------------
171
170
172 setup_args['packages'] = packages
171 setup_args['packages'] = packages
173 setup_args['package_data'] = package_data
172 setup_args['package_data'] = package_data
174 setup_args['scripts'] = scripts
173 setup_args['scripts'] = scripts
175 setup_args['data_files'] = data_files
174 setup_args['data_files'] = data_files
176 setup_args.update(setuptools_extra_args)
175 setup_args.update(setuptools_extra_args)
177
176
178 if __name__ == '__main__':
177 if __name__ == '__main__':
179 setup(**setup_args)
178 setup(**setup_args)
@@ -1,275 +1,277 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """
3 """
4 This module defines the things that are used in setup.py for building IPython
4 This module defines the things that are used in setup.py for building IPython
5
5
6 This includes:
6 This includes:
7
7
8 * The basic arguments to setup
8 * The basic arguments to setup
9 * Functions for finding things like packages, package data, etc.
9 * Functions for finding things like packages, package data, etc.
10 * A function for checking dependencies.
10 * A function for checking dependencies.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 import os, sys
26 import os, sys
27
27
28 from glob import glob
28 from glob import glob
29
29
30 from setupext import install_data_ext
30 from setupext import install_data_ext
31
31
32 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
33 # Useful globals and utility functions
33 # Useful globals and utility functions
34 #-------------------------------------------------------------------------------
34 #-------------------------------------------------------------------------------
35
35
36 # A few handy globals
36 # A few handy globals
37 isfile = os.path.isfile
37 isfile = os.path.isfile
38 pjoin = os.path.join
38 pjoin = os.path.join
39
39
40 def oscmd(s):
40 def oscmd(s):
41 print ">", s
41 print ">", s
42 os.system(s)
42 os.system(s)
43
43
44 # A little utility we'll need below, since glob() does NOT allow you to do
44 # A little utility we'll need below, since glob() does NOT allow you to do
45 # exclusion on multiple endings!
45 # exclusion on multiple endings!
46 def file_doesnt_endwith(test,endings):
46 def file_doesnt_endwith(test,endings):
47 """Return true if test is a file and its name does NOT end with any
47 """Return true if test is a file and its name does NOT end with any
48 of the strings listed in endings."""
48 of the strings listed in endings."""
49 if not isfile(test):
49 if not isfile(test):
50 return False
50 return False
51 for e in endings:
51 for e in endings:
52 if test.endswith(e):
52 if test.endswith(e):
53 return False
53 return False
54 return True
54 return True
55
55
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57 # Basic project information
57 # Basic project information
58 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
59
59
60 # Release.py contains version, authors, license, url, keywords, etc.
60 # Release.py contains version, authors, license, url, keywords, etc.
61 execfile(pjoin('IPython','Release.py'))
61 execfile(pjoin('IPython','Release.py'))
62
62
63 # Create a dict with the basic information
63 # Create a dict with the basic information
64 # This dict is eventually passed to setup after additional keys are added.
64 # This dict is eventually passed to setup after additional keys are added.
65 setup_args = dict(
65 setup_args = dict(
66 name = name,
66 name = name,
67 version = version,
67 version = version,
68 description = description,
68 description = description,
69 long_description = long_description,
69 long_description = long_description,
70 author = author,
70 author = author,
71 author_email = author_email,
71 author_email = author_email,
72 url = url,
72 url = url,
73 download_url = download_url,
73 download_url = download_url,
74 license = license,
74 license = license,
75 platforms = platforms,
75 platforms = platforms,
76 keywords = keywords,
76 keywords = keywords,
77 cmdclass = {'install_data': install_data_ext},
77 cmdclass = {'install_data': install_data_ext},
78 )
78 )
79
79
80
80
81 #---------------------------------------------------------------------------
81 #---------------------------------------------------------------------------
82 # Find packages
82 # Find packages
83 #---------------------------------------------------------------------------
83 #---------------------------------------------------------------------------
84
84
85 def add_package(packages,pname,config=False,tests=False,scripts=False,
85 def add_package(packages,pname,config=False,tests=False,scripts=False,
86 others=None):
86 others=None):
87 """
87 """
88 Add a package to the list of packages, including certain subpackages.
88 Add a package to the list of packages, including certain subpackages.
89 """
89 """
90 packages.append('.'.join(['IPython',pname]))
90 packages.append('.'.join(['IPython',pname]))
91 if config:
91 if config:
92 packages.append('.'.join(['IPython',pname,'config']))
92 packages.append('.'.join(['IPython',pname,'config']))
93 if tests:
93 if tests:
94 packages.append('.'.join(['IPython',pname,'tests']))
94 packages.append('.'.join(['IPython',pname,'tests']))
95 if scripts:
95 if scripts:
96 packages.append('.'.join(['IPython',pname,'scripts']))
96 packages.append('.'.join(['IPython',pname,'scripts']))
97 if others is not None:
97 if others is not None:
98 for o in others:
98 for o in others:
99 packages.append('.'.join(['IPython',pname,o]))
99 packages.append('.'.join(['IPython',pname,o]))
100
100
101 def find_packages():
101 def find_packages():
102 """
102 """
103 Find all of IPython's packages.
103 Find all of IPython's packages.
104 """
104 """
105 packages = ['IPython']
105 packages = ['IPython']
106 add_package(packages, 'config', tests=True)
106 add_package(packages, 'config', tests=True)
107 add_package(packages , 'Extensions')
107 add_package(packages , 'Extensions')
108 add_package(packages, 'external')
108 add_package(packages, 'external')
109 add_package(packages, 'gui')
109 add_package(packages, 'gui')
110 add_package(packages, 'gui.wx')
110 add_package(packages, 'gui.wx')
111 add_package(packages, 'frontend', tests=True)
111 add_package(packages, 'frontend', tests=True)
112 add_package(packages, 'frontend._process')
112 add_package(packages, 'frontend._process')
113 add_package(packages, 'frontend.wx')
113 add_package(packages, 'frontend.wx')
114 add_package(packages, 'frontend.cocoa', tests=True)
114 add_package(packages, 'frontend.cocoa', tests=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
116 add_package(packages, 'kernel.core', config=True, tests=True)
116 add_package(packages, 'kernel.core', config=True, tests=True)
117 add_package(packages, 'testing', tests=True)
117 add_package(packages, 'testing', tests=True)
118 add_package(packages, 'testing.plugin', tests=False)
118 add_package(packages, 'tools', tests=True)
119 add_package(packages, 'tools', tests=True)
119 add_package(packages, 'UserConfig')
120 add_package(packages, 'UserConfig')
120 return packages
121 return packages
121
122
122 #---------------------------------------------------------------------------
123 #---------------------------------------------------------------------------
123 # Find package data
124 # Find package data
124 #---------------------------------------------------------------------------
125 #---------------------------------------------------------------------------
125
126
126 def find_package_data():
127 def find_package_data():
127 """
128 """
128 Find IPython's package_data.
129 Find IPython's package_data.
129 """
130 """
130 # This is not enough for these things to appear in an sdist.
131 # This is not enough for these things to appear in an sdist.
131 # We need to muck with the MANIFEST to get this to work
132 # We need to muck with the MANIFEST to get this to work
132 package_data = {
133 package_data = {
133 'IPython.UserConfig' : ['*'],
134 'IPython.UserConfig' : ['*'],
134 'IPython.tools.tests' : ['*.txt'],
135 'IPython.tools.tests' : ['*.txt'],
135 'IPython.testing' : ['*.txt']
136 'IPython.testing' : ['*.txt']
136 }
137 }
137 return package_data
138 return package_data
138
139
139
140
140 #---------------------------------------------------------------------------
141 #---------------------------------------------------------------------------
141 # Find data files
142 # Find data files
142 #---------------------------------------------------------------------------
143 #---------------------------------------------------------------------------
143
144
144 def make_dir_struct(tag,base,out_base):
145 def make_dir_struct(tag,base,out_base):
145 """Make the directory structure of all files below a starting dir.
146 """Make the directory structure of all files below a starting dir.
146
147
147 This is just a convenience routine to help build a nested directory
148 This is just a convenience routine to help build a nested directory
148 hierarchy because distutils is too stupid to do this by itself.
149 hierarchy because distutils is too stupid to do this by itself.
149
150
150 XXX - this needs a proper docstring!
151 XXX - this needs a proper docstring!
151 """
152 """
152
153
153 # we'll use these a lot below
154 # we'll use these a lot below
154 lbase = len(base)
155 lbase = len(base)
155 pathsep = os.path.sep
156 pathsep = os.path.sep
156 lpathsep = len(pathsep)
157 lpathsep = len(pathsep)
157
158
158 out = []
159 out = []
159 for (dirpath,dirnames,filenames) in os.walk(base):
160 for (dirpath,dirnames,filenames) in os.walk(base):
160 # we need to strip out the dirpath from the base to map it to the
161 # we need to strip out the dirpath from the base to map it to the
161 # output (installation) path. This requires possibly stripping the
162 # output (installation) path. This requires possibly stripping the
162 # path separator, because otherwise pjoin will not work correctly
163 # path separator, because otherwise pjoin will not work correctly
163 # (pjoin('foo/','/bar') returns '/bar').
164 # (pjoin('foo/','/bar') returns '/bar').
164
165
165 dp_eff = dirpath[lbase:]
166 dp_eff = dirpath[lbase:]
166 if dp_eff.startswith(pathsep):
167 if dp_eff.startswith(pathsep):
167 dp_eff = dp_eff[lpathsep:]
168 dp_eff = dp_eff[lpathsep:]
168 # The output path must be anchored at the out_base marker
169 # The output path must be anchored at the out_base marker
169 out_path = pjoin(out_base,dp_eff)
170 out_path = pjoin(out_base,dp_eff)
170 # Now we can generate the final filenames. Since os.walk only produces
171 # Now we can generate the final filenames. Since os.walk only produces
171 # filenames, we must join back with the dirpath to get full valid file
172 # filenames, we must join back with the dirpath to get full valid file
172 # paths:
173 # paths:
173 pfiles = [pjoin(dirpath,f) for f in filenames]
174 pfiles = [pjoin(dirpath,f) for f in filenames]
174 # Finally, generate the entry we need, which is a triple of (tag,output
175 # Finally, generate the entry we need, which is a triple of (tag,output
175 # path, files) for use as a data_files parameter in install_data.
176 # path, files) for use as a data_files parameter in install_data.
176 out.append((tag,out_path,pfiles))
177 out.append((tag,out_path,pfiles))
177
178
178 return out
179 return out
179
180
180
181
181 def find_data_files():
182 def find_data_files():
182 """
183 """
183 Find IPython's data_files.
184 Find IPython's data_files.
184
185
185 Most of these are docs.
186 Most of these are docs.
186 """
187 """
187
188
188 docdirbase = 'share/doc/ipython'
189 docdirbase = 'share/doc/ipython'
189 manpagebase = 'share/man/man1'
190 manpagebase = 'share/man/man1'
190
191
191 # Simple file lists can be made by hand
192 # Simple file lists can be made by hand
192 manpages = filter(isfile, glob('docs/man/*.1.gz'))
193 manpages = filter(isfile, glob('docs/man/*.1.gz'))
193 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
194 igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*'))
194
195
195 # For nested structures, use the utility above
196 # For nested structures, use the utility above
196 example_files = make_dir_struct('data','docs/examples',
197 example_files = make_dir_struct('data','docs/examples',
197 pjoin(docdirbase,'examples'))
198 pjoin(docdirbase,'examples'))
198 manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual'))
199 manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual'))
199
200
200 # And assemble the entire output list
201 # And assemble the entire output list
201 data_files = [ ('data',manpagebase, manpages),
202 data_files = [ ('data',manpagebase, manpages),
202 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
203 ('data',pjoin(docdirbase,'extensions'),igridhelpfiles),
203 ] + manual_files + example_files
204 ] + manual_files + example_files
204
205
205 ## import pprint # dbg
206 ## import pprint # dbg
206 ## print '*'*80
207 ## print '*'*80
207 ## print 'data files'
208 ## print 'data files'
208 ## pprint.pprint(data_files)
209 ## pprint.pprint(data_files)
209 ## print '*'*80
210 ## print '*'*80
210
211
211 return data_files
212 return data_files
212
213
213 #---------------------------------------------------------------------------
214 #---------------------------------------------------------------------------
214 # Find scripts
215 # Find scripts
215 #---------------------------------------------------------------------------
216 #---------------------------------------------------------------------------
216
217
217 def find_scripts():
218 def find_scripts():
218 """
219 """
219 Find IPython's scripts.
220 Find IPython's scripts.
220 """
221 """
221 scripts = ['IPython/kernel/scripts/ipengine',
222 scripts = ['IPython/kernel/scripts/ipengine',
222 'IPython/kernel/scripts/ipcontroller',
223 'IPython/kernel/scripts/ipcontroller',
223 'IPython/kernel/scripts/ipcluster',
224 'IPython/kernel/scripts/ipcluster',
224 'scripts/ipython',
225 'scripts/ipython',
225 'scripts/ipythonx',
226 'scripts/ipythonx',
226 'scripts/pycolor',
227 'scripts/pycolor',
227 'scripts/irunner',
228 'scripts/irunner',
229 'scripts/iptest',
228 ]
230 ]
229
231
230 # Script to be run by the windows binary installer after the default setup
232 # Script to be run by the windows binary installer after the default setup
231 # routine, to add shortcuts and similar windows-only things. Windows
233 # routine, to add shortcuts and similar windows-only things. Windows
232 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
234 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
233 # doesn't find them.
235 # doesn't find them.
234 if 'bdist_wininst' in sys.argv:
236 if 'bdist_wininst' in sys.argv:
235 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
237 if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
236 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
238 print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting."
237 sys.exit(1)
239 sys.exit(1)
238 scripts.append('scripts/ipython_win_post_install.py')
240 scripts.append('scripts/ipython_win_post_install.py')
239
241
240 return scripts
242 return scripts
241
243
242 #---------------------------------------------------------------------------
244 #---------------------------------------------------------------------------
243 # Verify all dependencies
245 # Verify all dependencies
244 #---------------------------------------------------------------------------
246 #---------------------------------------------------------------------------
245
247
246 def check_for_dependencies():
248 def check_for_dependencies():
247 """Check for IPython's dependencies.
249 """Check for IPython's dependencies.
248
250
249 This function should NOT be called if running under setuptools!
251 This function should NOT be called if running under setuptools!
250 """
252 """
251 from setupext.setupext import (
253 from setupext.setupext import (
252 print_line, print_raw, print_status, print_message,
254 print_line, print_raw, print_status, print_message,
253 check_for_zopeinterface, check_for_twisted,
255 check_for_zopeinterface, check_for_twisted,
254 check_for_foolscap, check_for_pyopenssl,
256 check_for_foolscap, check_for_pyopenssl,
255 check_for_sphinx, check_for_pygments,
257 check_for_sphinx, check_for_pygments,
256 check_for_nose, check_for_pexpect
258 check_for_nose, check_for_pexpect
257 )
259 )
258 print_line()
260 print_line()
259 print_raw("BUILDING IPYTHON")
261 print_raw("BUILDING IPYTHON")
260 print_status('python', sys.version)
262 print_status('python', sys.version)
261 print_status('platform', sys.platform)
263 print_status('platform', sys.platform)
262 if sys.platform == 'win32':
264 if sys.platform == 'win32':
263 print_status('Windows version', sys.getwindowsversion())
265 print_status('Windows version', sys.getwindowsversion())
264
266
265 print_raw("")
267 print_raw("")
266 print_raw("OPTIONAL DEPENDENCIES")
268 print_raw("OPTIONAL DEPENDENCIES")
267
269
268 check_for_zopeinterface()
270 check_for_zopeinterface()
269 check_for_twisted()
271 check_for_twisted()
270 check_for_foolscap()
272 check_for_foolscap()
271 check_for_pyopenssl()
273 check_for_pyopenssl()
272 check_for_sphinx()
274 check_for_sphinx()
273 check_for_pygments()
275 check_for_pygments()
274 check_for_nose()
276 check_for_nose()
275 check_for_pexpect()
277 check_for_pexpect()
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now