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