Show More
@@ -0,0 +1,53 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # -*- coding: utf-8 -*- | |||
|
3 | """IPython Test Suite Runner. | |||
|
4 | """ | |||
|
5 | ||||
|
6 | import sys | |||
|
7 | import warnings | |||
|
8 | ||||
|
9 | from nose.core import TestProgram | |||
|
10 | import nose.plugins.builtin | |||
|
11 | ||||
|
12 | from IPython.testing.plugin.ipdoctest import IPythonDoctest | |||
|
13 | ||||
|
14 | def main(): | |||
|
15 | """Run the IPython test suite. | |||
|
16 | """ | |||
|
17 | ||||
|
18 | warnings.filterwarnings('ignore', | |||
|
19 | 'This will be removed soon. Use IPython.testing.util instead') | |||
|
20 | ||||
|
21 | ||||
|
22 | # construct list of plugins, omitting the existing doctest plugin | |||
|
23 | plugins = [IPythonDoctest()] | |||
|
24 | for p in nose.plugins.builtin.plugins: | |||
|
25 | plug = p() | |||
|
26 | if plug.name == 'doctest': | |||
|
27 | continue | |||
|
28 | ||||
|
29 | #print 'adding plugin:',plug.name # dbg | |||
|
30 | plugins.append(plug) | |||
|
31 | ||||
|
32 | argv = sys.argv + ['--doctest-tests','--doctest-extension=txt', | |||
|
33 | '--detailed-errors', | |||
|
34 | ||||
|
35 | # We add --exe because of setuptools' imbecility (it | |||
|
36 | # blindly does chmod +x on ALL files). Nose does the | |||
|
37 | # right thing and it tries to avoid executables, | |||
|
38 | # setuptools unfortunately forces our hand here. This | |||
|
39 | # has been discussed on the distutils list and the | |||
|
40 | # setuptools devs refuse to fix this problem! | |||
|
41 | '--exe', | |||
|
42 | ] | |||
|
43 | ||||
|
44 | has_ip = False | |||
|
45 | for arg in sys.argv: | |||
|
46 | if 'IPython' in arg: | |||
|
47 | has_ip = True | |||
|
48 | break | |||
|
49 | ||||
|
50 | if not has_ip: | |||
|
51 | argv.append('IPython') | |||
|
52 | ||||
|
53 | TestProgram(argv=argv,plugins=plugins) |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 |
@@ -0,0 +1,8 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # -*- coding: utf-8 -*- | |||
|
3 | """IPython Test Suite Runner. | |||
|
4 | """ | |||
|
5 | ||||
|
6 | from IPython.testing import iptest | |||
|
7 | ||||
|
8 | iptest.main() |
@@ -1,1236 +1,1238 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """IPython Shell classes. |
|
2 | """IPython Shell classes. | |
3 |
|
3 | |||
4 | All the matplotlib support code was co-developed with John Hunter, |
|
4 | All the matplotlib support code was co-developed with John Hunter, | |
5 | matplotlib's author. |
|
5 | matplotlib's author. | |
6 |
|
6 | |||
7 | $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $""" |
|
7 | $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $""" | |
8 |
|
8 | |||
9 | #***************************************************************************** |
|
9 | #***************************************************************************** | |
10 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
10 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
11 | # |
|
11 | # | |
12 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
13 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
14 | #***************************************************************************** |
|
14 | #***************************************************************************** | |
15 |
|
15 | |||
16 | from IPython import Release |
|
16 | from IPython import Release | |
17 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
17 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
18 | __license__ = Release.license |
|
18 | __license__ = Release.license | |
19 |
|
19 | |||
20 | # Code begins |
|
20 | # Code begins | |
21 | # Stdlib imports |
|
21 | # Stdlib imports | |
22 | import __builtin__ |
|
22 | import __builtin__ | |
23 | import __main__ |
|
23 | import __main__ | |
24 | import Queue |
|
24 | import Queue | |
25 | import inspect |
|
25 | import inspect | |
26 | import os |
|
26 | import os | |
27 | import sys |
|
27 | import sys | |
28 | import thread |
|
28 | import thread | |
29 | import threading |
|
29 | import threading | |
30 | import time |
|
30 | import time | |
31 |
|
31 | |||
32 | from signal import signal, SIGINT |
|
32 | from signal import signal, SIGINT | |
33 |
|
33 | |||
34 | try: |
|
34 | try: | |
35 | import ctypes |
|
35 | import ctypes | |
36 | HAS_CTYPES = True |
|
36 | HAS_CTYPES = True | |
37 | except ImportError: |
|
37 | except ImportError: | |
38 | HAS_CTYPES = False |
|
38 | HAS_CTYPES = False | |
39 |
|
39 | |||
40 | # IPython imports |
|
40 | # IPython imports | |
41 | import IPython |
|
41 | import IPython | |
42 | from IPython import ultraTB, ipapi |
|
42 | from IPython import ultraTB, ipapi | |
|
43 | from IPython.Magic import Magic | |||
43 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no |
|
44 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no | |
44 | from IPython.iplib import InteractiveShell |
|
45 | from IPython.iplib import InteractiveShell | |
45 | from IPython.ipmaker import make_IPython |
|
46 | from IPython.ipmaker import make_IPython | |
46 | from IPython.Magic import Magic |
|
|||
47 | from IPython.ipstruct import Struct |
|
47 | from IPython.ipstruct import Struct | |
|
48 | from IPython.testing import decorators as testdec | |||
48 |
|
49 | |||
49 | # Globals |
|
50 | # Globals | |
50 | # global flag to pass around information about Ctrl-C without exceptions |
|
51 | # global flag to pass around information about Ctrl-C without exceptions | |
51 | KBINT = False |
|
52 | KBINT = False | |
52 |
|
53 | |||
53 | # global flag to turn on/off Tk support. |
|
54 | # global flag to turn on/off Tk support. | |
54 | USE_TK = False |
|
55 | USE_TK = False | |
55 |
|
56 | |||
56 | # ID for the main thread, used for cross-thread exceptions |
|
57 | # ID for the main thread, used for cross-thread exceptions | |
57 | MAIN_THREAD_ID = thread.get_ident() |
|
58 | MAIN_THREAD_ID = thread.get_ident() | |
58 |
|
59 | |||
59 | # Tag when runcode() is active, for exception handling |
|
60 | # Tag when runcode() is active, for exception handling | |
60 | CODE_RUN = None |
|
61 | CODE_RUN = None | |
61 |
|
62 | |||
62 | # Default timeout for waiting for multithreaded shells (in seconds) |
|
63 | # Default timeout for waiting for multithreaded shells (in seconds) | |
63 | GUI_TIMEOUT = 10 |
|
64 | GUI_TIMEOUT = 10 | |
64 |
|
65 | |||
65 | #----------------------------------------------------------------------------- |
|
66 | #----------------------------------------------------------------------------- | |
66 | # This class is trivial now, but I want to have it in to publish a clean |
|
67 | # This class is trivial now, but I want to have it in to publish a clean | |
67 | # interface. Later when the internals are reorganized, code that uses this |
|
68 | # interface. Later when the internals are reorganized, code that uses this | |
68 | # shouldn't have to change. |
|
69 | # shouldn't have to change. | |
69 |
|
70 | |||
70 | class IPShell: |
|
71 | class IPShell: | |
71 | """Create an IPython instance.""" |
|
72 | """Create an IPython instance.""" | |
72 |
|
73 | |||
73 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
|
74 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, | |
74 | debug=1,shell_class=InteractiveShell): |
|
75 | debug=1,shell_class=InteractiveShell): | |
75 | self.IP = make_IPython(argv,user_ns=user_ns, |
|
76 | self.IP = make_IPython(argv,user_ns=user_ns, | |
76 | user_global_ns=user_global_ns, |
|
77 | user_global_ns=user_global_ns, | |
77 | debug=debug,shell_class=shell_class) |
|
78 | debug=debug,shell_class=shell_class) | |
78 |
|
79 | |||
79 | def mainloop(self,sys_exit=0,banner=None): |
|
80 | def mainloop(self,sys_exit=0,banner=None): | |
80 | self.IP.mainloop(banner) |
|
81 | self.IP.mainloop(banner) | |
81 | if sys_exit: |
|
82 | if sys_exit: | |
82 | sys.exit() |
|
83 | sys.exit() | |
83 |
|
84 | |||
84 | #----------------------------------------------------------------------------- |
|
85 | #----------------------------------------------------------------------------- | |
85 | def kill_embedded(self,parameter_s=''): |
|
86 | def kill_embedded(self,parameter_s=''): | |
86 | """%kill_embedded : deactivate for good the current embedded IPython. |
|
87 | """%kill_embedded : deactivate for good the current embedded IPython. | |
87 |
|
88 | |||
88 | This function (after asking for confirmation) sets an internal flag so that |
|
89 | This function (after asking for confirmation) sets an internal flag so that | |
89 | an embedded IPython will never activate again. This is useful to |
|
90 | an embedded IPython will never activate again. This is useful to | |
90 | permanently disable a shell that is being called inside a loop: once you've |
|
91 | permanently disable a shell that is being called inside a loop: once you've | |
91 | figured out what you needed from it, you may then kill it and the program |
|
92 | figured out what you needed from it, you may then kill it and the program | |
92 | will then continue to run without the interactive shell interfering again. |
|
93 | will then continue to run without the interactive shell interfering again. | |
93 | """ |
|
94 | """ | |
94 |
|
95 | |||
95 | kill = ask_yes_no("Are you sure you want to kill this embedded instance " |
|
96 | kill = ask_yes_no("Are you sure you want to kill this embedded instance " | |
96 | "(y/n)? [y/N] ",'n') |
|
97 | "(y/n)? [y/N] ",'n') | |
97 | if kill: |
|
98 | if kill: | |
98 | self.shell.embedded_active = False |
|
99 | self.shell.embedded_active = False | |
99 | print "This embedded IPython will not reactivate anymore once you exit." |
|
100 | print "This embedded IPython will not reactivate anymore once you exit." | |
100 |
|
101 | |||
101 | class IPShellEmbed: |
|
102 | class IPShellEmbed: | |
102 | """Allow embedding an IPython shell into a running program. |
|
103 | """Allow embedding an IPython shell into a running program. | |
103 |
|
104 | |||
104 | Instances of this class are callable, with the __call__ method being an |
|
105 | Instances of this class are callable, with the __call__ method being an | |
105 | alias to the embed() method of an InteractiveShell instance. |
|
106 | alias to the embed() method of an InteractiveShell instance. | |
106 |
|
107 | |||
107 | Usage (see also the example-embed.py file for a running example): |
|
108 | Usage (see also the example-embed.py file for a running example): | |
108 |
|
109 | |||
109 | ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override]) |
|
110 | ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override]) | |
110 |
|
111 | |||
111 | - argv: list containing valid command-line options for IPython, as they |
|
112 | - argv: list containing valid command-line options for IPython, as they | |
112 | would appear in sys.argv[1:]. |
|
113 | would appear in sys.argv[1:]. | |
113 |
|
114 | |||
114 | For example, the following command-line options: |
|
115 | For example, the following command-line options: | |
115 |
|
116 | |||
116 | $ ipython -prompt_in1 'Input <\\#>' -colors LightBG |
|
117 | $ ipython -prompt_in1 'Input <\\#>' -colors LightBG | |
117 |
|
118 | |||
118 | would be passed in the argv list as: |
|
119 | would be passed in the argv list as: | |
119 |
|
120 | |||
120 | ['-prompt_in1','Input <\\#>','-colors','LightBG'] |
|
121 | ['-prompt_in1','Input <\\#>','-colors','LightBG'] | |
121 |
|
122 | |||
122 | - banner: string which gets printed every time the interpreter starts. |
|
123 | - banner: string which gets printed every time the interpreter starts. | |
123 |
|
124 | |||
124 | - exit_msg: string which gets printed every time the interpreter exits. |
|
125 | - exit_msg: string which gets printed every time the interpreter exits. | |
125 |
|
126 | |||
126 | - rc_override: a dict or Struct of configuration options such as those |
|
127 | - rc_override: a dict or Struct of configuration options such as those | |
127 | used by IPython. These options are read from your ~/.ipython/ipythonrc |
|
128 | used by IPython. These options are read from your ~/.ipython/ipythonrc | |
128 | file when the Shell object is created. Passing an explicit rc_override |
|
129 | file when the Shell object is created. Passing an explicit rc_override | |
129 | dict with any options you want allows you to override those values at |
|
130 | dict with any options you want allows you to override those values at | |
130 | creation time without having to modify the file. This way you can create |
|
131 | creation time without having to modify the file. This way you can create | |
131 | embeddable instances configured in any way you want without editing any |
|
132 | embeddable instances configured in any way you want without editing any | |
132 | global files (thus keeping your interactive IPython configuration |
|
133 | global files (thus keeping your interactive IPython configuration | |
133 | unchanged). |
|
134 | unchanged). | |
134 |
|
135 | |||
135 | Then the ipshell instance can be called anywhere inside your code: |
|
136 | Then the ipshell instance can be called anywhere inside your code: | |
136 |
|
137 | |||
137 | ipshell(header='') -> Opens up an IPython shell. |
|
138 | ipshell(header='') -> Opens up an IPython shell. | |
138 |
|
139 | |||
139 | - header: string printed by the IPython shell upon startup. This can let |
|
140 | - header: string printed by the IPython shell upon startup. This can let | |
140 | you know where in your code you are when dropping into the shell. Note |
|
141 | you know where in your code you are when dropping into the shell. Note | |
141 | that 'banner' gets prepended to all calls, so header is used for |
|
142 | that 'banner' gets prepended to all calls, so header is used for | |
142 | location-specific information. |
|
143 | location-specific information. | |
143 |
|
144 | |||
144 | For more details, see the __call__ method below. |
|
145 | For more details, see the __call__ method below. | |
145 |
|
146 | |||
146 | When the IPython shell is exited with Ctrl-D, normal program execution |
|
147 | When the IPython shell is exited with Ctrl-D, normal program execution | |
147 | resumes. |
|
148 | resumes. | |
148 |
|
149 | |||
149 | This functionality was inspired by a posting on comp.lang.python by cmkl |
|
150 | This functionality was inspired by a posting on comp.lang.python by cmkl | |
150 | <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and |
|
151 | <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and | |
151 | by the IDL stop/continue commands.""" |
|
152 | by the IDL stop/continue commands.""" | |
152 |
|
153 | |||
153 | def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None, |
|
154 | def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None, | |
154 | user_ns=None): |
|
155 | user_ns=None): | |
155 | """Note that argv here is a string, NOT a list.""" |
|
156 | """Note that argv here is a string, NOT a list.""" | |
156 | self.set_banner(banner) |
|
157 | self.set_banner(banner) | |
157 | self.set_exit_msg(exit_msg) |
|
158 | self.set_exit_msg(exit_msg) | |
158 | self.set_dummy_mode(0) |
|
159 | self.set_dummy_mode(0) | |
159 |
|
160 | |||
160 | # sys.displayhook is a global, we need to save the user's original |
|
161 | # sys.displayhook is a global, we need to save the user's original | |
161 | # Don't rely on __displayhook__, as the user may have changed that. |
|
162 | # Don't rely on __displayhook__, as the user may have changed that. | |
162 | self.sys_displayhook_ori = sys.displayhook |
|
163 | self.sys_displayhook_ori = sys.displayhook | |
163 |
|
164 | |||
164 | # save readline completer status |
|
165 | # save readline completer status | |
165 | try: |
|
166 | try: | |
166 | #print 'Save completer',sys.ipcompleter # dbg |
|
167 | #print 'Save completer',sys.ipcompleter # dbg | |
167 | self.sys_ipcompleter_ori = sys.ipcompleter |
|
168 | self.sys_ipcompleter_ori = sys.ipcompleter | |
168 | except: |
|
169 | except: | |
169 | pass # not nested with IPython |
|
170 | pass # not nested with IPython | |
170 |
|
171 | |||
171 | self.IP = make_IPython(argv,rc_override=rc_override, |
|
172 | self.IP = make_IPython(argv,rc_override=rc_override, | |
172 | embedded=True, |
|
173 | embedded=True, | |
173 | user_ns=user_ns) |
|
174 | user_ns=user_ns) | |
174 |
|
175 | |||
175 | ip = ipapi.IPApi(self.IP) |
|
176 | ip = ipapi.IPApi(self.IP) | |
176 | ip.expose_magic("kill_embedded",kill_embedded) |
|
177 | ip.expose_magic("kill_embedded",kill_embedded) | |
177 |
|
178 | |||
178 | # copy our own displayhook also |
|
179 | # copy our own displayhook also | |
179 | self.sys_displayhook_embed = sys.displayhook |
|
180 | self.sys_displayhook_embed = sys.displayhook | |
180 | # and leave the system's display hook clean |
|
181 | # and leave the system's display hook clean | |
181 | sys.displayhook = self.sys_displayhook_ori |
|
182 | sys.displayhook = self.sys_displayhook_ori | |
182 | # don't use the ipython crash handler so that user exceptions aren't |
|
183 | # don't use the ipython crash handler so that user exceptions aren't | |
183 | # trapped |
|
184 | # trapped | |
184 | sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors, |
|
185 | sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors, | |
185 | mode = self.IP.rc.xmode, |
|
186 | mode = self.IP.rc.xmode, | |
186 | call_pdb = self.IP.rc.pdb) |
|
187 | call_pdb = self.IP.rc.pdb) | |
187 | self.restore_system_completer() |
|
188 | self.restore_system_completer() | |
188 |
|
189 | |||
189 | def restore_system_completer(self): |
|
190 | def restore_system_completer(self): | |
190 | """Restores the readline completer which was in place. |
|
191 | """Restores the readline completer which was in place. | |
191 |
|
192 | |||
192 | This allows embedded IPython within IPython not to disrupt the |
|
193 | This allows embedded IPython within IPython not to disrupt the | |
193 | parent's completion. |
|
194 | parent's completion. | |
194 | """ |
|
195 | """ | |
195 |
|
196 | |||
196 | try: |
|
197 | try: | |
197 | self.IP.readline.set_completer(self.sys_ipcompleter_ori) |
|
198 | self.IP.readline.set_completer(self.sys_ipcompleter_ori) | |
198 | sys.ipcompleter = self.sys_ipcompleter_ori |
|
199 | sys.ipcompleter = self.sys_ipcompleter_ori | |
199 | except: |
|
200 | except: | |
200 | pass |
|
201 | pass | |
201 |
|
202 | |||
202 | def __call__(self,header='',local_ns=None,global_ns=None,dummy=None): |
|
203 | def __call__(self,header='',local_ns=None,global_ns=None,dummy=None): | |
203 | """Activate the interactive interpreter. |
|
204 | """Activate the interactive interpreter. | |
204 |
|
205 | |||
205 | __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start |
|
206 | __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start | |
206 | the interpreter shell with the given local and global namespaces, and |
|
207 | the interpreter shell with the given local and global namespaces, and | |
207 | optionally print a header string at startup. |
|
208 | optionally print a header string at startup. | |
208 |
|
209 | |||
209 | The shell can be globally activated/deactivated using the |
|
210 | The shell can be globally activated/deactivated using the | |
210 | set/get_dummy_mode methods. This allows you to turn off a shell used |
|
211 | set/get_dummy_mode methods. This allows you to turn off a shell used | |
211 | for debugging globally. |
|
212 | for debugging globally. | |
212 |
|
213 | |||
213 | However, *each* time you call the shell you can override the current |
|
214 | However, *each* time you call the shell you can override the current | |
214 | state of dummy_mode with the optional keyword parameter 'dummy'. For |
|
215 | state of dummy_mode with the optional keyword parameter 'dummy'. For | |
215 | example, if you set dummy mode on with IPShell.set_dummy_mode(1), you |
|
216 | example, if you set dummy mode on with IPShell.set_dummy_mode(1), you | |
216 | can still have a specific call work by making it as IPShell(dummy=0). |
|
217 | can still have a specific call work by making it as IPShell(dummy=0). | |
217 |
|
218 | |||
218 | The optional keyword parameter dummy controls whether the call |
|
219 | The optional keyword parameter dummy controls whether the call | |
219 | actually does anything. """ |
|
220 | actually does anything. """ | |
220 |
|
221 | |||
221 | # If the user has turned it off, go away |
|
222 | # If the user has turned it off, go away | |
222 | if not self.IP.embedded_active: |
|
223 | if not self.IP.embedded_active: | |
223 | return |
|
224 | return | |
224 |
|
225 | |||
225 | # Normal exits from interactive mode set this flag, so the shell can't |
|
226 | # Normal exits from interactive mode set this flag, so the shell can't | |
226 | # re-enter (it checks this variable at the start of interactive mode). |
|
227 | # re-enter (it checks this variable at the start of interactive mode). | |
227 | self.IP.exit_now = False |
|
228 | self.IP.exit_now = False | |
228 |
|
229 | |||
229 | # Allow the dummy parameter to override the global __dummy_mode |
|
230 | # Allow the dummy parameter to override the global __dummy_mode | |
230 | if dummy or (dummy != 0 and self.__dummy_mode): |
|
231 | if dummy or (dummy != 0 and self.__dummy_mode): | |
231 | return |
|
232 | return | |
232 |
|
233 | |||
233 | # Set global subsystems (display,completions) to our values |
|
234 | # Set global subsystems (display,completions) to our values | |
234 | sys.displayhook = self.sys_displayhook_embed |
|
235 | sys.displayhook = self.sys_displayhook_embed | |
235 | if self.IP.has_readline: |
|
236 | if self.IP.has_readline: | |
236 | self.IP.set_completer() |
|
237 | self.IP.set_completer() | |
237 |
|
238 | |||
238 | if self.banner and header: |
|
239 | if self.banner and header: | |
239 | format = '%s\n%s\n' |
|
240 | format = '%s\n%s\n' | |
240 | else: |
|
241 | else: | |
241 | format = '%s%s\n' |
|
242 | format = '%s%s\n' | |
242 | banner = format % (self.banner,header) |
|
243 | banner = format % (self.banner,header) | |
243 |
|
244 | |||
244 | # Call the embedding code with a stack depth of 1 so it can skip over |
|
245 | # Call the embedding code with a stack depth of 1 so it can skip over | |
245 | # our call and get the original caller's namespaces. |
|
246 | # our call and get the original caller's namespaces. | |
246 | self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1) |
|
247 | self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1) | |
247 |
|
248 | |||
248 | if self.exit_msg: |
|
249 | if self.exit_msg: | |
249 | print self.exit_msg |
|
250 | print self.exit_msg | |
250 |
|
251 | |||
251 | # Restore global systems (display, completion) |
|
252 | # Restore global systems (display, completion) | |
252 | sys.displayhook = self.sys_displayhook_ori |
|
253 | sys.displayhook = self.sys_displayhook_ori | |
253 | self.restore_system_completer() |
|
254 | self.restore_system_completer() | |
254 |
|
255 | |||
255 | def set_dummy_mode(self,dummy): |
|
256 | def set_dummy_mode(self,dummy): | |
256 | """Sets the embeddable shell's dummy mode parameter. |
|
257 | """Sets the embeddable shell's dummy mode parameter. | |
257 |
|
258 | |||
258 | set_dummy_mode(dummy): dummy = 0 or 1. |
|
259 | set_dummy_mode(dummy): dummy = 0 or 1. | |
259 |
|
260 | |||
260 | This parameter is persistent and makes calls to the embeddable shell |
|
261 | This parameter is persistent and makes calls to the embeddable shell | |
261 | silently return without performing any action. This allows you to |
|
262 | silently return without performing any action. This allows you to | |
262 | globally activate or deactivate a shell you're using with a single call. |
|
263 | globally activate or deactivate a shell you're using with a single call. | |
263 |
|
264 | |||
264 | If you need to manually""" |
|
265 | If you need to manually""" | |
265 |
|
266 | |||
266 | if dummy not in [0,1,False,True]: |
|
267 | if dummy not in [0,1,False,True]: | |
267 | raise ValueError,'dummy parameter must be boolean' |
|
268 | raise ValueError,'dummy parameter must be boolean' | |
268 | self.__dummy_mode = dummy |
|
269 | self.__dummy_mode = dummy | |
269 |
|
270 | |||
270 | def get_dummy_mode(self): |
|
271 | def get_dummy_mode(self): | |
271 | """Return the current value of the dummy mode parameter. |
|
272 | """Return the current value of the dummy mode parameter. | |
272 | """ |
|
273 | """ | |
273 | return self.__dummy_mode |
|
274 | return self.__dummy_mode | |
274 |
|
275 | |||
275 | def set_banner(self,banner): |
|
276 | def set_banner(self,banner): | |
276 | """Sets the global banner. |
|
277 | """Sets the global banner. | |
277 |
|
278 | |||
278 | This banner gets prepended to every header printed when the shell |
|
279 | This banner gets prepended to every header printed when the shell | |
279 | instance is called.""" |
|
280 | instance is called.""" | |
280 |
|
281 | |||
281 | self.banner = banner |
|
282 | self.banner = banner | |
282 |
|
283 | |||
283 | def set_exit_msg(self,exit_msg): |
|
284 | def set_exit_msg(self,exit_msg): | |
284 | """Sets the global exit_msg. |
|
285 | """Sets the global exit_msg. | |
285 |
|
286 | |||
286 | This exit message gets printed upon exiting every time the embedded |
|
287 | This exit message gets printed upon exiting every time the embedded | |
287 | shell is called. It is None by default. """ |
|
288 | shell is called. It is None by default. """ | |
288 |
|
289 | |||
289 | self.exit_msg = exit_msg |
|
290 | self.exit_msg = exit_msg | |
290 |
|
291 | |||
291 | #----------------------------------------------------------------------------- |
|
292 | #----------------------------------------------------------------------------- | |
292 | if HAS_CTYPES: |
|
293 | if HAS_CTYPES: | |
293 | # Add async exception support. Trick taken from: |
|
294 | # Add async exception support. Trick taken from: | |
294 | # http://sebulba.wikispaces.com/recipe+thread2 |
|
295 | # http://sebulba.wikispaces.com/recipe+thread2 | |
295 | def _async_raise(tid, exctype): |
|
296 | def _async_raise(tid, exctype): | |
296 | """raises the exception, performs cleanup if needed""" |
|
297 | """raises the exception, performs cleanup if needed""" | |
297 | if not inspect.isclass(exctype): |
|
298 | if not inspect.isclass(exctype): | |
298 | raise TypeError("Only types can be raised (not instances)") |
|
299 | raise TypeError("Only types can be raised (not instances)") | |
299 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, |
|
300 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, | |
300 | ctypes.py_object(exctype)) |
|
301 | ctypes.py_object(exctype)) | |
301 | if res == 0: |
|
302 | if res == 0: | |
302 | raise ValueError("invalid thread id") |
|
303 | raise ValueError("invalid thread id") | |
303 | elif res != 1: |
|
304 | elif res != 1: | |
304 | # """if it returns a number greater than one, you're in trouble, |
|
305 | # """if it returns a number greater than one, you're in trouble, | |
305 | # and you should call it again with exc=NULL to revert the effect""" |
|
306 | # and you should call it again with exc=NULL to revert the effect""" | |
306 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) |
|
307 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) | |
307 | raise SystemError("PyThreadState_SetAsyncExc failed") |
|
308 | raise SystemError("PyThreadState_SetAsyncExc failed") | |
308 |
|
309 | |||
309 | def sigint_handler (signum,stack_frame): |
|
310 | def sigint_handler (signum,stack_frame): | |
310 | """Sigint handler for threaded apps. |
|
311 | """Sigint handler for threaded apps. | |
311 |
|
312 | |||
312 | This is a horrible hack to pass information about SIGINT _without_ |
|
313 | This is a horrible hack to pass information about SIGINT _without_ | |
313 | using exceptions, since I haven't been able to properly manage |
|
314 | using exceptions, since I haven't been able to properly manage | |
314 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
|
315 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be | |
315 | done (or at least that's my understanding from a c.l.py thread where |
|
316 | done (or at least that's my understanding from a c.l.py thread where | |
316 | this was discussed).""" |
|
317 | this was discussed).""" | |
317 |
|
318 | |||
318 | global KBINT |
|
319 | global KBINT | |
319 |
|
320 | |||
320 | if CODE_RUN: |
|
321 | if CODE_RUN: | |
321 | _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) |
|
322 | _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) | |
322 | else: |
|
323 | else: | |
323 | KBINT = True |
|
324 | KBINT = True | |
324 | print '\nKeyboardInterrupt - Press <Enter> to continue.', |
|
325 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
325 | Term.cout.flush() |
|
326 | Term.cout.flush() | |
326 |
|
327 | |||
327 | else: |
|
328 | else: | |
328 | def sigint_handler (signum,stack_frame): |
|
329 | def sigint_handler (signum,stack_frame): | |
329 | """Sigint handler for threaded apps. |
|
330 | """Sigint handler for threaded apps. | |
330 |
|
331 | |||
331 | This is a horrible hack to pass information about SIGINT _without_ |
|
332 | This is a horrible hack to pass information about SIGINT _without_ | |
332 | using exceptions, since I haven't been able to properly manage |
|
333 | using exceptions, since I haven't been able to properly manage | |
333 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
|
334 | cross-thread exceptions in GTK/WX. In fact, I don't think it can be | |
334 | done (or at least that's my understanding from a c.l.py thread where |
|
335 | done (or at least that's my understanding from a c.l.py thread where | |
335 | this was discussed).""" |
|
336 | this was discussed).""" | |
336 |
|
337 | |||
337 | global KBINT |
|
338 | global KBINT | |
338 |
|
339 | |||
339 | print '\nKeyboardInterrupt - Press <Enter> to continue.', |
|
340 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
340 | Term.cout.flush() |
|
341 | Term.cout.flush() | |
341 | # Set global flag so that runsource can know that Ctrl-C was hit |
|
342 | # Set global flag so that runsource can know that Ctrl-C was hit | |
342 | KBINT = True |
|
343 | KBINT = True | |
343 |
|
344 | |||
344 |
|
345 | |||
345 | class MTInteractiveShell(InteractiveShell): |
|
346 | class MTInteractiveShell(InteractiveShell): | |
346 | """Simple multi-threaded shell.""" |
|
347 | """Simple multi-threaded shell.""" | |
347 |
|
348 | |||
348 | # Threading strategy taken from: |
|
349 | # Threading strategy taken from: | |
349 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian |
|
350 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian | |
350 | # McErlean and John Finlay. Modified with corrections by Antoon Pardon, |
|
351 | # McErlean and John Finlay. Modified with corrections by Antoon Pardon, | |
351 | # from the pygtk mailing list, to avoid lockups with system calls. |
|
352 | # from the pygtk mailing list, to avoid lockups with system calls. | |
352 |
|
353 | |||
353 | # class attribute to indicate whether the class supports threads or not. |
|
354 | # class attribute to indicate whether the class supports threads or not. | |
354 | # Subclasses with thread support should override this as needed. |
|
355 | # Subclasses with thread support should override this as needed. | |
355 | isthreaded = True |
|
356 | isthreaded = True | |
356 |
|
357 | |||
357 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
|
358 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), | |
358 | user_ns=None,user_global_ns=None,banner2='', |
|
359 | user_ns=None,user_global_ns=None,banner2='', | |
359 | gui_timeout=GUI_TIMEOUT,**kw): |
|
360 | gui_timeout=GUI_TIMEOUT,**kw): | |
360 | """Similar to the normal InteractiveShell, but with threading control""" |
|
361 | """Similar to the normal InteractiveShell, but with threading control""" | |
361 |
|
362 | |||
362 | InteractiveShell.__init__(self,name,usage,rc,user_ns, |
|
363 | InteractiveShell.__init__(self,name,usage,rc,user_ns, | |
363 | user_global_ns,banner2) |
|
364 | user_global_ns,banner2) | |
364 |
|
365 | |||
365 | # Timeout we wait for GUI thread |
|
366 | # Timeout we wait for GUI thread | |
366 | self.gui_timeout = gui_timeout |
|
367 | self.gui_timeout = gui_timeout | |
367 |
|
368 | |||
368 | # A queue to hold the code to be executed. |
|
369 | # A queue to hold the code to be executed. | |
369 | self.code_queue = Queue.Queue() |
|
370 | self.code_queue = Queue.Queue() | |
370 |
|
371 | |||
371 | # Stuff to do at closing time |
|
372 | # Stuff to do at closing time | |
372 | self._kill = None |
|
373 | self._kill = None | |
373 | on_kill = kw.get('on_kill', []) |
|
374 | on_kill = kw.get('on_kill', []) | |
374 | # Check that all things to kill are callable: |
|
375 | # Check that all things to kill are callable: | |
375 | for t in on_kill: |
|
376 | for t in on_kill: | |
376 | if not callable(t): |
|
377 | if not callable(t): | |
377 | raise TypeError,'on_kill must be a list of callables' |
|
378 | raise TypeError,'on_kill must be a list of callables' | |
378 | self.on_kill = on_kill |
|
379 | self.on_kill = on_kill | |
379 | # thread identity of the "worker thread" (that may execute code directly) |
|
380 | # thread identity of the "worker thread" (that may execute code directly) | |
380 | self.worker_ident = None |
|
381 | self.worker_ident = None | |
381 |
|
382 | |||
382 | def runsource(self, source, filename="<input>", symbol="single"): |
|
383 | def runsource(self, source, filename="<input>", symbol="single"): | |
383 | """Compile and run some source in the interpreter. |
|
384 | """Compile and run some source in the interpreter. | |
384 |
|
385 | |||
385 | Modified version of code.py's runsource(), to handle threading issues. |
|
386 | Modified version of code.py's runsource(), to handle threading issues. | |
386 | See the original for full docstring details.""" |
|
387 | See the original for full docstring details.""" | |
387 |
|
388 | |||
388 | global KBINT |
|
389 | global KBINT | |
389 |
|
390 | |||
390 | # If Ctrl-C was typed, we reset the flag and return right away |
|
391 | # If Ctrl-C was typed, we reset the flag and return right away | |
391 | if KBINT: |
|
392 | if KBINT: | |
392 | KBINT = False |
|
393 | KBINT = False | |
393 | return False |
|
394 | return False | |
394 |
|
395 | |||
395 | if self._kill: |
|
396 | if self._kill: | |
396 | # can't queue new code if we are being killed |
|
397 | # can't queue new code if we are being killed | |
397 | return True |
|
398 | return True | |
398 |
|
399 | |||
399 | try: |
|
400 | try: | |
400 | code = self.compile(source, filename, symbol) |
|
401 | code = self.compile(source, filename, symbol) | |
401 | except (OverflowError, SyntaxError, ValueError): |
|
402 | except (OverflowError, SyntaxError, ValueError): | |
402 | # Case 1 |
|
403 | # Case 1 | |
403 | self.showsyntaxerror(filename) |
|
404 | self.showsyntaxerror(filename) | |
404 | return False |
|
405 | return False | |
405 |
|
406 | |||
406 | if code is None: |
|
407 | if code is None: | |
407 | # Case 2 |
|
408 | # Case 2 | |
408 | return True |
|
409 | return True | |
409 |
|
410 | |||
410 | # shortcut - if we are in worker thread, or the worker thread is not |
|
411 | # shortcut - if we are in worker thread, or the worker thread is not | |
411 | # running, execute directly (to allow recursion and prevent deadlock if |
|
412 | # running, execute directly (to allow recursion and prevent deadlock if | |
412 | # code is run early in IPython construction) |
|
413 | # code is run early in IPython construction) | |
413 |
|
414 | |||
414 | if (self.worker_ident is None |
|
415 | if (self.worker_ident is None | |
415 | or self.worker_ident == thread.get_ident() ): |
|
416 | or self.worker_ident == thread.get_ident() ): | |
416 | InteractiveShell.runcode(self,code) |
|
417 | InteractiveShell.runcode(self,code) | |
417 | return |
|
418 | return | |
418 |
|
419 | |||
419 | # Case 3 |
|
420 | # Case 3 | |
420 | # Store code in queue, so the execution thread can handle it. |
|
421 | # Store code in queue, so the execution thread can handle it. | |
421 |
|
422 | |||
422 | completed_ev, received_ev = threading.Event(), threading.Event() |
|
423 | completed_ev, received_ev = threading.Event(), threading.Event() | |
423 |
|
424 | |||
424 | self.code_queue.put((code,completed_ev, received_ev)) |
|
425 | self.code_queue.put((code,completed_ev, received_ev)) | |
425 | # first make sure the message was received, with timeout |
|
426 | # first make sure the message was received, with timeout | |
426 | received_ev.wait(self.gui_timeout) |
|
427 | received_ev.wait(self.gui_timeout) | |
427 | if not received_ev.isSet(): |
|
428 | if not received_ev.isSet(): | |
428 | # the mainloop is dead, start executing code directly |
|
429 | # the mainloop is dead, start executing code directly | |
429 | print "Warning: Timeout for mainloop thread exceeded" |
|
430 | print "Warning: Timeout for mainloop thread exceeded" | |
430 | print "switching to nonthreaded mode (until mainloop wakes up again)" |
|
431 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |
431 | self.worker_ident = None |
|
432 | self.worker_ident = None | |
432 | else: |
|
433 | else: | |
433 | completed_ev.wait() |
|
434 | completed_ev.wait() | |
434 | return False |
|
435 | return False | |
435 |
|
436 | |||
436 | def runcode(self): |
|
437 | def runcode(self): | |
437 | """Execute a code object. |
|
438 | """Execute a code object. | |
438 |
|
439 | |||
439 | Multithreaded wrapper around IPython's runcode().""" |
|
440 | Multithreaded wrapper around IPython's runcode().""" | |
440 |
|
441 | |||
441 | global CODE_RUN |
|
442 | global CODE_RUN | |
442 |
|
443 | |||
443 | # we are in worker thread, stash out the id for runsource() |
|
444 | # we are in worker thread, stash out the id for runsource() | |
444 | self.worker_ident = thread.get_ident() |
|
445 | self.worker_ident = thread.get_ident() | |
445 |
|
446 | |||
446 | if self._kill: |
|
447 | if self._kill: | |
447 | print >>Term.cout, 'Closing threads...', |
|
448 | print >>Term.cout, 'Closing threads...', | |
448 | Term.cout.flush() |
|
449 | Term.cout.flush() | |
449 | for tokill in self.on_kill: |
|
450 | for tokill in self.on_kill: | |
450 | tokill() |
|
451 | tokill() | |
451 | print >>Term.cout, 'Done.' |
|
452 | print >>Term.cout, 'Done.' | |
452 | # allow kill() to return |
|
453 | # allow kill() to return | |
453 | self._kill.set() |
|
454 | self._kill.set() | |
454 | return True |
|
455 | return True | |
455 |
|
456 | |||
456 | # Install sigint handler. We do it every time to ensure that if user |
|
457 | # Install sigint handler. We do it every time to ensure that if user | |
457 | # code modifies it, we restore our own handling. |
|
458 | # code modifies it, we restore our own handling. | |
458 | try: |
|
459 | try: | |
459 | signal(SIGINT,sigint_handler) |
|
460 | signal(SIGINT,sigint_handler) | |
460 | except SystemError: |
|
461 | except SystemError: | |
461 | # This happens under Windows, which seems to have all sorts |
|
462 | # This happens under Windows, which seems to have all sorts | |
462 | # of problems with signal handling. Oh well... |
|
463 | # of problems with signal handling. Oh well... | |
463 | pass |
|
464 | pass | |
464 |
|
465 | |||
465 | # Flush queue of pending code by calling the run methood of the parent |
|
466 | # Flush queue of pending code by calling the run methood of the parent | |
466 | # class with all items which may be in the queue. |
|
467 | # class with all items which may be in the queue. | |
467 | code_to_run = None |
|
468 | code_to_run = None | |
468 | while 1: |
|
469 | while 1: | |
469 | try: |
|
470 | try: | |
470 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() |
|
471 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() | |
471 | except Queue.Empty: |
|
472 | except Queue.Empty: | |
472 | break |
|
473 | break | |
473 | received_ev.set() |
|
474 | received_ev.set() | |
474 |
|
475 | |||
475 | # Exceptions need to be raised differently depending on which |
|
476 | # Exceptions need to be raised differently depending on which | |
476 | # thread is active. This convoluted try/except is only there to |
|
477 | # thread is active. This convoluted try/except is only there to | |
477 | # protect against asynchronous exceptions, to ensure that a KBINT |
|
478 | # protect against asynchronous exceptions, to ensure that a KBINT | |
478 | # at the wrong time doesn't deadlock everything. The global |
|
479 | # at the wrong time doesn't deadlock everything. The global | |
479 | # CODE_TO_RUN is set to true/false as close as possible to the |
|
480 | # CODE_TO_RUN is set to true/false as close as possible to the | |
480 | # runcode() call, so that the KBINT handler is correctly informed. |
|
481 | # runcode() call, so that the KBINT handler is correctly informed. | |
481 | try: |
|
482 | try: | |
482 | try: |
|
483 | try: | |
483 | CODE_RUN = True |
|
484 | CODE_RUN = True | |
484 | InteractiveShell.runcode(self,code_to_run) |
|
485 | InteractiveShell.runcode(self,code_to_run) | |
485 | except KeyboardInterrupt: |
|
486 | except KeyboardInterrupt: | |
486 | print "Keyboard interrupted in mainloop" |
|
487 | print "Keyboard interrupted in mainloop" | |
487 | while not self.code_queue.empty(): |
|
488 | while not self.code_queue.empty(): | |
488 | code, ev1,ev2 = self.code_queue.get_nowait() |
|
489 | code, ev1,ev2 = self.code_queue.get_nowait() | |
489 | ev1.set() |
|
490 | ev1.set() | |
490 | ev2.set() |
|
491 | ev2.set() | |
491 | break |
|
492 | break | |
492 | finally: |
|
493 | finally: | |
493 | CODE_RUN = False |
|
494 | CODE_RUN = False | |
494 | # allow runsource() return from wait |
|
495 | # allow runsource() return from wait | |
495 | completed_ev.set() |
|
496 | completed_ev.set() | |
496 |
|
497 | |||
497 |
|
498 | |||
498 | # This MUST return true for gtk threading to work |
|
499 | # This MUST return true for gtk threading to work | |
499 | return True |
|
500 | return True | |
500 |
|
501 | |||
501 | def kill(self): |
|
502 | def kill(self): | |
502 | """Kill the thread, returning when it has been shut down.""" |
|
503 | """Kill the thread, returning when it has been shut down.""" | |
503 | self._kill = threading.Event() |
|
504 | self._kill = threading.Event() | |
504 | self._kill.wait() |
|
505 | self._kill.wait() | |
505 |
|
506 | |||
506 | class MatplotlibShellBase: |
|
507 | class MatplotlibShellBase: | |
507 | """Mixin class to provide the necessary modifications to regular IPython |
|
508 | """Mixin class to provide the necessary modifications to regular IPython | |
508 | shell classes for matplotlib support. |
|
509 | shell classes for matplotlib support. | |
509 |
|
510 | |||
510 | Given Python's MRO, this should be used as the FIRST class in the |
|
511 | Given Python's MRO, this should be used as the FIRST class in the | |
511 | inheritance hierarchy, so that it overrides the relevant methods.""" |
|
512 | inheritance hierarchy, so that it overrides the relevant methods.""" | |
512 |
|
513 | |||
513 | def _matplotlib_config(self,name,user_ns,user_global_ns=None): |
|
514 | def _matplotlib_config(self,name,user_ns,user_global_ns=None): | |
514 | """Return items needed to setup the user's shell with matplotlib""" |
|
515 | """Return items needed to setup the user's shell with matplotlib""" | |
515 |
|
516 | |||
516 | # Initialize matplotlib to interactive mode always |
|
517 | # Initialize matplotlib to interactive mode always | |
517 | import matplotlib |
|
518 | import matplotlib | |
518 | from matplotlib import backends |
|
519 | from matplotlib import backends | |
519 | matplotlib.interactive(True) |
|
520 | matplotlib.interactive(True) | |
520 |
|
521 | |||
521 | def use(arg): |
|
522 | def use(arg): | |
522 | """IPython wrapper for matplotlib's backend switcher. |
|
523 | """IPython wrapper for matplotlib's backend switcher. | |
523 |
|
524 | |||
524 | In interactive use, we can not allow switching to a different |
|
525 | In interactive use, we can not allow switching to a different | |
525 | interactive backend, since thread conflicts will most likely crash |
|
526 | interactive backend, since thread conflicts will most likely crash | |
526 | the python interpreter. This routine does a safety check first, |
|
527 | the python interpreter. This routine does a safety check first, | |
527 | and refuses to perform a dangerous switch. It still allows |
|
528 | and refuses to perform a dangerous switch. It still allows | |
528 | switching to non-interactive backends.""" |
|
529 | switching to non-interactive backends.""" | |
529 |
|
530 | |||
530 | if arg in backends.interactive_bk and arg != self.mpl_backend: |
|
531 | if arg in backends.interactive_bk and arg != self.mpl_backend: | |
531 | m=('invalid matplotlib backend switch.\n' |
|
532 | m=('invalid matplotlib backend switch.\n' | |
532 | 'This script attempted to switch to the interactive ' |
|
533 | 'This script attempted to switch to the interactive ' | |
533 | 'backend: `%s`\n' |
|
534 | 'backend: `%s`\n' | |
534 | 'Your current choice of interactive backend is: `%s`\n\n' |
|
535 | 'Your current choice of interactive backend is: `%s`\n\n' | |
535 | 'Switching interactive matplotlib backends at runtime\n' |
|
536 | 'Switching interactive matplotlib backends at runtime\n' | |
536 | 'would crash the python interpreter, ' |
|
537 | 'would crash the python interpreter, ' | |
537 | 'and IPython has blocked it.\n\n' |
|
538 | 'and IPython has blocked it.\n\n' | |
538 | 'You need to either change your choice of matplotlib backend\n' |
|
539 | 'You need to either change your choice of matplotlib backend\n' | |
539 | 'by editing your .matplotlibrc file, or run this script as a \n' |
|
540 | 'by editing your .matplotlibrc file, or run this script as a \n' | |
540 | 'standalone file from the command line, not using IPython.\n' % |
|
541 | 'standalone file from the command line, not using IPython.\n' % | |
541 | (arg,self.mpl_backend) ) |
|
542 | (arg,self.mpl_backend) ) | |
542 | raise RuntimeError, m |
|
543 | raise RuntimeError, m | |
543 | else: |
|
544 | else: | |
544 | self.mpl_use(arg) |
|
545 | self.mpl_use(arg) | |
545 | self.mpl_use._called = True |
|
546 | self.mpl_use._called = True | |
546 |
|
547 | |||
547 | self.matplotlib = matplotlib |
|
548 | self.matplotlib = matplotlib | |
548 | self.mpl_backend = matplotlib.rcParams['backend'] |
|
549 | self.mpl_backend = matplotlib.rcParams['backend'] | |
549 |
|
550 | |||
550 | # we also need to block switching of interactive backends by use() |
|
551 | # we also need to block switching of interactive backends by use() | |
551 | self.mpl_use = matplotlib.use |
|
552 | self.mpl_use = matplotlib.use | |
552 | self.mpl_use._called = False |
|
553 | self.mpl_use._called = False | |
553 | # overwrite the original matplotlib.use with our wrapper |
|
554 | # overwrite the original matplotlib.use with our wrapper | |
554 | matplotlib.use = use |
|
555 | matplotlib.use = use | |
555 |
|
556 | |||
556 | # This must be imported last in the matplotlib series, after |
|
557 | # This must be imported last in the matplotlib series, after | |
557 | # backend/interactivity choices have been made |
|
558 | # backend/interactivity choices have been made | |
558 | import matplotlib.pylab as pylab |
|
559 | import matplotlib.pylab as pylab | |
559 | self.pylab = pylab |
|
560 | self.pylab = pylab | |
560 |
|
561 | |||
561 | self.pylab.show._needmain = False |
|
562 | self.pylab.show._needmain = False | |
562 | # We need to detect at runtime whether show() is called by the user. |
|
563 | # We need to detect at runtime whether show() is called by the user. | |
563 | # For this, we wrap it into a decorator which adds a 'called' flag. |
|
564 | # For this, we wrap it into a decorator which adds a 'called' flag. | |
564 | self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive) |
|
565 | self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive) | |
565 |
|
566 | |||
566 | # Build a user namespace initialized with matplotlib/matlab features. |
|
567 | # Build a user namespace initialized with matplotlib/matlab features. | |
567 | user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns, |
|
568 | user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns, | |
568 | user_global_ns) |
|
569 | user_global_ns) | |
569 |
|
570 | |||
570 | # Import numpy as np/pyplot as plt are conventions we're trying to |
|
571 | # Import numpy as np/pyplot as plt are conventions we're trying to | |
571 | # somewhat standardize on. Making them available to users by default |
|
572 | # somewhat standardize on. Making them available to users by default | |
572 | # will greatly help this. |
|
573 | # will greatly help this. | |
573 | exec ("import numpy\n" |
|
574 | exec ("import numpy\n" | |
574 | "import numpy as np\n" |
|
575 | "import numpy as np\n" | |
575 | "import matplotlib\n" |
|
576 | "import matplotlib\n" | |
576 | "import matplotlib.pylab as pylab\n" |
|
577 | "import matplotlib.pylab as pylab\n" | |
577 | "try:\n" |
|
578 | "try:\n" | |
578 | " import matplotlib.pyplot as plt\n" |
|
579 | " import matplotlib.pyplot as plt\n" | |
579 | "except ImportError:\n" |
|
580 | "except ImportError:\n" | |
580 | " pass\n" |
|
581 | " pass\n" | |
581 | ) in user_ns |
|
582 | ) in user_ns | |
582 |
|
583 | |||
583 | # Build matplotlib info banner |
|
584 | # Build matplotlib info banner | |
584 | b=""" |
|
585 | b=""" | |
585 | Welcome to pylab, a matplotlib-based Python environment. |
|
586 | Welcome to pylab, a matplotlib-based Python environment. | |
586 | For more information, type 'help(pylab)'. |
|
587 | For more information, type 'help(pylab)'. | |
587 | """ |
|
588 | """ | |
588 | return user_ns,user_global_ns,b |
|
589 | return user_ns,user_global_ns,b | |
589 |
|
590 | |||
590 | def mplot_exec(self,fname,*where,**kw): |
|
591 | def mplot_exec(self,fname,*where,**kw): | |
591 | """Execute a matplotlib script. |
|
592 | """Execute a matplotlib script. | |
592 |
|
593 | |||
593 | This is a call to execfile(), but wrapped in safeties to properly |
|
594 | This is a call to execfile(), but wrapped in safeties to properly | |
594 | handle interactive rendering and backend switching.""" |
|
595 | handle interactive rendering and backend switching.""" | |
595 |
|
596 | |||
596 | #print '*** Matplotlib runner ***' # dbg |
|
597 | #print '*** Matplotlib runner ***' # dbg | |
597 | # turn off rendering until end of script |
|
598 | # turn off rendering until end of script | |
598 | isInteractive = self.matplotlib.rcParams['interactive'] |
|
599 | isInteractive = self.matplotlib.rcParams['interactive'] | |
599 | self.matplotlib.interactive(False) |
|
600 | self.matplotlib.interactive(False) | |
600 | self.safe_execfile(fname,*where,**kw) |
|
601 | self.safe_execfile(fname,*where,**kw) | |
601 | self.matplotlib.interactive(isInteractive) |
|
602 | self.matplotlib.interactive(isInteractive) | |
602 | # make rendering call now, if the user tried to do it |
|
603 | # make rendering call now, if the user tried to do it | |
603 | if self.pylab.draw_if_interactive.called: |
|
604 | if self.pylab.draw_if_interactive.called: | |
604 | self.pylab.draw() |
|
605 | self.pylab.draw() | |
605 | self.pylab.draw_if_interactive.called = False |
|
606 | self.pylab.draw_if_interactive.called = False | |
606 |
|
607 | |||
607 | # if a backend switch was performed, reverse it now |
|
608 | # if a backend switch was performed, reverse it now | |
608 | if self.mpl_use._called: |
|
609 | if self.mpl_use._called: | |
609 | self.matplotlib.rcParams['backend'] = self.mpl_backend |
|
610 | self.matplotlib.rcParams['backend'] = self.mpl_backend | |
610 |
|
611 | |||
|
612 | @testdec.skip_doctest | |||
611 | def magic_run(self,parameter_s=''): |
|
613 | def magic_run(self,parameter_s=''): | |
612 | Magic.magic_run(self,parameter_s,runner=self.mplot_exec) |
|
614 | Magic.magic_run(self,parameter_s,runner=self.mplot_exec) | |
613 |
|
615 | |||
614 | # Fix the docstring so users see the original as well |
|
616 | # Fix the docstring so users see the original as well | |
615 | magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__, |
|
617 | magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__, | |
616 | "\n *** Modified %run for Matplotlib," |
|
618 | "\n *** Modified %run for Matplotlib," | |
617 | " with proper interactive handling ***") |
|
619 | " with proper interactive handling ***") | |
618 |
|
620 | |||
619 | # Now we provide 2 versions of a matplotlib-aware IPython base shells, single |
|
621 | # Now we provide 2 versions of a matplotlib-aware IPython base shells, single | |
620 | # and multithreaded. Note that these are meant for internal use, the IPShell* |
|
622 | # and multithreaded. Note that these are meant for internal use, the IPShell* | |
621 | # classes below are the ones meant for public consumption. |
|
623 | # classes below are the ones meant for public consumption. | |
622 |
|
624 | |||
623 | class MatplotlibShell(MatplotlibShellBase,InteractiveShell): |
|
625 | class MatplotlibShell(MatplotlibShellBase,InteractiveShell): | |
624 | """Single-threaded shell with matplotlib support.""" |
|
626 | """Single-threaded shell with matplotlib support.""" | |
625 |
|
627 | |||
626 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
|
628 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), | |
627 | user_ns=None,user_global_ns=None,**kw): |
|
629 | user_ns=None,user_global_ns=None,**kw): | |
628 | user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) |
|
630 | user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) | |
629 | InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, |
|
631 | InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, | |
630 | banner2=b2,**kw) |
|
632 | banner2=b2,**kw) | |
631 |
|
633 | |||
632 | class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell): |
|
634 | class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell): | |
633 | """Multi-threaded shell with matplotlib support.""" |
|
635 | """Multi-threaded shell with matplotlib support.""" | |
634 |
|
636 | |||
635 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
|
637 | def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), | |
636 | user_ns=None,user_global_ns=None, **kw): |
|
638 | user_ns=None,user_global_ns=None, **kw): | |
637 | user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) |
|
639 | user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) | |
638 | MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, |
|
640 | MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, | |
639 | banner2=b2,**kw) |
|
641 | banner2=b2,**kw) | |
640 |
|
642 | |||
641 | #----------------------------------------------------------------------------- |
|
643 | #----------------------------------------------------------------------------- | |
642 | # Utility functions for the different GUI enabled IPShell* classes. |
|
644 | # Utility functions for the different GUI enabled IPShell* classes. | |
643 |
|
645 | |||
644 | def get_tk(): |
|
646 | def get_tk(): | |
645 | """Tries to import Tkinter and returns a withdrawn Tkinter root |
|
647 | """Tries to import Tkinter and returns a withdrawn Tkinter root | |
646 | window. If Tkinter is already imported or not available, this |
|
648 | window. If Tkinter is already imported or not available, this | |
647 | returns None. This function calls `hijack_tk` underneath. |
|
649 | returns None. This function calls `hijack_tk` underneath. | |
648 | """ |
|
650 | """ | |
649 | if not USE_TK or sys.modules.has_key('Tkinter'): |
|
651 | if not USE_TK or sys.modules.has_key('Tkinter'): | |
650 | return None |
|
652 | return None | |
651 | else: |
|
653 | else: | |
652 | try: |
|
654 | try: | |
653 | import Tkinter |
|
655 | import Tkinter | |
654 | except ImportError: |
|
656 | except ImportError: | |
655 | return None |
|
657 | return None | |
656 | else: |
|
658 | else: | |
657 | hijack_tk() |
|
659 | hijack_tk() | |
658 | r = Tkinter.Tk() |
|
660 | r = Tkinter.Tk() | |
659 | r.withdraw() |
|
661 | r.withdraw() | |
660 | return r |
|
662 | return r | |
661 |
|
663 | |||
662 | def hijack_tk(): |
|
664 | def hijack_tk(): | |
663 | """Modifies Tkinter's mainloop with a dummy so when a module calls |
|
665 | """Modifies Tkinter's mainloop with a dummy so when a module calls | |
664 | mainloop, it does not block. |
|
666 | mainloop, it does not block. | |
665 |
|
667 | |||
666 | """ |
|
668 | """ | |
667 | def misc_mainloop(self, n=0): |
|
669 | def misc_mainloop(self, n=0): | |
668 | pass |
|
670 | pass | |
669 | def tkinter_mainloop(n=0): |
|
671 | def tkinter_mainloop(n=0): | |
670 | pass |
|
672 | pass | |
671 |
|
673 | |||
672 | import Tkinter |
|
674 | import Tkinter | |
673 | Tkinter.Misc.mainloop = misc_mainloop |
|
675 | Tkinter.Misc.mainloop = misc_mainloop | |
674 | Tkinter.mainloop = tkinter_mainloop |
|
676 | Tkinter.mainloop = tkinter_mainloop | |
675 |
|
677 | |||
676 | def update_tk(tk): |
|
678 | def update_tk(tk): | |
677 | """Updates the Tkinter event loop. This is typically called from |
|
679 | """Updates the Tkinter event loop. This is typically called from | |
678 | the respective WX or GTK mainloops. |
|
680 | the respective WX or GTK mainloops. | |
679 | """ |
|
681 | """ | |
680 | if tk: |
|
682 | if tk: | |
681 | tk.update() |
|
683 | tk.update() | |
682 |
|
684 | |||
683 | def hijack_wx(): |
|
685 | def hijack_wx(): | |
684 | """Modifies wxPython's MainLoop with a dummy so user code does not |
|
686 | """Modifies wxPython's MainLoop with a dummy so user code does not | |
685 | block IPython. The hijacked mainloop function is returned. |
|
687 | block IPython. The hijacked mainloop function is returned. | |
686 | """ |
|
688 | """ | |
687 | def dummy_mainloop(*args, **kw): |
|
689 | def dummy_mainloop(*args, **kw): | |
688 | pass |
|
690 | pass | |
689 |
|
691 | |||
690 | try: |
|
692 | try: | |
691 | import wx |
|
693 | import wx | |
692 | except ImportError: |
|
694 | except ImportError: | |
693 | # For very old versions of WX |
|
695 | # For very old versions of WX | |
694 | import wxPython as wx |
|
696 | import wxPython as wx | |
695 |
|
697 | |||
696 | ver = wx.__version__ |
|
698 | ver = wx.__version__ | |
697 | orig_mainloop = None |
|
699 | orig_mainloop = None | |
698 | if ver[:3] >= '2.5': |
|
700 | if ver[:3] >= '2.5': | |
699 | import wx |
|
701 | import wx | |
700 | if hasattr(wx, '_core_'): core = getattr(wx, '_core_') |
|
702 | if hasattr(wx, '_core_'): core = getattr(wx, '_core_') | |
701 | elif hasattr(wx, '_core'): core = getattr(wx, '_core') |
|
703 | elif hasattr(wx, '_core'): core = getattr(wx, '_core') | |
702 | else: raise AttributeError('Could not find wx core module') |
|
704 | else: raise AttributeError('Could not find wx core module') | |
703 | orig_mainloop = core.PyApp_MainLoop |
|
705 | orig_mainloop = core.PyApp_MainLoop | |
704 | core.PyApp_MainLoop = dummy_mainloop |
|
706 | core.PyApp_MainLoop = dummy_mainloop | |
705 | elif ver[:3] == '2.4': |
|
707 | elif ver[:3] == '2.4': | |
706 | orig_mainloop = wx.wxc.wxPyApp_MainLoop |
|
708 | orig_mainloop = wx.wxc.wxPyApp_MainLoop | |
707 | wx.wxc.wxPyApp_MainLoop = dummy_mainloop |
|
709 | wx.wxc.wxPyApp_MainLoop = dummy_mainloop | |
708 | else: |
|
710 | else: | |
709 | warn("Unable to find either wxPython version 2.4 or >= 2.5.") |
|
711 | warn("Unable to find either wxPython version 2.4 or >= 2.5.") | |
710 | return orig_mainloop |
|
712 | return orig_mainloop | |
711 |
|
713 | |||
712 | def hijack_gtk(): |
|
714 | def hijack_gtk(): | |
713 | """Modifies pyGTK's mainloop with a dummy so user code does not |
|
715 | """Modifies pyGTK's mainloop with a dummy so user code does not | |
714 | block IPython. This function returns the original `gtk.mainloop` |
|
716 | block IPython. This function returns the original `gtk.mainloop` | |
715 | function that has been hijacked. |
|
717 | function that has been hijacked. | |
716 | """ |
|
718 | """ | |
717 | def dummy_mainloop(*args, **kw): |
|
719 | def dummy_mainloop(*args, **kw): | |
718 | pass |
|
720 | pass | |
719 | import gtk |
|
721 | import gtk | |
720 | if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main |
|
722 | if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main | |
721 | else: orig_mainloop = gtk.mainloop |
|
723 | else: orig_mainloop = gtk.mainloop | |
722 | gtk.mainloop = dummy_mainloop |
|
724 | gtk.mainloop = dummy_mainloop | |
723 | gtk.main = dummy_mainloop |
|
725 | gtk.main = dummy_mainloop | |
724 | return orig_mainloop |
|
726 | return orig_mainloop | |
725 |
|
727 | |||
726 | def hijack_qt(): |
|
728 | def hijack_qt(): | |
727 | """Modifies PyQt's mainloop with a dummy so user code does not |
|
729 | """Modifies PyQt's mainloop with a dummy so user code does not | |
728 | block IPython. This function returns the original |
|
730 | block IPython. This function returns the original | |
729 | `qt.qApp.exec_loop` function that has been hijacked. |
|
731 | `qt.qApp.exec_loop` function that has been hijacked. | |
730 | """ |
|
732 | """ | |
731 | def dummy_mainloop(*args, **kw): |
|
733 | def dummy_mainloop(*args, **kw): | |
732 | pass |
|
734 | pass | |
733 | import qt |
|
735 | import qt | |
734 | orig_mainloop = qt.qApp.exec_loop |
|
736 | orig_mainloop = qt.qApp.exec_loop | |
735 | qt.qApp.exec_loop = dummy_mainloop |
|
737 | qt.qApp.exec_loop = dummy_mainloop | |
736 | qt.QApplication.exec_loop = dummy_mainloop |
|
738 | qt.QApplication.exec_loop = dummy_mainloop | |
737 | return orig_mainloop |
|
739 | return orig_mainloop | |
738 |
|
740 | |||
739 | def hijack_qt4(): |
|
741 | def hijack_qt4(): | |
740 | """Modifies PyQt4's mainloop with a dummy so user code does not |
|
742 | """Modifies PyQt4's mainloop with a dummy so user code does not | |
741 | block IPython. This function returns the original |
|
743 | block IPython. This function returns the original | |
742 | `QtGui.qApp.exec_` function that has been hijacked. |
|
744 | `QtGui.qApp.exec_` function that has been hijacked. | |
743 | """ |
|
745 | """ | |
744 | def dummy_mainloop(*args, **kw): |
|
746 | def dummy_mainloop(*args, **kw): | |
745 | pass |
|
747 | pass | |
746 | from PyQt4 import QtGui, QtCore |
|
748 | from PyQt4 import QtGui, QtCore | |
747 | orig_mainloop = QtGui.qApp.exec_ |
|
749 | orig_mainloop = QtGui.qApp.exec_ | |
748 | QtGui.qApp.exec_ = dummy_mainloop |
|
750 | QtGui.qApp.exec_ = dummy_mainloop | |
749 | QtGui.QApplication.exec_ = dummy_mainloop |
|
751 | QtGui.QApplication.exec_ = dummy_mainloop | |
750 | QtCore.QCoreApplication.exec_ = dummy_mainloop |
|
752 | QtCore.QCoreApplication.exec_ = dummy_mainloop | |
751 | return orig_mainloop |
|
753 | return orig_mainloop | |
752 |
|
754 | |||
753 | #----------------------------------------------------------------------------- |
|
755 | #----------------------------------------------------------------------------- | |
754 | # The IPShell* classes below are the ones meant to be run by external code as |
|
756 | # The IPShell* classes below are the ones meant to be run by external code as | |
755 | # IPython instances. Note that unless a specific threading strategy is |
|
757 | # IPython instances. Note that unless a specific threading strategy is | |
756 | # desired, the factory function start() below should be used instead (it |
|
758 | # desired, the factory function start() below should be used instead (it | |
757 | # selects the proper threaded class). |
|
759 | # selects the proper threaded class). | |
758 |
|
760 | |||
759 | class IPThread(threading.Thread): |
|
761 | class IPThread(threading.Thread): | |
760 | def run(self): |
|
762 | def run(self): | |
761 | self.IP.mainloop(self._banner) |
|
763 | self.IP.mainloop(self._banner) | |
762 | self.IP.kill() |
|
764 | self.IP.kill() | |
763 |
|
765 | |||
764 | class IPShellGTK(IPThread): |
|
766 | class IPShellGTK(IPThread): | |
765 | """Run a gtk mainloop() in a separate thread. |
|
767 | """Run a gtk mainloop() in a separate thread. | |
766 |
|
768 | |||
767 | Python commands can be passed to the thread where they will be executed. |
|
769 | Python commands can be passed to the thread where they will be executed. | |
768 | This is implemented by periodically checking for passed code using a |
|
770 | This is implemented by periodically checking for passed code using a | |
769 | GTK timeout callback.""" |
|
771 | GTK timeout callback.""" | |
770 |
|
772 | |||
771 | TIMEOUT = 100 # Millisecond interval between timeouts. |
|
773 | TIMEOUT = 100 # Millisecond interval between timeouts. | |
772 |
|
774 | |||
773 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
|
775 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, | |
774 | debug=1,shell_class=MTInteractiveShell): |
|
776 | debug=1,shell_class=MTInteractiveShell): | |
775 |
|
777 | |||
776 | import gtk |
|
778 | import gtk | |
777 |
|
779 | |||
778 | self.gtk = gtk |
|
780 | self.gtk = gtk | |
779 | self.gtk_mainloop = hijack_gtk() |
|
781 | self.gtk_mainloop = hijack_gtk() | |
780 |
|
782 | |||
781 | # Allows us to use both Tk and GTK. |
|
783 | # Allows us to use both Tk and GTK. | |
782 | self.tk = get_tk() |
|
784 | self.tk = get_tk() | |
783 |
|
785 | |||
784 | if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit |
|
786 | if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit | |
785 | else: mainquit = self.gtk.mainquit |
|
787 | else: mainquit = self.gtk.mainquit | |
786 |
|
788 | |||
787 | self.IP = make_IPython(argv,user_ns=user_ns, |
|
789 | self.IP = make_IPython(argv,user_ns=user_ns, | |
788 | user_global_ns=user_global_ns, |
|
790 | user_global_ns=user_global_ns, | |
789 | debug=debug, |
|
791 | debug=debug, | |
790 | shell_class=shell_class, |
|
792 | shell_class=shell_class, | |
791 | on_kill=[mainquit]) |
|
793 | on_kill=[mainquit]) | |
792 |
|
794 | |||
793 | # HACK: slot for banner in self; it will be passed to the mainloop |
|
795 | # HACK: slot for banner in self; it will be passed to the mainloop | |
794 | # method only and .run() needs it. The actual value will be set by |
|
796 | # method only and .run() needs it. The actual value will be set by | |
795 | # .mainloop(). |
|
797 | # .mainloop(). | |
796 | self._banner = None |
|
798 | self._banner = None | |
797 |
|
799 | |||
798 | threading.Thread.__init__(self) |
|
800 | threading.Thread.__init__(self) | |
799 |
|
801 | |||
800 | def mainloop(self,sys_exit=0,banner=None): |
|
802 | def mainloop(self,sys_exit=0,banner=None): | |
801 |
|
803 | |||
802 | self._banner = banner |
|
804 | self._banner = banner | |
803 |
|
805 | |||
804 | if self.gtk.pygtk_version >= (2,4,0): |
|
806 | if self.gtk.pygtk_version >= (2,4,0): | |
805 | import gobject |
|
807 | import gobject | |
806 | gobject.idle_add(self.on_timer) |
|
808 | gobject.idle_add(self.on_timer) | |
807 | else: |
|
809 | else: | |
808 | self.gtk.idle_add(self.on_timer) |
|
810 | self.gtk.idle_add(self.on_timer) | |
809 |
|
811 | |||
810 | if sys.platform != 'win32': |
|
812 | if sys.platform != 'win32': | |
811 | try: |
|
813 | try: | |
812 | if self.gtk.gtk_version[0] >= 2: |
|
814 | if self.gtk.gtk_version[0] >= 2: | |
813 | self.gtk.gdk.threads_init() |
|
815 | self.gtk.gdk.threads_init() | |
814 | except AttributeError: |
|
816 | except AttributeError: | |
815 | pass |
|
817 | pass | |
816 | except RuntimeError: |
|
818 | except RuntimeError: | |
817 | error('Your pyGTK likely has not been compiled with ' |
|
819 | error('Your pyGTK likely has not been compiled with ' | |
818 | 'threading support.\n' |
|
820 | 'threading support.\n' | |
819 | 'The exception printout is below.\n' |
|
821 | 'The exception printout is below.\n' | |
820 | 'You can either rebuild pyGTK with threads, or ' |
|
822 | 'You can either rebuild pyGTK with threads, or ' | |
821 | 'try using \n' |
|
823 | 'try using \n' | |
822 | 'matplotlib with a different backend (like Tk or WX).\n' |
|
824 | 'matplotlib with a different backend (like Tk or WX).\n' | |
823 | 'Note that matplotlib will most likely not work in its ' |
|
825 | 'Note that matplotlib will most likely not work in its ' | |
824 | 'current state!') |
|
826 | 'current state!') | |
825 | self.IP.InteractiveTB() |
|
827 | self.IP.InteractiveTB() | |
826 |
|
828 | |||
827 | self.start() |
|
829 | self.start() | |
828 | self.gtk.gdk.threads_enter() |
|
830 | self.gtk.gdk.threads_enter() | |
829 | self.gtk_mainloop() |
|
831 | self.gtk_mainloop() | |
830 | self.gtk.gdk.threads_leave() |
|
832 | self.gtk.gdk.threads_leave() | |
831 | self.join() |
|
833 | self.join() | |
832 |
|
834 | |||
833 | def on_timer(self): |
|
835 | def on_timer(self): | |
834 | """Called when GTK is idle. |
|
836 | """Called when GTK is idle. | |
835 |
|
837 | |||
836 | Must return True always, otherwise GTK stops calling it""" |
|
838 | Must return True always, otherwise GTK stops calling it""" | |
837 |
|
839 | |||
838 | update_tk(self.tk) |
|
840 | update_tk(self.tk) | |
839 | self.IP.runcode() |
|
841 | self.IP.runcode() | |
840 | time.sleep(0.01) |
|
842 | time.sleep(0.01) | |
841 | return True |
|
843 | return True | |
842 |
|
844 | |||
843 |
|
845 | |||
844 | class IPShellWX(IPThread): |
|
846 | class IPShellWX(IPThread): | |
845 | """Run a wx mainloop() in a separate thread. |
|
847 | """Run a wx mainloop() in a separate thread. | |
846 |
|
848 | |||
847 | Python commands can be passed to the thread where they will be executed. |
|
849 | Python commands can be passed to the thread where they will be executed. | |
848 | This is implemented by periodically checking for passed code using a |
|
850 | This is implemented by periodically checking for passed code using a | |
849 | GTK timeout callback.""" |
|
851 | GTK timeout callback.""" | |
850 |
|
852 | |||
851 | TIMEOUT = 100 # Millisecond interval between timeouts. |
|
853 | TIMEOUT = 100 # Millisecond interval between timeouts. | |
852 |
|
854 | |||
853 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
|
855 | def __init__(self,argv=None,user_ns=None,user_global_ns=None, | |
854 | debug=1,shell_class=MTInteractiveShell): |
|
856 | debug=1,shell_class=MTInteractiveShell): | |
855 |
|
857 | |||
856 | self.IP = make_IPython(argv,user_ns=user_ns, |
|
858 | self.IP = make_IPython(argv,user_ns=user_ns, | |
857 | user_global_ns=user_global_ns, |
|
859 | user_global_ns=user_global_ns, | |
858 | debug=debug, |
|
860 | debug=debug, | |
859 | shell_class=shell_class, |
|
861 | shell_class=shell_class, | |
860 | on_kill=[self.wxexit]) |
|
862 | on_kill=[self.wxexit]) | |
861 |
|
863 | |||
862 | wantedwxversion=self.IP.rc.wxversion |
|
864 | wantedwxversion=self.IP.rc.wxversion | |
863 | if wantedwxversion!="0": |
|
865 | if wantedwxversion!="0": | |
864 | try: |
|
866 | try: | |
865 | import wxversion |
|
867 | import wxversion | |
866 | except ImportError: |
|
868 | except ImportError: | |
867 | error('The wxversion module is needed for WX version selection') |
|
869 | error('The wxversion module is needed for WX version selection') | |
868 | else: |
|
870 | else: | |
869 | try: |
|
871 | try: | |
870 | wxversion.select(wantedwxversion) |
|
872 | wxversion.select(wantedwxversion) | |
871 | except: |
|
873 | except: | |
872 | self.IP.InteractiveTB() |
|
874 | self.IP.InteractiveTB() | |
873 | error('Requested wxPython version %s could not be loaded' % |
|
875 | error('Requested wxPython version %s could not be loaded' % | |
874 | wantedwxversion) |
|
876 | wantedwxversion) | |
875 |
|
877 | |||
876 | import wx |
|
878 | import wx | |
877 |
|
879 | |||
878 | threading.Thread.__init__(self) |
|
880 | threading.Thread.__init__(self) | |
879 | self.wx = wx |
|
881 | self.wx = wx | |
880 | self.wx_mainloop = hijack_wx() |
|
882 | self.wx_mainloop = hijack_wx() | |
881 |
|
883 | |||
882 | # Allows us to use both Tk and GTK. |
|
884 | # Allows us to use both Tk and GTK. | |
883 | self.tk = get_tk() |
|
885 | self.tk = get_tk() | |
884 |
|
886 | |||
885 | # HACK: slot for banner in self; it will be passed to the mainloop |
|
887 | # HACK: slot for banner in self; it will be passed to the mainloop | |
886 | # method only and .run() needs it. The actual value will be set by |
|
888 | # method only and .run() needs it. The actual value will be set by | |
887 | # .mainloop(). |
|
889 | # .mainloop(). | |
888 | self._banner = None |
|
890 | self._banner = None | |
889 |
|
891 | |||
890 | self.app = None |
|
892 | self.app = None | |
891 |
|
893 | |||
892 | def wxexit(self, *args): |
|
894 | def wxexit(self, *args): | |
893 | if self.app is not None: |
|
895 | if self.app is not None: | |
894 | self.app.agent.timer.Stop() |
|
896 | self.app.agent.timer.Stop() | |
895 | self.app.ExitMainLoop() |
|
897 | self.app.ExitMainLoop() | |
896 |
|
898 | |||
897 | def mainloop(self,sys_exit=0,banner=None): |
|
899 | def mainloop(self,sys_exit=0,banner=None): | |
898 |
|
900 | |||
899 | self._banner = banner |
|
901 | self._banner = banner | |
900 |
|
902 | |||
901 | self.start() |
|
903 | self.start() | |
902 |
|
904 | |||
903 | class TimerAgent(self.wx.MiniFrame): |
|
905 | class TimerAgent(self.wx.MiniFrame): | |
904 | wx = self.wx |
|
906 | wx = self.wx | |
905 | IP = self.IP |
|
907 | IP = self.IP | |
906 | tk = self.tk |
|
908 | tk = self.tk | |
907 | def __init__(self, parent, interval): |
|
909 | def __init__(self, parent, interval): | |
908 | style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ |
|
910 | style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ | |
909 | self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200), |
|
911 | self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200), | |
910 | size=(100, 100),style=style) |
|
912 | size=(100, 100),style=style) | |
911 | self.Show(False) |
|
913 | self.Show(False) | |
912 | self.interval = interval |
|
914 | self.interval = interval | |
913 | self.timerId = self.wx.NewId() |
|
915 | self.timerId = self.wx.NewId() | |
914 |
|
916 | |||
915 | def StartWork(self): |
|
917 | def StartWork(self): | |
916 | self.timer = self.wx.Timer(self, self.timerId) |
|
918 | self.timer = self.wx.Timer(self, self.timerId) | |
917 | self.wx.EVT_TIMER(self, self.timerId, self.OnTimer) |
|
919 | self.wx.EVT_TIMER(self, self.timerId, self.OnTimer) | |
918 | self.timer.Start(self.interval) |
|
920 | self.timer.Start(self.interval) | |
919 |
|
921 | |||
920 | def OnTimer(self, event): |
|
922 | def OnTimer(self, event): | |
921 | update_tk(self.tk) |
|
923 | update_tk(self.tk) | |
922 | self.IP.runcode() |
|
924 | self.IP.runcode() | |
923 |
|
925 | |||
924 | class App(self.wx.App): |
|
926 | class App(self.wx.App): | |
925 | wx = self.wx |
|
927 | wx = self.wx | |
926 | TIMEOUT = self.TIMEOUT |
|
928 | TIMEOUT = self.TIMEOUT | |
927 | def OnInit(self): |
|
929 | def OnInit(self): | |
928 | 'Create the main window and insert the custom frame' |
|
930 | 'Create the main window and insert the custom frame' | |
929 | self.agent = TimerAgent(None, self.TIMEOUT) |
|
931 | self.agent = TimerAgent(None, self.TIMEOUT) | |
930 | self.agent.Show(False) |
|
932 | self.agent.Show(False) | |
931 | self.agent.StartWork() |
|
933 | self.agent.StartWork() | |
932 | return True |
|
934 | return True | |
933 |
|
935 | |||
934 | self.app = App(redirect=False) |
|
936 | self.app = App(redirect=False) | |
935 | self.wx_mainloop(self.app) |
|
937 | self.wx_mainloop(self.app) | |
936 | self.join() |
|
938 | self.join() | |
937 |
|
939 | |||
938 |
|
940 | |||
939 | class IPShellQt(IPThread): |
|
941 | class IPShellQt(IPThread): | |
940 | """Run a Qt event loop in a separate thread. |
|
942 | """Run a Qt event loop in a separate thread. | |
941 |
|
943 | |||
942 | Python commands can be passed to the thread where they will be executed. |
|
944 | Python commands can be passed to the thread where they will be executed. | |
943 | This is implemented by periodically checking for passed code using a |
|
945 | This is implemented by periodically checking for passed code using a | |
944 | Qt timer / slot.""" |
|
946 | Qt timer / slot.""" | |
945 |
|
947 | |||
946 | TIMEOUT = 100 # Millisecond interval between timeouts. |
|
948 | TIMEOUT = 100 # Millisecond interval between timeouts. | |
947 |
|
949 | |||
948 | def __init__(self, argv=None, user_ns=None, user_global_ns=None, |
|
950 | def __init__(self, argv=None, user_ns=None, user_global_ns=None, | |
949 | debug=0, shell_class=MTInteractiveShell): |
|
951 | debug=0, shell_class=MTInteractiveShell): | |
950 |
|
952 | |||
951 | import qt |
|
953 | import qt | |
952 |
|
954 | |||
953 | self.exec_loop = hijack_qt() |
|
955 | self.exec_loop = hijack_qt() | |
954 |
|
956 | |||
955 | # Allows us to use both Tk and QT. |
|
957 | # Allows us to use both Tk and QT. | |
956 | self.tk = get_tk() |
|
958 | self.tk = get_tk() | |
957 |
|
959 | |||
958 | self.IP = make_IPython(argv, |
|
960 | self.IP = make_IPython(argv, | |
959 | user_ns=user_ns, |
|
961 | user_ns=user_ns, | |
960 | user_global_ns=user_global_ns, |
|
962 | user_global_ns=user_global_ns, | |
961 | debug=debug, |
|
963 | debug=debug, | |
962 | shell_class=shell_class, |
|
964 | shell_class=shell_class, | |
963 | on_kill=[qt.qApp.exit]) |
|
965 | on_kill=[qt.qApp.exit]) | |
964 |
|
966 | |||
965 | # HACK: slot for banner in self; it will be passed to the mainloop |
|
967 | # HACK: slot for banner in self; it will be passed to the mainloop | |
966 | # method only and .run() needs it. The actual value will be set by |
|
968 | # method only and .run() needs it. The actual value will be set by | |
967 | # .mainloop(). |
|
969 | # .mainloop(). | |
968 | self._banner = None |
|
970 | self._banner = None | |
969 |
|
971 | |||
970 | threading.Thread.__init__(self) |
|
972 | threading.Thread.__init__(self) | |
971 |
|
973 | |||
972 | def mainloop(self, sys_exit=0, banner=None): |
|
974 | def mainloop(self, sys_exit=0, banner=None): | |
973 |
|
975 | |||
974 | import qt |
|
976 | import qt | |
975 |
|
977 | |||
976 | self._banner = banner |
|
978 | self._banner = banner | |
977 |
|
979 | |||
978 | if qt.QApplication.startingUp(): |
|
980 | if qt.QApplication.startingUp(): | |
979 | a = qt.QApplication(sys.argv) |
|
981 | a = qt.QApplication(sys.argv) | |
980 |
|
982 | |||
981 | self.timer = qt.QTimer() |
|
983 | self.timer = qt.QTimer() | |
982 | qt.QObject.connect(self.timer, |
|
984 | qt.QObject.connect(self.timer, | |
983 | qt.SIGNAL('timeout()'), |
|
985 | qt.SIGNAL('timeout()'), | |
984 | self.on_timer) |
|
986 | self.on_timer) | |
985 |
|
987 | |||
986 | self.start() |
|
988 | self.start() | |
987 | self.timer.start(self.TIMEOUT, True) |
|
989 | self.timer.start(self.TIMEOUT, True) | |
988 | while True: |
|
990 | while True: | |
989 | if self.IP._kill: break |
|
991 | if self.IP._kill: break | |
990 | self.exec_loop() |
|
992 | self.exec_loop() | |
991 | self.join() |
|
993 | self.join() | |
992 |
|
994 | |||
993 | def on_timer(self): |
|
995 | def on_timer(self): | |
994 | update_tk(self.tk) |
|
996 | update_tk(self.tk) | |
995 | result = self.IP.runcode() |
|
997 | result = self.IP.runcode() | |
996 | self.timer.start(self.TIMEOUT, True) |
|
998 | self.timer.start(self.TIMEOUT, True) | |
997 | return result |
|
999 | return result | |
998 |
|
1000 | |||
999 |
|
1001 | |||
1000 | class IPShellQt4(IPThread): |
|
1002 | class IPShellQt4(IPThread): | |
1001 | """Run a Qt event loop in a separate thread. |
|
1003 | """Run a Qt event loop in a separate thread. | |
1002 |
|
1004 | |||
1003 | Python commands can be passed to the thread where they will be executed. |
|
1005 | Python commands can be passed to the thread where they will be executed. | |
1004 | This is implemented by periodically checking for passed code using a |
|
1006 | This is implemented by periodically checking for passed code using a | |
1005 | Qt timer / slot.""" |
|
1007 | Qt timer / slot.""" | |
1006 |
|
1008 | |||
1007 | TIMEOUT = 100 # Millisecond interval between timeouts. |
|
1009 | TIMEOUT = 100 # Millisecond interval between timeouts. | |
1008 |
|
1010 | |||
1009 | def __init__(self, argv=None, user_ns=None, user_global_ns=None, |
|
1011 | def __init__(self, argv=None, user_ns=None, user_global_ns=None, | |
1010 | debug=0, shell_class=MTInteractiveShell): |
|
1012 | debug=0, shell_class=MTInteractiveShell): | |
1011 |
|
1013 | |||
1012 | from PyQt4 import QtCore, QtGui |
|
1014 | from PyQt4 import QtCore, QtGui | |
1013 |
|
1015 | |||
1014 | try: |
|
1016 | try: | |
1015 | # present in PyQt4-4.2.1 or later |
|
1017 | # present in PyQt4-4.2.1 or later | |
1016 | QtCore.pyqtRemoveInputHook() |
|
1018 | QtCore.pyqtRemoveInputHook() | |
1017 | except AttributeError: |
|
1019 | except AttributeError: | |
1018 | pass |
|
1020 | pass | |
1019 |
|
1021 | |||
1020 | if QtCore.PYQT_VERSION_STR == '4.3': |
|
1022 | if QtCore.PYQT_VERSION_STR == '4.3': | |
1021 | warn('''PyQt4 version 4.3 detected. |
|
1023 | warn('''PyQt4 version 4.3 detected. | |
1022 | If you experience repeated threading warnings, please update PyQt4. |
|
1024 | If you experience repeated threading warnings, please update PyQt4. | |
1023 | ''') |
|
1025 | ''') | |
1024 |
|
1026 | |||
1025 | self.exec_ = hijack_qt4() |
|
1027 | self.exec_ = hijack_qt4() | |
1026 |
|
1028 | |||
1027 | # Allows us to use both Tk and QT. |
|
1029 | # Allows us to use both Tk and QT. | |
1028 | self.tk = get_tk() |
|
1030 | self.tk = get_tk() | |
1029 |
|
1031 | |||
1030 | self.IP = make_IPython(argv, |
|
1032 | self.IP = make_IPython(argv, | |
1031 | user_ns=user_ns, |
|
1033 | user_ns=user_ns, | |
1032 | user_global_ns=user_global_ns, |
|
1034 | user_global_ns=user_global_ns, | |
1033 | debug=debug, |
|
1035 | debug=debug, | |
1034 | shell_class=shell_class, |
|
1036 | shell_class=shell_class, | |
1035 | on_kill=[QtGui.qApp.exit]) |
|
1037 | on_kill=[QtGui.qApp.exit]) | |
1036 |
|
1038 | |||
1037 | # HACK: slot for banner in self; it will be passed to the mainloop |
|
1039 | # HACK: slot for banner in self; it will be passed to the mainloop | |
1038 | # method only and .run() needs it. The actual value will be set by |
|
1040 | # method only and .run() needs it. The actual value will be set by | |
1039 | # .mainloop(). |
|
1041 | # .mainloop(). | |
1040 | self._banner = None |
|
1042 | self._banner = None | |
1041 |
|
1043 | |||
1042 | threading.Thread.__init__(self) |
|
1044 | threading.Thread.__init__(self) | |
1043 |
|
1045 | |||
1044 | def mainloop(self, sys_exit=0, banner=None): |
|
1046 | def mainloop(self, sys_exit=0, banner=None): | |
1045 |
|
1047 | |||
1046 | from PyQt4 import QtCore, QtGui |
|
1048 | from PyQt4 import QtCore, QtGui | |
1047 |
|
1049 | |||
1048 | self._banner = banner |
|
1050 | self._banner = banner | |
1049 |
|
1051 | |||
1050 | if QtGui.QApplication.startingUp(): |
|
1052 | if QtGui.QApplication.startingUp(): | |
1051 | a = QtGui.QApplication(sys.argv) |
|
1053 | a = QtGui.QApplication(sys.argv) | |
1052 |
|
1054 | |||
1053 | self.timer = QtCore.QTimer() |
|
1055 | self.timer = QtCore.QTimer() | |
1054 | QtCore.QObject.connect(self.timer, |
|
1056 | QtCore.QObject.connect(self.timer, | |
1055 | QtCore.SIGNAL('timeout()'), |
|
1057 | QtCore.SIGNAL('timeout()'), | |
1056 | self.on_timer) |
|
1058 | self.on_timer) | |
1057 |
|
1059 | |||
1058 | self.start() |
|
1060 | self.start() | |
1059 | self.timer.start(self.TIMEOUT) |
|
1061 | self.timer.start(self.TIMEOUT) | |
1060 | while True: |
|
1062 | while True: | |
1061 | if self.IP._kill: break |
|
1063 | if self.IP._kill: break | |
1062 | self.exec_() |
|
1064 | self.exec_() | |
1063 | self.join() |
|
1065 | self.join() | |
1064 |
|
1066 | |||
1065 | def on_timer(self): |
|
1067 | def on_timer(self): | |
1066 | update_tk(self.tk) |
|
1068 | update_tk(self.tk) | |
1067 | result = self.IP.runcode() |
|
1069 | result = self.IP.runcode() | |
1068 | self.timer.start(self.TIMEOUT) |
|
1070 | self.timer.start(self.TIMEOUT) | |
1069 | return result |
|
1071 | return result | |
1070 |
|
1072 | |||
1071 |
|
1073 | |||
1072 | # A set of matplotlib public IPython shell classes, for single-threaded (Tk* |
|
1074 | # A set of matplotlib public IPython shell classes, for single-threaded (Tk* | |
1073 | # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use. |
|
1075 | # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use. | |
1074 | def _load_pylab(user_ns): |
|
1076 | def _load_pylab(user_ns): | |
1075 | """Allow users to disable pulling all of pylab into the top-level |
|
1077 | """Allow users to disable pulling all of pylab into the top-level | |
1076 | namespace. |
|
1078 | namespace. | |
1077 |
|
1079 | |||
1078 | This little utility must be called AFTER the actual ipython instance is |
|
1080 | This little utility must be called AFTER the actual ipython instance is | |
1079 | running, since only then will the options file have been fully parsed.""" |
|
1081 | running, since only then will the options file have been fully parsed.""" | |
1080 |
|
1082 | |||
1081 | ip = IPython.ipapi.get() |
|
1083 | ip = IPython.ipapi.get() | |
1082 | if ip.options.pylab_import_all: |
|
1084 | if ip.options.pylab_import_all: | |
1083 | ip.ex("from matplotlib.pylab import *") |
|
1085 | ip.ex("from matplotlib.pylab import *") | |
1084 | ip.IP.user_config_ns.update(ip.user_ns) |
|
1086 | ip.IP.user_config_ns.update(ip.user_ns) | |
1085 |
|
1087 | |||
1086 |
|
1088 | |||
1087 | class IPShellMatplotlib(IPShell): |
|
1089 | class IPShellMatplotlib(IPShell): | |
1088 | """Subclass IPShell with MatplotlibShell as the internal shell. |
|
1090 | """Subclass IPShell with MatplotlibShell as the internal shell. | |
1089 |
|
1091 | |||
1090 | Single-threaded class, meant for the Tk* and FLTK* backends. |
|
1092 | Single-threaded class, meant for the Tk* and FLTK* backends. | |
1091 |
|
1093 | |||
1092 | Having this on a separate class simplifies the external driver code.""" |
|
1094 | Having this on a separate class simplifies the external driver code.""" | |
1093 |
|
1095 | |||
1094 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
|
1096 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): | |
1095 | IPShell.__init__(self,argv,user_ns,user_global_ns,debug, |
|
1097 | IPShell.__init__(self,argv,user_ns,user_global_ns,debug, | |
1096 | shell_class=MatplotlibShell) |
|
1098 | shell_class=MatplotlibShell) | |
1097 | _load_pylab(self.IP.user_ns) |
|
1099 | _load_pylab(self.IP.user_ns) | |
1098 |
|
1100 | |||
1099 | class IPShellMatplotlibGTK(IPShellGTK): |
|
1101 | class IPShellMatplotlibGTK(IPShellGTK): | |
1100 | """Subclass IPShellGTK with MatplotlibMTShell as the internal shell. |
|
1102 | """Subclass IPShellGTK with MatplotlibMTShell as the internal shell. | |
1101 |
|
1103 | |||
1102 | Multi-threaded class, meant for the GTK* backends.""" |
|
1104 | Multi-threaded class, meant for the GTK* backends.""" | |
1103 |
|
1105 | |||
1104 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
|
1106 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): | |
1105 | IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug, |
|
1107 | IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug, | |
1106 | shell_class=MatplotlibMTShell) |
|
1108 | shell_class=MatplotlibMTShell) | |
1107 | _load_pylab(self.IP.user_ns) |
|
1109 | _load_pylab(self.IP.user_ns) | |
1108 |
|
1110 | |||
1109 | class IPShellMatplotlibWX(IPShellWX): |
|
1111 | class IPShellMatplotlibWX(IPShellWX): | |
1110 | """Subclass IPShellWX with MatplotlibMTShell as the internal shell. |
|
1112 | """Subclass IPShellWX with MatplotlibMTShell as the internal shell. | |
1111 |
|
1113 | |||
1112 | Multi-threaded class, meant for the WX* backends.""" |
|
1114 | Multi-threaded class, meant for the WX* backends.""" | |
1113 |
|
1115 | |||
1114 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
|
1116 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): | |
1115 | IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug, |
|
1117 | IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug, | |
1116 | shell_class=MatplotlibMTShell) |
|
1118 | shell_class=MatplotlibMTShell) | |
1117 | _load_pylab(self.IP.user_ns) |
|
1119 | _load_pylab(self.IP.user_ns) | |
1118 |
|
1120 | |||
1119 | class IPShellMatplotlibQt(IPShellQt): |
|
1121 | class IPShellMatplotlibQt(IPShellQt): | |
1120 | """Subclass IPShellQt with MatplotlibMTShell as the internal shell. |
|
1122 | """Subclass IPShellQt with MatplotlibMTShell as the internal shell. | |
1121 |
|
1123 | |||
1122 | Multi-threaded class, meant for the Qt* backends.""" |
|
1124 | Multi-threaded class, meant for the Qt* backends.""" | |
1123 |
|
1125 | |||
1124 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
|
1126 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): | |
1125 | IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug, |
|
1127 | IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug, | |
1126 | shell_class=MatplotlibMTShell) |
|
1128 | shell_class=MatplotlibMTShell) | |
1127 | _load_pylab(self.IP.user_ns) |
|
1129 | _load_pylab(self.IP.user_ns) | |
1128 |
|
1130 | |||
1129 | class IPShellMatplotlibQt4(IPShellQt4): |
|
1131 | class IPShellMatplotlibQt4(IPShellQt4): | |
1130 | """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell. |
|
1132 | """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell. | |
1131 |
|
1133 | |||
1132 | Multi-threaded class, meant for the Qt4* backends.""" |
|
1134 | Multi-threaded class, meant for the Qt4* backends.""" | |
1133 |
|
1135 | |||
1134 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
|
1136 | def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): | |
1135 | IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug, |
|
1137 | IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug, | |
1136 | shell_class=MatplotlibMTShell) |
|
1138 | shell_class=MatplotlibMTShell) | |
1137 | _load_pylab(self.IP.user_ns) |
|
1139 | _load_pylab(self.IP.user_ns) | |
1138 |
|
1140 | |||
1139 | #----------------------------------------------------------------------------- |
|
1141 | #----------------------------------------------------------------------------- | |
1140 | # Factory functions to actually start the proper thread-aware shell |
|
1142 | # Factory functions to actually start the proper thread-aware shell | |
1141 |
|
1143 | |||
1142 | def _select_shell(argv): |
|
1144 | def _select_shell(argv): | |
1143 | """Select a shell from the given argv vector. |
|
1145 | """Select a shell from the given argv vector. | |
1144 |
|
1146 | |||
1145 | This function implements the threading selection policy, allowing runtime |
|
1147 | This function implements the threading selection policy, allowing runtime | |
1146 | control of the threading mode, both for general users and for matplotlib. |
|
1148 | control of the threading mode, both for general users and for matplotlib. | |
1147 |
|
1149 | |||
1148 | Return: |
|
1150 | Return: | |
1149 | Shell class to be instantiated for runtime operation. |
|
1151 | Shell class to be instantiated for runtime operation. | |
1150 | """ |
|
1152 | """ | |
1151 |
|
1153 | |||
1152 | global USE_TK |
|
1154 | global USE_TK | |
1153 |
|
1155 | |||
1154 | mpl_shell = {'gthread' : IPShellMatplotlibGTK, |
|
1156 | mpl_shell = {'gthread' : IPShellMatplotlibGTK, | |
1155 | 'wthread' : IPShellMatplotlibWX, |
|
1157 | 'wthread' : IPShellMatplotlibWX, | |
1156 | 'qthread' : IPShellMatplotlibQt, |
|
1158 | 'qthread' : IPShellMatplotlibQt, | |
1157 | 'q4thread' : IPShellMatplotlibQt4, |
|
1159 | 'q4thread' : IPShellMatplotlibQt4, | |
1158 | 'tkthread' : IPShellMatplotlib, # Tk is built-in |
|
1160 | 'tkthread' : IPShellMatplotlib, # Tk is built-in | |
1159 | } |
|
1161 | } | |
1160 |
|
1162 | |||
1161 | th_shell = {'gthread' : IPShellGTK, |
|
1163 | th_shell = {'gthread' : IPShellGTK, | |
1162 | 'wthread' : IPShellWX, |
|
1164 | 'wthread' : IPShellWX, | |
1163 | 'qthread' : IPShellQt, |
|
1165 | 'qthread' : IPShellQt, | |
1164 | 'q4thread' : IPShellQt4, |
|
1166 | 'q4thread' : IPShellQt4, | |
1165 | 'tkthread' : IPShell, # Tk is built-in |
|
1167 | 'tkthread' : IPShell, # Tk is built-in | |
1166 | } |
|
1168 | } | |
1167 |
|
1169 | |||
1168 | backends = {'gthread' : 'GTKAgg', |
|
1170 | backends = {'gthread' : 'GTKAgg', | |
1169 | 'wthread' : 'WXAgg', |
|
1171 | 'wthread' : 'WXAgg', | |
1170 | 'qthread' : 'QtAgg', |
|
1172 | 'qthread' : 'QtAgg', | |
1171 | 'q4thread' :'Qt4Agg', |
|
1173 | 'q4thread' :'Qt4Agg', | |
1172 | 'tkthread' :'TkAgg', |
|
1174 | 'tkthread' :'TkAgg', | |
1173 | } |
|
1175 | } | |
1174 |
|
1176 | |||
1175 | all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', |
|
1177 | all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', | |
1176 | 'tkthread']) |
|
1178 | 'tkthread']) | |
1177 | user_opts = set([s.replace('-','') for s in argv[:3]]) |
|
1179 | user_opts = set([s.replace('-','') for s in argv[:3]]) | |
1178 | special_opts = user_opts & all_opts |
|
1180 | special_opts = user_opts & all_opts | |
1179 |
|
1181 | |||
1180 | if 'tk' in special_opts: |
|
1182 | if 'tk' in special_opts: | |
1181 | USE_TK = True |
|
1183 | USE_TK = True | |
1182 | special_opts.remove('tk') |
|
1184 | special_opts.remove('tk') | |
1183 |
|
1185 | |||
1184 | if 'pylab' in special_opts: |
|
1186 | if 'pylab' in special_opts: | |
1185 |
|
1187 | |||
1186 | try: |
|
1188 | try: | |
1187 | import matplotlib |
|
1189 | import matplotlib | |
1188 | except ImportError: |
|
1190 | except ImportError: | |
1189 | error('matplotlib could NOT be imported! Starting normal IPython.') |
|
1191 | error('matplotlib could NOT be imported! Starting normal IPython.') | |
1190 | return IPShell |
|
1192 | return IPShell | |
1191 |
|
1193 | |||
1192 | special_opts.remove('pylab') |
|
1194 | special_opts.remove('pylab') | |
1193 | # If there's any option left, it means the user wants to force the |
|
1195 | # If there's any option left, it means the user wants to force the | |
1194 | # threading backend, else it's auto-selected from the rc file |
|
1196 | # threading backend, else it's auto-selected from the rc file | |
1195 | if special_opts: |
|
1197 | if special_opts: | |
1196 | th_mode = special_opts.pop() |
|
1198 | th_mode = special_opts.pop() | |
1197 | matplotlib.rcParams['backend'] = backends[th_mode] |
|
1199 | matplotlib.rcParams['backend'] = backends[th_mode] | |
1198 | else: |
|
1200 | else: | |
1199 | backend = matplotlib.rcParams['backend'] |
|
1201 | backend = matplotlib.rcParams['backend'] | |
1200 | if backend.startswith('GTK'): |
|
1202 | if backend.startswith('GTK'): | |
1201 | th_mode = 'gthread' |
|
1203 | th_mode = 'gthread' | |
1202 | elif backend.startswith('WX'): |
|
1204 | elif backend.startswith('WX'): | |
1203 | th_mode = 'wthread' |
|
1205 | th_mode = 'wthread' | |
1204 | elif backend.startswith('Qt4'): |
|
1206 | elif backend.startswith('Qt4'): | |
1205 | th_mode = 'q4thread' |
|
1207 | th_mode = 'q4thread' | |
1206 | elif backend.startswith('Qt'): |
|
1208 | elif backend.startswith('Qt'): | |
1207 | th_mode = 'qthread' |
|
1209 | th_mode = 'qthread' | |
1208 | else: |
|
1210 | else: | |
1209 | # Any other backend, use plain Tk |
|
1211 | # Any other backend, use plain Tk | |
1210 | th_mode = 'tkthread' |
|
1212 | th_mode = 'tkthread' | |
1211 |
|
1213 | |||
1212 | return mpl_shell[th_mode] |
|
1214 | return mpl_shell[th_mode] | |
1213 | else: |
|
1215 | else: | |
1214 | # No pylab requested, just plain threads |
|
1216 | # No pylab requested, just plain threads | |
1215 | try: |
|
1217 | try: | |
1216 | th_mode = special_opts.pop() |
|
1218 | th_mode = special_opts.pop() | |
1217 | except KeyError: |
|
1219 | except KeyError: | |
1218 | th_mode = 'tkthread' |
|
1220 | th_mode = 'tkthread' | |
1219 | return th_shell[th_mode] |
|
1221 | return th_shell[th_mode] | |
1220 |
|
1222 | |||
1221 |
|
1223 | |||
1222 | # This is the one which should be called by external code. |
|
1224 | # This is the one which should be called by external code. | |
1223 | def start(user_ns = None): |
|
1225 | def start(user_ns = None): | |
1224 | """Return a running shell instance, dealing with threading options. |
|
1226 | """Return a running shell instance, dealing with threading options. | |
1225 |
|
1227 | |||
1226 | This is a factory function which will instantiate the proper IPython shell |
|
1228 | This is a factory function which will instantiate the proper IPython shell | |
1227 | based on the user's threading choice. Such a selector is needed because |
|
1229 | based on the user's threading choice. Such a selector is needed because | |
1228 | different GUI toolkits require different thread handling details.""" |
|
1230 | different GUI toolkits require different thread handling details.""" | |
1229 |
|
1231 | |||
1230 | shell = _select_shell(sys.argv) |
|
1232 | shell = _select_shell(sys.argv) | |
1231 | return shell(user_ns = user_ns) |
|
1233 | return shell(user_ns = user_ns) | |
1232 |
|
1234 | |||
1233 | # Some aliases for backwards compatibility |
|
1235 | # Some aliases for backwards compatibility | |
1234 | IPythonShell = IPShell |
|
1236 | IPythonShell = IPShell | |
1235 | IPythonShellEmbed = IPShellEmbed |
|
1237 | IPythonShellEmbed = IPShellEmbed | |
1236 | #************************ End of file <Shell.py> *************************** |
|
1238 | #************************ End of file <Shell.py> *************************** |
@@ -1,63 +1,67 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """ | |
3 | Test process execution and IO redirection. |
|
3 | Test process execution and IO redirection. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | __docformat__ = "restructuredtext en" |
|
6 | __docformat__ = "restructuredtext en" | |
7 |
|
7 | |||
8 | #------------------------------------------------------------------------------- |
|
8 | #------------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008 The IPython Development Team |
|
9 | # Copyright (C) 2008 The IPython Development Team | |
10 | # |
|
10 | # | |
11 | # Distributed under the terms of the BSD License. The full license is |
|
11 | # Distributed under the terms of the BSD License. The full license is | |
12 | # in the file COPYING, distributed as part of this software. |
|
12 | # in the file COPYING, distributed as part of this software. | |
13 | #------------------------------------------------------------------------------- |
|
13 | #------------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 | from cStringIO import StringIO |
|
15 | from cStringIO import StringIO | |
16 | from time import sleep |
|
16 | from time import sleep | |
17 | import sys |
|
17 | import sys | |
18 |
|
18 | |||
19 | from IPython.frontend._process import PipedProcess |
|
19 | from IPython.frontend._process import PipedProcess | |
|
20 | from IPython.testing import decorators as testdec | |||
20 |
|
21 | |||
21 | def test_capture_out(): |
|
22 | def test_capture_out(): | |
22 | """ A simple test to see if we can execute a process and get the output. |
|
23 | """ A simple test to see if we can execute a process and get the output. | |
23 | """ |
|
24 | """ | |
24 | s = StringIO() |
|
25 | s = StringIO() | |
25 | p = PipedProcess('echo 1', out_callback=s.write, ) |
|
26 | p = PipedProcess('echo 1', out_callback=s.write, ) | |
26 | p.start() |
|
27 | p.start() | |
27 | p.join() |
|
28 | p.join() | |
28 | assert s.getvalue() == '1\n' |
|
29 | result = s.getvalue().rstrip() | |
29 |
|
30 | assert result == '1' | ||
30 |
|
31 | |||
|
32 | # FIXME | |||
|
33 | @testdec.skip("This doesn't work under Windows") | |||
31 | def test_io(): |
|
34 | def test_io(): | |
32 | """ Checks that we can send characters on stdin to the process. |
|
35 | """ Checks that we can send characters on stdin to the process. | |
33 | """ |
|
36 | """ | |
34 | s = StringIO() |
|
37 | s = StringIO() | |
35 | p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"', |
|
38 | p = PipedProcess(sys.executable + ' -c "a = raw_input(); print a"', | |
36 | out_callback=s.write, ) |
|
39 | out_callback=s.write, ) | |
37 | p.start() |
|
40 | p.start() | |
38 | test_string = '12345\n' |
|
41 | test_string = '12345\n' | |
39 | while not hasattr(p, 'process'): |
|
42 | while not hasattr(p, 'process'): | |
40 | sleep(0.1) |
|
43 | sleep(0.1) | |
41 | p.process.stdin.write(test_string) |
|
44 | p.process.stdin.write(test_string) | |
42 | p.join() |
|
45 | p.join() | |
43 |
|
|
46 | result = s.getvalue() | |
|
47 | assert result == test_string | |||
44 |
|
48 | |||
45 |
|
49 | |||
46 | def test_kill(): |
|
50 | def test_kill(): | |
47 | """ Check that we can kill a process, and its subprocess. |
|
51 | """ Check that we can kill a process, and its subprocess. | |
48 | """ |
|
52 | """ | |
49 | s = StringIO() |
|
53 | s = StringIO() | |
50 | p = PipedProcess(sys.executable + ' -c "a = raw_input();"', |
|
54 | p = PipedProcess(sys.executable + ' -c "a = raw_input();"', | |
51 | out_callback=s.write, ) |
|
55 | out_callback=s.write, ) | |
52 | p.start() |
|
56 | p.start() | |
53 | while not hasattr(p, 'process'): |
|
57 | while not hasattr(p, 'process'): | |
54 | sleep(0.1) |
|
58 | sleep(0.1) | |
55 | p.process.kill() |
|
59 | p.process.kill() | |
56 | assert p.process.poll() is not None |
|
60 | assert p.process.poll() is not None | |
57 |
|
61 | |||
58 |
|
62 | |||
59 | if __name__ == '__main__': |
|
63 | if __name__ == '__main__': | |
60 | test_capture_out() |
|
64 | test_capture_out() | |
61 | test_io() |
|
65 | test_io() | |
62 | test_kill() |
|
66 | test_kill() | |
63 |
|
67 |
@@ -1,61 +1,68 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | """ |
|
2 | """ | |
3 | Test the output capture at the OS level, using file descriptors. |
|
3 | Test the output capture at the OS level, using file descriptors. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | __docformat__ = "restructuredtext en" |
|
6 | __docformat__ = "restructuredtext en" | |
7 |
|
7 | |||
8 | #------------------------------------------------------------------------------- |
|
8 | #------------------------------------------------------------------------------- | |
9 | # Copyright (C) 2008 The IPython Development Team |
|
9 | # Copyright (C) 2008 The IPython Development Team | |
10 | # |
|
10 | # | |
11 | # Distributed under the terms of the BSD License. The full license is |
|
11 | # Distributed under the terms of the BSD License. The full license is | |
12 | # in the file COPYING, distributed as part of this software. |
|
12 | # in the file COPYING, distributed as part of this software. | |
13 | #------------------------------------------------------------------------------- |
|
13 | #------------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | import os |
|
16 | import os | |
17 | from cStringIO import StringIO |
|
17 | from cStringIO import StringIO | |
18 |
|
18 | |||
|
19 | from IPython.testing import decorators as testdec | |||
19 |
|
20 | |||
|
21 | # FIXME | |||
|
22 | @testdec.skip("This doesn't work under Windows") | |||
20 | def test_redirector(): |
|
23 | def test_redirector(): | |
21 | """ Checks that the redirector can be used to do synchronous capture. |
|
24 | """ Checks that the redirector can be used to do synchronous capture. | |
22 | """ |
|
25 | """ | |
23 | from IPython.kernel.core.fd_redirector import FDRedirector |
|
26 | from IPython.kernel.core.fd_redirector import FDRedirector | |
24 | r = FDRedirector() |
|
27 | r = FDRedirector() | |
25 | out = StringIO() |
|
28 | out = StringIO() | |
26 | try: |
|
29 | try: | |
27 | r.start() |
|
30 | r.start() | |
28 | for i in range(10): |
|
31 | for i in range(10): | |
29 | os.system('echo %ic' % i) |
|
32 | os.system('echo %ic' % i) | |
30 | print >>out, r.getvalue(), |
|
33 | print >>out, r.getvalue(), | |
31 | print >>out, i |
|
34 | print >>out, i | |
32 | except: |
|
35 | except: | |
33 | r.stop() |
|
36 | r.stop() | |
34 | raise |
|
37 | raise | |
35 | r.stop() |
|
38 | r.stop() | |
36 | assert out.getvalue() == "".join("%ic\n%i\n" %(i, i) for i in range(10)) |
|
39 | result1 = out.getvalue() | |
37 |
|
40 | result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10)) | ||
|
41 | assert result1 == result2 | |||
38 |
|
42 | |||
|
43 | # FIXME | |||
|
44 | @testdec.skip("This doesn't work under Windows") | |||
39 | def test_redirector_output_trap(): |
|
45 | def test_redirector_output_trap(): | |
40 | """ This test check not only that the redirector_output_trap does |
|
46 | """ This test check not only that the redirector_output_trap does | |
41 | trap the output, but also that it does it in a gready way, that |
|
47 | trap the output, but also that it does it in a gready way, that | |
42 | is by calling the callback ASAP. |
|
48 | is by calling the callback ASAP. | |
43 | """ |
|
49 | """ | |
44 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap |
|
50 | from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap | |
45 | out = StringIO() |
|
51 | out = StringIO() | |
46 | trap = RedirectorOutputTrap(out.write, out.write) |
|
52 | trap = RedirectorOutputTrap(out.write, out.write) | |
47 | try: |
|
53 | try: | |
48 | trap.set() |
|
54 | trap.set() | |
49 | for i in range(10): |
|
55 | for i in range(10): | |
50 | os.system('echo %ic' % i) |
|
56 | os.system('echo %ic' % i) | |
51 | print "%ip" % i |
|
57 | print "%ip" % i | |
52 | print >>out, i |
|
58 | print >>out, i | |
53 | except: |
|
59 | except: | |
54 | trap.unset() |
|
60 | trap.unset() | |
55 | raise |
|
61 | raise | |
56 | trap.unset() |
|
62 | trap.unset() | |
57 | assert out.getvalue() == "".join("%ic\n%ip\n%i\n" %(i, i, i) |
|
63 | result1 = out.getvalue() | |
58 | for i in range(10)) |
|
64 | result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10)) | |
|
65 | assert result1 == result2 | |||
59 |
|
66 | |||
60 |
|
67 | |||
61 |
|
68 |
@@ -1,135 +1,147 b'' | |||||
1 | """Decorators for labeling test objects. |
|
1 | """Decorators for labeling test objects. | |
2 |
|
2 | |||
3 | Decorators that merely return a modified version of the original |
|
3 | Decorators that merely return a modified version of the original | |
4 | function object are straightforward. Decorators that return a new |
|
4 | function object are straightforward. Decorators that return a new | |
5 | function object need to use |
|
5 | function object need to use | |
6 | nose.tools.make_decorator(original_function)(decorator) in returning |
|
6 | nose.tools.make_decorator(original_function)(decorator) in returning | |
7 | the decorator, in order to preserve metadata such as function name, |
|
7 | the decorator, in order to preserve metadata such as function name, | |
8 | setup and teardown functions and so on - see nose.tools for more |
|
8 | setup and teardown functions and so on - see nose.tools for more | |
9 | information. |
|
9 | information. | |
10 |
|
10 | |||
11 | NOTE: This file contains IPython-specific decorators and imports the |
|
11 | NOTE: This file contains IPython-specific decorators and imports the | |
12 | numpy.testing.decorators file, which we've copied verbatim. Any of our own |
|
12 | numpy.testing.decorators file, which we've copied verbatim. Any of our own | |
13 | code will be added at the bottom if we end up extending this. |
|
13 | code will be added at the bottom if we end up extending this. | |
14 | """ |
|
14 | """ | |
15 |
|
15 | |||
16 | # Stdlib imports |
|
16 | # Stdlib imports | |
17 | import inspect |
|
17 | import inspect | |
18 |
|
18 | |||
19 | # Third-party imports |
|
19 | # Third-party imports | |
20 |
|
20 | |||
21 | # This is Michele Simionato's decorator module, also kept verbatim. |
|
21 | # This is Michele Simionato's decorator module, also kept verbatim. | |
22 | from decorator_msim import decorator, update_wrapper |
|
22 | from decorator_msim import decorator, update_wrapper | |
23 |
|
23 | |||
24 | # Grab the numpy-specific decorators which we keep in a file that we |
|
24 | # Grab the numpy-specific decorators which we keep in a file that we | |
25 | # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy |
|
25 | # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy | |
26 | # of numpy.testing.decorators. |
|
26 | # of numpy.testing.decorators. | |
27 | from decorators_numpy import * |
|
27 | from decorators_numpy import * | |
28 |
|
28 | |||
29 | ############################################################################## |
|
29 | ############################################################################## | |
30 | # Local code begins |
|
30 | # Local code begins | |
31 |
|
31 | |||
32 | # Utility functions |
|
32 | # Utility functions | |
33 |
|
33 | |||
34 | def apply_wrapper(wrapper,func): |
|
34 | def apply_wrapper(wrapper,func): | |
35 | """Apply a wrapper to a function for decoration. |
|
35 | """Apply a wrapper to a function for decoration. | |
36 |
|
36 | |||
37 | This mixes Michele Simionato's decorator tool with nose's make_decorator, |
|
37 | This mixes Michele Simionato's decorator tool with nose's make_decorator, | |
38 | to apply a wrapper in a decorator so that all nose attributes, as well as |
|
38 | to apply a wrapper in a decorator so that all nose attributes, as well as | |
39 | function signature and other properties, survive the decoration cleanly. |
|
39 | function signature and other properties, survive the decoration cleanly. | |
40 | This will ensure that wrapped functions can still be well introspected via |
|
40 | This will ensure that wrapped functions can still be well introspected via | |
41 | IPython, for example. |
|
41 | IPython, for example. | |
42 | """ |
|
42 | """ | |
43 | import nose.tools |
|
43 | import nose.tools | |
44 |
|
44 | |||
45 | return decorator(wrapper,nose.tools.make_decorator(func)(wrapper)) |
|
45 | return decorator(wrapper,nose.tools.make_decorator(func)(wrapper)) | |
46 |
|
46 | |||
47 |
|
47 | |||
48 | def make_label_dec(label,ds=None): |
|
48 | def make_label_dec(label,ds=None): | |
49 | """Factory function to create a decorator that applies one or more labels. |
|
49 | """Factory function to create a decorator that applies one or more labels. | |
50 |
|
50 | |||
51 | :Parameters: |
|
51 | :Parameters: | |
52 | label : string or sequence |
|
52 | label : string or sequence | |
53 | One or more labels that will be applied by the decorator to the functions |
|
53 | One or more labels that will be applied by the decorator to the functions | |
54 | it decorates. Labels are attributes of the decorated function with their |
|
54 | it decorates. Labels are attributes of the decorated function with their | |
55 | value set to True. |
|
55 | value set to True. | |
56 |
|
56 | |||
57 | :Keywords: |
|
57 | :Keywords: | |
58 | ds : string |
|
58 | ds : string | |
59 | An optional docstring for the resulting decorator. If not given, a |
|
59 | An optional docstring for the resulting decorator. If not given, a | |
60 | default docstring is auto-generated. |
|
60 | default docstring is auto-generated. | |
61 |
|
61 | |||
62 | :Returns: |
|
62 | :Returns: | |
63 | A decorator. |
|
63 | A decorator. | |
64 |
|
64 | |||
65 | :Examples: |
|
65 | :Examples: | |
66 |
|
66 | |||
67 | A simple labeling decorator: |
|
67 | A simple labeling decorator: | |
68 | >>> slow = make_label_dec('slow') |
|
68 | >>> slow = make_label_dec('slow') | |
69 | >>> print slow.__doc__ |
|
69 | >>> print slow.__doc__ | |
70 | Labels a test as 'slow'. |
|
70 | Labels a test as 'slow'. | |
71 |
|
71 | |||
72 | And one that uses multiple labels and a custom docstring: |
|
72 | And one that uses multiple labels and a custom docstring: | |
73 | >>> rare = make_label_dec(['slow','hard'], |
|
73 | >>> rare = make_label_dec(['slow','hard'], | |
74 | ... "Mix labels 'slow' and 'hard' for rare tests.") |
|
74 | ... "Mix labels 'slow' and 'hard' for rare tests.") | |
75 | >>> print rare.__doc__ |
|
75 | >>> print rare.__doc__ | |
76 | Mix labels 'slow' and 'hard' for rare tests. |
|
76 | Mix labels 'slow' and 'hard' for rare tests. | |
77 |
|
77 | |||
78 | Now, let's test using this one: |
|
78 | Now, let's test using this one: | |
79 | >>> @rare |
|
79 | >>> @rare | |
80 | ... def f(): pass |
|
80 | ... def f(): pass | |
81 | ... |
|
81 | ... | |
82 | >>> |
|
82 | >>> | |
83 | >>> f.slow |
|
83 | >>> f.slow | |
84 | True |
|
84 | True | |
85 | >>> f.hard |
|
85 | >>> f.hard | |
86 | True |
|
86 | True | |
87 | """ |
|
87 | """ | |
88 |
|
88 | |||
89 | if isinstance(label,basestring): |
|
89 | if isinstance(label,basestring): | |
90 | labels = [label] |
|
90 | labels = [label] | |
91 | else: |
|
91 | else: | |
92 | labels = label |
|
92 | labels = label | |
93 |
|
93 | |||
94 | # Validate that the given label(s) are OK for use in setattr() by doing a |
|
94 | # Validate that the given label(s) are OK for use in setattr() by doing a | |
95 | # dry run on a dummy function. |
|
95 | # dry run on a dummy function. | |
96 | tmp = lambda : None |
|
96 | tmp = lambda : None | |
97 | for label in labels: |
|
97 | for label in labels: | |
98 | setattr(tmp,label,True) |
|
98 | setattr(tmp,label,True) | |
99 |
|
99 | |||
100 | # This is the actual decorator we'll return |
|
100 | # This is the actual decorator we'll return | |
101 | def decor(f): |
|
101 | def decor(f): | |
102 | for label in labels: |
|
102 | for label in labels: | |
103 | setattr(f,label,True) |
|
103 | setattr(f,label,True) | |
104 | return f |
|
104 | return f | |
105 |
|
105 | |||
106 | # Apply the user's docstring, or autogenerate a basic one |
|
106 | # Apply the user's docstring, or autogenerate a basic one | |
107 | if ds is None: |
|
107 | if ds is None: | |
108 | ds = "Labels a test as %r." % label |
|
108 | ds = "Labels a test as %r." % label | |
109 | decor.__doc__ = ds |
|
109 | decor.__doc__ = ds | |
110 |
|
110 | |||
111 | return decor |
|
111 | return decor | |
112 |
|
112 | |||
113 | #----------------------------------------------------------------------------- |
|
113 | #----------------------------------------------------------------------------- | |
114 | # Decorators for public use |
|
114 | # Decorators for public use | |
115 |
|
115 | |||
116 | skip_doctest = make_label_dec('skip_doctest', |
|
116 | skip_doctest = make_label_dec('skip_doctest', | |
117 | """Decorator - mark a function or method for skipping its doctest. |
|
117 | """Decorator - mark a function or method for skipping its doctest. | |
118 |
|
118 | |||
119 | This decorator allows you to mark a function whose docstring you wish to |
|
119 | This decorator allows you to mark a function whose docstring you wish to | |
120 | omit from testing, while preserving the docstring for introspection, help, |
|
120 | omit from testing, while preserving the docstring for introspection, help, | |
121 | etc.""") |
|
121 | etc.""") | |
122 |
|
122 | |||
|
123 | def skip(msg=''): | |||
|
124 | """Decorator - mark a test function for skipping from test suite. | |||
123 |
|
|
125 | ||
124 | def skip(func): |
|
126 | :Parameters: | |
125 | """Decorator - mark a test function for skipping from test suite.""" |
|
127 | ||
|
128 | func : function | |||
|
129 | Test function to be skipped | |||
|
130 | ||||
|
131 | msg : string | |||
|
132 | Optional message to be added. | |||
|
133 | """ | |||
126 |
|
134 | |||
127 | import nose |
|
135 | import nose | |
128 |
|
||||
129 | def wrapper(*a,**k): |
|
|||
130 | raise nose.SkipTest("Skipping test for function: %s" % |
|
|||
131 | func.__name__) |
|
|||
132 |
|
||||
133 | return apply_wrapper(wrapper,func) |
|
|||
134 |
|
136 | |||
|
137 | def inner(func): | |||
|
138 | ||||
|
139 | def wrapper(*a,**k): | |||
|
140 | if msg: out = '\n'+msg | |||
|
141 | else: out = '' | |||
|
142 | raise nose.SkipTest("Skipping test for function: %s%s" % | |||
|
143 | (func.__name__,out)) | |||
|
144 | ||||
|
145 | return apply_wrapper(wrapper,func) | |||
135 |
|
146 | |||
|
147 | return inner |
@@ -1,784 +1,806 b'' | |||||
1 | """Nose Plugin that supports IPython doctests. |
|
1 | """Nose Plugin that supports IPython doctests. | |
2 |
|
2 | |||
3 | Limitations: |
|
3 | Limitations: | |
4 |
|
4 | |||
5 | - When generating examples for use as doctests, make sure that you have |
|
5 | - When generating examples for use as doctests, make sure that you have | |
6 | pretty-printing OFF. This can be done either by starting ipython with the |
|
6 | pretty-printing OFF. This can be done either by starting ipython with the | |
7 | flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by |
|
7 | flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by | |
8 | interactively disabling it with %Pprint. This is required so that IPython |
|
8 | interactively disabling it with %Pprint. This is required so that IPython | |
9 | output matches that of normal Python, which is used by doctest for internal |
|
9 | output matches that of normal Python, which is used by doctest for internal | |
10 | execution. |
|
10 | execution. | |
11 |
|
11 | |||
12 | - Do not rely on specific prompt numbers for results (such as using |
|
12 | - Do not rely on specific prompt numbers for results (such as using | |
13 | '_34==True', for example). For IPython tests run via an external process the |
|
13 | '_34==True', for example). For IPython tests run via an external process the | |
14 | prompt numbers may be different, and IPython tests run as normal python code |
|
14 | prompt numbers may be different, and IPython tests run as normal python code | |
15 | won't even have these special _NN variables set at all. |
|
15 | won't even have these special _NN variables set at all. | |
16 | """ |
|
16 | """ | |
17 |
|
17 | |||
18 |
|
18 | |||
19 | #----------------------------------------------------------------------------- |
|
19 | #----------------------------------------------------------------------------- | |
20 | # Module imports |
|
20 | # Module imports | |
21 |
|
21 | |||
22 | # From the standard library |
|
22 | # From the standard library | |
23 | import __builtin__ |
|
23 | import __builtin__ | |
24 | import commands |
|
24 | import commands | |
25 | import doctest |
|
25 | import doctest | |
26 | import inspect |
|
26 | import inspect | |
27 | import logging |
|
27 | import logging | |
28 | import os |
|
28 | import os | |
29 | import re |
|
29 | import re | |
30 | import sys |
|
30 | import sys | |
31 | import traceback |
|
31 | import traceback | |
32 | import unittest |
|
32 | import unittest | |
33 |
|
33 | |||
34 | from inspect import getmodule |
|
34 | from inspect import getmodule | |
35 | from StringIO import StringIO |
|
35 | from StringIO import StringIO | |
36 |
|
36 | |||
37 | # We are overriding the default doctest runner, so we need to import a few |
|
37 | # We are overriding the default doctest runner, so we need to import a few | |
38 | # things from doctest directly |
|
38 | # things from doctest directly | |
39 | from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE, |
|
39 | from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE, | |
40 | _unittest_reportflags, DocTestRunner, |
|
40 | _unittest_reportflags, DocTestRunner, | |
41 | _extract_future_flags, pdb, _OutputRedirectingPdb, |
|
41 | _extract_future_flags, pdb, _OutputRedirectingPdb, | |
42 | _exception_traceback, |
|
42 | _exception_traceback, | |
43 | linecache) |
|
43 | linecache) | |
44 |
|
44 | |||
45 | # Third-party modules |
|
45 | # Third-party modules | |
46 | import nose.core |
|
46 | import nose.core | |
47 |
|
47 | |||
48 | from nose.plugins import doctests, Plugin |
|
48 | from nose.plugins import doctests, Plugin | |
49 | from nose.util import anyp, getpackage, test_address, resolve_name, tolist |
|
49 | from nose.util import anyp, getpackage, test_address, resolve_name, tolist | |
50 |
|
50 | |||
51 | #----------------------------------------------------------------------------- |
|
51 | #----------------------------------------------------------------------------- | |
52 | # Module globals and other constants |
|
52 | # Module globals and other constants | |
53 |
|
53 | |||
54 | log = logging.getLogger(__name__) |
|
54 | log = logging.getLogger(__name__) | |
55 |
|
55 | |||
56 | ########################################################################### |
|
56 | ########################################################################### | |
57 | # *** HACK *** |
|
57 | # *** HACK *** | |
58 | # We must start our own ipython object and heavily muck with it so that all the |
|
58 | # We must start our own ipython object and heavily muck with it so that all the | |
59 | # modifications IPython makes to system behavior don't send the doctest |
|
59 | # modifications IPython makes to system behavior don't send the doctest | |
60 | # machinery into a fit. This code should be considered a gross hack, but it |
|
60 | # machinery into a fit. This code should be considered a gross hack, but it | |
61 | # gets the job done. |
|
61 | # gets the job done. | |
62 |
|
62 | |||
63 |
|
63 | |||
64 | # Hack to modify the %run command so we can sync the user's namespace with the |
|
64 | # Hack to modify the %run command so we can sync the user's namespace with the | |
65 | # test globals. Once we move over to a clean magic system, this will be done |
|
65 | # test globals. Once we move over to a clean magic system, this will be done | |
66 | # with much less ugliness. |
|
66 | # with much less ugliness. | |
67 |
|
67 | |||
68 | def _run_ns_sync(self,arg_s,runner=None): |
|
68 | def _run_ns_sync(self,arg_s,runner=None): | |
69 | """Modified version of %run that syncs testing namespaces. |
|
69 | """Modified version of %run that syncs testing namespaces. | |
70 |
|
70 | |||
71 | This is strictly needed for running doctests that call %run. |
|
71 | This is strictly needed for running doctests that call %run. | |
72 | """ |
|
72 | """ | |
73 |
|
73 | |||
74 | out = _ip.IP.magic_run_ori(arg_s,runner) |
|
74 | out = _ip.IP.magic_run_ori(arg_s,runner) | |
75 | _run_ns_sync.test_globs.update(_ip.user_ns) |
|
75 | _run_ns_sync.test_globs.update(_ip.user_ns) | |
76 | return out |
|
76 | return out | |
77 |
|
77 | |||
78 |
|
78 | |||
79 | class ipnsdict(dict): |
|
79 | class ipnsdict(dict): | |
80 | """A special subclass of dict for use as an IPython namespace in doctests. |
|
80 | """A special subclass of dict for use as an IPython namespace in doctests. | |
81 |
|
81 | |||
82 | This subclass adds a simple checkpointing capability so that when testing |
|
82 | This subclass adds a simple checkpointing capability so that when testing | |
83 | machinery clears it (we use it as the test execution context), it doesn't |
|
83 | machinery clears it (we use it as the test execution context), it doesn't | |
84 | get completely destroyed. |
|
84 | get completely destroyed. | |
85 | """ |
|
85 | """ | |
86 |
|
86 | |||
87 | def __init__(self,*a): |
|
87 | def __init__(self,*a): | |
88 | dict.__init__(self,*a) |
|
88 | dict.__init__(self,*a) | |
89 | self._savedict = {} |
|
89 | self._savedict = {} | |
90 |
|
90 | |||
91 | def clear(self): |
|
91 | def clear(self): | |
92 | dict.clear(self) |
|
92 | dict.clear(self) | |
93 | self.update(self._savedict) |
|
93 | self.update(self._savedict) | |
94 |
|
94 | |||
95 | def _checkpoint(self): |
|
95 | def _checkpoint(self): | |
96 | self._savedict.clear() |
|
96 | self._savedict.clear() | |
97 | self._savedict.update(self) |
|
97 | self._savedict.update(self) | |
98 |
|
98 | |||
99 | def update(self,other): |
|
99 | def update(self,other): | |
100 | self._checkpoint() |
|
100 | self._checkpoint() | |
101 | dict.update(self,other) |
|
101 | dict.update(self,other) | |
102 | # If '_' is in the namespace, python won't set it when executing code, |
|
102 | # If '_' is in the namespace, python won't set it when executing code, | |
103 | # and we have examples that test it. So we ensure that the namespace |
|
103 | # and we have examples that test it. So we ensure that the namespace | |
104 | # is always 'clean' of it before it's used for test code execution. |
|
104 | # is always 'clean' of it before it's used for test code execution. | |
105 | self.pop('_',None) |
|
105 | self.pop('_',None) | |
106 |
|
106 | |||
107 |
|
107 | |||
108 | def start_ipython(): |
|
108 | def start_ipython(): | |
109 | """Start a global IPython shell, which we need for IPython-specific syntax. |
|
109 | """Start a global IPython shell, which we need for IPython-specific syntax. | |
110 | """ |
|
110 | """ | |
111 | import new |
|
111 | import new | |
112 |
|
112 | |||
113 | import IPython |
|
113 | import IPython | |
114 |
|
114 | |||
115 | def xsys(cmd): |
|
115 | def xsys(cmd): | |
116 | """Execute a command and print its output. |
|
116 | """Execute a command and print its output. | |
117 |
|
117 | |||
118 | This is just a convenience function to replace the IPython system call |
|
118 | This is just a convenience function to replace the IPython system call | |
119 | with one that is more doctest-friendly. |
|
119 | with one that is more doctest-friendly. | |
120 | """ |
|
120 | """ | |
121 | cmd = _ip.IP.var_expand(cmd,depth=1) |
|
121 | cmd = _ip.IP.var_expand(cmd,depth=1) | |
122 | sys.stdout.write(commands.getoutput(cmd)) |
|
122 | sys.stdout.write(commands.getoutput(cmd)) | |
123 | sys.stdout.flush() |
|
123 | sys.stdout.flush() | |
124 |
|
124 | |||
125 | # Store certain global objects that IPython modifies |
|
125 | # Store certain global objects that IPython modifies | |
126 | _displayhook = sys.displayhook |
|
126 | _displayhook = sys.displayhook | |
127 | _excepthook = sys.excepthook |
|
127 | _excepthook = sys.excepthook | |
128 | _main = sys.modules.get('__main__') |
|
128 | _main = sys.modules.get('__main__') | |
129 |
|
129 | |||
130 | # Start IPython instance. We customize it to start with minimal frills. |
|
130 | # Start IPython instance. We customize it to start with minimal frills. | |
131 | user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict()) |
|
131 | user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict()) | |
132 |
|
132 | |||
133 | IPython.Shell.IPShell(['--classic','--noterm_title'], |
|
133 | IPython.Shell.IPShell(['--classic','--noterm_title'], | |
134 | user_ns,global_ns) |
|
134 | user_ns,global_ns) | |
135 |
|
135 | |||
136 | # Deactivate the various python system hooks added by ipython for |
|
136 | # Deactivate the various python system hooks added by ipython for | |
137 | # interactive convenience so we don't confuse the doctest system |
|
137 | # interactive convenience so we don't confuse the doctest system | |
138 | sys.modules['__main__'] = _main |
|
138 | sys.modules['__main__'] = _main | |
139 | sys.displayhook = _displayhook |
|
139 | sys.displayhook = _displayhook | |
140 | sys.excepthook = _excepthook |
|
140 | sys.excepthook = _excepthook | |
141 |
|
141 | |||
142 | # So that ipython magics and aliases can be doctested (they work by making |
|
142 | # So that ipython magics and aliases can be doctested (they work by making | |
143 | # a call into a global _ip object) |
|
143 | # a call into a global _ip object) | |
144 | _ip = IPython.ipapi.get() |
|
144 | _ip = IPython.ipapi.get() | |
145 | __builtin__._ip = _ip |
|
145 | __builtin__._ip = _ip | |
146 |
|
146 | |||
147 | # Modify the IPython system call with one that uses getoutput, so that we |
|
147 | # Modify the IPython system call with one that uses getoutput, so that we | |
148 | # can capture subcommands and print them to Python's stdout, otherwise the |
|
148 | # can capture subcommands and print them to Python's stdout, otherwise the | |
149 | # doctest machinery would miss them. |
|
149 | # doctest machinery would miss them. | |
150 | _ip.system = xsys |
|
150 | _ip.system = xsys | |
151 |
|
151 | |||
152 | # Also patch our %run function in. |
|
152 | # Also patch our %run function in. | |
153 | im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__) |
|
153 | im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__) | |
154 | _ip.IP.magic_run_ori = _ip.IP.magic_run |
|
154 | _ip.IP.magic_run_ori = _ip.IP.magic_run | |
155 | _ip.IP.magic_run = im |
|
155 | _ip.IP.magic_run = im | |
156 |
|
156 | |||
157 | # The start call MUST be made here. I'm not sure yet why it doesn't work if |
|
157 | # The start call MUST be made here. I'm not sure yet why it doesn't work if | |
158 | # it is made later, at plugin initialization time, but in all my tests, that's |
|
158 | # it is made later, at plugin initialization time, but in all my tests, that's | |
159 | # the case. |
|
159 | # the case. | |
160 | start_ipython() |
|
160 | start_ipython() | |
161 |
|
161 | |||
162 | # *** END HACK *** |
|
162 | # *** END HACK *** | |
163 | ########################################################################### |
|
163 | ########################################################################### | |
164 |
|
164 | |||
165 | # Classes and functions |
|
165 | # Classes and functions | |
166 |
|
166 | |||
167 | def is_extension_module(filename): |
|
167 | def is_extension_module(filename): | |
168 | """Return whether the given filename is an extension module. |
|
168 | """Return whether the given filename is an extension module. | |
169 |
|
169 | |||
170 | This simply checks that the extension is either .so or .pyd. |
|
170 | This simply checks that the extension is either .so or .pyd. | |
171 | """ |
|
171 | """ | |
172 | return os.path.splitext(filename)[1].lower() in ('.so','.pyd') |
|
172 | return os.path.splitext(filename)[1].lower() in ('.so','.pyd') | |
173 |
|
173 | |||
174 |
|
174 | |||
175 | class nodoc(object): |
|
175 | class nodoc(object): | |
176 | def __init__(self,obj): |
|
176 | def __init__(self,obj): | |
177 | self.obj = obj |
|
177 | self.obj = obj | |
178 |
|
178 | |||
179 | def __getattribute__(self,key): |
|
179 | def __getattribute__(self,key): | |
180 | if key == '__doc__': |
|
180 | if key == '__doc__': | |
181 | return None |
|
181 | return None | |
182 | else: |
|
182 | else: | |
183 | return getattr(object.__getattribute__(self,'obj'),key) |
|
183 | return getattr(object.__getattribute__(self,'obj'),key) | |
184 |
|
184 | |||
185 | # Modified version of the one in the stdlib, that fixes a python bug (doctests |
|
185 | # Modified version of the one in the stdlib, that fixes a python bug (doctests | |
186 | # not found in extension modules, http://bugs.python.org/issue3158) |
|
186 | # not found in extension modules, http://bugs.python.org/issue3158) | |
187 | class DocTestFinder(doctest.DocTestFinder): |
|
187 | class DocTestFinder(doctest.DocTestFinder): | |
188 |
|
188 | |||
189 | def _from_module(self, module, object): |
|
189 | def _from_module(self, module, object): | |
190 | """ |
|
190 | """ | |
191 | Return true if the given object is defined in the given |
|
191 | Return true if the given object is defined in the given | |
192 | module. |
|
192 | module. | |
193 | """ |
|
193 | """ | |
194 | if module is None: |
|
194 | if module is None: | |
195 | return True |
|
195 | return True | |
196 | elif inspect.isfunction(object): |
|
196 | elif inspect.isfunction(object): | |
197 | return module.__dict__ is object.func_globals |
|
197 | return module.__dict__ is object.func_globals | |
198 | elif inspect.isbuiltin(object): |
|
198 | elif inspect.isbuiltin(object): | |
199 | return module.__name__ == object.__module__ |
|
199 | return module.__name__ == object.__module__ | |
200 | elif inspect.isclass(object): |
|
200 | elif inspect.isclass(object): | |
201 | return module.__name__ == object.__module__ |
|
201 | return module.__name__ == object.__module__ | |
202 | elif inspect.ismethod(object): |
|
202 | elif inspect.ismethod(object): | |
203 | # This one may be a bug in cython that fails to correctly set the |
|
203 | # This one may be a bug in cython that fails to correctly set the | |
204 | # __module__ attribute of methods, but since the same error is easy |
|
204 | # __module__ attribute of methods, but since the same error is easy | |
205 | # to make by extension code writers, having this safety in place |
|
205 | # to make by extension code writers, having this safety in place | |
206 | # isn't such a bad idea |
|
206 | # isn't such a bad idea | |
207 | return module.__name__ == object.im_class.__module__ |
|
207 | return module.__name__ == object.im_class.__module__ | |
208 | elif inspect.getmodule(object) is not None: |
|
208 | elif inspect.getmodule(object) is not None: | |
209 | return module is inspect.getmodule(object) |
|
209 | return module is inspect.getmodule(object) | |
210 | elif hasattr(object, '__module__'): |
|
210 | elif hasattr(object, '__module__'): | |
211 | return module.__name__ == object.__module__ |
|
211 | return module.__name__ == object.__module__ | |
212 | elif isinstance(object, property): |
|
212 | elif isinstance(object, property): | |
213 | return True # [XX] no way not be sure. |
|
213 | return True # [XX] no way not be sure. | |
214 | else: |
|
214 | else: | |
215 | raise ValueError("object must be a class or function") |
|
215 | raise ValueError("object must be a class or function") | |
216 |
|
216 | |||
217 | def _find(self, tests, obj, name, module, source_lines, globs, seen): |
|
217 | def _find(self, tests, obj, name, module, source_lines, globs, seen): | |
218 | """ |
|
218 | """ | |
219 | Find tests for the given object and any contained objects, and |
|
219 | Find tests for the given object and any contained objects, and | |
220 | add them to `tests`. |
|
220 | add them to `tests`. | |
221 | """ |
|
221 | """ | |
222 |
|
222 | |||
223 | if hasattr(obj,"skip_doctest"): |
|
223 | if hasattr(obj,"skip_doctest"): | |
224 | #print 'SKIPPING DOCTEST FOR:',obj # dbg |
|
224 | #print 'SKIPPING DOCTEST FOR:',obj # dbg | |
225 | obj = nodoc(obj) |
|
225 | obj = nodoc(obj) | |
226 |
|
226 | |||
227 | doctest.DocTestFinder._find(self,tests, obj, name, module, |
|
227 | doctest.DocTestFinder._find(self,tests, obj, name, module, | |
228 | source_lines, globs, seen) |
|
228 | source_lines, globs, seen) | |
229 |
|
229 | |||
230 | # Below we re-run pieces of the above method with manual modifications, |
|
230 | # Below we re-run pieces of the above method with manual modifications, | |
231 | # because the original code is buggy and fails to correctly identify |
|
231 | # because the original code is buggy and fails to correctly identify | |
232 | # doctests in extension modules. |
|
232 | # doctests in extension modules. | |
233 |
|
233 | |||
234 | # Local shorthands |
|
234 | # Local shorthands | |
235 | from inspect import isroutine, isclass, ismodule |
|
235 | from inspect import isroutine, isclass, ismodule | |
236 |
|
236 | |||
237 | # Look for tests in a module's contained objects. |
|
237 | # Look for tests in a module's contained objects. | |
238 | if inspect.ismodule(obj) and self._recurse: |
|
238 | if inspect.ismodule(obj) and self._recurse: | |
239 | for valname, val in obj.__dict__.items(): |
|
239 | for valname, val in obj.__dict__.items(): | |
240 | valname1 = '%s.%s' % (name, valname) |
|
240 | valname1 = '%s.%s' % (name, valname) | |
241 | if ( (isroutine(val) or isclass(val)) |
|
241 | if ( (isroutine(val) or isclass(val)) | |
242 | and self._from_module(module, val) ): |
|
242 | and self._from_module(module, val) ): | |
243 |
|
243 | |||
244 | self._find(tests, val, valname1, module, source_lines, |
|
244 | self._find(tests, val, valname1, module, source_lines, | |
245 | globs, seen) |
|
245 | globs, seen) | |
246 |
|
246 | |||
247 | # Look for tests in a class's contained objects. |
|
247 | # Look for tests in a class's contained objects. | |
248 | if inspect.isclass(obj) and self._recurse: |
|
248 | if inspect.isclass(obj) and self._recurse: | |
249 | #print 'RECURSE into class:',obj # dbg |
|
249 | #print 'RECURSE into class:',obj # dbg | |
250 | for valname, val in obj.__dict__.items(): |
|
250 | for valname, val in obj.__dict__.items(): | |
251 | # Special handling for staticmethod/classmethod. |
|
251 | # Special handling for staticmethod/classmethod. | |
252 | if isinstance(val, staticmethod): |
|
252 | if isinstance(val, staticmethod): | |
253 | val = getattr(obj, valname) |
|
253 | val = getattr(obj, valname) | |
254 | if isinstance(val, classmethod): |
|
254 | if isinstance(val, classmethod): | |
255 | val = getattr(obj, valname).im_func |
|
255 | val = getattr(obj, valname).im_func | |
256 |
|
256 | |||
257 | # Recurse to methods, properties, and nested classes. |
|
257 | # Recurse to methods, properties, and nested classes. | |
258 | if ((inspect.isfunction(val) or inspect.isclass(val) or |
|
258 | if ((inspect.isfunction(val) or inspect.isclass(val) or | |
259 | inspect.ismethod(val) or |
|
259 | inspect.ismethod(val) or | |
260 | isinstance(val, property)) and |
|
260 | isinstance(val, property)) and | |
261 | self._from_module(module, val)): |
|
261 | self._from_module(module, val)): | |
262 | valname = '%s.%s' % (name, valname) |
|
262 | valname = '%s.%s' % (name, valname) | |
263 | self._find(tests, val, valname, module, source_lines, |
|
263 | self._find(tests, val, valname, module, source_lines, | |
264 | globs, seen) |
|
264 | globs, seen) | |
265 |
|
265 | |||
266 |
|
266 | |||
267 | class IPDoctestOutputChecker(doctest.OutputChecker): |
|
267 | class IPDoctestOutputChecker(doctest.OutputChecker): | |
268 | """Second-chance checker with support for random tests. |
|
268 | """Second-chance checker with support for random tests. | |
269 |
|
269 | |||
270 | If the default comparison doesn't pass, this checker looks in the expected |
|
270 | If the default comparison doesn't pass, this checker looks in the expected | |
271 | output string for flags that tell us to ignore the output. |
|
271 | output string for flags that tell us to ignore the output. | |
272 | """ |
|
272 | """ | |
273 |
|
273 | |||
274 | random_re = re.compile(r'#\s*random\s+') |
|
274 | random_re = re.compile(r'#\s*random\s+') | |
275 |
|
275 | |||
276 | def check_output(self, want, got, optionflags): |
|
276 | def check_output(self, want, got, optionflags): | |
277 | """Check output, accepting special markers embedded in the output. |
|
277 | """Check output, accepting special markers embedded in the output. | |
278 |
|
278 | |||
279 | If the output didn't pass the default validation but the special string |
|
279 | If the output didn't pass the default validation but the special string | |
280 | '#random' is included, we accept it.""" |
|
280 | '#random' is included, we accept it.""" | |
281 |
|
281 | |||
282 | # Let the original tester verify first, in case people have valid tests |
|
282 | # Let the original tester verify first, in case people have valid tests | |
283 | # that happen to have a comment saying '#random' embedded in. |
|
283 | # that happen to have a comment saying '#random' embedded in. | |
284 | ret = doctest.OutputChecker.check_output(self, want, got, |
|
284 | ret = doctest.OutputChecker.check_output(self, want, got, | |
285 | optionflags) |
|
285 | optionflags) | |
286 | if not ret and self.random_re.search(want): |
|
286 | if not ret and self.random_re.search(want): | |
287 | #print >> sys.stderr, 'RANDOM OK:',want # dbg |
|
287 | #print >> sys.stderr, 'RANDOM OK:',want # dbg | |
288 | return True |
|
288 | return True | |
289 |
|
289 | |||
290 | return ret |
|
290 | return ret | |
291 |
|
291 | |||
292 |
|
292 | |||
293 | class DocTestCase(doctests.DocTestCase): |
|
293 | class DocTestCase(doctests.DocTestCase): | |
294 | """Proxy for DocTestCase: provides an address() method that |
|
294 | """Proxy for DocTestCase: provides an address() method that | |
295 | returns the correct address for the doctest case. Otherwise |
|
295 | returns the correct address for the doctest case. Otherwise | |
296 | acts as a proxy to the test case. To provide hints for address(), |
|
296 | acts as a proxy to the test case. To provide hints for address(), | |
297 | an obj may also be passed -- this will be used as the test object |
|
297 | an obj may also be passed -- this will be used as the test object | |
298 | for purposes of determining the test address, if it is provided. |
|
298 | for purposes of determining the test address, if it is provided. | |
299 | """ |
|
299 | """ | |
300 |
|
300 | |||
301 | # Note: this method was taken from numpy's nosetester module. |
|
301 | # Note: this method was taken from numpy's nosetester module. | |
302 |
|
302 | |||
303 | # Subclass nose.plugins.doctests.DocTestCase to work around a bug in |
|
303 | # Subclass nose.plugins.doctests.DocTestCase to work around a bug in | |
304 | # its constructor that blocks non-default arguments from being passed |
|
304 | # its constructor that blocks non-default arguments from being passed | |
305 | # down into doctest.DocTestCase |
|
305 | # down into doctest.DocTestCase | |
306 |
|
306 | |||
307 | def __init__(self, test, optionflags=0, setUp=None, tearDown=None, |
|
307 | def __init__(self, test, optionflags=0, setUp=None, tearDown=None, | |
308 | checker=None, obj=None, result_var='_'): |
|
308 | checker=None, obj=None, result_var='_'): | |
309 | self._result_var = result_var |
|
309 | self._result_var = result_var | |
310 | doctests.DocTestCase.__init__(self, test, |
|
310 | doctests.DocTestCase.__init__(self, test, | |
311 | optionflags=optionflags, |
|
311 | optionflags=optionflags, | |
312 | setUp=setUp, tearDown=tearDown, |
|
312 | setUp=setUp, tearDown=tearDown, | |
313 | checker=checker) |
|
313 | checker=checker) | |
314 | # Now we must actually copy the original constructor from the stdlib |
|
314 | # Now we must actually copy the original constructor from the stdlib | |
315 | # doctest class, because we can't call it directly and a bug in nose |
|
315 | # doctest class, because we can't call it directly and a bug in nose | |
316 | # means it never gets passed the right arguments. |
|
316 | # means it never gets passed the right arguments. | |
317 |
|
317 | |||
318 | self._dt_optionflags = optionflags |
|
318 | self._dt_optionflags = optionflags | |
319 | self._dt_checker = checker |
|
319 | self._dt_checker = checker | |
320 | self._dt_test = test |
|
320 | self._dt_test = test | |
321 | self._dt_setUp = setUp |
|
321 | self._dt_setUp = setUp | |
322 | self._dt_tearDown = tearDown |
|
322 | self._dt_tearDown = tearDown | |
323 |
|
323 | |||
324 | # XXX - store this runner once in the object! |
|
324 | # XXX - store this runner once in the object! | |
325 | runner = IPDocTestRunner(optionflags=optionflags, |
|
325 | runner = IPDocTestRunner(optionflags=optionflags, | |
326 | checker=checker, verbose=False) |
|
326 | checker=checker, verbose=False) | |
327 | self._dt_runner = runner |
|
327 | self._dt_runner = runner | |
328 |
|
328 | |||
329 |
|
329 | |||
330 | # Each doctest should remember what directory it was loaded from... |
|
330 | # Each doctest should remember what directory it was loaded from... | |
331 | self._ori_dir = os.getcwd() |
|
331 | self._ori_dir = os.getcwd() | |
332 |
|
332 | |||
333 | # Modified runTest from the default stdlib |
|
333 | # Modified runTest from the default stdlib | |
334 | def runTest(self): |
|
334 | def runTest(self): | |
335 | test = self._dt_test |
|
335 | test = self._dt_test | |
336 | runner = self._dt_runner |
|
336 | runner = self._dt_runner | |
337 |
|
337 | |||
338 | old = sys.stdout |
|
338 | old = sys.stdout | |
339 | new = StringIO() |
|
339 | new = StringIO() | |
340 | optionflags = self._dt_optionflags |
|
340 | optionflags = self._dt_optionflags | |
341 |
|
341 | |||
342 | if not (optionflags & REPORTING_FLAGS): |
|
342 | if not (optionflags & REPORTING_FLAGS): | |
343 | # The option flags don't include any reporting flags, |
|
343 | # The option flags don't include any reporting flags, | |
344 | # so add the default reporting flags |
|
344 | # so add the default reporting flags | |
345 | optionflags |= _unittest_reportflags |
|
345 | optionflags |= _unittest_reportflags | |
346 |
|
346 | |||
347 | try: |
|
347 | try: | |
348 | # Save our current directory and switch out to the one where the |
|
348 | # Save our current directory and switch out to the one where the | |
349 | # test was originally created, in case another doctest did a |
|
349 | # test was originally created, in case another doctest did a | |
350 | # directory change. We'll restore this in the finally clause. |
|
350 | # directory change. We'll restore this in the finally clause. | |
351 | curdir = os.getcwd() |
|
351 | curdir = os.getcwd() | |
352 | os.chdir(self._ori_dir) |
|
352 | os.chdir(self._ori_dir) | |
353 |
|
353 | |||
354 | runner.DIVIDER = "-"*70 |
|
354 | runner.DIVIDER = "-"*70 | |
355 | failures, tries = runner.run(test,out=new.write, |
|
355 | failures, tries = runner.run(test,out=new.write, | |
356 | clear_globs=False) |
|
356 | clear_globs=False) | |
357 | finally: |
|
357 | finally: | |
358 | sys.stdout = old |
|
358 | sys.stdout = old | |
359 | os.chdir(curdir) |
|
359 | os.chdir(curdir) | |
360 |
|
360 | |||
361 | if failures: |
|
361 | if failures: | |
362 | raise self.failureException(self.format_failure(new.getvalue())) |
|
362 | raise self.failureException(self.format_failure(new.getvalue())) | |
363 |
|
363 | |||
364 | def setUp(self): |
|
364 | def setUp(self): | |
365 | """Modified test setup that syncs with ipython namespace""" |
|
365 | """Modified test setup that syncs with ipython namespace""" | |
366 |
|
366 | |||
367 | if isinstance(self._dt_test.examples[0],IPExample): |
|
367 | if isinstance(self._dt_test.examples[0],IPExample): | |
368 | # for IPython examples *only*, we swap the globals with the ipython |
|
368 | # for IPython examples *only*, we swap the globals with the ipython | |
369 | # namespace, after updating it with the globals (which doctest |
|
369 | # namespace, after updating it with the globals (which doctest | |
370 | # fills with the necessary info from the module being tested). |
|
370 | # fills with the necessary info from the module being tested). | |
371 | _ip.IP.user_ns.update(self._dt_test.globs) |
|
371 | _ip.IP.user_ns.update(self._dt_test.globs) | |
372 | self._dt_test.globs = _ip.IP.user_ns |
|
372 | self._dt_test.globs = _ip.IP.user_ns | |
373 |
|
373 | |||
374 | doctests.DocTestCase.setUp(self) |
|
374 | doctests.DocTestCase.setUp(self) | |
375 |
|
375 | |||
376 |
|
376 | |||
377 |
|
377 | |||
378 | # A simple subclassing of the original with a different class name, so we can |
|
378 | # A simple subclassing of the original with a different class name, so we can | |
379 | # distinguish and treat differently IPython examples from pure python ones. |
|
379 | # distinguish and treat differently IPython examples from pure python ones. | |
380 | class IPExample(doctest.Example): pass |
|
380 | class IPExample(doctest.Example): pass | |
381 |
|
381 | |||
382 |
|
382 | |||
383 | class IPExternalExample(doctest.Example): |
|
383 | class IPExternalExample(doctest.Example): | |
384 | """Doctest examples to be run in an external process.""" |
|
384 | """Doctest examples to be run in an external process.""" | |
385 |
|
385 | |||
386 | def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, |
|
386 | def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, | |
387 | options=None): |
|
387 | options=None): | |
388 | # Parent constructor |
|
388 | # Parent constructor | |
389 | doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options) |
|
389 | doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options) | |
390 |
|
390 | |||
391 | # An EXTRA newline is needed to prevent pexpect hangs |
|
391 | # An EXTRA newline is needed to prevent pexpect hangs | |
392 | self.source += '\n' |
|
392 | self.source += '\n' | |
393 |
|
393 | |||
394 |
|
394 | |||
395 | class IPDocTestParser(doctest.DocTestParser): |
|
395 | class IPDocTestParser(doctest.DocTestParser): | |
396 | """ |
|
396 | """ | |
397 | A class used to parse strings containing doctest examples. |
|
397 | A class used to parse strings containing doctest examples. | |
398 |
|
398 | |||
399 | Note: This is a version modified to properly recognize IPython input and |
|
399 | Note: This is a version modified to properly recognize IPython input and | |
400 | convert any IPython examples into valid Python ones. |
|
400 | convert any IPython examples into valid Python ones. | |
401 | """ |
|
401 | """ | |
402 | # This regular expression is used to find doctest examples in a |
|
402 | # This regular expression is used to find doctest examples in a | |
403 | # string. It defines three groups: `source` is the source code |
|
403 | # string. It defines three groups: `source` is the source code | |
404 | # (including leading indentation and prompts); `indent` is the |
|
404 | # (including leading indentation and prompts); `indent` is the | |
405 | # indentation of the first (PS1) line of the source code; and |
|
405 | # indentation of the first (PS1) line of the source code; and | |
406 | # `want` is the expected output (including leading indentation). |
|
406 | # `want` is the expected output (including leading indentation). | |
407 |
|
407 | |||
408 | # Classic Python prompts or default IPython ones |
|
408 | # Classic Python prompts or default IPython ones | |
409 | _PS1_PY = r'>>>' |
|
409 | _PS1_PY = r'>>>' | |
410 | _PS2_PY = r'\.\.\.' |
|
410 | _PS2_PY = r'\.\.\.' | |
411 |
|
411 | |||
412 | _PS1_IP = r'In\ \[\d+\]:' |
|
412 | _PS1_IP = r'In\ \[\d+\]:' | |
413 | _PS2_IP = r'\ \ \ \.\.\.+:' |
|
413 | _PS2_IP = r'\ \ \ \.\.\.+:' | |
414 |
|
414 | |||
415 | _RE_TPL = r''' |
|
415 | _RE_TPL = r''' | |
416 | # Source consists of a PS1 line followed by zero or more PS2 lines. |
|
416 | # Source consists of a PS1 line followed by zero or more PS2 lines. | |
417 | (?P<source> |
|
417 | (?P<source> | |
418 | (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line |
|
418 | (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line | |
419 | (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines |
|
419 | (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines | |
420 | \n? # a newline |
|
420 | \n? # a newline | |
421 | # Want consists of any non-blank lines that do not start with PS1. |
|
421 | # Want consists of any non-blank lines that do not start with PS1. | |
422 | (?P<want> (?:(?![ ]*$) # Not a blank line |
|
422 | (?P<want> (?:(?![ ]*$) # Not a blank line | |
423 | (?![ ]*%s) # Not a line starting with PS1 |
|
423 | (?![ ]*%s) # Not a line starting with PS1 | |
424 | (?![ ]*%s) # Not a line starting with PS2 |
|
424 | (?![ ]*%s) # Not a line starting with PS2 | |
425 | .*$\n? # But any other line |
|
425 | .*$\n? # But any other line | |
426 | )*) |
|
426 | )*) | |
427 | ''' |
|
427 | ''' | |
428 |
|
428 | |||
429 | _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY), |
|
429 | _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY), | |
430 | re.MULTILINE | re.VERBOSE) |
|
430 | re.MULTILINE | re.VERBOSE) | |
431 |
|
431 | |||
432 | _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), |
|
432 | _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), | |
433 | re.MULTILINE | re.VERBOSE) |
|
433 | re.MULTILINE | re.VERBOSE) | |
434 |
|
434 | |||
435 | # Mark a test as being fully random. In this case, we simply append the |
|
435 | # Mark a test as being fully random. In this case, we simply append the | |
436 | # random marker ('#random') to each individual example's output. This way |
|
436 | # random marker ('#random') to each individual example's output. This way | |
437 | # we don't need to modify any other code. |
|
437 | # we don't need to modify any other code. | |
438 | _RANDOM_TEST = re.compile(r'#\s*all-random\s+') |
|
438 | _RANDOM_TEST = re.compile(r'#\s*all-random\s+') | |
439 |
|
439 | |||
440 | # Mark tests to be executed in an external process - currently unsupported. |
|
440 | # Mark tests to be executed in an external process - currently unsupported. | |
441 | _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL') |
|
441 | _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL') | |
442 |
|
442 | |||
443 | def ip2py(self,source): |
|
443 | def ip2py(self,source): | |
444 | """Convert input IPython source into valid Python.""" |
|
444 | """Convert input IPython source into valid Python.""" | |
445 | out = [] |
|
445 | out = [] | |
446 | newline = out.append |
|
446 | newline = out.append | |
447 | for lnum,line in enumerate(source.splitlines()): |
|
447 | for lnum,line in enumerate(source.splitlines()): | |
448 | newline(_ip.IP.prefilter(line,lnum>0)) |
|
448 | newline(_ip.IP.prefilter(line,lnum>0)) | |
449 | newline('') # ensure a closing newline, needed by doctest |
|
449 | newline('') # ensure a closing newline, needed by doctest | |
450 | #print "PYSRC:", '\n'.join(out) # dbg |
|
450 | #print "PYSRC:", '\n'.join(out) # dbg | |
451 | return '\n'.join(out) |
|
451 | return '\n'.join(out) | |
452 |
|
452 | |||
453 | def parse(self, string, name='<string>'): |
|
453 | def parse(self, string, name='<string>'): | |
454 | """ |
|
454 | """ | |
455 | Divide the given string into examples and intervening text, |
|
455 | Divide the given string into examples and intervening text, | |
456 | and return them as a list of alternating Examples and strings. |
|
456 | and return them as a list of alternating Examples and strings. | |
457 | Line numbers for the Examples are 0-based. The optional |
|
457 | Line numbers for the Examples are 0-based. The optional | |
458 | argument `name` is a name identifying this string, and is only |
|
458 | argument `name` is a name identifying this string, and is only | |
459 | used for error messages. |
|
459 | used for error messages. | |
460 | """ |
|
460 | """ | |
461 |
|
461 | |||
462 | #print 'Parse string:\n',string # dbg |
|
462 | #print 'Parse string:\n',string # dbg | |
463 |
|
463 | |||
464 | string = string.expandtabs() |
|
464 | string = string.expandtabs() | |
465 | # If all lines begin with the same indentation, then strip it. |
|
465 | # If all lines begin with the same indentation, then strip it. | |
466 | min_indent = self._min_indent(string) |
|
466 | min_indent = self._min_indent(string) | |
467 | if min_indent > 0: |
|
467 | if min_indent > 0: | |
468 | string = '\n'.join([l[min_indent:] for l in string.split('\n')]) |
|
468 | string = '\n'.join([l[min_indent:] for l in string.split('\n')]) | |
469 |
|
469 | |||
470 | output = [] |
|
470 | output = [] | |
471 | charno, lineno = 0, 0 |
|
471 | charno, lineno = 0, 0 | |
472 |
|
472 | |||
473 | # We make 'all random' tests by adding the '# random' mark to every |
|
473 | # We make 'all random' tests by adding the '# random' mark to every | |
474 | # block of output in the test. |
|
474 | # block of output in the test. | |
475 | if self._RANDOM_TEST.search(string): |
|
475 | if self._RANDOM_TEST.search(string): | |
476 | random_marker = '\n# random' |
|
476 | random_marker = '\n# random' | |
477 | else: |
|
477 | else: | |
478 | random_marker = '' |
|
478 | random_marker = '' | |
479 |
|
479 | |||
480 | # Whether to convert the input from ipython to python syntax |
|
480 | # Whether to convert the input from ipython to python syntax | |
481 | ip2py = False |
|
481 | ip2py = False | |
482 | # Find all doctest examples in the string. First, try them as Python |
|
482 | # Find all doctest examples in the string. First, try them as Python | |
483 | # examples, then as IPython ones |
|
483 | # examples, then as IPython ones | |
484 | terms = list(self._EXAMPLE_RE_PY.finditer(string)) |
|
484 | terms = list(self._EXAMPLE_RE_PY.finditer(string)) | |
485 | if terms: |
|
485 | if terms: | |
486 | # Normal Python example |
|
486 | # Normal Python example | |
487 | #print '-'*70 # dbg |
|
487 | #print '-'*70 # dbg | |
488 | #print 'PyExample, Source:\n',string # dbg |
|
488 | #print 'PyExample, Source:\n',string # dbg | |
489 | #print '-'*70 # dbg |
|
489 | #print '-'*70 # dbg | |
490 | Example = doctest.Example |
|
490 | Example = doctest.Example | |
491 | else: |
|
491 | else: | |
492 | # It's an ipython example. Note that IPExamples are run |
|
492 | # It's an ipython example. Note that IPExamples are run | |
493 | # in-process, so their syntax must be turned into valid python. |
|
493 | # in-process, so their syntax must be turned into valid python. | |
494 | # IPExternalExamples are run out-of-process (via pexpect) so they |
|
494 | # IPExternalExamples are run out-of-process (via pexpect) so they | |
495 | # don't need any filtering (a real ipython will be executing them). |
|
495 | # don't need any filtering (a real ipython will be executing them). | |
496 | terms = list(self._EXAMPLE_RE_IP.finditer(string)) |
|
496 | terms = list(self._EXAMPLE_RE_IP.finditer(string)) | |
497 | if self._EXTERNAL_IP.search(string): |
|
497 | if self._EXTERNAL_IP.search(string): | |
498 | #print '-'*70 # dbg |
|
498 | #print '-'*70 # dbg | |
499 | #print 'IPExternalExample, Source:\n',string # dbg |
|
499 | #print 'IPExternalExample, Source:\n',string # dbg | |
500 | #print '-'*70 # dbg |
|
500 | #print '-'*70 # dbg | |
501 | Example = IPExternalExample |
|
501 | Example = IPExternalExample | |
502 | else: |
|
502 | else: | |
503 | #print '-'*70 # dbg |
|
503 | #print '-'*70 # dbg | |
504 | #print 'IPExample, Source:\n',string # dbg |
|
504 | #print 'IPExample, Source:\n',string # dbg | |
505 | #print '-'*70 # dbg |
|
505 | #print '-'*70 # dbg | |
506 | Example = IPExample |
|
506 | Example = IPExample | |
507 | ip2py = True |
|
507 | ip2py = True | |
508 |
|
508 | |||
509 | for m in terms: |
|
509 | for m in terms: | |
510 | # Add the pre-example text to `output`. |
|
510 | # Add the pre-example text to `output`. | |
511 | output.append(string[charno:m.start()]) |
|
511 | output.append(string[charno:m.start()]) | |
512 | # Update lineno (lines before this example) |
|
512 | # Update lineno (lines before this example) | |
513 | lineno += string.count('\n', charno, m.start()) |
|
513 | lineno += string.count('\n', charno, m.start()) | |
514 | # Extract info from the regexp match. |
|
514 | # Extract info from the regexp match. | |
515 | (source, options, want, exc_msg) = \ |
|
515 | (source, options, want, exc_msg) = \ | |
516 | self._parse_example(m, name, lineno,ip2py) |
|
516 | self._parse_example(m, name, lineno,ip2py) | |
517 |
|
517 | |||
518 | # Append the random-output marker (it defaults to empty in most |
|
518 | # Append the random-output marker (it defaults to empty in most | |
519 | # cases, it's only non-empty for 'all-random' tests): |
|
519 | # cases, it's only non-empty for 'all-random' tests): | |
520 | want += random_marker |
|
520 | want += random_marker | |
521 |
|
521 | |||
522 | if Example is IPExternalExample: |
|
522 | if Example is IPExternalExample: | |
523 | options[doctest.NORMALIZE_WHITESPACE] = True |
|
523 | options[doctest.NORMALIZE_WHITESPACE] = True | |
524 | want += '\n' |
|
524 | want += '\n' | |
525 |
|
525 | |||
526 | # Create an Example, and add it to the list. |
|
526 | # Create an Example, and add it to the list. | |
527 | if not self._IS_BLANK_OR_COMMENT(source): |
|
527 | if not self._IS_BLANK_OR_COMMENT(source): | |
528 | output.append(Example(source, want, exc_msg, |
|
528 | output.append(Example(source, want, exc_msg, | |
529 | lineno=lineno, |
|
529 | lineno=lineno, | |
530 | indent=min_indent+len(m.group('indent')), |
|
530 | indent=min_indent+len(m.group('indent')), | |
531 | options=options)) |
|
531 | options=options)) | |
532 | # Update lineno (lines inside this example) |
|
532 | # Update lineno (lines inside this example) | |
533 | lineno += string.count('\n', m.start(), m.end()) |
|
533 | lineno += string.count('\n', m.start(), m.end()) | |
534 | # Update charno. |
|
534 | # Update charno. | |
535 | charno = m.end() |
|
535 | charno = m.end() | |
536 | # Add any remaining post-example text to `output`. |
|
536 | # Add any remaining post-example text to `output`. | |
537 | output.append(string[charno:]) |
|
537 | output.append(string[charno:]) | |
538 | return output |
|
538 | return output | |
539 |
|
539 | |||
540 | def _parse_example(self, m, name, lineno,ip2py=False): |
|
540 | def _parse_example(self, m, name, lineno,ip2py=False): | |
541 | """ |
|
541 | """ | |
542 | Given a regular expression match from `_EXAMPLE_RE` (`m`), |
|
542 | Given a regular expression match from `_EXAMPLE_RE` (`m`), | |
543 | return a pair `(source, want)`, where `source` is the matched |
|
543 | return a pair `(source, want)`, where `source` is the matched | |
544 | example's source code (with prompts and indentation stripped); |
|
544 | example's source code (with prompts and indentation stripped); | |
545 | and `want` is the example's expected output (with indentation |
|
545 | and `want` is the example's expected output (with indentation | |
546 | stripped). |
|
546 | stripped). | |
547 |
|
547 | |||
548 | `name` is the string's name, and `lineno` is the line number |
|
548 | `name` is the string's name, and `lineno` is the line number | |
549 | where the example starts; both are used for error messages. |
|
549 | where the example starts; both are used for error messages. | |
550 |
|
550 | |||
551 | Optional: |
|
551 | Optional: | |
552 | `ip2py`: if true, filter the input via IPython to convert the syntax |
|
552 | `ip2py`: if true, filter the input via IPython to convert the syntax | |
553 | into valid python. |
|
553 | into valid python. | |
554 | """ |
|
554 | """ | |
555 |
|
555 | |||
556 | # Get the example's indentation level. |
|
556 | # Get the example's indentation level. | |
557 | indent = len(m.group('indent')) |
|
557 | indent = len(m.group('indent')) | |
558 |
|
558 | |||
559 | # Divide source into lines; check that they're properly |
|
559 | # Divide source into lines; check that they're properly | |
560 | # indented; and then strip their indentation & prompts. |
|
560 | # indented; and then strip their indentation & prompts. | |
561 | source_lines = m.group('source').split('\n') |
|
561 | source_lines = m.group('source').split('\n') | |
562 |
|
562 | |||
563 | # We're using variable-length input prompts |
|
563 | # We're using variable-length input prompts | |
564 | ps1 = m.group('ps1') |
|
564 | ps1 = m.group('ps1') | |
565 | ps2 = m.group('ps2') |
|
565 | ps2 = m.group('ps2') | |
566 | ps1_len = len(ps1) |
|
566 | ps1_len = len(ps1) | |
567 |
|
567 | |||
568 | self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len) |
|
568 | self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len) | |
569 | if ps2: |
|
569 | if ps2: | |
570 | self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno) |
|
570 | self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno) | |
571 |
|
571 | |||
572 | source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines]) |
|
572 | source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines]) | |
573 |
|
573 | |||
574 | if ip2py: |
|
574 | if ip2py: | |
575 | # Convert source input from IPython into valid Python syntax |
|
575 | # Convert source input from IPython into valid Python syntax | |
576 | source = self.ip2py(source) |
|
576 | source = self.ip2py(source) | |
577 |
|
577 | |||
578 | # Divide want into lines; check that it's properly indented; and |
|
578 | # Divide want into lines; check that it's properly indented; and | |
579 | # then strip the indentation. Spaces before the last newline should |
|
579 | # then strip the indentation. Spaces before the last newline should | |
580 | # be preserved, so plain rstrip() isn't good enough. |
|
580 | # be preserved, so plain rstrip() isn't good enough. | |
581 | want = m.group('want') |
|
581 | want = m.group('want') | |
582 | want_lines = want.split('\n') |
|
582 | want_lines = want.split('\n') | |
583 | if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): |
|
583 | if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): | |
584 | del want_lines[-1] # forget final newline & spaces after it |
|
584 | del want_lines[-1] # forget final newline & spaces after it | |
585 | self._check_prefix(want_lines, ' '*indent, name, |
|
585 | self._check_prefix(want_lines, ' '*indent, name, | |
586 | lineno + len(source_lines)) |
|
586 | lineno + len(source_lines)) | |
587 |
|
587 | |||
588 | # Remove ipython output prompt that might be present in the first line |
|
588 | # Remove ipython output prompt that might be present in the first line | |
589 | want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0]) |
|
589 | want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0]) | |
590 |
|
590 | |||
591 | want = '\n'.join([wl[indent:] for wl in want_lines]) |
|
591 | want = '\n'.join([wl[indent:] for wl in want_lines]) | |
592 |
|
592 | |||
593 | # If `want` contains a traceback message, then extract it. |
|
593 | # If `want` contains a traceback message, then extract it. | |
594 | m = self._EXCEPTION_RE.match(want) |
|
594 | m = self._EXCEPTION_RE.match(want) | |
595 | if m: |
|
595 | if m: | |
596 | exc_msg = m.group('msg') |
|
596 | exc_msg = m.group('msg') | |
597 | else: |
|
597 | else: | |
598 | exc_msg = None |
|
598 | exc_msg = None | |
599 |
|
599 | |||
600 | # Extract options from the source. |
|
600 | # Extract options from the source. | |
601 | options = self._find_options(source, name, lineno) |
|
601 | options = self._find_options(source, name, lineno) | |
602 |
|
602 | |||
603 | return source, options, want, exc_msg |
|
603 | return source, options, want, exc_msg | |
604 |
|
604 | |||
605 | def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len): |
|
605 | def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len): | |
606 | """ |
|
606 | """ | |
607 | Given the lines of a source string (including prompts and |
|
607 | Given the lines of a source string (including prompts and | |
608 | leading indentation), check to make sure that every prompt is |
|
608 | leading indentation), check to make sure that every prompt is | |
609 | followed by a space character. If any line is not followed by |
|
609 | followed by a space character. If any line is not followed by | |
610 | a space character, then raise ValueError. |
|
610 | a space character, then raise ValueError. | |
611 |
|
611 | |||
612 | Note: IPython-modified version which takes the input prompt length as a |
|
612 | Note: IPython-modified version which takes the input prompt length as a | |
613 | parameter, so that prompts of variable length can be dealt with. |
|
613 | parameter, so that prompts of variable length can be dealt with. | |
614 | """ |
|
614 | """ | |
615 | space_idx = indent+ps1_len |
|
615 | space_idx = indent+ps1_len | |
616 | min_len = space_idx+1 |
|
616 | min_len = space_idx+1 | |
617 | for i, line in enumerate(lines): |
|
617 | for i, line in enumerate(lines): | |
618 | if len(line) >= min_len and line[space_idx] != ' ': |
|
618 | if len(line) >= min_len and line[space_idx] != ' ': | |
619 | raise ValueError('line %r of the docstring for %s ' |
|
619 | raise ValueError('line %r of the docstring for %s ' | |
620 | 'lacks blank after %s: %r' % |
|
620 | 'lacks blank after %s: %r' % | |
621 | (lineno+i+1, name, |
|
621 | (lineno+i+1, name, | |
622 | line[indent:space_idx], line)) |
|
622 | line[indent:space_idx], line)) | |
623 |
|
623 | |||
624 |
|
624 | |||
625 | SKIP = doctest.register_optionflag('SKIP') |
|
625 | SKIP = doctest.register_optionflag('SKIP') | |
626 |
|
626 | |||
627 |
|
627 | |||
628 | class IPDocTestRunner(doctest.DocTestRunner,object): |
|
628 | class IPDocTestRunner(doctest.DocTestRunner,object): | |
629 | """Test runner that synchronizes the IPython namespace with test globals. |
|
629 | """Test runner that synchronizes the IPython namespace with test globals. | |
630 | """ |
|
630 | """ | |
631 |
|
631 | |||
632 | def run(self, test, compileflags=None, out=None, clear_globs=True): |
|
632 | def run(self, test, compileflags=None, out=None, clear_globs=True): | |
633 |
|
633 | |||
634 | # Hack: ipython needs access to the execution context of the example, |
|
634 | # Hack: ipython needs access to the execution context of the example, | |
635 | # so that it can propagate user variables loaded by %run into |
|
635 | # so that it can propagate user variables loaded by %run into | |
636 | # test.globs. We put them here into our modified %run as a function |
|
636 | # test.globs. We put them here into our modified %run as a function | |
637 | # attribute. Our new %run will then only make the namespace update |
|
637 | # attribute. Our new %run will then only make the namespace update | |
638 | # when called (rather than unconconditionally updating test.globs here |
|
638 | # when called (rather than unconconditionally updating test.globs here | |
639 | # for all examples, most of which won't be calling %run anyway). |
|
639 | # for all examples, most of which won't be calling %run anyway). | |
640 | _run_ns_sync.test_globs = test.globs |
|
640 | _run_ns_sync.test_globs = test.globs | |
641 |
|
641 | |||
642 | return super(IPDocTestRunner,self).run(test, |
|
642 | return super(IPDocTestRunner,self).run(test, | |
643 | compileflags,out,clear_globs) |
|
643 | compileflags,out,clear_globs) | |
644 |
|
644 | |||
645 |
|
645 | |||
646 | class DocFileCase(doctest.DocFileCase): |
|
646 | class DocFileCase(doctest.DocFileCase): | |
647 | """Overrides to provide filename |
|
647 | """Overrides to provide filename | |
648 | """ |
|
648 | """ | |
649 | def address(self): |
|
649 | def address(self): | |
650 | return (self._dt_test.filename, None, None) |
|
650 | return (self._dt_test.filename, None, None) | |
651 |
|
651 | |||
652 |
|
652 | |||
653 | class ExtensionDoctest(doctests.Doctest): |
|
653 | class ExtensionDoctest(doctests.Doctest): | |
654 | """Nose Plugin that supports doctests in extension modules. |
|
654 | """Nose Plugin that supports doctests in extension modules. | |
655 | """ |
|
655 | """ | |
656 | name = 'extdoctest' # call nosetests with --with-extdoctest |
|
656 | name = 'extdoctest' # call nosetests with --with-extdoctest | |
657 | enabled = True |
|
657 | enabled = True | |
658 |
|
658 | |||
659 | def options(self, parser, env=os.environ): |
|
659 | def options(self, parser, env=os.environ): | |
660 | Plugin.options(self, parser, env) |
|
660 | Plugin.options(self, parser, env) | |
|
661 | parser.add_option('--doctest-tests', action='store_true', | |||
|
662 | dest='doctest_tests', | |||
|
663 | default=env.get('NOSE_DOCTEST_TESTS',True), | |||
|
664 | help="Also look for doctests in test modules. " | |||
|
665 | "Note that classes, methods and functions should " | |||
|
666 | "have either doctests or non-doctest tests, " | |||
|
667 | "not both. [NOSE_DOCTEST_TESTS]") | |||
|
668 | parser.add_option('--doctest-extension', action="append", | |||
|
669 | dest="doctestExtension", | |||
|
670 | help="Also look for doctests in files with " | |||
|
671 | "this extension [NOSE_DOCTEST_EXTENSION]") | |||
|
672 | # Set the default as a list, if given in env; otherwise | |||
|
673 | # an additional value set on the command line will cause | |||
|
674 | # an error. | |||
|
675 | env_setting = env.get('NOSE_DOCTEST_EXTENSION') | |||
|
676 | if env_setting is not None: | |||
|
677 | parser.set_defaults(doctestExtension=tolist(env_setting)) | |||
|
678 | ||||
661 |
|
679 | |||
662 | def configure(self, options, config): |
|
680 | def configure(self, options, config): | |
663 | Plugin.configure(self, options, config) |
|
681 | Plugin.configure(self, options, config) | |
664 | self.doctest_tests = options.doctest_tests |
|
682 | self.doctest_tests = options.doctest_tests | |
665 | self.extension = tolist(options.doctestExtension) |
|
683 | self.extension = tolist(options.doctestExtension) | |
666 |
|
684 | |||
667 | self.parser = doctest.DocTestParser() |
|
685 | self.parser = doctest.DocTestParser() | |
668 | self.finder = DocTestFinder() |
|
686 | self.finder = DocTestFinder() | |
669 | self.checker = IPDoctestOutputChecker() |
|
687 | self.checker = IPDoctestOutputChecker() | |
670 | self.globs = None |
|
688 | self.globs = None | |
671 | self.extraglobs = None |
|
689 | self.extraglobs = None | |
672 |
|
690 | |||
673 | def loadTestsFromExtensionModule(self,filename): |
|
691 | def loadTestsFromExtensionModule(self,filename): | |
674 | bpath,mod = os.path.split(filename) |
|
692 | bpath,mod = os.path.split(filename) | |
675 | modname = os.path.splitext(mod)[0] |
|
693 | modname = os.path.splitext(mod)[0] | |
676 | try: |
|
694 | try: | |
677 | sys.path.append(bpath) |
|
695 | sys.path.append(bpath) | |
678 | module = __import__(modname) |
|
696 | module = __import__(modname) | |
679 | tests = list(self.loadTestsFromModule(module)) |
|
697 | tests = list(self.loadTestsFromModule(module)) | |
680 | finally: |
|
698 | finally: | |
681 | sys.path.pop() |
|
699 | sys.path.pop() | |
682 | return tests |
|
700 | return tests | |
683 |
|
701 | |||
684 | # NOTE: the method below is almost a copy of the original one in nose, with |
|
702 | # NOTE: the method below is almost a copy of the original one in nose, with | |
685 | # a few modifications to control output checking. |
|
703 | # a few modifications to control output checking. | |
686 |
|
704 | |||
687 | def loadTestsFromModule(self, module): |
|
705 | def loadTestsFromModule(self, module): | |
688 | #print 'lTM',module # dbg |
|
706 | #print 'lTM',module # dbg | |
689 |
|
707 | |||
690 | if not self.matches(module.__name__): |
|
708 | if not self.matches(module.__name__): | |
691 | log.debug("Doctest doesn't want module %s", module) |
|
709 | log.debug("Doctest doesn't want module %s", module) | |
692 | return |
|
710 | return | |
693 |
|
711 | |||
694 | tests = self.finder.find(module,globs=self.globs, |
|
712 | tests = self.finder.find(module,globs=self.globs, | |
695 | extraglobs=self.extraglobs) |
|
713 | extraglobs=self.extraglobs) | |
696 | if not tests: |
|
714 | if not tests: | |
697 | return |
|
715 | return | |
698 |
|
716 | |||
699 | # always use whitespace and ellipsis options |
|
717 | # always use whitespace and ellipsis options | |
700 | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS |
|
718 | optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | |
701 |
|
719 | |||
702 | tests.sort() |
|
720 | tests.sort() | |
703 | module_file = module.__file__ |
|
721 | module_file = module.__file__ | |
704 | if module_file[-4:] in ('.pyc', '.pyo'): |
|
722 | if module_file[-4:] in ('.pyc', '.pyo'): | |
705 | module_file = module_file[:-1] |
|
723 | module_file = module_file[:-1] | |
706 | for test in tests: |
|
724 | for test in tests: | |
707 | if not test.examples: |
|
725 | if not test.examples: | |
708 | continue |
|
726 | continue | |
709 | if not test.filename: |
|
727 | if not test.filename: | |
710 | test.filename = module_file |
|
728 | test.filename = module_file | |
711 |
|
729 | |||
712 | yield DocTestCase(test, |
|
730 | yield DocTestCase(test, | |
713 | optionflags=optionflags, |
|
731 | optionflags=optionflags, | |
714 | checker=self.checker) |
|
732 | checker=self.checker) | |
715 |
|
733 | |||
716 |
|
734 | |||
717 | def loadTestsFromFile(self, filename): |
|
735 | def loadTestsFromFile(self, filename): | |
718 | #print 'lTF',filename # dbg |
|
736 | #print 'lTF',filename # dbg | |
719 |
|
737 | |||
720 | if is_extension_module(filename): |
|
738 | if is_extension_module(filename): | |
721 | for t in self.loadTestsFromExtensionModule(filename): |
|
739 | for t in self.loadTestsFromExtensionModule(filename): | |
722 | yield t |
|
740 | yield t | |
723 | else: |
|
741 | else: | |
724 | if self.extension and anyp(filename.endswith, self.extension): |
|
742 | if self.extension and anyp(filename.endswith, self.extension): | |
725 | name = os.path.basename(filename) |
|
743 | name = os.path.basename(filename) | |
726 | dh = open(filename) |
|
744 | dh = open(filename) | |
727 | try: |
|
745 | try: | |
728 | doc = dh.read() |
|
746 | doc = dh.read() | |
729 | finally: |
|
747 | finally: | |
730 | dh.close() |
|
748 | dh.close() | |
731 | test = self.parser.get_doctest( |
|
749 | test = self.parser.get_doctest( | |
732 | doc, globs={'__file__': filename}, name=name, |
|
750 | doc, globs={'__file__': filename}, name=name, | |
733 | filename=filename, lineno=0) |
|
751 | filename=filename, lineno=0) | |
734 | if test.examples: |
|
752 | if test.examples: | |
735 | #print 'FileCase:',test.examples # dbg |
|
753 | #print 'FileCase:',test.examples # dbg | |
736 | yield DocFileCase(test) |
|
754 | yield DocFileCase(test) | |
737 | else: |
|
755 | else: | |
738 | yield False # no tests to load |
|
756 | yield False # no tests to load | |
739 |
|
757 | |||
740 | def wantFile(self,filename): |
|
758 | def wantFile(self,filename): | |
741 | """Return whether the given filename should be scanned for tests. |
|
759 | """Return whether the given filename should be scanned for tests. | |
742 |
|
760 | |||
743 | Modified version that accepts extension modules as valid containers for |
|
761 | Modified version that accepts extension modules as valid containers for | |
744 | doctests. |
|
762 | doctests. | |
745 | """ |
|
763 | """ | |
746 |
|
|
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,180 +1,185 b'' | |||||
1 | # Module imports |
|
1 | # Module imports | |
2 | # Std lib |
|
2 | # Std lib | |
3 | import inspect |
|
3 | import inspect | |
4 |
|
4 | |||
5 | # Third party |
|
5 | # Third party | |
6 |
|
6 | |||
7 | # Our own |
|
7 | # Our own | |
8 | from IPython.testing import decorators as dec |
|
8 | from IPython.testing import decorators as dec | |
9 |
|
9 | |||
10 | #----------------------------------------------------------------------------- |
|
10 | #----------------------------------------------------------------------------- | |
11 | # Utilities |
|
11 | # Utilities | |
12 |
|
12 | |||
13 | # Note: copied from OInspect, kept here so the testing stuff doesn't create |
|
13 | # Note: copied from OInspect, kept here so the testing stuff doesn't create | |
14 | # circular dependencies and is easier to reuse. |
|
14 | # circular dependencies and is easier to reuse. | |
15 | def getargspec(obj): |
|
15 | def getargspec(obj): | |
16 | """Get the names and default values of a function's arguments. |
|
16 | """Get the names and default values of a function's arguments. | |
17 |
|
17 | |||
18 | A tuple of four things is returned: (args, varargs, varkw, defaults). |
|
18 | A tuple of four things is returned: (args, varargs, varkw, defaults). | |
19 | 'args' is a list of the argument names (it may contain nested lists). |
|
19 | 'args' is a list of the argument names (it may contain nested lists). | |
20 | 'varargs' and 'varkw' are the names of the * and ** arguments or None. |
|
20 | 'varargs' and 'varkw' are the names of the * and ** arguments or None. | |
21 | 'defaults' is an n-tuple of the default values of the last n arguments. |
|
21 | 'defaults' is an n-tuple of the default values of the last n arguments. | |
22 |
|
22 | |||
23 | Modified version of inspect.getargspec from the Python Standard |
|
23 | Modified version of inspect.getargspec from the Python Standard | |
24 | Library.""" |
|
24 | Library.""" | |
25 |
|
25 | |||
26 | if inspect.isfunction(obj): |
|
26 | if inspect.isfunction(obj): | |
27 | func_obj = obj |
|
27 | func_obj = obj | |
28 | elif inspect.ismethod(obj): |
|
28 | elif inspect.ismethod(obj): | |
29 | func_obj = obj.im_func |
|
29 | func_obj = obj.im_func | |
30 | else: |
|
30 | else: | |
31 | raise TypeError, 'arg is not a Python function' |
|
31 | raise TypeError, 'arg is not a Python function' | |
32 | args, varargs, varkw = inspect.getargs(func_obj.func_code) |
|
32 | args, varargs, varkw = inspect.getargs(func_obj.func_code) | |
33 | return args, varargs, varkw, func_obj.func_defaults |
|
33 | return args, varargs, varkw, func_obj.func_defaults | |
34 |
|
34 | |||
35 | #----------------------------------------------------------------------------- |
|
35 | #----------------------------------------------------------------------------- | |
36 | # Testing functions |
|
36 | # Testing functions | |
37 |
|
37 | |||
38 | def test_trivial(): |
|
38 | def test_trivial(): | |
39 | """A trivial passing test.""" |
|
39 | """A trivial passing test.""" | |
40 | pass |
|
40 | pass | |
41 |
|
41 | |||
42 |
|
42 | |||
43 | @dec.skip |
|
43 | @dec.skip | |
44 | def test_deliberately_broken(): |
|
44 | def test_deliberately_broken(): | |
45 | """A deliberately broken test - we want to skip this one.""" |
|
45 | """A deliberately broken test - we want to skip this one.""" | |
46 | 1/0 |
|
46 | 1/0 | |
47 |
|
47 | |||
|
48 | @dec.skip('foo') | |||
|
49 | def test_deliberately_broken2(): | |||
|
50 | """Another deliberately broken test - we want to skip this one.""" | |||
|
51 | 1/0 | |||
|
52 | ||||
48 |
|
53 | |||
49 | # Verify that we can correctly skip the doctest for a function at will, but |
|
54 | # Verify that we can correctly skip the doctest for a function at will, but | |
50 | # that the docstring itself is NOT destroyed by the decorator. |
|
55 | # that the docstring itself is NOT destroyed by the decorator. | |
51 | @dec.skip_doctest |
|
56 | @dec.skip_doctest | |
52 | def doctest_bad(x,y=1,**k): |
|
57 | def doctest_bad(x,y=1,**k): | |
53 | """A function whose doctest we need to skip. |
|
58 | """A function whose doctest we need to skip. | |
54 |
|
59 | |||
55 | >>> 1+1 |
|
60 | >>> 1+1 | |
56 | 3 |
|
61 | 3 | |
57 | """ |
|
62 | """ | |
58 | print 'x:',x |
|
63 | print 'x:',x | |
59 | print 'y:',y |
|
64 | print 'y:',y | |
60 | print 'k:',k |
|
65 | print 'k:',k | |
61 |
|
66 | |||
62 |
|
67 | |||
63 | def call_doctest_bad(): |
|
68 | def call_doctest_bad(): | |
64 | """Check that we can still call the decorated functions. |
|
69 | """Check that we can still call the decorated functions. | |
65 |
|
70 | |||
66 | >>> doctest_bad(3,y=4) |
|
71 | >>> doctest_bad(3,y=4) | |
67 | x: 3 |
|
72 | x: 3 | |
68 | y: 4 |
|
73 | y: 4 | |
69 | k: {} |
|
74 | k: {} | |
70 | """ |
|
75 | """ | |
71 | pass |
|
76 | pass | |
72 |
|
77 | |||
73 |
|
78 | |||
74 | # Doctest skipping should work for class methods too |
|
79 | # Doctest skipping should work for class methods too | |
75 | class foo(object): |
|
80 | class foo(object): | |
76 | """Foo |
|
81 | """Foo | |
77 |
|
82 | |||
78 | Example: |
|
83 | Example: | |
79 |
|
84 | |||
80 | >>> 1+1 |
|
85 | >>> 1+1 | |
81 | 2 |
|
86 | 2 | |
82 | """ |
|
87 | """ | |
83 |
|
88 | |||
84 | @dec.skip_doctest |
|
89 | @dec.skip_doctest | |
85 | def __init__(self,x): |
|
90 | def __init__(self,x): | |
86 | """Make a foo. |
|
91 | """Make a foo. | |
87 |
|
92 | |||
88 | Example: |
|
93 | Example: | |
89 |
|
94 | |||
90 | >>> f = foo(3) |
|
95 | >>> f = foo(3) | |
91 | junk |
|
96 | junk | |
92 | """ |
|
97 | """ | |
93 | print 'Making a foo.' |
|
98 | print 'Making a foo.' | |
94 | self.x = x |
|
99 | self.x = x | |
95 |
|
100 | |||
96 | @dec.skip_doctest |
|
101 | @dec.skip_doctest | |
97 | def bar(self,y): |
|
102 | def bar(self,y): | |
98 | """Example: |
|
103 | """Example: | |
99 |
|
104 | |||
100 | >>> f = foo(3) |
|
105 | >>> f = foo(3) | |
101 | >>> f.bar(0) |
|
106 | >>> f.bar(0) | |
102 | boom! |
|
107 | boom! | |
103 | >>> 1/0 |
|
108 | >>> 1/0 | |
104 | bam! |
|
109 | bam! | |
105 | """ |
|
110 | """ | |
106 | return 1/y |
|
111 | return 1/y | |
107 |
|
112 | |||
108 | def baz(self,y): |
|
113 | def baz(self,y): | |
109 | """Example: |
|
114 | """Example: | |
110 |
|
115 | |||
111 | >>> f = foo(3) |
|
116 | >>> f = foo(3) | |
112 | Making a foo. |
|
117 | Making a foo. | |
113 | >>> f.baz(3) |
|
118 | >>> f.baz(3) | |
114 | True |
|
119 | True | |
115 | """ |
|
120 | """ | |
116 | return self.x==y |
|
121 | return self.x==y | |
117 |
|
122 | |||
118 |
|
123 | |||
119 | def test_skip_dt_decorator(): |
|
124 | def test_skip_dt_decorator(): | |
120 | """Doctest-skipping decorator should preserve the docstring. |
|
125 | """Doctest-skipping decorator should preserve the docstring. | |
121 | """ |
|
126 | """ | |
122 | # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring! |
|
127 | # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring! | |
123 | check = """A function whose doctest we need to skip. |
|
128 | check = """A function whose doctest we need to skip. | |
124 |
|
129 | |||
125 | >>> 1+1 |
|
130 | >>> 1+1 | |
126 | 3 |
|
131 | 3 | |
127 | """ |
|
132 | """ | |
128 | # Fetch the docstring from doctest_bad after decoration. |
|
133 | # Fetch the docstring from doctest_bad after decoration. | |
129 | val = doctest_bad.__doc__ |
|
134 | val = doctest_bad.__doc__ | |
130 |
|
135 | |||
131 | assert check==val,"doctest_bad docstrings don't match" |
|
136 | assert check==val,"doctest_bad docstrings don't match" | |
132 |
|
137 | |||
133 |
|
138 | |||
134 | def test_skip_dt_decorator2(): |
|
139 | def test_skip_dt_decorator2(): | |
135 | """Doctest-skipping decorator should preserve function signature. |
|
140 | """Doctest-skipping decorator should preserve function signature. | |
136 | """ |
|
141 | """ | |
137 | # Hardcoded correct answer |
|
142 | # Hardcoded correct answer | |
138 | dtargs = (['x', 'y'], None, 'k', (1,)) |
|
143 | dtargs = (['x', 'y'], None, 'k', (1,)) | |
139 | # Introspect out the value |
|
144 | # Introspect out the value | |
140 | dtargsr = getargspec(doctest_bad) |
|
145 | dtargsr = getargspec(doctest_bad) | |
141 | assert dtargsr==dtargs, \ |
|
146 | assert dtargsr==dtargs, \ | |
142 | "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,) |
|
147 | "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,) | |
143 |
|
148 | |||
144 |
|
149 | |||
145 | def doctest_run(): |
|
150 | def doctest_run(): | |
146 | """Test running a trivial script. |
|
151 | """Test running a trivial script. | |
147 |
|
152 | |||
148 | In [13]: run simplevars.py |
|
153 | In [13]: run simplevars.py | |
149 | x is: 1 |
|
154 | x is: 1 | |
150 | """ |
|
155 | """ | |
151 |
|
156 | |||
152 | #@dec.skip_doctest |
|
157 | #@dec.skip_doctest | |
153 | def doctest_runvars(): |
|
158 | def doctest_runvars(): | |
154 | """Test that variables defined in scripts get loaded correcly via %run. |
|
159 | """Test that variables defined in scripts get loaded correcly via %run. | |
155 |
|
160 | |||
156 | In [13]: run simplevars.py |
|
161 | In [13]: run simplevars.py | |
157 | x is: 1 |
|
162 | x is: 1 | |
158 |
|
163 | |||
159 | In [14]: x |
|
164 | In [14]: x | |
160 | Out[14]: 1 |
|
165 | Out[14]: 1 | |
161 | """ |
|
166 | """ | |
162 |
|
167 | |||
163 | def doctest_ivars(): |
|
168 | def doctest_ivars(): | |
164 | """Test that variables defined interactively are picked up. |
|
169 | """Test that variables defined interactively are picked up. | |
165 | In [5]: zz=1 |
|
170 | In [5]: zz=1 | |
166 |
|
171 | |||
167 | In [6]: zz |
|
172 | In [6]: zz | |
168 | Out[6]: 1 |
|
173 | Out[6]: 1 | |
169 | """ |
|
174 | """ | |
170 |
|
175 | |||
171 | @dec.skip_doctest |
|
176 | @dec.skip_doctest | |
172 | def doctest_refs(): |
|
177 | def doctest_refs(): | |
173 | """DocTest reference holding issues when running scripts. |
|
178 | """DocTest reference holding issues when running scripts. | |
174 |
|
179 | |||
175 | In [32]: run show_refs.py |
|
180 | In [32]: run show_refs.py | |
176 | c referrers: [<type 'dict'>] |
|
181 | c referrers: [<type 'dict'>] | |
177 |
|
182 | |||
178 | In [33]: map(type,gc.get_referrers(c)) |
|
183 | In [33]: map(type,gc.get_referrers(c)) | |
179 | Out[33]: [<type 'dict'>] |
|
184 | Out[33]: [<type 'dict'>] | |
180 | """ |
|
185 | """ |
@@ -1,86 +1,87 b'' | |||||
1 | """DEPRECATED - use IPython.testing.util instead. |
|
1 | """DEPRECATED - use IPython.testing.util instead. | |
2 |
|
2 | |||
3 | Utilities for testing code. |
|
3 | Utilities for testing code. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | ############################################################################# |
|
6 | ############################################################################# | |
7 |
|
7 | |||
8 | # This was old testing code we never really used in IPython. The pieces of |
|
8 | # This was old testing code we never really used in IPython. The pieces of | |
9 | # testing machinery from snakeoil that were good have already been merged into |
|
9 | # testing machinery from snakeoil that were good have already been merged into | |
10 | # the nose plugin, so this can be taken away soon. Leave a warning for now, |
|
10 | # the nose plugin, so this can be taken away soon. Leave a warning for now, | |
11 | # we'll remove it in a later release (around 0.10 or so). |
|
11 | # we'll remove it in a later release (around 0.10 or so). | |
|
12 | ||||
12 | from warnings import warn |
|
13 | from warnings import warn | |
13 | warn('This will be removed soon. Use IPython.testing.util instead', |
|
14 | warn('This will be removed soon. Use IPython.testing.util instead', | |
14 | DeprecationWarning) |
|
15 | DeprecationWarning) | |
15 |
|
16 | |||
16 | ############################################################################# |
|
17 | ############################################################################# | |
17 |
|
18 | |||
18 | # Required modules and packages |
|
19 | # Required modules and packages | |
19 |
|
20 | |||
20 | # Standard Python lib |
|
21 | # Standard Python lib | |
21 | import os |
|
22 | import os | |
22 | import sys |
|
23 | import sys | |
23 |
|
24 | |||
24 | # From this project |
|
25 | # From this project | |
25 | from IPython.tools import utils |
|
26 | from IPython.tools import utils | |
26 |
|
27 | |||
27 | # path to our own installation, so we can find source files under this. |
|
28 | # path to our own installation, so we can find source files under this. | |
28 | TEST_PATH = os.path.dirname(os.path.abspath(__file__)) |
|
29 | TEST_PATH = os.path.dirname(os.path.abspath(__file__)) | |
29 |
|
30 | |||
30 | # Global flag, used by vprint |
|
31 | # Global flag, used by vprint | |
31 | VERBOSE = '-v' in sys.argv or '--verbose' in sys.argv |
|
32 | VERBOSE = '-v' in sys.argv or '--verbose' in sys.argv | |
32 |
|
33 | |||
33 | ########################################################################## |
|
34 | ########################################################################## | |
34 | # Code begins |
|
35 | # Code begins | |
35 |
|
36 | |||
36 | # Some utility functions |
|
37 | # Some utility functions | |
37 | def vprint(*args): |
|
38 | def vprint(*args): | |
38 | """Print-like function which relies on a global VERBOSE flag.""" |
|
39 | """Print-like function which relies on a global VERBOSE flag.""" | |
39 | if not VERBOSE: |
|
40 | if not VERBOSE: | |
40 | return |
|
41 | return | |
41 |
|
42 | |||
42 | write = sys.stdout.write |
|
43 | write = sys.stdout.write | |
43 | for item in args: |
|
44 | for item in args: | |
44 | write(str(item)) |
|
45 | write(str(item)) | |
45 | write('\n') |
|
46 | write('\n') | |
46 | sys.stdout.flush() |
|
47 | sys.stdout.flush() | |
47 |
|
48 | |||
48 | def test_path(path): |
|
49 | def test_path(path): | |
49 | """Return a path as a subdir of the test package. |
|
50 | """Return a path as a subdir of the test package. | |
50 |
|
51 | |||
51 | This finds the correct path of the test package on disk, and prepends it |
|
52 | This finds the correct path of the test package on disk, and prepends it | |
52 | to the input path.""" |
|
53 | to the input path.""" | |
53 |
|
54 | |||
54 | return os.path.join(TEST_PATH,path) |
|
55 | return os.path.join(TEST_PATH,path) | |
55 |
|
56 | |||
56 | def fullPath(startPath,files): |
|
57 | def fullPath(startPath,files): | |
57 | """Make full paths for all the listed files, based on startPath. |
|
58 | """Make full paths for all the listed files, based on startPath. | |
58 |
|
59 | |||
59 | Only the base part of startPath is kept, since this routine is typically |
|
60 | Only the base part of startPath is kept, since this routine is typically | |
60 | used with a script's __file__ variable as startPath. The base of startPath |
|
61 | used with a script's __file__ variable as startPath. The base of startPath | |
61 | is then prepended to all the listed files, forming the output list. |
|
62 | is then prepended to all the listed files, forming the output list. | |
62 |
|
63 | |||
63 | :Parameters: |
|
64 | :Parameters: | |
64 | startPath : string |
|
65 | startPath : string | |
65 | Initial path to use as the base for the results. This path is split |
|
66 | Initial path to use as the base for the results. This path is split | |
66 | using os.path.split() and only its first component is kept. |
|
67 | using os.path.split() and only its first component is kept. | |
67 |
|
68 | |||
68 | files : string or list |
|
69 | files : string or list | |
69 | One or more files. |
|
70 | One or more files. | |
70 |
|
71 | |||
71 | :Examples: |
|
72 | :Examples: | |
72 |
|
73 | |||
73 | >>> fullPath('/foo/bar.py',['a.txt','b.txt']) |
|
74 | >>> fullPath('/foo/bar.py',['a.txt','b.txt']) | |
74 | ['/foo/a.txt', '/foo/b.txt'] |
|
75 | ['/foo/a.txt', '/foo/b.txt'] | |
75 |
|
76 | |||
76 | >>> fullPath('/foo',['a.txt','b.txt']) |
|
77 | >>> fullPath('/foo',['a.txt','b.txt']) | |
77 | ['/a.txt', '/b.txt'] |
|
78 | ['/a.txt', '/b.txt'] | |
78 |
|
79 | |||
79 | If a single file is given, the output is still a list: |
|
80 | If a single file is given, the output is still a list: | |
80 | >>> fullPath('/foo','a.txt') |
|
81 | >>> fullPath('/foo','a.txt') | |
81 | ['/a.txt'] |
|
82 | ['/a.txt'] | |
82 | """ |
|
83 | """ | |
83 |
|
84 | |||
84 | files = utils.list_strings(files) |
|
85 | files = utils.list_strings(files) | |
85 | base = os.path.split(startPath)[0] |
|
86 | base = os.path.split(startPath)[0] | |
86 | return [ os.path.join(base,f) for f in files ] |
|
87 | return [ os.path.join(base,f) for f in files ] |
@@ -1,1061 +1,1062 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | ultraTB.py -- Spice up your tracebacks! |
|
3 | ultraTB.py -- Spice up your tracebacks! | |
4 |
|
4 | |||
5 | * ColorTB |
|
5 | * ColorTB | |
6 | I've always found it a bit hard to visually parse tracebacks in Python. The |
|
6 | I've always found it a bit hard to visually parse tracebacks in Python. The | |
7 | ColorTB class is a solution to that problem. It colors the different parts of a |
|
7 | ColorTB class is a solution to that problem. It colors the different parts of a | |
8 | traceback in a manner similar to what you would expect from a syntax-highlighting |
|
8 | traceback in a manner similar to what you would expect from a syntax-highlighting | |
9 | text editor. |
|
9 | text editor. | |
10 |
|
10 | |||
11 | Installation instructions for ColorTB: |
|
11 | Installation instructions for ColorTB: | |
12 | import sys,ultraTB |
|
12 | import sys,ultraTB | |
13 | sys.excepthook = ultraTB.ColorTB() |
|
13 | sys.excepthook = ultraTB.ColorTB() | |
14 |
|
14 | |||
15 | * VerboseTB |
|
15 | * VerboseTB | |
16 | I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds |
|
16 | I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds | |
17 | of useful info when a traceback occurs. Ping originally had it spit out HTML |
|
17 | of useful info when a traceback occurs. Ping originally had it spit out HTML | |
18 | and intended it for CGI programmers, but why should they have all the fun? I |
|
18 | and intended it for CGI programmers, but why should they have all the fun? I | |
19 | altered it to spit out colored text to the terminal. It's a bit overwhelming, |
|
19 | altered it to spit out colored text to the terminal. It's a bit overwhelming, | |
20 | but kind of neat, and maybe useful for long-running programs that you believe |
|
20 | but kind of neat, and maybe useful for long-running programs that you believe | |
21 | are bug-free. If a crash *does* occur in that type of program you want details. |
|
21 | are bug-free. If a crash *does* occur in that type of program you want details. | |
22 | Give it a shot--you'll love it or you'll hate it. |
|
22 | Give it a shot--you'll love it or you'll hate it. | |
23 |
|
23 | |||
24 | Note: |
|
24 | Note: | |
25 |
|
25 | |||
26 | The Verbose mode prints the variables currently visible where the exception |
|
26 | The Verbose mode prints the variables currently visible where the exception | |
27 | happened (shortening their strings if too long). This can potentially be |
|
27 | happened (shortening their strings if too long). This can potentially be | |
28 | very slow, if you happen to have a huge data structure whose string |
|
28 | very slow, if you happen to have a huge data structure whose string | |
29 | representation is complex to compute. Your computer may appear to freeze for |
|
29 | representation is complex to compute. Your computer may appear to freeze for | |
30 | a while with cpu usage at 100%. If this occurs, you can cancel the traceback |
|
30 | a while with cpu usage at 100%. If this occurs, you can cancel the traceback | |
31 | with Ctrl-C (maybe hitting it more than once). |
|
31 | with Ctrl-C (maybe hitting it more than once). | |
32 |
|
32 | |||
33 | If you encounter this kind of situation often, you may want to use the |
|
33 | If you encounter this kind of situation often, you may want to use the | |
34 | Verbose_novars mode instead of the regular Verbose, which avoids formatting |
|
34 | Verbose_novars mode instead of the regular Verbose, which avoids formatting | |
35 | variables (but otherwise includes the information and context given by |
|
35 | variables (but otherwise includes the information and context given by | |
36 | Verbose). |
|
36 | Verbose). | |
37 |
|
37 | |||
38 |
|
38 | |||
39 | Installation instructions for ColorTB: |
|
39 | Installation instructions for ColorTB: | |
40 | import sys,ultraTB |
|
40 | import sys,ultraTB | |
41 | sys.excepthook = ultraTB.VerboseTB() |
|
41 | sys.excepthook = ultraTB.VerboseTB() | |
42 |
|
42 | |||
43 | Note: Much of the code in this module was lifted verbatim from the standard |
|
43 | Note: Much of the code in this module was lifted verbatim from the standard | |
44 | library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. |
|
44 | library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. | |
45 |
|
45 | |||
46 | * Color schemes |
|
46 | * Color schemes | |
47 | The colors are defined in the class TBTools through the use of the |
|
47 | The colors are defined in the class TBTools through the use of the | |
48 | ColorSchemeTable class. Currently the following exist: |
|
48 | ColorSchemeTable class. Currently the following exist: | |
49 |
|
49 | |||
50 | - NoColor: allows all of this module to be used in any terminal (the color |
|
50 | - NoColor: allows all of this module to be used in any terminal (the color | |
51 | escapes are just dummy blank strings). |
|
51 | escapes are just dummy blank strings). | |
52 |
|
52 | |||
53 | - Linux: is meant to look good in a terminal like the Linux console (black |
|
53 | - Linux: is meant to look good in a terminal like the Linux console (black | |
54 | or very dark background). |
|
54 | or very dark background). | |
55 |
|
55 | |||
56 | - LightBG: similar to Linux but swaps dark/light colors to be more readable |
|
56 | - LightBG: similar to Linux but swaps dark/light colors to be more readable | |
57 | in light background terminals. |
|
57 | in light background terminals. | |
58 |
|
58 | |||
59 | You can implement other color schemes easily, the syntax is fairly |
|
59 | You can implement other color schemes easily, the syntax is fairly | |
60 | self-explanatory. Please send back new schemes you develop to the author for |
|
60 | self-explanatory. Please send back new schemes you develop to the author for | |
61 | possible inclusion in future releases. |
|
61 | possible inclusion in future releases. | |
62 |
|
62 | |||
63 | $Id: ultraTB.py 2908 2007-12-30 21:07:46Z vivainio $""" |
|
63 | $Id: ultraTB.py 2908 2007-12-30 21:07:46Z vivainio $""" | |
64 |
|
64 | |||
65 | #***************************************************************************** |
|
65 | #***************************************************************************** | |
66 | # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu> |
|
66 | # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu> | |
67 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
67 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |
68 | # |
|
68 | # | |
69 | # Distributed under the terms of the BSD License. The full license is in |
|
69 | # Distributed under the terms of the BSD License. The full license is in | |
70 | # the file COPYING, distributed as part of this software. |
|
70 | # the file COPYING, distributed as part of this software. | |
71 | #***************************************************************************** |
|
71 | #***************************************************************************** | |
72 |
|
72 | |||
73 | from IPython import Release |
|
73 | from IPython import Release | |
74 | __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+ |
|
74 | __author__ = '%s <%s>\n%s <%s>' % (Release.authors['Nathan']+ | |
75 | Release.authors['Fernando']) |
|
75 | Release.authors['Fernando']) | |
76 | __license__ = Release.license |
|
76 | __license__ = Release.license | |
77 |
|
77 | |||
78 | # Required modules |
|
78 | # Required modules | |
79 | import inspect |
|
79 | import inspect | |
80 | import keyword |
|
80 | import keyword | |
81 | import linecache |
|
81 | import linecache | |
82 | import os |
|
82 | import os | |
83 | import pydoc |
|
83 | import pydoc | |
84 | import re |
|
84 | import re | |
85 | import string |
|
85 | import string | |
86 | import sys |
|
86 | import sys | |
87 | import time |
|
87 | import time | |
88 | import tokenize |
|
88 | import tokenize | |
89 | import traceback |
|
89 | import traceback | |
90 | import types |
|
90 | import types | |
91 |
|
91 | |||
92 | # For purposes of monkeypatching inspect to fix a bug in it. |
|
92 | # For purposes of monkeypatching inspect to fix a bug in it. | |
93 | from inspect import getsourcefile, getfile, getmodule,\ |
|
93 | from inspect import getsourcefile, getfile, getmodule,\ | |
94 | ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode |
|
94 | ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode | |
95 |
|
95 | |||
96 |
|
96 | |||
97 | # IPython's own modules |
|
97 | # IPython's own modules | |
98 | # Modified pdb which doesn't damage IPython's readline handling |
|
98 | # Modified pdb which doesn't damage IPython's readline handling | |
99 | from IPython import Debugger, PyColorize |
|
99 | from IPython import Debugger, PyColorize | |
100 | from IPython.ipstruct import Struct |
|
100 | from IPython.ipstruct import Struct | |
101 | from IPython.excolors import ExceptionColors |
|
101 | from IPython.excolors import ExceptionColors | |
102 | from IPython.genutils import Term,uniq_stable,error,info |
|
102 | from IPython.genutils import Term,uniq_stable,error,info | |
103 |
|
103 | |||
104 | # Globals |
|
104 | # Globals | |
105 | # amount of space to put line numbers before verbose tracebacks |
|
105 | # amount of space to put line numbers before verbose tracebacks | |
106 | INDENT_SIZE = 8 |
|
106 | INDENT_SIZE = 8 | |
107 |
|
107 | |||
108 | # Default color scheme. This is used, for example, by the traceback |
|
108 | # Default color scheme. This is used, for example, by the traceback | |
109 | # formatter. When running in an actual IPython instance, the user's rc.colors |
|
109 | # formatter. When running in an actual IPython instance, the user's rc.colors | |
110 | # value is used, but havinga module global makes this functionality available |
|
110 | # value is used, but havinga module global makes this functionality available | |
111 | # to users of ultraTB who are NOT running inside ipython. |
|
111 | # to users of ultraTB who are NOT running inside ipython. | |
112 | DEFAULT_SCHEME = 'NoColor' |
|
112 | DEFAULT_SCHEME = 'NoColor' | |
113 |
|
113 | |||
114 | #--------------------------------------------------------------------------- |
|
114 | #--------------------------------------------------------------------------- | |
115 | # Code begins |
|
115 | # Code begins | |
116 |
|
116 | |||
117 | # Utility functions |
|
117 | # Utility functions | |
118 | def inspect_error(): |
|
118 | def inspect_error(): | |
119 | """Print a message about internal inspect errors. |
|
119 | """Print a message about internal inspect errors. | |
120 |
|
120 | |||
121 | These are unfortunately quite common.""" |
|
121 | These are unfortunately quite common.""" | |
122 |
|
122 | |||
123 | error('Internal Python error in the inspect module.\n' |
|
123 | error('Internal Python error in the inspect module.\n' | |
124 | 'Below is the traceback from this internal error.\n') |
|
124 | 'Below is the traceback from this internal error.\n') | |
125 |
|
125 | |||
126 |
|
126 | |||
127 | def findsource(object): |
|
127 | def findsource(object): | |
128 | """Return the entire source file and starting line number for an object. |
|
128 | """Return the entire source file and starting line number for an object. | |
129 |
|
129 | |||
130 | The argument may be a module, class, method, function, traceback, frame, |
|
130 | The argument may be a module, class, method, function, traceback, frame, | |
131 | or code object. The source code is returned as a list of all the lines |
|
131 | or code object. The source code is returned as a list of all the lines | |
132 | in the file and the line number indexes a line in that list. An IOError |
|
132 | in the file and the line number indexes a line in that list. An IOError | |
133 | is raised if the source code cannot be retrieved. |
|
133 | is raised if the source code cannot be retrieved. | |
134 |
|
134 | |||
135 | FIXED version with which we monkeypatch the stdlib to work around a bug.""" |
|
135 | FIXED version with which we monkeypatch the stdlib to work around a bug.""" | |
136 |
|
136 | |||
137 | file = getsourcefile(object) or getfile(object) |
|
137 | file = getsourcefile(object) or getfile(object) | |
138 | # If the object is a frame, then trying to get the globals dict from its |
|
138 | # If the object is a frame, then trying to get the globals dict from its | |
139 | # module won't work. Instead, the frame object itself has the globals |
|
139 | # module won't work. Instead, the frame object itself has the globals | |
140 | # dictionary. |
|
140 | # dictionary. | |
141 | globals_dict = None |
|
141 | globals_dict = None | |
142 | if inspect.isframe(object): |
|
142 | if inspect.isframe(object): | |
143 | # XXX: can this ever be false? |
|
143 | # XXX: can this ever be false? | |
144 | globals_dict = object.f_globals |
|
144 | globals_dict = object.f_globals | |
145 | else: |
|
145 | else: | |
146 | module = getmodule(object, file) |
|
146 | module = getmodule(object, file) | |
147 | if module: |
|
147 | if module: | |
148 | globals_dict = module.__dict__ |
|
148 | globals_dict = module.__dict__ | |
149 | lines = linecache.getlines(file, globals_dict) |
|
149 | lines = linecache.getlines(file, globals_dict) | |
150 | if not lines: |
|
150 | if not lines: | |
151 | raise IOError('could not get source code') |
|
151 | raise IOError('could not get source code') | |
152 |
|
152 | |||
153 | if ismodule(object): |
|
153 | if ismodule(object): | |
154 | return lines, 0 |
|
154 | return lines, 0 | |
155 |
|
155 | |||
156 | if isclass(object): |
|
156 | if isclass(object): | |
157 | name = object.__name__ |
|
157 | name = object.__name__ | |
158 | pat = re.compile(r'^(\s*)class\s*' + name + r'\b') |
|
158 | pat = re.compile(r'^(\s*)class\s*' + name + r'\b') | |
159 | # make some effort to find the best matching class definition: |
|
159 | # make some effort to find the best matching class definition: | |
160 | # use the one with the least indentation, which is the one |
|
160 | # use the one with the least indentation, which is the one | |
161 | # that's most probably not inside a function definition. |
|
161 | # that's most probably not inside a function definition. | |
162 | candidates = [] |
|
162 | candidates = [] | |
163 | for i in range(len(lines)): |
|
163 | for i in range(len(lines)): | |
164 | match = pat.match(lines[i]) |
|
164 | match = pat.match(lines[i]) | |
165 | if match: |
|
165 | if match: | |
166 | # if it's at toplevel, it's already the best one |
|
166 | # if it's at toplevel, it's already the best one | |
167 | if lines[i][0] == 'c': |
|
167 | if lines[i][0] == 'c': | |
168 | return lines, i |
|
168 | return lines, i | |
169 | # else add whitespace to candidate list |
|
169 | # else add whitespace to candidate list | |
170 | candidates.append((match.group(1), i)) |
|
170 | candidates.append((match.group(1), i)) | |
171 | if candidates: |
|
171 | if candidates: | |
172 | # this will sort by whitespace, and by line number, |
|
172 | # this will sort by whitespace, and by line number, | |
173 | # less whitespace first |
|
173 | # less whitespace first | |
174 | candidates.sort() |
|
174 | candidates.sort() | |
175 | return lines, candidates[0][1] |
|
175 | return lines, candidates[0][1] | |
176 | else: |
|
176 | else: | |
177 | raise IOError('could not find class definition') |
|
177 | raise IOError('could not find class definition') | |
178 |
|
178 | |||
179 | if ismethod(object): |
|
179 | if ismethod(object): | |
180 | object = object.im_func |
|
180 | object = object.im_func | |
181 | if isfunction(object): |
|
181 | if isfunction(object): | |
182 | object = object.func_code |
|
182 | object = object.func_code | |
183 | if istraceback(object): |
|
183 | if istraceback(object): | |
184 | object = object.tb_frame |
|
184 | object = object.tb_frame | |
185 | if isframe(object): |
|
185 | if isframe(object): | |
186 | object = object.f_code |
|
186 | object = object.f_code | |
187 | if iscode(object): |
|
187 | if iscode(object): | |
188 | if not hasattr(object, 'co_firstlineno'): |
|
188 | if not hasattr(object, 'co_firstlineno'): | |
189 | raise IOError('could not find function definition') |
|
189 | raise IOError('could not find function definition') | |
190 | pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)') |
|
190 | pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)') | |
191 | pmatch = pat.match |
|
191 | pmatch = pat.match | |
192 | # fperez - fix: sometimes, co_firstlineno can give a number larger than |
|
192 | # fperez - fix: sometimes, co_firstlineno can give a number larger than | |
193 | # the length of lines, which causes an error. Safeguard against that. |
|
193 | # the length of lines, which causes an error. Safeguard against that. | |
194 | lnum = min(object.co_firstlineno,len(lines))-1 |
|
194 | lnum = min(object.co_firstlineno,len(lines))-1 | |
195 | while lnum > 0: |
|
195 | while lnum > 0: | |
196 | if pmatch(lines[lnum]): break |
|
196 | if pmatch(lines[lnum]): break | |
197 | lnum -= 1 |
|
197 | lnum -= 1 | |
198 |
|
198 | |||
199 | return lines, lnum |
|
199 | return lines, lnum | |
200 | raise IOError('could not find code object') |
|
200 | raise IOError('could not find code object') | |
201 |
|
201 | |||
202 | # Monkeypatch inspect to apply our bugfix. This code only works with py25 |
|
202 | # Monkeypatch inspect to apply our bugfix. This code only works with py25 | |
203 | if sys.version_info[:2] >= (2,5): |
|
203 | if sys.version_info[:2] >= (2,5): | |
204 | inspect.findsource = findsource |
|
204 | inspect.findsource = findsource | |
205 |
|
205 | |||
206 | def fix_frame_records_filenames(records): |
|
206 | def fix_frame_records_filenames(records): | |
207 | """Try to fix the filenames in each record from inspect.getinnerframes(). |
|
207 | """Try to fix the filenames in each record from inspect.getinnerframes(). | |
208 |
|
208 | |||
209 | Particularly, modules loaded from within zip files have useless filenames |
|
209 | Particularly, modules loaded from within zip files have useless filenames | |
210 | attached to their code object, and inspect.getinnerframes() just uses it. |
|
210 | attached to their code object, and inspect.getinnerframes() just uses it. | |
211 | """ |
|
211 | """ | |
212 | fixed_records = [] |
|
212 | fixed_records = [] | |
213 | for frame, filename, line_no, func_name, lines, index in records: |
|
213 | for frame, filename, line_no, func_name, lines, index in records: | |
214 | # Look inside the frame's globals dictionary for __file__, which should |
|
214 | # Look inside the frame's globals dictionary for __file__, which should | |
215 | # be better. |
|
215 | # be better. | |
216 | better_fn = frame.f_globals.get('__file__', None) |
|
216 | better_fn = frame.f_globals.get('__file__', None) | |
217 | if isinstance(better_fn, str): |
|
217 | if isinstance(better_fn, str): | |
218 | # Check the type just in case someone did something weird with |
|
218 | # Check the type just in case someone did something weird with | |
219 | # __file__. It might also be None if the error occurred during |
|
219 | # __file__. It might also be None if the error occurred during | |
220 | # import. |
|
220 | # import. | |
221 | filename = better_fn |
|
221 | filename = better_fn | |
222 | fixed_records.append((frame, filename, line_no, func_name, lines, index)) |
|
222 | fixed_records.append((frame, filename, line_no, func_name, lines, index)) | |
223 | return fixed_records |
|
223 | return fixed_records | |
224 |
|
224 | |||
225 |
|
225 | |||
226 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): |
|
226 | def _fixed_getinnerframes(etb, context=1,tb_offset=0): | |
227 | import linecache |
|
227 | import linecache | |
228 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 |
|
228 | LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 | |
229 |
|
229 | |||
230 | records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) |
|
230 | records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) | |
231 |
|
231 | |||
232 | # If the error is at the console, don't build any context, since it would |
|
232 | # If the error is at the console, don't build any context, since it would | |
233 | # otherwise produce 5 blank lines printed out (there is no file at the |
|
233 | # otherwise produce 5 blank lines printed out (there is no file at the | |
234 | # console) |
|
234 | # console) | |
235 | rec_check = records[tb_offset:] |
|
235 | rec_check = records[tb_offset:] | |
236 | try: |
|
236 | try: | |
237 | rname = rec_check[0][1] |
|
237 | rname = rec_check[0][1] | |
238 | if rname == '<ipython console>' or rname.endswith('<string>'): |
|
238 | if rname == '<ipython console>' or rname.endswith('<string>'): | |
239 | return rec_check |
|
239 | return rec_check | |
240 | except IndexError: |
|
240 | except IndexError: | |
241 | pass |
|
241 | pass | |
242 |
|
242 | |||
243 | aux = traceback.extract_tb(etb) |
|
243 | aux = traceback.extract_tb(etb) | |
244 | assert len(records) == len(aux) |
|
244 | assert len(records) == len(aux) | |
245 | for i, (file, lnum, _, _) in zip(range(len(records)), aux): |
|
245 | for i, (file, lnum, _, _) in zip(range(len(records)), aux): | |
246 | maybeStart = lnum-1 - context//2 |
|
246 | maybeStart = lnum-1 - context//2 | |
247 | start = max(maybeStart, 0) |
|
247 | start = max(maybeStart, 0) | |
248 | end = start + context |
|
248 | end = start + context | |
249 | lines = linecache.getlines(file)[start:end] |
|
249 | lines = linecache.getlines(file)[start:end] | |
250 | # pad with empty lines if necessary |
|
250 | # pad with empty lines if necessary | |
251 | if maybeStart < 0: |
|
251 | if maybeStart < 0: | |
252 | lines = (['\n'] * -maybeStart) + lines |
|
252 | lines = (['\n'] * -maybeStart) + lines | |
253 | if len(lines) < context: |
|
253 | if len(lines) < context: | |
254 | lines += ['\n'] * (context - len(lines)) |
|
254 | lines += ['\n'] * (context - len(lines)) | |
255 | buf = list(records[i]) |
|
255 | buf = list(records[i]) | |
256 | buf[LNUM_POS] = lnum |
|
256 | buf[LNUM_POS] = lnum | |
257 | buf[INDEX_POS] = lnum - 1 - start |
|
257 | buf[INDEX_POS] = lnum - 1 - start | |
258 | buf[LINES_POS] = lines |
|
258 | buf[LINES_POS] = lines | |
259 | records[i] = tuple(buf) |
|
259 | records[i] = tuple(buf) | |
260 | return records[tb_offset:] |
|
260 | return records[tb_offset:] | |
261 |
|
261 | |||
262 | # Helper function -- largely belongs to VerboseTB, but we need the same |
|
262 | # Helper function -- largely belongs to VerboseTB, but we need the same | |
263 | # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they |
|
263 | # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they | |
264 | # can be recognized properly by ipython.el's py-traceback-line-re |
|
264 | # can be recognized properly by ipython.el's py-traceback-line-re | |
265 | # (SyntaxErrors have to be treated specially because they have no traceback) |
|
265 | # (SyntaxErrors have to be treated specially because they have no traceback) | |
266 |
|
266 | |||
267 | _parser = PyColorize.Parser() |
|
267 | _parser = PyColorize.Parser() | |
268 |
|
268 | |||
269 | def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None): |
|
269 | def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None): | |
270 | numbers_width = INDENT_SIZE - 1 |
|
270 | numbers_width = INDENT_SIZE - 1 | |
271 | res = [] |
|
271 | res = [] | |
272 | i = lnum - index |
|
272 | i = lnum - index | |
273 |
|
273 | |||
274 | # This lets us get fully syntax-highlighted tracebacks. |
|
274 | # This lets us get fully syntax-highlighted tracebacks. | |
275 | if scheme is None: |
|
275 | if scheme is None: | |
276 | try: |
|
276 | try: | |
277 | scheme = __IPYTHON__.rc.colors |
|
277 | scheme = __IPYTHON__.rc.colors | |
278 | except: |
|
278 | except: | |
279 | scheme = DEFAULT_SCHEME |
|
279 | scheme = DEFAULT_SCHEME | |
280 | _line_format = _parser.format2 |
|
280 | _line_format = _parser.format2 | |
281 |
|
281 | |||
282 | for line in lines: |
|
282 | for line in lines: | |
283 | new_line, err = _line_format(line,'str',scheme) |
|
283 | new_line, err = _line_format(line,'str',scheme) | |
284 | if not err: line = new_line |
|
284 | if not err: line = new_line | |
285 |
|
285 | |||
286 | if i == lnum: |
|
286 | if i == lnum: | |
287 | # This is the line with the error |
|
287 | # This is the line with the error | |
288 | pad = numbers_width - len(str(i)) |
|
288 | pad = numbers_width - len(str(i)) | |
289 | if pad >= 3: |
|
289 | if pad >= 3: | |
290 | marker = '-'*(pad-3) + '-> ' |
|
290 | marker = '-'*(pad-3) + '-> ' | |
291 | elif pad == 2: |
|
291 | elif pad == 2: | |
292 | marker = '> ' |
|
292 | marker = '> ' | |
293 | elif pad == 1: |
|
293 | elif pad == 1: | |
294 | marker = '>' |
|
294 | marker = '>' | |
295 | else: |
|
295 | else: | |
296 | marker = '' |
|
296 | marker = '' | |
297 | num = marker + str(i) |
|
297 | num = marker + str(i) | |
298 | line = '%s%s%s %s%s' %(Colors.linenoEm, num, |
|
298 | line = '%s%s%s %s%s' %(Colors.linenoEm, num, | |
299 | Colors.line, line, Colors.Normal) |
|
299 | Colors.line, line, Colors.Normal) | |
300 | else: |
|
300 | else: | |
301 | num = '%*s' % (numbers_width,i) |
|
301 | num = '%*s' % (numbers_width,i) | |
302 | line = '%s%s%s %s' %(Colors.lineno, num, |
|
302 | line = '%s%s%s %s' %(Colors.lineno, num, | |
303 | Colors.Normal, line) |
|
303 | Colors.Normal, line) | |
304 |
|
304 | |||
305 | res.append(line) |
|
305 | res.append(line) | |
306 | if lvals and i == lnum: |
|
306 | if lvals and i == lnum: | |
307 | res.append(lvals + '\n') |
|
307 | res.append(lvals + '\n') | |
308 | i = i + 1 |
|
308 | i = i + 1 | |
309 | return res |
|
309 | return res | |
310 |
|
310 | |||
311 |
|
311 | |||
312 | #--------------------------------------------------------------------------- |
|
312 | #--------------------------------------------------------------------------- | |
313 | # Module classes |
|
313 | # Module classes | |
314 | class TBTools: |
|
314 | class TBTools: | |
315 | """Basic tools used by all traceback printer classes.""" |
|
315 | """Basic tools used by all traceback printer classes.""" | |
316 |
|
316 | |||
317 | def __init__(self,color_scheme = 'NoColor',call_pdb=False): |
|
317 | def __init__(self,color_scheme = 'NoColor',call_pdb=False): | |
318 | # Whether to call the interactive pdb debugger after printing |
|
318 | # Whether to call the interactive pdb debugger after printing | |
319 | # tracebacks or not |
|
319 | # tracebacks or not | |
320 | self.call_pdb = call_pdb |
|
320 | self.call_pdb = call_pdb | |
321 |
|
321 | |||
322 | # Create color table |
|
322 | # Create color table | |
323 | self.color_scheme_table = ExceptionColors |
|
323 | self.color_scheme_table = ExceptionColors | |
324 |
|
324 | |||
325 | self.set_colors(color_scheme) |
|
325 | self.set_colors(color_scheme) | |
326 | self.old_scheme = color_scheme # save initial value for toggles |
|
326 | self.old_scheme = color_scheme # save initial value for toggles | |
327 |
|
327 | |||
328 | if call_pdb: |
|
328 | if call_pdb: | |
329 | self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name) |
|
329 | self.pdb = Debugger.Pdb(self.color_scheme_table.active_scheme_name) | |
330 | else: |
|
330 | else: | |
331 | self.pdb = None |
|
331 | self.pdb = None | |
332 |
|
332 | |||
333 | def set_colors(self,*args,**kw): |
|
333 | def set_colors(self,*args,**kw): | |
334 | """Shorthand access to the color table scheme selector method.""" |
|
334 | """Shorthand access to the color table scheme selector method.""" | |
335 |
|
335 | |||
336 | # Set own color table |
|
336 | # Set own color table | |
337 | self.color_scheme_table.set_active_scheme(*args,**kw) |
|
337 | self.color_scheme_table.set_active_scheme(*args,**kw) | |
338 | # for convenience, set Colors to the active scheme |
|
338 | # for convenience, set Colors to the active scheme | |
339 | self.Colors = self.color_scheme_table.active_colors |
|
339 | self.Colors = self.color_scheme_table.active_colors | |
340 | # Also set colors of debugger |
|
340 | # Also set colors of debugger | |
341 | if hasattr(self,'pdb') and self.pdb is not None: |
|
341 | if hasattr(self,'pdb') and self.pdb is not None: | |
342 | self.pdb.set_colors(*args,**kw) |
|
342 | self.pdb.set_colors(*args,**kw) | |
343 |
|
343 | |||
344 | def color_toggle(self): |
|
344 | def color_toggle(self): | |
345 | """Toggle between the currently active color scheme and NoColor.""" |
|
345 | """Toggle between the currently active color scheme and NoColor.""" | |
346 |
|
346 | |||
347 | if self.color_scheme_table.active_scheme_name == 'NoColor': |
|
347 | if self.color_scheme_table.active_scheme_name == 'NoColor': | |
348 | self.color_scheme_table.set_active_scheme(self.old_scheme) |
|
348 | self.color_scheme_table.set_active_scheme(self.old_scheme) | |
349 | self.Colors = self.color_scheme_table.active_colors |
|
349 | self.Colors = self.color_scheme_table.active_colors | |
350 | else: |
|
350 | else: | |
351 | self.old_scheme = self.color_scheme_table.active_scheme_name |
|
351 | self.old_scheme = self.color_scheme_table.active_scheme_name | |
352 | self.color_scheme_table.set_active_scheme('NoColor') |
|
352 | self.color_scheme_table.set_active_scheme('NoColor') | |
353 | self.Colors = self.color_scheme_table.active_colors |
|
353 | self.Colors = self.color_scheme_table.active_colors | |
354 |
|
354 | |||
355 | #--------------------------------------------------------------------------- |
|
355 | #--------------------------------------------------------------------------- | |
356 | class ListTB(TBTools): |
|
356 | class ListTB(TBTools): | |
357 | """Print traceback information from a traceback list, with optional color. |
|
357 | """Print traceback information from a traceback list, with optional color. | |
358 |
|
358 | |||
359 | Calling: requires 3 arguments: |
|
359 | Calling: requires 3 arguments: | |
360 | (etype, evalue, elist) |
|
360 | (etype, evalue, elist) | |
361 | as would be obtained by: |
|
361 | as would be obtained by: | |
362 | etype, evalue, tb = sys.exc_info() |
|
362 | etype, evalue, tb = sys.exc_info() | |
363 | if tb: |
|
363 | if tb: | |
364 | elist = traceback.extract_tb(tb) |
|
364 | elist = traceback.extract_tb(tb) | |
365 | else: |
|
365 | else: | |
366 | elist = None |
|
366 | elist = None | |
367 |
|
367 | |||
368 | It can thus be used by programs which need to process the traceback before |
|
368 | It can thus be used by programs which need to process the traceback before | |
369 | printing (such as console replacements based on the code module from the |
|
369 | printing (such as console replacements based on the code module from the | |
370 | standard library). |
|
370 | standard library). | |
371 |
|
371 | |||
372 | Because they are meant to be called without a full traceback (only a |
|
372 | Because they are meant to be called without a full traceback (only a | |
373 | list), instances of this class can't call the interactive pdb debugger.""" |
|
373 | list), instances of this class can't call the interactive pdb debugger.""" | |
374 |
|
374 | |||
375 | def __init__(self,color_scheme = 'NoColor'): |
|
375 | def __init__(self,color_scheme = 'NoColor'): | |
376 | TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0) |
|
376 | TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0) | |
377 |
|
377 | |||
378 | def __call__(self, etype, value, elist): |
|
378 | def __call__(self, etype, value, elist): | |
379 | Term.cout.flush() |
|
379 | Term.cout.flush() | |
380 | print >> Term.cerr, self.text(etype,value,elist) |
|
380 | print >> Term.cerr, self.text(etype,value,elist) | |
381 | Term.cerr.flush() |
|
381 | Term.cerr.flush() | |
382 |
|
382 | |||
383 | def text(self,etype, value, elist,context=5): |
|
383 | def text(self,etype, value, elist,context=5): | |
384 | """Return a color formatted string with the traceback info.""" |
|
384 | """Return a color formatted string with the traceback info.""" | |
385 |
|
385 | |||
386 | Colors = self.Colors |
|
386 | Colors = self.Colors | |
387 | out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)] |
|
387 | out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)] | |
388 | if elist: |
|
388 | if elist: | |
389 | out_string.append('Traceback %s(most recent call last)%s:' % \ |
|
389 | out_string.append('Traceback %s(most recent call last)%s:' % \ | |
390 | (Colors.normalEm, Colors.Normal) + '\n') |
|
390 | (Colors.normalEm, Colors.Normal) + '\n') | |
391 | out_string.extend(self._format_list(elist)) |
|
391 | out_string.extend(self._format_list(elist)) | |
392 | lines = self._format_exception_only(etype, value) |
|
392 | lines = self._format_exception_only(etype, value) | |
393 | for line in lines[:-1]: |
|
393 | for line in lines[:-1]: | |
394 | out_string.append(" "+line) |
|
394 | out_string.append(" "+line) | |
395 | out_string.append(lines[-1]) |
|
395 | out_string.append(lines[-1]) | |
396 | return ''.join(out_string) |
|
396 | return ''.join(out_string) | |
397 |
|
397 | |||
398 | def _format_list(self, extracted_list): |
|
398 | def _format_list(self, extracted_list): | |
399 | """Format a list of traceback entry tuples for printing. |
|
399 | """Format a list of traceback entry tuples for printing. | |
400 |
|
400 | |||
401 | Given a list of tuples as returned by extract_tb() or |
|
401 | Given a list of tuples as returned by extract_tb() or | |
402 | extract_stack(), return a list of strings ready for printing. |
|
402 | extract_stack(), return a list of strings ready for printing. | |
403 | Each string in the resulting list corresponds to the item with the |
|
403 | Each string in the resulting list corresponds to the item with the | |
404 | same index in the argument list. Each string ends in a newline; |
|
404 | same index in the argument list. Each string ends in a newline; | |
405 | the strings may contain internal newlines as well, for those items |
|
405 | the strings may contain internal newlines as well, for those items | |
406 | whose source text line is not None. |
|
406 | whose source text line is not None. | |
407 |
|
407 | |||
408 | Lifted almost verbatim from traceback.py |
|
408 | Lifted almost verbatim from traceback.py | |
409 | """ |
|
409 | """ | |
410 |
|
410 | |||
411 | Colors = self.Colors |
|
411 | Colors = self.Colors | |
412 | list = [] |
|
412 | list = [] | |
413 | for filename, lineno, name, line in extracted_list[:-1]: |
|
413 | for filename, lineno, name, line in extracted_list[:-1]: | |
414 | item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \ |
|
414 | item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \ | |
415 | (Colors.filename, filename, Colors.Normal, |
|
415 | (Colors.filename, filename, Colors.Normal, | |
416 | Colors.lineno, lineno, Colors.Normal, |
|
416 | Colors.lineno, lineno, Colors.Normal, | |
417 | Colors.name, name, Colors.Normal) |
|
417 | Colors.name, name, Colors.Normal) | |
418 | if line: |
|
418 | if line: | |
419 | item = item + ' %s\n' % line.strip() |
|
419 | item = item + ' %s\n' % line.strip() | |
420 | list.append(item) |
|
420 | list.append(item) | |
421 | # Emphasize the last entry |
|
421 | # Emphasize the last entry | |
422 | filename, lineno, name, line = extracted_list[-1] |
|
422 | filename, lineno, name, line = extracted_list[-1] | |
423 | item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \ |
|
423 | item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \ | |
424 | (Colors.normalEm, |
|
424 | (Colors.normalEm, | |
425 | Colors.filenameEm, filename, Colors.normalEm, |
|
425 | Colors.filenameEm, filename, Colors.normalEm, | |
426 | Colors.linenoEm, lineno, Colors.normalEm, |
|
426 | Colors.linenoEm, lineno, Colors.normalEm, | |
427 | Colors.nameEm, name, Colors.normalEm, |
|
427 | Colors.nameEm, name, Colors.normalEm, | |
428 | Colors.Normal) |
|
428 | Colors.Normal) | |
429 | if line: |
|
429 | if line: | |
430 | item = item + '%s %s%s\n' % (Colors.line, line.strip(), |
|
430 | item = item + '%s %s%s\n' % (Colors.line, line.strip(), | |
431 | Colors.Normal) |
|
431 | Colors.Normal) | |
432 | list.append(item) |
|
432 | list.append(item) | |
433 | return list |
|
433 | return list | |
434 |
|
434 | |||
435 | def _format_exception_only(self, etype, value): |
|
435 | def _format_exception_only(self, etype, value): | |
436 | """Format the exception part of a traceback. |
|
436 | """Format the exception part of a traceback. | |
437 |
|
437 | |||
438 | The arguments are the exception type and value such as given by |
|
438 | The arguments are the exception type and value such as given by | |
439 | sys.exc_info()[:2]. The return value is a list of strings, each ending |
|
439 | sys.exc_info()[:2]. The return value is a list of strings, each ending | |
440 | in a newline. Normally, the list contains a single string; however, |
|
440 | in a newline. Normally, the list contains a single string; however, | |
441 | for SyntaxError exceptions, it contains several lines that (when |
|
441 | for SyntaxError exceptions, it contains several lines that (when | |
442 | printed) display detailed information about where the syntax error |
|
442 | printed) display detailed information about where the syntax error | |
443 | occurred. The message indicating which exception occurred is the |
|
443 | occurred. The message indicating which exception occurred is the | |
444 | always last string in the list. |
|
444 | always last string in the list. | |
445 |
|
445 | |||
446 | Also lifted nearly verbatim from traceback.py |
|
446 | Also lifted nearly verbatim from traceback.py | |
447 | """ |
|
447 | """ | |
448 |
|
448 | |||
|
449 | have_filedata = False | |||
449 | Colors = self.Colors |
|
450 | Colors = self.Colors | |
450 | list = [] |
|
451 | list = [] | |
451 | try: |
|
452 | try: | |
452 | stype = Colors.excName + etype.__name__ + Colors.Normal |
|
453 | stype = Colors.excName + etype.__name__ + Colors.Normal | |
453 | except AttributeError: |
|
454 | except AttributeError: | |
454 | stype = etype # String exceptions don't get special coloring |
|
455 | stype = etype # String exceptions don't get special coloring | |
455 | if value is None: |
|
456 | if value is None: | |
456 | list.append( str(stype) + '\n') |
|
457 | list.append( str(stype) + '\n') | |
457 | else: |
|
458 | else: | |
458 | if etype is SyntaxError: |
|
459 | if etype is SyntaxError: | |
459 | try: |
|
460 | try: | |
460 | msg, (filename, lineno, offset, line) = value |
|
461 | msg, (filename, lineno, offset, line) = value | |
461 | except: |
|
462 | except: | |
462 | have_filedata = False |
|
463 | have_filedata = False | |
463 | else: |
|
464 | else: | |
464 | have_filedata = True |
|
465 | have_filedata = True | |
465 | #print 'filename is',filename # dbg |
|
466 | #print 'filename is',filename # dbg | |
466 | if not filename: filename = "<string>" |
|
467 | if not filename: filename = "<string>" | |
467 | list.append('%s File %s"%s"%s, line %s%d%s\n' % \ |
|
468 | list.append('%s File %s"%s"%s, line %s%d%s\n' % \ | |
468 | (Colors.normalEm, |
|
469 | (Colors.normalEm, | |
469 | Colors.filenameEm, filename, Colors.normalEm, |
|
470 | Colors.filenameEm, filename, Colors.normalEm, | |
470 | Colors.linenoEm, lineno, Colors.Normal )) |
|
471 | Colors.linenoEm, lineno, Colors.Normal )) | |
471 | if line is not None: |
|
472 | if line is not None: | |
472 | i = 0 |
|
473 | i = 0 | |
473 | while i < len(line) and line[i].isspace(): |
|
474 | while i < len(line) and line[i].isspace(): | |
474 | i = i+1 |
|
475 | i = i+1 | |
475 | list.append('%s %s%s\n' % (Colors.line, |
|
476 | list.append('%s %s%s\n' % (Colors.line, | |
476 | line.strip(), |
|
477 | line.strip(), | |
477 | Colors.Normal)) |
|
478 | Colors.Normal)) | |
478 | if offset is not None: |
|
479 | if offset is not None: | |
479 | s = ' ' |
|
480 | s = ' ' | |
480 | for c in line[i:offset-1]: |
|
481 | for c in line[i:offset-1]: | |
481 | if c.isspace(): |
|
482 | if c.isspace(): | |
482 | s = s + c |
|
483 | s = s + c | |
483 | else: |
|
484 | else: | |
484 | s = s + ' ' |
|
485 | s = s + ' ' | |
485 | list.append('%s%s^%s\n' % (Colors.caret, s, |
|
486 | list.append('%s%s^%s\n' % (Colors.caret, s, | |
486 | Colors.Normal) ) |
|
487 | Colors.Normal) ) | |
487 | value = msg |
|
488 | value = msg | |
488 | s = self._some_str(value) |
|
489 | s = self._some_str(value) | |
489 | if s: |
|
490 | if s: | |
490 | list.append('%s%s:%s %s\n' % (str(stype), Colors.excName, |
|
491 | list.append('%s%s:%s %s\n' % (str(stype), Colors.excName, | |
491 | Colors.Normal, s)) |
|
492 | Colors.Normal, s)) | |
492 | else: |
|
493 | else: | |
493 | list.append('%s\n' % str(stype)) |
|
494 | list.append('%s\n' % str(stype)) | |
494 |
|
495 | |||
495 | # vds:>> |
|
496 | # vds:>> | |
496 | if have_filedata: |
|
497 | if have_filedata: | |
497 | __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) |
|
498 | __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) | |
498 | # vds:<< |
|
499 | # vds:<< | |
499 |
|
500 | |||
500 | return list |
|
501 | return list | |
501 |
|
502 | |||
502 | def _some_str(self, value): |
|
503 | def _some_str(self, value): | |
503 | # Lifted from traceback.py |
|
504 | # Lifted from traceback.py | |
504 | try: |
|
505 | try: | |
505 | return str(value) |
|
506 | return str(value) | |
506 | except: |
|
507 | except: | |
507 | return '<unprintable %s object>' % type(value).__name__ |
|
508 | return '<unprintable %s object>' % type(value).__name__ | |
508 |
|
509 | |||
509 | #---------------------------------------------------------------------------- |
|
510 | #---------------------------------------------------------------------------- | |
510 | class VerboseTB(TBTools): |
|
511 | class VerboseTB(TBTools): | |
511 | """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead |
|
512 | """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead | |
512 | of HTML. Requires inspect and pydoc. Crazy, man. |
|
513 | of HTML. Requires inspect and pydoc. Crazy, man. | |
513 |
|
514 | |||
514 | Modified version which optionally strips the topmost entries from the |
|
515 | Modified version which optionally strips the topmost entries from the | |
515 | traceback, to be used with alternate interpreters (because their own code |
|
516 | traceback, to be used with alternate interpreters (because their own code | |
516 | would appear in the traceback).""" |
|
517 | would appear in the traceback).""" | |
517 |
|
518 | |||
518 | def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0, |
|
519 | def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0, | |
519 | call_pdb = 0, include_vars=1): |
|
520 | call_pdb = 0, include_vars=1): | |
520 | """Specify traceback offset, headers and color scheme. |
|
521 | """Specify traceback offset, headers and color scheme. | |
521 |
|
522 | |||
522 | Define how many frames to drop from the tracebacks. Calling it with |
|
523 | Define how many frames to drop from the tracebacks. Calling it with | |
523 | tb_offset=1 allows use of this handler in interpreters which will have |
|
524 | tb_offset=1 allows use of this handler in interpreters which will have | |
524 | their own code at the top of the traceback (VerboseTB will first |
|
525 | their own code at the top of the traceback (VerboseTB will first | |
525 | remove that frame before printing the traceback info).""" |
|
526 | remove that frame before printing the traceback info).""" | |
526 | TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb) |
|
527 | TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb) | |
527 | self.tb_offset = tb_offset |
|
528 | self.tb_offset = tb_offset | |
528 | self.long_header = long_header |
|
529 | self.long_header = long_header | |
529 | self.include_vars = include_vars |
|
530 | self.include_vars = include_vars | |
530 |
|
531 | |||
531 | def text(self, etype, evalue, etb, context=5): |
|
532 | def text(self, etype, evalue, etb, context=5): | |
532 | """Return a nice text document describing the traceback.""" |
|
533 | """Return a nice text document describing the traceback.""" | |
533 |
|
534 | |||
534 | # some locals |
|
535 | # some locals | |
535 | try: |
|
536 | try: | |
536 | etype = etype.__name__ |
|
537 | etype = etype.__name__ | |
537 | except AttributeError: |
|
538 | except AttributeError: | |
538 | pass |
|
539 | pass | |
539 | Colors = self.Colors # just a shorthand + quicker name lookup |
|
540 | Colors = self.Colors # just a shorthand + quicker name lookup | |
540 | ColorsNormal = Colors.Normal # used a lot |
|
541 | ColorsNormal = Colors.Normal # used a lot | |
541 | col_scheme = self.color_scheme_table.active_scheme_name |
|
542 | col_scheme = self.color_scheme_table.active_scheme_name | |
542 | indent = ' '*INDENT_SIZE |
|
543 | indent = ' '*INDENT_SIZE | |
543 | em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal) |
|
544 | em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal) | |
544 | undefined = '%sundefined%s' % (Colors.em, ColorsNormal) |
|
545 | undefined = '%sundefined%s' % (Colors.em, ColorsNormal) | |
545 | exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal) |
|
546 | exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal) | |
546 |
|
547 | |||
547 | # some internal-use functions |
|
548 | # some internal-use functions | |
548 | def text_repr(value): |
|
549 | def text_repr(value): | |
549 | """Hopefully pretty robust repr equivalent.""" |
|
550 | """Hopefully pretty robust repr equivalent.""" | |
550 | # this is pretty horrible but should always return *something* |
|
551 | # this is pretty horrible but should always return *something* | |
551 | try: |
|
552 | try: | |
552 | return pydoc.text.repr(value) |
|
553 | return pydoc.text.repr(value) | |
553 | except KeyboardInterrupt: |
|
554 | except KeyboardInterrupt: | |
554 | raise |
|
555 | raise | |
555 | except: |
|
556 | except: | |
556 | try: |
|
557 | try: | |
557 | return repr(value) |
|
558 | return repr(value) | |
558 | except KeyboardInterrupt: |
|
559 | except KeyboardInterrupt: | |
559 | raise |
|
560 | raise | |
560 | except: |
|
561 | except: | |
561 | try: |
|
562 | try: | |
562 | # all still in an except block so we catch |
|
563 | # all still in an except block so we catch | |
563 | # getattr raising |
|
564 | # getattr raising | |
564 | name = getattr(value, '__name__', None) |
|
565 | name = getattr(value, '__name__', None) | |
565 | if name: |
|
566 | if name: | |
566 | # ick, recursion |
|
567 | # ick, recursion | |
567 | return text_repr(name) |
|
568 | return text_repr(name) | |
568 | klass = getattr(value, '__class__', None) |
|
569 | klass = getattr(value, '__class__', None) | |
569 | if klass: |
|
570 | if klass: | |
570 | return '%s instance' % text_repr(klass) |
|
571 | return '%s instance' % text_repr(klass) | |
571 | except KeyboardInterrupt: |
|
572 | except KeyboardInterrupt: | |
572 | raise |
|
573 | raise | |
573 | except: |
|
574 | except: | |
574 | return 'UNRECOVERABLE REPR FAILURE' |
|
575 | return 'UNRECOVERABLE REPR FAILURE' | |
575 | def eqrepr(value, repr=text_repr): return '=%s' % repr(value) |
|
576 | def eqrepr(value, repr=text_repr): return '=%s' % repr(value) | |
576 | def nullrepr(value, repr=text_repr): return '' |
|
577 | def nullrepr(value, repr=text_repr): return '' | |
577 |
|
578 | |||
578 | # meat of the code begins |
|
579 | # meat of the code begins | |
579 | try: |
|
580 | try: | |
580 | etype = etype.__name__ |
|
581 | etype = etype.__name__ | |
581 | except AttributeError: |
|
582 | except AttributeError: | |
582 | pass |
|
583 | pass | |
583 |
|
584 | |||
584 | if self.long_header: |
|
585 | if self.long_header: | |
585 | # Header with the exception type, python version, and date |
|
586 | # Header with the exception type, python version, and date | |
586 | pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable |
|
587 | pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable | |
587 | date = time.ctime(time.time()) |
|
588 | date = time.ctime(time.time()) | |
588 |
|
589 | |||
589 | head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal, |
|
590 | head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal, | |
590 | exc, ' '*(75-len(str(etype))-len(pyver)), |
|
591 | exc, ' '*(75-len(str(etype))-len(pyver)), | |
591 | pyver, string.rjust(date, 75) ) |
|
592 | pyver, string.rjust(date, 75) ) | |
592 | head += "\nA problem occured executing Python code. Here is the sequence of function"\ |
|
593 | head += "\nA problem occured executing Python code. Here is the sequence of function"\ | |
593 | "\ncalls leading up to the error, with the most recent (innermost) call last." |
|
594 | "\ncalls leading up to the error, with the most recent (innermost) call last." | |
594 | else: |
|
595 | else: | |
595 | # Simplified header |
|
596 | # Simplified header | |
596 | head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc, |
|
597 | head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc, | |
597 | string.rjust('Traceback (most recent call last)', |
|
598 | string.rjust('Traceback (most recent call last)', | |
598 | 75 - len(str(etype)) ) ) |
|
599 | 75 - len(str(etype)) ) ) | |
599 | frames = [] |
|
600 | frames = [] | |
600 | # Flush cache before calling inspect. This helps alleviate some of the |
|
601 | # Flush cache before calling inspect. This helps alleviate some of the | |
601 | # problems with python 2.3's inspect.py. |
|
602 | # problems with python 2.3's inspect.py. | |
602 | linecache.checkcache() |
|
603 | linecache.checkcache() | |
603 | # Drop topmost frames if requested |
|
604 | # Drop topmost frames if requested | |
604 | try: |
|
605 | try: | |
605 | # Try the default getinnerframes and Alex's: Alex's fixes some |
|
606 | # Try the default getinnerframes and Alex's: Alex's fixes some | |
606 | # problems, but it generates empty tracebacks for console errors |
|
607 | # problems, but it generates empty tracebacks for console errors | |
607 | # (5 blanks lines) where none should be returned. |
|
608 | # (5 blanks lines) where none should be returned. | |
608 | #records = inspect.getinnerframes(etb, context)[self.tb_offset:] |
|
609 | #records = inspect.getinnerframes(etb, context)[self.tb_offset:] | |
609 | #print 'python records:', records # dbg |
|
610 | #print 'python records:', records # dbg | |
610 | records = _fixed_getinnerframes(etb, context,self.tb_offset) |
|
611 | records = _fixed_getinnerframes(etb, context,self.tb_offset) | |
611 | #print 'alex records:', records # dbg |
|
612 | #print 'alex records:', records # dbg | |
612 | except: |
|
613 | except: | |
613 |
|
614 | |||
614 | # FIXME: I've been getting many crash reports from python 2.3 |
|
615 | # FIXME: I've been getting many crash reports from python 2.3 | |
615 | # users, traceable to inspect.py. If I can find a small test-case |
|
616 | # users, traceable to inspect.py. If I can find a small test-case | |
616 | # to reproduce this, I should either write a better workaround or |
|
617 | # to reproduce this, I should either write a better workaround or | |
617 | # file a bug report against inspect (if that's the real problem). |
|
618 | # file a bug report against inspect (if that's the real problem). | |
618 | # So far, I haven't been able to find an isolated example to |
|
619 | # So far, I haven't been able to find an isolated example to | |
619 | # reproduce the problem. |
|
620 | # reproduce the problem. | |
620 | inspect_error() |
|
621 | inspect_error() | |
621 | traceback.print_exc(file=Term.cerr) |
|
622 | traceback.print_exc(file=Term.cerr) | |
622 | info('\nUnfortunately, your original traceback can not be constructed.\n') |
|
623 | info('\nUnfortunately, your original traceback can not be constructed.\n') | |
623 | return '' |
|
624 | return '' | |
624 |
|
625 | |||
625 | # build some color string templates outside these nested loops |
|
626 | # build some color string templates outside these nested loops | |
626 | tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal) |
|
627 | tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal) | |
627 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, |
|
628 | tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, | |
628 | ColorsNormal) |
|
629 | ColorsNormal) | |
629 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ |
|
630 | tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ | |
630 | (Colors.vName, Colors.valEm, ColorsNormal) |
|
631 | (Colors.vName, Colors.valEm, ColorsNormal) | |
631 | tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) |
|
632 | tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) | |
632 | tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal, |
|
633 | tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal, | |
633 | Colors.vName, ColorsNormal) |
|
634 | Colors.vName, ColorsNormal) | |
634 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) |
|
635 | tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) | |
635 | tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal) |
|
636 | tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal) | |
636 | tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line, |
|
637 | tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line, | |
637 | ColorsNormal) |
|
638 | ColorsNormal) | |
638 |
|
639 | |||
639 | # now, loop over all records printing context and info |
|
640 | # now, loop over all records printing context and info | |
640 | abspath = os.path.abspath |
|
641 | abspath = os.path.abspath | |
641 | for frame, file, lnum, func, lines, index in records: |
|
642 | for frame, file, lnum, func, lines, index in records: | |
642 | #print '*** record:',file,lnum,func,lines,index # dbg |
|
643 | #print '*** record:',file,lnum,func,lines,index # dbg | |
643 | try: |
|
644 | try: | |
644 | file = file and abspath(file) or '?' |
|
645 | file = file and abspath(file) or '?' | |
645 | except OSError: |
|
646 | except OSError: | |
646 | # if file is '<console>' or something not in the filesystem, |
|
647 | # if file is '<console>' or something not in the filesystem, | |
647 | # the abspath call will throw an OSError. Just ignore it and |
|
648 | # the abspath call will throw an OSError. Just ignore it and | |
648 | # keep the original file string. |
|
649 | # keep the original file string. | |
649 | pass |
|
650 | pass | |
650 | link = tpl_link % file |
|
651 | link = tpl_link % file | |
651 | try: |
|
652 | try: | |
652 | args, varargs, varkw, locals = inspect.getargvalues(frame) |
|
653 | args, varargs, varkw, locals = inspect.getargvalues(frame) | |
653 | except: |
|
654 | except: | |
654 | # This can happen due to a bug in python2.3. We should be |
|
655 | # This can happen due to a bug in python2.3. We should be | |
655 | # able to remove this try/except when 2.4 becomes a |
|
656 | # able to remove this try/except when 2.4 becomes a | |
656 | # requirement. Bug details at http://python.org/sf/1005466 |
|
657 | # requirement. Bug details at http://python.org/sf/1005466 | |
657 | inspect_error() |
|
658 | inspect_error() | |
658 | traceback.print_exc(file=Term.cerr) |
|
659 | traceback.print_exc(file=Term.cerr) | |
659 | info("\nIPython's exception reporting continues...\n") |
|
660 | info("\nIPython's exception reporting continues...\n") | |
660 |
|
661 | |||
661 | if func == '?': |
|
662 | if func == '?': | |
662 | call = '' |
|
663 | call = '' | |
663 | else: |
|
664 | else: | |
664 | # Decide whether to include variable details or not |
|
665 | # Decide whether to include variable details or not | |
665 | var_repr = self.include_vars and eqrepr or nullrepr |
|
666 | var_repr = self.include_vars and eqrepr or nullrepr | |
666 | try: |
|
667 | try: | |
667 | call = tpl_call % (func,inspect.formatargvalues(args, |
|
668 | call = tpl_call % (func,inspect.formatargvalues(args, | |
668 | varargs, varkw, |
|
669 | varargs, varkw, | |
669 | locals,formatvalue=var_repr)) |
|
670 | locals,formatvalue=var_repr)) | |
670 | except KeyError: |
|
671 | except KeyError: | |
671 | # Very odd crash from inspect.formatargvalues(). The |
|
672 | # Very odd crash from inspect.formatargvalues(). The | |
672 | # scenario under which it appeared was a call to |
|
673 | # scenario under which it appeared was a call to | |
673 | # view(array,scale) in NumTut.view.view(), where scale had |
|
674 | # view(array,scale) in NumTut.view.view(), where scale had | |
674 | # been defined as a scalar (it should be a tuple). Somehow |
|
675 | # been defined as a scalar (it should be a tuple). Somehow | |
675 | # inspect messes up resolving the argument list of view() |
|
676 | # inspect messes up resolving the argument list of view() | |
676 | # and barfs out. At some point I should dig into this one |
|
677 | # and barfs out. At some point I should dig into this one | |
677 | # and file a bug report about it. |
|
678 | # and file a bug report about it. | |
678 | inspect_error() |
|
679 | inspect_error() | |
679 | traceback.print_exc(file=Term.cerr) |
|
680 | traceback.print_exc(file=Term.cerr) | |
680 | info("\nIPython's exception reporting continues...\n") |
|
681 | info("\nIPython's exception reporting continues...\n") | |
681 | call = tpl_call_fail % func |
|
682 | call = tpl_call_fail % func | |
682 |
|
683 | |||
683 | # Initialize a list of names on the current line, which the |
|
684 | # Initialize a list of names on the current line, which the | |
684 | # tokenizer below will populate. |
|
685 | # tokenizer below will populate. | |
685 | names = [] |
|
686 | names = [] | |
686 |
|
687 | |||
687 | def tokeneater(token_type, token, start, end, line): |
|
688 | def tokeneater(token_type, token, start, end, line): | |
688 | """Stateful tokeneater which builds dotted names. |
|
689 | """Stateful tokeneater which builds dotted names. | |
689 |
|
690 | |||
690 | The list of names it appends to (from the enclosing scope) can |
|
691 | The list of names it appends to (from the enclosing scope) can | |
691 | contain repeated composite names. This is unavoidable, since |
|
692 | contain repeated composite names. This is unavoidable, since | |
692 | there is no way to disambguate partial dotted structures until |
|
693 | there is no way to disambguate partial dotted structures until | |
693 | the full list is known. The caller is responsible for pruning |
|
694 | the full list is known. The caller is responsible for pruning | |
694 | the final list of duplicates before using it.""" |
|
695 | the final list of duplicates before using it.""" | |
695 |
|
696 | |||
696 | # build composite names |
|
697 | # build composite names | |
697 | if token == '.': |
|
698 | if token == '.': | |
698 | try: |
|
699 | try: | |
699 | names[-1] += '.' |
|
700 | names[-1] += '.' | |
700 | # store state so the next token is added for x.y.z names |
|
701 | # store state so the next token is added for x.y.z names | |
701 | tokeneater.name_cont = True |
|
702 | tokeneater.name_cont = True | |
702 | return |
|
703 | return | |
703 | except IndexError: |
|
704 | except IndexError: | |
704 | pass |
|
705 | pass | |
705 | if token_type == tokenize.NAME and token not in keyword.kwlist: |
|
706 | if token_type == tokenize.NAME and token not in keyword.kwlist: | |
706 | if tokeneater.name_cont: |
|
707 | if tokeneater.name_cont: | |
707 | # Dotted names |
|
708 | # Dotted names | |
708 | names[-1] += token |
|
709 | names[-1] += token | |
709 | tokeneater.name_cont = False |
|
710 | tokeneater.name_cont = False | |
710 | else: |
|
711 | else: | |
711 | # Regular new names. We append everything, the caller |
|
712 | # Regular new names. We append everything, the caller | |
712 | # will be responsible for pruning the list later. It's |
|
713 | # will be responsible for pruning the list later. It's | |
713 | # very tricky to try to prune as we go, b/c composite |
|
714 | # very tricky to try to prune as we go, b/c composite | |
714 | # names can fool us. The pruning at the end is easy |
|
715 | # names can fool us. The pruning at the end is easy | |
715 | # to do (or the caller can print a list with repeated |
|
716 | # to do (or the caller can print a list with repeated | |
716 | # names if so desired. |
|
717 | # names if so desired. | |
717 | names.append(token) |
|
718 | names.append(token) | |
718 | elif token_type == tokenize.NEWLINE: |
|
719 | elif token_type == tokenize.NEWLINE: | |
719 | raise IndexError |
|
720 | raise IndexError | |
720 | # we need to store a bit of state in the tokenizer to build |
|
721 | # we need to store a bit of state in the tokenizer to build | |
721 | # dotted names |
|
722 | # dotted names | |
722 | tokeneater.name_cont = False |
|
723 | tokeneater.name_cont = False | |
723 |
|
724 | |||
724 | def linereader(file=file, lnum=[lnum], getline=linecache.getline): |
|
725 | def linereader(file=file, lnum=[lnum], getline=linecache.getline): | |
725 | line = getline(file, lnum[0]) |
|
726 | line = getline(file, lnum[0]) | |
726 | lnum[0] += 1 |
|
727 | lnum[0] += 1 | |
727 | return line |
|
728 | return line | |
728 |
|
729 | |||
729 | # Build the list of names on this line of code where the exception |
|
730 | # Build the list of names on this line of code where the exception | |
730 | # occurred. |
|
731 | # occurred. | |
731 | try: |
|
732 | try: | |
732 | # This builds the names list in-place by capturing it from the |
|
733 | # This builds the names list in-place by capturing it from the | |
733 | # enclosing scope. |
|
734 | # enclosing scope. | |
734 | tokenize.tokenize(linereader, tokeneater) |
|
735 | tokenize.tokenize(linereader, tokeneater) | |
735 | except IndexError: |
|
736 | except IndexError: | |
736 | # signals exit of tokenizer |
|
737 | # signals exit of tokenizer | |
737 | pass |
|
738 | pass | |
738 | except tokenize.TokenError,msg: |
|
739 | except tokenize.TokenError,msg: | |
739 | _m = ("An unexpected error occurred while tokenizing input\n" |
|
740 | _m = ("An unexpected error occurred while tokenizing input\n" | |
740 | "The following traceback may be corrupted or invalid\n" |
|
741 | "The following traceback may be corrupted or invalid\n" | |
741 | "The error message is: %s\n" % msg) |
|
742 | "The error message is: %s\n" % msg) | |
742 | error(_m) |
|
743 | error(_m) | |
743 |
|
744 | |||
744 | # prune names list of duplicates, but keep the right order |
|
745 | # prune names list of duplicates, but keep the right order | |
745 | unique_names = uniq_stable(names) |
|
746 | unique_names = uniq_stable(names) | |
746 |
|
747 | |||
747 | # Start loop over vars |
|
748 | # Start loop over vars | |
748 | lvals = [] |
|
749 | lvals = [] | |
749 | if self.include_vars: |
|
750 | if self.include_vars: | |
750 | for name_full in unique_names: |
|
751 | for name_full in unique_names: | |
751 | name_base = name_full.split('.',1)[0] |
|
752 | name_base = name_full.split('.',1)[0] | |
752 | if name_base in frame.f_code.co_varnames: |
|
753 | if name_base in frame.f_code.co_varnames: | |
753 | if locals.has_key(name_base): |
|
754 | if locals.has_key(name_base): | |
754 | try: |
|
755 | try: | |
755 | value = repr(eval(name_full,locals)) |
|
756 | value = repr(eval(name_full,locals)) | |
756 | except: |
|
757 | except: | |
757 | value = undefined |
|
758 | value = undefined | |
758 | else: |
|
759 | else: | |
759 | value = undefined |
|
760 | value = undefined | |
760 | name = tpl_local_var % name_full |
|
761 | name = tpl_local_var % name_full | |
761 | else: |
|
762 | else: | |
762 | if frame.f_globals.has_key(name_base): |
|
763 | if frame.f_globals.has_key(name_base): | |
763 | try: |
|
764 | try: | |
764 | value = repr(eval(name_full,frame.f_globals)) |
|
765 | value = repr(eval(name_full,frame.f_globals)) | |
765 | except: |
|
766 | except: | |
766 | value = undefined |
|
767 | value = undefined | |
767 | else: |
|
768 | else: | |
768 | value = undefined |
|
769 | value = undefined | |
769 | name = tpl_global_var % name_full |
|
770 | name = tpl_global_var % name_full | |
770 | lvals.append(tpl_name_val % (name,value)) |
|
771 | lvals.append(tpl_name_val % (name,value)) | |
771 | if lvals: |
|
772 | if lvals: | |
772 | lvals = '%s%s' % (indent,em_normal.join(lvals)) |
|
773 | lvals = '%s%s' % (indent,em_normal.join(lvals)) | |
773 | else: |
|
774 | else: | |
774 | lvals = '' |
|
775 | lvals = '' | |
775 |
|
776 | |||
776 | level = '%s %s\n' % (link,call) |
|
777 | level = '%s %s\n' % (link,call) | |
777 |
|
778 | |||
778 | if index is None: |
|
779 | if index is None: | |
779 | frames.append(level) |
|
780 | frames.append(level) | |
780 | else: |
|
781 | else: | |
781 | frames.append('%s%s' % (level,''.join( |
|
782 | frames.append('%s%s' % (level,''.join( | |
782 | _formatTracebackLines(lnum,index,lines,Colors,lvals, |
|
783 | _formatTracebackLines(lnum,index,lines,Colors,lvals, | |
783 | col_scheme)))) |
|
784 | col_scheme)))) | |
784 |
|
785 | |||
785 | # Get (safely) a string form of the exception info |
|
786 | # Get (safely) a string form of the exception info | |
786 | try: |
|
787 | try: | |
787 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
788 | etype_str,evalue_str = map(str,(etype,evalue)) | |
788 | except: |
|
789 | except: | |
789 | # User exception is improperly defined. |
|
790 | # User exception is improperly defined. | |
790 | etype,evalue = str,sys.exc_info()[:2] |
|
791 | etype,evalue = str,sys.exc_info()[:2] | |
791 | etype_str,evalue_str = map(str,(etype,evalue)) |
|
792 | etype_str,evalue_str = map(str,(etype,evalue)) | |
792 | # ... and format it |
|
793 | # ... and format it | |
793 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, |
|
794 | exception = ['%s%s%s: %s' % (Colors.excName, etype_str, | |
794 | ColorsNormal, evalue_str)] |
|
795 | ColorsNormal, evalue_str)] | |
795 | if type(evalue) is types.InstanceType: |
|
796 | if type(evalue) is types.InstanceType: | |
796 | try: |
|
797 | try: | |
797 | names = [w for w in dir(evalue) if isinstance(w, basestring)] |
|
798 | names = [w for w in dir(evalue) if isinstance(w, basestring)] | |
798 | except: |
|
799 | except: | |
799 | # Every now and then, an object with funny inernals blows up |
|
800 | # Every now and then, an object with funny inernals blows up | |
800 | # when dir() is called on it. We do the best we can to report |
|
801 | # when dir() is called on it. We do the best we can to report | |
801 | # the problem and continue |
|
802 | # the problem and continue | |
802 | _m = '%sException reporting error (object with broken dir())%s:' |
|
803 | _m = '%sException reporting error (object with broken dir())%s:' | |
803 | exception.append(_m % (Colors.excName,ColorsNormal)) |
|
804 | exception.append(_m % (Colors.excName,ColorsNormal)) | |
804 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) |
|
805 | etype_str,evalue_str = map(str,sys.exc_info()[:2]) | |
805 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, |
|
806 | exception.append('%s%s%s: %s' % (Colors.excName,etype_str, | |
806 | ColorsNormal, evalue_str)) |
|
807 | ColorsNormal, evalue_str)) | |
807 | names = [] |
|
808 | names = [] | |
808 | for name in names: |
|
809 | for name in names: | |
809 | value = text_repr(getattr(evalue, name)) |
|
810 | value = text_repr(getattr(evalue, name)) | |
810 | exception.append('\n%s%s = %s' % (indent, name, value)) |
|
811 | exception.append('\n%s%s = %s' % (indent, name, value)) | |
811 |
|
812 | |||
812 | # vds: >> |
|
813 | # vds: >> | |
813 | if records: |
|
814 | if records: | |
814 | filepath, lnum = records[-1][1:3] |
|
815 | filepath, lnum = records[-1][1:3] | |
815 | #print "file:", str(file), "linenb", str(lnum) # dbg |
|
816 | #print "file:", str(file), "linenb", str(lnum) # dbg | |
816 | filepath = os.path.abspath(filepath) |
|
817 | filepath = os.path.abspath(filepath) | |
817 | __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0) |
|
818 | __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0) | |
818 | # vds: << |
|
819 | # vds: << | |
819 |
|
820 | |||
820 | # return all our info assembled as a single string |
|
821 | # return all our info assembled as a single string | |
821 | return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) ) |
|
822 | return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) ) | |
822 |
|
823 | |||
823 | def debugger(self,force=False): |
|
824 | def debugger(self,force=False): | |
824 | """Call up the pdb debugger if desired, always clean up the tb |
|
825 | """Call up the pdb debugger if desired, always clean up the tb | |
825 | reference. |
|
826 | reference. | |
826 |
|
827 | |||
827 | Keywords: |
|
828 | Keywords: | |
828 |
|
829 | |||
829 | - force(False): by default, this routine checks the instance call_pdb |
|
830 | - force(False): by default, this routine checks the instance call_pdb | |
830 | flag and does not actually invoke the debugger if the flag is false. |
|
831 | flag and does not actually invoke the debugger if the flag is false. | |
831 | The 'force' option forces the debugger to activate even if the flag |
|
832 | The 'force' option forces the debugger to activate even if the flag | |
832 | is false. |
|
833 | is false. | |
833 |
|
834 | |||
834 | If the call_pdb flag is set, the pdb interactive debugger is |
|
835 | If the call_pdb flag is set, the pdb interactive debugger is | |
835 | invoked. In all cases, the self.tb reference to the current traceback |
|
836 | invoked. In all cases, the self.tb reference to the current traceback | |
836 | is deleted to prevent lingering references which hamper memory |
|
837 | is deleted to prevent lingering references which hamper memory | |
837 | management. |
|
838 | management. | |
838 |
|
839 | |||
839 | Note that each call to pdb() does an 'import readline', so if your app |
|
840 | Note that each call to pdb() does an 'import readline', so if your app | |
840 | requires a special setup for the readline completers, you'll have to |
|
841 | requires a special setup for the readline completers, you'll have to | |
841 | fix that by hand after invoking the exception handler.""" |
|
842 | fix that by hand after invoking the exception handler.""" | |
842 |
|
843 | |||
843 | if force or self.call_pdb: |
|
844 | if force or self.call_pdb: | |
844 | if self.pdb is None: |
|
845 | if self.pdb is None: | |
845 | self.pdb = Debugger.Pdb( |
|
846 | self.pdb = Debugger.Pdb( | |
846 | self.color_scheme_table.active_scheme_name) |
|
847 | self.color_scheme_table.active_scheme_name) | |
847 | # the system displayhook may have changed, restore the original |
|
848 | # the system displayhook may have changed, restore the original | |
848 | # for pdb |
|
849 | # for pdb | |
849 | dhook = sys.displayhook |
|
850 | dhook = sys.displayhook | |
850 | sys.displayhook = sys.__displayhook__ |
|
851 | sys.displayhook = sys.__displayhook__ | |
851 | self.pdb.reset() |
|
852 | self.pdb.reset() | |
852 | # Find the right frame so we don't pop up inside ipython itself |
|
853 | # Find the right frame so we don't pop up inside ipython itself | |
853 | if hasattr(self,'tb'): |
|
854 | if hasattr(self,'tb'): | |
854 | etb = self.tb |
|
855 | etb = self.tb | |
855 | else: |
|
856 | else: | |
856 | etb = self.tb = sys.last_traceback |
|
857 | etb = self.tb = sys.last_traceback | |
857 | while self.tb.tb_next is not None: |
|
858 | while self.tb.tb_next is not None: | |
858 | self.tb = self.tb.tb_next |
|
859 | self.tb = self.tb.tb_next | |
859 | try: |
|
860 | try: | |
860 | if etb and etb.tb_next: |
|
861 | if etb and etb.tb_next: | |
861 | etb = etb.tb_next |
|
862 | etb = etb.tb_next | |
862 | self.pdb.botframe = etb.tb_frame |
|
863 | self.pdb.botframe = etb.tb_frame | |
863 | self.pdb.interaction(self.tb.tb_frame, self.tb) |
|
864 | self.pdb.interaction(self.tb.tb_frame, self.tb) | |
864 | finally: |
|
865 | finally: | |
865 | sys.displayhook = dhook |
|
866 | sys.displayhook = dhook | |
866 |
|
867 | |||
867 | if hasattr(self,'tb'): |
|
868 | if hasattr(self,'tb'): | |
868 | del self.tb |
|
869 | del self.tb | |
869 |
|
870 | |||
870 | def handler(self, info=None): |
|
871 | def handler(self, info=None): | |
871 | (etype, evalue, etb) = info or sys.exc_info() |
|
872 | (etype, evalue, etb) = info or sys.exc_info() | |
872 | self.tb = etb |
|
873 | self.tb = etb | |
873 | Term.cout.flush() |
|
874 | Term.cout.flush() | |
874 | print >> Term.cerr, self.text(etype, evalue, etb) |
|
875 | print >> Term.cerr, self.text(etype, evalue, etb) | |
875 | Term.cerr.flush() |
|
876 | Term.cerr.flush() | |
876 |
|
877 | |||
877 | # Changed so an instance can just be called as VerboseTB_inst() and print |
|
878 | # Changed so an instance can just be called as VerboseTB_inst() and print | |
878 | # out the right info on its own. |
|
879 | # out the right info on its own. | |
879 | def __call__(self, etype=None, evalue=None, etb=None): |
|
880 | def __call__(self, etype=None, evalue=None, etb=None): | |
880 | """This hook can replace sys.excepthook (for Python 2.1 or higher).""" |
|
881 | """This hook can replace sys.excepthook (for Python 2.1 or higher).""" | |
881 | if etb is None: |
|
882 | if etb is None: | |
882 | self.handler() |
|
883 | self.handler() | |
883 | else: |
|
884 | else: | |
884 | self.handler((etype, evalue, etb)) |
|
885 | self.handler((etype, evalue, etb)) | |
885 | try: |
|
886 | try: | |
886 | self.debugger() |
|
887 | self.debugger() | |
887 | except KeyboardInterrupt: |
|
888 | except KeyboardInterrupt: | |
888 | print "\nKeyboardInterrupt" |
|
889 | print "\nKeyboardInterrupt" | |
889 |
|
890 | |||
890 | #---------------------------------------------------------------------------- |
|
891 | #---------------------------------------------------------------------------- | |
891 | class FormattedTB(VerboseTB,ListTB): |
|
892 | class FormattedTB(VerboseTB,ListTB): | |
892 | """Subclass ListTB but allow calling with a traceback. |
|
893 | """Subclass ListTB but allow calling with a traceback. | |
893 |
|
894 | |||
894 | It can thus be used as a sys.excepthook for Python > 2.1. |
|
895 | It can thus be used as a sys.excepthook for Python > 2.1. | |
895 |
|
896 | |||
896 | Also adds 'Context' and 'Verbose' modes, not available in ListTB. |
|
897 | Also adds 'Context' and 'Verbose' modes, not available in ListTB. | |
897 |
|
898 | |||
898 | Allows a tb_offset to be specified. This is useful for situations where |
|
899 | Allows a tb_offset to be specified. This is useful for situations where | |
899 | one needs to remove a number of topmost frames from the traceback (such as |
|
900 | one needs to remove a number of topmost frames from the traceback (such as | |
900 | occurs with python programs that themselves execute other python code, |
|
901 | occurs with python programs that themselves execute other python code, | |
901 | like Python shells). """ |
|
902 | like Python shells). """ | |
902 |
|
903 | |||
903 | def __init__(self, mode = 'Plain', color_scheme='Linux', |
|
904 | def __init__(self, mode = 'Plain', color_scheme='Linux', | |
904 | tb_offset = 0,long_header=0,call_pdb=0,include_vars=0): |
|
905 | tb_offset = 0,long_header=0,call_pdb=0,include_vars=0): | |
905 |
|
906 | |||
906 | # NEVER change the order of this list. Put new modes at the end: |
|
907 | # NEVER change the order of this list. Put new modes at the end: | |
907 | self.valid_modes = ['Plain','Context','Verbose'] |
|
908 | self.valid_modes = ['Plain','Context','Verbose'] | |
908 | self.verbose_modes = self.valid_modes[1:3] |
|
909 | self.verbose_modes = self.valid_modes[1:3] | |
909 |
|
910 | |||
910 | VerboseTB.__init__(self,color_scheme,tb_offset,long_header, |
|
911 | VerboseTB.__init__(self,color_scheme,tb_offset,long_header, | |
911 | call_pdb=call_pdb,include_vars=include_vars) |
|
912 | call_pdb=call_pdb,include_vars=include_vars) | |
912 | self.set_mode(mode) |
|
913 | self.set_mode(mode) | |
913 |
|
914 | |||
914 | def _extract_tb(self,tb): |
|
915 | def _extract_tb(self,tb): | |
915 | if tb: |
|
916 | if tb: | |
916 | return traceback.extract_tb(tb) |
|
917 | return traceback.extract_tb(tb) | |
917 | else: |
|
918 | else: | |
918 | return None |
|
919 | return None | |
919 |
|
920 | |||
920 | def text(self, etype, value, tb,context=5,mode=None): |
|
921 | def text(self, etype, value, tb,context=5,mode=None): | |
921 | """Return formatted traceback. |
|
922 | """Return formatted traceback. | |
922 |
|
923 | |||
923 | If the optional mode parameter is given, it overrides the current |
|
924 | If the optional mode parameter is given, it overrides the current | |
924 | mode.""" |
|
925 | mode.""" | |
925 |
|
926 | |||
926 | if mode is None: |
|
927 | if mode is None: | |
927 | mode = self.mode |
|
928 | mode = self.mode | |
928 | if mode in self.verbose_modes: |
|
929 | if mode in self.verbose_modes: | |
929 | # verbose modes need a full traceback |
|
930 | # verbose modes need a full traceback | |
930 | return VerboseTB.text(self,etype, value, tb,context=5) |
|
931 | return VerboseTB.text(self,etype, value, tb,context=5) | |
931 | else: |
|
932 | else: | |
932 | # We must check the source cache because otherwise we can print |
|
933 | # We must check the source cache because otherwise we can print | |
933 | # out-of-date source code. |
|
934 | # out-of-date source code. | |
934 | linecache.checkcache() |
|
935 | linecache.checkcache() | |
935 | # Now we can extract and format the exception |
|
936 | # Now we can extract and format the exception | |
936 | elist = self._extract_tb(tb) |
|
937 | elist = self._extract_tb(tb) | |
937 | if len(elist) > self.tb_offset: |
|
938 | if len(elist) > self.tb_offset: | |
938 | del elist[:self.tb_offset] |
|
939 | del elist[:self.tb_offset] | |
939 | return ListTB.text(self,etype,value,elist) |
|
940 | return ListTB.text(self,etype,value,elist) | |
940 |
|
941 | |||
941 | def set_mode(self,mode=None): |
|
942 | def set_mode(self,mode=None): | |
942 | """Switch to the desired mode. |
|
943 | """Switch to the desired mode. | |
943 |
|
944 | |||
944 | If mode is not specified, cycles through the available modes.""" |
|
945 | If mode is not specified, cycles through the available modes.""" | |
945 |
|
946 | |||
946 | if not mode: |
|
947 | if not mode: | |
947 | new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \ |
|
948 | new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \ | |
948 | len(self.valid_modes) |
|
949 | len(self.valid_modes) | |
949 | self.mode = self.valid_modes[new_idx] |
|
950 | self.mode = self.valid_modes[new_idx] | |
950 | elif mode not in self.valid_modes: |
|
951 | elif mode not in self.valid_modes: | |
951 | raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\ |
|
952 | raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\ | |
952 | 'Valid modes: '+str(self.valid_modes) |
|
953 | 'Valid modes: '+str(self.valid_modes) | |
953 | else: |
|
954 | else: | |
954 | self.mode = mode |
|
955 | self.mode = mode | |
955 | # include variable details only in 'Verbose' mode |
|
956 | # include variable details only in 'Verbose' mode | |
956 | self.include_vars = (self.mode == self.valid_modes[2]) |
|
957 | self.include_vars = (self.mode == self.valid_modes[2]) | |
957 |
|
958 | |||
958 | # some convenient shorcuts |
|
959 | # some convenient shorcuts | |
959 | def plain(self): |
|
960 | def plain(self): | |
960 | self.set_mode(self.valid_modes[0]) |
|
961 | self.set_mode(self.valid_modes[0]) | |
961 |
|
962 | |||
962 | def context(self): |
|
963 | def context(self): | |
963 | self.set_mode(self.valid_modes[1]) |
|
964 | self.set_mode(self.valid_modes[1]) | |
964 |
|
965 | |||
965 | def verbose(self): |
|
966 | def verbose(self): | |
966 | self.set_mode(self.valid_modes[2]) |
|
967 | self.set_mode(self.valid_modes[2]) | |
967 |
|
968 | |||
968 | #---------------------------------------------------------------------------- |
|
969 | #---------------------------------------------------------------------------- | |
969 | class AutoFormattedTB(FormattedTB): |
|
970 | class AutoFormattedTB(FormattedTB): | |
970 | """A traceback printer which can be called on the fly. |
|
971 | """A traceback printer which can be called on the fly. | |
971 |
|
972 | |||
972 | It will find out about exceptions by itself. |
|
973 | It will find out about exceptions by itself. | |
973 |
|
974 | |||
974 | A brief example: |
|
975 | A brief example: | |
975 |
|
976 | |||
976 | AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') |
|
977 | AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') | |
977 | try: |
|
978 | try: | |
978 | ... |
|
979 | ... | |
979 | except: |
|
980 | except: | |
980 | AutoTB() # or AutoTB(out=logfile) where logfile is an open file object |
|
981 | AutoTB() # or AutoTB(out=logfile) where logfile is an open file object | |
981 | """ |
|
982 | """ | |
982 | def __call__(self,etype=None,evalue=None,etb=None, |
|
983 | def __call__(self,etype=None,evalue=None,etb=None, | |
983 | out=None,tb_offset=None): |
|
984 | out=None,tb_offset=None): | |
984 | """Print out a formatted exception traceback. |
|
985 | """Print out a formatted exception traceback. | |
985 |
|
986 | |||
986 | Optional arguments: |
|
987 | Optional arguments: | |
987 | - out: an open file-like object to direct output to. |
|
988 | - out: an open file-like object to direct output to. | |
988 |
|
989 | |||
989 | - tb_offset: the number of frames to skip over in the stack, on a |
|
990 | - tb_offset: the number of frames to skip over in the stack, on a | |
990 | per-call basis (this overrides temporarily the instance's tb_offset |
|
991 | per-call basis (this overrides temporarily the instance's tb_offset | |
991 | given at initialization time. """ |
|
992 | given at initialization time. """ | |
992 |
|
993 | |||
993 | if out is None: |
|
994 | if out is None: | |
994 | out = Term.cerr |
|
995 | out = Term.cerr | |
995 | Term.cout.flush() |
|
996 | Term.cout.flush() | |
996 | if tb_offset is not None: |
|
997 | if tb_offset is not None: | |
997 | tb_offset, self.tb_offset = self.tb_offset, tb_offset |
|
998 | tb_offset, self.tb_offset = self.tb_offset, tb_offset | |
998 | print >> out, self.text(etype, evalue, etb) |
|
999 | print >> out, self.text(etype, evalue, etb) | |
999 | self.tb_offset = tb_offset |
|
1000 | self.tb_offset = tb_offset | |
1000 | else: |
|
1001 | else: | |
1001 | print >> out, self.text(etype, evalue, etb) |
|
1002 | print >> out, self.text(etype, evalue, etb) | |
1002 | out.flush() |
|
1003 | out.flush() | |
1003 | try: |
|
1004 | try: | |
1004 | self.debugger() |
|
1005 | self.debugger() | |
1005 | except KeyboardInterrupt: |
|
1006 | except KeyboardInterrupt: | |
1006 | print "\nKeyboardInterrupt" |
|
1007 | print "\nKeyboardInterrupt" | |
1007 |
|
1008 | |||
1008 | def text(self,etype=None,value=None,tb=None,context=5,mode=None): |
|
1009 | def text(self,etype=None,value=None,tb=None,context=5,mode=None): | |
1009 | if etype is None: |
|
1010 | if etype is None: | |
1010 | etype,value,tb = sys.exc_info() |
|
1011 | etype,value,tb = sys.exc_info() | |
1011 | self.tb = tb |
|
1012 | self.tb = tb | |
1012 | return FormattedTB.text(self,etype,value,tb,context=5,mode=mode) |
|
1013 | return FormattedTB.text(self,etype,value,tb,context=5,mode=mode) | |
1013 |
|
1014 | |||
1014 | #--------------------------------------------------------------------------- |
|
1015 | #--------------------------------------------------------------------------- | |
1015 | # A simple class to preserve Nathan's original functionality. |
|
1016 | # A simple class to preserve Nathan's original functionality. | |
1016 | class ColorTB(FormattedTB): |
|
1017 | class ColorTB(FormattedTB): | |
1017 | """Shorthand to initialize a FormattedTB in Linux colors mode.""" |
|
1018 | """Shorthand to initialize a FormattedTB in Linux colors mode.""" | |
1018 | def __init__(self,color_scheme='Linux',call_pdb=0): |
|
1019 | def __init__(self,color_scheme='Linux',call_pdb=0): | |
1019 | FormattedTB.__init__(self,color_scheme=color_scheme, |
|
1020 | FormattedTB.__init__(self,color_scheme=color_scheme, | |
1020 | call_pdb=call_pdb) |
|
1021 | call_pdb=call_pdb) | |
1021 |
|
1022 | |||
1022 | #---------------------------------------------------------------------------- |
|
1023 | #---------------------------------------------------------------------------- | |
1023 | # module testing (minimal) |
|
1024 | # module testing (minimal) | |
1024 | if __name__ == "__main__": |
|
1025 | if __name__ == "__main__": | |
1025 | def spam(c, (d, e)): |
|
1026 | def spam(c, (d, e)): | |
1026 | x = c + d |
|
1027 | x = c + d | |
1027 | y = c * d |
|
1028 | y = c * d | |
1028 | foo(x, y) |
|
1029 | foo(x, y) | |
1029 |
|
1030 | |||
1030 | def foo(a, b, bar=1): |
|
1031 | def foo(a, b, bar=1): | |
1031 | eggs(a, b + bar) |
|
1032 | eggs(a, b + bar) | |
1032 |
|
1033 | |||
1033 | def eggs(f, g, z=globals()): |
|
1034 | def eggs(f, g, z=globals()): | |
1034 | h = f + g |
|
1035 | h = f + g | |
1035 | i = f - g |
|
1036 | i = f - g | |
1036 | return h / i |
|
1037 | return h / i | |
1037 |
|
1038 | |||
1038 | print '' |
|
1039 | print '' | |
1039 | print '*** Before ***' |
|
1040 | print '*** Before ***' | |
1040 | try: |
|
1041 | try: | |
1041 | print spam(1, (2, 3)) |
|
1042 | print spam(1, (2, 3)) | |
1042 | except: |
|
1043 | except: | |
1043 | traceback.print_exc() |
|
1044 | traceback.print_exc() | |
1044 | print '' |
|
1045 | print '' | |
1045 |
|
1046 | |||
1046 | handler = ColorTB() |
|
1047 | handler = ColorTB() | |
1047 | print '*** ColorTB ***' |
|
1048 | print '*** ColorTB ***' | |
1048 | try: |
|
1049 | try: | |
1049 | print spam(1, (2, 3)) |
|
1050 | print spam(1, (2, 3)) | |
1050 | except: |
|
1051 | except: | |
1051 | apply(handler, sys.exc_info() ) |
|
1052 | apply(handler, sys.exc_info() ) | |
1052 | print '' |
|
1053 | print '' | |
1053 |
|
1054 | |||
1054 | handler = VerboseTB() |
|
1055 | handler = VerboseTB() | |
1055 | print '*** VerboseTB ***' |
|
1056 | print '*** VerboseTB ***' | |
1056 | try: |
|
1057 | try: | |
1057 | print spam(1, (2, 3)) |
|
1058 | print spam(1, (2, 3)) | |
1058 | except: |
|
1059 | except: | |
1059 | apply(handler, sys.exc_info() ) |
|
1060 | apply(handler, sys.exc_info() ) | |
1060 | print '' |
|
1061 | print '' | |
1061 |
|
1062 |
@@ -1,391 +1,397 b'' | |||||
1 | .. _development: |
|
1 | .. _development: | |
2 |
|
2 | |||
3 | ================================== |
|
3 | ================================== | |
4 | IPython development guidelines |
|
4 | IPython development guidelines | |
5 | ================================== |
|
5 | ================================== | |
6 |
|
6 | |||
7 | .. contents:: |
|
7 | .. contents:: | |
8 |
|
8 | |||
9 |
|
9 | |||
10 | Overview |
|
10 | Overview | |
11 | ======== |
|
11 | ======== | |
12 |
|
12 | |||
13 | IPython is the next generation of IPython. It is named such for two reasons: |
|
13 | IPython is the next generation of IPython. It is named such for two reasons: | |
14 |
|
14 | |||
15 | - Eventually, IPython will become IPython version 1.0. |
|
15 | - Eventually, IPython will become IPython version 1.0. | |
16 | - This new code base needs to be able to co-exist with the existing IPython until |
|
16 | - This new code base needs to be able to co-exist with the existing IPython until | |
17 | it is a full replacement for it. Thus we needed a different name. We couldn't |
|
17 | it is a full replacement for it. Thus we needed a different name. We couldn't | |
18 | use ``ipython`` (lowercase) as some files systems are case insensitive. |
|
18 | use ``ipython`` (lowercase) as some files systems are case insensitive. | |
19 |
|
19 | |||
20 | There are two, no three, main goals of the IPython effort: |
|
20 | There are two, no three, main goals of the IPython effort: | |
21 |
|
21 | |||
22 | 1. Clean up the existing codebase and write lots of tests. |
|
22 | 1. Clean up the existing codebase and write lots of tests. | |
23 | 2. Separate the core functionality of IPython from the terminal to enable IPython |
|
23 | 2. Separate the core functionality of IPython from the terminal to enable IPython | |
24 | to be used from within a variety of GUI applications. |
|
24 | to be used from within a variety of GUI applications. | |
25 | 3. Implement a system for interactive parallel computing. |
|
25 | 3. Implement a system for interactive parallel computing. | |
26 |
|
26 | |||
27 | While the third goal may seem a bit unrelated to the main focus of IPython, it turns |
|
27 | While the third goal may seem a bit unrelated to the main focus of IPython, it turns | |
28 | out that the technologies required for this goal are nearly identical with those |
|
28 | out that the technologies required for this goal are nearly identical with those | |
29 | required for goal two. This is the main reason the interactive parallel computing |
|
29 | required for goal two. This is the main reason the interactive parallel computing | |
30 | capabilities are being put into IPython proper. Currently the third of these goals is |
|
30 | capabilities are being put into IPython proper. Currently the third of these goals is | |
31 | furthest along. |
|
31 | furthest along. | |
32 |
|
32 | |||
33 | This document describes IPython from the perspective of developers. |
|
33 | This document describes IPython from the perspective of developers. | |
34 |
|
34 | |||
35 |
|
35 | |||
36 | Project organization |
|
36 | Project organization | |
37 | ==================== |
|
37 | ==================== | |
38 |
|
38 | |||
39 | Subpackages |
|
39 | Subpackages | |
40 | ----------- |
|
40 | ----------- | |
41 |
|
41 | |||
42 | IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own: |
|
42 | IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own: | |
43 |
|
43 | |||
44 | - **Dependencies**. One of the most important things to keep in mind in |
|
44 | - **Dependencies**. One of the most important things to keep in mind in | |
45 | partitioning code amongst subpackages, is that they should be used to cleanly |
|
45 | partitioning code amongst subpackages, is that they should be used to cleanly | |
46 | encapsulate dependencies. |
|
46 | encapsulate dependencies. | |
47 | - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that |
|
47 | - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that | |
48 | contains all of the tests for that package. For information about writing tests |
|
48 | contains all of the tests for that package. For information about writing tests | |
49 | for IPython, see the `Testing System`_ section of this document. |
|
49 | for IPython, see the `Testing System`_ section of this document. | |
50 | - **Configuration**. Each subpackage should have its own ``config`` subdirectory |
|
50 | - **Configuration**. Each subpackage should have its own ``config`` subdirectory | |
51 | that contains the configuration information for the components of the |
|
51 | that contains the configuration information for the components of the | |
52 | subpackage. For information about how the IPython configuration system |
|
52 | subpackage. For information about how the IPython configuration system | |
53 | works, see the `Configuration System`_ section of this document. |
|
53 | works, see the `Configuration System`_ section of this document. | |
54 | - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that |
|
54 | - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that | |
55 | contains all of the command line scripts associated with the subpackage. |
|
55 | contains all of the command line scripts associated with the subpackage. | |
56 |
|
56 | |||
57 | Installation and dependencies |
|
57 | Installation and dependencies | |
58 | ----------------------------- |
|
58 | ----------------------------- | |
59 |
|
59 | |||
60 | IPython will not use `setuptools`_ for installation. Instead, we will use standard |
|
60 | IPython will not use `setuptools`_ for installation. Instead, we will use standard | |
61 | ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice |
|
61 | ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice | |
62 | features that `setuptools`_ has (like namespace packages), the current implementation |
|
62 | features that `setuptools`_ has (like namespace packages), the current implementation | |
63 | of `setuptools`_ has performance problems, particularly on shared file systems. In |
|
63 | of `setuptools`_ has performance problems, particularly on shared file systems. In | |
64 | particular, when Python packages are installed on NSF file systems, import times |
|
64 | particular, when Python packages are installed on NSF file systems, import times | |
65 | become much too long (up towards 10 seconds). |
|
65 | become much too long (up towards 10 seconds). | |
66 |
|
66 | |||
67 | Because IPython is being used extensively in the context of high performance |
|
67 | Because IPython is being used extensively in the context of high performance | |
68 | computing, where performance is critical but shared file systems are common, we feel |
|
68 | computing, where performance is critical but shared file systems are common, we feel | |
69 | these performance hits are not acceptable. Thus, until the performance problems |
|
69 | these performance hits are not acceptable. Thus, until the performance problems | |
70 | associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We |
|
70 | associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We | |
71 | are hopeful that these problems will be addressed and that we will eventually begin |
|
71 | are hopeful that these problems will be addressed and that we will eventually begin | |
72 | using `setuptools`_. Because of this, we are trying to organize IPython in a way that |
|
72 | using `setuptools`_. Because of this, we are trying to organize IPython in a way that | |
73 | will make the eventual transition to `setuptools`_ as painless as possible. |
|
73 | will make the eventual transition to `setuptools`_ as painless as possible. | |
74 |
|
74 | |||
75 | Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows: |
|
75 | Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows: | |
76 |
|
76 | |||
77 | - Distinguish between required and optional dependencies. However, the required |
|
77 | - Distinguish between required and optional dependencies. However, the required | |
78 | dependencies for IPython should be only the Python standard library. |
|
78 | dependencies for IPython should be only the Python standard library. | |
79 | - Upon installation check to see which optional dependencies are present and tell |
|
79 | - Upon installation check to see which optional dependencies are present and tell | |
80 | the user which parts of IPython need which optional dependencies. |
|
80 | the user which parts of IPython need which optional dependencies. | |
81 |
|
81 | |||
82 | It is absolutely critical that each subpackage of IPython has a clearly specified set |
|
82 | It is absolutely critical that each subpackage of IPython has a clearly specified set | |
83 | of dependencies and that dependencies are not carelessly inherited from other IPython |
|
83 | of dependencies and that dependencies are not carelessly inherited from other IPython | |
84 | subpackages. Furthermore, tests that have certain dependencies should not fail if |
|
84 | subpackages. Furthermore, tests that have certain dependencies should not fail if | |
85 | those dependencies are not present. Instead they should be skipped and print a |
|
85 | those dependencies are not present. Instead they should be skipped and print a | |
86 | message. |
|
86 | message. | |
87 |
|
87 | |||
88 | .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools |
|
88 | .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools | |
89 | .. _distutils: http://docs.python.org/lib/module-distutils.html |
|
89 | .. _distutils: http://docs.python.org/lib/module-distutils.html | |
90 | .. _Matplotlib: http://matplotlib.sourceforge.net/ |
|
90 | .. _Matplotlib: http://matplotlib.sourceforge.net/ | |
91 |
|
91 | |||
92 | Specific subpackages |
|
92 | Specific subpackages | |
93 | -------------------- |
|
93 | -------------------- | |
94 |
|
94 | |||
95 | ``core`` |
|
95 | ``core`` | |
96 | This is the core functionality of IPython that is independent of the |
|
96 | This is the core functionality of IPython that is independent of the | |
97 | terminal, network and GUIs. Most of the code that is in the current |
|
97 | terminal, network and GUIs. Most of the code that is in the current | |
98 | IPython trunk will be refactored, cleaned up and moved here. |
|
98 | IPython trunk will be refactored, cleaned up and moved here. | |
99 |
|
99 | |||
100 | ``kernel`` |
|
100 | ``kernel`` | |
101 | The enables the IPython core to be expose to a the network. This is |
|
101 | The enables the IPython core to be expose to a the network. This is | |
102 | also where all of the parallel computing capabilities are to be found. |
|
102 | also where all of the parallel computing capabilities are to be found. | |
103 |
|
103 | |||
104 | ``config`` |
|
104 | ``config`` | |
105 | The configuration package used by IPython. |
|
105 | The configuration package used by IPython. | |
106 |
|
106 | |||
107 | ``frontends`` |
|
107 | ``frontends`` | |
108 | The various frontends for IPython. A frontend is the end-user application |
|
108 | The various frontends for IPython. A frontend is the end-user application | |
109 | that exposes the capabilities of IPython to the user. The most basic frontend |
|
109 | that exposes the capabilities of IPython to the user. The most basic frontend | |
110 | will simply be a terminal based application that looks just like today 's |
|
110 | will simply be a terminal based application that looks just like today 's | |
111 | IPython. Other frontends will likely be more powerful and based on GUI toolkits. |
|
111 | IPython. Other frontends will likely be more powerful and based on GUI toolkits. | |
112 |
|
112 | |||
113 | ``notebook`` |
|
113 | ``notebook`` | |
114 | An application that allows users to work with IPython notebooks. |
|
114 | An application that allows users to work with IPython notebooks. | |
115 |
|
115 | |||
116 | ``tools`` |
|
116 | ``tools`` | |
117 | This is where general utilities go. |
|
117 | This is where general utilities go. | |
118 |
|
118 | |||
119 |
|
119 | |||
120 | Version control |
|
120 | Version control | |
121 | =============== |
|
121 | =============== | |
122 |
|
122 | |||
123 | In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people |
|
123 | In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people | |
124 | to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython |
|
124 | to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython | |
125 | development. First, you should install Bazaar. After you have done that, make |
|
125 | development. First, you should install Bazaar. After you have done that, make | |
126 | sure that it is working by getting the latest main branch of IPython:: |
|
126 | sure that it is working by getting the latest main branch of IPython:: | |
127 |
|
127 | |||
128 | $ bzr branch lp:ipython |
|
128 | $ bzr branch lp:ipython | |
129 |
|
129 | |||
130 | Now you can create a new branch for you to do your work in:: |
|
130 | Now you can create a new branch for you to do your work in:: | |
131 |
|
131 | |||
132 | $ bzr branch ipython ipython-mybranch |
|
132 | $ bzr branch ipython ipython-mybranch | |
133 |
|
133 | |||
134 | The typical work cycle in this branch will be to make changes in `ipython-mybranch` |
|
134 | The typical work cycle in this branch will be to make changes in `ipython-mybranch` | |
135 | and then commit those changes using the commit command:: |
|
135 | and then commit those changes using the commit command:: | |
136 |
|
136 | |||
137 | $ ...do work in ipython-mybranch... |
|
137 | $ ...do work in ipython-mybranch... | |
138 | $ bzr ci -m "the commit message goes here" |
|
138 | $ bzr ci -m "the commit message goes here" | |
139 |
|
139 | |||
140 | Please note that since we now don't use an old-style linear ChangeLog |
|
140 | Please note that since we now don't use an old-style linear ChangeLog | |
141 | (that tends to cause problems with distributed version control |
|
141 | (that tends to cause problems with distributed version control | |
142 | systems), you should ensure that your log messages are reasonably |
|
142 | systems), you should ensure that your log messages are reasonably | |
143 | detailed. Use a docstring-like approach in the commit messages |
|
143 | detailed. Use a docstring-like approach in the commit messages | |
144 | (including the second line being left *blank*):: |
|
144 | (including the second line being left *blank*):: | |
145 |
|
145 | |||
146 | Single line summary of changes being committed. |
|
146 | Single line summary of changes being committed. | |
147 |
|
147 | |||
148 | - more details when warranted ... |
|
148 | - more details when warranted ... | |
149 | - including crediting outside contributors if they sent the |
|
149 | - including crediting outside contributors if they sent the | |
150 | code/bug/idea! |
|
150 | code/bug/idea! | |
151 |
|
151 | |||
152 | If we couple this with a policy of making single commits for each |
|
152 | If we couple this with a policy of making single commits for each | |
153 | reasonably atomic change, the bzr log should give an excellent view of |
|
153 | reasonably atomic change, the bzr log should give an excellent view of | |
154 | the project, and the `--short` log option becomes a nice summary. |
|
154 | the project, and the `--short` log option becomes a nice summary. | |
155 |
|
155 | |||
156 | While working with this branch, it is a good idea to merge in changes that have been |
|
156 | While working with this branch, it is a good idea to merge in changes that have been | |
157 | made upstream in the parent branch. This can be done by doing:: |
|
157 | made upstream in the parent branch. This can be done by doing:: | |
158 |
|
158 | |||
159 | $ bzr pull |
|
159 | $ bzr pull | |
160 |
|
160 | |||
161 | If this command shows that the branches have diverged, then you should do a merge |
|
161 | If this command shows that the branches have diverged, then you should do a merge | |
162 | instead:: |
|
162 | instead:: | |
163 |
|
163 | |||
164 | $ bzr merge lp:ipython |
|
164 | $ bzr merge lp:ipython | |
165 |
|
165 | |||
166 | If you want others to be able to see your branch, you can create an account with |
|
166 | If you want others to be able to see your branch, you can create an account with | |
167 | launchpad and push the branch to your own workspace:: |
|
167 | launchpad and push the branch to your own workspace:: | |
168 |
|
168 | |||
169 | $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch |
|
169 | $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch | |
170 |
|
170 | |||
171 | Finally, once the work in your branch is done, you can merge your changes back into |
|
171 | Finally, once the work in your branch is done, you can merge your changes back into | |
172 | the `ipython` branch by using merge:: |
|
172 | the `ipython` branch by using merge:: | |
173 |
|
173 | |||
174 | $ cd ipython |
|
174 | $ cd ipython | |
175 | $ merge ../ipython-mybranch |
|
175 | $ merge ../ipython-mybranch | |
176 | [resolve any conflicts] |
|
176 | [resolve any conflicts] | |
177 | $ bzr ci -m "Fixing that bug" |
|
177 | $ bzr ci -m "Fixing that bug" | |
178 | $ bzr push |
|
178 | $ bzr push | |
179 |
|
179 | |||
180 | But this will require you to have write permissions to the `ipython` branch. It you don't |
|
180 | But this will require you to have write permissions to the `ipython` branch. It you don't | |
181 | you can tell one of the IPython devs about your branch and they can do the merge for you. |
|
181 | you can tell one of the IPython devs about your branch and they can do the merge for you. | |
182 |
|
182 | |||
183 | More information about Bazaar workflows can be found `here`__. |
|
183 | More information about Bazaar workflows can be found `here`__. | |
184 |
|
184 | |||
185 | .. __: http://subversion.tigris.org/ |
|
185 | .. __: http://subversion.tigris.org/ | |
186 | .. __: http://bazaar-vcs.org/ |
|
186 | .. __: http://bazaar-vcs.org/ | |
187 | .. __: http://www.launchpad.net/ipython |
|
187 | .. __: http://www.launchpad.net/ipython | |
188 | .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html |
|
188 | .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html | |
189 |
|
189 | |||
190 | Documentation |
|
190 | Documentation | |
191 | ============= |
|
191 | ============= | |
192 |
|
192 | |||
193 | Standalone documentation |
|
193 | Standalone documentation | |
194 | ------------------------ |
|
194 | ------------------------ | |
195 |
|
195 | |||
196 | All standalone documentation should be written in plain text (``.txt``) files using |
|
196 | All standalone documentation should be written in plain text (``.txt``) files using | |
197 | `reStructuredText`_ for markup and formatting. All such documentation should be placed |
|
197 | `reStructuredText`_ for markup and formatting. All such documentation should be placed | |
198 | in the top level directory ``docs`` of the IPython source tree. Or, when appropriate, |
|
198 | in the top level directory ``docs`` of the IPython source tree. Or, when appropriate, | |
199 | a suitably named subdirectory should be used. The documentation in this location will |
|
199 | a suitably named subdirectory should be used. The documentation in this location will | |
200 | serve as the main source for IPython documentation and all existing documentation |
|
200 | serve as the main source for IPython documentation and all existing documentation | |
201 | should be converted to this format. |
|
201 | should be converted to this format. | |
202 |
|
202 | |||
203 | In the future, the text files in the ``docs`` directory will be used to generate all |
|
203 | In the future, the text files in the ``docs`` directory will be used to generate all | |
204 | forms of documentation for IPython. This include documentation on the IPython website |
|
204 | forms of documentation for IPython. This include documentation on the IPython website | |
205 | as well as *pdf* documentation. |
|
205 | as well as *pdf* documentation. | |
206 |
|
206 | |||
207 | .. _reStructuredText: http://docutils.sourceforge.net/rst.html |
|
207 | .. _reStructuredText: http://docutils.sourceforge.net/rst.html | |
208 |
|
208 | |||
209 | Docstring format |
|
209 | Docstring format | |
210 | ---------------- |
|
210 | ---------------- | |
211 |
|
211 | |||
212 | Good docstrings are very important. All new code will use `Epydoc`_ for generating API |
|
212 | Good docstrings are very important. All new code will use `Epydoc`_ for generating API | |
213 | docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use |
|
213 | docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use | |
214 | `reStructuredText`_ for markup and formatting, since it is understood by a wide |
|
214 | `reStructuredText`_ for markup and formatting, since it is understood by a wide | |
215 | variety of tools. This means that if in the future we have any reason to change from |
|
215 | variety of tools. This means that if in the future we have any reason to change from | |
216 | `Epydoc`_ to something else, we'll have fewer transition pains. |
|
216 | `Epydoc`_ to something else, we'll have fewer transition pains. | |
217 |
|
217 | |||
218 | Details about using `reStructuredText`_ for docstrings can be found `here |
|
218 | Details about using `reStructuredText`_ for docstrings can be found `here | |
219 | <http://epydoc.sourceforge.net/manual-othermarkup.html>`_. |
|
219 | <http://epydoc.sourceforge.net/manual-othermarkup.html>`_. | |
220 |
|
220 | |||
221 | .. _Epydoc: http://epydoc.sourceforge.net/ |
|
221 | .. _Epydoc: http://epydoc.sourceforge.net/ | |
222 |
|
222 | |||
223 | Additional PEPs of interest regarding documentation of code: |
|
223 | Additional PEPs of interest regarding documentation of code: | |
224 |
|
224 | |||
225 | - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_ |
|
225 | - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_ | |
226 | - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_ |
|
226 | - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_ | |
227 | - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_ |
|
227 | - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_ | |
228 |
|
228 | |||
229 |
|
229 | |||
230 | Coding conventions |
|
230 | Coding conventions | |
231 | ================== |
|
231 | ================== | |
232 |
|
232 | |||
233 | General |
|
233 | General | |
234 | ------- |
|
234 | ------- | |
235 |
|
235 | |||
236 | In general, we'll try to follow the standard Python style conventions as described here: |
|
236 | In general, we'll try to follow the standard Python style conventions as described here: | |
237 |
|
237 | |||
238 | - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_ |
|
238 | - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_ | |
239 |
|
239 | |||
240 |
|
240 | |||
241 | Other comments: |
|
241 | Other comments: | |
242 |
|
242 | |||
243 | - In a large file, top level classes and functions should be |
|
243 | - In a large file, top level classes and functions should be | |
244 | separated by 2-3 lines to make it easier to separate them visually. |
|
244 | separated by 2-3 lines to make it easier to separate them visually. | |
245 | - Use 4 spaces for indentation. |
|
245 | - Use 4 spaces for indentation. | |
246 | - Keep the ordering of methods the same in classes that have the same |
|
246 | - Keep the ordering of methods the same in classes that have the same | |
247 | methods. This is particularly true for classes that implement |
|
247 | methods. This is particularly true for classes that implement | |
248 | similar interfaces and for interfaces that are similar. |
|
248 | similar interfaces and for interfaces that are similar. | |
249 |
|
249 | |||
250 | Naming conventions |
|
250 | Naming conventions | |
251 | ------------------ |
|
251 | ------------------ | |
252 |
|
252 | |||
253 | In terms of naming conventions, we'll follow the guidelines from the `Style Guide for |
|
253 | In terms of naming conventions, we'll follow the guidelines from the `Style Guide for | |
254 | Python Code`_. |
|
254 | Python Code`_. | |
255 |
|
255 | |||
256 | For all new IPython code (and much existing code is being refactored), we'll use: |
|
256 | For all new IPython code (and much existing code is being refactored), we'll use: | |
257 |
|
257 | |||
258 | - All ``lowercase`` module names. |
|
258 | - All ``lowercase`` module names. | |
259 |
|
259 | |||
260 | - ``CamelCase`` for class names. |
|
260 | - ``CamelCase`` for class names. | |
261 |
|
261 | |||
262 | - ``lowercase_with_underscores`` for methods, functions, variables and attributes. |
|
262 | - ``lowercase_with_underscores`` for methods, functions, variables and attributes. | |
263 |
|
263 | |||
264 | This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new |
|
264 | This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new | |
265 | convention, providing shadow names for backward compatibility in public interfaces. |
|
265 | convention, providing shadow names for backward compatibility in public interfaces. | |
266 |
|
266 | |||
267 | There are, however, some important exceptions to these rules. In some cases, IPython |
|
267 | There are, however, some important exceptions to these rules. In some cases, IPython | |
268 | code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy: |
|
268 | code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy: | |
269 |
|
269 | |||
270 | - If you are subclassing a class that uses different conventions, use its |
|
270 | - If you are subclassing a class that uses different conventions, use its | |
271 | naming conventions throughout your subclass. Thus, if you are creating a |
|
271 | naming conventions throughout your subclass. Thus, if you are creating a | |
272 | Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.`` |
|
272 | Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.`` | |
273 |
|
273 | |||
274 | - All IPython's official interfaces should use our conventions. In some cases |
|
274 | - All IPython's official interfaces should use our conventions. In some cases | |
275 | this will mean that you need to provide shadow names (first implement ``fooBar`` |
|
275 | this will mean that you need to provide shadow names (first implement ``fooBar`` | |
276 | and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it |
|
276 | and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it | |
277 | will probably be necessary at times. But, please use this sparingly! |
|
277 | will probably be necessary at times. But, please use this sparingly! | |
278 |
|
278 | |||
279 | Implementation-specific *private* methods will use ``_single_underscore_prefix``. |
|
279 | Implementation-specific *private* methods will use ``_single_underscore_prefix``. | |
280 | Names with a leading double underscore will *only* be used in special cases, as they |
|
280 | Names with a leading double underscore will *only* be used in special cases, as they | |
281 | makes subclassing difficult (such names are not easily seen by child classes). |
|
281 | makes subclassing difficult (such names are not easily seen by child classes). | |
282 |
|
282 | |||
283 | Occasionally some run-in lowercase names are used, but mostly for very short names or |
|
283 | Occasionally some run-in lowercase names are used, but mostly for very short names or | |
284 | where we are implementing methods very similar to existing ones in a base class (like |
|
284 | where we are implementing methods very similar to existing ones in a base class (like | |
285 | ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent). |
|
285 | ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent). | |
286 |
|
286 | |||
287 | The old IPython codebase has a big mix of classes and modules prefixed with an |
|
287 | The old IPython codebase has a big mix of classes and modules prefixed with an | |
288 | explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as |
|
288 | explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as | |
289 | namespaces offer cleaner prefixing. The only case where this approach is justified is |
|
289 | namespaces offer cleaner prefixing. The only case where this approach is justified is | |
290 | for classes which are expected to be imported into external namespaces and a very |
|
290 | for classes which are expected to be imported into external namespaces and a very | |
291 | generic name (like Shell) is too likely to clash with something else. We'll need to |
|
291 | generic name (like Shell) is too likely to clash with something else. We'll need to | |
292 | revisit this issue as we clean up and refactor the code, but in general we should |
|
292 | revisit this issue as we clean up and refactor the code, but in general we should | |
293 | remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix |
|
293 | remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix | |
294 | seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred. |
|
294 | seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred. | |
295 |
|
295 | |||
296 | .. _devel_testing: |
|
296 | .. _devel_testing: | |
297 |
|
297 | |||
298 | Testing system |
|
298 | Testing system | |
299 | ============== |
|
299 | ============== | |
300 |
|
300 | |||
301 | It is extremely important that all code contributed to IPython has tests. Tests should |
|
301 | It is extremely important that all code contributed to IPython has tests. Tests should | |
302 | be written as unittests, doctests or as entities that the `Nose`_ testing package will |
|
302 | be written as unittests, doctests or as entities that the `Nose`_ testing package will | |
303 | find. Regardless of how the tests are written, we will use `Nose`_ for discovering and |
|
303 | find. Regardless of how the tests are written, we will use `Nose`_ for discovering and | |
304 | running the tests. `Nose`_ will be required to run the IPython test suite, but will |
|
304 | running the tests. `Nose`_ will be required to run the IPython test suite, but will | |
305 | not be required to simply use IPython. |
|
305 | not be required to simply use IPython. | |
306 |
|
306 | |||
307 | .. _Nose: http://code.google.com/p/python-nose/ |
|
307 | .. _Nose: http://code.google.com/p/python-nose/ | |
308 |
|
308 | |||
309 | Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class |
|
309 | Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class | |
310 | that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to |
|
310 | that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to | |
311 | run the tests and the twisted reactor will be handled correctly. |
|
311 | run the tests and the twisted reactor will be handled correctly. | |
312 |
|
312 | |||
313 | .. __: http://www.twistedmatrix.com |
|
313 | .. __: http://www.twistedmatrix.com | |
314 |
|
314 | |||
315 | Each subpackage in IPython should have its own ``tests`` directory that contains all |
|
315 | Each subpackage in IPython should have its own ``tests`` directory that contains all | |
316 | of the tests for that subpackage. This allows each subpackage to be self-contained. If |
|
316 | of the tests for that subpackage. This allows each subpackage to be self-contained. If | |
317 | a subpackage has any dependencies beyond the Python standard library, the tests for |
|
317 | a subpackage has any dependencies beyond the Python standard library, the tests for | |
318 | that subpackage should be skipped if the dependencies are not found. This is very |
|
318 | that subpackage should be skipped if the dependencies are not found. This is very | |
319 | important so users don't get tests failing simply because they don't have dependencies. |
|
319 | important so users don't get tests failing simply because they don't have dependencies. | |
320 |
|
320 | |||
321 | We also need to look into use Noses ability to tag tests to allow a more modular |
|
321 | We also need to look into use Noses ability to tag tests to allow a more modular | |
322 | approach of running tests. |
|
322 | approach of running tests. | |
323 |
|
323 | |||
324 | .. _devel_config: |
|
324 | .. _devel_config: | |
325 |
|
325 | |||
326 | Configuration system |
|
326 | Configuration system | |
327 | ==================== |
|
327 | ==================== | |
328 |
|
328 | |||
329 | IPython uses `.ini`_ files for configuration purposes. This represents a huge |
|
329 | IPython uses `.ini`_ files for configuration purposes. This represents a huge | |
330 | improvement over the configuration system used in IPython. IPython works with these |
|
330 | improvement over the configuration system used in IPython. IPython works with these | |
331 | files using the `ConfigObj`_ package, which IPython includes as |
|
331 | files using the `ConfigObj`_ package, which IPython includes as | |
332 | ``ipython1/external/configobj.py``. |
|
332 | ``ipython1/external/configobj.py``. | |
333 |
|
333 | |||
334 | Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython |
|
334 | Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython | |
335 | should contain a ``config`` subdirectory that contains all of the configuration |
|
335 | should contain a ``config`` subdirectory that contains all of the configuration | |
336 | information for the subpackage. To see how configuration information is defined (along |
|
336 | information for the subpackage. To see how configuration information is defined (along | |
337 | with defaults) see at the examples in ``ipython1/kernel/config`` and |
|
337 | with defaults) see at the examples in ``ipython1/kernel/config`` and | |
338 | ``ipython1/core/config``. Likewise, to see how the configuration information is used, |
|
338 | ``ipython1/core/config``. Likewise, to see how the configuration information is used, | |
339 | see examples in ``ipython1/kernel/scripts/ipengine.py``. |
|
339 | see examples in ``ipython1/kernel/scripts/ipengine.py``. | |
340 |
|
340 | |||
341 | Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are |
|
341 | Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are | |
342 | calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model. |
|
342 | calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model. | |
343 | We won't actually use `Traits`_, but will implement something similar in pure Python. |
|
343 | We won't actually use `Traits`_, but will implement something similar in pure Python. | |
344 | But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files |
|
344 | But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files | |
345 | underneath the hood. Talk to Fernando if you are interested in working on this part of |
|
345 | underneath the hood. Talk to Fernando if you are interested in working on this part of | |
346 | IPython. The current prototype of ``tconfig`` is located in the IPython sandbox. |
|
346 | IPython. The current prototype of ``tconfig`` is located in the IPython sandbox. | |
347 |
|
347 | |||
348 | .. _.ini: http://docs.python.org/lib/module-ConfigParser.html |
|
348 | .. _.ini: http://docs.python.org/lib/module-ConfigParser.html | |
349 | .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html |
|
349 | .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html | |
350 | .. _Traits: http://code.enthought.com/traits/ |
|
350 | .. _Traits: http://code.enthought.com/traits/ | |
351 |
|
351 | |||
352 | Installation and testing scenarios |
|
352 | Installation and testing scenarios | |
353 | ================================== |
|
353 | ================================== | |
354 |
|
354 | |||
355 | This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies. |
|
355 | This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies. | |
356 |
|
356 | |||
357 | Installation scenarios |
|
357 | Installation scenarios under Linux and OS X | |
358 | ---------------------- |
|
358 | ------------------------------------------- | |
359 |
|
359 | |||
360 | 1. Install from tarball using `python setup.py install`. |
|
360 | 1. Install from tarball using `python setup.py install`. | |
361 | a. With only readline+nose dependencies installed. |
|
361 | a. With only readline+nose dependencies installed. | |
362 | b. With all dependencies installed (readline, zope.interface, |
|
362 | b. With all dependencies installed (readline, zope.interface, | |
363 | Twisted, foolscap, Sphinx, nose, pyOpenSSL). |
|
363 | Twisted, foolscap, Sphinx, nose, pyOpenSSL). | |
364 | 2. Install using easy_install. |
|
364 | 2. Install using easy_install. | |
365 | a. With only readline+nose dependencies installed. |
|
365 | a. With only readline+nose dependencies installed. | |
366 | i. Default dependencies: `easy_install ipython-0.9.beta3-py2.5.egg` |
|
366 | i. Default dependencies: `easy_install ipython-0.9.beta3-py2.5.egg` | |
367 | ii. Optional dependency sets: `easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]` |
|
367 | ii. Optional dependency sets: `easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]` | |
368 | b. With all dependencies already installed. |
|
368 | b. With all dependencies already installed. | |
369 |
|
369 | |||
|
370 | Installation scenarios under Win32 | |||
|
371 | ---------------------------------- | |||
|
372 | ||||
|
373 | 1. Install everything from .exe installers | |||
|
374 | 2. easy_install? | |||
|
375 | ||||
370 |
|
376 | |||
371 | Tests to run for these scenarios |
|
377 | Tests to run for these scenarios | |
372 | -------------------------------- |
|
378 | -------------------------------- | |
373 |
|
379 | |||
374 | 1. Run the full test suite. |
|
380 | 1. Run the full test suite. | |
375 | 2. Start a controller and engines and try a few things by hand. |
|
381 | 2. Start a controller and engines and try a few things by hand. | |
376 | a. Using ipcluster. |
|
382 | a. Using ipcluster. | |
377 | b. Using ipcontroller/ipengine by hand. |
|
383 | b. Using ipcontroller/ipengine by hand. | |
378 | 3. Run a few of the parallel examples. |
|
384 | 3. Run a few of the parallel examples. | |
379 | 4. Try the kernel with and without security with and without PyOpenSSL |
|
385 | 4. Try the kernel with and without security with and without PyOpenSSL | |
380 | installed. |
|
386 | installed. | |
381 | 5. Beat on the IPython terminal a bunch. |
|
387 | 5. Beat on the IPython terminal a bunch. | |
382 | 6. Make sure that furl files are being put in proper locations. |
|
388 | 6. Make sure that furl files are being put in proper locations. | |
383 |
|
389 | |||
384 |
|
390 | |||
385 |
|
391 | |||
386 |
|
392 | |||
387 |
|
393 | |||
388 |
|
394 | |||
389 |
|
395 | |||
390 |
|
396 | |||
391 |
|
397 |
@@ -1,179 +1,178 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # -*- coding: utf-8 -*- |
|
2 | # -*- coding: utf-8 -*- | |
3 | """Setup script for IPython. |
|
3 | """Setup script for IPython. | |
4 |
|
4 | |||
5 | Under Posix environments it works like a typical setup.py script. |
|
5 | Under Posix environments it works like a typical setup.py script. | |
6 | Under Windows, the command sdist is not supported, since IPython |
|
6 | Under Windows, the command sdist is not supported, since IPython | |
7 | requires utilities which are not available under Windows.""" |
|
7 | requires utilities which are not available under Windows.""" | |
8 |
|
8 | |||
9 | #------------------------------------------------------------------------------- |
|
9 | #------------------------------------------------------------------------------- | |
10 | # Copyright (C) 2008 The IPython Development Team |
|
10 | # Copyright (C) 2008 The IPython Development Team | |
11 | # |
|
11 | # | |
12 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
13 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
14 | #------------------------------------------------------------------------------- |
|
14 | #------------------------------------------------------------------------------- | |
15 |
|
15 | |||
16 | #------------------------------------------------------------------------------- |
|
16 | #------------------------------------------------------------------------------- | |
17 | # Imports |
|
17 | # Imports | |
18 | #------------------------------------------------------------------------------- |
|
18 | #------------------------------------------------------------------------------- | |
19 |
|
19 | |||
20 | # Stdlib imports |
|
20 | # Stdlib imports | |
21 | import os |
|
21 | import os | |
22 | import sys |
|
22 | import sys | |
23 |
|
23 | |||
24 | from glob import glob |
|
24 | from glob import glob | |
25 |
|
25 | |||
26 | # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly |
|
26 | # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly | |
27 | # update it when the contents of directories change. |
|
27 | # update it when the contents of directories change. | |
28 | if os.path.exists('MANIFEST'): os.remove('MANIFEST') |
|
28 | if os.path.exists('MANIFEST'): os.remove('MANIFEST') | |
29 |
|
29 | |||
30 | from distutils.core import setup |
|
30 | from distutils.core import setup | |
31 |
|
31 | |||
32 | # Local imports |
|
32 | # Local imports | |
33 | from IPython.genutils import target_update |
|
33 | from IPython.genutils import target_update | |
34 |
|
34 | |||
35 | from setupbase import ( |
|
35 | from setupbase import ( | |
36 | setup_args, |
|
36 | setup_args, | |
37 | find_packages, |
|
37 | find_packages, | |
38 | find_package_data, |
|
38 | find_package_data, | |
39 | find_scripts, |
|
39 | find_scripts, | |
40 | find_data_files, |
|
40 | find_data_files, | |
41 | check_for_dependencies |
|
41 | check_for_dependencies | |
42 | ) |
|
42 | ) | |
43 |
|
43 | |||
44 | isfile = os.path.isfile |
|
44 | isfile = os.path.isfile | |
45 |
|
45 | |||
46 | #------------------------------------------------------------------------------- |
|
46 | #------------------------------------------------------------------------------- | |
47 | # Handle OS specific things |
|
47 | # Handle OS specific things | |
48 | #------------------------------------------------------------------------------- |
|
48 | #------------------------------------------------------------------------------- | |
49 |
|
49 | |||
50 | if os.name == 'posix': |
|
50 | if os.name == 'posix': | |
51 | os_name = 'posix' |
|
51 | os_name = 'posix' | |
52 | elif os.name in ['nt','dos']: |
|
52 | elif os.name in ['nt','dos']: | |
53 | os_name = 'windows' |
|
53 | os_name = 'windows' | |
54 | else: |
|
54 | else: | |
55 | print 'Unsupported operating system:',os.name |
|
55 | print 'Unsupported operating system:',os.name | |
56 | sys.exit(1) |
|
56 | sys.exit(1) | |
57 |
|
57 | |||
58 | # Under Windows, 'sdist' has not been supported. Now that the docs build with |
|
58 | # Under Windows, 'sdist' has not been supported. Now that the docs build with | |
59 | # Sphinx it might work, but let's not turn it on until someone confirms that it |
|
59 | # Sphinx it might work, but let's not turn it on until someone confirms that it | |
60 | # actually works. |
|
60 | # actually works. | |
61 | if os_name == 'windows' and 'sdist' in sys.argv: |
|
61 | if os_name == 'windows' and 'sdist' in sys.argv: | |
62 | print 'The sdist command is not available under Windows. Exiting.' |
|
62 | print 'The sdist command is not available under Windows. Exiting.' | |
63 | sys.exit(1) |
|
63 | sys.exit(1) | |
64 |
|
64 | |||
65 | #------------------------------------------------------------------------------- |
|
65 | #------------------------------------------------------------------------------- | |
66 | # Things related to the IPython documentation |
|
66 | # Things related to the IPython documentation | |
67 | #------------------------------------------------------------------------------- |
|
67 | #------------------------------------------------------------------------------- | |
68 |
|
68 | |||
69 | # update the manuals when building a source dist |
|
69 | # update the manuals when building a source dist | |
70 | if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): |
|
70 | if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): | |
71 | import textwrap |
|
71 | import textwrap | |
72 |
|
72 | |||
73 | # List of things to be updated. Each entry is a triplet of args for |
|
73 | # List of things to be updated. Each entry is a triplet of args for | |
74 | # target_update() |
|
74 | # target_update() | |
75 | to_update = [ |
|
75 | to_update = [ | |
76 | # FIXME - Disabled for now: we need to redo an automatic way |
|
76 | # FIXME - Disabled for now: we need to redo an automatic way | |
77 | # of generating the magic info inside the rst. |
|
77 | # of generating the magic info inside the rst. | |
78 | #('docs/magic.tex', |
|
78 | #('docs/magic.tex', | |
79 | #['IPython/Magic.py'], |
|
79 | #['IPython/Magic.py'], | |
80 | #"cd doc && ./update_magic.sh" ), |
|
80 | #"cd doc && ./update_magic.sh" ), | |
81 |
|
81 | |||
82 | ('docs/man/ipython.1.gz', |
|
82 | ('docs/man/ipython.1.gz', | |
83 | ['docs/man/ipython.1'], |
|
83 | ['docs/man/ipython.1'], | |
84 | "cd docs/man && gzip -9c ipython.1 > ipython.1.gz"), |
|
84 | "cd docs/man && gzip -9c ipython.1 > ipython.1.gz"), | |
85 |
|
85 | |||
86 | ('docs/man/pycolor.1.gz', |
|
86 | ('docs/man/pycolor.1.gz', | |
87 | ['docs/man/pycolor.1'], |
|
87 | ['docs/man/pycolor.1'], | |
88 | "cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz"), |
|
88 | "cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz"), | |
89 | ] |
|
89 | ] | |
90 |
|
90 | |||
91 | # Only build the docs is sphinx is present |
|
91 | # Only build the docs is sphinx is present | |
92 | try: |
|
92 | try: | |
93 | import sphinx |
|
93 | import sphinx | |
94 | except ImportError: |
|
94 | except ImportError: | |
95 | pass |
|
95 | pass | |
96 | else: |
|
96 | else: | |
97 | pass |
|
97 | pass | |
98 | # BEG: This is disabled as I am not sure what to depend on. |
|
98 | # BEG: This is disabled as I am not sure what to depend on. | |
99 | # I actually don't think we should be automatically building |
|
99 | # I actually don't think we should be automatically building | |
100 | # the docs for people. |
|
100 | # the docs for people. | |
101 | # The do_sphinx scripts builds html and pdf, so just one |
|
101 | # The do_sphinx scripts builds html and pdf, so just one | |
102 | # target is enough to cover all manual generation |
|
102 | # target is enough to cover all manual generation | |
103 | # to_update.append( |
|
103 | # to_update.append( | |
104 | # ('docs/manual/ipython.pdf', |
|
104 | # ('docs/manual/ipython.pdf', | |
105 | # ['IPython/Release.py','docs/source/ipython.rst'], |
|
105 | # ['IPython/Release.py','docs/source/ipython.rst'], | |
106 | # "cd docs && python do_sphinx.py") |
|
106 | # "cd docs && python do_sphinx.py") | |
107 | # ) |
|
107 | # ) | |
108 |
|
108 | |||
109 | [ target_update(*t) for t in to_update ] |
|
109 | [ target_update(*t) for t in to_update ] | |
110 |
|
110 | |||
111 | # Build the docs |
|
111 | # Build the docs | |
112 | os.system('cd docs && make dist') |
|
112 | os.system('cd docs && make dist') | |
113 |
|
113 | |||
114 | #--------------------------------------------------------------------------- |
|
114 | #--------------------------------------------------------------------------- | |
115 | # Find all the packages, package data, scripts and data_files |
|
115 | # Find all the packages, package data, scripts and data_files | |
116 | #--------------------------------------------------------------------------- |
|
116 | #--------------------------------------------------------------------------- | |
117 |
|
117 | |||
118 | packages = find_packages() |
|
118 | packages = find_packages() | |
119 | package_data = find_package_data() |
|
119 | package_data = find_package_data() | |
120 | scripts = find_scripts() |
|
120 | scripts = find_scripts() | |
121 | data_files = find_data_files() |
|
121 | data_files = find_data_files() | |
122 |
|
122 | |||
123 | #--------------------------------------------------------------------------- |
|
123 | #--------------------------------------------------------------------------- | |
124 | # Handle dependencies and setuptools specific things |
|
124 | # Handle dependencies and setuptools specific things | |
125 | #--------------------------------------------------------------------------- |
|
125 | #--------------------------------------------------------------------------- | |
126 |
|
126 | |||
127 | # This dict is used for passing extra arguments that are setuptools |
|
127 | # This dict is used for passing extra arguments that are setuptools | |
128 | # specific to setup |
|
128 | # specific to setup | |
129 | setuptools_extra_args = {} |
|
129 | setuptools_extra_args = {} | |
130 |
|
130 | |||
131 | if 'setuptools' in sys.modules: |
|
131 | if 'setuptools' in sys.modules: | |
132 | setuptools_extra_args['zip_safe'] = False |
|
132 | setuptools_extra_args['zip_safe'] = False | |
133 | setuptools_extra_args['entry_points'] = { |
|
133 | setuptools_extra_args['entry_points'] = { | |
134 | 'console_scripts': [ |
|
134 | 'console_scripts': [ | |
135 | 'ipython = IPython.ipapi:launch_new_instance', |
|
135 | 'ipython = IPython.ipapi:launch_new_instance', | |
136 | 'pycolor = IPython.PyColorize:main', |
|
136 | 'pycolor = IPython.PyColorize:main', | |
137 | 'ipcontroller = IPython.kernel.scripts.ipcontroller:main', |
|
137 | 'ipcontroller = IPython.kernel.scripts.ipcontroller:main', | |
138 | 'ipengine = IPython.kernel.scripts.ipengine:main', |
|
138 | 'ipengine = IPython.kernel.scripts.ipengine:main', | |
139 | 'ipcluster = IPython.kernel.scripts.ipcluster:main', |
|
139 | 'ipcluster = IPython.kernel.scripts.ipcluster:main', | |
140 | 'ipythonx = IPython.frontend.wx.ipythonx:main' |
|
140 | 'ipythonx = IPython.frontend.wx.ipythonx:main', | |
|
141 | 'iptest = IPython.testing.iptest:main', | |||
141 | ] |
|
142 | ] | |
142 | } |
|
143 | } | |
143 |
setup_args[ |
|
144 | setup_args['extras_require'] = dict( | |
144 | kernel = [ |
|
145 | kernel = [ | |
145 |
|
|
146 | 'zope.interface>=3.4.1', | |
146 |
|
|
147 | 'Twisted>=8.0.1', | |
147 |
|
|
148 | 'foolscap>=0.2.6' | |
148 | ], |
|
149 | ], | |
149 |
doc= |
|
150 | doc='Sphinx>=0.3', | |
150 | test='nose>=0.10.1', |
|
151 | test='nose>=0.10.1', | |
151 |
security= |
|
152 | security='pyOpenSSL>=0.6' | |
152 | ) |
|
153 | ) | |
153 | # Allow setuptools to handle the scripts |
|
154 | # Allow setuptools to handle the scripts | |
154 | scripts = [] |
|
155 | scripts = [] | |
155 | # eggs will lack docs, examples |
|
|||
156 | data_files = [] |
|
|||
157 | else: |
|
156 | else: | |
158 | # package_data of setuptools was introduced to distutils in 2.4 |
|
157 | # package_data of setuptools was introduced to distutils in 2.4 | |
159 | cfgfiles = filter(isfile, glob('IPython/UserConfig/*')) |
|
158 | cfgfiles = filter(isfile, glob('IPython/UserConfig/*')) | |
160 | if sys.version_info < (2,4): |
|
159 | if sys.version_info < (2,4): | |
161 | data_files.append(('lib', 'IPython/UserConfig', cfgfiles)) |
|
160 | data_files.append(('lib', 'IPython/UserConfig', cfgfiles)) | |
162 | # If we are running without setuptools, call this function which will |
|
161 | # If we are running without setuptools, call this function which will | |
163 | # check for dependencies an inform the user what is needed. This is |
|
162 | # check for dependencies an inform the user what is needed. This is | |
164 | # just to make life easy for users. |
|
163 | # just to make life easy for users. | |
165 | check_for_dependencies() |
|
164 | check_for_dependencies() | |
166 |
|
165 | |||
167 |
|
166 | |||
168 | #--------------------------------------------------------------------------- |
|
167 | #--------------------------------------------------------------------------- | |
169 | # Do the actual setup now |
|
168 | # Do the actual setup now | |
170 | #--------------------------------------------------------------------------- |
|
169 | #--------------------------------------------------------------------------- | |
171 |
|
170 | |||
172 | setup_args['packages'] = packages |
|
171 | setup_args['packages'] = packages | |
173 | setup_args['package_data'] = package_data |
|
172 | setup_args['package_data'] = package_data | |
174 | setup_args['scripts'] = scripts |
|
173 | setup_args['scripts'] = scripts | |
175 | setup_args['data_files'] = data_files |
|
174 | setup_args['data_files'] = data_files | |
176 | setup_args.update(setuptools_extra_args) |
|
175 | setup_args.update(setuptools_extra_args) | |
177 |
|
176 | |||
178 | if __name__ == '__main__': |
|
177 | if __name__ == '__main__': | |
179 | setup(**setup_args) |
|
178 | setup(**setup_args) |
@@ -1,275 +1,277 b'' | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 |
|
2 | |||
3 | """ |
|
3 | """ | |
4 | This module defines the things that are used in setup.py for building IPython |
|
4 | This module defines the things that are used in setup.py for building IPython | |
5 |
|
5 | |||
6 | This includes: |
|
6 | This includes: | |
7 |
|
7 | |||
8 | * The basic arguments to setup |
|
8 | * The basic arguments to setup | |
9 | * Functions for finding things like packages, package data, etc. |
|
9 | * Functions for finding things like packages, package data, etc. | |
10 | * A function for checking dependencies. |
|
10 | * A function for checking dependencies. | |
11 | """ |
|
11 | """ | |
12 |
|
12 | |||
13 | __docformat__ = "restructuredtext en" |
|
13 | __docformat__ = "restructuredtext en" | |
14 |
|
14 | |||
15 | #------------------------------------------------------------------------------- |
|
15 | #------------------------------------------------------------------------------- | |
16 | # Copyright (C) 2008 The IPython Development Team |
|
16 | # Copyright (C) 2008 The IPython Development Team | |
17 | # |
|
17 | # | |
18 | # Distributed under the terms of the BSD License. The full license is in |
|
18 | # Distributed under the terms of the BSD License. The full license is in | |
19 | # the file COPYING, distributed as part of this software. |
|
19 | # the file COPYING, distributed as part of this software. | |
20 | #------------------------------------------------------------------------------- |
|
20 | #------------------------------------------------------------------------------- | |
21 |
|
21 | |||
22 | #------------------------------------------------------------------------------- |
|
22 | #------------------------------------------------------------------------------- | |
23 | # Imports |
|
23 | # Imports | |
24 | #------------------------------------------------------------------------------- |
|
24 | #------------------------------------------------------------------------------- | |
25 |
|
25 | |||
26 | import os, sys |
|
26 | import os, sys | |
27 |
|
27 | |||
28 | from glob import glob |
|
28 | from glob import glob | |
29 |
|
29 | |||
30 | from setupext import install_data_ext |
|
30 | from setupext import install_data_ext | |
31 |
|
31 | |||
32 | #------------------------------------------------------------------------------- |
|
32 | #------------------------------------------------------------------------------- | |
33 | # Useful globals and utility functions |
|
33 | # Useful globals and utility functions | |
34 | #------------------------------------------------------------------------------- |
|
34 | #------------------------------------------------------------------------------- | |
35 |
|
35 | |||
36 | # A few handy globals |
|
36 | # A few handy globals | |
37 | isfile = os.path.isfile |
|
37 | isfile = os.path.isfile | |
38 | pjoin = os.path.join |
|
38 | pjoin = os.path.join | |
39 |
|
39 | |||
40 | def oscmd(s): |
|
40 | def oscmd(s): | |
41 | print ">", s |
|
41 | print ">", s | |
42 | os.system(s) |
|
42 | os.system(s) | |
43 |
|
43 | |||
44 | # A little utility we'll need below, since glob() does NOT allow you to do |
|
44 | # A little utility we'll need below, since glob() does NOT allow you to do | |
45 | # exclusion on multiple endings! |
|
45 | # exclusion on multiple endings! | |
46 | def file_doesnt_endwith(test,endings): |
|
46 | def file_doesnt_endwith(test,endings): | |
47 | """Return true if test is a file and its name does NOT end with any |
|
47 | """Return true if test is a file and its name does NOT end with any | |
48 | of the strings listed in endings.""" |
|
48 | of the strings listed in endings.""" | |
49 | if not isfile(test): |
|
49 | if not isfile(test): | |
50 | return False |
|
50 | return False | |
51 | for e in endings: |
|
51 | for e in endings: | |
52 | if test.endswith(e): |
|
52 | if test.endswith(e): | |
53 | return False |
|
53 | return False | |
54 | return True |
|
54 | return True | |
55 |
|
55 | |||
56 | #--------------------------------------------------------------------------- |
|
56 | #--------------------------------------------------------------------------- | |
57 | # Basic project information |
|
57 | # Basic project information | |
58 | #--------------------------------------------------------------------------- |
|
58 | #--------------------------------------------------------------------------- | |
59 |
|
59 | |||
60 | # Release.py contains version, authors, license, url, keywords, etc. |
|
60 | # Release.py contains version, authors, license, url, keywords, etc. | |
61 | execfile(pjoin('IPython','Release.py')) |
|
61 | execfile(pjoin('IPython','Release.py')) | |
62 |
|
62 | |||
63 | # Create a dict with the basic information |
|
63 | # Create a dict with the basic information | |
64 | # This dict is eventually passed to setup after additional keys are added. |
|
64 | # This dict is eventually passed to setup after additional keys are added. | |
65 | setup_args = dict( |
|
65 | setup_args = dict( | |
66 | name = name, |
|
66 | name = name, | |
67 | version = version, |
|
67 | version = version, | |
68 | description = description, |
|
68 | description = description, | |
69 | long_description = long_description, |
|
69 | long_description = long_description, | |
70 | author = author, |
|
70 | author = author, | |
71 | author_email = author_email, |
|
71 | author_email = author_email, | |
72 | url = url, |
|
72 | url = url, | |
73 | download_url = download_url, |
|
73 | download_url = download_url, | |
74 | license = license, |
|
74 | license = license, | |
75 | platforms = platforms, |
|
75 | platforms = platforms, | |
76 | keywords = keywords, |
|
76 | keywords = keywords, | |
77 | cmdclass = {'install_data': install_data_ext}, |
|
77 | cmdclass = {'install_data': install_data_ext}, | |
78 | ) |
|
78 | ) | |
79 |
|
79 | |||
80 |
|
80 | |||
81 | #--------------------------------------------------------------------------- |
|
81 | #--------------------------------------------------------------------------- | |
82 | # Find packages |
|
82 | # Find packages | |
83 | #--------------------------------------------------------------------------- |
|
83 | #--------------------------------------------------------------------------- | |
84 |
|
84 | |||
85 | def add_package(packages,pname,config=False,tests=False,scripts=False, |
|
85 | def add_package(packages,pname,config=False,tests=False,scripts=False, | |
86 | others=None): |
|
86 | others=None): | |
87 | """ |
|
87 | """ | |
88 | Add a package to the list of packages, including certain subpackages. |
|
88 | Add a package to the list of packages, including certain subpackages. | |
89 | """ |
|
89 | """ | |
90 | packages.append('.'.join(['IPython',pname])) |
|
90 | packages.append('.'.join(['IPython',pname])) | |
91 | if config: |
|
91 | if config: | |
92 | packages.append('.'.join(['IPython',pname,'config'])) |
|
92 | packages.append('.'.join(['IPython',pname,'config'])) | |
93 | if tests: |
|
93 | if tests: | |
94 | packages.append('.'.join(['IPython',pname,'tests'])) |
|
94 | packages.append('.'.join(['IPython',pname,'tests'])) | |
95 | if scripts: |
|
95 | if scripts: | |
96 | packages.append('.'.join(['IPython',pname,'scripts'])) |
|
96 | packages.append('.'.join(['IPython',pname,'scripts'])) | |
97 | if others is not None: |
|
97 | if others is not None: | |
98 | for o in others: |
|
98 | for o in others: | |
99 | packages.append('.'.join(['IPython',pname,o])) |
|
99 | packages.append('.'.join(['IPython',pname,o])) | |
100 |
|
100 | |||
101 | def find_packages(): |
|
101 | def find_packages(): | |
102 | """ |
|
102 | """ | |
103 | Find all of IPython's packages. |
|
103 | Find all of IPython's packages. | |
104 | """ |
|
104 | """ | |
105 | packages = ['IPython'] |
|
105 | packages = ['IPython'] | |
106 | add_package(packages, 'config', tests=True) |
|
106 | add_package(packages, 'config', tests=True) | |
107 | add_package(packages , 'Extensions') |
|
107 | add_package(packages , 'Extensions') | |
108 | add_package(packages, 'external') |
|
108 | add_package(packages, 'external') | |
109 | add_package(packages, 'gui') |
|
109 | add_package(packages, 'gui') | |
110 | add_package(packages, 'gui.wx') |
|
110 | add_package(packages, 'gui.wx') | |
111 | add_package(packages, 'frontend', tests=True) |
|
111 | add_package(packages, 'frontend', tests=True) | |
112 | add_package(packages, 'frontend._process') |
|
112 | add_package(packages, 'frontend._process') | |
113 | add_package(packages, 'frontend.wx') |
|
113 | add_package(packages, 'frontend.wx') | |
114 | add_package(packages, 'frontend.cocoa', tests=True) |
|
114 | add_package(packages, 'frontend.cocoa', tests=True) | |
115 | add_package(packages, 'kernel', config=True, tests=True, scripts=True) |
|
115 | add_package(packages, 'kernel', config=True, tests=True, scripts=True) | |
116 | add_package(packages, 'kernel.core', config=True, tests=True) |
|
116 | add_package(packages, 'kernel.core', config=True, tests=True) | |
117 | add_package(packages, 'testing', tests=True) |
|
117 | add_package(packages, 'testing', tests=True) | |
|
118 | add_package(packages, 'testing.plugin', tests=False) | |||
118 | add_package(packages, 'tools', tests=True) |
|
119 | add_package(packages, 'tools', tests=True) | |
119 | add_package(packages, 'UserConfig') |
|
120 | add_package(packages, 'UserConfig') | |
120 | return packages |
|
121 | return packages | |
121 |
|
122 | |||
122 | #--------------------------------------------------------------------------- |
|
123 | #--------------------------------------------------------------------------- | |
123 | # Find package data |
|
124 | # Find package data | |
124 | #--------------------------------------------------------------------------- |
|
125 | #--------------------------------------------------------------------------- | |
125 |
|
126 | |||
126 | def find_package_data(): |
|
127 | def find_package_data(): | |
127 | """ |
|
128 | """ | |
128 | Find IPython's package_data. |
|
129 | Find IPython's package_data. | |
129 | """ |
|
130 | """ | |
130 | # This is not enough for these things to appear in an sdist. |
|
131 | # This is not enough for these things to appear in an sdist. | |
131 | # We need to muck with the MANIFEST to get this to work |
|
132 | # We need to muck with the MANIFEST to get this to work | |
132 | package_data = { |
|
133 | package_data = { | |
133 | 'IPython.UserConfig' : ['*'], |
|
134 | 'IPython.UserConfig' : ['*'], | |
134 | 'IPython.tools.tests' : ['*.txt'], |
|
135 | 'IPython.tools.tests' : ['*.txt'], | |
135 | 'IPython.testing' : ['*.txt'] |
|
136 | 'IPython.testing' : ['*.txt'] | |
136 | } |
|
137 | } | |
137 | return package_data |
|
138 | return package_data | |
138 |
|
139 | |||
139 |
|
140 | |||
140 | #--------------------------------------------------------------------------- |
|
141 | #--------------------------------------------------------------------------- | |
141 | # Find data files |
|
142 | # Find data files | |
142 | #--------------------------------------------------------------------------- |
|
143 | #--------------------------------------------------------------------------- | |
143 |
|
144 | |||
144 | def make_dir_struct(tag,base,out_base): |
|
145 | def make_dir_struct(tag,base,out_base): | |
145 | """Make the directory structure of all files below a starting dir. |
|
146 | """Make the directory structure of all files below a starting dir. | |
146 |
|
147 | |||
147 | This is just a convenience routine to help build a nested directory |
|
148 | This is just a convenience routine to help build a nested directory | |
148 | hierarchy because distutils is too stupid to do this by itself. |
|
149 | hierarchy because distutils is too stupid to do this by itself. | |
149 |
|
150 | |||
150 | XXX - this needs a proper docstring! |
|
151 | XXX - this needs a proper docstring! | |
151 | """ |
|
152 | """ | |
152 |
|
153 | |||
153 | # we'll use these a lot below |
|
154 | # we'll use these a lot below | |
154 | lbase = len(base) |
|
155 | lbase = len(base) | |
155 | pathsep = os.path.sep |
|
156 | pathsep = os.path.sep | |
156 | lpathsep = len(pathsep) |
|
157 | lpathsep = len(pathsep) | |
157 |
|
158 | |||
158 | out = [] |
|
159 | out = [] | |
159 | for (dirpath,dirnames,filenames) in os.walk(base): |
|
160 | for (dirpath,dirnames,filenames) in os.walk(base): | |
160 | # we need to strip out the dirpath from the base to map it to the |
|
161 | # we need to strip out the dirpath from the base to map it to the | |
161 | # output (installation) path. This requires possibly stripping the |
|
162 | # output (installation) path. This requires possibly stripping the | |
162 | # path separator, because otherwise pjoin will not work correctly |
|
163 | # path separator, because otherwise pjoin will not work correctly | |
163 | # (pjoin('foo/','/bar') returns '/bar'). |
|
164 | # (pjoin('foo/','/bar') returns '/bar'). | |
164 |
|
165 | |||
165 | dp_eff = dirpath[lbase:] |
|
166 | dp_eff = dirpath[lbase:] | |
166 | if dp_eff.startswith(pathsep): |
|
167 | if dp_eff.startswith(pathsep): | |
167 | dp_eff = dp_eff[lpathsep:] |
|
168 | dp_eff = dp_eff[lpathsep:] | |
168 | # The output path must be anchored at the out_base marker |
|
169 | # The output path must be anchored at the out_base marker | |
169 | out_path = pjoin(out_base,dp_eff) |
|
170 | out_path = pjoin(out_base,dp_eff) | |
170 | # Now we can generate the final filenames. Since os.walk only produces |
|
171 | # Now we can generate the final filenames. Since os.walk only produces | |
171 | # filenames, we must join back with the dirpath to get full valid file |
|
172 | # filenames, we must join back with the dirpath to get full valid file | |
172 | # paths: |
|
173 | # paths: | |
173 | pfiles = [pjoin(dirpath,f) for f in filenames] |
|
174 | pfiles = [pjoin(dirpath,f) for f in filenames] | |
174 | # Finally, generate the entry we need, which is a triple of (tag,output |
|
175 | # Finally, generate the entry we need, which is a triple of (tag,output | |
175 | # path, files) for use as a data_files parameter in install_data. |
|
176 | # path, files) for use as a data_files parameter in install_data. | |
176 | out.append((tag,out_path,pfiles)) |
|
177 | out.append((tag,out_path,pfiles)) | |
177 |
|
178 | |||
178 | return out |
|
179 | return out | |
179 |
|
180 | |||
180 |
|
181 | |||
181 | def find_data_files(): |
|
182 | def find_data_files(): | |
182 | """ |
|
183 | """ | |
183 | Find IPython's data_files. |
|
184 | Find IPython's data_files. | |
184 |
|
185 | |||
185 | Most of these are docs. |
|
186 | Most of these are docs. | |
186 | """ |
|
187 | """ | |
187 |
|
188 | |||
188 | docdirbase = 'share/doc/ipython' |
|
189 | docdirbase = 'share/doc/ipython' | |
189 | manpagebase = 'share/man/man1' |
|
190 | manpagebase = 'share/man/man1' | |
190 |
|
191 | |||
191 | # Simple file lists can be made by hand |
|
192 | # Simple file lists can be made by hand | |
192 | manpages = filter(isfile, glob('docs/man/*.1.gz')) |
|
193 | manpages = filter(isfile, glob('docs/man/*.1.gz')) | |
193 | igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*')) |
|
194 | igridhelpfiles = filter(isfile, glob('IPython/Extensions/igrid_help.*')) | |
194 |
|
195 | |||
195 | # For nested structures, use the utility above |
|
196 | # For nested structures, use the utility above | |
196 | example_files = make_dir_struct('data','docs/examples', |
|
197 | example_files = make_dir_struct('data','docs/examples', | |
197 | pjoin(docdirbase,'examples')) |
|
198 | pjoin(docdirbase,'examples')) | |
198 | manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual')) |
|
199 | manual_files = make_dir_struct('data','docs/dist',pjoin(docdirbase,'manual')) | |
199 |
|
200 | |||
200 | # And assemble the entire output list |
|
201 | # And assemble the entire output list | |
201 | data_files = [ ('data',manpagebase, manpages), |
|
202 | data_files = [ ('data',manpagebase, manpages), | |
202 | ('data',pjoin(docdirbase,'extensions'),igridhelpfiles), |
|
203 | ('data',pjoin(docdirbase,'extensions'),igridhelpfiles), | |
203 | ] + manual_files + example_files |
|
204 | ] + manual_files + example_files | |
204 |
|
205 | |||
205 | ## import pprint # dbg |
|
206 | ## import pprint # dbg | |
206 | ## print '*'*80 |
|
207 | ## print '*'*80 | |
207 | ## print 'data files' |
|
208 | ## print 'data files' | |
208 | ## pprint.pprint(data_files) |
|
209 | ## pprint.pprint(data_files) | |
209 | ## print '*'*80 |
|
210 | ## print '*'*80 | |
210 |
|
211 | |||
211 | return data_files |
|
212 | return data_files | |
212 |
|
213 | |||
213 | #--------------------------------------------------------------------------- |
|
214 | #--------------------------------------------------------------------------- | |
214 | # Find scripts |
|
215 | # Find scripts | |
215 | #--------------------------------------------------------------------------- |
|
216 | #--------------------------------------------------------------------------- | |
216 |
|
217 | |||
217 | def find_scripts(): |
|
218 | def find_scripts(): | |
218 | """ |
|
219 | """ | |
219 | Find IPython's scripts. |
|
220 | Find IPython's scripts. | |
220 | """ |
|
221 | """ | |
221 | scripts = ['IPython/kernel/scripts/ipengine', |
|
222 | scripts = ['IPython/kernel/scripts/ipengine', | |
222 | 'IPython/kernel/scripts/ipcontroller', |
|
223 | 'IPython/kernel/scripts/ipcontroller', | |
223 |
|
|
224 | 'IPython/kernel/scripts/ipcluster', | |
224 | 'scripts/ipython', |
|
225 | 'scripts/ipython', | |
225 | 'scripts/ipythonx', |
|
226 | 'scripts/ipythonx', | |
226 | 'scripts/pycolor', |
|
227 | 'scripts/pycolor', | |
227 | 'scripts/irunner', |
|
228 | 'scripts/irunner', | |
|
229 | 'scripts/iptest', | |||
228 | ] |
|
230 | ] | |
229 |
|
231 | |||
230 | # Script to be run by the windows binary installer after the default setup |
|
232 | # Script to be run by the windows binary installer after the default setup | |
231 | # routine, to add shortcuts and similar windows-only things. Windows |
|
233 | # routine, to add shortcuts and similar windows-only things. Windows | |
232 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils |
|
234 | # post-install scripts MUST reside in the scripts/ dir, otherwise distutils | |
233 | # doesn't find them. |
|
235 | # doesn't find them. | |
234 | if 'bdist_wininst' in sys.argv: |
|
236 | if 'bdist_wininst' in sys.argv: | |
235 | if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): |
|
237 | if len(sys.argv) > 2 and ('sdist' in sys.argv or 'bdist_rpm' in sys.argv): | |
236 | print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting." |
|
238 | print >> sys.stderr,"ERROR: bdist_wininst must be run alone. Exiting." | |
237 | sys.exit(1) |
|
239 | sys.exit(1) | |
238 | scripts.append('scripts/ipython_win_post_install.py') |
|
240 | scripts.append('scripts/ipython_win_post_install.py') | |
239 |
|
241 | |||
240 | return scripts |
|
242 | return scripts | |
241 |
|
243 | |||
242 | #--------------------------------------------------------------------------- |
|
244 | #--------------------------------------------------------------------------- | |
243 | # Verify all dependencies |
|
245 | # Verify all dependencies | |
244 | #--------------------------------------------------------------------------- |
|
246 | #--------------------------------------------------------------------------- | |
245 |
|
247 | |||
246 | def check_for_dependencies(): |
|
248 | def check_for_dependencies(): | |
247 | """Check for IPython's dependencies. |
|
249 | """Check for IPython's dependencies. | |
248 |
|
250 | |||
249 | This function should NOT be called if running under setuptools! |
|
251 | This function should NOT be called if running under setuptools! | |
250 | """ |
|
252 | """ | |
251 | from setupext.setupext import ( |
|
253 | from setupext.setupext import ( | |
252 | print_line, print_raw, print_status, print_message, |
|
254 | print_line, print_raw, print_status, print_message, | |
253 | check_for_zopeinterface, check_for_twisted, |
|
255 | check_for_zopeinterface, check_for_twisted, | |
254 | check_for_foolscap, check_for_pyopenssl, |
|
256 | check_for_foolscap, check_for_pyopenssl, | |
255 | check_for_sphinx, check_for_pygments, |
|
257 | check_for_sphinx, check_for_pygments, | |
256 | check_for_nose, check_for_pexpect |
|
258 | check_for_nose, check_for_pexpect | |
257 | ) |
|
259 | ) | |
258 | print_line() |
|
260 | print_line() | |
259 | print_raw("BUILDING IPYTHON") |
|
261 | print_raw("BUILDING IPYTHON") | |
260 | print_status('python', sys.version) |
|
262 | print_status('python', sys.version) | |
261 | print_status('platform', sys.platform) |
|
263 | print_status('platform', sys.platform) | |
262 | if sys.platform == 'win32': |
|
264 | if sys.platform == 'win32': | |
263 | print_status('Windows version', sys.getwindowsversion()) |
|
265 | print_status('Windows version', sys.getwindowsversion()) | |
264 |
|
266 | |||
265 | print_raw("") |
|
267 | print_raw("") | |
266 | print_raw("OPTIONAL DEPENDENCIES") |
|
268 | print_raw("OPTIONAL DEPENDENCIES") | |
267 |
|
269 | |||
268 | check_for_zopeinterface() |
|
270 | check_for_zopeinterface() | |
269 | check_for_twisted() |
|
271 | check_for_twisted() | |
270 | check_for_foolscap() |
|
272 | check_for_foolscap() | |
271 | check_for_pyopenssl() |
|
273 | check_for_pyopenssl() | |
272 | check_for_sphinx() |
|
274 | check_for_sphinx() | |
273 | check_for_pygments() |
|
275 | check_for_pygments() | |
274 | check_for_nose() |
|
276 | check_for_nose() | |
275 | check_for_pexpect() |
|
277 | check_for_pexpect() |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now