diff --git a/IPython/Magic.py b/IPython/Magic.py index 4d4154f..64bfc97 100644 --- a/IPython/Magic.py +++ b/IPython/Magic.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Magic functions for InteractiveShell. -$Id: Magic.py 962 2005-12-28 18:04:59Z fperez $""" +$Id: Magic.py 965 2005-12-28 23:23:09Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Janko Hauser and @@ -1049,14 +1049,21 @@ Currently the magic system has the following functions:\n""" try: pdb = {'off':0,'0':0,'on':1,'1':1}[par] except KeyError: - print 'Incorrect argument. Use on/1, off/0 or nothing for a toggle.' + print 'Incorrect argument. Use on/1, off/0, or nothing for a toggle.' return else: self.shell.InteractiveTB.call_pdb = pdb else: - self.shell.InteractiveTB.call_pdb = 1 - self.shell.InteractiveTB.call_pdb - print 'Automatic pdb calling has been turned',\ - on_off(self.shell.InteractiveTB.call_pdb) + new_pdb = not self.shell.InteractiveTB.call_pdb + self.shell.InteractiveTB.call_pdb = new_pdb + if self.shell.isthreaded: + try: + self.sys_excepthook.call_pdb = new_pdb + except: + warn('Failed to activate pdb for threaded exception handler') + + print 'Automatic pdb calling has been turned',on_off(new_pdb) + def magic_prun(self, parameter_s ='',user_mode=1, @@ -1895,12 +1902,23 @@ Currently the magic system has the following functions:\n""" If called without arguments, acts as a toggle.""" + def xmode_switch_err(name): + warn('Error changing %s exception modes.\n%s' % + (name,sys.exc_info()[1])) + new_mode = parameter_s.strip().capitalize() try: - self.InteractiveTB.set_mode(mode = new_mode) + self.InteractiveTB.set_mode(mode=new_mode) print 'Exception reporting mode:',self.InteractiveTB.mode except: - warn('Error changing exception modes.\n' + str(sys.exc_info()[1])) + xmode_switch_err('user') + + # threaded shells use a special handler in sys.excepthook + if self.isthreaded: + try: + self.shell.sys_excepthook.set_mode(mode=new_mode) + except: + xmode_switch_err('threaded') def magic_colors(self,parameter_s = ''): """Switch color scheme for prompts, info system and exception handlers. @@ -1908,6 +1926,11 @@ Currently the magic system has the following functions:\n""" Currently implemented schemes: NoColor, Linux, LightBG. Color scheme names are not case-sensitive.""" + + def color_switch_err(name): + warn('Error changing %s color schemes.\n%s' % + (name,sys.exc_info()[1])) + new_scheme = parameter_s.strip() if not new_scheme: @@ -1943,8 +1966,7 @@ Defaulting color scheme to 'NoColor'""" try: self.shell.outputcache.set_colors(new_scheme) except: - warn('Error changing prompt color schemes.\n' - + str(sys.exc_info()[1])) + color_switch_err('prompt') else: self.shell.rc.colors = \ self.shell.outputcache.color_table.active_scheme_name @@ -1953,15 +1975,21 @@ Defaulting color scheme to 'NoColor'""" self.shell.InteractiveTB.set_colors(scheme = new_scheme) self.shell.SyntaxTB.set_colors(scheme = new_scheme) except: - warn('Error changing exception color schemes.\n' - + str(sys.exc_info()[1])) + color_switch_err('exception') + + # threaded shells use a verbose traceback in sys.excepthook + if self.isthreaded: + try: + self.shell.sys_excepthook.set_colors(scheme=new_scheme) + except: + color_switch_err('system exception handler') + # Set info (for 'object?') colors if self.shell.rc.color_info: try: self.shell.inspector.set_active_scheme(new_scheme) except: - warn('Error changing object inspector color schemes.\n' - + str(sys.exc_info()[1])) + color_switch_err('object inspector') else: self.shell.inspector.set_active_scheme('NoColor') diff --git a/IPython/Shell.py b/IPython/Shell.py index 5b3ce1b..8c5a8c3 100644 --- a/IPython/Shell.py +++ b/IPython/Shell.py @@ -4,7 +4,7 @@ All the matplotlib support code was co-developed with John Hunter, matplotlib's author. -$Id: Shell.py 964 2005-12-28 21:03:01Z fperez $""" +$Id: Shell.py 965 2005-12-28 23:23:09Z fperez $""" #***************************************************************************** # Copyright (C) 2001-2004 Fernando Perez @@ -260,6 +260,10 @@ class MTInteractiveShell(InteractiveShell): # McErlean and John Finlay. Modified with corrections by Antoon Pardon, # from the pygtk mailing list, to avoid lockups with system calls. + # class attribute to indicate whether the class supports threads or not. + # Subclasses with thread support should override this as needed. + isthreaded = True + def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), user_ns=None,user_global_ns=None,banner2='',**kw): """Similar to the normal InteractiveShell, but with threading control""" diff --git a/IPython/iplib.py b/IPython/iplib.py index 33ce52a..807b777 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -6,7 +6,7 @@ Requires Python 2.1 or newer. This file contains all the classes and helper functions specific to IPython. -$Id: iplib.py 964 2005-12-28 21:03:01Z fperez $ +$Id: iplib.py 965 2005-12-28 23:23:09Z fperez $ """ #***************************************************************************** @@ -232,6 +232,10 @@ class SyntaxTB(ultraTB.ListTB): class InteractiveShell(Logger, Magic): """An enhanced console for Python.""" + # class attribute to indicate whether the class supports threads or not. + # Subclasses with thread support should override this as needed. + isthreaded = False + def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), user_ns = None,user_global_ns=None,banner2='', custom_exceptions=((),None),embedded=False): @@ -268,6 +272,9 @@ class InteractiveShell(Logger, Magic): except AttributeError: pass + # Store the actual shell's name + self.name = name + # We need to know whether the instance is meant for embedding, since # global/local namespaces need to be handled differently in that case self.embedded = embedded @@ -405,9 +412,6 @@ class InteractiveShell(Logger, Magic): self.user_ns['In'] = self.input_hist self.user_ns['Out'] = self.output_hist - # Store the actual shell's name - self.name = name - # Object variable to store code object waiting execution. This is # used mainly by the multithreaded shells, but it can come in handy in # other situations. No need to use a Queue here, since it's a single @@ -565,14 +569,35 @@ class InteractiveShell(Logger, Magic): self.banner2 = banner2 # TraceBack handlers: - # Need two, one for syntax errors and one for other exceptions. + + # Syntax error handler. self.SyntaxTB = SyntaxTB(color_scheme='NoColor') + # The interactive one is initialized with an offset, meaning we always # want to remove the topmost item in the traceback, which is our own # internal code. Valid modes: ['Plain','Context','Verbose'] self.InteractiveTB = ultraTB.AutoFormattedTB(mode = 'Plain', color_scheme='NoColor', tb_offset = 1) + + # IPython itself shouldn't crash. This will produce a detailed + # post-mortem if it does. But we only install the crash handler for + # non-threaded shells, the threaded ones use a normal verbose reporter + # and lose the crash handler. This is because exceptions in the main + # thread (such as in GUI code) propagate directly to sys.excepthook, + # and there's no point in printing crash dumps for every user exception. + if self.isthreaded: + sys.excepthook = ultraTB.FormattedTB() + else: + from IPython import CrashHandler + sys.excepthook = CrashHandler.CrashHandler(self) + + # The instance will store a pointer to this, so that runtime code + # (such as magics) can access it. This is because during the + # read-eval loop, it gets temporarily overwritten (to deal with GUI + # frameworks). + self.sys_excepthook = sys.excepthook + # and add any custom exception handlers the user may have specified self.set_custom_exc(*custom_exceptions) @@ -618,6 +643,38 @@ class InteractiveShell(Logger, Magic): self.init_auto_alias() # end __init__ + def post_config_initialization(self): + """Post configuration init method + + This is called after the configuration files have been processed to + 'finalize' the initialization.""" + + rc = self.rc + + # Load readline proper + if rc.readline: + self.init_readline() + + # Set user colors (don't do it in the constructor above so that it + # doesn't crash if colors option is invalid) + self.magic_colors(rc.colors) + + # Load user aliases + for alias in rc.alias: + self.magic_alias(alias) + + # dynamic data that survives through sessions + # XXX make the filename a config option? + persist_base = 'persist' + if rc.profile: + persist_base += '_%s' % rc.profile + self.persist_fname = os.path.join(rc.ipythondir,persist_base) + + try: + self.persist = pickle.load(file(self.persist_fname)) + except: + self.persist = {} + def set_hook(self,name,hook): """set_hook(name,hook) -> sets an internal IPython hook. @@ -727,38 +784,6 @@ class InteractiveShell(Logger, Magic): self.Completer.namespace = self.user_ns self.Completer.global_namespace = self.user_global_ns - def post_config_initialization(self): - """Post configuration init method - - This is called after the configuration files have been processed to - 'finalize' the initialization.""" - - rc = self.rc - - # Load readline proper - if rc.readline: - self.init_readline() - - # Set user colors (don't do it in the constructor above so that it - # doesn't crash if colors option is invalid) - self.magic_colors(rc.colors) - - # Load user aliases - for alias in rc.alias: - self.magic_alias(alias) - - # dynamic data that survives through sessions - # XXX make the filename a config option? - persist_base = 'persist' - if rc.profile: - persist_base += '_%s' % rc.profile - self.persist_fname = os.path.join(rc.ipythondir,persist_base) - - try: - self.persist = pickle.load(file(self.persist_fname)) - except: - self.persist = {} - def init_auto_alias(self): """Define some aliases automatically. @@ -1462,6 +1487,10 @@ want to merge them back into the new files.""" % locals() # Set our own excepthook in case the user code tries to call it # directly, so that the IPython crash handler doesn't get triggered old_excepthook,sys.excepthook = sys.excepthook, self.excepthook + + # we save the original sys.excepthook in the instance, in case config + # code (such as magics) needs access to it. + self.sys_excepthook = old_excepthook outflag = 1 # happens in more places, so it's easier as default try: try: @@ -1954,7 +1983,7 @@ want to merge them back into the new files.""" % locals() try: execfile(fname,*where) except SyntaxError: - etype, evalue = sys.exc_info()[0:2] + etype,evalue = sys.exc_info()[:2] self.SyntaxTB(etype,evalue,[]) warn('Failure executing file: <%s>' % fname) except SystemExit,status: diff --git a/IPython/ipmaker.py b/IPython/ipmaker.py index 746b609..85bdee3 100644 --- a/IPython/ipmaker.py +++ b/IPython/ipmaker.py @@ -6,7 +6,7 @@ Requires Python 2.1 or better. This file contains the main make_IPython() starter function. -$Id: ipmaker.py 964 2005-12-28 21:03:01Z fperez $""" +$Id: ipmaker.py 965 2005-12-28 23:23:09Z fperez $""" #***************************************************************************** # Copyright (C) 2001-2004 Fernando Perez. @@ -94,15 +94,11 @@ def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1, from site import _Helper IP.user_ns['help'] = _Helper() + if DEVDEBUG: # For developer debugging only (global flag) from IPython import ultraTB sys.excepthook = ultraTB.VerboseTB(call_pdb=1) - else: - # IPython itself shouldn't crash. This will produce a detailed - # post-mortem if it does - from IPython import CrashHandler - sys.excepthook = CrashHandler.CrashHandler(IP) IP.BANNER_PARTS = ['Python %s\n' 'Type "copyright", "credits" or "license" ' diff --git a/IPython/ultraTB.py b/IPython/ultraTB.py index b052331..74db9f4 100644 --- a/IPython/ultraTB.py +++ b/IPython/ultraTB.py @@ -60,7 +60,7 @@ You can implement other color schemes easily, the syntax is fairly self-explanatory. Please send back new schemes you develop to the author for possible inclusion in future releases. -$Id: ultraTB.py 958 2005-12-27 23:17:51Z fperez $""" +$Id: ultraTB.py 965 2005-12-28 23:23:09Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Nathaniel Gray @@ -621,10 +621,10 @@ class VerboseTB(TBTools): # out the right info on its own. def __call__(self, etype=None, evalue=None, etb=None): """This hook can replace sys.excepthook (for Python 2.1 or higher).""" - if etb is not None: - self.handler((etype, evalue, etb)) - else: + if etb is None: self.handler() + else: + self.handler((etype, evalue, etb)) self.debugger() #---------------------------------------------------------------------------- diff --git a/doc/ChangeLog b/doc/ChangeLog index 297d516..53c22a2 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -5,8 +5,20 @@ questions part 2 - \/ characters revisited' on the iypthon user list: http://scipy.net/pipermail/ipython-user/2005-June/000907.html + (InteractiveShell.__init__): fix tab-completion bug in threaded shells. + (InteractiveShell.__init__): change threaded shells to not use the + ipython crash handler. This was causing more problems than not, + as exceptions in the main thread (GUI code, typically) would + always show up as a 'crash', when they really weren't. + + The colors and exception mode commands (%colors/%xmode) have been + synchronized to also take this into account, so users can get + verbose exceptions for their threaded code as well. I also added + support for activating pdb inside this exception handler as well, + so now GUI authors can use IPython's enhanced pdb at runtime. + * IPython/ipmaker.py (make_IPython): make the autoedit_syntax flag true by default, and add it to the shipped ipythonrc file. Since this asks the user before proceeding, I think it's OK to make it