Show More
@@ -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,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 |
|
|
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/ |
|
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,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,179 +1,177 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 | ] |
|
141 | ] | |
142 | } |
|
142 | } | |
143 | setup_args["extras_require"] = dict( |
|
143 | setup_args["extras_require"] = dict( | |
144 | kernel = [ |
|
144 | kernel = [ | |
145 | "zope.interface>=3.4.1", |
|
145 | "zope.interface>=3.4.1", | |
146 | "Twisted>=8.0.1", |
|
146 | "Twisted>=8.0.1", | |
147 | "foolscap>=0.2.6" |
|
147 | "foolscap>=0.2.6" | |
148 | ], |
|
148 | ], | |
149 | doc=['Sphinx>=0.3','pygments'], |
|
149 | doc=['Sphinx>=0.3','pygments'], | |
150 | test='nose>=0.10.1', |
|
150 | test='nose>=0.10.1', | |
151 | security=["pyOpenSSL>=0.6"] |
|
151 | security=["pyOpenSSL>=0.6"] | |
152 | ) |
|
152 | ) | |
153 | # Allow setuptools to handle the scripts |
|
153 | # Allow setuptools to handle the scripts | |
154 | scripts = [] |
|
154 | scripts = [] | |
155 | # eggs will lack docs, examples |
|
|||
156 | data_files = [] |
|
|||
157 | else: |
|
155 | else: | |
158 | # package_data of setuptools was introduced to distutils in 2.4 |
|
156 | # package_data of setuptools was introduced to distutils in 2.4 | |
159 | cfgfiles = filter(isfile, glob('IPython/UserConfig/*')) |
|
157 | cfgfiles = filter(isfile, glob('IPython/UserConfig/*')) | |
160 | if sys.version_info < (2,4): |
|
158 | if sys.version_info < (2,4): | |
161 | data_files.append(('lib', 'IPython/UserConfig', cfgfiles)) |
|
159 | data_files.append(('lib', 'IPython/UserConfig', cfgfiles)) | |
162 | # If we are running without setuptools, call this function which will |
|
160 | # If we are running without setuptools, call this function which will | |
163 | # check for dependencies an inform the user what is needed. This is |
|
161 | # check for dependencies an inform the user what is needed. This is | |
164 | # just to make life easy for users. |
|
162 | # just to make life easy for users. | |
165 | check_for_dependencies() |
|
163 | check_for_dependencies() | |
166 |
|
164 | |||
167 |
|
165 | |||
168 | #--------------------------------------------------------------------------- |
|
166 | #--------------------------------------------------------------------------- | |
169 | # Do the actual setup now |
|
167 | # Do the actual setup now | |
170 | #--------------------------------------------------------------------------- |
|
168 | #--------------------------------------------------------------------------- | |
171 |
|
169 | |||
172 | setup_args['packages'] = packages |
|
170 | setup_args['packages'] = packages | |
173 | setup_args['package_data'] = package_data |
|
171 | setup_args['package_data'] = package_data | |
174 | setup_args['scripts'] = scripts |
|
172 | setup_args['scripts'] = scripts | |
175 | setup_args['data_files'] = data_files |
|
173 | setup_args['data_files'] = data_files | |
176 | setup_args.update(setuptools_extra_args) |
|
174 | setup_args.update(setuptools_extra_args) | |
177 |
|
175 | |||
178 | if __name__ == '__main__': |
|
176 | if __name__ == '__main__': | |
179 | setup(**setup_args) |
|
177 | setup(**setup_args) |
@@ -1,275 +1,276 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, 'tools', tests=True) |
|
118 | add_package(packages, 'tools', tests=True) | |
119 | add_package(packages, 'UserConfig') |
|
119 | add_package(packages, 'UserConfig') | |
120 | return packages |
|
120 | return packages | |
121 |
|
121 | |||
122 | #--------------------------------------------------------------------------- |
|
122 | #--------------------------------------------------------------------------- | |
123 | # Find package data |
|
123 | # Find package data | |
124 | #--------------------------------------------------------------------------- |
|
124 | #--------------------------------------------------------------------------- | |
125 |
|
125 | |||
126 | def find_package_data(): |
|
126 | def find_package_data(): | |
127 | """ |
|
127 | """ | |
128 | Find IPython's package_data. |
|
128 | Find IPython's package_data. | |
129 | """ |
|
129 | """ | |
130 | # This is not enough for these things to appear in an sdist. |
|
130 | # 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 |
|
131 | # We need to muck with the MANIFEST to get this to work | |
132 | package_data = { |
|
132 | package_data = { | |
133 | 'IPython.UserConfig' : ['*'], |
|
133 | 'IPython.UserConfig' : ['*'], | |
134 | 'IPython.tools.tests' : ['*.txt'], |
|
134 | 'IPython.tools.tests' : ['*.txt'], | |
135 | 'IPython.testing' : ['*.txt'] |
|
135 | 'IPython.testing' : ['*.txt'] | |
136 | } |
|
136 | } | |
137 | return package_data |
|
137 | return package_data | |
138 |
|
138 | |||
139 |
|
139 | |||
140 | #--------------------------------------------------------------------------- |
|
140 | #--------------------------------------------------------------------------- | |
141 | # Find data files |
|
141 | # Find data files | |
142 | #--------------------------------------------------------------------------- |
|
142 | #--------------------------------------------------------------------------- | |
143 |
|
143 | |||
144 | def make_dir_struct(tag,base,out_base): |
|
144 | def make_dir_struct(tag,base,out_base): | |
145 | """Make the directory structure of all files below a starting dir. |
|
145 | """Make the directory structure of all files below a starting dir. | |
146 |
|
146 | |||
147 | This is just a convenience routine to help build a nested directory |
|
147 | This is just a convenience routine to help build a nested directory | |
148 | hierarchy because distutils is too stupid to do this by itself. |
|
148 | hierarchy because distutils is too stupid to do this by itself. | |
149 |
|
149 | |||
150 | XXX - this needs a proper docstring! |
|
150 | XXX - this needs a proper docstring! | |
151 | """ |
|
151 | """ | |
152 |
|
152 | |||
153 | # we'll use these a lot below |
|
153 | # we'll use these a lot below | |
154 | lbase = len(base) |
|
154 | lbase = len(base) | |
155 | pathsep = os.path.sep |
|
155 | pathsep = os.path.sep | |
156 | lpathsep = len(pathsep) |
|
156 | lpathsep = len(pathsep) | |
157 |
|
157 | |||
158 | out = [] |
|
158 | out = [] | |
159 | for (dirpath,dirnames,filenames) in os.walk(base): |
|
159 | for (dirpath,dirnames,filenames) in os.walk(base): | |
160 | # we need to strip out the dirpath from the base to map it to the |
|
160 | # we need to strip out the dirpath from the base to map it to the | |
161 | # output (installation) path. This requires possibly stripping the |
|
161 | # output (installation) path. This requires possibly stripping the | |
162 | # path separator, because otherwise pjoin will not work correctly |
|
162 | # path separator, because otherwise pjoin will not work correctly | |
163 | # (pjoin('foo/','/bar') returns '/bar'). |
|
163 | # (pjoin('foo/','/bar') returns '/bar'). | |
164 |
|
164 | |||
165 | dp_eff = dirpath[lbase:] |
|
165 | dp_eff = dirpath[lbase:] | |
166 | if dp_eff.startswith(pathsep): |
|
166 | if dp_eff.startswith(pathsep): | |
167 | dp_eff = dp_eff[lpathsep:] |
|
167 | dp_eff = dp_eff[lpathsep:] | |
168 | # The output path must be anchored at the out_base marker |
|
168 | # The output path must be anchored at the out_base marker | |
169 | out_path = pjoin(out_base,dp_eff) |
|
169 | out_path = pjoin(out_base,dp_eff) | |
170 | # Now we can generate the final filenames. Since os.walk only produces |
|
170 | # 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 |
|
171 | # filenames, we must join back with the dirpath to get full valid file | |
172 | # paths: |
|
172 | # paths: | |
173 | pfiles = [pjoin(dirpath,f) for f in filenames] |
|
173 | pfiles = [pjoin(dirpath,f) for f in filenames] | |
174 | # Finally, generate the entry we need, which is a triple of (tag,output |
|
174 | # 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. |
|
175 | # path, files) for use as a data_files parameter in install_data. | |
176 | out.append((tag,out_path,pfiles)) |
|
176 | out.append((tag,out_path,pfiles)) | |
177 |
|
177 | |||
178 | return out |
|
178 | return out | |
179 |
|
179 | |||
180 |
|
180 | |||
181 | def find_data_files(): |
|
181 | def find_data_files(): | |
182 | """ |
|
182 | """ | |
183 | Find IPython's data_files. |
|
183 | Find IPython's data_files. | |
184 |
|
184 | |||
185 | Most of these are docs. |
|
185 | Most of these are docs. | |
186 | """ |
|
186 | """ | |
187 |
|
187 | |||
188 | docdirbase = 'share/doc/ipython' |
|
188 | docdirbase = 'share/doc/ipython' | |
189 | manpagebase = 'share/man/man1' |
|
189 | manpagebase = 'share/man/man1' | |
190 |
|
190 | |||
191 | # Simple file lists can be made by hand |
|
191 | # Simple file lists can be made by hand | |
192 | manpages = filter(isfile, glob('docs/man/*.1.gz')) |
|
192 | manpages = filter(isfile, glob('docs/man/*.1.gz')) | |
193 | igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*')) |
|
193 | igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*')) | |
194 |
|
194 | |||
195 | # For nested structures, use the utility above |
|
195 | # For nested structures, use the utility above | |
196 | example_files = make_dir_struct('data','docs/examples', |
|
196 | example_files = make_dir_struct('data','docs/examples', | |
197 | pjoin(docdirbase,'examples')) |
|
197 | pjoin(docdirbase,'examples')) | |
198 | manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual')) |
|
198 | manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual')) | |
199 |
|
199 | |||
200 | # And assemble the entire output list |
|
200 | # And assemble the entire output list | |
201 | data_files = [ ('data',manpagebase, manpages), |
|
201 | data_files = [ ('data',manpagebase, manpages), | |
202 | ('data',pjoin(docdirbase,'extensions'),igridhelpfiles), |
|
202 | ('data',pjoin(docdirbase,'extensions'),igridhelpfiles), | |
203 | ] + manual_files + example_files |
|
203 | ] + manual_files + example_files | |
204 |
|
204 | |||
205 | ## import pprint # dbg |
|
205 | ## import pprint # dbg | |
206 | ## print '*'*80 |
|
206 | ## print '*'*80 | |
207 | ## print 'data files' |
|
207 | ## print 'data files' | |
208 | ## pprint.pprint(data_files) |
|
208 | ## pprint.pprint(data_files) | |
209 | ## print '*'*80 |
|
209 | ## print '*'*80 | |
210 |
|
210 | |||
211 | return data_files |
|
211 | return data_files | |
212 |
|
212 | |||
213 | #--------------------------------------------------------------------------- |
|
213 | #--------------------------------------------------------------------------- | |
214 | # Find scripts |
|
214 | # Find scripts | |
215 | #--------------------------------------------------------------------------- |
|
215 | #--------------------------------------------------------------------------- | |
216 |
|
216 | |||
217 | def find_scripts(): |
|
217 | def find_scripts(): | |
218 | """ |
|
218 | """ | |
219 | Find IPython's scripts. |
|
219 | Find IPython's scripts. | |
220 | """ |
|
220 | """ | |
221 | scripts = ['IPython/kernel/scripts/ipengine', |
|
221 | scripts = ['IPython/kernel/scripts/ipengine', | |
222 | 'IPython/kernel/scripts/ipcontroller', |
|
222 | 'IPython/kernel/scripts/ipcontroller', | |
223 |
|
|
223 | 'IPython/kernel/scripts/ipcluster', | |
224 | 'scripts/ipython', |
|
224 | 'scripts/ipython', | |
225 | 'scripts/ipythonx', |
|
225 | 'scripts/ipythonx', | |
226 | 'scripts/pycolor', |
|
226 | 'scripts/pycolor', | |
227 | 'scripts/irunner', |
|
227 | 'scripts/irunner', | |
|
228 | 'scripts/iptest', | |||
228 | ] |
|
229 | ] | |
229 |
|
230 | |||
230 | # Script to be run by the windows binary installer after the default setup |
|
231 | # Script to be run by the windows binary installer after the default setup | |
231 | # routine, to add shortcuts and similar windows-only things. Windows |
|
232 | # routine, to add shortcuts and similar windows-only things. Windows | |
232 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils |
|
233 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils | |
233 | # doesn't find them. |
|
234 | # doesn't find them. | |
234 | if 'bdist_wininst' in sys.argv: |
|
235 | if 'bdist_wininst' in sys.argv: | |
235 | if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): |
|
236 | 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." |
|
237 | print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting." | |
237 | sys.exit(1) |
|
238 | sys.exit(1) | |
238 | scripts.append('scripts/ipython_win_post_install.py') |
|
239 | scripts.append('scripts/ipython_win_post_install.py') | |
239 |
|
240 | |||
240 | return scripts |
|
241 | return scripts | |
241 |
|
242 | |||
242 | #--------------------------------------------------------------------------- |
|
243 | #--------------------------------------------------------------------------- | |
243 | # Verify all dependencies |
|
244 | # Verify all dependencies | |
244 | #--------------------------------------------------------------------------- |
|
245 | #--------------------------------------------------------------------------- | |
245 |
|
246 | |||
246 | def check_for_dependencies(): |
|
247 | def check_for_dependencies(): | |
247 | """Check for IPython's dependencies. |
|
248 | """Check for IPython's dependencies. | |
248 |
|
249 | |||
249 | This function should NOT be called if running under setuptools! |
|
250 | This function should NOT be called if running under setuptools! | |
250 | """ |
|
251 | """ | |
251 | from setupext.setupext import ( |
|
252 | from setupext.setupext import ( | |
252 | print_line, print_raw, print_status, print_message, |
|
253 | print_line, print_raw, print_status, print_message, | |
253 | check_for_zopeinterface, check_for_twisted, |
|
254 | check_for_zopeinterface, check_for_twisted, | |
254 | check_for_foolscap, check_for_pyopenssl, |
|
255 | check_for_foolscap, check_for_pyopenssl, | |
255 | check_for_sphinx, check_for_pygments, |
|
256 | check_for_sphinx, check_for_pygments, | |
256 | check_for_nose, check_for_pexpect |
|
257 | check_for_nose, check_for_pexpect | |
257 | ) |
|
258 | ) | |
258 | print_line() |
|
259 | print_line() | |
259 | print_raw("BUILDING IPYTHON") |
|
260 | print_raw("BUILDING IPYTHON") | |
260 | print_status('python', sys.version) |
|
261 | print_status('python', sys.version) | |
261 | print_status('platform', sys.platform) |
|
262 | print_status('platform', sys.platform) | |
262 | if sys.platform == 'win32': |
|
263 | if sys.platform == 'win32': | |
263 | print_status('Windows version', sys.getwindowsversion()) |
|
264 | print_status('Windows version', sys.getwindowsversion()) | |
264 |
|
265 | |||
265 | print_raw("") |
|
266 | print_raw("") | |
266 | print_raw("OPTIONAL DEPENDENCIES") |
|
267 | print_raw("OPTIONAL DEPENDENCIES") | |
267 |
|
268 | |||
268 | check_for_zopeinterface() |
|
269 | check_for_zopeinterface() | |
269 | check_for_twisted() |
|
270 | check_for_twisted() | |
270 | check_for_foolscap() |
|
271 | check_for_foolscap() | |
271 | check_for_pyopenssl() |
|
272 | check_for_pyopenssl() | |
272 | check_for_sphinx() |
|
273 | check_for_sphinx() | |
273 | check_for_pygments() |
|
274 | check_for_pygments() | |
274 | check_for_nose() |
|
275 | check_for_nose() | |
275 | check_for_pexpect() |
|
276 | check_for_pexpect() |
General Comments 0
You need to be logged in to leave comments.
Login now