diff --git a/IPython/ColorANSI.py b/IPython/ColorANSI.py index 780e9e9..ea875c0 100644 --- a/IPython/ColorANSI.py +++ b/IPython/ColorANSI.py @@ -1,170 +1,170 @@ -# -*- coding: utf-8 -*- -"""Tools for coloring text in ANSI terminals. - -$Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $""" - -#***************************************************************************** -# Copyright (C) 2002-2006 Fernando Perez. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -from IPython import Release -__author__ = '%s <%s>' % Release.authors['Fernando'] -__license__ = Release.license - -__all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] - -import os - -from IPython.ipstruct import Struct - -def make_color_table(in_class): - """Build a set of color attributes in a class. - - Helper function for building the *TermColors classes.""" - - color_templates = ( - ("Black" , "0;30"), - ("Red" , "0;31"), - ("Green" , "0;32"), - ("Brown" , "0;33"), - ("Blue" , "0;34"), - ("Purple" , "0;35"), - ("Cyan" , "0;36"), - ("LightGray" , "0;37"), - ("DarkGray" , "1;30"), - ("LightRed" , "1;31"), - ("LightGreen" , "1;32"), - ("Yellow" , "1;33"), - ("LightBlue" , "1;34"), - ("LightPurple" , "1;35"), - ("LightCyan" , "1;36"), - ("White" , "1;37"), ) - - for name,value in color_templates: - setattr(in_class,name,in_class._base % value) - -class TermColors: - """Color escape sequences. - - This class defines the escape sequences for all the standard (ANSI?) - colors in terminals. Also defines a NoColor escape which is just the null - string, suitable for defining 'dummy' color schemes in terminals which get - confused by color escapes. - - This class should be used as a mixin for building color schemes.""" - - NoColor = '' # for color schemes in color-less terminals. - Normal = '\033[0m' # Reset normal coloring - _base = '\033[%sm' # Template for all other colors - -# Build the actual color table as a set of class attributes: -make_color_table(TermColors) - -class InputTermColors: - """Color escape sequences for input prompts. - - This class is similar to TermColors, but the escapes are wrapped in \001 - and \002 so that readline can properly know the length of each line and - can wrap lines accordingly. Use this class for any colored text which - needs to be used in input prompts, such as in calls to raw_input(). - - This class defines the escape sequences for all the standard (ANSI?) - colors in terminals. Also defines a NoColor escape which is just the null - string, suitable for defining 'dummy' color schemes in terminals which get - confused by color escapes. - - This class should be used as a mixin for building color schemes.""" - - NoColor = '' # for color schemes in color-less terminals. - - if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': - # (X)emacs on W32 gets confused with \001 and \002 so we remove them - Normal = '\033[0m' # Reset normal coloring - _base = '\033[%sm' # Template for all other colors - else: - Normal = '\001\033[0m\002' # Reset normal coloring - _base = '\001\033[%sm\002' # Template for all other colors - -# Build the actual color table as a set of class attributes: -make_color_table(InputTermColors) - -class ColorScheme: - """Generic color scheme class. Just a name and a Struct.""" - def __init__(self,__scheme_name_,colordict=None,**colormap): - self.name = __scheme_name_ - if colordict is None: - self.colors = Struct(**colormap) - else: - self.colors = Struct(colordict) - - def copy(self,name=None): - """Return a full copy of the object, optionally renaming it.""" - if name is None: - name = self.name - return ColorScheme(name,self.colors.__dict__) - -class ColorSchemeTable(dict): - """General class to handle tables of color schemes. - - It's basically a dict of color schemes with a couple of shorthand - attributes and some convenient methods. - - active_scheme_name -> obvious - active_colors -> actual color table of the active scheme""" - - def __init__(self,scheme_list=None,default_scheme=''): - """Create a table of color schemes. - - The table can be created empty and manually filled or it can be - created with a list of valid color schemes AND the specification for - the default active scheme. - """ - - # create object attributes to be set later - self.active_scheme_name = '' - self.active_colors = None - - if scheme_list: - if default_scheme == '': - raise ValueError,'you must specify the default color scheme' - for scheme in scheme_list: - self.add_scheme(scheme) - self.set_active_scheme(default_scheme) - - def copy(self): - """Return full copy of object""" - return ColorSchemeTable(self.values(),self.active_scheme_name) - - def add_scheme(self,new_scheme): - """Add a new color scheme to the table.""" - if not isinstance(new_scheme,ColorScheme): - raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' - self[new_scheme.name] = new_scheme - - def set_active_scheme(self,scheme,case_sensitive=0): - """Set the currently active scheme. - - Names are by default compared in a case-insensitive way, but this can - be changed by setting the parameter case_sensitive to true.""" - - scheme_names = self.keys() - if case_sensitive: - valid_schemes = scheme_names - scheme_test = scheme - else: - valid_schemes = [s.lower() for s in scheme_names] - scheme_test = scheme.lower() - try: - scheme_idx = valid_schemes.index(scheme_test) - except ValueError: - raise ValueError,'Unrecognized color scheme: ' + scheme + \ - '\nValid schemes: '+str(scheme_names).replace("'', ",'') - else: - active = scheme_names[scheme_idx] - self.active_scheme_name = active - self.active_colors = self[active].colors - # Now allow using '' as an index for the current active scheme - self[''] = self[active] +# -*- coding: utf-8 -*- +"""Tools for coloring text in ANSI terminals. + +$Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $""" + +#***************************************************************************** +# Copyright (C) 2002-2006 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +from IPython import Release +__author__ = '%s <%s>' % Release.authors['Fernando'] +__license__ = Release.license + +__all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] + +import os + +from IPython.ipstruct import Struct + +def make_color_table(in_class): + """Build a set of color attributes in a class. + + Helper function for building the *TermColors classes.""" + + color_templates = ( + ("Black" , "0;30"), + ("Red" , "0;31"), + ("Green" , "0;32"), + ("Brown" , "0;33"), + ("Blue" , "0;34"), + ("Purple" , "0;35"), + ("Cyan" , "0;36"), + ("LightGray" , "0;37"), + ("DarkGray" , "1;30"), + ("LightRed" , "1;31"), + ("LightGreen" , "1;32"), + ("Yellow" , "1;33"), + ("LightBlue" , "1;34"), + ("LightPurple" , "1;35"), + ("LightCyan" , "1;36"), + ("White" , "1;37"), ) + + for name,value in color_templates: + setattr(in_class,name,in_class._base % value) + +class TermColors: + """Color escape sequences. + + This class defines the escape sequences for all the standard (ANSI?) + colors in terminals. Also defines a NoColor escape which is just the null + string, suitable for defining 'dummy' color schemes in terminals which get + confused by color escapes. + + This class should be used as a mixin for building color schemes.""" + + NoColor = '' # for color schemes in color-less terminals. + Normal = '\033[0m' # Reset normal coloring + _base = '\033[%sm' # Template for all other colors + +# Build the actual color table as a set of class attributes: +make_color_table(TermColors) + +class InputTermColors: + """Color escape sequences for input prompts. + + This class is similar to TermColors, but the escapes are wrapped in \001 + and \002 so that readline can properly know the length of each line and + can wrap lines accordingly. Use this class for any colored text which + needs to be used in input prompts, such as in calls to raw_input(). + + This class defines the escape sequences for all the standard (ANSI?) + colors in terminals. Also defines a NoColor escape which is just the null + string, suitable for defining 'dummy' color schemes in terminals which get + confused by color escapes. + + This class should be used as a mixin for building color schemes.""" + + NoColor = '' # for color schemes in color-less terminals. + + if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': + # (X)emacs on W32 gets confused with \001 and \002 so we remove them + Normal = '\033[0m' # Reset normal coloring + _base = '\033[%sm' # Template for all other colors + else: + Normal = '\001\033[0m\002' # Reset normal coloring + _base = '\001\033[%sm\002' # Template for all other colors + +# Build the actual color table as a set of class attributes: +make_color_table(InputTermColors) + +class ColorScheme: + """Generic color scheme class. Just a name and a Struct.""" + def __init__(self,__scheme_name_,colordict=None,**colormap): + self.name = __scheme_name_ + if colordict is None: + self.colors = Struct(**colormap) + else: + self.colors = Struct(colordict) + + def copy(self,name=None): + """Return a full copy of the object, optionally renaming it.""" + if name is None: + name = self.name + return ColorScheme(name,self.colors.__dict__) + +class ColorSchemeTable(dict): + """General class to handle tables of color schemes. + + It's basically a dict of color schemes with a couple of shorthand + attributes and some convenient methods. + + active_scheme_name -> obvious + active_colors -> actual color table of the active scheme""" + + def __init__(self,scheme_list=None,default_scheme=''): + """Create a table of color schemes. + + The table can be created empty and manually filled or it can be + created with a list of valid color schemes AND the specification for + the default active scheme. + """ + + # create object attributes to be set later + self.active_scheme_name = '' + self.active_colors = None + + if scheme_list: + if default_scheme == '': + raise ValueError,'you must specify the default color scheme' + for scheme in scheme_list: + self.add_scheme(scheme) + self.set_active_scheme(default_scheme) + + def copy(self): + """Return full copy of object""" + return ColorSchemeTable(self.values(),self.active_scheme_name) + + def add_scheme(self,new_scheme): + """Add a new color scheme to the table.""" + if not isinstance(new_scheme,ColorScheme): + raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' + self[new_scheme.name] = new_scheme + + def set_active_scheme(self,scheme,case_sensitive=0): + """Set the currently active scheme. + + Names are by default compared in a case-insensitive way, but this can + be changed by setting the parameter case_sensitive to true.""" + + scheme_names = self.keys() + if case_sensitive: + valid_schemes = scheme_names + scheme_test = scheme + else: + valid_schemes = [s.lower() for s in scheme_names] + scheme_test = scheme.lower() + try: + scheme_idx = valid_schemes.index(scheme_test) + except ValueError: + raise ValueError,'Unrecognized color scheme: ' + scheme + \ + '\nValid schemes: '+str(scheme_names).replace("'', ",'') + else: + active = scheme_names[scheme_idx] + self.active_scheme_name = active + self.active_colors = self[active].colors + # Now allow using '' as an index for the current active scheme + self[''] = self[active] diff --git a/IPython/ConfigLoader.py b/IPython/ConfigLoader.py index 10a245a..c7a67f8 100644 --- a/IPython/ConfigLoader.py +++ b/IPython/ConfigLoader.py @@ -1,116 +1,116 @@ -# -*- coding: utf-8 -*- -"""Configuration loader - -$Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $""" - -#***************************************************************************** -# Copyright (C) 2001-2006 Fernando Perez. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -from IPython import Release -__author__ = '%s <%s>' % Release.authors['Fernando'] -__license__ = Release.license - -import exceptions -import os -from pprint import pprint - -from IPython import ultraTB -from IPython.ipstruct import Struct -from IPython.genutils import * - -class ConfigLoaderError(exceptions.Exception): - """Exception for ConfigLoader class.""" - - def __init__(self,args=None): - self.args = args - -class ConfigLoader: - - """Configuration file loader capable of handling recursive inclusions and - with parametrized conflict resolution for multiply found keys.""" - - def __init__(self,conflict=None,field_sep=None,reclimit=15): - - """The reclimit parameter controls the number of recursive - configuration file inclusions. This way we can stop early on (before - python's own recursion limit is hit) if there is a circular - inclusion. - - - conflict: dictionary for conflict resolutions (see Struct.merge()) - - """ - self.conflict = conflict - self.field_sep = field_sep - self.reset(reclimit) - - def reset(self,reclimit=15): - self.reclimit = reclimit - self.recdepth = 0 - self.included = [] - - def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw): - """Load a configuration file, return the resulting Struct. - - Call: load_config(fname,convert=None,conflict=None,recurse_key='') - - - fname: file to load from. - - convert: dictionary of type conversions (see read_dict()) - - recurse_key: keyword in dictionary to trigger recursive file - inclusions. - """ - - if self.recdepth > self.reclimit: - raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\ - 'exceeded: ' + `self.recdepth` + \ - '.\nMaybe you have a circular chain of inclusions?' - self.recdepth += 1 - fname = filefind(fname,incpath) - data = Struct() - # avoid including the same file more than once - if fname in self.included: - return data - Xinfo = ultraTB.AutoFormattedTB() - if convert==None and recurse_key : convert = {qwflat:recurse_key} - # for production, change warn to 0: - data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1, - warn=0,no_empty=0,**kw)) - # keep track of successfully loaded files - self.included.append(fname) - if recurse_key in data.keys(): - for incfilename in data[recurse_key]: - found=0 - try: - incfile = filefind(incfilename,incpath) - except IOError: - if os.name in ['nt','dos']: - try: - # Try again with '.ini' extension - incfilename += '.ini' - incfile = filefind(incfilename,incpath) - except IOError: - found = 0 - else: - found = 1 - else: - found = 0 - else: - found = 1 - if found: - try: - data.merge(self.load(incfile,convert,recurse_key, - incpath,**kw), - self.conflict) - except: - Xinfo() - warn('Problem loading included file: '+ - `incfilename` + '. Ignoring it...') - else: - warn('File `%s` not found. Included by %s' % (incfilename,fname)) - - return data - -# end ConfigLoader +# -*- coding: utf-8 -*- +"""Configuration loader + +$Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $""" + +#***************************************************************************** +# Copyright (C) 2001-2006 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +from IPython import Release +__author__ = '%s <%s>' % Release.authors['Fernando'] +__license__ = Release.license + +import exceptions +import os +from pprint import pprint + +from IPython import ultraTB +from IPython.ipstruct import Struct +from IPython.genutils import * + +class ConfigLoaderError(exceptions.Exception): + """Exception for ConfigLoader class.""" + + def __init__(self,args=None): + self.args = args + +class ConfigLoader: + + """Configuration file loader capable of handling recursive inclusions and + with parametrized conflict resolution for multiply found keys.""" + + def __init__(self,conflict=None,field_sep=None,reclimit=15): + + """The reclimit parameter controls the number of recursive + configuration file inclusions. This way we can stop early on (before + python's own recursion limit is hit) if there is a circular + inclusion. + + - conflict: dictionary for conflict resolutions (see Struct.merge()) + + """ + self.conflict = conflict + self.field_sep = field_sep + self.reset(reclimit) + + def reset(self,reclimit=15): + self.reclimit = reclimit + self.recdepth = 0 + self.included = [] + + def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw): + """Load a configuration file, return the resulting Struct. + + Call: load_config(fname,convert=None,conflict=None,recurse_key='') + + - fname: file to load from. + - convert: dictionary of type conversions (see read_dict()) + - recurse_key: keyword in dictionary to trigger recursive file + inclusions. + """ + + if self.recdepth > self.reclimit: + raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\ + 'exceeded: ' + `self.recdepth` + \ + '.\nMaybe you have a circular chain of inclusions?' + self.recdepth += 1 + fname = filefind(fname,incpath) + data = Struct() + # avoid including the same file more than once + if fname in self.included: + return data + Xinfo = ultraTB.AutoFormattedTB() + if convert==None and recurse_key : convert = {qwflat:recurse_key} + # for production, change warn to 0: + data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1, + warn=0,no_empty=0,**kw)) + # keep track of successfully loaded files + self.included.append(fname) + if recurse_key in data.keys(): + for incfilename in data[recurse_key]: + found=0 + try: + incfile = filefind(incfilename,incpath) + except IOError: + if os.name in ['nt','dos']: + try: + # Try again with '.ini' extension + incfilename += '.ini' + incfile = filefind(incfilename,incpath) + except IOError: + found = 0 + else: + found = 1 + else: + found = 0 + else: + found = 1 + if found: + try: + data.merge(self.load(incfile,convert,recurse_key, + incpath,**kw), + self.conflict) + except: + Xinfo() + warn('Problem loading included file: '+ + `incfilename` + '. Ignoring it...') + else: + warn('File `%s` not found. Included by %s' % (incfilename,fname)) + + return data + +# end ConfigLoader diff --git a/IPython/CrashHandler.py b/IPython/CrashHandler.py index 4cb6cb7..857abe5 100644 --- a/IPython/CrashHandler.py +++ b/IPython/CrashHandler.py @@ -1,228 +1,228 @@ -# -*- coding: utf-8 -*- -"""sys.excepthook for IPython itself, leaves a detailed report on disk. - -$Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $""" - -#***************************************************************************** -# Copyright (C) 2001-2006 Fernando Perez. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -from IPython import Release -__author__ = '%s <%s>' % Release.authors['Fernando'] -__license__ = Release.license -__version__ = Release.version - -#**************************************************************************** -# Required modules - -# From the standard library -import os -import sys -from pprint import pprint,pformat - -# Homebrewed -from IPython.Itpl import Itpl,itpl,printpl -from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names -from IPython import ultraTB -from IPython.genutils import * - -#**************************************************************************** -class CrashHandler: - """Customizable crash handlers for IPython-based systems. - - Instances of this class provide a __call__ method which can be used as a - sys.excepthook, i.e., the __call__ signature is: - - def __call__(self,etype, evalue, etb) - - """ - - def __init__(self,IP,app_name,contact_name,contact_email, - bug_tracker,crash_report_fname, - show_crash_traceback=True): - """New crash handler. - - Inputs: - - - IP: a running IPython instance, which will be queried at crash time - for internal information. - - - app_name: a string containing the name of your application. - - - contact_name: a string with the name of the person to contact. - - - contact_email: a string with the email address of the contact. - - - bug_tracker: a string with the URL for your project's bug tracker. - - - crash_report_fname: a string with the filename for the crash report - to be saved in. These reports are left in the ipython user directory - as determined by the running IPython instance. - - Optional inputs: - - - show_crash_traceback(True): if false, don't print the crash - traceback on stderr, only generate the on-disk report - - - Non-argument instance attributes: - - These instances contain some non-argument attributes which allow for - further customization of the crash handler's behavior. Please see the - source for further details. - """ - - # apply args into instance - self.IP = IP # IPython instance - self.app_name = app_name - self.contact_name = contact_name - self.contact_email = contact_email - self.bug_tracker = bug_tracker - self.crash_report_fname = crash_report_fname - self.show_crash_traceback = show_crash_traceback - - # Hardcoded defaults, which can be overridden either by subclasses or - # at runtime for the instance. - - # Template for the user message. Subclasses which completely override - # this, or user apps, can modify it to suit their tastes. It gets - # expanded using itpl, so calls of the kind $self.foo are valid. - self.user_message_template = """ -Oops, $self.app_name crashed. We do our best to make it stable, but... - -A crash report was automatically generated with the following information: - - A verbatim copy of the crash traceback. - - A copy of your input history during this session. - - Data on your current $self.app_name configuration. - -It was left in the file named: -\t'$self.crash_report_fname' -If you can email this file to the developers, the information in it will help -them in understanding and correcting the problem. - -You can mail it to: $self.contact_name at $self.contact_email -with the subject '$self.app_name Crash Report'. - -If you want to do it now, the following command will work (under Unix): -mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname - -To ensure accurate tracking of this issue, please file a report about it at: -$self.bug_tracker -""" - - def __call__(self,etype, evalue, etb): - """Handle an exception, call for compatible with sys.excepthook""" - - # Report tracebacks shouldn't use color in general (safer for users) - color_scheme = 'NoColor' - - # Use this ONLY for developer debugging (keep commented out for release) - #color_scheme = 'Linux' # dbg - - try: - rptdir = self.IP.rc.ipythondir - except: - rptdir = os.getcwd() - if not os.path.isdir(rptdir): - rptdir = os.getcwd() - report_name = os.path.join(rptdir,self.crash_report_fname) - # write the report filename into the instance dict so it can get - # properly expanded out in the user message template - self.crash_report_fname = report_name - TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme, - long_header=1) - traceback = TBhandler.text(etype,evalue,etb,context=31) - - # print traceback to screen - if self.show_crash_traceback: - print >> sys.stderr, traceback - - # and generate a complete report on disk - try: - report = open(report_name,'w') - except: - print >> sys.stderr, 'Could not create crash report on disk.' - return - - # Inform user on stderr of what happened - msg = itpl('\n'+'*'*70+'\n'+self.user_message_template) - print >> sys.stderr, msg - - # Construct report on disk - report.write(self.make_report(traceback)) - report.close() - raw_input("Press enter to exit:") - - def make_report(self,traceback): - """Return a string containing a crash report.""" - - sec_sep = '\n\n'+'*'*75+'\n\n' - - report = [] - rpt_add = report.append - - rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') - rpt_add('IPython version: %s \n\n' % Release.version) - rpt_add('SVN revision : %s \n\n' % Release.revision) - rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % - (os.name,sys.platform) ) - rpt_add(sec_sep+'Current user configuration structure:\n\n') - rpt_add(pformat(self.IP.rc.dict())) - rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) - try: - rpt_add(sec_sep+"History of session input:") - for line in self.IP.user_ns['_ih']: - rpt_add(line) - rpt_add('\n*** Last line of input (may not be in above history):\n') - rpt_add(self.IP._last_input_line+'\n') - except: - pass - - return ''.join(report) - -class IPythonCrashHandler(CrashHandler): - """sys.excepthook for IPython itself, leaves a detailed report on disk.""" - - def __init__(self,IP): - - # Set here which of the IPython authors should be listed as contact - AUTHOR_CONTACT = 'Ville' - - # Set argument defaults - app_name = 'IPython' - bug_tracker = 'http://projects.scipy.org/ipython/ipython/report' - contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2] - crash_report_fname = 'IPython_crash_report.txt' - # Call parent constructor - CrashHandler.__init__(self,IP,app_name,contact_name,contact_email, - bug_tracker,crash_report_fname) - - def make_report(self,traceback): - """Return a string containing a crash report.""" - - sec_sep = '\n\n'+'*'*75+'\n\n' - - report = [] - rpt_add = report.append - - rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') - rpt_add('IPython version: %s \n\n' % Release.version) - rpt_add('SVN revision : %s \n\n' % Release.revision) - rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % - (os.name,sys.platform) ) - rpt_add(sec_sep+'Current user configuration structure:\n\n') - rpt_add(pformat(self.IP.rc.dict())) - rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) - try: - rpt_add(sec_sep+"History of session input:") - for line in self.IP.user_ns['_ih']: - rpt_add(line) - rpt_add('\n*** Last line of input (may not be in above history):\n') - rpt_add(self.IP._last_input_line+'\n') - except: - pass - - return ''.join(report) +# -*- coding: utf-8 -*- +"""sys.excepthook for IPython itself, leaves a detailed report on disk. + +$Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $""" + +#***************************************************************************** +# Copyright (C) 2001-2006 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +from IPython import Release +__author__ = '%s <%s>' % Release.authors['Fernando'] +__license__ = Release.license +__version__ = Release.version + +#**************************************************************************** +# Required modules + +# From the standard library +import os +import sys +from pprint import pprint,pformat + +# Homebrewed +from IPython.Itpl import Itpl,itpl,printpl +from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names +from IPython import ultraTB +from IPython.genutils import * + +#**************************************************************************** +class CrashHandler: + """Customizable crash handlers for IPython-based systems. + + Instances of this class provide a __call__ method which can be used as a + sys.excepthook, i.e., the __call__ signature is: + + def __call__(self,etype, evalue, etb) + + """ + + def __init__(self,IP,app_name,contact_name,contact_email, + bug_tracker,crash_report_fname, + show_crash_traceback=True): + """New crash handler. + + Inputs: + + - IP: a running IPython instance, which will be queried at crash time + for internal information. + + - app_name: a string containing the name of your application. + + - contact_name: a string with the name of the person to contact. + + - contact_email: a string with the email address of the contact. + + - bug_tracker: a string with the URL for your project's bug tracker. + + - crash_report_fname: a string with the filename for the crash report + to be saved in. These reports are left in the ipython user directory + as determined by the running IPython instance. + + Optional inputs: + + - show_crash_traceback(True): if false, don't print the crash + traceback on stderr, only generate the on-disk report + + + Non-argument instance attributes: + + These instances contain some non-argument attributes which allow for + further customization of the crash handler's behavior. Please see the + source for further details. + """ + + # apply args into instance + self.IP = IP # IPython instance + self.app_name = app_name + self.contact_name = contact_name + self.contact_email = contact_email + self.bug_tracker = bug_tracker + self.crash_report_fname = crash_report_fname + self.show_crash_traceback = show_crash_traceback + + # Hardcoded defaults, which can be overridden either by subclasses or + # at runtime for the instance. + + # Template for the user message. Subclasses which completely override + # this, or user apps, can modify it to suit their tastes. It gets + # expanded using itpl, so calls of the kind $self.foo are valid. + self.user_message_template = """ +Oops, $self.app_name crashed. We do our best to make it stable, but... + +A crash report was automatically generated with the following information: + - A verbatim copy of the crash traceback. + - A copy of your input history during this session. + - Data on your current $self.app_name configuration. + +It was left in the file named: +\t'$self.crash_report_fname' +If you can email this file to the developers, the information in it will help +them in understanding and correcting the problem. + +You can mail it to: $self.contact_name at $self.contact_email +with the subject '$self.app_name Crash Report'. + +If you want to do it now, the following command will work (under Unix): +mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname + +To ensure accurate tracking of this issue, please file a report about it at: +$self.bug_tracker +""" + + def __call__(self,etype, evalue, etb): + """Handle an exception, call for compatible with sys.excepthook""" + + # Report tracebacks shouldn't use color in general (safer for users) + color_scheme = 'NoColor' + + # Use this ONLY for developer debugging (keep commented out for release) + #color_scheme = 'Linux' # dbg + + try: + rptdir = self.IP.rc.ipythondir + except: + rptdir = os.getcwd() + if not os.path.isdir(rptdir): + rptdir = os.getcwd() + report_name = os.path.join(rptdir,self.crash_report_fname) + # write the report filename into the instance dict so it can get + # properly expanded out in the user message template + self.crash_report_fname = report_name + TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme, + long_header=1) + traceback = TBhandler.text(etype,evalue,etb,context=31) + + # print traceback to screen + if self.show_crash_traceback: + print >> sys.stderr, traceback + + # and generate a complete report on disk + try: + report = open(report_name,'w') + except: + print >> sys.stderr, 'Could not create crash report on disk.' + return + + # Inform user on stderr of what happened + msg = itpl('\n'+'*'*70+'\n'+self.user_message_template) + print >> sys.stderr, msg + + # Construct report on disk + report.write(self.make_report(traceback)) + report.close() + raw_input("Press enter to exit:") + + def make_report(self,traceback): + """Return a string containing a crash report.""" + + sec_sep = '\n\n'+'*'*75+'\n\n' + + report = [] + rpt_add = report.append + + rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') + rpt_add('IPython version: %s \n\n' % Release.version) + rpt_add('SVN revision : %s \n\n' % Release.revision) + rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % + (os.name,sys.platform) ) + rpt_add(sec_sep+'Current user configuration structure:\n\n') + rpt_add(pformat(self.IP.rc.dict())) + rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) + try: + rpt_add(sec_sep+"History of session input:") + for line in self.IP.user_ns['_ih']: + rpt_add(line) + rpt_add('\n*** Last line of input (may not be in above history):\n') + rpt_add(self.IP._last_input_line+'\n') + except: + pass + + return ''.join(report) + +class IPythonCrashHandler(CrashHandler): + """sys.excepthook for IPython itself, leaves a detailed report on disk.""" + + def __init__(self,IP): + + # Set here which of the IPython authors should be listed as contact + AUTHOR_CONTACT = 'Ville' + + # Set argument defaults + app_name = 'IPython' + bug_tracker = 'http://projects.scipy.org/ipython/ipython/report' + contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2] + crash_report_fname = 'IPython_crash_report.txt' + # Call parent constructor + CrashHandler.__init__(self,IP,app_name,contact_name,contact_email, + bug_tracker,crash_report_fname) + + def make_report(self,traceback): + """Return a string containing a crash report.""" + + sec_sep = '\n\n'+'*'*75+'\n\n' + + report = [] + rpt_add = report.append + + rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') + rpt_add('IPython version: %s \n\n' % Release.version) + rpt_add('SVN revision : %s \n\n' % Release.revision) + rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % + (os.name,sys.platform) ) + rpt_add(sec_sep+'Current user configuration structure:\n\n') + rpt_add(pformat(self.IP.rc.dict())) + rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) + try: + rpt_add(sec_sep+"History of session input:") + for line in self.IP.user_ns['_ih']: + rpt_add(line) + rpt_add('\n*** Last line of input (may not be in above history):\n') + rpt_add(self.IP._last_input_line+'\n') + except: + pass + + return ''.join(report) diff --git a/IPython/DPyGetOpt.py b/IPython/DPyGetOpt.py index e9d8ce4..dd2cf86 100644 --- a/IPython/DPyGetOpt.py +++ b/IPython/DPyGetOpt.py @@ -1,693 +1,693 @@ -# -*- coding: utf-8 -*- -"""DPyGetOpt -- Demiurge Python GetOptions Module - - $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $ - -This module is modeled after perl's Getopt::Long module-- which -is, in turn, modeled after GNU's extended getopt() function. - -Upon instantiation, the option specification should be a sequence -(list) of option definitions. - -Options that take no arguments should simply contain the name of -the option. If a ! is post-pended, the option can be negated by -prepending 'no'; ie 'debug!' specifies that -debug and -nodebug -should be accepted. - -Mandatory arguments to options are specified using a postpended -'=' + a type specifier. '=s' specifies a mandatory string -argument, '=i' specifies a mandatory integer argument, and '=f' -specifies a mandatory real number. In all cases, the '=' can be -substituted with ':' to specify that the argument is optional. - -Dashes '-' in option names are allowed. - -If an option has the character '@' postpended (after the -argumentation specification), it can appear multiple times within -each argument list that is processed. The results will be stored -in a list. - -The option name can actually be a list of names separated by '|' -characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar, -and -baz options that appear on within the parsed argument list -must have a real number argument and that the accumulated list -of values will be available under the name 'foo' - -$Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $""" - -#***************************************************************************** -# -# Copyright (c) 2001 Bill Bumgarner -# -# -# Published under the terms of the MIT license, hereby reproduced: -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. -# -#***************************************************************************** - -__author__ = 'Bill Bumgarner ' -__license__ = 'MIT' -__version__ = '1.2' - -# Modified to use re instead of regex and regsub modules. -# 2001/5/7, Jonathan Hogg - -import re -import string -import sys -import types - -class Error(Exception): - """Base class for exceptions in the DPyGetOpt module.""" - -class ArgumentError(Error): - """Exception indicating an error in the arguments passed to - DPyGetOpt.processArguments.""" - -class SpecificationError(Error): - """Exception indicating an error with an option specification.""" - -class TerminationError(Error): - """Exception indicating an error with an option processing terminator.""" - -specificationExpr = re.compile('(?P.)(?P.)(?P@?)') - -ArgRequired = 'Requires an Argument' -ArgOptional = 'Argument Optional' - -# The types modules is not used for these identifiers because there -# is no identifier for 'boolean' or 'generic' -StringArgType = 'String Argument Type' -IntegerArgType = 'Integer Argument Type' -RealArgType = 'Real Argument Type' -BooleanArgType = 'Boolean Argument Type' -GenericArgType = 'Generic Argument Type' - -# dictionary of conversion functions-- boolean and generic options -# do not accept arguments and do not need conversion functions; -# the identity function is used purely for convenience. -ConversionFunctions = { - StringArgType : lambda x: x, - IntegerArgType : string.atoi, - RealArgType : string.atof, - BooleanArgType : lambda x: x, - GenericArgType : lambda x: x, - } - -class DPyGetOpt: - - def __init__(self, spec = None, terminators = ['--']): - """ - Declare and intialize instance variables - - Yes, declaration is not necessary... but one of the things - I sorely miss from C/Obj-C is the concept of having an - interface definition that clearly declares all instance - variables and methods without providing any implementation - details. it is a useful reference! - - all instance variables are initialized to 0/Null/None of - the appropriate type-- not even the default value... - """ - -# sys.stderr.write(string.join(spec) + "\n") - - self.allowAbbreviations = 1 # boolean, 1 if abbreviations will - # be expanded - self.freeValues = [] # list, contains free values - self.ignoreCase = 0 # boolean, YES if ignoring case - self.needsParse = 0 # boolean, YES if need to reparse parameter spec - self.optionNames = {} # dict, all option names-- value is index of tuple - self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--') - self.optionTuples = [] # list o' tuples containing defn of options AND aliases - self.optionValues = {} # dict, option names (after alias expansion) -> option value(s) - self.orderMixed = 0 # boolean, YES if options can be mixed with args - self.posixCompliance = 0 # boolean, YES indicates posix like behaviour - self.spec = [] # list, raw specs (in case it must be reparsed) - self.terminators = terminators # list, strings that terminate argument processing - self.termValues = [] # list, values after terminator - self.terminator = None # full name of terminator that ended - # option processing - - # set up defaults - self.setPosixCompliance() - self.setIgnoreCase() - self.setAllowAbbreviations() - - # parse spec-- if present - if spec: - self.parseConfiguration(spec) - - def setPosixCompliance(self, aFlag = 0): - """ - Enables and disables posix compliance. - - When enabled, '+' can be used as an option prefix and free - values can be mixed with options. - """ - self.posixCompliance = aFlag - self.needsParse = 1 - - if self.posixCompliance: - self.optionStartExpr = re.compile('(--|-)(?P