##// END OF EJS Templates
Remove svn-style $Id marks from docstrings and Release imports....
Fernando Perez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,182 +1,177 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for coloring text in ANSI terminals.
2 """Tools for coloring text in ANSI terminals.
3
3 """
4 $Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $"""
5
4
6 #*****************************************************************************
5 #*****************************************************************************
7 # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu>
8 #
7 #
9 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
10 #*****************************************************************************
12
11
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16
17 __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable']
12 __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable']
18
13
19 import os
14 import os
20
15
21 from IPython.ipstruct import Struct
16 from IPython.ipstruct import Struct
22
17
23 def make_color_table(in_class):
18 def make_color_table(in_class):
24 """Build a set of color attributes in a class.
19 """Build a set of color attributes in a class.
25
20
26 Helper function for building the *TermColors classes."""
21 Helper function for building the *TermColors classes."""
27
22
28 color_templates = (
23 color_templates = (
29 # Dark colors
24 # Dark colors
30 ("Black" , "0;30"),
25 ("Black" , "0;30"),
31 ("Red" , "0;31"),
26 ("Red" , "0;31"),
32 ("Green" , "0;32"),
27 ("Green" , "0;32"),
33 ("Brown" , "0;33"),
28 ("Brown" , "0;33"),
34 ("Blue" , "0;34"),
29 ("Blue" , "0;34"),
35 ("Purple" , "0;35"),
30 ("Purple" , "0;35"),
36 ("Cyan" , "0;36"),
31 ("Cyan" , "0;36"),
37 ("LightGray" , "0;37"),
32 ("LightGray" , "0;37"),
38 # Light colors
33 # Light colors
39 ("DarkGray" , "1;30"),
34 ("DarkGray" , "1;30"),
40 ("LightRed" , "1;31"),
35 ("LightRed" , "1;31"),
41 ("LightGreen" , "1;32"),
36 ("LightGreen" , "1;32"),
42 ("Yellow" , "1;33"),
37 ("Yellow" , "1;33"),
43 ("LightBlue" , "1;34"),
38 ("LightBlue" , "1;34"),
44 ("LightPurple" , "1;35"),
39 ("LightPurple" , "1;35"),
45 ("LightCyan" , "1;36"),
40 ("LightCyan" , "1;36"),
46 ("White" , "1;37"),
41 ("White" , "1;37"),
47 # Blinking colors. Probably should not be used in anything serious.
42 # Blinking colors. Probably should not be used in anything serious.
48 ("BlinkBlack" , "5;30"),
43 ("BlinkBlack" , "5;30"),
49 ("BlinkRed" , "5;31"),
44 ("BlinkRed" , "5;31"),
50 ("BlinkGreen" , "5;32"),
45 ("BlinkGreen" , "5;32"),
51 ("BlinkYellow" , "5;33"),
46 ("BlinkYellow" , "5;33"),
52 ("BlinkBlue" , "5;34"),
47 ("BlinkBlue" , "5;34"),
53 ("BlinkPurple" , "5;35"),
48 ("BlinkPurple" , "5;35"),
54 ("BlinkCyan" , "5;36"),
49 ("BlinkCyan" , "5;36"),
55 ("BlinkLightGray", "5;37"),
50 ("BlinkLightGray", "5;37"),
56 )
51 )
57
52
58 for name,value in color_templates:
53 for name,value in color_templates:
59 setattr(in_class,name,in_class._base % value)
54 setattr(in_class,name,in_class._base % value)
60
55
61 class TermColors:
56 class TermColors:
62 """Color escape sequences.
57 """Color escape sequences.
63
58
64 This class defines the escape sequences for all the standard (ANSI?)
59 This class defines the escape sequences for all the standard (ANSI?)
65 colors in terminals. Also defines a NoColor escape which is just the null
60 colors in terminals. Also defines a NoColor escape which is just the null
66 string, suitable for defining 'dummy' color schemes in terminals which get
61 string, suitable for defining 'dummy' color schemes in terminals which get
67 confused by color escapes.
62 confused by color escapes.
68
63
69 This class should be used as a mixin for building color schemes."""
64 This class should be used as a mixin for building color schemes."""
70
65
71 NoColor = '' # for color schemes in color-less terminals.
66 NoColor = '' # for color schemes in color-less terminals.
72 Normal = '\033[0m' # Reset normal coloring
67 Normal = '\033[0m' # Reset normal coloring
73 _base = '\033[%sm' # Template for all other colors
68 _base = '\033[%sm' # Template for all other colors
74
69
75 # Build the actual color table as a set of class attributes:
70 # Build the actual color table as a set of class attributes:
76 make_color_table(TermColors)
71 make_color_table(TermColors)
77
72
78 class InputTermColors:
73 class InputTermColors:
79 """Color escape sequences for input prompts.
74 """Color escape sequences for input prompts.
80
75
81 This class is similar to TermColors, but the escapes are wrapped in \001
76 This class is similar to TermColors, but the escapes are wrapped in \001
82 and \002 so that readline can properly know the length of each line and
77 and \002 so that readline can properly know the length of each line and
83 can wrap lines accordingly. Use this class for any colored text which
78 can wrap lines accordingly. Use this class for any colored text which
84 needs to be used in input prompts, such as in calls to raw_input().
79 needs to be used in input prompts, such as in calls to raw_input().
85
80
86 This class defines the escape sequences for all the standard (ANSI?)
81 This class defines the escape sequences for all the standard (ANSI?)
87 colors in terminals. Also defines a NoColor escape which is just the null
82 colors in terminals. Also defines a NoColor escape which is just the null
88 string, suitable for defining 'dummy' color schemes in terminals which get
83 string, suitable for defining 'dummy' color schemes in terminals which get
89 confused by color escapes.
84 confused by color escapes.
90
85
91 This class should be used as a mixin for building color schemes."""
86 This class should be used as a mixin for building color schemes."""
92
87
93 NoColor = '' # for color schemes in color-less terminals.
88 NoColor = '' # for color schemes in color-less terminals.
94
89
95 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
90 if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs':
96 # (X)emacs on W32 gets confused with \001 and \002 so we remove them
91 # (X)emacs on W32 gets confused with \001 and \002 so we remove them
97 Normal = '\033[0m' # Reset normal coloring
92 Normal = '\033[0m' # Reset normal coloring
98 _base = '\033[%sm' # Template for all other colors
93 _base = '\033[%sm' # Template for all other colors
99 else:
94 else:
100 Normal = '\001\033[0m\002' # Reset normal coloring
95 Normal = '\001\033[0m\002' # Reset normal coloring
101 _base = '\001\033[%sm\002' # Template for all other colors
96 _base = '\001\033[%sm\002' # Template for all other colors
102
97
103 # Build the actual color table as a set of class attributes:
98 # Build the actual color table as a set of class attributes:
104 make_color_table(InputTermColors)
99 make_color_table(InputTermColors)
105
100
106 class ColorScheme:
101 class ColorScheme:
107 """Generic color scheme class. Just a name and a Struct."""
102 """Generic color scheme class. Just a name and a Struct."""
108 def __init__(self,__scheme_name_,colordict=None,**colormap):
103 def __init__(self,__scheme_name_,colordict=None,**colormap):
109 self.name = __scheme_name_
104 self.name = __scheme_name_
110 if colordict is None:
105 if colordict is None:
111 self.colors = Struct(**colormap)
106 self.colors = Struct(**colormap)
112 else:
107 else:
113 self.colors = Struct(colordict)
108 self.colors = Struct(colordict)
114
109
115 def copy(self,name=None):
110 def copy(self,name=None):
116 """Return a full copy of the object, optionally renaming it."""
111 """Return a full copy of the object, optionally renaming it."""
117 if name is None:
112 if name is None:
118 name = self.name
113 name = self.name
119 return ColorScheme(name,self.colors.__dict__)
114 return ColorScheme(name,self.colors.__dict__)
120
115
121 class ColorSchemeTable(dict):
116 class ColorSchemeTable(dict):
122 """General class to handle tables of color schemes.
117 """General class to handle tables of color schemes.
123
118
124 It's basically a dict of color schemes with a couple of shorthand
119 It's basically a dict of color schemes with a couple of shorthand
125 attributes and some convenient methods.
120 attributes and some convenient methods.
126
121
127 active_scheme_name -> obvious
122 active_scheme_name -> obvious
128 active_colors -> actual color table of the active scheme"""
123 active_colors -> actual color table of the active scheme"""
129
124
130 def __init__(self,scheme_list=None,default_scheme=''):
125 def __init__(self,scheme_list=None,default_scheme=''):
131 """Create a table of color schemes.
126 """Create a table of color schemes.
132
127
133 The table can be created empty and manually filled or it can be
128 The table can be created empty and manually filled or it can be
134 created with a list of valid color schemes AND the specification for
129 created with a list of valid color schemes AND the specification for
135 the default active scheme.
130 the default active scheme.
136 """
131 """
137
132
138 # create object attributes to be set later
133 # create object attributes to be set later
139 self.active_scheme_name = ''
134 self.active_scheme_name = ''
140 self.active_colors = None
135 self.active_colors = None
141
136
142 if scheme_list:
137 if scheme_list:
143 if default_scheme == '':
138 if default_scheme == '':
144 raise ValueError,'you must specify the default color scheme'
139 raise ValueError,'you must specify the default color scheme'
145 for scheme in scheme_list:
140 for scheme in scheme_list:
146 self.add_scheme(scheme)
141 self.add_scheme(scheme)
147 self.set_active_scheme(default_scheme)
142 self.set_active_scheme(default_scheme)
148
143
149 def copy(self):
144 def copy(self):
150 """Return full copy of object"""
145 """Return full copy of object"""
151 return ColorSchemeTable(self.values(),self.active_scheme_name)
146 return ColorSchemeTable(self.values(),self.active_scheme_name)
152
147
153 def add_scheme(self,new_scheme):
148 def add_scheme(self,new_scheme):
154 """Add a new color scheme to the table."""
149 """Add a new color scheme to the table."""
155 if not isinstance(new_scheme,ColorScheme):
150 if not isinstance(new_scheme,ColorScheme):
156 raise ValueError,'ColorSchemeTable only accepts ColorScheme instances'
151 raise ValueError,'ColorSchemeTable only accepts ColorScheme instances'
157 self[new_scheme.name] = new_scheme
152 self[new_scheme.name] = new_scheme
158
153
159 def set_active_scheme(self,scheme,case_sensitive=0):
154 def set_active_scheme(self,scheme,case_sensitive=0):
160 """Set the currently active scheme.
155 """Set the currently active scheme.
161
156
162 Names are by default compared in a case-insensitive way, but this can
157 Names are by default compared in a case-insensitive way, but this can
163 be changed by setting the parameter case_sensitive to true."""
158 be changed by setting the parameter case_sensitive to true."""
164
159
165 scheme_names = self.keys()
160 scheme_names = self.keys()
166 if case_sensitive:
161 if case_sensitive:
167 valid_schemes = scheme_names
162 valid_schemes = scheme_names
168 scheme_test = scheme
163 scheme_test = scheme
169 else:
164 else:
170 valid_schemes = [s.lower() for s in scheme_names]
165 valid_schemes = [s.lower() for s in scheme_names]
171 scheme_test = scheme.lower()
166 scheme_test = scheme.lower()
172 try:
167 try:
173 scheme_idx = valid_schemes.index(scheme_test)
168 scheme_idx = valid_schemes.index(scheme_test)
174 except ValueError:
169 except ValueError:
175 raise ValueError,'Unrecognized color scheme: ' + scheme + \
170 raise ValueError,'Unrecognized color scheme: ' + scheme + \
176 '\nValid schemes: '+str(scheme_names).replace("'', ",'')
171 '\nValid schemes: '+str(scheme_names).replace("'', ",'')
177 else:
172 else:
178 active = scheme_names[scheme_idx]
173 active = scheme_names[scheme_idx]
179 self.active_scheme_name = active
174 self.active_scheme_name = active
180 self.active_colors = self[active].colors
175 self.active_colors = self[active].colors
181 # Now allow using '' as an index for the current active scheme
176 # Now allow using '' as an index for the current active scheme
182 self[''] = self[active]
177 self[''] = self[active]
@@ -1,116 +1,111 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Configuration loader
2 """Configuration loader
3
3 """
4 $Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $"""
5
4
6 #*****************************************************************************
5 #*****************************************************************************
7 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
8 #
7 #
9 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
10 #*****************************************************************************
12
11
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16
17 import exceptions
12 import exceptions
18 import os
13 import os
19 from pprint import pprint
14 from pprint import pprint
20
15
21 from IPython import ultraTB
16 from IPython import ultraTB
22 from IPython.ipstruct import Struct
17 from IPython.ipstruct import Struct
23 from IPython.genutils import *
18 from IPython.genutils import *
24
19
25 class ConfigLoaderError(exceptions.Exception):
20 class ConfigLoaderError(exceptions.Exception):
26 """Exception for ConfigLoader class."""
21 """Exception for ConfigLoader class."""
27
22
28 def __init__(self,args=None):
23 def __init__(self,args=None):
29 self.args = args
24 self.args = args
30
25
31 class ConfigLoader:
26 class ConfigLoader:
32
27
33 """Configuration file loader capable of handling recursive inclusions and
28 """Configuration file loader capable of handling recursive inclusions and
34 with parametrized conflict resolution for multiply found keys."""
29 with parametrized conflict resolution for multiply found keys."""
35
30
36 def __init__(self,conflict=None,field_sep=None,reclimit=15):
31 def __init__(self,conflict=None,field_sep=None,reclimit=15):
37
32
38 """The reclimit parameter controls the number of recursive
33 """The reclimit parameter controls the number of recursive
39 configuration file inclusions. This way we can stop early on (before
34 configuration file inclusions. This way we can stop early on (before
40 python's own recursion limit is hit) if there is a circular
35 python's own recursion limit is hit) if there is a circular
41 inclusion.
36 inclusion.
42
37
43 - conflict: dictionary for conflict resolutions (see Struct.merge())
38 - conflict: dictionary for conflict resolutions (see Struct.merge())
44
39
45 """
40 """
46 self.conflict = conflict
41 self.conflict = conflict
47 self.field_sep = field_sep
42 self.field_sep = field_sep
48 self.reset(reclimit)
43 self.reset(reclimit)
49
44
50 def reset(self,reclimit=15):
45 def reset(self,reclimit=15):
51 self.reclimit = reclimit
46 self.reclimit = reclimit
52 self.recdepth = 0
47 self.recdepth = 0
53 self.included = []
48 self.included = []
54
49
55 def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw):
50 def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw):
56 """Load a configuration file, return the resulting Struct.
51 """Load a configuration file, return the resulting Struct.
57
52
58 Call: load_config(fname,convert=None,conflict=None,recurse_key='')
53 Call: load_config(fname,convert=None,conflict=None,recurse_key='')
59
54
60 - fname: file to load from.
55 - fname: file to load from.
61 - convert: dictionary of type conversions (see read_dict())
56 - convert: dictionary of type conversions (see read_dict())
62 - recurse_key: keyword in dictionary to trigger recursive file
57 - recurse_key: keyword in dictionary to trigger recursive file
63 inclusions.
58 inclusions.
64 """
59 """
65
60
66 if self.recdepth > self.reclimit:
61 if self.recdepth > self.reclimit:
67 raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\
62 raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\
68 'exceeded: ' + `self.recdepth` + \
63 'exceeded: ' + `self.recdepth` + \
69 '.\nMaybe you have a circular chain of inclusions?'
64 '.\nMaybe you have a circular chain of inclusions?'
70 self.recdepth += 1
65 self.recdepth += 1
71 fname = filefind(fname,incpath)
66 fname = filefind(fname,incpath)
72 data = Struct()
67 data = Struct()
73 # avoid including the same file more than once
68 # avoid including the same file more than once
74 if fname in self.included:
69 if fname in self.included:
75 return data
70 return data
76 Xinfo = ultraTB.AutoFormattedTB(color_scheme='NoColor')
71 Xinfo = ultraTB.AutoFormattedTB(color_scheme='NoColor')
77 if convert==None and recurse_key : convert = {qwflat:recurse_key}
72 if convert==None and recurse_key : convert = {qwflat:recurse_key}
78 # for production, change warn to 0:
73 # for production, change warn to 0:
79 data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1,
74 data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1,
80 warn=0,no_empty=0,**kw))
75 warn=0,no_empty=0,**kw))
81 # keep track of successfully loaded files
76 # keep track of successfully loaded files
82 self.included.append(fname)
77 self.included.append(fname)
83 if recurse_key in data:
78 if recurse_key in data:
84 for incfilename in data[recurse_key]:
79 for incfilename in data[recurse_key]:
85 found=0
80 found=0
86 try:
81 try:
87 incfile = filefind(incfilename,incpath)
82 incfile = filefind(incfilename,incpath)
88 except IOError:
83 except IOError:
89 if os.name in ['nt','dos']:
84 if os.name in ['nt','dos']:
90 try:
85 try:
91 # Try again with '.ini' extension
86 # Try again with '.ini' extension
92 incfilename += '.ini'
87 incfilename += '.ini'
93 incfile = filefind(incfilename,incpath)
88 incfile = filefind(incfilename,incpath)
94 except IOError:
89 except IOError:
95 found = 0
90 found = 0
96 else:
91 else:
97 found = 1
92 found = 1
98 else:
93 else:
99 found = 0
94 found = 0
100 else:
95 else:
101 found = 1
96 found = 1
102 if found:
97 if found:
103 try:
98 try:
104 data.merge(self.load(incfile,convert,recurse_key,
99 data.merge(self.load(incfile,convert,recurse_key,
105 incpath,**kw),
100 incpath,**kw),
106 self.conflict)
101 self.conflict)
107 except:
102 except:
108 Xinfo()
103 Xinfo()
109 warn('Problem loading included file: '+
104 warn('Problem loading included file: '+
110 `incfilename` + '. Ignoring it...')
105 `incfilename` + '. Ignoring it...')
111 else:
106 else:
112 warn('File `%s` not found. Included by %s' % (incfilename,fname))
107 warn('File `%s` not found. Included by %s' % (incfilename,fname))
113
108
114 return data
109 return data
115
110
116 # end ConfigLoader
111 # end ConfigLoader
@@ -1,228 +1,227 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3 """
4 $Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $"""
5
4
6 #*****************************************************************************
5 #*****************************************************************************
7 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
8 #
7 #
9 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
10 #*****************************************************************************
12
11
13 from IPython import Release
12 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
13 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
14 __license__ = Release.license
16 __version__ = Release.version
15 __version__ = Release.version
17
16
18 #****************************************************************************
17 #****************************************************************************
19 # Required modules
18 # Required modules
20
19
21 # From the standard library
20 # From the standard library
22 import os
21 import os
23 import sys
22 import sys
24 from pprint import pprint,pformat
23 from pprint import pprint,pformat
25
24
26 # Homebrewed
25 # Homebrewed
27 from IPython.Itpl import Itpl,itpl,printpl
26 from IPython.Itpl import Itpl,itpl,printpl
28 from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names
27 from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names
29 from IPython import ultraTB
28 from IPython import ultraTB
30 from IPython.genutils import *
29 from IPython.genutils import *
31
30
32 #****************************************************************************
31 #****************************************************************************
33 class CrashHandler:
32 class CrashHandler:
34 """Customizable crash handlers for IPython-based systems.
33 """Customizable crash handlers for IPython-based systems.
35
34
36 Instances of this class provide a __call__ method which can be used as a
35 Instances of this class provide a __call__ method which can be used as a
37 sys.excepthook, i.e., the __call__ signature is:
36 sys.excepthook, i.e., the __call__ signature is:
38
37
39 def __call__(self,etype, evalue, etb)
38 def __call__(self,etype, evalue, etb)
40
39
41 """
40 """
42
41
43 def __init__(self,IP,app_name,contact_name,contact_email,
42 def __init__(self,IP,app_name,contact_name,contact_email,
44 bug_tracker,crash_report_fname,
43 bug_tracker,crash_report_fname,
45 show_crash_traceback=True):
44 show_crash_traceback=True):
46 """New crash handler.
45 """New crash handler.
47
46
48 Inputs:
47 Inputs:
49
48
50 - IP: a running IPython instance, which will be queried at crash time
49 - IP: a running IPython instance, which will be queried at crash time
51 for internal information.
50 for internal information.
52
51
53 - app_name: a string containing the name of your application.
52 - app_name: a string containing the name of your application.
54
53
55 - contact_name: a string with the name of the person to contact.
54 - contact_name: a string with the name of the person to contact.
56
55
57 - contact_email: a string with the email address of the contact.
56 - contact_email: a string with the email address of the contact.
58
57
59 - bug_tracker: a string with the URL for your project's bug tracker.
58 - bug_tracker: a string with the URL for your project's bug tracker.
60
59
61 - crash_report_fname: a string with the filename for the crash report
60 - crash_report_fname: a string with the filename for the crash report
62 to be saved in. These reports are left in the ipython user directory
61 to be saved in. These reports are left in the ipython user directory
63 as determined by the running IPython instance.
62 as determined by the running IPython instance.
64
63
65 Optional inputs:
64 Optional inputs:
66
65
67 - show_crash_traceback(True): if false, don't print the crash
66 - show_crash_traceback(True): if false, don't print the crash
68 traceback on stderr, only generate the on-disk report
67 traceback on stderr, only generate the on-disk report
69
68
70
69
71 Non-argument instance attributes:
70 Non-argument instance attributes:
72
71
73 These instances contain some non-argument attributes which allow for
72 These instances contain some non-argument attributes which allow for
74 further customization of the crash handler's behavior. Please see the
73 further customization of the crash handler's behavior. Please see the
75 source for further details.
74 source for further details.
76 """
75 """
77
76
78 # apply args into instance
77 # apply args into instance
79 self.IP = IP # IPython instance
78 self.IP = IP # IPython instance
80 self.app_name = app_name
79 self.app_name = app_name
81 self.contact_name = contact_name
80 self.contact_name = contact_name
82 self.contact_email = contact_email
81 self.contact_email = contact_email
83 self.bug_tracker = bug_tracker
82 self.bug_tracker = bug_tracker
84 self.crash_report_fname = crash_report_fname
83 self.crash_report_fname = crash_report_fname
85 self.show_crash_traceback = show_crash_traceback
84 self.show_crash_traceback = show_crash_traceback
86
85
87 # Hardcoded defaults, which can be overridden either by subclasses or
86 # Hardcoded defaults, which can be overridden either by subclasses or
88 # at runtime for the instance.
87 # at runtime for the instance.
89
88
90 # Template for the user message. Subclasses which completely override
89 # Template for the user message. Subclasses which completely override
91 # this, or user apps, can modify it to suit their tastes. It gets
90 # this, or user apps, can modify it to suit their tastes. It gets
92 # expanded using itpl, so calls of the kind $self.foo are valid.
91 # expanded using itpl, so calls of the kind $self.foo are valid.
93 self.user_message_template = """
92 self.user_message_template = """
94 Oops, $self.app_name crashed. We do our best to make it stable, but...
93 Oops, $self.app_name crashed. We do our best to make it stable, but...
95
94
96 A crash report was automatically generated with the following information:
95 A crash report was automatically generated with the following information:
97 - A verbatim copy of the crash traceback.
96 - A verbatim copy of the crash traceback.
98 - A copy of your input history during this session.
97 - A copy of your input history during this session.
99 - Data on your current $self.app_name configuration.
98 - Data on your current $self.app_name configuration.
100
99
101 It was left in the file named:
100 It was left in the file named:
102 \t'$self.crash_report_fname'
101 \t'$self.crash_report_fname'
103 If you can email this file to the developers, the information in it will help
102 If you can email this file to the developers, the information in it will help
104 them in understanding and correcting the problem.
103 them in understanding and correcting the problem.
105
104
106 You can mail it to: $self.contact_name at $self.contact_email
105 You can mail it to: $self.contact_name at $self.contact_email
107 with the subject '$self.app_name Crash Report'.
106 with the subject '$self.app_name Crash Report'.
108
107
109 If you want to do it now, the following command will work (under Unix):
108 If you want to do it now, the following command will work (under Unix):
110 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
109 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
111
110
112 To ensure accurate tracking of this issue, please file a report about it at:
111 To ensure accurate tracking of this issue, please file a report about it at:
113 $self.bug_tracker
112 $self.bug_tracker
114 """
113 """
115
114
116 def __call__(self,etype, evalue, etb):
115 def __call__(self,etype, evalue, etb):
117 """Handle an exception, call for compatible with sys.excepthook"""
116 """Handle an exception, call for compatible with sys.excepthook"""
118
117
119 # Report tracebacks shouldn't use color in general (safer for users)
118 # Report tracebacks shouldn't use color in general (safer for users)
120 color_scheme = 'NoColor'
119 color_scheme = 'NoColor'
121
120
122 # Use this ONLY for developer debugging (keep commented out for release)
121 # Use this ONLY for developer debugging (keep commented out for release)
123 #color_scheme = 'Linux' # dbg
122 #color_scheme = 'Linux' # dbg
124
123
125 try:
124 try:
126 rptdir = self.IP.rc.ipythondir
125 rptdir = self.IP.rc.ipythondir
127 except:
126 except:
128 rptdir = os.getcwd()
127 rptdir = os.getcwd()
129 if not os.path.isdir(rptdir):
128 if not os.path.isdir(rptdir):
130 rptdir = os.getcwd()
129 rptdir = os.getcwd()
131 report_name = os.path.join(rptdir,self.crash_report_fname)
130 report_name = os.path.join(rptdir,self.crash_report_fname)
132 # write the report filename into the instance dict so it can get
131 # write the report filename into the instance dict so it can get
133 # properly expanded out in the user message template
132 # properly expanded out in the user message template
134 self.crash_report_fname = report_name
133 self.crash_report_fname = report_name
135 TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme,
134 TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme,
136 long_header=1)
135 long_header=1)
137 traceback = TBhandler.text(etype,evalue,etb,context=31)
136 traceback = TBhandler.text(etype,evalue,etb,context=31)
138
137
139 # print traceback to screen
138 # print traceback to screen
140 if self.show_crash_traceback:
139 if self.show_crash_traceback:
141 print >> sys.stderr, traceback
140 print >> sys.stderr, traceback
142
141
143 # and generate a complete report on disk
142 # and generate a complete report on disk
144 try:
143 try:
145 report = open(report_name,'w')
144 report = open(report_name,'w')
146 except:
145 except:
147 print >> sys.stderr, 'Could not create crash report on disk.'
146 print >> sys.stderr, 'Could not create crash report on disk.'
148 return
147 return
149
148
150 # Inform user on stderr of what happened
149 # Inform user on stderr of what happened
151 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
150 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
152 print >> sys.stderr, msg
151 print >> sys.stderr, msg
153
152
154 # Construct report on disk
153 # Construct report on disk
155 report.write(self.make_report(traceback))
154 report.write(self.make_report(traceback))
156 report.close()
155 report.close()
157 raw_input("Press enter to exit:")
156 raw_input("Press enter to exit:")
158
157
159 def make_report(self,traceback):
158 def make_report(self,traceback):
160 """Return a string containing a crash report."""
159 """Return a string containing a crash report."""
161
160
162 sec_sep = '\n\n'+'*'*75+'\n\n'
161 sec_sep = '\n\n'+'*'*75+'\n\n'
163
162
164 report = []
163 report = []
165 rpt_add = report.append
164 rpt_add = report.append
166
165
167 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
166 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
168 rpt_add('IPython version: %s \n\n' % Release.version)
167 rpt_add('IPython version: %s \n\n' % Release.version)
169 rpt_add('SVN revision : %s \n\n' % Release.revision)
168 rpt_add('SVN revision : %s \n\n' % Release.revision)
170 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
169 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
171 (os.name,sys.platform) )
170 (os.name,sys.platform) )
172 rpt_add(sec_sep+'Current user configuration structure:\n\n')
171 rpt_add(sec_sep+'Current user configuration structure:\n\n')
173 rpt_add(pformat(self.IP.rc.dict()))
172 rpt_add(pformat(self.IP.rc.dict()))
174 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
173 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
175 try:
174 try:
176 rpt_add(sec_sep+"History of session input:")
175 rpt_add(sec_sep+"History of session input:")
177 for line in self.IP.user_ns['_ih']:
176 for line in self.IP.user_ns['_ih']:
178 rpt_add(line)
177 rpt_add(line)
179 rpt_add('\n*** Last line of input (may not be in above history):\n')
178 rpt_add('\n*** Last line of input (may not be in above history):\n')
180 rpt_add(self.IP._last_input_line+'\n')
179 rpt_add(self.IP._last_input_line+'\n')
181 except:
180 except:
182 pass
181 pass
183
182
184 return ''.join(report)
183 return ''.join(report)
185
184
186 class IPythonCrashHandler(CrashHandler):
185 class IPythonCrashHandler(CrashHandler):
187 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
186 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
188
187
189 def __init__(self,IP):
188 def __init__(self,IP):
190
189
191 # Set here which of the IPython authors should be listed as contact
190 # Set here which of the IPython authors should be listed as contact
192 AUTHOR_CONTACT = 'Ville'
191 AUTHOR_CONTACT = 'Ville'
193
192
194 # Set argument defaults
193 # Set argument defaults
195 app_name = 'IPython'
194 app_name = 'IPython'
196 bug_tracker = 'http://projects.scipy.org/ipython/ipython/report'
195 bug_tracker = 'http://projects.scipy.org/ipython/ipython/report'
197 contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2]
196 contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2]
198 crash_report_fname = 'IPython_crash_report.txt'
197 crash_report_fname = 'IPython_crash_report.txt'
199 # Call parent constructor
198 # Call parent constructor
200 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
199 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
201 bug_tracker,crash_report_fname)
200 bug_tracker,crash_report_fname)
202
201
203 def make_report(self,traceback):
202 def make_report(self,traceback):
204 """Return a string containing a crash report."""
203 """Return a string containing a crash report."""
205
204
206 sec_sep = '\n\n'+'*'*75+'\n\n'
205 sec_sep = '\n\n'+'*'*75+'\n\n'
207
206
208 report = []
207 report = []
209 rpt_add = report.append
208 rpt_add = report.append
210
209
211 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
210 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
212 rpt_add('IPython version: %s \n\n' % Release.version)
211 rpt_add('IPython version: %s \n\n' % Release.version)
213 rpt_add('SVN revision : %s \n\n' % Release.revision)
212 rpt_add('SVN revision : %s \n\n' % Release.revision)
214 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
213 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
215 (os.name,sys.platform) )
214 (os.name,sys.platform) )
216 rpt_add(sec_sep+'Current user configuration structure:\n\n')
215 rpt_add(sec_sep+'Current user configuration structure:\n\n')
217 rpt_add(pformat(self.IP.rc.dict()))
216 rpt_add(pformat(self.IP.rc.dict()))
218 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
217 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
219 try:
218 try:
220 rpt_add(sec_sep+"History of session input:")
219 rpt_add(sec_sep+"History of session input:")
221 for line in self.IP.user_ns['_ih']:
220 for line in self.IP.user_ns['_ih']:
222 rpt_add(line)
221 rpt_add(line)
223 rpt_add('\n*** Last line of input (may not be in above history):\n')
222 rpt_add('\n*** Last line of input (may not be in above history):\n')
224 rpt_add(self.IP._last_input_line+'\n')
223 rpt_add(self.IP._last_input_line+'\n')
225 except:
224 except:
226 pass
225 pass
227
226
228 return ''.join(report)
227 return ''.join(report)
@@ -1,693 +1,690 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """DPyGetOpt -- Demiurge Python GetOptions Module
2 """DPyGetOpt -- Demiurge Python GetOptions Module
3
3
4 $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $
5
6 This module is modeled after perl's Getopt::Long module-- which
4 This module is modeled after perl's Getopt::Long module-- which
7 is, in turn, modeled after GNU's extended getopt() function.
5 is, in turn, modeled after GNU's extended getopt() function.
8
6
9 Upon instantiation, the option specification should be a sequence
7 Upon instantiation, the option specification should be a sequence
10 (list) of option definitions.
8 (list) of option definitions.
11
9
12 Options that take no arguments should simply contain the name of
10 Options that take no arguments should simply contain the name of
13 the option. If a ! is post-pended, the option can be negated by
11 the option. If a ! is post-pended, the option can be negated by
14 prepending 'no'; ie 'debug!' specifies that -debug and -nodebug
12 prepending 'no'; ie 'debug!' specifies that -debug and -nodebug
15 should be accepted.
13 should be accepted.
16
14
17 Mandatory arguments to options are specified using a postpended
15 Mandatory arguments to options are specified using a postpended
18 '=' + a type specifier. '=s' specifies a mandatory string
16 '=' + a type specifier. '=s' specifies a mandatory string
19 argument, '=i' specifies a mandatory integer argument, and '=f'
17 argument, '=i' specifies a mandatory integer argument, and '=f'
20 specifies a mandatory real number. In all cases, the '=' can be
18 specifies a mandatory real number. In all cases, the '=' can be
21 substituted with ':' to specify that the argument is optional.
19 substituted with ':' to specify that the argument is optional.
22
20
23 Dashes '-' in option names are allowed.
21 Dashes '-' in option names are allowed.
24
22
25 If an option has the character '@' postpended (after the
23 If an option has the character '@' postpended (after the
26 argumentation specification), it can appear multiple times within
24 argumentation specification), it can appear multiple times within
27 each argument list that is processed. The results will be stored
25 each argument list that is processed. The results will be stored
28 in a list.
26 in a list.
29
27
30 The option name can actually be a list of names separated by '|'
28 The option name can actually be a list of names separated by '|'
31 characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar,
29 characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar,
32 and -baz options that appear on within the parsed argument list
30 and -baz options that appear on within the parsed argument list
33 must have a real number argument and that the accumulated list
31 must have a real number argument and that the accumulated list
34 of values will be available under the name 'foo'
32 of values will be available under the name 'foo'
35
33 """
36 $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $"""
37
34
38 #*****************************************************************************
35 #*****************************************************************************
39 #
36 #
40 # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com>
37 # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com>
41 #
38 #
42 #
39 #
43 # Published under the terms of the MIT license, hereby reproduced:
40 # Published under the terms of the MIT license, hereby reproduced:
44 #
41 #
45 # Permission is hereby granted, free of charge, to any person obtaining a copy
42 # Permission is hereby granted, free of charge, to any person obtaining a copy
46 # of this software and associated documentation files (the "Software"), to
43 # of this software and associated documentation files (the "Software"), to
47 # deal in the Software without restriction, including without limitation the
44 # deal in the Software without restriction, including without limitation the
48 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
45 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
49 # sell copies of the Software, and to permit persons to whom the Software is
46 # sell copies of the Software, and to permit persons to whom the Software is
50 # furnished to do so, subject to the following conditions:
47 # furnished to do so, subject to the following conditions:
51 #
48 #
52 # The above copyright notice and this permission notice shall be included in
49 # The above copyright notice and this permission notice shall be included in
53 # all copies or substantial portions of the Software.
50 # all copies or substantial portions of the Software.
54 #
51 #
55 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
56 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
60 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
57 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
61 # IN THE SOFTWARE.
58 # IN THE SOFTWARE.
62 #
59 #
63 #*****************************************************************************
60 #*****************************************************************************
64
61
65 __author__ = 'Bill Bumgarner <bbum@friday.com>'
62 __author__ = 'Bill Bumgarner <bbum@friday.com>'
66 __license__ = 'MIT'
63 __license__ = 'MIT'
67 __version__ = '1.2'
64 __version__ = '1.2'
68
65
69 # Modified to use re instead of regex and regsub modules.
66 # Modified to use re instead of regex and regsub modules.
70 # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com>
67 # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com>
71
68
72 import re
69 import re
73 import string
70 import string
74 import sys
71 import sys
75 import types
72 import types
76
73
77 class Error(Exception):
74 class Error(Exception):
78 """Base class for exceptions in the DPyGetOpt module."""
75 """Base class for exceptions in the DPyGetOpt module."""
79
76
80 class ArgumentError(Error):
77 class ArgumentError(Error):
81 """Exception indicating an error in the arguments passed to
78 """Exception indicating an error in the arguments passed to
82 DPyGetOpt.processArguments."""
79 DPyGetOpt.processArguments."""
83
80
84 class SpecificationError(Error):
81 class SpecificationError(Error):
85 """Exception indicating an error with an option specification."""
82 """Exception indicating an error with an option specification."""
86
83
87 class TerminationError(Error):
84 class TerminationError(Error):
88 """Exception indicating an error with an option processing terminator."""
85 """Exception indicating an error with an option processing terminator."""
89
86
90 specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)')
87 specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)')
91
88
92 ArgRequired = 'Requires an Argument'
89 ArgRequired = 'Requires an Argument'
93 ArgOptional = 'Argument Optional'
90 ArgOptional = 'Argument Optional'
94
91
95 # The types modules is not used for these identifiers because there
92 # The types modules is not used for these identifiers because there
96 # is no identifier for 'boolean' or 'generic'
93 # is no identifier for 'boolean' or 'generic'
97 StringArgType = 'String Argument Type'
94 StringArgType = 'String Argument Type'
98 IntegerArgType = 'Integer Argument Type'
95 IntegerArgType = 'Integer Argument Type'
99 RealArgType = 'Real Argument Type'
96 RealArgType = 'Real Argument Type'
100 BooleanArgType = 'Boolean Argument Type'
97 BooleanArgType = 'Boolean Argument Type'
101 GenericArgType = 'Generic Argument Type'
98 GenericArgType = 'Generic Argument Type'
102
99
103 # dictionary of conversion functions-- boolean and generic options
100 # dictionary of conversion functions-- boolean and generic options
104 # do not accept arguments and do not need conversion functions;
101 # do not accept arguments and do not need conversion functions;
105 # the identity function is used purely for convenience.
102 # the identity function is used purely for convenience.
106 ConversionFunctions = {
103 ConversionFunctions = {
107 StringArgType : lambda x: x,
104 StringArgType : lambda x: x,
108 IntegerArgType : string.atoi,
105 IntegerArgType : string.atoi,
109 RealArgType : string.atof,
106 RealArgType : string.atof,
110 BooleanArgType : lambda x: x,
107 BooleanArgType : lambda x: x,
111 GenericArgType : lambda x: x,
108 GenericArgType : lambda x: x,
112 }
109 }
113
110
114 class DPyGetOpt:
111 class DPyGetOpt:
115
112
116 def __init__(self, spec = None, terminators = ['--']):
113 def __init__(self, spec = None, terminators = ['--']):
117 """
114 """
118 Declare and intialize instance variables
115 Declare and intialize instance variables
119
116
120 Yes, declaration is not necessary... but one of the things
117 Yes, declaration is not necessary... but one of the things
121 I sorely miss from C/Obj-C is the concept of having an
118 I sorely miss from C/Obj-C is the concept of having an
122 interface definition that clearly declares all instance
119 interface definition that clearly declares all instance
123 variables and methods without providing any implementation
120 variables and methods without providing any implementation
124 details. it is a useful reference!
121 details. it is a useful reference!
125
122
126 all instance variables are initialized to 0/Null/None of
123 all instance variables are initialized to 0/Null/None of
127 the appropriate type-- not even the default value...
124 the appropriate type-- not even the default value...
128 """
125 """
129
126
130 # sys.stderr.write(string.join(spec) + "\n")
127 # sys.stderr.write(string.join(spec) + "\n")
131
128
132 self.allowAbbreviations = 1 # boolean, 1 if abbreviations will
129 self.allowAbbreviations = 1 # boolean, 1 if abbreviations will
133 # be expanded
130 # be expanded
134 self.freeValues = [] # list, contains free values
131 self.freeValues = [] # list, contains free values
135 self.ignoreCase = 0 # boolean, YES if ignoring case
132 self.ignoreCase = 0 # boolean, YES if ignoring case
136 self.needsParse = 0 # boolean, YES if need to reparse parameter spec
133 self.needsParse = 0 # boolean, YES if need to reparse parameter spec
137 self.optionNames = {} # dict, all option names-- value is index of tuple
134 self.optionNames = {} # dict, all option names-- value is index of tuple
138 self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--')
135 self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--')
139 self.optionTuples = [] # list o' tuples containing defn of options AND aliases
136 self.optionTuples = [] # list o' tuples containing defn of options AND aliases
140 self.optionValues = {} # dict, option names (after alias expansion) -> option value(s)
137 self.optionValues = {} # dict, option names (after alias expansion) -> option value(s)
141 self.orderMixed = 0 # boolean, YES if options can be mixed with args
138 self.orderMixed = 0 # boolean, YES if options can be mixed with args
142 self.posixCompliance = 0 # boolean, YES indicates posix like behaviour
139 self.posixCompliance = 0 # boolean, YES indicates posix like behaviour
143 self.spec = [] # list, raw specs (in case it must be reparsed)
140 self.spec = [] # list, raw specs (in case it must be reparsed)
144 self.terminators = terminators # list, strings that terminate argument processing
141 self.terminators = terminators # list, strings that terminate argument processing
145 self.termValues = [] # list, values after terminator
142 self.termValues = [] # list, values after terminator
146 self.terminator = None # full name of terminator that ended
143 self.terminator = None # full name of terminator that ended
147 # option processing
144 # option processing
148
145
149 # set up defaults
146 # set up defaults
150 self.setPosixCompliance()
147 self.setPosixCompliance()
151 self.setIgnoreCase()
148 self.setIgnoreCase()
152 self.setAllowAbbreviations()
149 self.setAllowAbbreviations()
153
150
154 # parse spec-- if present
151 # parse spec-- if present
155 if spec:
152 if spec:
156 self.parseConfiguration(spec)
153 self.parseConfiguration(spec)
157
154
158 def setPosixCompliance(self, aFlag = 0):
155 def setPosixCompliance(self, aFlag = 0):
159 """
156 """
160 Enables and disables posix compliance.
157 Enables and disables posix compliance.
161
158
162 When enabled, '+' can be used as an option prefix and free
159 When enabled, '+' can be used as an option prefix and free
163 values can be mixed with options.
160 values can be mixed with options.
164 """
161 """
165 self.posixCompliance = aFlag
162 self.posixCompliance = aFlag
166 self.needsParse = 1
163 self.needsParse = 1
167
164
168 if self.posixCompliance:
165 if self.posixCompliance:
169 self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
166 self.optionStartExpr = re.compile('(--|-)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
170 self.orderMixed = 0
167 self.orderMixed = 0
171 else:
168 else:
172 self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
169 self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?')
173 self.orderMixed = 1
170 self.orderMixed = 1
174
171
175 def isPosixCompliant(self):
172 def isPosixCompliant(self):
176 """
173 """
177 Returns the value of the posix compliance flag.
174 Returns the value of the posix compliance flag.
178 """
175 """
179 return self.posixCompliance
176 return self.posixCompliance
180
177
181 def setIgnoreCase(self, aFlag = 1):
178 def setIgnoreCase(self, aFlag = 1):
182 """
179 """
183 Enables and disables ignoring case during option processing.
180 Enables and disables ignoring case during option processing.
184 """
181 """
185 self.needsParse = 1
182 self.needsParse = 1
186 self.ignoreCase = aFlag
183 self.ignoreCase = aFlag
187
184
188 def ignoreCase(self):
185 def ignoreCase(self):
189 """
186 """
190 Returns 1 if the option processor will ignore case when
187 Returns 1 if the option processor will ignore case when
191 processing options.
188 processing options.
192 """
189 """
193 return self.ignoreCase
190 return self.ignoreCase
194
191
195 def setAllowAbbreviations(self, aFlag = 1):
192 def setAllowAbbreviations(self, aFlag = 1):
196 """
193 """
197 Enables and disables the expansion of abbreviations during
194 Enables and disables the expansion of abbreviations during
198 option processing.
195 option processing.
199 """
196 """
200 self.allowAbbreviations = aFlag
197 self.allowAbbreviations = aFlag
201
198
202 def willAllowAbbreviations(self):
199 def willAllowAbbreviations(self):
203 """
200 """
204 Returns 1 if abbreviated options will be automatically
201 Returns 1 if abbreviated options will be automatically
205 expanded to the non-abbreviated form (instead of causing an
202 expanded to the non-abbreviated form (instead of causing an
206 unrecognized option error).
203 unrecognized option error).
207 """
204 """
208 return self.allowAbbreviations
205 return self.allowAbbreviations
209
206
210 def addTerminator(self, newTerm):
207 def addTerminator(self, newTerm):
211 """
208 """
212 Adds newTerm as terminator of option processing.
209 Adds newTerm as terminator of option processing.
213
210
214 Whenever the option processor encounters one of the terminators
211 Whenever the option processor encounters one of the terminators
215 during option processing, the processing of options terminates
212 during option processing, the processing of options terminates
216 immediately, all remaining options are stored in the termValues
213 immediately, all remaining options are stored in the termValues
217 instance variable and the full name of the terminator is stored
214 instance variable and the full name of the terminator is stored
218 in the terminator instance variable.
215 in the terminator instance variable.
219 """
216 """
220 self.terminators = self.terminators + [newTerm]
217 self.terminators = self.terminators + [newTerm]
221
218
222 def _addOption(self, oTuple):
219 def _addOption(self, oTuple):
223 """
220 """
224 Adds the option described by oTuple (name, (type, mode,
221 Adds the option described by oTuple (name, (type, mode,
225 default), alias) to optionTuples. Adds index keyed under name
222 default), alias) to optionTuples. Adds index keyed under name
226 to optionNames. Raises SpecificationError if name already in
223 to optionNames. Raises SpecificationError if name already in
227 optionNames
224 optionNames
228 """
225 """
229 (name, (type, mode, default, multi), realName) = oTuple
226 (name, (type, mode, default, multi), realName) = oTuple
230
227
231 # verify name and add to option names dictionary
228 # verify name and add to option names dictionary
232 if self.optionNames.has_key(name):
229 if self.optionNames.has_key(name):
233 if realName:
230 if realName:
234 raise SpecificationError('Alias \'' + name + '\' for \'' +
231 raise SpecificationError('Alias \'' + name + '\' for \'' +
235 realName +
232 realName +
236 '\' already used for another option or alias.')
233 '\' already used for another option or alias.')
237 else:
234 else:
238 raise SpecificationError('Option named \'' + name +
235 raise SpecificationError('Option named \'' + name +
239 '\' specified more than once. Specification: '
236 '\' specified more than once. Specification: '
240 + option)
237 + option)
241
238
242 # validated. add to optionNames
239 # validated. add to optionNames
243 self.optionNames[name] = self.tupleIndex
240 self.optionNames[name] = self.tupleIndex
244 self.tupleIndex = self.tupleIndex + 1
241 self.tupleIndex = self.tupleIndex + 1
245
242
246 # add to optionTuples
243 # add to optionTuples
247 self.optionTuples = self.optionTuples + [oTuple]
244 self.optionTuples = self.optionTuples + [oTuple]
248
245
249 # if type is boolean, add negation
246 # if type is boolean, add negation
250 if type == BooleanArgType:
247 if type == BooleanArgType:
251 alias = 'no' + name
248 alias = 'no' + name
252 specTuple = (type, mode, 0, multi)
249 specTuple = (type, mode, 0, multi)
253 oTuple = (alias, specTuple, name)
250 oTuple = (alias, specTuple, name)
254
251
255 # verify name and add to option names dictionary
252 # verify name and add to option names dictionary
256 if self.optionNames.has_key(alias):
253 if self.optionNames.has_key(alias):
257 if realName:
254 if realName:
258 raise SpecificationError('Negated alias \'' + name +
255 raise SpecificationError('Negated alias \'' + name +
259 '\' for \'' + realName +
256 '\' for \'' + realName +
260 '\' already used for another option or alias.')
257 '\' already used for another option or alias.')
261 else:
258 else:
262 raise SpecificationError('Negated option named \'' + name +
259 raise SpecificationError('Negated option named \'' + name +
263 '\' specified more than once. Specification: '
260 '\' specified more than once. Specification: '
264 + option)
261 + option)
265
262
266 # validated. add to optionNames
263 # validated. add to optionNames
267 self.optionNames[alias] = self.tupleIndex
264 self.optionNames[alias] = self.tupleIndex
268 self.tupleIndex = self.tupleIndex + 1
265 self.tupleIndex = self.tupleIndex + 1
269
266
270 # add to optionTuples
267 # add to optionTuples
271 self.optionTuples = self.optionTuples + [oTuple]
268 self.optionTuples = self.optionTuples + [oTuple]
272
269
273 def addOptionConfigurationTuple(self, oTuple):
270 def addOptionConfigurationTuple(self, oTuple):
274 (name, argSpec, realName) = oTuple
271 (name, argSpec, realName) = oTuple
275 if self.ignoreCase:
272 if self.ignoreCase:
276 name = string.lower(name)
273 name = string.lower(name)
277 if realName:
274 if realName:
278 realName = string.lower(realName)
275 realName = string.lower(realName)
279 else:
276 else:
280 realName = name
277 realName = name
281
278
282 oTuple = (name, argSpec, realName)
279 oTuple = (name, argSpec, realName)
283
280
284 # add option
281 # add option
285 self._addOption(oTuple)
282 self._addOption(oTuple)
286
283
287 def addOptionConfigurationTuples(self, oTuple):
284 def addOptionConfigurationTuples(self, oTuple):
288 if type(oTuple) is ListType:
285 if type(oTuple) is ListType:
289 for t in oTuple:
286 for t in oTuple:
290 self.addOptionConfigurationTuple(t)
287 self.addOptionConfigurationTuple(t)
291 else:
288 else:
292 self.addOptionConfigurationTuple(oTuple)
289 self.addOptionConfigurationTuple(oTuple)
293
290
294 def parseConfiguration(self, spec):
291 def parseConfiguration(self, spec):
295 # destroy previous stored information + store raw spec
292 # destroy previous stored information + store raw spec
296 self.spec = spec
293 self.spec = spec
297 self.optionTuples = []
294 self.optionTuples = []
298 self.optionNames = {}
295 self.optionNames = {}
299 self.tupleIndex = 0
296 self.tupleIndex = 0
300
297
301 tupleIndex = 0
298 tupleIndex = 0
302
299
303 # create some regex's for parsing each spec
300 # create some regex's for parsing each spec
304 splitExpr = \
301 splitExpr = \
305 re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?')
302 re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?')
306 for option in spec:
303 for option in spec:
307 # push to lower case (does not negatively affect
304 # push to lower case (does not negatively affect
308 # specification)
305 # specification)
309 if self.ignoreCase:
306 if self.ignoreCase:
310 option = string.lower(option)
307 option = string.lower(option)
311
308
312 # break into names, specification
309 # break into names, specification
313 match = splitExpr.match(option)
310 match = splitExpr.match(option)
314 if match is None:
311 if match is None:
315 raise SpecificationError('Invalid specification {' + option +
312 raise SpecificationError('Invalid specification {' + option +
316 '}')
313 '}')
317
314
318 names = match.group('names')
315 names = match.group('names')
319 specification = match.group('spec')
316 specification = match.group('spec')
320
317
321 # break name into name, aliases
318 # break name into name, aliases
322 nlist = string.split(names, '|')
319 nlist = string.split(names, '|')
323
320
324 # get name
321 # get name
325 name = nlist[0]
322 name = nlist[0]
326 aliases = nlist[1:]
323 aliases = nlist[1:]
327
324
328 # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)')
325 # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)')
329 if not specification:
326 if not specification:
330 #spec tuple is ('type', 'arg mode', 'default value', 'multiple')
327 #spec tuple is ('type', 'arg mode', 'default value', 'multiple')
331 argType = GenericArgType
328 argType = GenericArgType
332 argMode = None
329 argMode = None
333 argDefault = 1
330 argDefault = 1
334 argMultiple = 0
331 argMultiple = 0
335 elif specification == '!':
332 elif specification == '!':
336 argType = BooleanArgType
333 argType = BooleanArgType
337 argMode = None
334 argMode = None
338 argDefault = 1
335 argDefault = 1
339 argMultiple = 0
336 argMultiple = 0
340 else:
337 else:
341 # parse
338 # parse
342 match = specificationExpr.match(specification)
339 match = specificationExpr.match(specification)
343 if match is None:
340 if match is None:
344 # failed to parse, die
341 # failed to parse, die
345 raise SpecificationError('Invalid configuration for option \''
342 raise SpecificationError('Invalid configuration for option \''
346 + option + '\'')
343 + option + '\'')
347
344
348 # determine mode
345 # determine mode
349 required = match.group('required')
346 required = match.group('required')
350 if required == '=':
347 if required == '=':
351 argMode = ArgRequired
348 argMode = ArgRequired
352 elif required == ':':
349 elif required == ':':
353 argMode = ArgOptional
350 argMode = ArgOptional
354 else:
351 else:
355 raise SpecificationError('Unknown requirement configuration \''
352 raise SpecificationError('Unknown requirement configuration \''
356 + required + '\'')
353 + required + '\'')
357
354
358 # determine type
355 # determine type
359 type = match.group('type')
356 type = match.group('type')
360 if type == 's':
357 if type == 's':
361 argType = StringArgType
358 argType = StringArgType
362 argDefault = ''
359 argDefault = ''
363 elif type == 'i':
360 elif type == 'i':
364 argType = IntegerArgType
361 argType = IntegerArgType
365 argDefault = 1
362 argDefault = 1
366 elif type == 'f' or type == 'n':
363 elif type == 'f' or type == 'n':
367 argType = RealArgType
364 argType = RealArgType
368 argDefault = 1
365 argDefault = 1
369 else:
366 else:
370 raise SpecificationError('Unknown type specifier \'' +
367 raise SpecificationError('Unknown type specifier \'' +
371 type + '\'')
368 type + '\'')
372
369
373 # determine quantity
370 # determine quantity
374 if match.group('multi') == '@':
371 if match.group('multi') == '@':
375 argMultiple = 1
372 argMultiple = 1
376 else:
373 else:
377 argMultiple = 0
374 argMultiple = 0
378 ## end else (of not specification)
375 ## end else (of not specification)
379
376
380 # construct specification tuple
377 # construct specification tuple
381 specTuple = (argType, argMode, argDefault, argMultiple)
378 specTuple = (argType, argMode, argDefault, argMultiple)
382
379
383 # add the option-- option tuple is (name, specTuple, real name)
380 # add the option-- option tuple is (name, specTuple, real name)
384 oTuple = (name, specTuple, name)
381 oTuple = (name, specTuple, name)
385 self._addOption(oTuple)
382 self._addOption(oTuple)
386
383
387 for alias in aliases:
384 for alias in aliases:
388 # drop to all lower (if configured to do so)
385 # drop to all lower (if configured to do so)
389 if self.ignoreCase:
386 if self.ignoreCase:
390 alias = string.lower(alias)
387 alias = string.lower(alias)
391 # create configuration tuple
388 # create configuration tuple
392 oTuple = (alias, specTuple, name)
389 oTuple = (alias, specTuple, name)
393 # add
390 # add
394 self._addOption(oTuple)
391 self._addOption(oTuple)
395
392
396 # successfully parsed....
393 # successfully parsed....
397 self.needsParse = 0
394 self.needsParse = 0
398
395
399 def _getArgTuple(self, argName):
396 def _getArgTuple(self, argName):
400 """
397 """
401 Returns a list containing all the specification tuples that
398 Returns a list containing all the specification tuples that
402 match argName. If none match, None is returned. If one
399 match argName. If none match, None is returned. If one
403 matches, a list with one tuple is returned. If more than one
400 matches, a list with one tuple is returned. If more than one
404 match, a list containing all the tuples that matched is
401 match, a list containing all the tuples that matched is
405 returned.
402 returned.
406
403
407 In other words, this function does not pass judgement upon the
404 In other words, this function does not pass judgement upon the
408 validity of multiple matches.
405 validity of multiple matches.
409 """
406 """
410 # is it in the optionNames dict?
407 # is it in the optionNames dict?
411
408
412 try:
409 try:
413 # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n")
410 # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n")
414
411
415 # yes, get index
412 # yes, get index
416 tupleIndex = self.optionNames[argName]
413 tupleIndex = self.optionNames[argName]
417 # and return tuple as element of list
414 # and return tuple as element of list
418 return [self.optionTuples[tupleIndex]]
415 return [self.optionTuples[tupleIndex]]
419 except KeyError:
416 except KeyError:
420 # are abbreviations allowed?
417 # are abbreviations allowed?
421 if not self.allowAbbreviations:
418 if not self.allowAbbreviations:
422 # No! terefore, this cannot be valid argument-- nothing found
419 # No! terefore, this cannot be valid argument-- nothing found
423 return None
420 return None
424
421
425 # argName might be an abbreviation (and, abbreviations must
422 # argName might be an abbreviation (and, abbreviations must
426 # be allowed... or this would not have been reached!)
423 # be allowed... or this would not have been reached!)
427
424
428 # create regex for argName
425 # create regex for argName
429 argExpr = re.compile('^' + argName)
426 argExpr = re.compile('^' + argName)
430
427
431 tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None,
428 tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None,
432 self.optionTuples)
429 self.optionTuples)
433
430
434 if not len(tuples):
431 if not len(tuples):
435 return None
432 return None
436 else:
433 else:
437 return tuples
434 return tuples
438
435
439 def _isTerminator(self, optionName):
436 def _isTerminator(self, optionName):
440 """
437 """
441 Returns the full name of the terminator if optionName is a valid
438 Returns the full name of the terminator if optionName is a valid
442 terminator. If it is, sets self.terminator to the full name of
439 terminator. If it is, sets self.terminator to the full name of
443 the terminator.
440 the terminator.
444
441
445 If more than one terminator matched, raises a TerminationError with a
442 If more than one terminator matched, raises a TerminationError with a
446 string describing the ambiguity.
443 string describing the ambiguity.
447 """
444 """
448
445
449 # sys.stderr.write(optionName + "\n")
446 # sys.stderr.write(optionName + "\n")
450 # sys.stderr.write(repr(self.terminators))
447 # sys.stderr.write(repr(self.terminators))
451
448
452 if optionName in self.terminators:
449 if optionName in self.terminators:
453 self.terminator = optionName
450 self.terminator = optionName
454 elif not self.allowAbbreviations:
451 elif not self.allowAbbreviations:
455 return None
452 return None
456
453
457 # regex thing in bogus
454 # regex thing in bogus
458 # termExpr = regex.compile('^' + optionName)
455 # termExpr = regex.compile('^' + optionName)
459
456
460 terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators)
457 terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators)
461
458
462 if not len(terms):
459 if not len(terms):
463 return None
460 return None
464 elif len(terms) > 1:
461 elif len(terms) > 1:
465 raise TerminationError('Ambiguous terminator \'' + optionName +
462 raise TerminationError('Ambiguous terminator \'' + optionName +
466 '\' matches ' + repr(terms))
463 '\' matches ' + repr(terms))
467
464
468 self.terminator = terms[0]
465 self.terminator = terms[0]
469 return self.terminator
466 return self.terminator
470
467
471 def processArguments(self, args = None):
468 def processArguments(self, args = None):
472 """
469 """
473 Processes args, a list of arguments (including options).
470 Processes args, a list of arguments (including options).
474
471
475 If args is the same as sys.argv, automatically trims the first
472 If args is the same as sys.argv, automatically trims the first
476 argument (the executable name/path).
473 argument (the executable name/path).
477
474
478 If an exception is not raised, the argument list was parsed
475 If an exception is not raised, the argument list was parsed
479 correctly.
476 correctly.
480
477
481 Upon successful completion, the freeValues instance variable
478 Upon successful completion, the freeValues instance variable
482 will contain all the arguments that were not associated with an
479 will contain all the arguments that were not associated with an
483 option in the order they were encountered. optionValues is a
480 option in the order they were encountered. optionValues is a
484 dictionary containing the value of each option-- the method
481 dictionary containing the value of each option-- the method
485 valueForOption() can be used to query this dictionary.
482 valueForOption() can be used to query this dictionary.
486 terminator will contain the argument encountered that terminated
483 terminator will contain the argument encountered that terminated
487 option processing (or None, if a terminator was never
484 option processing (or None, if a terminator was never
488 encountered) and termValues will contain all of the options that
485 encountered) and termValues will contain all of the options that
489 appeared after the Terminator (or an empty list).
486 appeared after the Terminator (or an empty list).
490 """
487 """
491
488
492 if hasattr(sys, "argv") and args == sys.argv:
489 if hasattr(sys, "argv") and args == sys.argv:
493 args = sys.argv[1:]
490 args = sys.argv[1:]
494
491
495 max = len(args) # maximum index + 1
492 max = len(args) # maximum index + 1
496 self.freeValues = [] # array to hold return values
493 self.freeValues = [] # array to hold return values
497 self.optionValues= {}
494 self.optionValues= {}
498 index = 0 # initial index
495 index = 0 # initial index
499 self.terminator = None
496 self.terminator = None
500 self.termValues = []
497 self.termValues = []
501
498
502 while index < max:
499 while index < max:
503 # obtain argument
500 # obtain argument
504 arg = args[index]
501 arg = args[index]
505 # increment index -- REMEMBER; it is NOW incremented
502 # increment index -- REMEMBER; it is NOW incremented
506 index = index + 1
503 index = index + 1
507
504
508 # terminate immediately if option terminator encountered
505 # terminate immediately if option terminator encountered
509 if self._isTerminator(arg):
506 if self._isTerminator(arg):
510 self.freeValues = self.freeValues + args[index:]
507 self.freeValues = self.freeValues + args[index:]
511 self.termValues = args[index:]
508 self.termValues = args[index:]
512 return
509 return
513
510
514 # is this possibly an option?
511 # is this possibly an option?
515 match = self.optionStartExpr.match(arg)
512 match = self.optionStartExpr.match(arg)
516 if match is None:
513 if match is None:
517 # not an option-- add to freeValues
514 # not an option-- add to freeValues
518 self.freeValues = self.freeValues + [arg]
515 self.freeValues = self.freeValues + [arg]
519 if not self.orderMixed:
516 if not self.orderMixed:
520 # mixing not allowed; add rest of args as freeValues
517 # mixing not allowed; add rest of args as freeValues
521 self.freeValues = self.freeValues + args[index:]
518 self.freeValues = self.freeValues + args[index:]
522 # return to caller
519 # return to caller
523 return
520 return
524 else:
521 else:
525 continue
522 continue
526
523
527 # grab name
524 # grab name
528 optName = match.group('option')
525 optName = match.group('option')
529
526
530 # obtain next argument-- index has already been incremented
527 # obtain next argument-- index has already been incremented
531 nextArg = match.group('arg')
528 nextArg = match.group('arg')
532 if nextArg:
529 if nextArg:
533 nextArg = nextArg[1:]
530 nextArg = nextArg[1:]
534 index = index - 1 # put it back
531 index = index - 1 # put it back
535 else:
532 else:
536 try:
533 try:
537 nextArg = args[index]
534 nextArg = args[index]
538 except:
535 except:
539 nextArg = None
536 nextArg = None
540
537
541 # transpose to lower case, if necessary
538 # transpose to lower case, if necessary
542 if self.ignoreCase:
539 if self.ignoreCase:
543 optName = string.lower(optName)
540 optName = string.lower(optName)
544
541
545 # obtain defining tuple
542 # obtain defining tuple
546 tuples = self._getArgTuple(optName)
543 tuples = self._getArgTuple(optName)
547
544
548 if tuples == None:
545 if tuples == None:
549 raise ArgumentError('Illegal option \'' + arg + '\'')
546 raise ArgumentError('Illegal option \'' + arg + '\'')
550 elif len(tuples) > 1:
547 elif len(tuples) > 1:
551 raise ArgumentError('Ambiguous option \'' + arg +
548 raise ArgumentError('Ambiguous option \'' + arg +
552 '\'; matches ' +
549 '\'; matches ' +
553 repr(map(lambda x: x[0], tuples)))
550 repr(map(lambda x: x[0], tuples)))
554 else:
551 else:
555 config = tuples[0]
552 config = tuples[0]
556
553
557 # config is now set to the configuration tuple for the
554 # config is now set to the configuration tuple for the
558 # argument
555 # argument
559 (fullName, spec, realName) = config
556 (fullName, spec, realName) = config
560 (optType, optMode, optDefault, optMultiple) = spec
557 (optType, optMode, optDefault, optMultiple) = spec
561
558
562 # if opt mode required, but nextArg is none, raise an error
559 # if opt mode required, but nextArg is none, raise an error
563 if (optMode == ArgRequired):
560 if (optMode == ArgRequired):
564 if (not nextArg) or self._isTerminator(nextArg):
561 if (not nextArg) or self._isTerminator(nextArg):
565 # print nextArg
562 # print nextArg
566 raise ArgumentError('Option \'' + arg +
563 raise ArgumentError('Option \'' + arg +
567 '\' requires an argument of type ' +
564 '\' requires an argument of type ' +
568 optType)
565 optType)
569
566
570 if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)):
567 if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)):
571 # nextArg defined, option configured to possibly consume arg
568 # nextArg defined, option configured to possibly consume arg
572 try:
569 try:
573 # grab conversion function-- the try is more for internal diagnostics
570 # grab conversion function-- the try is more for internal diagnostics
574 func = ConversionFunctions[optType]
571 func = ConversionFunctions[optType]
575 try:
572 try:
576 optionValue = func(nextArg)
573 optionValue = func(nextArg)
577 index = index + 1
574 index = index + 1
578 except:
575 except:
579 # only raise conversion error if REQUIRED to consume argument
576 # only raise conversion error if REQUIRED to consume argument
580 if optMode == ArgRequired:
577 if optMode == ArgRequired:
581 raise ArgumentError('Invalid argument to option \''
578 raise ArgumentError('Invalid argument to option \''
582 + arg + '\'; should be \'' +
579 + arg + '\'; should be \'' +
583 optType + '\'')
580 optType + '\'')
584 else:
581 else:
585 optionValue = optDefault
582 optionValue = optDefault
586 except ArgumentError:
583 except ArgumentError:
587 raise
584 raise
588 except:
585 except:
589 raise ArgumentError('(' + arg +
586 raise ArgumentError('(' + arg +
590 ') Conversion function for \'' +
587 ') Conversion function for \'' +
591 optType + '\' not found.')
588 optType + '\' not found.')
592 else:
589 else:
593 optionValue = optDefault
590 optionValue = optDefault
594
591
595 # add value to options dictionary
592 # add value to options dictionary
596 if optMultiple:
593 if optMultiple:
597 # can be multiple values
594 # can be multiple values
598 try:
595 try:
599 # try to append element
596 # try to append element
600 self.optionValues[realName] = self.optionValues[realName] + [optionValue]
597 self.optionValues[realName] = self.optionValues[realName] + [optionValue]
601 except:
598 except:
602 # failed-- must not exist; add it
599 # failed-- must not exist; add it
603 self.optionValues[realName] = [optionValue]
600 self.optionValues[realName] = [optionValue]
604 else:
601 else:
605 # only one value per
602 # only one value per
606 if self.isPosixCompliant and self.optionValues.has_key(realName):
603 if self.isPosixCompliant and self.optionValues.has_key(realName):
607 raise ArgumentError('Argument \'' + arg +
604 raise ArgumentError('Argument \'' + arg +
608 '\' occurs multiple times.')
605 '\' occurs multiple times.')
609
606
610 self.optionValues[realName] = optionValue
607 self.optionValues[realName] = optionValue
611
608
612 def valueForOption(self, optionName, defaultValue = None):
609 def valueForOption(self, optionName, defaultValue = None):
613 """
610 """
614 Return the value associated with optionName. If optionName was
611 Return the value associated with optionName. If optionName was
615 not encountered during parsing of the arguments, returns the
612 not encountered during parsing of the arguments, returns the
616 defaultValue (which defaults to None).
613 defaultValue (which defaults to None).
617 """
614 """
618 try:
615 try:
619 optionValue = self.optionValues[optionName]
616 optionValue = self.optionValues[optionName]
620 except:
617 except:
621 optionValue = defaultValue
618 optionValue = defaultValue
622
619
623 return optionValue
620 return optionValue
624
621
625 ##
622 ##
626 ## test/example section
623 ## test/example section
627 ##
624 ##
628 test_error = 'Test Run Amok!'
625 test_error = 'Test Run Amok!'
629 def _test():
626 def _test():
630 """
627 """
631 A relatively complete test suite.
628 A relatively complete test suite.
632 """
629 """
633 try:
630 try:
634 DPyGetOpt(['foo', 'bar=s', 'foo'])
631 DPyGetOpt(['foo', 'bar=s', 'foo'])
635 except Error, exc:
632 except Error, exc:
636 print 'EXCEPTION (should be \'foo\' already used..): %s' % exc
633 print 'EXCEPTION (should be \'foo\' already used..): %s' % exc
637
634
638 try:
635 try:
639 DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!'])
636 DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!'])
640 except Error, exc:
637 except Error, exc:
641 print 'EXCEPTION (should be duplicate alias/name error): %s' % exc
638 print 'EXCEPTION (should be duplicate alias/name error): %s' % exc
642
639
643 x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@'])
640 x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@'])
644 try:
641 try:
645 x.processArguments(['-app', '29.3'])
642 x.processArguments(['-app', '29.3'])
646 except Error, exc:
643 except Error, exc:
647 print 'EXCEPTION (should be ambiguous argument): %s' % exc
644 print 'EXCEPTION (should be ambiguous argument): %s' % exc
648
645
649 x = DPyGetOpt(['foo'], ['antigravity', 'antithesis'])
646 x = DPyGetOpt(['foo'], ['antigravity', 'antithesis'])
650 try:
647 try:
651 x.processArguments(['-foo', 'anti'])
648 x.processArguments(['-foo', 'anti'])
652 except Error, exc:
649 except Error, exc:
653 print 'EXCEPTION (should be ambiguous terminator): %s' % exc
650 print 'EXCEPTION (should be ambiguous terminator): %s' % exc
654
651
655 profile = ['plain-option',
652 profile = ['plain-option',
656 'boolean-option!',
653 'boolean-option!',
657 'list-of-integers=i@',
654 'list-of-integers=i@',
658 'list-real-option|list-real-alias|list-real-pseudonym=f@',
655 'list-real-option|list-real-alias|list-real-pseudonym=f@',
659 'optional-string-option:s',
656 'optional-string-option:s',
660 'abbreviated-string-list=s@']
657 'abbreviated-string-list=s@']
661
658
662 terminators = ['terminator']
659 terminators = ['terminator']
663
660
664 args = ['-plain-option',
661 args = ['-plain-option',
665 '+noboolean-option',
662 '+noboolean-option',
666 '--list-of-integers', '1',
663 '--list-of-integers', '1',
667 '+list-of-integers', '2',
664 '+list-of-integers', '2',
668 '-list-of-integers', '3',
665 '-list-of-integers', '3',
669 'freeargone',
666 'freeargone',
670 '-list-real-option', '1.1',
667 '-list-real-option', '1.1',
671 '+list-real-alias', '1.2',
668 '+list-real-alias', '1.2',
672 '--list-real-pseudonym', '1.3',
669 '--list-real-pseudonym', '1.3',
673 'freeargtwo',
670 'freeargtwo',
674 '-abbreviated-string-list', 'String1',
671 '-abbreviated-string-list', 'String1',
675 '--abbreviated-s', 'String2',
672 '--abbreviated-s', 'String2',
676 '-abbrev', 'String3',
673 '-abbrev', 'String3',
677 '-a', 'String4',
674 '-a', 'String4',
678 '-optional-string-option',
675 '-optional-string-option',
679 'term',
676 'term',
680 'next option should look like an invalid arg',
677 'next option should look like an invalid arg',
681 '-a']
678 '-a']
682
679
683
680
684 print 'Using profile: ' + repr(profile)
681 print 'Using profile: ' + repr(profile)
685 print 'With terminator: ' + repr(terminators)
682 print 'With terminator: ' + repr(terminators)
686 print 'Processing arguments: ' + repr(args)
683 print 'Processing arguments: ' + repr(args)
687
684
688 go = DPyGetOpt(profile, terminators)
685 go = DPyGetOpt(profile, terminators)
689 go.processArguments(args)
686 go.processArguments(args)
690
687
691 print 'Options (and values): ' + repr(go.optionValues)
688 print 'Options (and values): ' + repr(go.optionValues)
692 print 'free args: ' + repr(go.freeValues)
689 print 'free args: ' + repr(go.freeValues)
693 print 'term args: ' + repr(go.termValues)
690 print 'term args: ' + repr(go.termValues)
@@ -1,528 +1,522 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 http://www.python.org/2.2.3/license.html
16 http://www.python.org/2.2.3/license.html"""
17
18 $Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $"""
19
17
20 #*****************************************************************************
18 #*****************************************************************************
21 #
19 #
22 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
23 #
21 #
24 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
25 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
26 #
24 #
27 #
25 #
28 #*****************************************************************************
26 #*****************************************************************************
29
27
30 from IPython import Release
31 __author__ = '%s <%s>' % Release.authors['Fernando']
32 __license__ = 'Python'
33
34 import bdb
28 import bdb
35 import cmd
29 import cmd
36 import linecache
30 import linecache
37 import os
31 import os
38 import sys
32 import sys
39
33
40 from IPython import PyColorize, ColorANSI, ipapi
34 from IPython import PyColorize, ColorANSI, ipapi
41 from IPython.genutils import Term
35 from IPython.genutils import Term
42 from IPython.excolors import exception_colors
36 from IPython.excolors import exception_colors
43
37
44 # See if we can use pydb.
38 # See if we can use pydb.
45 has_pydb = False
39 has_pydb = False
46 prompt = 'ipdb> '
40 prompt = 'ipdb> '
47 #We have to check this directly from sys.argv, config struct not yet available
41 #We have to check this directly from sys.argv, config struct not yet available
48 if '-pydb' in sys.argv:
42 if '-pydb' in sys.argv:
49 try:
43 try:
50 import pydb
44 import pydb
51 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
45 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
52 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
46 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
53 # better protect against it.
47 # better protect against it.
54 has_pydb = True
48 has_pydb = True
55 except ImportError:
49 except ImportError:
56 print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
50 print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available"
57
51
58 if has_pydb:
52 if has_pydb:
59 from pydb import Pdb as OldPdb
53 from pydb import Pdb as OldPdb
60 #print "Using pydb for %run -d and post-mortem" #dbg
54 #print "Using pydb for %run -d and post-mortem" #dbg
61 prompt = 'ipydb> '
55 prompt = 'ipydb> '
62 else:
56 else:
63 from pdb import Pdb as OldPdb
57 from pdb import Pdb as OldPdb
64
58
65 # Allow the set_trace code to operate outside of an ipython instance, even if
59 # Allow the set_trace code to operate outside of an ipython instance, even if
66 # it does so with some limitations. The rest of this support is implemented in
60 # it does so with some limitations. The rest of this support is implemented in
67 # the Tracer constructor.
61 # the Tracer constructor.
68 def BdbQuit_excepthook(et,ev,tb):
62 def BdbQuit_excepthook(et,ev,tb):
69 if et==bdb.BdbQuit:
63 if et==bdb.BdbQuit:
70 print 'Exiting Debugger.'
64 print 'Exiting Debugger.'
71 else:
65 else:
72 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
66 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
73
67
74 def BdbQuit_IPython_excepthook(self,et,ev,tb):
68 def BdbQuit_IPython_excepthook(self,et,ev,tb):
75 print 'Exiting Debugger.'
69 print 'Exiting Debugger.'
76
70
77 class Tracer(object):
71 class Tracer(object):
78 """Class for local debugging, similar to pdb.set_trace.
72 """Class for local debugging, similar to pdb.set_trace.
79
73
80 Instances of this class, when called, behave like pdb.set_trace, but
74 Instances of this class, when called, behave like pdb.set_trace, but
81 providing IPython's enhanced capabilities.
75 providing IPython's enhanced capabilities.
82
76
83 This is implemented as a class which must be initialized in your own code
77 This is implemented as a class which must be initialized in your own code
84 and not as a standalone function because we need to detect at runtime
78 and not as a standalone function because we need to detect at runtime
85 whether IPython is already active or not. That detection is done in the
79 whether IPython is already active or not. That detection is done in the
86 constructor, ensuring that this code plays nicely with a running IPython,
80 constructor, ensuring that this code plays nicely with a running IPython,
87 while functioning acceptably (though with limitations) if outside of it.
81 while functioning acceptably (though with limitations) if outside of it.
88 """
82 """
89
83
90 def __init__(self,colors=None):
84 def __init__(self,colors=None):
91 """Create a local debugger instance.
85 """Create a local debugger instance.
92
86
93 :Parameters:
87 :Parameters:
94
88
95 - `colors` (None): a string containing the name of the color scheme to
89 - `colors` (None): a string containing the name of the color scheme to
96 use, it must be one of IPython's valid color schemes. If not given, the
90 use, it must be one of IPython's valid color schemes. If not given, the
97 function will default to the current IPython scheme when running inside
91 function will default to the current IPython scheme when running inside
98 IPython, and to 'NoColor' otherwise.
92 IPython, and to 'NoColor' otherwise.
99
93
100 Usage example:
94 Usage example:
101
95
102 from IPython.Debugger import Tracer; debug_here = Tracer()
96 from IPython.Debugger import Tracer; debug_here = Tracer()
103
97
104 ... later in your code
98 ... later in your code
105 debug_here() # -> will open up the debugger at that point.
99 debug_here() # -> will open up the debugger at that point.
106
100
107 Once the debugger activates, you can use all of its regular commands to
101 Once the debugger activates, you can use all of its regular commands to
108 step through code, set breakpoints, etc. See the pdb documentation
102 step through code, set breakpoints, etc. See the pdb documentation
109 from the Python standard library for usage details.
103 from the Python standard library for usage details.
110 """
104 """
111
105
112 global __IPYTHON__
106 global __IPYTHON__
113 try:
107 try:
114 __IPYTHON__
108 __IPYTHON__
115 except NameError:
109 except NameError:
116 # Outside of ipython, we set our own exception hook manually
110 # Outside of ipython, we set our own exception hook manually
117 __IPYTHON__ = ipapi.get(True,False)
111 __IPYTHON__ = ipapi.get(True,False)
118 BdbQuit_excepthook.excepthook_ori = sys.excepthook
112 BdbQuit_excepthook.excepthook_ori = sys.excepthook
119 sys.excepthook = BdbQuit_excepthook
113 sys.excepthook = BdbQuit_excepthook
120 def_colors = 'NoColor'
114 def_colors = 'NoColor'
121 try:
115 try:
122 # Limited tab completion support
116 # Limited tab completion support
123 import rlcompleter,readline
117 import rlcompleter,readline
124 readline.parse_and_bind('tab: complete')
118 readline.parse_and_bind('tab: complete')
125 except ImportError:
119 except ImportError:
126 pass
120 pass
127 else:
121 else:
128 # In ipython, we use its custom exception handler mechanism
122 # In ipython, we use its custom exception handler mechanism
129 ip = ipapi.get()
123 ip = ipapi.get()
130 def_colors = ip.options.colors
124 def_colors = ip.options.colors
131 ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
125 ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
132
126
133 if colors is None:
127 if colors is None:
134 colors = def_colors
128 colors = def_colors
135 self.debugger = Pdb(colors)
129 self.debugger = Pdb(colors)
136
130
137 def __call__(self):
131 def __call__(self):
138 """Starts an interactive debugger at the point where called.
132 """Starts an interactive debugger at the point where called.
139
133
140 This is similar to the pdb.set_trace() function from the std lib, but
134 This is similar to the pdb.set_trace() function from the std lib, but
141 using IPython's enhanced debugger."""
135 using IPython's enhanced debugger."""
142
136
143 self.debugger.set_trace(sys._getframe().f_back)
137 self.debugger.set_trace(sys._getframe().f_back)
144
138
145 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
139 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
146 """Make new_fn have old_fn's doc string. This is particularly useful
140 """Make new_fn have old_fn's doc string. This is particularly useful
147 for the do_... commands that hook into the help system.
141 for the do_... commands that hook into the help system.
148 Adapted from from a comp.lang.python posting
142 Adapted from from a comp.lang.python posting
149 by Duncan Booth."""
143 by Duncan Booth."""
150 def wrapper(*args, **kw):
144 def wrapper(*args, **kw):
151 return new_fn(*args, **kw)
145 return new_fn(*args, **kw)
152 if old_fn.__doc__:
146 if old_fn.__doc__:
153 wrapper.__doc__ = old_fn.__doc__ + additional_text
147 wrapper.__doc__ = old_fn.__doc__ + additional_text
154 return wrapper
148 return wrapper
155
149
156 def _file_lines(fname):
150 def _file_lines(fname):
157 """Return the contents of a named file as a list of lines.
151 """Return the contents of a named file as a list of lines.
158
152
159 This function never raises an IOError exception: if the file can't be
153 This function never raises an IOError exception: if the file can't be
160 read, it simply returns an empty list."""
154 read, it simply returns an empty list."""
161
155
162 try:
156 try:
163 outfile = open(fname)
157 outfile = open(fname)
164 except IOError:
158 except IOError:
165 return []
159 return []
166 else:
160 else:
167 out = outfile.readlines()
161 out = outfile.readlines()
168 outfile.close()
162 outfile.close()
169 return out
163 return out
170
164
171 class Pdb(OldPdb):
165 class Pdb(OldPdb):
172 """Modified Pdb class, does not load readline."""
166 """Modified Pdb class, does not load readline."""
173
167
174 if sys.version[:3] >= '2.5' or has_pydb:
168 if sys.version[:3] >= '2.5' or has_pydb:
175 def __init__(self,color_scheme='NoColor',completekey=None,
169 def __init__(self,color_scheme='NoColor',completekey=None,
176 stdin=None, stdout=None):
170 stdin=None, stdout=None):
177
171
178 # Parent constructor:
172 # Parent constructor:
179 if has_pydb and completekey is None:
173 if has_pydb and completekey is None:
180 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
174 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
181 else:
175 else:
182 OldPdb.__init__(self,completekey,stdin,stdout)
176 OldPdb.__init__(self,completekey,stdin,stdout)
183
177
184 self.prompt = prompt # The default prompt is '(Pdb)'
178 self.prompt = prompt # The default prompt is '(Pdb)'
185
179
186 # IPython changes...
180 # IPython changes...
187 self.is_pydb = has_pydb
181 self.is_pydb = has_pydb
188
182
189 if self.is_pydb:
183 if self.is_pydb:
190
184
191 # iplib.py's ipalias seems to want pdb's checkline
185 # iplib.py's ipalias seems to want pdb's checkline
192 # which located in pydb.fn
186 # which located in pydb.fn
193 import pydb.fns
187 import pydb.fns
194 self.checkline = lambda filename, lineno: \
188 self.checkline = lambda filename, lineno: \
195 pydb.fns.checkline(self, filename, lineno)
189 pydb.fns.checkline(self, filename, lineno)
196
190
197 self.curframe = None
191 self.curframe = None
198 self.do_restart = self.new_do_restart
192 self.do_restart = self.new_do_restart
199
193
200 self.old_all_completions = __IPYTHON__.Completer.all_completions
194 self.old_all_completions = __IPYTHON__.Completer.all_completions
201 __IPYTHON__.Completer.all_completions=self.all_completions
195 __IPYTHON__.Completer.all_completions=self.all_completions
202
196
203 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
197 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
204 OldPdb.do_list)
198 OldPdb.do_list)
205 self.do_l = self.do_list
199 self.do_l = self.do_list
206 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
200 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
207 OldPdb.do_frame)
201 OldPdb.do_frame)
208
202
209 self.aliases = {}
203 self.aliases = {}
210
204
211 # Create color table: we copy the default one from the traceback
205 # Create color table: we copy the default one from the traceback
212 # module and add a few attributes needed for debugging
206 # module and add a few attributes needed for debugging
213 self.color_scheme_table = exception_colors()
207 self.color_scheme_table = exception_colors()
214
208
215 # shorthands
209 # shorthands
216 C = ColorANSI.TermColors
210 C = ColorANSI.TermColors
217 cst = self.color_scheme_table
211 cst = self.color_scheme_table
218
212
219 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
213 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
220 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
214 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
221
215
222 cst['Linux'].colors.breakpoint_enabled = C.LightRed
216 cst['Linux'].colors.breakpoint_enabled = C.LightRed
223 cst['Linux'].colors.breakpoint_disabled = C.Red
217 cst['Linux'].colors.breakpoint_disabled = C.Red
224
218
225 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
219 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
226 cst['LightBG'].colors.breakpoint_disabled = C.Red
220 cst['LightBG'].colors.breakpoint_disabled = C.Red
227
221
228 self.set_colors(color_scheme)
222 self.set_colors(color_scheme)
229
223
230 # Add a python parser so we can syntax highlight source while
224 # Add a python parser so we can syntax highlight source while
231 # debugging.
225 # debugging.
232 self.parser = PyColorize.Parser()
226 self.parser = PyColorize.Parser()
233
227
234
228
235 else:
229 else:
236 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
230 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
237 # because it binds readline and breaks tab-completion. This means we
231 # because it binds readline and breaks tab-completion. This means we
238 # have to COPY the constructor here.
232 # have to COPY the constructor here.
239 def __init__(self,color_scheme='NoColor'):
233 def __init__(self,color_scheme='NoColor'):
240 bdb.Bdb.__init__(self)
234 bdb.Bdb.__init__(self)
241 cmd.Cmd.__init__(self,completekey=None) # don't load readline
235 cmd.Cmd.__init__(self,completekey=None) # don't load readline
242 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
236 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
243 self.aliases = {}
237 self.aliases = {}
244
238
245 # These two lines are part of the py2.4 constructor, let's put them
239 # These two lines are part of the py2.4 constructor, let's put them
246 # unconditionally here as they won't cause any problems in 2.3.
240 # unconditionally here as they won't cause any problems in 2.3.
247 self.mainpyfile = ''
241 self.mainpyfile = ''
248 self._wait_for_mainpyfile = 0
242 self._wait_for_mainpyfile = 0
249
243
250 # Read $HOME/.pdbrc and ./.pdbrc
244 # Read $HOME/.pdbrc and ./.pdbrc
251 try:
245 try:
252 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
246 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
253 ".pdbrc"))
247 ".pdbrc"))
254 except KeyError:
248 except KeyError:
255 self.rcLines = []
249 self.rcLines = []
256 self.rcLines.extend(_file_lines(".pdbrc"))
250 self.rcLines.extend(_file_lines(".pdbrc"))
257
251
258 # Create color table: we copy the default one from the traceback
252 # Create color table: we copy the default one from the traceback
259 # module and add a few attributes needed for debugging
253 # module and add a few attributes needed for debugging
260 self.color_scheme_table = exception_colors()
254 self.color_scheme_table = exception_colors()
261
255
262 # shorthands
256 # shorthands
263 C = ColorANSI.TermColors
257 C = ColorANSI.TermColors
264 cst = self.color_scheme_table
258 cst = self.color_scheme_table
265
259
266 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
260 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
267 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
261 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
268
262
269 cst['Linux'].colors.breakpoint_enabled = C.LightRed
263 cst['Linux'].colors.breakpoint_enabled = C.LightRed
270 cst['Linux'].colors.breakpoint_disabled = C.Red
264 cst['Linux'].colors.breakpoint_disabled = C.Red
271
265
272 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
273 cst['LightBG'].colors.breakpoint_disabled = C.Red
267 cst['LightBG'].colors.breakpoint_disabled = C.Red
274
268
275 self.set_colors(color_scheme)
269 self.set_colors(color_scheme)
276
270
277 # Add a python parser so we can syntax highlight source while
271 # Add a python parser so we can syntax highlight source while
278 # debugging.
272 # debugging.
279 self.parser = PyColorize.Parser()
273 self.parser = PyColorize.Parser()
280
274
281 def set_colors(self, scheme):
275 def set_colors(self, scheme):
282 """Shorthand access to the color table scheme selector method."""
276 """Shorthand access to the color table scheme selector method."""
283 self.color_scheme_table.set_active_scheme(scheme)
277 self.color_scheme_table.set_active_scheme(scheme)
284
278
285 def interaction(self, frame, traceback):
279 def interaction(self, frame, traceback):
286 __IPYTHON__.set_completer_frame(frame)
280 __IPYTHON__.set_completer_frame(frame)
287 OldPdb.interaction(self, frame, traceback)
281 OldPdb.interaction(self, frame, traceback)
288
282
289 def new_do_up(self, arg):
283 def new_do_up(self, arg):
290 OldPdb.do_up(self, arg)
284 OldPdb.do_up(self, arg)
291 __IPYTHON__.set_completer_frame(self.curframe)
285 __IPYTHON__.set_completer_frame(self.curframe)
292 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
286 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
293
287
294 def new_do_down(self, arg):
288 def new_do_down(self, arg):
295 OldPdb.do_down(self, arg)
289 OldPdb.do_down(self, arg)
296 __IPYTHON__.set_completer_frame(self.curframe)
290 __IPYTHON__.set_completer_frame(self.curframe)
297
291
298 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
292 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
299
293
300 def new_do_frame(self, arg):
294 def new_do_frame(self, arg):
301 OldPdb.do_frame(self, arg)
295 OldPdb.do_frame(self, arg)
302 __IPYTHON__.set_completer_frame(self.curframe)
296 __IPYTHON__.set_completer_frame(self.curframe)
303
297
304 def new_do_quit(self, arg):
298 def new_do_quit(self, arg):
305
299
306 if hasattr(self, 'old_all_completions'):
300 if hasattr(self, 'old_all_completions'):
307 __IPYTHON__.Completer.all_completions=self.old_all_completions
301 __IPYTHON__.Completer.all_completions=self.old_all_completions
308
302
309
303
310 return OldPdb.do_quit(self, arg)
304 return OldPdb.do_quit(self, arg)
311
305
312 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
306 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
313
307
314 def new_do_restart(self, arg):
308 def new_do_restart(self, arg):
315 """Restart command. In the context of ipython this is exactly the same
309 """Restart command. In the context of ipython this is exactly the same
316 thing as 'quit'."""
310 thing as 'quit'."""
317 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
311 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
318 return self.do_quit(arg)
312 return self.do_quit(arg)
319
313
320 def postloop(self):
314 def postloop(self):
321 __IPYTHON__.set_completer_frame(None)
315 __IPYTHON__.set_completer_frame(None)
322
316
323 def print_stack_trace(self):
317 def print_stack_trace(self):
324 try:
318 try:
325 for frame_lineno in self.stack:
319 for frame_lineno in self.stack:
326 self.print_stack_entry(frame_lineno, context = 5)
320 self.print_stack_entry(frame_lineno, context = 5)
327 except KeyboardInterrupt:
321 except KeyboardInterrupt:
328 pass
322 pass
329
323
330 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
324 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
331 context = 3):
325 context = 3):
332 #frame, lineno = frame_lineno
326 #frame, lineno = frame_lineno
333 print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
327 print >>Term.cout, self.format_stack_entry(frame_lineno, '', context)
334
328
335 # vds: >>
329 # vds: >>
336 frame, lineno = frame_lineno
330 frame, lineno = frame_lineno
337 filename = frame.f_code.co_filename
331 filename = frame.f_code.co_filename
338 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
332 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
339 # vds: <<
333 # vds: <<
340
334
341 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
335 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
342 import linecache, repr
336 import linecache, repr
343
337
344 ret = []
338 ret = []
345
339
346 Colors = self.color_scheme_table.active_colors
340 Colors = self.color_scheme_table.active_colors
347 ColorsNormal = Colors.Normal
341 ColorsNormal = Colors.Normal
348 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
342 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
349 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
343 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
350 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
344 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
351 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
345 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
352 ColorsNormal)
346 ColorsNormal)
353
347
354 frame, lineno = frame_lineno
348 frame, lineno = frame_lineno
355
349
356 return_value = ''
350 return_value = ''
357 if '__return__' in frame.f_locals:
351 if '__return__' in frame.f_locals:
358 rv = frame.f_locals['__return__']
352 rv = frame.f_locals['__return__']
359 #return_value += '->'
353 #return_value += '->'
360 return_value += repr.repr(rv) + '\n'
354 return_value += repr.repr(rv) + '\n'
361 ret.append(return_value)
355 ret.append(return_value)
362
356
363 #s = filename + '(' + `lineno` + ')'
357 #s = filename + '(' + `lineno` + ')'
364 filename = self.canonic(frame.f_code.co_filename)
358 filename = self.canonic(frame.f_code.co_filename)
365 link = tpl_link % filename
359 link = tpl_link % filename
366
360
367 if frame.f_code.co_name:
361 if frame.f_code.co_name:
368 func = frame.f_code.co_name
362 func = frame.f_code.co_name
369 else:
363 else:
370 func = "<lambda>"
364 func = "<lambda>"
371
365
372 call = ''
366 call = ''
373 if func != '?':
367 if func != '?':
374 if '__args__' in frame.f_locals:
368 if '__args__' in frame.f_locals:
375 args = repr.repr(frame.f_locals['__args__'])
369 args = repr.repr(frame.f_locals['__args__'])
376 else:
370 else:
377 args = '()'
371 args = '()'
378 call = tpl_call % (func, args)
372 call = tpl_call % (func, args)
379
373
380 # The level info should be generated in the same format pdb uses, to
374 # The level info should be generated in the same format pdb uses, to
381 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
375 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
382 if frame is self.curframe:
376 if frame is self.curframe:
383 ret.append('> ')
377 ret.append('> ')
384 else:
378 else:
385 ret.append(' ')
379 ret.append(' ')
386 ret.append('%s(%s)%s\n' % (link,lineno,call))
380 ret.append('%s(%s)%s\n' % (link,lineno,call))
387
381
388 start = lineno - 1 - context//2
382 start = lineno - 1 - context//2
389 lines = linecache.getlines(filename)
383 lines = linecache.getlines(filename)
390 start = max(start, 0)
384 start = max(start, 0)
391 start = min(start, len(lines) - context)
385 start = min(start, len(lines) - context)
392 lines = lines[start : start + context]
386 lines = lines[start : start + context]
393
387
394 for i,line in enumerate(lines):
388 for i,line in enumerate(lines):
395 show_arrow = (start + 1 + i == lineno)
389 show_arrow = (start + 1 + i == lineno)
396 linetpl = (frame is self.curframe or show_arrow) \
390 linetpl = (frame is self.curframe or show_arrow) \
397 and tpl_line_em \
391 and tpl_line_em \
398 or tpl_line
392 or tpl_line
399 ret.append(self.__format_line(linetpl, filename,
393 ret.append(self.__format_line(linetpl, filename,
400 start + 1 + i, line,
394 start + 1 + i, line,
401 arrow = show_arrow) )
395 arrow = show_arrow) )
402
396
403 return ''.join(ret)
397 return ''.join(ret)
404
398
405 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
399 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
406 bp_mark = ""
400 bp_mark = ""
407 bp_mark_color = ""
401 bp_mark_color = ""
408
402
409 scheme = self.color_scheme_table.active_scheme_name
403 scheme = self.color_scheme_table.active_scheme_name
410 new_line, err = self.parser.format2(line, 'str', scheme)
404 new_line, err = self.parser.format2(line, 'str', scheme)
411 if not err: line = new_line
405 if not err: line = new_line
412
406
413 bp = None
407 bp = None
414 if lineno in self.get_file_breaks(filename):
408 if lineno in self.get_file_breaks(filename):
415 bps = self.get_breaks(filename, lineno)
409 bps = self.get_breaks(filename, lineno)
416 bp = bps[-1]
410 bp = bps[-1]
417
411
418 if bp:
412 if bp:
419 Colors = self.color_scheme_table.active_colors
413 Colors = self.color_scheme_table.active_colors
420 bp_mark = str(bp.number)
414 bp_mark = str(bp.number)
421 bp_mark_color = Colors.breakpoint_enabled
415 bp_mark_color = Colors.breakpoint_enabled
422 if not bp.enabled:
416 if not bp.enabled:
423 bp_mark_color = Colors.breakpoint_disabled
417 bp_mark_color = Colors.breakpoint_disabled
424
418
425 numbers_width = 7
419 numbers_width = 7
426 if arrow:
420 if arrow:
427 # This is the line with the error
421 # This is the line with the error
428 pad = numbers_width - len(str(lineno)) - len(bp_mark)
422 pad = numbers_width - len(str(lineno)) - len(bp_mark)
429 if pad >= 3:
423 if pad >= 3:
430 marker = '-'*(pad-3) + '-> '
424 marker = '-'*(pad-3) + '-> '
431 elif pad == 2:
425 elif pad == 2:
432 marker = '> '
426 marker = '> '
433 elif pad == 1:
427 elif pad == 1:
434 marker = '>'
428 marker = '>'
435 else:
429 else:
436 marker = ''
430 marker = ''
437 num = '%s%s' % (marker, str(lineno))
431 num = '%s%s' % (marker, str(lineno))
438 line = tpl_line % (bp_mark_color + bp_mark, num, line)
432 line = tpl_line % (bp_mark_color + bp_mark, num, line)
439 else:
433 else:
440 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
434 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
441 line = tpl_line % (bp_mark_color + bp_mark, num, line)
435 line = tpl_line % (bp_mark_color + bp_mark, num, line)
442
436
443 return line
437 return line
444
438
445 def list_command_pydb(self, arg):
439 def list_command_pydb(self, arg):
446 """List command to use if we have a newer pydb installed"""
440 """List command to use if we have a newer pydb installed"""
447 filename, first, last = OldPdb.parse_list_cmd(self, arg)
441 filename, first, last = OldPdb.parse_list_cmd(self, arg)
448 if filename is not None:
442 if filename is not None:
449 self.print_list_lines(filename, first, last)
443 self.print_list_lines(filename, first, last)
450
444
451 def print_list_lines(self, filename, first, last):
445 def print_list_lines(self, filename, first, last):
452 """The printing (as opposed to the parsing part of a 'list'
446 """The printing (as opposed to the parsing part of a 'list'
453 command."""
447 command."""
454 try:
448 try:
455 Colors = self.color_scheme_table.active_colors
449 Colors = self.color_scheme_table.active_colors
456 ColorsNormal = Colors.Normal
450 ColorsNormal = Colors.Normal
457 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
451 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
458 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
452 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
459 src = []
453 src = []
460 for lineno in range(first, last+1):
454 for lineno in range(first, last+1):
461 line = linecache.getline(filename, lineno)
455 line = linecache.getline(filename, lineno)
462 if not line:
456 if not line:
463 break
457 break
464
458
465 if lineno == self.curframe.f_lineno:
459 if lineno == self.curframe.f_lineno:
466 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
460 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
467 else:
461 else:
468 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
462 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
469
463
470 src.append(line)
464 src.append(line)
471 self.lineno = lineno
465 self.lineno = lineno
472
466
473 print >>Term.cout, ''.join(src)
467 print >>Term.cout, ''.join(src)
474
468
475 except KeyboardInterrupt:
469 except KeyboardInterrupt:
476 pass
470 pass
477
471
478 def do_list(self, arg):
472 def do_list(self, arg):
479 self.lastcmd = 'list'
473 self.lastcmd = 'list'
480 last = None
474 last = None
481 if arg:
475 if arg:
482 try:
476 try:
483 x = eval(arg, {}, {})
477 x = eval(arg, {}, {})
484 if type(x) == type(()):
478 if type(x) == type(()):
485 first, last = x
479 first, last = x
486 first = int(first)
480 first = int(first)
487 last = int(last)
481 last = int(last)
488 if last < first:
482 if last < first:
489 # Assume it's a count
483 # Assume it's a count
490 last = first + last
484 last = first + last
491 else:
485 else:
492 first = max(1, int(x) - 5)
486 first = max(1, int(x) - 5)
493 except:
487 except:
494 print '*** Error in argument:', `arg`
488 print '*** Error in argument:', `arg`
495 return
489 return
496 elif self.lineno is None:
490 elif self.lineno is None:
497 first = max(1, self.curframe.f_lineno - 5)
491 first = max(1, self.curframe.f_lineno - 5)
498 else:
492 else:
499 first = self.lineno + 1
493 first = self.lineno + 1
500 if last is None:
494 if last is None:
501 last = first + 10
495 last = first + 10
502 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
496 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
503
497
504 # vds: >>
498 # vds: >>
505 lineno = first
499 lineno = first
506 filename = self.curframe.f_code.co_filename
500 filename = self.curframe.f_code.co_filename
507 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
501 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
508 # vds: <<
502 # vds: <<
509
503
510 do_l = do_list
504 do_l = do_list
511
505
512 def do_pdef(self, arg):
506 def do_pdef(self, arg):
513 """The debugger interface to magic_pdef"""
507 """The debugger interface to magic_pdef"""
514 namespaces = [('Locals', self.curframe.f_locals),
508 namespaces = [('Locals', self.curframe.f_locals),
515 ('Globals', self.curframe.f_globals)]
509 ('Globals', self.curframe.f_globals)]
516 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
510 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
517
511
518 def do_pdoc(self, arg):
512 def do_pdoc(self, arg):
519 """The debugger interface to magic_pdoc"""
513 """The debugger interface to magic_pdoc"""
520 namespaces = [('Locals', self.curframe.f_locals),
514 namespaces = [('Locals', self.curframe.f_locals),
521 ('Globals', self.curframe.f_globals)]
515 ('Globals', self.curframe.f_globals)]
522 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
516 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
523
517
524 def do_pinfo(self, arg):
518 def do_pinfo(self, arg):
525 """The debugger equivalant of ?obj"""
519 """The debugger equivalant of ?obj"""
526 namespaces = [('Locals', self.curframe.f_locals),
520 namespaces = [('Locals', self.curframe.f_locals),
527 ('Globals', self.curframe.f_globals)]
521 ('Globals', self.curframe.f_globals)]
528 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
522 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
@@ -1,259 +1,253 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Modified input prompt for executing files.
2 """Modified input prompt for executing files.
3
3
4 We define a special input line filter to allow typing lines which begin with
4 We define a special input line filter to allow typing lines which begin with
5 '~', '/' or '.'. If one of those strings is encountered, it is automatically
5 '~', '/' or '.'. If one of those strings is encountered, it is automatically
6 executed.
6 executed.
7
7 """
8 $Id: InterpreterExec.py 2724 2007-09-07 08:05:38Z fperez $"""
9
8
10 #*****************************************************************************
9 #*****************************************************************************
11 # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl>
10 # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl>
12 # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu>
11 # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu>
13 #
12 #
14 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
15 #*****************************************************************************
17
16
18 from IPython import Release
19 __author__ = 'W.J. van der Laan <gnufnork@hetdigitalegat.nl>, '\
20 '%s <%s>' % Release.authors['Fernando']
21 __license__ = Release.license
22
23 # TODO: deprecated
17 # TODO: deprecated
24 def prefilter_shell(self,line,continuation):
18 def prefilter_shell(self,line,continuation):
25 """Alternate prefilter, modified for shell-like functionality.
19 """Alternate prefilter, modified for shell-like functionality.
26
20
27 - Execute all lines beginning with '~', '/' or '.'
21 - Execute all lines beginning with '~', '/' or '.'
28 - $var=cmd <=> %sc var=cmd
22 - $var=cmd <=> %sc var=cmd
29 - $$var=cmd <=> %sc -l var=cmd
23 - $$var=cmd <=> %sc -l var=cmd
30 """
24 """
31
25
32 if line:
26 if line:
33 l0 = line[0]
27 l0 = line[0]
34 if l0 in '~/.':
28 if l0 in '~/.':
35 return self._prefilter("!%s"%line,continuation)
29 return self._prefilter("!%s"%line,continuation)
36 elif l0=='$':
30 elif l0=='$':
37 lrest = line[1:]
31 lrest = line[1:]
38 if lrest.startswith('$'):
32 if lrest.startswith('$'):
39 # $$var=cmd <=> %sc -l var=cmd
33 # $$var=cmd <=> %sc -l var=cmd
40 return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]),
34 return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]),
41 continuation)
35 continuation)
42 else:
36 else:
43 # $var=cmd <=> %sc var=cmd
37 # $var=cmd <=> %sc var=cmd
44 return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest),
38 return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest),
45 continuation)
39 continuation)
46 else:
40 else:
47 return self._prefilter(line,continuation)
41 return self._prefilter(line,continuation)
48 else:
42 else:
49 return self._prefilter(line,continuation)
43 return self._prefilter(line,continuation)
50
44
51 # Rebind this to be the new IPython prefilter:
45 # Rebind this to be the new IPython prefilter:
52 from IPython.iplib import InteractiveShell
46 from IPython.iplib import InteractiveShell
53 InteractiveShell.prefilter = prefilter_shell
47 InteractiveShell.prefilter = prefilter_shell
54 # Clean up the namespace.
48 # Clean up the namespace.
55 del InteractiveShell,prefilter_shell
49 del InteractiveShell,prefilter_shell
56
50
57 # Provide pysh and further shell-oriented services
51 # Provide pysh and further shell-oriented services
58 import os,sys,shutil
52 import os,sys,shutil
59 from IPython.genutils import system,shell,getoutput,getoutputerror
53 from IPython.genutils import system,shell,getoutput,getoutputerror
60
54
61 # Short aliases for getting shell output as a string and a list
55 # Short aliases for getting shell output as a string and a list
62 sout = getoutput
56 sout = getoutput
63 lout = lambda cmd: getoutput(cmd,split=1)
57 lout = lambda cmd: getoutput(cmd,split=1)
64
58
65 # Empty function, meant as a docstring holder so help(pysh) works.
59 # Empty function, meant as a docstring holder so help(pysh) works.
66 def pysh():
60 def pysh():
67 """Pysh is a set of modules and extensions to IPython which make shell-like
61 """Pysh is a set of modules and extensions to IPython which make shell-like
68 usage with Python syntax more convenient. Keep in mind that pysh is NOT a
62 usage with Python syntax more convenient. Keep in mind that pysh is NOT a
69 full-blown shell, so don't try to make it your /etc/passwd entry!
63 full-blown shell, so don't try to make it your /etc/passwd entry!
70
64
71 In particular, it has no job control, so if you type Ctrl-Z (under Unix),
65 In particular, it has no job control, so if you type Ctrl-Z (under Unix),
72 you'll suspend pysh itself, not the process you just started.
66 you'll suspend pysh itself, not the process you just started.
73
67
74 Since pysh is really nothing but a customized IPython, you should
68 Since pysh is really nothing but a customized IPython, you should
75 familiarize yourself with IPython's features. This brief help mainly
69 familiarize yourself with IPython's features. This brief help mainly
76 documents areas in which pysh differs from the normal IPython.
70 documents areas in which pysh differs from the normal IPython.
77
71
78 ALIASES
72 ALIASES
79 -------
73 -------
80 All of your $PATH has been loaded as IPython aliases, so you should be
74 All of your $PATH has been loaded as IPython aliases, so you should be
81 able to type any normal system command and have it executed. See %alias?
75 able to type any normal system command and have it executed. See %alias?
82 and %unalias? for details on the alias facilities.
76 and %unalias? for details on the alias facilities.
83
77
84 SPECIAL SYNTAX
78 SPECIAL SYNTAX
85 --------------
79 --------------
86 Any lines which begin with '~', '/' and '.' will be executed as shell
80 Any lines which begin with '~', '/' and '.' will be executed as shell
87 commands instead of as Python code. The special escapes below are also
81 commands instead of as Python code. The special escapes below are also
88 recognized. !cmd is valid in single or multi-line input, all others are
82 recognized. !cmd is valid in single or multi-line input, all others are
89 only valid in single-line input:
83 only valid in single-line input:
90
84
91 !cmd - pass 'cmd' directly to the shell
85 !cmd - pass 'cmd' directly to the shell
92 !!cmd - execute 'cmd' and return output as a list (split on '\\n')
86 !!cmd - execute 'cmd' and return output as a list (split on '\\n')
93 $var=cmd - capture output of cmd into var, as a string
87 $var=cmd - capture output of cmd into var, as a string
94 $$var=cmd - capture output of cmd into var, as a list (split on '\\n')
88 $$var=cmd - capture output of cmd into var, as a list (split on '\\n')
95
89
96 The $/$$ syntaxes make Python variables from system output, which you can
90 The $/$$ syntaxes make Python variables from system output, which you can
97 later use for further scripting. The converse is also possible: when
91 later use for further scripting. The converse is also possible: when
98 executing an alias or calling to the system via !/!!, you can expand any
92 executing an alias or calling to the system via !/!!, you can expand any
99 python variable or expression by prepending it with $. Full details of
93 python variable or expression by prepending it with $. Full details of
100 the allowed syntax can be found in Python's PEP 215.
94 the allowed syntax can be found in Python's PEP 215.
101
95
102 A few brief examples will illustrate these:
96 A few brief examples will illustrate these:
103
97
104 fperez[~/test]|3> !ls *s.py
98 fperez[~/test]|3> !ls *s.py
105 scopes.py strings.py
99 scopes.py strings.py
106
100
107 ls is an internal alias, so there's no need to use !:
101 ls is an internal alias, so there's no need to use !:
108 fperez[~/test]|4> ls *s.py
102 fperez[~/test]|4> ls *s.py
109 scopes.py* strings.py
103 scopes.py* strings.py
110
104
111 !!ls will return the output into a Python variable:
105 !!ls will return the output into a Python variable:
112 fperez[~/test]|5> !!ls *s.py
106 fperez[~/test]|5> !!ls *s.py
113 <5> ['scopes.py', 'strings.py']
107 <5> ['scopes.py', 'strings.py']
114 fperez[~/test]|6> print _5
108 fperez[~/test]|6> print _5
115 ['scopes.py', 'strings.py']
109 ['scopes.py', 'strings.py']
116
110
117 $ and $$ allow direct capture to named variables:
111 $ and $$ allow direct capture to named variables:
118 fperez[~/test]|7> $astr = ls *s.py
112 fperez[~/test]|7> $astr = ls *s.py
119 fperez[~/test]|8> astr
113 fperez[~/test]|8> astr
120 <8> 'scopes.py\\nstrings.py'
114 <8> 'scopes.py\\nstrings.py'
121
115
122 fperez[~/test]|9> $$alist = ls *s.py
116 fperez[~/test]|9> $$alist = ls *s.py
123 fperez[~/test]|10> alist
117 fperez[~/test]|10> alist
124 <10> ['scopes.py', 'strings.py']
118 <10> ['scopes.py', 'strings.py']
125
119
126 alist is now a normal python list you can loop over. Using $ will expand
120 alist is now a normal python list you can loop over. Using $ will expand
127 back the python values when alias calls are made:
121 back the python values when alias calls are made:
128 fperez[~/test]|11> for f in alist:
122 fperez[~/test]|11> for f in alist:
129 |..> print 'file',f,
123 |..> print 'file',f,
130 |..> wc -l $f
124 |..> wc -l $f
131 |..>
125 |..>
132 file scopes.py 13 scopes.py
126 file scopes.py 13 scopes.py
133 file strings.py 4 strings.py
127 file strings.py 4 strings.py
134
128
135 Note that you may need to protect your variables with braces if you want
129 Note that you may need to protect your variables with braces if you want
136 to append strings to their names. To copy all files in alist to .bak
130 to append strings to their names. To copy all files in alist to .bak
137 extensions, you must use:
131 extensions, you must use:
138 fperez[~/test]|12> for f in alist:
132 fperez[~/test]|12> for f in alist:
139 |..> cp $f ${f}.bak
133 |..> cp $f ${f}.bak
140
134
141 If you try using $f.bak, you'll get an AttributeError exception saying
135 If you try using $f.bak, you'll get an AttributeError exception saying
142 that your string object doesn't have a .bak attribute. This is because
136 that your string object doesn't have a .bak attribute. This is because
143 the $ expansion mechanism allows you to expand full Python expressions:
137 the $ expansion mechanism allows you to expand full Python expressions:
144 fperez[~/test]|13> echo "sys.platform is: $sys.platform"
138 fperez[~/test]|13> echo "sys.platform is: $sys.platform"
145 sys.platform is: linux2
139 sys.platform is: linux2
146
140
147 IPython's input history handling is still active, which allows you to
141 IPython's input history handling is still active, which allows you to
148 rerun a single block of multi-line input by simply using exec:
142 rerun a single block of multi-line input by simply using exec:
149 fperez[~/test]|14> $$alist = ls *.eps
143 fperez[~/test]|14> $$alist = ls *.eps
150 fperez[~/test]|15> exec _i11
144 fperez[~/test]|15> exec _i11
151 file image2.eps 921 image2.eps
145 file image2.eps 921 image2.eps
152 file image.eps 921 image.eps
146 file image.eps 921 image.eps
153
147
154 While these are new special-case syntaxes, they are designed to allow very
148 While these are new special-case syntaxes, they are designed to allow very
155 efficient use of the shell with minimal typing. At an interactive shell
149 efficient use of the shell with minimal typing. At an interactive shell
156 prompt, conciseness of expression wins over readability.
150 prompt, conciseness of expression wins over readability.
157
151
158 USEFUL FUNCTIONS AND MODULES
152 USEFUL FUNCTIONS AND MODULES
159 ----------------------------
153 ----------------------------
160 The os, sys and shutil modules from the Python standard library are
154 The os, sys and shutil modules from the Python standard library are
161 automatically loaded. Some additional functions, useful for shell usage,
155 automatically loaded. Some additional functions, useful for shell usage,
162 are listed below. You can request more help about them with '?'.
156 are listed below. You can request more help about them with '?'.
163
157
164 shell - execute a command in the underlying system shell
158 shell - execute a command in the underlying system shell
165 system - like shell(), but return the exit status of the command
159 system - like shell(), but return the exit status of the command
166 sout - capture the output of a command as a string
160 sout - capture the output of a command as a string
167 lout - capture the output of a command as a list (split on '\\n')
161 lout - capture the output of a command as a list (split on '\\n')
168 getoutputerror - capture (output,error) of a shell command
162 getoutputerror - capture (output,error) of a shell command
169
163
170 sout/lout are the functional equivalents of $/$$. They are provided to
164 sout/lout are the functional equivalents of $/$$. They are provided to
171 allow you to capture system output in the middle of true python code,
165 allow you to capture system output in the middle of true python code,
172 function definitions, etc (where $ and $$ are invalid).
166 function definitions, etc (where $ and $$ are invalid).
173
167
174 DIRECTORY MANAGEMENT
168 DIRECTORY MANAGEMENT
175 --------------------
169 --------------------
176 Since each command passed by pysh to the underlying system is executed in
170 Since each command passed by pysh to the underlying system is executed in
177 a subshell which exits immediately, you can NOT use !cd to navigate the
171 a subshell which exits immediately, you can NOT use !cd to navigate the
178 filesystem.
172 filesystem.
179
173
180 Pysh provides its own builtin '%cd' magic command to move in the
174 Pysh provides its own builtin '%cd' magic command to move in the
181 filesystem (the % is not required with automagic on). It also maintains a
175 filesystem (the % is not required with automagic on). It also maintains a
182 list of visited directories (use %dhist to see it) and allows direct
176 list of visited directories (use %dhist to see it) and allows direct
183 switching to any of them. Type 'cd?' for more details.
177 switching to any of them. Type 'cd?' for more details.
184
178
185 %pushd, %popd and %dirs are provided for directory stack handling.
179 %pushd, %popd and %dirs are provided for directory stack handling.
186
180
187 PROMPT CUSTOMIZATION
181 PROMPT CUSTOMIZATION
188 --------------------
182 --------------------
189
183
190 The supplied ipythonrc-pysh profile comes with an example of a very
184 The supplied ipythonrc-pysh profile comes with an example of a very
191 colored and detailed prompt, mainly to serve as an illustration. The
185 colored and detailed prompt, mainly to serve as an illustration. The
192 valid escape sequences, besides color names, are:
186 valid escape sequences, besides color names, are:
193
187
194 \\# - Prompt number.
188 \\# - Prompt number.
195 \\D - Dots, as many as there are digits in \\# (so they align).
189 \\D - Dots, as many as there are digits in \\# (so they align).
196 \\w - Current working directory (cwd).
190 \\w - Current working directory (cwd).
197 \\W - Basename of current working directory.
191 \\W - Basename of current working directory.
198 \\XN - Where N=0..5. N terms of the cwd, with $HOME written as ~.
192 \\XN - Where N=0..5. N terms of the cwd, with $HOME written as ~.
199 \\YN - Where N=0..5. Like XN, but if ~ is term N+1 it's also shown.
193 \\YN - Where N=0..5. Like XN, but if ~ is term N+1 it's also shown.
200 \\u - Username.
194 \\u - Username.
201 \\H - Full hostname.
195 \\H - Full hostname.
202 \\h - Hostname up to first '.'
196 \\h - Hostname up to first '.'
203 \\$ - Root symbol ($ or #).
197 \\$ - Root symbol ($ or #).
204 \\t - Current time, in H:M:S format.
198 \\t - Current time, in H:M:S format.
205 \\v - IPython release version.
199 \\v - IPython release version.
206 \\n - Newline.
200 \\n - Newline.
207 \\r - Carriage return.
201 \\r - Carriage return.
208 \\\\ - An explicitly escaped '\\'.
202 \\\\ - An explicitly escaped '\\'.
209
203
210 You can configure your prompt colors using any ANSI color escape. Each
204 You can configure your prompt colors using any ANSI color escape. Each
211 color escape sets the color for any subsequent text, until another escape
205 color escape sets the color for any subsequent text, until another escape
212 comes in and changes things. The valid color escapes are:
206 comes in and changes things. The valid color escapes are:
213
207
214 \\C_Black
208 \\C_Black
215 \\C_Blue
209 \\C_Blue
216 \\C_Brown
210 \\C_Brown
217 \\C_Cyan
211 \\C_Cyan
218 \\C_DarkGray
212 \\C_DarkGray
219 \\C_Green
213 \\C_Green
220 \\C_LightBlue
214 \\C_LightBlue
221 \\C_LightCyan
215 \\C_LightCyan
222 \\C_LightGray
216 \\C_LightGray
223 \\C_LightGreen
217 \\C_LightGreen
224 \\C_LightPurple
218 \\C_LightPurple
225 \\C_LightRed
219 \\C_LightRed
226 \\C_Purple
220 \\C_Purple
227 \\C_Red
221 \\C_Red
228 \\C_White
222 \\C_White
229 \\C_Yellow
223 \\C_Yellow
230 \\C_Normal - Stop coloring, defaults to your terminal settings.
224 \\C_Normal - Stop coloring, defaults to your terminal settings.
231 """
225 """
232 pass
226 pass
233
227
234 # Configure a few things. Much of this is fairly hackish, since IPython
228 # Configure a few things. Much of this is fairly hackish, since IPython
235 # doesn't really expose a clean API for it. Be careful if you start making
229 # doesn't really expose a clean API for it. Be careful if you start making
236 # many modifications here.
230 # many modifications here.
237
231
238
232
239 # Set the 'cd' command to quiet mode, a more shell-like behavior
233 # Set the 'cd' command to quiet mode, a more shell-like behavior
240 __IPYTHON__.default_option('cd','-q')
234 __IPYTHON__.default_option('cd','-q')
241
235
242 # This is redundant, ipy_user_conf.py will determine this
236 # This is redundant, ipy_user_conf.py will determine this
243 # Load all of $PATH as aliases
237 # Load all of $PATH as aliases
244 __IPYTHON__.magic_rehashx()
238 __IPYTHON__.magic_rehashx()
245
239
246 # Remove %sc,%sx if present as aliases
240 # Remove %sc,%sx if present as aliases
247 __IPYTHON__.magic_unalias('sc')
241 __IPYTHON__.magic_unalias('sc')
248 __IPYTHON__.magic_unalias('sx')
242 __IPYTHON__.magic_unalias('sx')
249
243
250 # We need different criteria for line-splitting, so that aliases such as
244 # We need different criteria for line-splitting, so that aliases such as
251 # 'gnome-terminal' are interpreted as a single alias instead of variable
245 # 'gnome-terminal' are interpreted as a single alias instead of variable
252 # 'gnome' minus variable 'terminal'.
246 # 'gnome' minus variable 'terminal'.
253 import re
247 import re
254 __IPYTHON__.line_split = re.compile(r'^([\s*,;/])'
248 __IPYTHON__.line_split = re.compile(r'^([\s*,;/])'
255 r'([\?\w\.\-\+]+\w*\s*)'
249 r'([\?\w\.\-\+]+\w*\s*)'
256 r'(\(?.*$)')
250 r'(\(?.*$)')
257
251
258 # Namespace cleanup
252 # Namespace cleanup
259 del re
253 del re
@@ -1,66 +1,63 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """ IPython extension: new prefilters for output grabbing
2 """ IPython extension: new prefilters for output grabbing
3
3
4 Provides
4 Provides
5
5
6 var = %magic blah blah
6 var = %magic blah blah
7
7
8 var = !ls
8 var = !ls
9
10 $Id: genutils.py 1077 2006-01-24 18:15:27Z vivainio $
11
12 """
9 """
13
10
14 import IPython.ipapi
11 import IPython.ipapi
15 from IPython.genutils import *
12 from IPython.genutils import *
16
13
17 ip = IPython.ipapi.get()
14 ip = IPython.ipapi.get()
18
15
19 import re
16 import re
20
17
21 def hnd_magic(line,mo):
18 def hnd_magic(line,mo):
22 """ Handle a = %mymagic blah blah """
19 """ Handle a = %mymagic blah blah """
23 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
20 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
24 #mag = 'ipmagic
21 #mag = 'ipmagic
25 #return "%s = %s"
22 #return "%s = %s"
26 var = mo.group('varname')
23 var = mo.group('varname')
27 cmd = mo.group('cmd')
24 cmd = mo.group('cmd')
28 expr = make_quoted_expr(cmd)
25 expr = make_quoted_expr(cmd)
29 return itpl('$var = _ip.magic($expr)')
26 return itpl('$var = _ip.magic($expr)')
30
27
31 def hnd_syscmd(line,mo):
28 def hnd_syscmd(line,mo):
32 """ Handle a = !ls """
29 """ Handle a = !ls """
33 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
30 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
34 #mag = 'ipmagic
31 #mag = 'ipmagic
35 #return "%s = %s"
32 #return "%s = %s"
36 var = mo.group('varname')
33 var = mo.group('varname')
37 cmd = mo.group('cmd')
34 cmd = mo.group('cmd')
38 expr = make_quoted_expr(itpl("sc -l =$cmd"))
35 expr = make_quoted_expr(itpl("sc -l =$cmd"))
39 return itpl('$var = _ip.magic($expr)')
36 return itpl('$var = _ip.magic($expr)')
40
37
41 def install_re_handler(pat, hnd):
38 def install_re_handler(pat, hnd):
42 ip.meta.re_prefilters.append((re.compile(pat), hnd))
39 ip.meta.re_prefilters.append((re.compile(pat), hnd))
43
40
44 def init_handlers():
41 def init_handlers():
45
42
46 ip.meta.re_prefilters = []
43 ip.meta.re_prefilters = []
47
44
48 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
45 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
49 hnd_magic
46 hnd_magic
50 )
47 )
51
48
52 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
49 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
53 hnd_syscmd
50 hnd_syscmd
54 )
51 )
55
52
56 init_handlers()
53 init_handlers()
57
54
58 def regex_prefilter_f(self,line):
55 def regex_prefilter_f(self,line):
59 for pat, handler in ip.meta.re_prefilters:
56 for pat, handler in ip.meta.re_prefilters:
60 mo = pat.match(line)
57 mo = pat.match(line)
61 if mo:
58 if mo:
62 return handler(line,mo)
59 return handler(line,mo)
63
60
64 raise IPython.ipapi.TryNext
61 raise IPython.ipapi.TryNext
65
62
66 ip.set_hook('input_prefilter', regex_prefilter_f)
63 ip.set_hook('input_prefilter', regex_prefilter_f)
@@ -1,184 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 %store magic for lightweight persistence.
3 %store magic for lightweight persistence.
4
4
5 Stores variables, aliases etc. in PickleShare database.
5 Stores variables, aliases etc. in PickleShare database.
6
7 $Id: iplib.py 1107 2006-01-30 19:02:20Z vivainio $
8 """
6 """
9
7
10 import IPython.ipapi
8 import IPython.ipapi
11 from IPython.ipapi import UsageError
9 from IPython.ipapi import UsageError
12 ip = IPython.ipapi.get()
10 ip = IPython.ipapi.get()
13
11
14 import pickleshare
12 import pickleshare
15
13
16 import inspect,pickle,os,sys,textwrap
14 import inspect,pickle,os,sys,textwrap
17 from IPython.FakeModule import FakeModule
15 from IPython.FakeModule import FakeModule
18
16
19 def restore_aliases(self):
17 def restore_aliases(self):
20 ip = self.getapi()
18 ip = self.getapi()
21 staliases = ip.db.get('stored_aliases', {})
19 staliases = ip.db.get('stored_aliases', {})
22 for k,v in staliases.items():
20 for k,v in staliases.items():
23 #print "restore alias",k,v # dbg
21 #print "restore alias",k,v # dbg
24 #self.alias_table[k] = v
22 #self.alias_table[k] = v
25 ip.defalias(k,v)
23 ip.defalias(k,v)
26
24
27
25
28 def refresh_variables(ip):
26 def refresh_variables(ip):
29 db = ip.db
27 db = ip.db
30 for key in db.keys('autorestore/*'):
28 for key in db.keys('autorestore/*'):
31 # strip autorestore
29 # strip autorestore
32 justkey = os.path.basename(key)
30 justkey = os.path.basename(key)
33 try:
31 try:
34 obj = db[key]
32 obj = db[key]
35 except KeyError:
33 except KeyError:
36 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
34 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
37 print "The error was:",sys.exc_info()[0]
35 print "The error was:",sys.exc_info()[0]
38 else:
36 else:
39 #print "restored",justkey,"=",obj #dbg
37 #print "restored",justkey,"=",obj #dbg
40 ip.user_ns[justkey] = obj
38 ip.user_ns[justkey] = obj
41
39
42
40
43 def restore_dhist(ip):
41 def restore_dhist(ip):
44 db = ip.db
42 db = ip.db
45 ip.user_ns['_dh'] = db.get('dhist',[])
43 ip.user_ns['_dh'] = db.get('dhist',[])
46
44
47 def restore_data(self):
45 def restore_data(self):
48 ip = self.getapi()
46 ip = self.getapi()
49 refresh_variables(ip)
47 refresh_variables(ip)
50 restore_aliases(self)
48 restore_aliases(self)
51 restore_dhist(self)
49 restore_dhist(self)
52 raise IPython.ipapi.TryNext
50 raise IPython.ipapi.TryNext
53
51
54 ip.set_hook('late_startup_hook', restore_data)
52 ip.set_hook('late_startup_hook', restore_data)
55
53
56 def magic_store(self, parameter_s=''):
54 def magic_store(self, parameter_s=''):
57 """Lightweight persistence for python variables.
55 """Lightweight persistence for python variables.
58
56
59 Example:
57 Example:
60
58
61 ville@badger[~]|1> A = ['hello',10,'world']\\
59 ville@badger[~]|1> A = ['hello',10,'world']\\
62 ville@badger[~]|2> %store A\\
60 ville@badger[~]|2> %store A\\
63 ville@badger[~]|3> Exit
61 ville@badger[~]|3> Exit
64
62
65 (IPython session is closed and started again...)
63 (IPython session is closed and started again...)
66
64
67 ville@badger:~$ ipython -p pysh\\
65 ville@badger:~$ ipython -p pysh\\
68 ville@badger[~]|1> print A
66 ville@badger[~]|1> print A
69
67
70 ['hello', 10, 'world']
68 ['hello', 10, 'world']
71
69
72 Usage:
70 Usage:
73
71
74 %store - Show list of all variables and their current values\\
72 %store - Show list of all variables and their current values\\
75 %store <var> - Store the *current* value of the variable to disk\\
73 %store <var> - Store the *current* value of the variable to disk\\
76 %store -d <var> - Remove the variable and its value from storage\\
74 %store -d <var> - Remove the variable and its value from storage\\
77 %store -z - Remove all variables from storage\\
75 %store -z - Remove all variables from storage\\
78 %store -r - Refresh all variables from store (delete current vals)\\
76 %store -r - Refresh all variables from store (delete current vals)\\
79 %store foo >a.txt - Store value of foo to new file a.txt\\
77 %store foo >a.txt - Store value of foo to new file a.txt\\
80 %store foo >>a.txt - Append value of foo to file a.txt\\
78 %store foo >>a.txt - Append value of foo to file a.txt\\
81
79
82 It should be noted that if you change the value of a variable, you
80 It should be noted that if you change the value of a variable, you
83 need to %store it again if you want to persist the new value.
81 need to %store it again if you want to persist the new value.
84
82
85 Note also that the variables will need to be pickleable; most basic
83 Note also that the variables will need to be pickleable; most basic
86 python types can be safely %stored.
84 python types can be safely %stored.
87
85
88 Also aliases can be %store'd across sessions.
86 Also aliases can be %store'd across sessions.
89 """
87 """
90
88
91 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
89 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
92 args = argsl.split(None,1)
90 args = argsl.split(None,1)
93 ip = self.getapi()
91 ip = self.getapi()
94 db = ip.db
92 db = ip.db
95 # delete
93 # delete
96 if opts.has_key('d'):
94 if opts.has_key('d'):
97 try:
95 try:
98 todel = args[0]
96 todel = args[0]
99 except IndexError:
97 except IndexError:
100 raise UsageError('You must provide the variable to forget')
98 raise UsageError('You must provide the variable to forget')
101 else:
99 else:
102 try:
100 try:
103 del db['autorestore/' + todel]
101 del db['autorestore/' + todel]
104 except:
102 except:
105 raise UsageError("Can't delete variable '%s'" % todel)
103 raise UsageError("Can't delete variable '%s'" % todel)
106 # reset
104 # reset
107 elif opts.has_key('z'):
105 elif opts.has_key('z'):
108 for k in db.keys('autorestore/*'):
106 for k in db.keys('autorestore/*'):
109 del db[k]
107 del db[k]
110
108
111 elif opts.has_key('r'):
109 elif opts.has_key('r'):
112 refresh_variables(ip)
110 refresh_variables(ip)
113
111
114
112
115 # run without arguments -> list variables & values
113 # run without arguments -> list variables & values
116 elif not args:
114 elif not args:
117 vars = self.db.keys('autorestore/*')
115 vars = self.db.keys('autorestore/*')
118 vars.sort()
116 vars.sort()
119 if vars:
117 if vars:
120 size = max(map(len,vars))
118 size = max(map(len,vars))
121 else:
119 else:
122 size = 0
120 size = 0
123
121
124 print 'Stored variables and their in-db values:'
122 print 'Stored variables and their in-db values:'
125 fmt = '%-'+str(size)+'s -> %s'
123 fmt = '%-'+str(size)+'s -> %s'
126 get = db.get
124 get = db.get
127 for var in vars:
125 for var in vars:
128 justkey = os.path.basename(var)
126 justkey = os.path.basename(var)
129 # print 30 first characters from every var
127 # print 30 first characters from every var
130 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
128 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
131
129
132 # default action - store the variable
130 # default action - store the variable
133 else:
131 else:
134 # %store foo >file.txt or >>file.txt
132 # %store foo >file.txt or >>file.txt
135 if len(args) > 1 and args[1].startswith('>'):
133 if len(args) > 1 and args[1].startswith('>'):
136 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
134 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
137 if args[1].startswith('>>'):
135 if args[1].startswith('>>'):
138 fil = open(fnam,'a')
136 fil = open(fnam,'a')
139 else:
137 else:
140 fil = open(fnam,'w')
138 fil = open(fnam,'w')
141 obj = ip.ev(args[0])
139 obj = ip.ev(args[0])
142 print "Writing '%s' (%s) to file '%s'." % (args[0],
140 print "Writing '%s' (%s) to file '%s'." % (args[0],
143 obj.__class__.__name__, fnam)
141 obj.__class__.__name__, fnam)
144
142
145
143
146 if not isinstance (obj,basestring):
144 if not isinstance (obj,basestring):
147 from pprint import pprint
145 from pprint import pprint
148 pprint(obj,fil)
146 pprint(obj,fil)
149 else:
147 else:
150 fil.write(obj)
148 fil.write(obj)
151 if not obj.endswith('\n'):
149 if not obj.endswith('\n'):
152 fil.write('\n')
150 fil.write('\n')
153
151
154 fil.close()
152 fil.close()
155 return
153 return
156
154
157 # %store foo
155 # %store foo
158 try:
156 try:
159 obj = ip.user_ns[args[0]]
157 obj = ip.user_ns[args[0]]
160 except KeyError:
158 except KeyError:
161 # it might be an alias
159 # it might be an alias
162 if args[0] in self.alias_table:
160 if args[0] in self.alias_table:
163 staliases = db.get('stored_aliases',{})
161 staliases = db.get('stored_aliases',{})
164 staliases[ args[0] ] = self.alias_table[ args[0] ]
162 staliases[ args[0] ] = self.alias_table[ args[0] ]
165 db['stored_aliases'] = staliases
163 db['stored_aliases'] = staliases
166 print "Alias stored:", args[0], self.alias_table[ args[0] ]
164 print "Alias stored:", args[0], self.alias_table[ args[0] ]
167 return
165 return
168 else:
166 else:
169 raise UsageError("Unknown variable '%s'" % args[0])
167 raise UsageError("Unknown variable '%s'" % args[0])
170
168
171 else:
169 else:
172 if isinstance(inspect.getmodule(obj), FakeModule):
170 if isinstance(inspect.getmodule(obj), FakeModule):
173 print textwrap.dedent("""\
171 print textwrap.dedent("""\
174 Warning:%s is %s
172 Warning:%s is %s
175 Proper storage of interactively declared classes (or instances
173 Proper storage of interactively declared classes (or instances
176 of those classes) is not possible! Only instances
174 of those classes) is not possible! Only instances
177 of classes in real modules on file system can be %%store'd.
175 of classes in real modules on file system can be %%store'd.
178 """ % (args[0], obj) )
176 """ % (args[0], obj) )
179 return
177 return
180 #pickled = pickle.dumps(obj)
178 #pickled = pickle.dumps(obj)
181 self.db[ 'autorestore/' + args[0] ] = obj
179 self.db[ 'autorestore/' + args[0] ] = obj
182 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
180 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
183
181
184 ip.expose_magic('store',magic_store)
182 ip.expose_magic('store',magic_store)
@@ -1,43 +1,42 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Class which mimics a module.
3 Class which mimics a module.
4
4
5 Needed to allow pickle to correctly resolve namespaces during IPython
5 Needed to allow pickle to correctly resolve namespaces during IPython
6 sessions.
6 sessions.
7
7 """
8 $Id: FakeModule.py 2754 2007-09-09 10:16:59Z fperez $"""
9
8
10 #*****************************************************************************
9 #*****************************************************************************
11 # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu>
10 # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu>
12 #
11 #
13 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
14 #*****************************************************************************
16
15
17 import types
16 import types
18
17
19 class FakeModule(types.ModuleType):
18 class FakeModule(types.ModuleType):
20 """Simple class with attribute access to fake a module.
19 """Simple class with attribute access to fake a module.
21
20
22 This is not meant to replace a module, but to allow inserting a fake
21 This is not meant to replace a module, but to allow inserting a fake
23 module in sys.modules so that systems which rely on run-time module
22 module in sys.modules so that systems which rely on run-time module
24 importing (like shelve and pickle) work correctly in interactive IPython
23 importing (like shelve and pickle) work correctly in interactive IPython
25 sessions.
24 sessions.
26
25
27 Do NOT use this code for anything other than this IPython private hack."""
26 Do NOT use this code for anything other than this IPython private hack."""
28
27
29 def __init__(self,adict=None):
28 def __init__(self,adict=None):
30
29
31 # tmp to force __dict__ instance creation, else self.__dict__ fails
30 # tmp to force __dict__ instance creation, else self.__dict__ fails
32 self.__iptmp = None
31 self.__iptmp = None
33
32
34 # It seems pydoc (and perhaps others) needs any module instance to
33 # It seems pydoc (and perhaps others) needs any module instance to
35 # implement a __nonzero__ method, so we add it if missing:
34 # implement a __nonzero__ method, so we add it if missing:
36 self.__dict__.setdefault('__nonzero__',lambda : True)
35 self.__dict__.setdefault('__nonzero__',lambda : True)
37 self.__dict__.setdefault('__file__',__file__)
36 self.__dict__.setdefault('__file__',__file__)
38
37
39 # cleanup our temp trick
38 # cleanup our temp trick
40 del self.__iptmp
39 del self.__iptmp
41
40
42 if adict is not None:
41 if adict is not None:
43 self.__dict__.update(adict)
42 self.__dict__.update(adict)
@@ -1,666 +1,665 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Improved replacement for the Gnuplot.Gnuplot class.
2 """Improved replacement for the Gnuplot.Gnuplot class.
3
3
4 This module imports Gnuplot and replaces some of its functionality with
4 This module imports Gnuplot and replaces some of its functionality with
5 improved versions. They add better handling of arrays for plotting and more
5 improved versions. They add better handling of arrays for plotting and more
6 convenient PostScript generation, plus some fixes for hardcopy().
6 convenient PostScript generation, plus some fixes for hardcopy().
7
7
8 It also adds a convenient plot2 method for plotting dictionaries and
8 It also adds a convenient plot2 method for plotting dictionaries and
9 lists/tuples of arrays.
9 lists/tuples of arrays.
10
10
11 This module is meant to be used as a drop-in replacement to the original
11 This module is meant to be used as a drop-in replacement to the original
12 Gnuplot, so it should be safe to do:
12 Gnuplot, so it should be safe to do:
13
13
14 import IPython.Gnuplot2 as Gnuplot
14 import IPython.Gnuplot2 as Gnuplot
15
15 """
16 $Id: Gnuplot2.py 1210 2006-03-13 01:19:31Z fperez $"""
17
16
18 import cStringIO
17 import cStringIO
19 import os
18 import os
20 import string
19 import string
21 import sys
20 import sys
22 import tempfile
21 import tempfile
23 import time
22 import time
24 import types
23 import types
25
24
26 import Gnuplot as Gnuplot_ori
25 import Gnuplot as Gnuplot_ori
27 import Numeric
26 import Numeric
28
27
29 from IPython.genutils import popkey,xsys
28 from IPython.genutils import popkey,xsys
30
29
31 # needed by hardcopy():
30 # needed by hardcopy():
32 gp = Gnuplot_ori.gp
31 gp = Gnuplot_ori.gp
33
32
34 # Patch for Gnuplot.py 1.6 compatibility.
33 # Patch for Gnuplot.py 1.6 compatibility.
35 # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz>
34 # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz>
36 try:
35 try:
37 OptionException = Gnuplot_ori.PlotItems.OptionException
36 OptionException = Gnuplot_ori.PlotItems.OptionException
38 except AttributeError:
37 except AttributeError:
39 OptionException = Gnuplot_ori.Errors.OptionError
38 OptionException = Gnuplot_ori.Errors.OptionError
40
39
41 # exhibit a similar interface to Gnuplot so it can be somewhat drop-in
40 # exhibit a similar interface to Gnuplot so it can be somewhat drop-in
42 Data = Gnuplot_ori.Data
41 Data = Gnuplot_ori.Data
43 Func = Gnuplot_ori.Func
42 Func = Gnuplot_ori.Func
44 GridData = Gnuplot_ori.GridData
43 GridData = Gnuplot_ori.GridData
45 PlotItem = Gnuplot_ori.PlotItem
44 PlotItem = Gnuplot_ori.PlotItem
46 PlotItems = Gnuplot_ori.PlotItems
45 PlotItems = Gnuplot_ori.PlotItems
47
46
48 # Modify some of Gnuplot's functions with improved versions (or bugfixed, in
47 # Modify some of Gnuplot's functions with improved versions (or bugfixed, in
49 # hardcopy's case). In order to preserve the docstrings at runtime, I've
48 # hardcopy's case). In order to preserve the docstrings at runtime, I've
50 # copied them from the original code.
49 # copied them from the original code.
51
50
52 # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit
51 # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit
53 # of version checking.
52 # of version checking.
54
53
55 if Gnuplot_ori.__version__ <= '1.6':
54 if Gnuplot_ori.__version__ <= '1.6':
56 _BaseFileItem = PlotItems.File
55 _BaseFileItem = PlotItems.File
57 _BaseTempFileItem = PlotItems.TempFile
56 _BaseTempFileItem = PlotItems.TempFile
58
57
59 # Fix the File class to add the 'index' option for Gnuplot versions < 1.7
58 # Fix the File class to add the 'index' option for Gnuplot versions < 1.7
60 class File(_BaseFileItem):
59 class File(_BaseFileItem):
61
60
62 _option_list = _BaseFileItem._option_list.copy()
61 _option_list = _BaseFileItem._option_list.copy()
63 _option_list.update({
62 _option_list.update({
64 'index' : lambda self, index: self.set_option_index(index),
63 'index' : lambda self, index: self.set_option_index(index),
65 })
64 })
66
65
67 # A new initializer is needed b/c we want to add a modified
66 # A new initializer is needed b/c we want to add a modified
68 # _option_sequence list which includes 'index' in the right place.
67 # _option_sequence list which includes 'index' in the right place.
69 def __init__(self,*args,**kw):
68 def __init__(self,*args,**kw):
70 self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes',
69 self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes',
71 'title', 'with']
70 'title', 'with']
72
71
73 _BaseFileItem.__init__(self,*args,**kw)
72 _BaseFileItem.__init__(self,*args,**kw)
74
73
75 # Let's fix the constructor docstring
74 # Let's fix the constructor docstring
76 __newdoc = \
75 __newdoc = \
77 """Additional Keyword arguments added by IPython:
76 """Additional Keyword arguments added by IPython:
78
77
79 'index=<int>' -- similar to the `index` keyword in Gnuplot.
78 'index=<int>' -- similar to the `index` keyword in Gnuplot.
80 This allows only some of the datasets in a file to be
79 This allows only some of the datasets in a file to be
81 plotted. Datasets within a file are assumed to be separated
80 plotted. Datasets within a file are assumed to be separated
82 by _pairs_ of blank lines, and the first one is numbered as
81 by _pairs_ of blank lines, and the first one is numbered as
83 0 (similar to C/Python usage)."""
82 0 (similar to C/Python usage)."""
84 __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc
83 __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc
85
84
86 def set_option_index(self, index):
85 def set_option_index(self, index):
87 if index is None:
86 if index is None:
88 self.clear_option('index')
87 self.clear_option('index')
89 elif type(index) in [type(''), type(1)]:
88 elif type(index) in [type(''), type(1)]:
90 self._options['index'] = (index, 'index %s' % index)
89 self._options['index'] = (index, 'index %s' % index)
91 elif type(index) is type(()):
90 elif type(index) is type(()):
92 self._options['index'] = (index,'index %s' %
91 self._options['index'] = (index,'index %s' %
93 string.join(map(repr, index), ':'))
92 string.join(map(repr, index), ':'))
94 else:
93 else:
95 raise OptionException('index=%s' % (index,))
94 raise OptionException('index=%s' % (index,))
96
95
97 # We need a FileClass with a different name from 'File', which is a
96 # We need a FileClass with a different name from 'File', which is a
98 # factory function in 1.7, so that our String class can subclass FileClass
97 # factory function in 1.7, so that our String class can subclass FileClass
99 # in any version.
98 # in any version.
100 _FileClass = File
99 _FileClass = File
101
100
102 elif Gnuplot_ori.__version__ =='1.7':
101 elif Gnuplot_ori.__version__ =='1.7':
103 _FileClass = _BaseFileItem = PlotItems._FileItem
102 _FileClass = _BaseFileItem = PlotItems._FileItem
104 _BaseTempFileItem = PlotItems._TempFileItem
103 _BaseTempFileItem = PlotItems._TempFileItem
105 File = PlotItems.File
104 File = PlotItems.File
106
105
107 else: # changes in the newer version (svn as of March'06)
106 else: # changes in the newer version (svn as of March'06)
108 _FileClass = _BaseFileItem = PlotItems._FileItem
107 _FileClass = _BaseFileItem = PlotItems._FileItem
109 _BaseTempFileItem = PlotItems._NewFileItem
108 _BaseTempFileItem = PlotItems._NewFileItem
110 File = PlotItems.File
109 File = PlotItems.File
111
110
112
111
113 # Now, we can add our generic code which is version independent
112 # Now, we can add our generic code which is version independent
114
113
115 # First some useful utilities
114 # First some useful utilities
116 def eps_fix_bbox(fname):
115 def eps_fix_bbox(fname):
117 """Fix the bounding box of an eps file by running ps2eps on it.
116 """Fix the bounding box of an eps file by running ps2eps on it.
118
117
119 If its name ends in .eps, the original file is removed.
118 If its name ends in .eps, the original file is removed.
120
119
121 This is particularly useful for plots made by Gnuplot with square aspect
120 This is particularly useful for plots made by Gnuplot with square aspect
122 ratio: there is a bug in Gnuplot which makes it generate a bounding box
121 ratio: there is a bug in Gnuplot which makes it generate a bounding box
123 which is far wider than the actual plot.
122 which is far wider than the actual plot.
124
123
125 This function assumes that ps2eps is installed in your system."""
124 This function assumes that ps2eps is installed in your system."""
126
125
127 # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The
126 # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The
128 # others make output with bitmapped fonts, which looks horrible.
127 # others make output with bitmapped fonts, which looks horrible.
129 print 'Fixing eps file: <%s>' % fname
128 print 'Fixing eps file: <%s>' % fname
130 xsys('ps2eps -f -q -l %s' % fname)
129 xsys('ps2eps -f -q -l %s' % fname)
131 if fname.endswith('.eps'):
130 if fname.endswith('.eps'):
132 os.rename(fname+'.eps',fname)
131 os.rename(fname+'.eps',fname)
133
132
134 def is_list1d(x,containers = [types.ListType,types.TupleType]):
133 def is_list1d(x,containers = [types.ListType,types.TupleType]):
135 """Returns true if x appears to be a 1d list/tuple/array.
134 """Returns true if x appears to be a 1d list/tuple/array.
136
135
137 The heuristics are: identify Numeric arrays, or lists/tuples whose first
136 The heuristics are: identify Numeric arrays, or lists/tuples whose first
138 element is not itself a list/tuple. This way zipped lists should work like
137 element is not itself a list/tuple. This way zipped lists should work like
139 the original Gnuplot. There's no inexpensive way to know if a list doesn't
138 the original Gnuplot. There's no inexpensive way to know if a list doesn't
140 have a composite object after its first element, so that kind of input
139 have a composite object after its first element, so that kind of input
141 will produce an error. But it should work well in most cases.
140 will produce an error. But it should work well in most cases.
142 """
141 """
143 x_type = type(x)
142 x_type = type(x)
144
143
145 return x_type == Numeric.ArrayType and len(x.shape)==1 or \
144 return x_type == Numeric.ArrayType and len(x.shape)==1 or \
146 (x_type in containers and
145 (x_type in containers and
147 type(x[0]) not in containers + [Numeric.ArrayType])
146 type(x[0]) not in containers + [Numeric.ArrayType])
148
147
149 def zip_items(items,titles=None):
148 def zip_items(items,titles=None):
150 """zip together neighboring 1-d arrays, and zip standalone ones
149 """zip together neighboring 1-d arrays, and zip standalone ones
151 with their index. Leave other plot items alone."""
150 with their index. Leave other plot items alone."""
152
151
153 class StandaloneItem(Exception): pass
152 class StandaloneItem(Exception): pass
154
153
155 def get_titles(titles):
154 def get_titles(titles):
156 """Return the next title and the input titles array.
155 """Return the next title and the input titles array.
157
156
158 The input array may be changed to None when no titles are left to
157 The input array may be changed to None when no titles are left to
159 prevent extra unnecessary calls to this function."""
158 prevent extra unnecessary calls to this function."""
160
159
161 try:
160 try:
162 title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope
161 title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope
163 except IndexError:
162 except IndexError:
164 titles = None # so we don't enter again
163 titles = None # so we don't enter again
165 title = None
164 title = None
166 else:
165 else:
167 tit_ct[0] += 1
166 tit_ct[0] += 1
168 return title,titles
167 return title,titles
169
168
170 new_items = []
169 new_items = []
171
170
172 if titles:
171 if titles:
173 # Initialize counter. It was put in a list as a hack to allow the
172 # Initialize counter. It was put in a list as a hack to allow the
174 # nested get_titles to modify it without raising a NameError.
173 # nested get_titles to modify it without raising a NameError.
175 tit_ct = [0]
174 tit_ct = [0]
176
175
177 n = 0 # this loop needs to be done by hand
176 n = 0 # this loop needs to be done by hand
178 while n < len(items):
177 while n < len(items):
179 item = items[n]
178 item = items[n]
180 try:
179 try:
181 if is_list1d(item):
180 if is_list1d(item):
182 if n==len(items)-1: # last in list
181 if n==len(items)-1: # last in list
183 raise StandaloneItem
182 raise StandaloneItem
184 else: # check the next item and zip together if needed
183 else: # check the next item and zip together if needed
185 next_item = items[n+1]
184 next_item = items[n+1]
186 if next_item is None:
185 if next_item is None:
187 n += 1
186 n += 1
188 raise StandaloneItem
187 raise StandaloneItem
189 elif is_list1d(next_item):
188 elif is_list1d(next_item):
190 # this would be best done with an iterator
189 # this would be best done with an iterator
191 if titles:
190 if titles:
192 title,titles = get_titles(titles)
191 title,titles = get_titles(titles)
193 else:
192 else:
194 title = None
193 title = None
195 new_items.append(Data(zip(item,next_item),
194 new_items.append(Data(zip(item,next_item),
196 title=title))
195 title=title))
197 n += 1 # avoid double-inclusion of next item
196 n += 1 # avoid double-inclusion of next item
198 else: # can't zip with next, zip with own index list
197 else: # can't zip with next, zip with own index list
199 raise StandaloneItem
198 raise StandaloneItem
200 else: # not 1-d array
199 else: # not 1-d array
201 new_items.append(item)
200 new_items.append(item)
202 except StandaloneItem:
201 except StandaloneItem:
203 if titles:
202 if titles:
204 title,titles = get_titles(titles)
203 title,titles = get_titles(titles)
205 else:
204 else:
206 title = None
205 title = None
207 new_items.append(Data(zip(range(len(item)),item),title=title))
206 new_items.append(Data(zip(range(len(item)),item),title=title))
208 except AttributeError:
207 except AttributeError:
209 new_items.append(item)
208 new_items.append(item)
210 n+=1
209 n+=1
211
210
212 return new_items
211 return new_items
213
212
214 # And some classes with enhanced functionality.
213 # And some classes with enhanced functionality.
215 class String(_FileClass):
214 class String(_FileClass):
216 """Make a PlotItem from data in a string with the same format as a File.
215 """Make a PlotItem from data in a string with the same format as a File.
217
216
218 This allows writing data directly inside python scripts using the exact
217 This allows writing data directly inside python scripts using the exact
219 same format and manipulation options which would be used for external
218 same format and manipulation options which would be used for external
220 files."""
219 files."""
221
220
222 def __init__(self, data_str, **keyw):
221 def __init__(self, data_str, **keyw):
223 """Construct a String object.
222 """Construct a String object.
224
223
225 <data_str> is a string formatted exactly like a valid Gnuplot data
224 <data_str> is a string formatted exactly like a valid Gnuplot data
226 file would be. All options from the File constructor are valid here.
225 file would be. All options from the File constructor are valid here.
227
226
228 Warning: when used for interactive plotting in scripts which exit
227 Warning: when used for interactive plotting in scripts which exit
229 immediately, you may get an error because the temporary file used to
228 immediately, you may get an error because the temporary file used to
230 hold the string data was deleted before Gnuplot had a chance to see
229 hold the string data was deleted before Gnuplot had a chance to see
231 it. You can work around this problem by putting a raw_input() call at
230 it. You can work around this problem by putting a raw_input() call at
232 the end of the script.
231 the end of the script.
233
232
234 This problem does not appear when generating PostScript output, only
233 This problem does not appear when generating PostScript output, only
235 with Gnuplot windows."""
234 with Gnuplot windows."""
236
235
237 self.tmpfile = _BaseTempFileItem()
236 self.tmpfile = _BaseTempFileItem()
238 tmpfile = file(self.tmpfile.filename,'w')
237 tmpfile = file(self.tmpfile.filename,'w')
239 tmpfile.write(data_str)
238 tmpfile.write(data_str)
240 _BaseFileItem.__init__(self,self.tmpfile,**keyw)
239 _BaseFileItem.__init__(self,self.tmpfile,**keyw)
241
240
242
241
243 class Gnuplot(Gnuplot_ori.Gnuplot):
242 class Gnuplot(Gnuplot_ori.Gnuplot):
244 """Improved Gnuplot class.
243 """Improved Gnuplot class.
245
244
246 Enhancements: better plot,replot and hardcopy methods. New methods for
245 Enhancements: better plot,replot and hardcopy methods. New methods for
247 quick range setting.
246 quick range setting.
248 """
247 """
249
248
250 def xrange(self,min='*',max='*'):
249 def xrange(self,min='*',max='*'):
251 """Set xrange. If min/max is omitted, it is set to '*' (auto).
250 """Set xrange. If min/max is omitted, it is set to '*' (auto).
252
251
253 Note that this is different from the regular Gnuplot behavior, where
252 Note that this is different from the regular Gnuplot behavior, where
254 an unspecified limit means no change. Here any unspecified limit is
253 an unspecified limit means no change. Here any unspecified limit is
255 set to autoscaling, allowing these functions to be used for full
254 set to autoscaling, allowing these functions to be used for full
256 autoscaling when called with no arguments.
255 autoscaling when called with no arguments.
257
256
258 To preserve one limit's current value while changing the other, an
257 To preserve one limit's current value while changing the other, an
259 explicit '' argument must be given as the limit to be kept.
258 explicit '' argument must be given as the limit to be kept.
260
259
261 Similar functions exist for [y{2}z{2}rtuv]range."""
260 Similar functions exist for [y{2}z{2}rtuv]range."""
262
261
263 self('set xrange [%s:%s]' % (min,max))
262 self('set xrange [%s:%s]' % (min,max))
264
263
265 def yrange(self,min='*',max='*'):
264 def yrange(self,min='*',max='*'):
266 self('set yrange [%s:%s]' % (min,max))
265 self('set yrange [%s:%s]' % (min,max))
267
266
268 def zrange(self,min='*',max='*'):
267 def zrange(self,min='*',max='*'):
269 self('set zrange [%s:%s]' % (min,max))
268 self('set zrange [%s:%s]' % (min,max))
270
269
271 def x2range(self,min='*',max='*'):
270 def x2range(self,min='*',max='*'):
272 self('set xrange [%s:%s]' % (min,max))
271 self('set xrange [%s:%s]' % (min,max))
273
272
274 def y2range(self,min='*',max='*'):
273 def y2range(self,min='*',max='*'):
275 self('set yrange [%s:%s]' % (min,max))
274 self('set yrange [%s:%s]' % (min,max))
276
275
277 def z2range(self,min='*',max='*'):
276 def z2range(self,min='*',max='*'):
278 self('set zrange [%s:%s]' % (min,max))
277 self('set zrange [%s:%s]' % (min,max))
279
278
280 def rrange(self,min='*',max='*'):
279 def rrange(self,min='*',max='*'):
281 self('set rrange [%s:%s]' % (min,max))
280 self('set rrange [%s:%s]' % (min,max))
282
281
283 def trange(self,min='*',max='*'):
282 def trange(self,min='*',max='*'):
284 self('set trange [%s:%s]' % (min,max))
283 self('set trange [%s:%s]' % (min,max))
285
284
286 def urange(self,min='*',max='*'):
285 def urange(self,min='*',max='*'):
287 self('set urange [%s:%s]' % (min,max))
286 self('set urange [%s:%s]' % (min,max))
288
287
289 def vrange(self,min='*',max='*'):
288 def vrange(self,min='*',max='*'):
290 self('set vrange [%s:%s]' % (min,max))
289 self('set vrange [%s:%s]' % (min,max))
291
290
292 def set_ps(self,option):
291 def set_ps(self,option):
293 """Set an option for the PostScript terminal and reset default term."""
292 """Set an option for the PostScript terminal and reset default term."""
294
293
295 self('set terminal postscript %s ' % option)
294 self('set terminal postscript %s ' % option)
296 self('set terminal %s' % gp.GnuplotOpts.default_term)
295 self('set terminal %s' % gp.GnuplotOpts.default_term)
297
296
298 def __plot_ps(self, plot_method,*items, **keyw):
297 def __plot_ps(self, plot_method,*items, **keyw):
299 """Wrapper for plot/splot/replot, with processing of hardcopy options.
298 """Wrapper for plot/splot/replot, with processing of hardcopy options.
300
299
301 For internal use only."""
300 For internal use only."""
302
301
303 # Filter out PostScript options which will crash the normal plot/replot
302 # Filter out PostScript options which will crash the normal plot/replot
304 psargs = {'filename':None,
303 psargs = {'filename':None,
305 'mode':None,
304 'mode':None,
306 'eps':None,
305 'eps':None,
307 'enhanced':None,
306 'enhanced':None,
308 'color':None,
307 'color':None,
309 'solid':None,
308 'solid':None,
310 'duplexing':None,
309 'duplexing':None,
311 'fontname':None,
310 'fontname':None,
312 'fontsize':None,
311 'fontsize':None,
313 'debug':0 }
312 'debug':0 }
314
313
315 for k in psargs.keys():
314 for k in psargs.keys():
316 if keyw.has_key(k):
315 if keyw.has_key(k):
317 psargs[k] = keyw[k]
316 psargs[k] = keyw[k]
318 del keyw[k]
317 del keyw[k]
319
318
320 # Filter out other options the original plot doesn't know
319 # Filter out other options the original plot doesn't know
321 hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None)
320 hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None)
322 titles = popkey(keyw,'titles',0)
321 titles = popkey(keyw,'titles',0)
323
322
324 # the filename keyword should control hardcopy generation, this is an
323 # the filename keyword should control hardcopy generation, this is an
325 # override switch only which needs to be explicitly set to zero
324 # override switch only which needs to be explicitly set to zero
326 if hardcopy:
325 if hardcopy:
327 if psargs['filename'] is None:
326 if psargs['filename'] is None:
328 raise ValueError, \
327 raise ValueError, \
329 'If you request hardcopy, you must give a filename.'
328 'If you request hardcopy, you must give a filename.'
330
329
331 # set null output so nothing goes to screen. hardcopy() restores output
330 # set null output so nothing goes to screen. hardcopy() restores output
332 self('set term dumb')
331 self('set term dumb')
333 # I don't know how to prevent screen output in Windows
332 # I don't know how to prevent screen output in Windows
334 if os.name == 'posix':
333 if os.name == 'posix':
335 self('set output "/dev/null"')
334 self('set output "/dev/null"')
336
335
337 new_items = zip_items(items,titles)
336 new_items = zip_items(items,titles)
338 # plot_method is either plot or replot from the original Gnuplot class:
337 # plot_method is either plot or replot from the original Gnuplot class:
339 plot_method(self,*new_items,**keyw)
338 plot_method(self,*new_items,**keyw)
340
339
341 # Do hardcopy if requested
340 # Do hardcopy if requested
342 if hardcopy:
341 if hardcopy:
343 if psargs['filename'].endswith('.eps'):
342 if psargs['filename'].endswith('.eps'):
344 psargs['eps'] = 1
343 psargs['eps'] = 1
345 self.hardcopy(**psargs)
344 self.hardcopy(**psargs)
346
345
347 def plot(self, *items, **keyw):
346 def plot(self, *items, **keyw):
348 """Draw a new plot.
347 """Draw a new plot.
349
348
350 Clear the current plot and create a new 2-d plot containing
349 Clear the current plot and create a new 2-d plot containing
351 the specified items. Each arguments should be of the
350 the specified items. Each arguments should be of the
352 following types:
351 following types:
353
352
354 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most
353 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most
355 flexible way to call plot because the PlotItems can
354 flexible way to call plot because the PlotItems can
356 contain suboptions. Moreover, PlotItems can be saved to
355 contain suboptions. Moreover, PlotItems can be saved to
357 variables so that their lifetime is longer than one plot
356 variables so that their lifetime is longer than one plot
358 command; thus they can be replotted with minimal overhead.
357 command; thus they can be replotted with minimal overhead.
359
358
360 'string' (e.g., 'sin(x)') -- The string is interpreted as
359 'string' (e.g., 'sin(x)') -- The string is interpreted as
361 'Func(string)' (a function that is computed by gnuplot).
360 'Func(string)' (a function that is computed by gnuplot).
362
361
363 Anything else -- The object, which should be convertible to an
362 Anything else -- The object, which should be convertible to an
364 array, is passed to the 'Data' constructor, and thus
363 array, is passed to the 'Data' constructor, and thus
365 plotted as data. If the conversion fails, an exception is
364 plotted as data. If the conversion fails, an exception is
366 raised.
365 raised.
367
366
368
367
369 This is a modified version of plot(). Compared to the original in
368 This is a modified version of plot(). Compared to the original in
370 Gnuplot.py, this version has several enhancements, listed below.
369 Gnuplot.py, this version has several enhancements, listed below.
371
370
372
371
373 Modifications to the input arguments
372 Modifications to the input arguments
374 ------------------------------------
373 ------------------------------------
375
374
376 (1-d array means Numeric array, list or tuple):
375 (1-d array means Numeric array, list or tuple):
377
376
378 (i) Any 1-d array which is NOT followed by another 1-d array, is
377 (i) Any 1-d array which is NOT followed by another 1-d array, is
379 automatically zipped with range(len(array_1d)). Typing g.plot(y) will
378 automatically zipped with range(len(array_1d)). Typing g.plot(y) will
380 plot y against its indices.
379 plot y against its indices.
381
380
382 (ii) If two 1-d arrays are contiguous in the argument list, they are
381 (ii) If two 1-d arrays are contiguous in the argument list, they are
383 automatically zipped together. So g.plot(x,y) plots y vs. x, and
382 automatically zipped together. So g.plot(x,y) plots y vs. x, and
384 g.plot(x1,y1,x2,y2) plots y1 vs. x1 and y2 vs. x2.
383 g.plot(x1,y1,x2,y2) plots y1 vs. x1 and y2 vs. x2.
385
384
386 (iii) Any 1-d array which is followed by None is automatically zipped
385 (iii) Any 1-d array which is followed by None is automatically zipped
387 with range(len(array_1d)). In this form, typing g.plot(y1,None,y2)
386 with range(len(array_1d)). In this form, typing g.plot(y1,None,y2)
388 will plot both y1 and y2 against their respective indices (and NOT
387 will plot both y1 and y2 against their respective indices (and NOT
389 versus one another). The None prevents zipping y1 and y2 together, and
388 versus one another). The None prevents zipping y1 and y2 together, and
390 since y2 is unpaired it is automatically zipped to its indices by (i)
389 since y2 is unpaired it is automatically zipped to its indices by (i)
391
390
392 (iv) Any other arguments which don't match these cases are left alone and
391 (iv) Any other arguments which don't match these cases are left alone and
393 passed to the code below.
392 passed to the code below.
394
393
395 For lists or tuples, the heuristics used to determine whether they are
394 For lists or tuples, the heuristics used to determine whether they are
396 in fact 1-d is fairly simplistic: their first element is checked, and
395 in fact 1-d is fairly simplistic: their first element is checked, and
397 if it is not a list or tuple itself, it is assumed that the whole
396 if it is not a list or tuple itself, it is assumed that the whole
398 object is one-dimensional.
397 object is one-dimensional.
399
398
400 An additional optional keyword 'titles' has been added: it must be a
399 An additional optional keyword 'titles' has been added: it must be a
401 list of strings to be used as labels for the individual plots which
400 list of strings to be used as labels for the individual plots which
402 are NOT PlotItem objects (since those objects carry their own labels
401 are NOT PlotItem objects (since those objects carry their own labels
403 within).
402 within).
404
403
405
404
406 PostScript generation
405 PostScript generation
407 ---------------------
406 ---------------------
408
407
409 This version of plot() also handles automatically the production of
408 This version of plot() also handles automatically the production of
410 PostScript output. The main options are (given as keyword arguments):
409 PostScript output. The main options are (given as keyword arguments):
411
410
412 - filename: a string, typically ending in .eps. If given, the plot is
411 - filename: a string, typically ending in .eps. If given, the plot is
413 sent to this file in PostScript format.
412 sent to this file in PostScript format.
414
413
415 - hardcopy: this can be set to 0 to override 'filename'. It does not
414 - hardcopy: this can be set to 0 to override 'filename'. It does not
416 need to be given to produce PostScript, its purpose is to allow
415 need to be given to produce PostScript, its purpose is to allow
417 switching PostScript output off globally in scripts without having to
416 switching PostScript output off globally in scripts without having to
418 manually change 'filename' values in multiple calls.
417 manually change 'filename' values in multiple calls.
419
418
420 All other keywords accepted by Gnuplot.hardcopy() are transparently
419 All other keywords accepted by Gnuplot.hardcopy() are transparently
421 passed, and safely ignored if output is sent to the screen instead of
420 passed, and safely ignored if output is sent to the screen instead of
422 PostScript.
421 PostScript.
423
422
424 For example:
423 For example:
425
424
426 In [1]: x=frange(0,2*pi,npts=100)
425 In [1]: x=frange(0,2*pi,npts=100)
427
426
428 Generate a plot in file 'sin.eps':
427 Generate a plot in file 'sin.eps':
429
428
430 In [2]: plot(x,sin(x),filename = 'sin.eps')
429 In [2]: plot(x,sin(x),filename = 'sin.eps')
431
430
432 Plot to screen instead, without having to change the filename:
431 Plot to screen instead, without having to change the filename:
433
432
434 In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0)
433 In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0)
435
434
436 Pass the 'color=0' option to hardcopy for monochrome output:
435 Pass the 'color=0' option to hardcopy for monochrome output:
437
436
438 In [4]: plot(x,sin(x),filename = 'sin.eps',color=0)
437 In [4]: plot(x,sin(x),filename = 'sin.eps',color=0)
439
438
440 PostScript generation through plot() is useful mainly for scripting
439 PostScript generation through plot() is useful mainly for scripting
441 uses where you are not interested in interactive plotting. For
440 uses where you are not interested in interactive plotting. For
442 interactive use, the hardcopy() function is typically more convenient:
441 interactive use, the hardcopy() function is typically more convenient:
443
442
444 In [5]: plot(x,sin(x))
443 In [5]: plot(x,sin(x))
445
444
446 In [6]: hardcopy('sin.eps') """
445 In [6]: hardcopy('sin.eps') """
447
446
448 self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw)
447 self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw)
449
448
450 def plot2(self,arg,**kw):
449 def plot2(self,arg,**kw):
451 """Plot the entries of a dictionary or a list/tuple of arrays.
450 """Plot the entries of a dictionary or a list/tuple of arrays.
452
451
453 This simple utility calls plot() with a list of Gnuplot.Data objects
452 This simple utility calls plot() with a list of Gnuplot.Data objects
454 constructed either from the values of the input dictionary, or the entries
453 constructed either from the values of the input dictionary, or the entries
455 in it if it is a tuple or list. Each item gets labeled with the key/index
454 in it if it is a tuple or list. Each item gets labeled with the key/index
456 in the Gnuplot legend.
455 in the Gnuplot legend.
457
456
458 Each item is plotted by zipping it with a list of its indices.
457 Each item is plotted by zipping it with a list of its indices.
459
458
460 Any keywords are passed directly to plot()."""
459 Any keywords are passed directly to plot()."""
461
460
462 if hasattr(arg,'keys'):
461 if hasattr(arg,'keys'):
463 keys = arg.keys()
462 keys = arg.keys()
464 keys.sort()
463 keys.sort()
465 else:
464 else:
466 keys = range(len(arg))
465 keys = range(len(arg))
467
466
468 pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys]
467 pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys]
469 self.plot(*pitems,**kw)
468 self.plot(*pitems,**kw)
470
469
471 def splot(self, *items, **keyw):
470 def splot(self, *items, **keyw):
472 """Draw a new three-dimensional plot.
471 """Draw a new three-dimensional plot.
473
472
474 Clear the current plot and create a new 3-d plot containing
473 Clear the current plot and create a new 3-d plot containing
475 the specified items. Arguments can be of the following types:
474 the specified items. Arguments can be of the following types:
476
475
477 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This
476 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This
478 is the most flexible way to call plot because the
477 is the most flexible way to call plot because the
479 PlotItems can contain suboptions. Moreover, PlotItems can
478 PlotItems can contain suboptions. Moreover, PlotItems can
480 be saved to variables so that their lifetime is longer
479 be saved to variables so that their lifetime is longer
481 than one plot command--thus they can be replotted with
480 than one plot command--thus they can be replotted with
482 minimal overhead.
481 minimal overhead.
483
482
484 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a
483 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a
485 'Func()' (a function that is computed by gnuplot).
484 'Func()' (a function that is computed by gnuplot).
486
485
487 Anything else -- The object is converted to a Data() item, and
486 Anything else -- The object is converted to a Data() item, and
488 thus plotted as data. Note that each data point should
487 thus plotted as data. Note that each data point should
489 normally have at least three values associated with it
488 normally have at least three values associated with it
490 (i.e., x, y, and z). If the conversion fails, an
489 (i.e., x, y, and z). If the conversion fails, an
491 exception is raised.
490 exception is raised.
492
491
493 This is a modified version of splot(). Compared to the original in
492 This is a modified version of splot(). Compared to the original in
494 Gnuplot.py, this version has several enhancements, listed in the
493 Gnuplot.py, this version has several enhancements, listed in the
495 plot() documentation.
494 plot() documentation.
496 """
495 """
497
496
498 self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw)
497 self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw)
499
498
500 def replot(self, *items, **keyw):
499 def replot(self, *items, **keyw):
501 """Replot the data, possibly adding new 'PlotItem's.
500 """Replot the data, possibly adding new 'PlotItem's.
502
501
503 Replot the existing graph, using the items in the current
502 Replot the existing graph, using the items in the current
504 itemlist. If arguments are specified, they are interpreted as
503 itemlist. If arguments are specified, they are interpreted as
505 additional items to be plotted alongside the existing items on
504 additional items to be plotted alongside the existing items on
506 the same graph. See 'plot' for details.
505 the same graph. See 'plot' for details.
507
506
508 If you want to replot to a postscript file, you MUST give the
507 If you want to replot to a postscript file, you MUST give the
509 'filename' keyword argument in each call to replot. The Gnuplot python
508 'filename' keyword argument in each call to replot. The Gnuplot python
510 interface has no way of knowing that your previous call to
509 interface has no way of knowing that your previous call to
511 Gnuplot.plot() was meant for PostScript output."""
510 Gnuplot.plot() was meant for PostScript output."""
512
511
513 self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw)
512 self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw)
514
513
515 # The original hardcopy has a bug. See fix at the end. The rest of the code
514 # The original hardcopy has a bug. See fix at the end. The rest of the code
516 # was lifted verbatim from the original, so that people using IPython get the
515 # was lifted verbatim from the original, so that people using IPython get the
517 # benefits without having to manually patch Gnuplot.py
516 # benefits without having to manually patch Gnuplot.py
518 def hardcopy(self, filename=None,
517 def hardcopy(self, filename=None,
519 mode=None,
518 mode=None,
520 eps=None,
519 eps=None,
521 enhanced=None,
520 enhanced=None,
522 color=None,
521 color=None,
523 solid=None,
522 solid=None,
524 duplexing=None,
523 duplexing=None,
525 fontname=None,
524 fontname=None,
526 fontsize=None,
525 fontsize=None,
527 debug = 0,
526 debug = 0,
528 ):
527 ):
529 """Create a hardcopy of the current plot.
528 """Create a hardcopy of the current plot.
530
529
531 Create a postscript hardcopy of the current plot to the
530 Create a postscript hardcopy of the current plot to the
532 default printer (if configured) or to the specified filename.
531 default printer (if configured) or to the specified filename.
533
532
534 Note that gnuplot remembers the postscript suboptions across
533 Note that gnuplot remembers the postscript suboptions across
535 terminal changes. Therefore if you set, for example, color=1
534 terminal changes. Therefore if you set, for example, color=1
536 for one hardcopy then the next hardcopy will also be color
535 for one hardcopy then the next hardcopy will also be color
537 unless you explicitly choose color=0. Alternately you can
536 unless you explicitly choose color=0. Alternately you can
538 force all of the options to their defaults by setting
537 force all of the options to their defaults by setting
539 mode='default'. I consider this to be a bug in gnuplot.
538 mode='default'. I consider this to be a bug in gnuplot.
540
539
541 Keyword arguments:
540 Keyword arguments:
542
541
543 'filename=<string>' -- if a filename is specified, save the
542 'filename=<string>' -- if a filename is specified, save the
544 output in that file; otherwise print it immediately
543 output in that file; otherwise print it immediately
545 using the 'default_lpr' configuration option. If the
544 using the 'default_lpr' configuration option. If the
546 filename ends in '.eps', EPS mode is automatically
545 filename ends in '.eps', EPS mode is automatically
547 selected (like manually specifying eps=1 or mode='eps').
546 selected (like manually specifying eps=1 or mode='eps').
548
547
549 'mode=<string>' -- set the postscript submode ('landscape',
548 'mode=<string>' -- set the postscript submode ('landscape',
550 'portrait', 'eps', or 'default'). The default is
549 'portrait', 'eps', or 'default'). The default is
551 to leave this option unspecified.
550 to leave this option unspecified.
552
551
553 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to
552 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to
554 generate encapsulated postscript.
553 generate encapsulated postscript.
555
554
556 'enhanced=<bool>' -- if set (the default), then generate
555 'enhanced=<bool>' -- if set (the default), then generate
557 enhanced postscript, which allows extra features like
556 enhanced postscript, which allows extra features like
558 font-switching, superscripts, and subscripts in axis
557 font-switching, superscripts, and subscripts in axis
559 labels. (Some old gnuplot versions do not support
558 labels. (Some old gnuplot versions do not support
560 enhanced postscript; if this is the case set
559 enhanced postscript; if this is the case set
561 gp.GnuplotOpts.prefer_enhanced_postscript=None.)
560 gp.GnuplotOpts.prefer_enhanced_postscript=None.)
562
561
563 'color=<bool>' -- if set, create a plot with color. Default
562 'color=<bool>' -- if set, create a plot with color. Default
564 is to leave this option unchanged.
563 is to leave this option unchanged.
565
564
566 'solid=<bool>' -- if set, force lines to be solid (i.e., not
565 'solid=<bool>' -- if set, force lines to be solid (i.e., not
567 dashed).
566 dashed).
568
567
569 'duplexing=<string>' -- set duplexing option ('defaultplex',
568 'duplexing=<string>' -- set duplexing option ('defaultplex',
570 'simplex', or 'duplex'). Only request double-sided
569 'simplex', or 'duplex'). Only request double-sided
571 printing if your printer can handle it. Actually this
570 printing if your printer can handle it. Actually this
572 option is probably meaningless since hardcopy() can only
571 option is probably meaningless since hardcopy() can only
573 print a single plot at a time.
572 print a single plot at a time.
574
573
575 'fontname=<string>' -- set the default font to <string>,
574 'fontname=<string>' -- set the default font to <string>,
576 which must be a valid postscript font. The default is
575 which must be a valid postscript font. The default is
577 to leave this option unspecified.
576 to leave this option unspecified.
578
577
579 'fontsize=<double>' -- set the default font size, in
578 'fontsize=<double>' -- set the default font size, in
580 postscript points.
579 postscript points.
581
580
582 'debug=<bool>' -- print extra debugging information (useful if
581 'debug=<bool>' -- print extra debugging information (useful if
583 your PostScript files are misteriously not being created).
582 your PostScript files are misteriously not being created).
584 """
583 """
585
584
586 if filename is None:
585 if filename is None:
587 assert gp.GnuplotOpts.default_lpr is not None, \
586 assert gp.GnuplotOpts.default_lpr is not None, \
588 OptionException('default_lpr is not set, so you can only '
587 OptionException('default_lpr is not set, so you can only '
589 'print to a file.')
588 'print to a file.')
590 filename = gp.GnuplotOpts.default_lpr
589 filename = gp.GnuplotOpts.default_lpr
591 lpr_output = 1
590 lpr_output = 1
592 else:
591 else:
593 if filename.endswith('.eps'):
592 if filename.endswith('.eps'):
594 eps = 1
593 eps = 1
595 lpr_output = 0
594 lpr_output = 0
596
595
597 # Be careful processing the options. If the user didn't
596 # Be careful processing the options. If the user didn't
598 # request an option explicitly, do not specify it on the 'set
597 # request an option explicitly, do not specify it on the 'set
599 # terminal' line (don't even specify the default value for the
598 # terminal' line (don't even specify the default value for the
600 # option). This is to avoid confusing older versions of
599 # option). This is to avoid confusing older versions of
601 # gnuplot that do not support all of these options. The
600 # gnuplot that do not support all of these options. The
602 # exception is 'enhanced', which is just too useful to have to
601 # exception is 'enhanced', which is just too useful to have to
603 # specify each time!
602 # specify each time!
604
603
605 setterm = ['set', 'terminal', 'postscript']
604 setterm = ['set', 'terminal', 'postscript']
606 if eps:
605 if eps:
607 assert mode is None or mode=='eps', \
606 assert mode is None or mode=='eps', \
608 OptionException('eps option and mode are incompatible')
607 OptionException('eps option and mode are incompatible')
609 setterm.append('eps')
608 setterm.append('eps')
610 else:
609 else:
611 if mode is not None:
610 if mode is not None:
612 assert mode in ['landscape', 'portrait', 'eps', 'default'], \
611 assert mode in ['landscape', 'portrait', 'eps', 'default'], \
613 OptionException('illegal mode "%s"' % mode)
612 OptionException('illegal mode "%s"' % mode)
614 setterm.append(mode)
613 setterm.append(mode)
615 if enhanced is None:
614 if enhanced is None:
616 enhanced = gp.GnuplotOpts.prefer_enhanced_postscript
615 enhanced = gp.GnuplotOpts.prefer_enhanced_postscript
617 if enhanced is not None:
616 if enhanced is not None:
618 if enhanced: setterm.append('enhanced')
617 if enhanced: setterm.append('enhanced')
619 else: setterm.append('noenhanced')
618 else: setterm.append('noenhanced')
620 if color is not None:
619 if color is not None:
621 if color: setterm.append('color')
620 if color: setterm.append('color')
622 else: setterm.append('monochrome')
621 else: setterm.append('monochrome')
623 if solid is not None:
622 if solid is not None:
624 if solid: setterm.append('solid')
623 if solid: setterm.append('solid')
625 else: setterm.append('dashed')
624 else: setterm.append('dashed')
626 if duplexing is not None:
625 if duplexing is not None:
627 assert duplexing in ['defaultplex', 'simplex', 'duplex'], \
626 assert duplexing in ['defaultplex', 'simplex', 'duplex'], \
628 OptionException('illegal duplexing mode "%s"' % duplexing)
627 OptionException('illegal duplexing mode "%s"' % duplexing)
629 setterm.append(duplexing)
628 setterm.append(duplexing)
630 if fontname is not None:
629 if fontname is not None:
631 setterm.append('"%s"' % fontname)
630 setterm.append('"%s"' % fontname)
632 if fontsize is not None:
631 if fontsize is not None:
633 setterm.append('%s' % fontsize)
632 setterm.append('%s' % fontsize)
634
633
635 self(string.join(setterm))
634 self(string.join(setterm))
636 self.set_string('output', filename)
635 self.set_string('output', filename)
637 # replot the current figure (to the printer):
636 # replot the current figure (to the printer):
638 self.refresh()
637 self.refresh()
639
638
640 # fperez. Ugly kludge: often for some reason the file is NOT created
639 # fperez. Ugly kludge: often for some reason the file is NOT created
641 # and we must reissue the creation commands. I have no idea why!
640 # and we must reissue the creation commands. I have no idea why!
642 if not lpr_output:
641 if not lpr_output:
643 #print 'Hardcopy <%s>' % filename # dbg
642 #print 'Hardcopy <%s>' % filename # dbg
644 maxtries = 20
643 maxtries = 20
645 delay = 0.1 # delay (in seconds) between print attempts
644 delay = 0.1 # delay (in seconds) between print attempts
646 for i in range(maxtries):
645 for i in range(maxtries):
647 time.sleep(0.05) # safety, very small delay
646 time.sleep(0.05) # safety, very small delay
648 if os.path.isfile(filename):
647 if os.path.isfile(filename):
649 if debug:
648 if debug:
650 print 'Hardcopy to file <%s> success at attempt #%s.' \
649 print 'Hardcopy to file <%s> success at attempt #%s.' \
651 % (filename,i+1)
650 % (filename,i+1)
652 break
651 break
653 time.sleep(delay)
652 time.sleep(delay)
654 # try again, issue all commands just in case
653 # try again, issue all commands just in case
655 self(string.join(setterm))
654 self(string.join(setterm))
656 self.set_string('output', filename)
655 self.set_string('output', filename)
657 self.refresh()
656 self.refresh()
658 if not os.path.isfile(filename):
657 if not os.path.isfile(filename):
659 print >> sys.stderr,'ERROR: Tried %s times and failed to '\
658 print >> sys.stderr,'ERROR: Tried %s times and failed to '\
660 'create hardcopy file `%s`' % (maxtries,filename)
659 'create hardcopy file `%s`' % (maxtries,filename)
661
660
662 # reset the terminal to its `default' setting:
661 # reset the terminal to its `default' setting:
663 self('set terminal %s' % gp.GnuplotOpts.default_term)
662 self('set terminal %s' % gp.GnuplotOpts.default_term)
664 self.set_string('output')
663 self.set_string('output')
665
664
666 #********************** End of file <Gnuplot2.py> ************************
665 #********************** End of file <Gnuplot2.py> ************************
@@ -1,148 +1,147 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Interactive functions and magic functions for Gnuplot usage.
2 """Interactive functions and magic functions for Gnuplot usage.
3
3
4 This requires the Gnuplot.py module for interfacing python with Gnuplot, which
4 This requires the Gnuplot.py module for interfacing python with Gnuplot, which
5 can be downloaded from:
5 can be downloaded from:
6
6
7 http://gnuplot-py.sourceforge.net/
7 http://gnuplot-py.sourceforge.net/
8
8
9 See gphelp() below for details on the services offered by this module.
9 See gphelp() below for details on the services offered by this module.
10
10
11 Inspired by a suggestion/request from Arnd Baecker.
11 Inspired by a suggestion/request from Arnd Baecker.
12
12 """
13 $Id: GnuplotInteractive.py 389 2004-10-09 07:59:30Z fperez $"""
14
13
15 __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot',
14 __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot',
16 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid',
15 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid',
17 'gphelp']
16 'gphelp']
18
17
19 import IPython.GnuplotRuntime as GRun
18 import IPython.GnuplotRuntime as GRun
20 from IPython.genutils import page,warn
19 from IPython.genutils import page,warn
21
20
22 # Set global names for interactive use
21 # Set global names for interactive use
23 Gnuplot = GRun.Gnuplot
22 Gnuplot = GRun.Gnuplot
24 gp_new = GRun.gp_new
23 gp_new = GRun.gp_new
25 gp = GRun.gp
24 gp = GRun.gp
26 plot = gp.plot
25 plot = gp.plot
27 plot2 = gp.plot2
26 plot2 = gp.plot2
28 splot = gp.splot
27 splot = gp.splot
29 replot = gp.replot
28 replot = gp.replot
30 hardcopy = gp.hardcopy
29 hardcopy = gp.hardcopy
31
30
32 # Accessors for the main plot object constructors:
31 # Accessors for the main plot object constructors:
33 gpdata = Gnuplot.Data
32 gpdata = Gnuplot.Data
34 gpfile = Gnuplot.File
33 gpfile = Gnuplot.File
35 gpstring = Gnuplot.String
34 gpstring = Gnuplot.String
36 gpfunc = Gnuplot.Func
35 gpfunc = Gnuplot.Func
37 gpgrid = Gnuplot.GridData
36 gpgrid = Gnuplot.GridData
38
37
39 def gphelp():
38 def gphelp():
40 """Print information about the Gnuplot facilities in IPython."""
39 """Print information about the Gnuplot facilities in IPython."""
41
40
42 page("""
41 page("""
43 IPython provides an interface to access the Gnuplot scientific plotting
42 IPython provides an interface to access the Gnuplot scientific plotting
44 system, in an environment similar to that of Mathematica or Matlab.
43 system, in an environment similar to that of Mathematica or Matlab.
45
44
46 New top-level global objects
45 New top-level global objects
47 ----------------------------
46 ----------------------------
48
47
49 Please see their respective docstrings for further details.
48 Please see their respective docstrings for further details.
50
49
51 - gp: a running Gnuplot instance. You can access its methods as
50 - gp: a running Gnuplot instance. You can access its methods as
52 gp.<method>. gp(`a string`) will execute the given string as if it had been
51 gp.<method>. gp(`a string`) will execute the given string as if it had been
53 typed in an interactive gnuplot window.
52 typed in an interactive gnuplot window.
54
53
55 - plot, splot, replot and hardcopy: aliases to the methods of the same name in
54 - plot, splot, replot and hardcopy: aliases to the methods of the same name in
56 the global running Gnuplot instance gp. These allow you to simply type:
55 the global running Gnuplot instance gp. These allow you to simply type:
57
56
58 In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array
57 In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array
59
58
60 and obtain a plot of sin(x) vs x with the title 'Sin(x)'.
59 and obtain a plot of sin(x) vs x with the title 'Sin(x)'.
61
60
62 - gp_new: a function which returns a new Gnuplot instance. This can be used to
61 - gp_new: a function which returns a new Gnuplot instance. This can be used to
63 have multiple Gnuplot instances running in your session to compare different
62 have multiple Gnuplot instances running in your session to compare different
64 plots, each in a separate window.
63 plots, each in a separate window.
65
64
66 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
65 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
67 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
66 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
68 functions with improved versions (Gnuplot2 comes with IPython).
67 functions with improved versions (Gnuplot2 comes with IPython).
69
68
70 - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data,
69 - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data,
71 Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData
70 Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData
72 respectively. These functions create objects which can then be passed to the
71 respectively. These functions create objects which can then be passed to the
73 plotting commands. See the Gnuplot.py documentation for details.
72 plotting commands. See the Gnuplot.py documentation for details.
74
73
75 Keep in mind that all commands passed to a Gnuplot instance are executed in
74 Keep in mind that all commands passed to a Gnuplot instance are executed in
76 the Gnuplot namespace, where no Python variables exist. For example, for
75 the Gnuplot namespace, where no Python variables exist. For example, for
77 plotting sin(x) vs x as above, typing
76 plotting sin(x) vs x as above, typing
78
77
79 In [2]: gp('plot x,sin(x)')
78 In [2]: gp('plot x,sin(x)')
80
79
81 would not work. Instead, you would get the plot of BOTH the functions 'x' and
80 would not work. Instead, you would get the plot of BOTH the functions 'x' and
82 'sin(x)', since Gnuplot doesn't know about the 'x' Python array. The plot()
81 'sin(x)', since Gnuplot doesn't know about the 'x' Python array. The plot()
83 method lives in python and does know about these variables.
82 method lives in python and does know about these variables.
84
83
85
84
86 New magic functions
85 New magic functions
87 -------------------
86 -------------------
88
87
89 %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where
88 %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where
90 each line of input is executed.
89 each line of input is executed.
91
90
92 %gp_set_default: reset the value of IPython's global Gnuplot instance.""")
91 %gp_set_default: reset the value of IPython's global Gnuplot instance.""")
93
92
94 # Code below is all for IPython use
93 # Code below is all for IPython use
95 # Define the magic functions for communicating with the above gnuplot instance.
94 # Define the magic functions for communicating with the above gnuplot instance.
96 def magic_gpc(self,parameter_s=''):
95 def magic_gpc(self,parameter_s=''):
97 """Execute a gnuplot command or open a gnuplot shell.
96 """Execute a gnuplot command or open a gnuplot shell.
98
97
99 Usage (omit the % if automagic is on). There are two ways to use it:
98 Usage (omit the % if automagic is on). There are two ways to use it:
100
99
101 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance.
100 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance.
102
101
103 2) %gpc -> will open up a prompt (gnuplot>>>) which takes input like the
102 2) %gpc -> will open up a prompt (gnuplot>>>) which takes input like the
104 standard gnuplot interactive prompt. If you need to type a multi-line
103 standard gnuplot interactive prompt. If you need to type a multi-line
105 command, use \\ at the end of each intermediate line.
104 command, use \\ at the end of each intermediate line.
106
105
107 Upon exiting of the gnuplot sub-shell, you return to your IPython
106 Upon exiting of the gnuplot sub-shell, you return to your IPython
108 session (the gnuplot sub-shell can be invoked as many times as needed).
107 session (the gnuplot sub-shell can be invoked as many times as needed).
109 """
108 """
110
109
111 if parameter_s.strip():
110 if parameter_s.strip():
112 self.shell.gnuplot(parameter_s)
111 self.shell.gnuplot(parameter_s)
113 else:
112 else:
114 self.shell.gnuplot.interact()
113 self.shell.gnuplot.interact()
115
114
116 def magic_gp_set_default(self,parameter_s=''):
115 def magic_gp_set_default(self,parameter_s=''):
117 """Set the default gnuplot instance accessed by the %gp magic function.
116 """Set the default gnuplot instance accessed by the %gp magic function.
118
117
119 %gp_set_default name
118 %gp_set_default name
120
119
121 Call with the name of the new instance at the command line. If you want to
120 Call with the name of the new instance at the command line. If you want to
122 set this instance in your own code (using an embedded IPython, for
121 set this instance in your own code (using an embedded IPython, for
123 example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot
122 example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot
124 instance object."""
123 instance object."""
125
124
126 gname = parameter_s.strip()
125 gname = parameter_s.strip()
127 G = eval(gname,self.shell.user_ns)
126 G = eval(gname,self.shell.user_ns)
128 self.shell.gnuplot = G
127 self.shell.gnuplot = G
129 self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2,
128 self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2,
130 'replot':G.replot,'hardcopy':G.hardcopy})
129 'replot':G.replot,'hardcopy':G.hardcopy})
131
130
132 try:
131 try:
133 __IPYTHON__
132 __IPYTHON__
134 except NameError:
133 except NameError:
135 pass
134 pass
136 else:
135 else:
137 # make the global Gnuplot instance known to IPython
136 # make the global Gnuplot instance known to IPython
138 __IPYTHON__.gnuplot = GRun.gp
137 __IPYTHON__.gnuplot = GRun.gp
139 __IPYTHON__.gnuplot.shell_first_time = 1
138 __IPYTHON__.gnuplot.shell_first_time = 1
140
139
141 print """*** Type `gphelp` for help on the Gnuplot integration features."""
140 print """*** Type `gphelp` for help on the Gnuplot integration features."""
142
141
143 # Add the new magic functions to the class dict
142 # Add the new magic functions to the class dict
144 from IPython.iplib import InteractiveShell
143 from IPython.iplib import InteractiveShell
145 InteractiveShell.magic_gpc = magic_gpc
144 InteractiveShell.magic_gpc = magic_gpc
146 InteractiveShell.magic_gp_set_default = magic_gp_set_default
145 InteractiveShell.magic_gp_set_default = magic_gp_set_default
147
146
148 #********************** End of file <GnuplotInteractive.py> *******************
147 #********************** End of file <GnuplotInteractive.py> *******************
@@ -1,147 +1,146 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Basic Gnuplot functionality for inclusion in other code.
2 """Basic Gnuplot functionality for inclusion in other code.
3
3
4 This module creates a running Gnuplot instance called 'gp' and builds other
4 This module creates a running Gnuplot instance called 'gp' and builds other
5 convenient globals for quick use in running scripts. It is intended to allow
5 convenient globals for quick use in running scripts. It is intended to allow
6 you to script plotting tasks in Python with a minimum of effort. A typical
6 you to script plotting tasks in Python with a minimum of effort. A typical
7 usage would be:
7 usage would be:
8
8
9 import IPython.GnuplotRuntime as GP # or some other short name
9 import IPython.GnuplotRuntime as GP # or some other short name
10 GP.gp.plot(GP.File('your_data.dat'))
10 GP.gp.plot(GP.File('your_data.dat'))
11
11
12
12
13 This module exposes the following objects:
13 This module exposes the following objects:
14
14
15 - gp: a running Gnuplot instance. You can access its methods as
15 - gp: a running Gnuplot instance. You can access its methods as
16 gp.<method>. gp(`a string`) will execute the given string as if it had been
16 gp.<method>. gp(`a string`) will execute the given string as if it had been
17 typed in an interactive gnuplot window.
17 typed in an interactive gnuplot window.
18
18
19 - gp_new: a function which returns a new Gnuplot instance. This can be used to
19 - gp_new: a function which returns a new Gnuplot instance. This can be used to
20 have multiple Gnuplot instances running in your session to compare different
20 have multiple Gnuplot instances running in your session to compare different
21 plots.
21 plots.
22
22
23 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
23 - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for
24 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
24 the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its
25 functions with improved versions (Gnuplot2 comes with IPython).
25 functions with improved versions (Gnuplot2 comes with IPython).
26
26
27 - Data: alias to Gnuplot.Data, makes a PlotItem from array data.
27 - Data: alias to Gnuplot.Data, makes a PlotItem from array data.
28
28
29 - File: alias to Gnuplot.File, makes a PlotItem from a file.
29 - File: alias to Gnuplot.File, makes a PlotItem from a file.
30
30
31 - String: alias to Gnuplot.String, makes a PlotItem from a string formatted
31 - String: alias to Gnuplot.String, makes a PlotItem from a string formatted
32 exactly like a file for Gnuplot.File would be.
32 exactly like a file for Gnuplot.File would be.
33
33
34 - Func: alias to Gnuplot.Func, makes a PlotItem from a function string.
34 - Func: alias to Gnuplot.Func, makes a PlotItem from a function string.
35
35
36 - GridData: alias to Gnuplot.GridData, makes a PlotItem from grid data.
36 - GridData: alias to Gnuplot.GridData, makes a PlotItem from grid data.
37
37
38 - pm3d_config: a string with Gnuplot commands to set up the pm3d mode for
38 - pm3d_config: a string with Gnuplot commands to set up the pm3d mode for
39 surface plotting. You can activate it simply by calling gp(pm3d_config).
39 surface plotting. You can activate it simply by calling gp(pm3d_config).
40
40
41 - eps_fix_bbox: A Unix-only function to fix eps files with bad bounding boxes
41 - eps_fix_bbox: A Unix-only function to fix eps files with bad bounding boxes
42 (which Gnuplot generates when the plot size is set to square).
42 (which Gnuplot generates when the plot size is set to square).
43
43
44 This requires the Gnuplot.py module for interfacing Python with Gnuplot, which
44 This requires the Gnuplot.py module for interfacing Python with Gnuplot, which
45 can be downloaded from:
45 can be downloaded from:
46
46
47 http://gnuplot-py.sourceforge.net/
47 http://gnuplot-py.sourceforge.net/
48
48
49 Inspired by a suggestion/request from Arnd Baecker.
49 Inspired by a suggestion/request from Arnd Baecker.
50
50 """
51 $Id: GnuplotRuntime.py 389 2004-10-09 07:59:30Z fperez $"""
52
51
53 __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData',
52 __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData',
54 'pm3d_config','eps_fix_bbox']
53 'pm3d_config','eps_fix_bbox']
55
54
56 import os,tempfile,sys
55 import os,tempfile,sys
57 from IPython.genutils import getoutput
56 from IPython.genutils import getoutput
58
57
59 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
60 # Notes on mouse support for Gnuplot.py
59 # Notes on mouse support for Gnuplot.py
61
60
62 # If you do not have a mouse-enabled gnuplot, set gnuplot_mouse to 0. If you
61 # If you do not have a mouse-enabled gnuplot, set gnuplot_mouse to 0. If you
63 # use gnuplot, you should really grab a recent, mouse enabled copy. It is an
62 # use gnuplot, you should really grab a recent, mouse enabled copy. It is an
64 # extremely useful feature. Mouse support is official as of gnuplot 4.0,
63 # extremely useful feature. Mouse support is official as of gnuplot 4.0,
65 # released in April 2004.
64 # released in April 2004.
66
65
67 # For the mouse features to work correctly, you MUST set your Gnuplot.py
66 # For the mouse features to work correctly, you MUST set your Gnuplot.py
68 # module to use temporary files instead of 'inline data' for data
67 # module to use temporary files instead of 'inline data' for data
69 # communication. Note that this is the default, so unless you've manually
68 # communication. Note that this is the default, so unless you've manually
70 # fiddled with it you should be ok. If you need to make changes, in the
69 # fiddled with it you should be ok. If you need to make changes, in the
71 # Gnuplot module directory, loook for the gp_unix.py file and make sure the
70 # Gnuplot module directory, loook for the gp_unix.py file and make sure the
72 # prefer_inline_data variable is set to 0. If you set it to 1 Gnuplot.py will
71 # prefer_inline_data variable is set to 0. If you set it to 1 Gnuplot.py will
73 # try to pass the data to gnuplot via standard input, which completely
72 # try to pass the data to gnuplot via standard input, which completely
74 # confuses the mouse control system (even though it may be a bit faster than
73 # confuses the mouse control system (even though it may be a bit faster than
75 # using temp files).
74 # using temp files).
76
75
77 # As of Gnuplot.py v1.7, a new option was added to use FIFOs (pipes). This
76 # As of Gnuplot.py v1.7, a new option was added to use FIFOs (pipes). This
78 # mechanism, while fast, also breaks the mouse system. You must therefore set
77 # mechanism, while fast, also breaks the mouse system. You must therefore set
79 # the variable prefer_fifo_data to 0 in gp_unix.py.
78 # the variable prefer_fifo_data to 0 in gp_unix.py.
80
79
81 tmpname = tempfile.mktemp()
80 tmpname = tempfile.mktemp()
82 open(tmpname,'w').write('set mouse')
81 open(tmpname,'w').write('set mouse')
83 gnu_out = getoutput('gnuplot '+ tmpname)
82 gnu_out = getoutput('gnuplot '+ tmpname)
84 os.unlink(tmpname)
83 os.unlink(tmpname)
85 if gnu_out: # Gnuplot won't print anything if it has mouse support
84 if gnu_out: # Gnuplot won't print anything if it has mouse support
86 print "*** Your version of Gnuplot appears not to have mouse support."
85 print "*** Your version of Gnuplot appears not to have mouse support."
87 gnuplot_mouse = 0
86 gnuplot_mouse = 0
88 else:
87 else:
89 gnuplot_mouse = 1
88 gnuplot_mouse = 1
90 del tmpname,gnu_out
89 del tmpname,gnu_out
91
90
92 # Default state for persistence of new gnuplot instances
91 # Default state for persistence of new gnuplot instances
93 if os.name in ['nt','dos'] or sys.platform == 'cygwin':
92 if os.name in ['nt','dos'] or sys.platform == 'cygwin':
94 gnuplot_persist = 0
93 gnuplot_persist = 0
95 else:
94 else:
96 gnuplot_persist = 1
95 gnuplot_persist = 1
97
96
98 import IPython.Gnuplot2 as Gnuplot
97 import IPython.Gnuplot2 as Gnuplot
99
98
100 class NotGiven: pass
99 class NotGiven: pass
101
100
102 def gp_new(mouse=NotGiven,persist=NotGiven):
101 def gp_new(mouse=NotGiven,persist=NotGiven):
103 """Return a new Gnuplot instance.
102 """Return a new Gnuplot instance.
104
103
105 The instance returned uses the improved methods defined in Gnuplot2.
104 The instance returned uses the improved methods defined in Gnuplot2.
106
105
107 Options (boolean):
106 Options (boolean):
108
107
109 - mouse: if unspecified, the module global gnuplot_mouse is used.
108 - mouse: if unspecified, the module global gnuplot_mouse is used.
110
109
111 - persist: if unspecified, the module global gnuplot_persist is used."""
110 - persist: if unspecified, the module global gnuplot_persist is used."""
112
111
113 if mouse is NotGiven:
112 if mouse is NotGiven:
114 mouse = gnuplot_mouse
113 mouse = gnuplot_mouse
115 if persist is NotGiven:
114 if persist is NotGiven:
116 persist = gnuplot_persist
115 persist = gnuplot_persist
117 g = Gnuplot.Gnuplot(persist=persist)
116 g = Gnuplot.Gnuplot(persist=persist)
118 if mouse:
117 if mouse:
119 g('set mouse')
118 g('set mouse')
120 return g
119 return g
121
120
122 # Global-level names.
121 # Global-level names.
123
122
124 # A global Gnuplot instance for interactive use:
123 # A global Gnuplot instance for interactive use:
125 gp = gp_new()
124 gp = gp_new()
126
125
127 # Accessors for the main plot object constructors:
126 # Accessors for the main plot object constructors:
128 Data = Gnuplot.Data
127 Data = Gnuplot.Data
129 File = Gnuplot.File
128 File = Gnuplot.File
130 Func = Gnuplot.Func
129 Func = Gnuplot.Func
131 String = Gnuplot.String
130 String = Gnuplot.String
132 GridData = Gnuplot.GridData
131 GridData = Gnuplot.GridData
133
132
134 # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot
133 # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot
135 # generates when the plot size is set to square):
134 # generates when the plot size is set to square):
136 eps_fix_bbox = Gnuplot.eps_fix_bbox
135 eps_fix_bbox = Gnuplot.eps_fix_bbox
137
136
138 # String for configuring pm3d. Simply call g(pm3d_config) to execute it. pm3d
137 # String for configuring pm3d. Simply call g(pm3d_config) to execute it. pm3d
139 # is a very nice mode for plotting colormaps on surfaces. Modify the defaults
138 # is a very nice mode for plotting colormaps on surfaces. Modify the defaults
140 # below to suit your taste.
139 # below to suit your taste.
141 pm3d_config = """
140 pm3d_config = """
142 set pm3d solid
141 set pm3d solid
143 set hidden3d
142 set hidden3d
144 unset surface
143 unset surface
145 set isosamples 50
144 set isosamples 50
146 """
145 """
147 #******************** End of file <GnuplotRuntime.py> ******************
146 #******************** End of file <GnuplotRuntime.py> ******************
@@ -1,292 +1,290 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
3
3
4 This module lets you quickly and conveniently interpolate values into
4 This module lets you quickly and conveniently interpolate values into
5 strings (in the flavour of Perl or Tcl, but with less extraneous
5 strings (in the flavour of Perl or Tcl, but with less extraneous
6 punctuation). You get a bit more power than in the other languages,
6 punctuation). You get a bit more power than in the other languages,
7 because this module allows subscripting, slicing, function calls,
7 because this module allows subscripting, slicing, function calls,
8 attribute lookup, or arbitrary expressions. Variables and expressions
8 attribute lookup, or arbitrary expressions. Variables and expressions
9 are evaluated in the namespace of the caller.
9 are evaluated in the namespace of the caller.
10
10
11 The itpl() function returns the result of interpolating a string, and
11 The itpl() function returns the result of interpolating a string, and
12 printpl() prints out an interpolated string. Here are some examples:
12 printpl() prints out an interpolated string. Here are some examples:
13
13
14 from Itpl import printpl
14 from Itpl import printpl
15 printpl("Here is a $string.")
15 printpl("Here is a $string.")
16 printpl("Here is a $module.member.")
16 printpl("Here is a $module.member.")
17 printpl("Here is an $object.member.")
17 printpl("Here is an $object.member.")
18 printpl("Here is a $functioncall(with, arguments).")
18 printpl("Here is a $functioncall(with, arguments).")
19 printpl("Here is an ${arbitrary + expression}.")
19 printpl("Here is an ${arbitrary + expression}.")
20 printpl("Here is an $array[3] member.")
20 printpl("Here is an $array[3] member.")
21 printpl("Here is a $dictionary['member'].")
21 printpl("Here is a $dictionary['member'].")
22
22
23 The filter() function filters a file object so that output through it
23 The filter() function filters a file object so that output through it
24 is interpolated. This lets you produce the illusion that Python knows
24 is interpolated. This lets you produce the illusion that Python knows
25 how to do interpolation:
25 how to do interpolation:
26
26
27 import Itpl
27 import Itpl
28 sys.stdout = Itpl.filter()
28 sys.stdout = Itpl.filter()
29 f = "fancy"
29 f = "fancy"
30 print "Isn't this $f?"
30 print "Is this not $f?"
31 print "Standard output has been replaced with a $sys.stdout object."
31 print "Standard output has been replaced with a $sys.stdout object."
32 sys.stdout = Itpl.unfilter()
32 sys.stdout = Itpl.unfilter()
33 print "Okay, back $to $normal."
33 print "Okay, back $to $normal."
34
34
35 Under the hood, the Itpl class represents a string that knows how to
35 Under the hood, the Itpl class represents a string that knows how to
36 interpolate values. An instance of the class parses the string once
36 interpolate values. An instance of the class parses the string once
37 upon initialization; the evaluation and substitution can then be done
37 upon initialization; the evaluation and substitution can then be done
38 each time the instance is evaluated with str(instance). For example:
38 each time the instance is evaluated with str(instance). For example:
39
39
40 from Itpl import Itpl
40 from Itpl import Itpl
41 s = Itpl("Here is $foo.")
41 s = Itpl("Here is $foo.")
42 foo = 5
42 foo = 5
43 print str(s)
43 print str(s)
44 foo = "bar"
44 foo = "bar"
45 print str(s)
45 print str(s)
46
46 """
47 $Id: Itpl.py 2918 2007-12-31 14:34:47Z vivainio $
48 """ # ' -> close an open quote for stupid emacs
49
47
50 #*****************************************************************************
48 #*****************************************************************************
51 #
49 #
52 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
50 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
53 #
51 #
54 #
52 #
55 # Published under the terms of the MIT license, hereby reproduced:
53 # Published under the terms of the MIT license, hereby reproduced:
56 #
54 #
57 # Permission is hereby granted, free of charge, to any person obtaining a copy
55 # Permission is hereby granted, free of charge, to any person obtaining a copy
58 # of this software and associated documentation files (the "Software"), to
56 # of this software and associated documentation files (the "Software"), to
59 # deal in the Software without restriction, including without limitation the
57 # deal in the Software without restriction, including without limitation the
60 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
58 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
61 # sell copies of the Software, and to permit persons to whom the Software is
59 # sell copies of the Software, and to permit persons to whom the Software is
62 # furnished to do so, subject to the following conditions:
60 # furnished to do so, subject to the following conditions:
63 #
61 #
64 # The above copyright notice and this permission notice shall be included in
62 # The above copyright notice and this permission notice shall be included in
65 # all copies or substantial portions of the Software.
63 # all copies or substantial portions of the Software.
66 #
64 #
67 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
69 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
72 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
70 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
73 # IN THE SOFTWARE.
71 # IN THE SOFTWARE.
74 #
72 #
75 #*****************************************************************************
73 #*****************************************************************************
76
74
77 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
75 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
78 __license__ = 'MIT'
76 __license__ = 'MIT'
79
77
80 import string
78 import string
81 import sys
79 import sys
82 from tokenize import tokenprog
80 from tokenize import tokenprog
83 from types import StringType
81 from types import StringType
84
82
85 class ItplError(ValueError):
83 class ItplError(ValueError):
86 def __init__(self, text, pos):
84 def __init__(self, text, pos):
87 self.text = text
85 self.text = text
88 self.pos = pos
86 self.pos = pos
89 def __str__(self):
87 def __str__(self):
90 return "unfinished expression in %s at char %d" % (
88 return "unfinished expression in %s at char %d" % (
91 repr(self.text), self.pos)
89 repr(self.text), self.pos)
92
90
93 def matchorfail(text, pos):
91 def matchorfail(text, pos):
94 match = tokenprog.match(text, pos)
92 match = tokenprog.match(text, pos)
95 if match is None:
93 if match is None:
96 raise ItplError(text, pos)
94 raise ItplError(text, pos)
97
95
98 return match, match.end()
96 return match, match.end()
99
97
100 try:
98 try:
101 itpl_encoding = sys.stdin.encoding or 'ascii'
99 itpl_encoding = sys.stdin.encoding or 'ascii'
102 except AttributeError:
100 except AttributeError:
103 itpl_encoding = 'ascii'
101 itpl_encoding = 'ascii'
104
102
105
103
106
104
107 class Itpl:
105 class Itpl:
108 """Class representing a string with interpolation abilities.
106 """Class representing a string with interpolation abilities.
109
107
110 Upon creation, an instance works out what parts of the format
108 Upon creation, an instance works out what parts of the format
111 string are literal and what parts need to be evaluated. The
109 string are literal and what parts need to be evaluated. The
112 evaluation and substitution happens in the namespace of the
110 evaluation and substitution happens in the namespace of the
113 caller when str(instance) is called."""
111 caller when str(instance) is called."""
114
112
115 def __init__(self, format,codec=itpl_encoding,encoding_errors='backslashreplace'):
113 def __init__(self, format,codec=itpl_encoding,encoding_errors='backslashreplace'):
116 """The single mandatory argument to this constructor is a format
114 """The single mandatory argument to this constructor is a format
117 string.
115 string.
118
116
119 The format string is parsed according to the following rules:
117 The format string is parsed according to the following rules:
120
118
121 1. A dollar sign and a name, possibly followed by any of:
119 1. A dollar sign and a name, possibly followed by any of:
122 - an open-paren, and anything up to the matching paren
120 - an open-paren, and anything up to the matching paren
123 - an open-bracket, and anything up to the matching bracket
121 - an open-bracket, and anything up to the matching bracket
124 - a period and a name
122 - a period and a name
125 any number of times, is evaluated as a Python expression.
123 any number of times, is evaluated as a Python expression.
126
124
127 2. A dollar sign immediately followed by an open-brace, and
125 2. A dollar sign immediately followed by an open-brace, and
128 anything up to the matching close-brace, is evaluated as
126 anything up to the matching close-brace, is evaluated as
129 a Python expression.
127 a Python expression.
130
128
131 3. Outside of the expressions described in the above two rules,
129 3. Outside of the expressions described in the above two rules,
132 two dollar signs in a row give you one literal dollar sign.
130 two dollar signs in a row give you one literal dollar sign.
133
131
134 Optional arguments:
132 Optional arguments:
135
133
136 - codec('utf_8'): a string containing the name of a valid Python
134 - codec('utf_8'): a string containing the name of a valid Python
137 codec.
135 codec.
138
136
139 - encoding_errors('backslashreplace'): a string with a valid error handling
137 - encoding_errors('backslashreplace'): a string with a valid error handling
140 policy. See the codecs module documentation for details.
138 policy. See the codecs module documentation for details.
141
139
142 These are used to encode the format string if a call to str() fails on
140 These are used to encode the format string if a call to str() fails on
143 the expanded result."""
141 the expanded result."""
144
142
145 if not isinstance(format,basestring):
143 if not isinstance(format,basestring):
146 raise TypeError, "needs string initializer"
144 raise TypeError, "needs string initializer"
147 self.format = format
145 self.format = format
148 self.codec = codec
146 self.codec = codec
149 self.encoding_errors = encoding_errors
147 self.encoding_errors = encoding_errors
150
148
151 namechars = "abcdefghijklmnopqrstuvwxyz" \
149 namechars = "abcdefghijklmnopqrstuvwxyz" \
152 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
150 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
153 chunks = []
151 chunks = []
154 pos = 0
152 pos = 0
155
153
156 while 1:
154 while 1:
157 dollar = string.find(format, "$", pos)
155 dollar = string.find(format, "$", pos)
158 if dollar < 0: break
156 if dollar < 0: break
159 nextchar = format[dollar+1]
157 nextchar = format[dollar+1]
160
158
161 if nextchar == "{":
159 if nextchar == "{":
162 chunks.append((0, format[pos:dollar]))
160 chunks.append((0, format[pos:dollar]))
163 pos, level = dollar+2, 1
161 pos, level = dollar+2, 1
164 while level:
162 while level:
165 match, pos = matchorfail(format, pos)
163 match, pos = matchorfail(format, pos)
166 tstart, tend = match.regs[3]
164 tstart, tend = match.regs[3]
167 token = format[tstart:tend]
165 token = format[tstart:tend]
168 if token == "{": level = level+1
166 if token == "{": level = level+1
169 elif token == "}": level = level-1
167 elif token == "}": level = level-1
170 chunks.append((1, format[dollar+2:pos-1]))
168 chunks.append((1, format[dollar+2:pos-1]))
171
169
172 elif nextchar in namechars:
170 elif nextchar in namechars:
173 chunks.append((0, format[pos:dollar]))
171 chunks.append((0, format[pos:dollar]))
174 match, pos = matchorfail(format, dollar+1)
172 match, pos = matchorfail(format, dollar+1)
175 while pos < len(format):
173 while pos < len(format):
176 if format[pos] == "." and \
174 if format[pos] == "." and \
177 pos+1 < len(format) and format[pos+1] in namechars:
175 pos+1 < len(format) and format[pos+1] in namechars:
178 match, pos = matchorfail(format, pos+1)
176 match, pos = matchorfail(format, pos+1)
179 elif format[pos] in "([":
177 elif format[pos] in "([":
180 pos, level = pos+1, 1
178 pos, level = pos+1, 1
181 while level:
179 while level:
182 match, pos = matchorfail(format, pos)
180 match, pos = matchorfail(format, pos)
183 tstart, tend = match.regs[3]
181 tstart, tend = match.regs[3]
184 token = format[tstart:tend]
182 token = format[tstart:tend]
185 if token[0] in "([": level = level+1
183 if token[0] in "([": level = level+1
186 elif token[0] in ")]": level = level-1
184 elif token[0] in ")]": level = level-1
187 else: break
185 else: break
188 chunks.append((1, format[dollar+1:pos]))
186 chunks.append((1, format[dollar+1:pos]))
189
187
190 else:
188 else:
191 chunks.append((0, format[pos:dollar+1]))
189 chunks.append((0, format[pos:dollar+1]))
192 pos = dollar + 1 + (nextchar == "$")
190 pos = dollar + 1 + (nextchar == "$")
193
191
194 if pos < len(format): chunks.append((0, format[pos:]))
192 if pos < len(format): chunks.append((0, format[pos:]))
195 self.chunks = chunks
193 self.chunks = chunks
196
194
197 def __repr__(self):
195 def __repr__(self):
198 return "<Itpl %s >" % repr(self.format)
196 return "<Itpl %s >" % repr(self.format)
199
197
200 def _str(self,glob,loc):
198 def _str(self,glob,loc):
201 """Evaluate to a string in the given globals/locals.
199 """Evaluate to a string in the given globals/locals.
202
200
203 The final output is built by calling str(), but if this fails, the
201 The final output is built by calling str(), but if this fails, the
204 result is encoded with the instance's codec and error handling policy,
202 result is encoded with the instance's codec and error handling policy,
205 via a call to out.encode(self.codec,self.encoding_errors)"""
203 via a call to out.encode(self.codec,self.encoding_errors)"""
206 result = []
204 result = []
207 app = result.append
205 app = result.append
208 for live, chunk in self.chunks:
206 for live, chunk in self.chunks:
209 if live:
207 if live:
210 val = eval(chunk,glob,loc)
208 val = eval(chunk,glob,loc)
211 try:
209 try:
212 app(str(val))
210 app(str(val))
213 except UnicodeEncodeError:
211 except UnicodeEncodeError:
214 app(unicode(val))
212 app(unicode(val))
215
213
216 else: app(chunk)
214 else: app(chunk)
217 out = ''.join(result)
215 out = ''.join(result)
218 try:
216 try:
219 return str(out)
217 return str(out)
220 except UnicodeError:
218 except UnicodeError:
221 return out.encode(self.codec,self.encoding_errors)
219 return out.encode(self.codec,self.encoding_errors)
222
220
223 def __str__(self):
221 def __str__(self):
224 """Evaluate and substitute the appropriate parts of the string."""
222 """Evaluate and substitute the appropriate parts of the string."""
225
223
226 # We need to skip enough frames to get to the actual caller outside of
224 # We need to skip enough frames to get to the actual caller outside of
227 # Itpl.
225 # Itpl.
228 frame = sys._getframe(1)
226 frame = sys._getframe(1)
229 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
227 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
230 loc, glob = frame.f_locals, frame.f_globals
228 loc, glob = frame.f_locals, frame.f_globals
231
229
232 return self._str(glob,loc)
230 return self._str(glob,loc)
233
231
234 class ItplNS(Itpl):
232 class ItplNS(Itpl):
235 """Class representing a string with interpolation abilities.
233 """Class representing a string with interpolation abilities.
236
234
237 This inherits from Itpl, but at creation time a namespace is provided
235 This inherits from Itpl, but at creation time a namespace is provided
238 where the evaluation will occur. The interpolation becomes a bit more
236 where the evaluation will occur. The interpolation becomes a bit more
239 efficient, as no traceback needs to be extracte. It also allows the
237 efficient, as no traceback needs to be extracte. It also allows the
240 caller to supply a different namespace for the interpolation to occur than
238 caller to supply a different namespace for the interpolation to occur than
241 its own."""
239 its own."""
242
240
243 def __init__(self, format,globals,locals=None,
241 def __init__(self, format,globals,locals=None,
244 codec='utf_8',encoding_errors='backslashreplace'):
242 codec='utf_8',encoding_errors='backslashreplace'):
245 """ItplNS(format,globals[,locals]) -> interpolating string instance.
243 """ItplNS(format,globals[,locals]) -> interpolating string instance.
246
244
247 This constructor, besides a format string, takes a globals dictionary
245 This constructor, besides a format string, takes a globals dictionary
248 and optionally a locals (which defaults to globals if not provided).
246 and optionally a locals (which defaults to globals if not provided).
249
247
250 For further details, see the Itpl constructor."""
248 For further details, see the Itpl constructor."""
251
249
252 if locals is None:
250 if locals is None:
253 locals = globals
251 locals = globals
254 self.globals = globals
252 self.globals = globals
255 self.locals = locals
253 self.locals = locals
256 Itpl.__init__(self,format,codec,encoding_errors)
254 Itpl.__init__(self,format,codec,encoding_errors)
257
255
258 def __str__(self):
256 def __str__(self):
259 """Evaluate and substitute the appropriate parts of the string."""
257 """Evaluate and substitute the appropriate parts of the string."""
260 return self._str(self.globals,self.locals)
258 return self._str(self.globals,self.locals)
261
259
262 def __repr__(self):
260 def __repr__(self):
263 return "<ItplNS %s >" % repr(self.format)
261 return "<ItplNS %s >" % repr(self.format)
264
262
265 # utilities for fast printing
263 # utilities for fast printing
266 def itpl(text): return str(Itpl(text))
264 def itpl(text): return str(Itpl(text))
267 def printpl(text): print itpl(text)
265 def printpl(text): print itpl(text)
268 # versions with namespace
266 # versions with namespace
269 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
267 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
270 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
268 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
271
269
272 class ItplFile:
270 class ItplFile:
273 """A file object that filters each write() through an interpolator."""
271 """A file object that filters each write() through an interpolator."""
274 def __init__(self, file): self.file = file
272 def __init__(self, file): self.file = file
275 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
273 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
276 def __getattr__(self, attr): return getattr(self.file, attr)
274 def __getattr__(self, attr): return getattr(self.file, attr)
277 def write(self, text): self.file.write(str(Itpl(text)))
275 def write(self, text): self.file.write(str(Itpl(text)))
278
276
279 def filter(file=sys.stdout):
277 def filter(file=sys.stdout):
280 """Return an ItplFile that filters writes to the given file object.
278 """Return an ItplFile that filters writes to the given file object.
281
279
282 'file = filter(file)' replaces 'file' with a filtered object that
280 'file = filter(file)' replaces 'file' with a filtered object that
283 has a write() method. When called with no argument, this creates
281 has a write() method. When called with no argument, this creates
284 a filter to sys.stdout."""
282 a filter to sys.stdout."""
285 return ItplFile(file)
283 return ItplFile(file)
286
284
287 def unfilter(ifile=None):
285 def unfilter(ifile=None):
288 """Return the original file that corresponds to the given ItplFile.
286 """Return the original file that corresponds to the given ItplFile.
289
287
290 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
288 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
291 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
289 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
292 return ifile and ifile.file or sys.stdout.file
290 return ifile and ifile.file or sys.stdout.file
@@ -1,270 +1,263 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Logger class for IPython's logging facilities.
3 Logger class for IPython's logging facilities.
4
5 $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $
6 """
4 """
7
5
8 #*****************************************************************************
6 #*****************************************************************************
9 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
7 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 #
9 #
12 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
14 #*****************************************************************************
12 #*****************************************************************************
15
13
16 #****************************************************************************
14 #****************************************************************************
17 # Modules and globals
15 # Modules and globals
18
16
19 from IPython import Release
20 __author__ = '%s <%s>\n%s <%s>' % \
21 ( Release.authors['Janko'] + Release.authors['Fernando'] )
22 __license__ = Release.license
23
24 # Python standard modules
17 # Python standard modules
25 import glob
18 import glob
26 import os
19 import os
27 import time
20 import time
28
21
29 #****************************************************************************
22 #****************************************************************************
30 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
23 # FIXME: This class isn't a mixin anymore, but it still needs attributes from
31 # ipython and does input cache management. Finish cleanup later...
24 # ipython and does input cache management. Finish cleanup later...
32
25
33 class Logger(object):
26 class Logger(object):
34 """A Logfile class with different policies for file creation"""
27 """A Logfile class with different policies for file creation"""
35
28
36 def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
29 def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'):
37
30
38 self._i00,self._i,self._ii,self._iii = '','','',''
31 self._i00,self._i,self._ii,self._iii = '','','',''
39
32
40 # this is the full ipython instance, we need some attributes from it
33 # this is the full ipython instance, we need some attributes from it
41 # which won't exist until later. What a mess, clean up later...
34 # which won't exist until later. What a mess, clean up later...
42 self.shell = shell
35 self.shell = shell
43
36
44 self.logfname = logfname
37 self.logfname = logfname
45 self.loghead = loghead
38 self.loghead = loghead
46 self.logmode = logmode
39 self.logmode = logmode
47 self.logfile = None
40 self.logfile = None
48
41
49 # Whether to log raw or processed input
42 # Whether to log raw or processed input
50 self.log_raw_input = False
43 self.log_raw_input = False
51
44
52 # whether to also log output
45 # whether to also log output
53 self.log_output = False
46 self.log_output = False
54
47
55 # whether to put timestamps before each log entry
48 # whether to put timestamps before each log entry
56 self.timestamp = False
49 self.timestamp = False
57
50
58 # activity control flags
51 # activity control flags
59 self.log_active = False
52 self.log_active = False
60
53
61 # logmode is a validated property
54 # logmode is a validated property
62 def _set_mode(self,mode):
55 def _set_mode(self,mode):
63 if mode not in ['append','backup','global','over','rotate']:
56 if mode not in ['append','backup','global','over','rotate']:
64 raise ValueError,'invalid log mode %s given' % mode
57 raise ValueError,'invalid log mode %s given' % mode
65 self._logmode = mode
58 self._logmode = mode
66
59
67 def _get_mode(self):
60 def _get_mode(self):
68 return self._logmode
61 return self._logmode
69
62
70 logmode = property(_get_mode,_set_mode)
63 logmode = property(_get_mode,_set_mode)
71
64
72 def logstart(self,logfname=None,loghead=None,logmode=None,
65 def logstart(self,logfname=None,loghead=None,logmode=None,
73 log_output=False,timestamp=False,log_raw_input=False):
66 log_output=False,timestamp=False,log_raw_input=False):
74 """Generate a new log-file with a default header.
67 """Generate a new log-file with a default header.
75
68
76 Raises RuntimeError if the log has already been started"""
69 Raises RuntimeError if the log has already been started"""
77
70
78 if self.logfile is not None:
71 if self.logfile is not None:
79 raise RuntimeError('Log file is already active: %s' %
72 raise RuntimeError('Log file is already active: %s' %
80 self.logfname)
73 self.logfname)
81
74
82 self.log_active = True
75 self.log_active = True
83
76
84 # The parameters can override constructor defaults
77 # The parameters can override constructor defaults
85 if logfname is not None: self.logfname = logfname
78 if logfname is not None: self.logfname = logfname
86 if loghead is not None: self.loghead = loghead
79 if loghead is not None: self.loghead = loghead
87 if logmode is not None: self.logmode = logmode
80 if logmode is not None: self.logmode = logmode
88
81
89 # Parameters not part of the constructor
82 # Parameters not part of the constructor
90 self.timestamp = timestamp
83 self.timestamp = timestamp
91 self.log_output = log_output
84 self.log_output = log_output
92 self.log_raw_input = log_raw_input
85 self.log_raw_input = log_raw_input
93
86
94 # init depending on the log mode requested
87 # init depending on the log mode requested
95 isfile = os.path.isfile
88 isfile = os.path.isfile
96 logmode = self.logmode
89 logmode = self.logmode
97
90
98 if logmode == 'append':
91 if logmode == 'append':
99 self.logfile = open(self.logfname,'a')
92 self.logfile = open(self.logfname,'a')
100
93
101 elif logmode == 'backup':
94 elif logmode == 'backup':
102 if isfile(self.logfname):
95 if isfile(self.logfname):
103 backup_logname = self.logfname+'~'
96 backup_logname = self.logfname+'~'
104 # Manually remove any old backup, since os.rename may fail
97 # Manually remove any old backup, since os.rename may fail
105 # under Windows.
98 # under Windows.
106 if isfile(backup_logname):
99 if isfile(backup_logname):
107 os.remove(backup_logname)
100 os.remove(backup_logname)
108 os.rename(self.logfname,backup_logname)
101 os.rename(self.logfname,backup_logname)
109 self.logfile = open(self.logfname,'w')
102 self.logfile = open(self.logfname,'w')
110
103
111 elif logmode == 'global':
104 elif logmode == 'global':
112 self.logfname = os.path.join(self.shell.home_dir,self.logfname)
105 self.logfname = os.path.join(self.shell.home_dir,self.logfname)
113 self.logfile = open(self.logfname, 'a')
106 self.logfile = open(self.logfname, 'a')
114
107
115 elif logmode == 'over':
108 elif logmode == 'over':
116 if isfile(self.logfname):
109 if isfile(self.logfname):
117 os.remove(self.logfname)
110 os.remove(self.logfname)
118 self.logfile = open(self.logfname,'w')
111 self.logfile = open(self.logfname,'w')
119
112
120 elif logmode == 'rotate':
113 elif logmode == 'rotate':
121 if isfile(self.logfname):
114 if isfile(self.logfname):
122 if isfile(self.logfname+'.001~'):
115 if isfile(self.logfname+'.001~'):
123 old = glob.glob(self.logfname+'.*~')
116 old = glob.glob(self.logfname+'.*~')
124 old.sort()
117 old.sort()
125 old.reverse()
118 old.reverse()
126 for f in old:
119 for f in old:
127 root, ext = os.path.splitext(f)
120 root, ext = os.path.splitext(f)
128 num = int(ext[1:-1])+1
121 num = int(ext[1:-1])+1
129 os.rename(f, root+'.'+`num`.zfill(3)+'~')
122 os.rename(f, root+'.'+`num`.zfill(3)+'~')
130 os.rename(self.logfname, self.logfname+'.001~')
123 os.rename(self.logfname, self.logfname+'.001~')
131 self.logfile = open(self.logfname,'w')
124 self.logfile = open(self.logfname,'w')
132
125
133 if logmode != 'append':
126 if logmode != 'append':
134 self.logfile.write(self.loghead)
127 self.logfile.write(self.loghead)
135
128
136 self.logfile.flush()
129 self.logfile.flush()
137
130
138 def switch_log(self,val):
131 def switch_log(self,val):
139 """Switch logging on/off. val should be ONLY a boolean."""
132 """Switch logging on/off. val should be ONLY a boolean."""
140
133
141 if val not in [False,True,0,1]:
134 if val not in [False,True,0,1]:
142 raise ValueError, \
135 raise ValueError, \
143 'Call switch_log ONLY with a boolean argument, not with:',val
136 'Call switch_log ONLY with a boolean argument, not with:',val
144
137
145 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
138 label = {0:'OFF',1:'ON',False:'OFF',True:'ON'}
146
139
147 if self.logfile is None:
140 if self.logfile is None:
148 print """
141 print """
149 Logging hasn't been started yet (use logstart for that).
142 Logging hasn't been started yet (use logstart for that).
150
143
151 %logon/%logoff are for temporarily starting and stopping logging for a logfile
144 %logon/%logoff are for temporarily starting and stopping logging for a logfile
152 which already exists. But you must first start the logging process with
145 which already exists. But you must first start the logging process with
153 %logstart (optionally giving a logfile name)."""
146 %logstart (optionally giving a logfile name)."""
154
147
155 else:
148 else:
156 if self.log_active == val:
149 if self.log_active == val:
157 print 'Logging is already',label[val]
150 print 'Logging is already',label[val]
158 else:
151 else:
159 print 'Switching logging',label[val]
152 print 'Switching logging',label[val]
160 self.log_active = not self.log_active
153 self.log_active = not self.log_active
161 self.log_active_out = self.log_active
154 self.log_active_out = self.log_active
162
155
163 def logstate(self):
156 def logstate(self):
164 """Print a status message about the logger."""
157 """Print a status message about the logger."""
165 if self.logfile is None:
158 if self.logfile is None:
166 print 'Logging has not been activated.'
159 print 'Logging has not been activated.'
167 else:
160 else:
168 state = self.log_active and 'active' or 'temporarily suspended'
161 state = self.log_active and 'active' or 'temporarily suspended'
169 print 'Filename :',self.logfname
162 print 'Filename :',self.logfname
170 print 'Mode :',self.logmode
163 print 'Mode :',self.logmode
171 print 'Output logging :',self.log_output
164 print 'Output logging :',self.log_output
172 print 'Raw input log :',self.log_raw_input
165 print 'Raw input log :',self.log_raw_input
173 print 'Timestamping :',self.timestamp
166 print 'Timestamping :',self.timestamp
174 print 'State :',state
167 print 'State :',state
175
168
176 def log(self,line_ori,line_mod,continuation=None):
169 def log(self,line_ori,line_mod,continuation=None):
177 """Write the line to a log and create input cache variables _i*.
170 """Write the line to a log and create input cache variables _i*.
178
171
179 Inputs:
172 Inputs:
180
173
181 - line_ori: unmodified input line from the user. This is not
174 - line_ori: unmodified input line from the user. This is not
182 necessarily valid Python.
175 necessarily valid Python.
183
176
184 - line_mod: possibly modified input, such as the transformations made
177 - line_mod: possibly modified input, such as the transformations made
185 by input prefilters or input handlers of various kinds. This should
178 by input prefilters or input handlers of various kinds. This should
186 always be valid Python.
179 always be valid Python.
187
180
188 - continuation: if True, indicates this is part of multi-line input."""
181 - continuation: if True, indicates this is part of multi-line input."""
189
182
190 # update the auto _i tables
183 # update the auto _i tables
191 #print '***logging line',line_mod # dbg
184 #print '***logging line',line_mod # dbg
192 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
185 #print '***cache_count', self.shell.outputcache.prompt_count # dbg
193 try:
186 try:
194 input_hist = self.shell.user_ns['_ih']
187 input_hist = self.shell.user_ns['_ih']
195 except:
188 except:
196 #print 'userns:',self.shell.user_ns.keys() # dbg
189 #print 'userns:',self.shell.user_ns.keys() # dbg
197 return
190 return
198
191
199 out_cache = self.shell.outputcache
192 out_cache = self.shell.outputcache
200
193
201 # add blank lines if the input cache fell out of sync.
194 # add blank lines if the input cache fell out of sync.
202 if out_cache.do_full_cache and \
195 if out_cache.do_full_cache and \
203 out_cache.prompt_count +1 > len(input_hist):
196 out_cache.prompt_count +1 > len(input_hist):
204 input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
197 input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist)))
205
198
206 if not continuation and line_mod:
199 if not continuation and line_mod:
207 self._iii = self._ii
200 self._iii = self._ii
208 self._ii = self._i
201 self._ii = self._i
209 self._i = self._i00
202 self._i = self._i00
210 # put back the final \n of every input line
203 # put back the final \n of every input line
211 self._i00 = line_mod+'\n'
204 self._i00 = line_mod+'\n'
212 #print 'Logging input:<%s>' % line_mod # dbg
205 #print 'Logging input:<%s>' % line_mod # dbg
213 input_hist.append(self._i00)
206 input_hist.append(self._i00)
214 #print '---[%s]' % (len(input_hist)-1,) # dbg
207 #print '---[%s]' % (len(input_hist)-1,) # dbg
215
208
216 # hackish access to top-level namespace to create _i1,_i2... dynamically
209 # hackish access to top-level namespace to create _i1,_i2... dynamically
217 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
210 to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii}
218 if self.shell.outputcache.do_full_cache:
211 if self.shell.outputcache.do_full_cache:
219 in_num = self.shell.outputcache.prompt_count
212 in_num = self.shell.outputcache.prompt_count
220
213
221 # but if the opposite is true (a macro can produce multiple inputs
214 # but if the opposite is true (a macro can produce multiple inputs
222 # with no output display called), then bring the output counter in
215 # with no output display called), then bring the output counter in
223 # sync:
216 # sync:
224 last_num = len(input_hist)-1
217 last_num = len(input_hist)-1
225 if in_num != last_num:
218 if in_num != last_num:
226 in_num = self.shell.outputcache.prompt_count = last_num
219 in_num = self.shell.outputcache.prompt_count = last_num
227 new_i = '_i%s' % in_num
220 new_i = '_i%s' % in_num
228 if continuation:
221 if continuation:
229 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
222 self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod)
230 input_hist[in_num] = self._i00
223 input_hist[in_num] = self._i00
231 to_main[new_i] = self._i00
224 to_main[new_i] = self._i00
232 self.shell.user_ns.update(to_main)
225 self.shell.user_ns.update(to_main)
233
226
234 # Write the log line, but decide which one according to the
227 # Write the log line, but decide which one according to the
235 # log_raw_input flag, set when the log is started.
228 # log_raw_input flag, set when the log is started.
236 if self.log_raw_input:
229 if self.log_raw_input:
237 self.log_write(line_ori)
230 self.log_write(line_ori)
238 else:
231 else:
239 self.log_write(line_mod)
232 self.log_write(line_mod)
240
233
241 def log_write(self,data,kind='input'):
234 def log_write(self,data,kind='input'):
242 """Write data to the log file, if active"""
235 """Write data to the log file, if active"""
243
236
244 #print 'data: %r' % data # dbg
237 #print 'data: %r' % data # dbg
245 if self.log_active and data:
238 if self.log_active and data:
246 write = self.logfile.write
239 write = self.logfile.write
247 if kind=='input':
240 if kind=='input':
248 if self.timestamp:
241 if self.timestamp:
249 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
242 write(time.strftime('# %a, %d %b %Y %H:%M:%S\n',
250 time.localtime()))
243 time.localtime()))
251 write('%s\n' % data)
244 write('%s\n' % data)
252 elif kind=='output' and self.log_output:
245 elif kind=='output' and self.log_output:
253 odata = '\n'.join(['#[Out]# %s' % s
246 odata = '\n'.join(['#[Out]# %s' % s
254 for s in data.split('\n')])
247 for s in data.split('\n')])
255 write('%s\n' % odata)
248 write('%s\n' % odata)
256 self.logfile.flush()
249 self.logfile.flush()
257
250
258 def logstop(self):
251 def logstop(self):
259 """Fully stop logging and close log file.
252 """Fully stop logging and close log file.
260
253
261 In order to start logging again, a new logstart() call needs to be
254 In order to start logging again, a new logstart() call needs to be
262 made, possibly (though not necessarily) with a new filename, mode and
255 made, possibly (though not necessarily) with a new filename, mode and
263 other options."""
256 other options."""
264
257
265 self.logfile.close()
258 self.logfile.close()
266 self.logfile = None
259 self.logfile = None
267 self.log_active = False
260 self.log_active = False
268
261
269 # For backwards compatibility, in case anyone was using this.
262 # For backwards compatibility, in case anyone was using this.
270 close_log = logstop
263 close_log = logstop
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,613 +1,607 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for inspecting Python objects.
2 """Tools for inspecting Python objects.
3
3
4 Uses syntax highlighting for presenting the various information elements.
4 Uses syntax highlighting for presenting the various information elements.
5
5
6 Similar in spirit to the inspect module, but all calls take a name argument to
6 Similar in spirit to the inspect module, but all calls take a name argument to
7 reference the name under which an object is being read.
7 reference the name under which an object is being read.
8
9 $Id: OInspect.py 2843 2007-10-15 21:22:32Z fperez $
10 """
8 """
11
9
12 #*****************************************************************************
10 #*****************************************************************************
13 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
14 #
12 #
15 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
17 #*****************************************************************************
15 #*****************************************************************************
18
16
19 from IPython import Release
20 __author__ = '%s <%s>' % Release.authors['Fernando']
21 __license__ = Release.license
22
23 __all__ = ['Inspector','InspectColors']
17 __all__ = ['Inspector','InspectColors']
24
18
25 # stdlib modules
19 # stdlib modules
26 import __builtin__
20 import __builtin__
27 import StringIO
21 import StringIO
28 import inspect
22 import inspect
29 import linecache
23 import linecache
30 import os
24 import os
31 import string
25 import string
32 import sys
26 import sys
33 import types
27 import types
34
28
35 # IPython's own
29 # IPython's own
36 from IPython import PyColorize
30 from IPython import PyColorize
37 from IPython.genutils import page,indent,Term
31 from IPython.genutils import page,indent,Term
38 from IPython.Itpl import itpl
32 from IPython.Itpl import itpl
39 from IPython.wildcard import list_namespace
33 from IPython.wildcard import list_namespace
40 from IPython.ColorANSI import *
34 from IPython.ColorANSI import *
41
35
42 #****************************************************************************
36 #****************************************************************************
43 # HACK!!! This is a crude fix for bugs in python 2.3's inspect module. We
37 # HACK!!! This is a crude fix for bugs in python 2.3's inspect module. We
44 # simply monkeypatch inspect with code copied from python 2.4.
38 # simply monkeypatch inspect with code copied from python 2.4.
45 if sys.version_info[:2] == (2,3):
39 if sys.version_info[:2] == (2,3):
46 from inspect import ismodule, getabsfile, modulesbyfile
40 from inspect import ismodule, getabsfile, modulesbyfile
47 def getmodule(object):
41 def getmodule(object):
48 """Return the module an object was defined in, or None if not found."""
42 """Return the module an object was defined in, or None if not found."""
49 if ismodule(object):
43 if ismodule(object):
50 return object
44 return object
51 if hasattr(object, '__module__'):
45 if hasattr(object, '__module__'):
52 return sys.modules.get(object.__module__)
46 return sys.modules.get(object.__module__)
53 try:
47 try:
54 file = getabsfile(object)
48 file = getabsfile(object)
55 except TypeError:
49 except TypeError:
56 return None
50 return None
57 if file in modulesbyfile:
51 if file in modulesbyfile:
58 return sys.modules.get(modulesbyfile[file])
52 return sys.modules.get(modulesbyfile[file])
59 for module in sys.modules.values():
53 for module in sys.modules.values():
60 if hasattr(module, '__file__'):
54 if hasattr(module, '__file__'):
61 modulesbyfile[
55 modulesbyfile[
62 os.path.realpath(
56 os.path.realpath(
63 getabsfile(module))] = module.__name__
57 getabsfile(module))] = module.__name__
64 if file in modulesbyfile:
58 if file in modulesbyfile:
65 return sys.modules.get(modulesbyfile[file])
59 return sys.modules.get(modulesbyfile[file])
66 main = sys.modules['__main__']
60 main = sys.modules['__main__']
67 if not hasattr(object, '__name__'):
61 if not hasattr(object, '__name__'):
68 return None
62 return None
69 if hasattr(main, object.__name__):
63 if hasattr(main, object.__name__):
70 mainobject = getattr(main, object.__name__)
64 mainobject = getattr(main, object.__name__)
71 if mainobject is object:
65 if mainobject is object:
72 return main
66 return main
73 builtin = sys.modules['__builtin__']
67 builtin = sys.modules['__builtin__']
74 if hasattr(builtin, object.__name__):
68 if hasattr(builtin, object.__name__):
75 builtinobject = getattr(builtin, object.__name__)
69 builtinobject = getattr(builtin, object.__name__)
76 if builtinobject is object:
70 if builtinobject is object:
77 return builtin
71 return builtin
78
72
79 inspect.getmodule = getmodule
73 inspect.getmodule = getmodule
80
74
81 #****************************************************************************
75 #****************************************************************************
82 # Builtin color schemes
76 # Builtin color schemes
83
77
84 Colors = TermColors # just a shorthand
78 Colors = TermColors # just a shorthand
85
79
86 # Build a few color schemes
80 # Build a few color schemes
87 NoColor = ColorScheme(
81 NoColor = ColorScheme(
88 'NoColor',{
82 'NoColor',{
89 'header' : Colors.NoColor,
83 'header' : Colors.NoColor,
90 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
84 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
91 } )
85 } )
92
86
93 LinuxColors = ColorScheme(
87 LinuxColors = ColorScheme(
94 'Linux',{
88 'Linux',{
95 'header' : Colors.LightRed,
89 'header' : Colors.LightRed,
96 'normal' : Colors.Normal # color off (usu. Colors.Normal)
90 'normal' : Colors.Normal # color off (usu. Colors.Normal)
97 } )
91 } )
98
92
99 LightBGColors = ColorScheme(
93 LightBGColors = ColorScheme(
100 'LightBG',{
94 'LightBG',{
101 'header' : Colors.Red,
95 'header' : Colors.Red,
102 'normal' : Colors.Normal # color off (usu. Colors.Normal)
96 'normal' : Colors.Normal # color off (usu. Colors.Normal)
103 } )
97 } )
104
98
105 # Build table of color schemes (needed by the parser)
99 # Build table of color schemes (needed by the parser)
106 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
100 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
107 'Linux')
101 'Linux')
108
102
109 #****************************************************************************
103 #****************************************************************************
110 # Auxiliary functions
104 # Auxiliary functions
111 def getdoc(obj):
105 def getdoc(obj):
112 """Stable wrapper around inspect.getdoc.
106 """Stable wrapper around inspect.getdoc.
113
107
114 This can't crash because of attribute problems.
108 This can't crash because of attribute problems.
115
109
116 It also attempts to call a getdoc() method on the given object. This
110 It also attempts to call a getdoc() method on the given object. This
117 allows objects which provide their docstrings via non-standard mechanisms
111 allows objects which provide their docstrings via non-standard mechanisms
118 (like Pyro proxies) to still be inspected by ipython's ? system."""
112 (like Pyro proxies) to still be inspected by ipython's ? system."""
119
113
120 ds = None # default return value
114 ds = None # default return value
121 try:
115 try:
122 ds = inspect.getdoc(obj)
116 ds = inspect.getdoc(obj)
123 except:
117 except:
124 # Harden against an inspect failure, which can occur with
118 # Harden against an inspect failure, which can occur with
125 # SWIG-wrapped extensions.
119 # SWIG-wrapped extensions.
126 pass
120 pass
127 # Allow objects to offer customized documentation via a getdoc method:
121 # Allow objects to offer customized documentation via a getdoc method:
128 try:
122 try:
129 ds2 = obj.getdoc()
123 ds2 = obj.getdoc()
130 except:
124 except:
131 pass
125 pass
132 else:
126 else:
133 # if we get extra info, we add it to the normal docstring.
127 # if we get extra info, we add it to the normal docstring.
134 if ds is None:
128 if ds is None:
135 ds = ds2
129 ds = ds2
136 else:
130 else:
137 ds = '%s\n%s' % (ds,ds2)
131 ds = '%s\n%s' % (ds,ds2)
138 return ds
132 return ds
139
133
140
134
141 def getsource(obj,is_binary=False):
135 def getsource(obj,is_binary=False):
142 """Wrapper around inspect.getsource.
136 """Wrapper around inspect.getsource.
143
137
144 This can be modified by other projects to provide customized source
138 This can be modified by other projects to provide customized source
145 extraction.
139 extraction.
146
140
147 Inputs:
141 Inputs:
148
142
149 - obj: an object whose source code we will attempt to extract.
143 - obj: an object whose source code we will attempt to extract.
150
144
151 Optional inputs:
145 Optional inputs:
152
146
153 - is_binary: whether the object is known to come from a binary source.
147 - is_binary: whether the object is known to come from a binary source.
154 This implementation will skip returning any output for binary objects, but
148 This implementation will skip returning any output for binary objects, but
155 custom extractors may know how to meaningfully process them."""
149 custom extractors may know how to meaningfully process them."""
156
150
157 if is_binary:
151 if is_binary:
158 return None
152 return None
159 else:
153 else:
160 try:
154 try:
161 src = inspect.getsource(obj)
155 src = inspect.getsource(obj)
162 except TypeError:
156 except TypeError:
163 if hasattr(obj,'__class__'):
157 if hasattr(obj,'__class__'):
164 src = inspect.getsource(obj.__class__)
158 src = inspect.getsource(obj.__class__)
165 return src
159 return src
166
160
167 def getargspec(obj):
161 def getargspec(obj):
168 """Get the names and default values of a function's arguments.
162 """Get the names and default values of a function's arguments.
169
163
170 A tuple of four things is returned: (args, varargs, varkw, defaults).
164 A tuple of four things is returned: (args, varargs, varkw, defaults).
171 'args' is a list of the argument names (it may contain nested lists).
165 'args' is a list of the argument names (it may contain nested lists).
172 'varargs' and 'varkw' are the names of the * and ** arguments or None.
166 'varargs' and 'varkw' are the names of the * and ** arguments or None.
173 'defaults' is an n-tuple of the default values of the last n arguments.
167 'defaults' is an n-tuple of the default values of the last n arguments.
174
168
175 Modified version of inspect.getargspec from the Python Standard
169 Modified version of inspect.getargspec from the Python Standard
176 Library."""
170 Library."""
177
171
178 if inspect.isfunction(obj):
172 if inspect.isfunction(obj):
179 func_obj = obj
173 func_obj = obj
180 elif inspect.ismethod(obj):
174 elif inspect.ismethod(obj):
181 func_obj = obj.im_func
175 func_obj = obj.im_func
182 else:
176 else:
183 raise TypeError, 'arg is not a Python function'
177 raise TypeError, 'arg is not a Python function'
184 args, varargs, varkw = inspect.getargs(func_obj.func_code)
178 args, varargs, varkw = inspect.getargs(func_obj.func_code)
185 return args, varargs, varkw, func_obj.func_defaults
179 return args, varargs, varkw, func_obj.func_defaults
186
180
187 #****************************************************************************
181 #****************************************************************************
188 # Class definitions
182 # Class definitions
189
183
190 class myStringIO(StringIO.StringIO):
184 class myStringIO(StringIO.StringIO):
191 """Adds a writeln method to normal StringIO."""
185 """Adds a writeln method to normal StringIO."""
192 def writeln(self,*arg,**kw):
186 def writeln(self,*arg,**kw):
193 """Does a write() and then a write('\n')"""
187 """Does a write() and then a write('\n')"""
194 self.write(*arg,**kw)
188 self.write(*arg,**kw)
195 self.write('\n')
189 self.write('\n')
196
190
197
191
198 class Inspector:
192 class Inspector:
199 def __init__(self,color_table,code_color_table,scheme,
193 def __init__(self,color_table,code_color_table,scheme,
200 str_detail_level=0):
194 str_detail_level=0):
201 self.color_table = color_table
195 self.color_table = color_table
202 self.parser = PyColorize.Parser(code_color_table,out='str')
196 self.parser = PyColorize.Parser(code_color_table,out='str')
203 self.format = self.parser.format
197 self.format = self.parser.format
204 self.str_detail_level = str_detail_level
198 self.str_detail_level = str_detail_level
205 self.set_active_scheme(scheme)
199 self.set_active_scheme(scheme)
206
200
207 def __getdef(self,obj,oname=''):
201 def __getdef(self,obj,oname=''):
208 """Return the definition header for any callable object.
202 """Return the definition header for any callable object.
209
203
210 If any exception is generated, None is returned instead and the
204 If any exception is generated, None is returned instead and the
211 exception is suppressed."""
205 exception is suppressed."""
212
206
213 try:
207 try:
214 return oname + inspect.formatargspec(*getargspec(obj))
208 return oname + inspect.formatargspec(*getargspec(obj))
215 except:
209 except:
216 return None
210 return None
217
211
218 def __head(self,h):
212 def __head(self,h):
219 """Return a header string with proper colors."""
213 """Return a header string with proper colors."""
220 return '%s%s%s' % (self.color_table.active_colors.header,h,
214 return '%s%s%s' % (self.color_table.active_colors.header,h,
221 self.color_table.active_colors.normal)
215 self.color_table.active_colors.normal)
222
216
223 def set_active_scheme(self,scheme):
217 def set_active_scheme(self,scheme):
224 self.color_table.set_active_scheme(scheme)
218 self.color_table.set_active_scheme(scheme)
225 self.parser.color_table.set_active_scheme(scheme)
219 self.parser.color_table.set_active_scheme(scheme)
226
220
227 def noinfo(self,msg,oname):
221 def noinfo(self,msg,oname):
228 """Generic message when no information is found."""
222 """Generic message when no information is found."""
229 print 'No %s found' % msg,
223 print 'No %s found' % msg,
230 if oname:
224 if oname:
231 print 'for %s' % oname
225 print 'for %s' % oname
232 else:
226 else:
233 print
227 print
234
228
235 def pdef(self,obj,oname=''):
229 def pdef(self,obj,oname=''):
236 """Print the definition header for any callable object.
230 """Print the definition header for any callable object.
237
231
238 If the object is a class, print the constructor information."""
232 If the object is a class, print the constructor information."""
239
233
240 if not callable(obj):
234 if not callable(obj):
241 print 'Object is not callable.'
235 print 'Object is not callable.'
242 return
236 return
243
237
244 header = ''
238 header = ''
245
239
246 if inspect.isclass(obj):
240 if inspect.isclass(obj):
247 header = self.__head('Class constructor information:\n')
241 header = self.__head('Class constructor information:\n')
248 obj = obj.__init__
242 obj = obj.__init__
249 elif type(obj) is types.InstanceType:
243 elif type(obj) is types.InstanceType:
250 obj = obj.__call__
244 obj = obj.__call__
251
245
252 output = self.__getdef(obj,oname)
246 output = self.__getdef(obj,oname)
253 if output is None:
247 if output is None:
254 self.noinfo('definition header',oname)
248 self.noinfo('definition header',oname)
255 else:
249 else:
256 print >>Term.cout, header,self.format(output),
250 print >>Term.cout, header,self.format(output),
257
251
258 def pdoc(self,obj,oname='',formatter = None):
252 def pdoc(self,obj,oname='',formatter = None):
259 """Print the docstring for any object.
253 """Print the docstring for any object.
260
254
261 Optional:
255 Optional:
262 -formatter: a function to run the docstring through for specially
256 -formatter: a function to run the docstring through for specially
263 formatted docstrings."""
257 formatted docstrings."""
264
258
265 head = self.__head # so that itpl can find it even if private
259 head = self.__head # so that itpl can find it even if private
266 ds = getdoc(obj)
260 ds = getdoc(obj)
267 if formatter:
261 if formatter:
268 ds = formatter(ds)
262 ds = formatter(ds)
269 if inspect.isclass(obj):
263 if inspect.isclass(obj):
270 init_ds = getdoc(obj.__init__)
264 init_ds = getdoc(obj.__init__)
271 output = itpl('$head("Class Docstring:")\n'
265 output = itpl('$head("Class Docstring:")\n'
272 '$indent(ds)\n'
266 '$indent(ds)\n'
273 '$head("Constructor Docstring"):\n'
267 '$head("Constructor Docstring"):\n'
274 '$indent(init_ds)')
268 '$indent(init_ds)')
275 elif (type(obj) is types.InstanceType or isinstance(obj,object)) \
269 elif (type(obj) is types.InstanceType or isinstance(obj,object)) \
276 and hasattr(obj,'__call__'):
270 and hasattr(obj,'__call__'):
277 call_ds = getdoc(obj.__call__)
271 call_ds = getdoc(obj.__call__)
278 if call_ds:
272 if call_ds:
279 output = itpl('$head("Class Docstring:")\n$indent(ds)\n'
273 output = itpl('$head("Class Docstring:")\n$indent(ds)\n'
280 '$head("Calling Docstring:")\n$indent(call_ds)')
274 '$head("Calling Docstring:")\n$indent(call_ds)')
281 else:
275 else:
282 output = ds
276 output = ds
283 else:
277 else:
284 output = ds
278 output = ds
285 if output is None:
279 if output is None:
286 self.noinfo('documentation',oname)
280 self.noinfo('documentation',oname)
287 return
281 return
288 page(output)
282 page(output)
289
283
290 def psource(self,obj,oname=''):
284 def psource(self,obj,oname=''):
291 """Print the source code for an object."""
285 """Print the source code for an object."""
292
286
293 # Flush the source cache because inspect can return out-of-date source
287 # Flush the source cache because inspect can return out-of-date source
294 linecache.checkcache()
288 linecache.checkcache()
295 try:
289 try:
296 src = getsource(obj)
290 src = getsource(obj)
297 except:
291 except:
298 self.noinfo('source',oname)
292 self.noinfo('source',oname)
299 else:
293 else:
300 page(self.format(src))
294 page(self.format(src))
301
295
302 def pfile(self,obj,oname=''):
296 def pfile(self,obj,oname=''):
303 """Show the whole file where an object was defined."""
297 """Show the whole file where an object was defined."""
304
298
305 try:
299 try:
306 try:
300 try:
307 lineno = inspect.getsourcelines(obj)[1]
301 lineno = inspect.getsourcelines(obj)[1]
308 except TypeError:
302 except TypeError:
309 # For instances, try the class object like getsource() does
303 # For instances, try the class object like getsource() does
310 if hasattr(obj,'__class__'):
304 if hasattr(obj,'__class__'):
311 lineno = inspect.getsourcelines(obj.__class__)[1]
305 lineno = inspect.getsourcelines(obj.__class__)[1]
312 # Adjust the inspected object so getabsfile() below works
306 # Adjust the inspected object so getabsfile() below works
313 obj = obj.__class__
307 obj = obj.__class__
314 except:
308 except:
315 self.noinfo('file',oname)
309 self.noinfo('file',oname)
316 return
310 return
317
311
318 # We only reach this point if object was successfully queried
312 # We only reach this point if object was successfully queried
319
313
320 # run contents of file through pager starting at line
314 # run contents of file through pager starting at line
321 # where the object is defined
315 # where the object is defined
322 ofile = inspect.getabsfile(obj)
316 ofile = inspect.getabsfile(obj)
323
317
324 if (ofile.endswith('.so') or ofile.endswith('.dll')):
318 if (ofile.endswith('.so') or ofile.endswith('.dll')):
325 print 'File %r is binary, not printing.' % ofile
319 print 'File %r is binary, not printing.' % ofile
326 elif not os.path.isfile(ofile):
320 elif not os.path.isfile(ofile):
327 print 'File %r does not exist, not printing.' % ofile
321 print 'File %r does not exist, not printing.' % ofile
328 else:
322 else:
329 # Print only text files, not extension binaries. Note that
323 # Print only text files, not extension binaries. Note that
330 # getsourcelines returns lineno with 1-offset and page() uses
324 # getsourcelines returns lineno with 1-offset and page() uses
331 # 0-offset, so we must adjust.
325 # 0-offset, so we must adjust.
332 page(self.format(open(ofile).read()),lineno-1)
326 page(self.format(open(ofile).read()),lineno-1)
333
327
334 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
328 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
335 """Show detailed information about an object.
329 """Show detailed information about an object.
336
330
337 Optional arguments:
331 Optional arguments:
338
332
339 - oname: name of the variable pointing to the object.
333 - oname: name of the variable pointing to the object.
340
334
341 - formatter: special formatter for docstrings (see pdoc)
335 - formatter: special formatter for docstrings (see pdoc)
342
336
343 - info: a structure with some information fields which may have been
337 - info: a structure with some information fields which may have been
344 precomputed already.
338 precomputed already.
345
339
346 - detail_level: if set to 1, more information is given.
340 - detail_level: if set to 1, more information is given.
347 """
341 """
348
342
349 obj_type = type(obj)
343 obj_type = type(obj)
350
344
351 header = self.__head
345 header = self.__head
352 if info is None:
346 if info is None:
353 ismagic = 0
347 ismagic = 0
354 isalias = 0
348 isalias = 0
355 ospace = ''
349 ospace = ''
356 else:
350 else:
357 ismagic = info.ismagic
351 ismagic = info.ismagic
358 isalias = info.isalias
352 isalias = info.isalias
359 ospace = info.namespace
353 ospace = info.namespace
360 # Get docstring, special-casing aliases:
354 # Get docstring, special-casing aliases:
361 if isalias:
355 if isalias:
362 if not callable(obj):
356 if not callable(obj):
363 try:
357 try:
364 ds = "Alias to the system command:\n %s" % obj[1]
358 ds = "Alias to the system command:\n %s" % obj[1]
365 except:
359 except:
366 ds = "Alias: " + str(obj)
360 ds = "Alias: " + str(obj)
367 else:
361 else:
368 ds = "Alias to " + str(obj)
362 ds = "Alias to " + str(obj)
369 if obj.__doc__:
363 if obj.__doc__:
370 ds += "\nDocstring:\n" + obj.__doc__
364 ds += "\nDocstring:\n" + obj.__doc__
371 else:
365 else:
372 ds = getdoc(obj)
366 ds = getdoc(obj)
373 if ds is None:
367 if ds is None:
374 ds = '<no docstring>'
368 ds = '<no docstring>'
375 if formatter is not None:
369 if formatter is not None:
376 ds = formatter(ds)
370 ds = formatter(ds)
377
371
378 # store output in a list which gets joined with \n at the end.
372 # store output in a list which gets joined with \n at the end.
379 out = myStringIO()
373 out = myStringIO()
380
374
381 string_max = 200 # max size of strings to show (snipped if longer)
375 string_max = 200 # max size of strings to show (snipped if longer)
382 shalf = int((string_max -5)/2)
376 shalf = int((string_max -5)/2)
383
377
384 if ismagic:
378 if ismagic:
385 obj_type_name = 'Magic function'
379 obj_type_name = 'Magic function'
386 elif isalias:
380 elif isalias:
387 obj_type_name = 'System alias'
381 obj_type_name = 'System alias'
388 else:
382 else:
389 obj_type_name = obj_type.__name__
383 obj_type_name = obj_type.__name__
390 out.writeln(header('Type:\t\t')+obj_type_name)
384 out.writeln(header('Type:\t\t')+obj_type_name)
391
385
392 try:
386 try:
393 bclass = obj.__class__
387 bclass = obj.__class__
394 out.writeln(header('Base Class:\t')+str(bclass))
388 out.writeln(header('Base Class:\t')+str(bclass))
395 except: pass
389 except: pass
396
390
397 # String form, but snip if too long in ? form (full in ??)
391 # String form, but snip if too long in ? form (full in ??)
398 if detail_level >= self.str_detail_level:
392 if detail_level >= self.str_detail_level:
399 try:
393 try:
400 ostr = str(obj)
394 ostr = str(obj)
401 str_head = 'String Form:'
395 str_head = 'String Form:'
402 if not detail_level and len(ostr)>string_max:
396 if not detail_level and len(ostr)>string_max:
403 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
397 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
404 ostr = ("\n" + " " * len(str_head.expandtabs())).\
398 ostr = ("\n" + " " * len(str_head.expandtabs())).\
405 join(map(string.strip,ostr.split("\n")))
399 join(map(string.strip,ostr.split("\n")))
406 if ostr.find('\n') > -1:
400 if ostr.find('\n') > -1:
407 # Print multi-line strings starting at the next line.
401 # Print multi-line strings starting at the next line.
408 str_sep = '\n'
402 str_sep = '\n'
409 else:
403 else:
410 str_sep = '\t'
404 str_sep = '\t'
411 out.writeln("%s%s%s" % (header(str_head),str_sep,ostr))
405 out.writeln("%s%s%s" % (header(str_head),str_sep,ostr))
412 except:
406 except:
413 pass
407 pass
414
408
415 if ospace:
409 if ospace:
416 out.writeln(header('Namespace:\t')+ospace)
410 out.writeln(header('Namespace:\t')+ospace)
417
411
418 # Length (for strings and lists)
412 # Length (for strings and lists)
419 try:
413 try:
420 length = str(len(obj))
414 length = str(len(obj))
421 out.writeln(header('Length:\t\t')+length)
415 out.writeln(header('Length:\t\t')+length)
422 except: pass
416 except: pass
423
417
424 # Filename where object was defined
418 # Filename where object was defined
425 binary_file = False
419 binary_file = False
426 try:
420 try:
427 try:
421 try:
428 fname = inspect.getabsfile(obj)
422 fname = inspect.getabsfile(obj)
429 except TypeError:
423 except TypeError:
430 # For an instance, the file that matters is where its class was
424 # For an instance, the file that matters is where its class was
431 # declared.
425 # declared.
432 if hasattr(obj,'__class__'):
426 if hasattr(obj,'__class__'):
433 fname = inspect.getabsfile(obj.__class__)
427 fname = inspect.getabsfile(obj.__class__)
434 if fname.endswith('<string>'):
428 if fname.endswith('<string>'):
435 fname = 'Dynamically generated function. No source code available.'
429 fname = 'Dynamically generated function. No source code available.'
436 if (fname.endswith('.so') or fname.endswith('.dll')):
430 if (fname.endswith('.so') or fname.endswith('.dll')):
437 binary_file = True
431 binary_file = True
438 out.writeln(header('File:\t\t')+fname)
432 out.writeln(header('File:\t\t')+fname)
439 except:
433 except:
440 # if anything goes wrong, we don't want to show source, so it's as
434 # if anything goes wrong, we don't want to show source, so it's as
441 # if the file was binary
435 # if the file was binary
442 binary_file = True
436 binary_file = True
443
437
444 # reconstruct the function definition and print it:
438 # reconstruct the function definition and print it:
445 defln = self.__getdef(obj,oname)
439 defln = self.__getdef(obj,oname)
446 if defln:
440 if defln:
447 out.write(header('Definition:\t')+self.format(defln))
441 out.write(header('Definition:\t')+self.format(defln))
448
442
449 # Docstrings only in detail 0 mode, since source contains them (we
443 # Docstrings only in detail 0 mode, since source contains them (we
450 # avoid repetitions). If source fails, we add them back, see below.
444 # avoid repetitions). If source fails, we add them back, see below.
451 if ds and detail_level == 0:
445 if ds and detail_level == 0:
452 out.writeln(header('Docstring:\n') + indent(ds))
446 out.writeln(header('Docstring:\n') + indent(ds))
453
447
454 # Original source code for any callable
448 # Original source code for any callable
455 if detail_level:
449 if detail_level:
456 # Flush the source cache because inspect can return out-of-date
450 # Flush the source cache because inspect can return out-of-date
457 # source
451 # source
458 linecache.checkcache()
452 linecache.checkcache()
459 source_success = False
453 source_success = False
460 try:
454 try:
461 try:
455 try:
462 src = getsource(obj,binary_file)
456 src = getsource(obj,binary_file)
463 except TypeError:
457 except TypeError:
464 if hasattr(obj,'__class__'):
458 if hasattr(obj,'__class__'):
465 src = getsource(obj.__class__,binary_file)
459 src = getsource(obj.__class__,binary_file)
466 if src is not None:
460 if src is not None:
467 source = self.format(src)
461 source = self.format(src)
468 out.write(header('Source:\n')+source.rstrip())
462 out.write(header('Source:\n')+source.rstrip())
469 source_success = True
463 source_success = True
470 except Exception, msg:
464 except Exception, msg:
471 pass
465 pass
472
466
473 if ds and not source_success:
467 if ds and not source_success:
474 out.writeln(header('Docstring [source file open failed]:\n')
468 out.writeln(header('Docstring [source file open failed]:\n')
475 + indent(ds))
469 + indent(ds))
476
470
477 # Constructor docstring for classes
471 # Constructor docstring for classes
478 if inspect.isclass(obj):
472 if inspect.isclass(obj):
479 # reconstruct the function definition and print it:
473 # reconstruct the function definition and print it:
480 try:
474 try:
481 obj_init = obj.__init__
475 obj_init = obj.__init__
482 except AttributeError:
476 except AttributeError:
483 init_def = init_ds = None
477 init_def = init_ds = None
484 else:
478 else:
485 init_def = self.__getdef(obj_init,oname)
479 init_def = self.__getdef(obj_init,oname)
486 init_ds = getdoc(obj_init)
480 init_ds = getdoc(obj_init)
487 # Skip Python's auto-generated docstrings
481 # Skip Python's auto-generated docstrings
488 if init_ds and \
482 if init_ds and \
489 init_ds.startswith('x.__init__(...) initializes'):
483 init_ds.startswith('x.__init__(...) initializes'):
490 init_ds = None
484 init_ds = None
491
485
492 if init_def or init_ds:
486 if init_def or init_ds:
493 out.writeln(header('\nConstructor information:'))
487 out.writeln(header('\nConstructor information:'))
494 if init_def:
488 if init_def:
495 out.write(header('Definition:\t')+ self.format(init_def))
489 out.write(header('Definition:\t')+ self.format(init_def))
496 if init_ds:
490 if init_ds:
497 out.writeln(header('Docstring:\n') + indent(init_ds))
491 out.writeln(header('Docstring:\n') + indent(init_ds))
498 # and class docstring for instances:
492 # and class docstring for instances:
499 elif obj_type is types.InstanceType or \
493 elif obj_type is types.InstanceType or \
500 isinstance(obj,object):
494 isinstance(obj,object):
501
495
502 # First, check whether the instance docstring is identical to the
496 # First, check whether the instance docstring is identical to the
503 # class one, and print it separately if they don't coincide. In
497 # class one, and print it separately if they don't coincide. In
504 # most cases they will, but it's nice to print all the info for
498 # most cases they will, but it's nice to print all the info for
505 # objects which use instance-customized docstrings.
499 # objects which use instance-customized docstrings.
506 if ds:
500 if ds:
507 try:
501 try:
508 cls = getattr(obj,'__class__')
502 cls = getattr(obj,'__class__')
509 except:
503 except:
510 class_ds = None
504 class_ds = None
511 else:
505 else:
512 class_ds = getdoc(cls)
506 class_ds = getdoc(cls)
513 # Skip Python's auto-generated docstrings
507 # Skip Python's auto-generated docstrings
514 if class_ds and \
508 if class_ds and \
515 (class_ds.startswith('function(code, globals[,') or \
509 (class_ds.startswith('function(code, globals[,') or \
516 class_ds.startswith('instancemethod(function, instance,') or \
510 class_ds.startswith('instancemethod(function, instance,') or \
517 class_ds.startswith('module(name[,') ):
511 class_ds.startswith('module(name[,') ):
518 class_ds = None
512 class_ds = None
519 if class_ds and ds != class_ds:
513 if class_ds and ds != class_ds:
520 out.writeln(header('Class Docstring:\n') +
514 out.writeln(header('Class Docstring:\n') +
521 indent(class_ds))
515 indent(class_ds))
522
516
523 # Next, try to show constructor docstrings
517 # Next, try to show constructor docstrings
524 try:
518 try:
525 init_ds = getdoc(obj.__init__)
519 init_ds = getdoc(obj.__init__)
526 # Skip Python's auto-generated docstrings
520 # Skip Python's auto-generated docstrings
527 if init_ds and \
521 if init_ds and \
528 init_ds.startswith('x.__init__(...) initializes'):
522 init_ds.startswith('x.__init__(...) initializes'):
529 init_ds = None
523 init_ds = None
530 except AttributeError:
524 except AttributeError:
531 init_ds = None
525 init_ds = None
532 if init_ds:
526 if init_ds:
533 out.writeln(header('Constructor Docstring:\n') +
527 out.writeln(header('Constructor Docstring:\n') +
534 indent(init_ds))
528 indent(init_ds))
535
529
536 # Call form docstring for callable instances
530 # Call form docstring for callable instances
537 if hasattr(obj,'__call__'):
531 if hasattr(obj,'__call__'):
538 #out.writeln(header('Callable:\t')+'Yes')
532 #out.writeln(header('Callable:\t')+'Yes')
539 call_def = self.__getdef(obj.__call__,oname)
533 call_def = self.__getdef(obj.__call__,oname)
540 #if call_def is None:
534 #if call_def is None:
541 # out.writeln(header('Call def:\t')+
535 # out.writeln(header('Call def:\t')+
542 # 'Calling definition not available.')
536 # 'Calling definition not available.')
543 if call_def is not None:
537 if call_def is not None:
544 out.writeln(header('Call def:\t')+self.format(call_def))
538 out.writeln(header('Call def:\t')+self.format(call_def))
545 call_ds = getdoc(obj.__call__)
539 call_ds = getdoc(obj.__call__)
546 # Skip Python's auto-generated docstrings
540 # Skip Python's auto-generated docstrings
547 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
541 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
548 call_ds = None
542 call_ds = None
549 if call_ds:
543 if call_ds:
550 out.writeln(header('Call docstring:\n') + indent(call_ds))
544 out.writeln(header('Call docstring:\n') + indent(call_ds))
551
545
552 # Finally send to printer/pager
546 # Finally send to printer/pager
553 output = out.getvalue()
547 output = out.getvalue()
554 if output:
548 if output:
555 page(output)
549 page(output)
556 # end pinfo
550 # end pinfo
557
551
558 def psearch(self,pattern,ns_table,ns_search=[],
552 def psearch(self,pattern,ns_table,ns_search=[],
559 ignore_case=False,show_all=False):
553 ignore_case=False,show_all=False):
560 """Search namespaces with wildcards for objects.
554 """Search namespaces with wildcards for objects.
561
555
562 Arguments:
556 Arguments:
563
557
564 - pattern: string containing shell-like wildcards to use in namespace
558 - pattern: string containing shell-like wildcards to use in namespace
565 searches and optionally a type specification to narrow the search to
559 searches and optionally a type specification to narrow the search to
566 objects of that type.
560 objects of that type.
567
561
568 - ns_table: dict of name->namespaces for search.
562 - ns_table: dict of name->namespaces for search.
569
563
570 Optional arguments:
564 Optional arguments:
571
565
572 - ns_search: list of namespace names to include in search.
566 - ns_search: list of namespace names to include in search.
573
567
574 - ignore_case(False): make the search case-insensitive.
568 - ignore_case(False): make the search case-insensitive.
575
569
576 - show_all(False): show all names, including those starting with
570 - show_all(False): show all names, including those starting with
577 underscores.
571 underscores.
578 """
572 """
579 #print 'ps pattern:<%r>' % pattern # dbg
573 #print 'ps pattern:<%r>' % pattern # dbg
580
574
581 # defaults
575 # defaults
582 type_pattern = 'all'
576 type_pattern = 'all'
583 filter = ''
577 filter = ''
584
578
585 cmds = pattern.split()
579 cmds = pattern.split()
586 len_cmds = len(cmds)
580 len_cmds = len(cmds)
587 if len_cmds == 1:
581 if len_cmds == 1:
588 # Only filter pattern given
582 # Only filter pattern given
589 filter = cmds[0]
583 filter = cmds[0]
590 elif len_cmds == 2:
584 elif len_cmds == 2:
591 # Both filter and type specified
585 # Both filter and type specified
592 filter,type_pattern = cmds
586 filter,type_pattern = cmds
593 else:
587 else:
594 raise ValueError('invalid argument string for psearch: <%s>' %
588 raise ValueError('invalid argument string for psearch: <%s>' %
595 pattern)
589 pattern)
596
590
597 # filter search namespaces
591 # filter search namespaces
598 for name in ns_search:
592 for name in ns_search:
599 if name not in ns_table:
593 if name not in ns_table:
600 raise ValueError('invalid namespace <%s>. Valid names: %s' %
594 raise ValueError('invalid namespace <%s>. Valid names: %s' %
601 (name,ns_table.keys()))
595 (name,ns_table.keys()))
602
596
603 #print 'type_pattern:',type_pattern # dbg
597 #print 'type_pattern:',type_pattern # dbg
604 search_result = []
598 search_result = []
605 for ns_name in ns_search:
599 for ns_name in ns_search:
606 ns = ns_table[ns_name]
600 ns = ns_table[ns_name]
607 tmp_res = list(list_namespace(ns,type_pattern,filter,
601 tmp_res = list(list_namespace(ns,type_pattern,filter,
608 ignore_case=ignore_case,
602 ignore_case=ignore_case,
609 show_all=show_all))
603 show_all=show_all))
610 search_result.extend(tmp_res)
604 search_result.extend(tmp_res)
611 search_result.sort()
605 search_result.sort()
612
606
613 page('\n'.join(search_result))
607 page('\n'.join(search_result))
@@ -1,263 +1,258 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Class to trap stdout and stderr and log them separately.
2 """Class to trap stdout and stderr and log them separately.
3
3 """
4 $Id: OutputTrap.py 958 2005-12-27 23:17:51Z fperez $"""
5
4
6 #*****************************************************************************
5 #*****************************************************************************
7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
6 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
8 #
7 #
9 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
10 #*****************************************************************************
12
11
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
16
17 import exceptions
12 import exceptions
18 import sys
13 import sys
19 from cStringIO import StringIO
14 from cStringIO import StringIO
20
15
21 class OutputTrapError(exceptions.Exception):
16 class OutputTrapError(exceptions.Exception):
22 """Exception for OutputTrap class."""
17 """Exception for OutputTrap class."""
23
18
24 def __init__(self,args=None):
19 def __init__(self,args=None):
25 exceptions.Exception.__init__(self)
20 exceptions.Exception.__init__(self)
26 self.args = args
21 self.args = args
27
22
28 class OutputTrap:
23 class OutputTrap:
29
24
30 """Class to trap standard output and standard error. They get logged in
25 """Class to trap standard output and standard error. They get logged in
31 StringIO objects which are available as <instance>.out and
26 StringIO objects which are available as <instance>.out and
32 <instance>.err. The class also offers summary methods which format this
27 <instance>.err. The class also offers summary methods which format this
33 data a bit.
28 data a bit.
34
29
35 A word of caution: because it blocks messages, using this class can make
30 A word of caution: because it blocks messages, using this class can make
36 debugging very tricky. If you are having bizarre problems silently, try
31 debugging very tricky. If you are having bizarre problems silently, try
37 turning your output traps off for a while. You can call the constructor
32 turning your output traps off for a while. You can call the constructor
38 with the parameter debug=1 for these cases. This turns actual trapping
33 with the parameter debug=1 for these cases. This turns actual trapping
39 off, but you can keep the rest of your code unchanged (this has already
34 off, but you can keep the rest of your code unchanged (this has already
40 been a life saver).
35 been a life saver).
41
36
42 Example:
37 Example:
43
38
44 # config: trapper with a line of dots as log separator (final '\\n' needed)
39 # config: trapper with a line of dots as log separator (final '\\n' needed)
45 config = OutputTrap('Config','Out ','Err ','.'*80+'\\n')
40 config = OutputTrap('Config','Out ','Err ','.'*80+'\\n')
46
41
47 # start trapping output
42 # start trapping output
48 config.trap_all()
43 config.trap_all()
49
44
50 # now all output is logged ...
45 # now all output is logged ...
51 # do stuff...
46 # do stuff...
52
47
53 # output back to normal:
48 # output back to normal:
54 config.release_all()
49 config.release_all()
55
50
56 # print all that got logged:
51 # print all that got logged:
57 print config.summary()
52 print config.summary()
58
53
59 # print individual raw data:
54 # print individual raw data:
60 print config.out.getvalue()
55 print config.out.getvalue()
61 print config.err.getvalue()
56 print config.err.getvalue()
62 """
57 """
63
58
64 def __init__(self,name='Generic Output Trap',
59 def __init__(self,name='Generic Output Trap',
65 out_head='Standard Output. ',err_head='Standard Error. ',
60 out_head='Standard Output. ',err_head='Standard Error. ',
66 sum_sep='\n',debug=0,trap_out=0,trap_err=0,
61 sum_sep='\n',debug=0,trap_out=0,trap_err=0,
67 quiet_out=0,quiet_err=0):
62 quiet_out=0,quiet_err=0):
68 self.name = name
63 self.name = name
69 self.out_head = out_head
64 self.out_head = out_head
70 self.err_head = err_head
65 self.err_head = err_head
71 self.sum_sep = sum_sep
66 self.sum_sep = sum_sep
72 self.out = StringIO()
67 self.out = StringIO()
73 self.err = StringIO()
68 self.err = StringIO()
74 self.out_save = None
69 self.out_save = None
75 self.err_save = None
70 self.err_save = None
76 self.debug = debug
71 self.debug = debug
77 self.quiet_out = quiet_out
72 self.quiet_out = quiet_out
78 self.quiet_err = quiet_err
73 self.quiet_err = quiet_err
79 if trap_out:
74 if trap_out:
80 self.trap_out()
75 self.trap_out()
81 if trap_err:
76 if trap_err:
82 self.trap_err()
77 self.trap_err()
83
78
84 def trap_out(self):
79 def trap_out(self):
85 """Trap and log stdout."""
80 """Trap and log stdout."""
86 if sys.stdout is self.out:
81 if sys.stdout is self.out:
87 raise OutputTrapError,'You are already trapping stdout.'
82 raise OutputTrapError,'You are already trapping stdout.'
88 if not self.debug:
83 if not self.debug:
89 self._out_save = sys.stdout
84 self._out_save = sys.stdout
90 sys.stdout = self.out
85 sys.stdout = self.out
91
86
92 def release_out(self):
87 def release_out(self):
93 """Release stdout."""
88 """Release stdout."""
94 if not self.debug:
89 if not self.debug:
95 if not sys.stdout is self.out:
90 if not sys.stdout is self.out:
96 raise OutputTrapError,'You are not trapping stdout.'
91 raise OutputTrapError,'You are not trapping stdout.'
97 sys.stdout = self._out_save
92 sys.stdout = self._out_save
98 self.out_save = None
93 self.out_save = None
99
94
100 def summary_out(self):
95 def summary_out(self):
101 """Return as a string the log from stdout."""
96 """Return as a string the log from stdout."""
102 out = self.out.getvalue()
97 out = self.out.getvalue()
103 if out:
98 if out:
104 if self.quiet_out:
99 if self.quiet_out:
105 return out
100 return out
106 else:
101 else:
107 return self.out_head + 'Log by '+ self.name + ':\n' + out
102 return self.out_head + 'Log by '+ self.name + ':\n' + out
108 else:
103 else:
109 return ''
104 return ''
110
105
111 def flush_out(self):
106 def flush_out(self):
112 """Flush the stdout log. All data held in the log is lost."""
107 """Flush the stdout log. All data held in the log is lost."""
113
108
114 self.out.close()
109 self.out.close()
115 self.out = StringIO()
110 self.out = StringIO()
116
111
117 def trap_err(self):
112 def trap_err(self):
118 """Trap and log stderr."""
113 """Trap and log stderr."""
119 if sys.stderr is self.err:
114 if sys.stderr is self.err:
120 raise OutputTrapError,'You are already trapping stderr.'
115 raise OutputTrapError,'You are already trapping stderr.'
121 if not self.debug:
116 if not self.debug:
122 self._err_save = sys.stderr
117 self._err_save = sys.stderr
123 sys.stderr = self.err
118 sys.stderr = self.err
124
119
125 def release_err(self):
120 def release_err(self):
126 """Release stderr."""
121 """Release stderr."""
127 if not self.debug:
122 if not self.debug:
128 if not sys.stderr is self.err:
123 if not sys.stderr is self.err:
129 raise OutputTrapError,'You are not trapping stderr.'
124 raise OutputTrapError,'You are not trapping stderr.'
130 sys.stderr = self._err_save
125 sys.stderr = self._err_save
131 self.err_save = None
126 self.err_save = None
132
127
133 def summary_err(self):
128 def summary_err(self):
134 """Return as a string the log from stderr."""
129 """Return as a string the log from stderr."""
135 err = self.err.getvalue()
130 err = self.err.getvalue()
136 if err:
131 if err:
137 if self.quiet_err:
132 if self.quiet_err:
138 return err
133 return err
139 else:
134 else:
140 return self.err_head + 'Log by '+ self.name + ':\n' + err
135 return self.err_head + 'Log by '+ self.name + ':\n' + err
141 else:
136 else:
142 return ''
137 return ''
143
138
144 def flush_err(self):
139 def flush_err(self):
145 """Flush the stdout log. All data held in the log is lost."""
140 """Flush the stdout log. All data held in the log is lost."""
146
141
147 self.err.close()
142 self.err.close()
148 self.err = StringIO()
143 self.err = StringIO()
149
144
150 def trap_all(self):
145 def trap_all(self):
151 """Trap and log both stdout and stderr.
146 """Trap and log both stdout and stderr.
152
147
153 Cacthes and discards OutputTrapError exceptions raised."""
148 Cacthes and discards OutputTrapError exceptions raised."""
154 try:
149 try:
155 self.trap_out()
150 self.trap_out()
156 except OutputTrapError:
151 except OutputTrapError:
157 pass
152 pass
158 try:
153 try:
159 self.trap_err()
154 self.trap_err()
160 except OutputTrapError:
155 except OutputTrapError:
161 pass
156 pass
162
157
163 def release_all(self):
158 def release_all(self):
164 """Release both stdout and stderr.
159 """Release both stdout and stderr.
165
160
166 Cacthes and discards OutputTrapError exceptions raised."""
161 Cacthes and discards OutputTrapError exceptions raised."""
167 try:
162 try:
168 self.release_out()
163 self.release_out()
169 except OutputTrapError:
164 except OutputTrapError:
170 pass
165 pass
171 try:
166 try:
172 self.release_err()
167 self.release_err()
173 except OutputTrapError:
168 except OutputTrapError:
174 pass
169 pass
175
170
176 def summary_all(self):
171 def summary_all(self):
177 """Return as a string the log from stdout and stderr, prepending a separator
172 """Return as a string the log from stdout and stderr, prepending a separator
178 to each (defined in __init__ as sum_sep)."""
173 to each (defined in __init__ as sum_sep)."""
179 sum = ''
174 sum = ''
180 sout = self.summary_out()
175 sout = self.summary_out()
181 if sout:
176 if sout:
182 sum += self.sum_sep + sout
177 sum += self.sum_sep + sout
183 serr = self.summary_err()
178 serr = self.summary_err()
184 if serr:
179 if serr:
185 sum += '\n'+self.sum_sep + serr
180 sum += '\n'+self.sum_sep + serr
186 return sum
181 return sum
187
182
188 def flush_all(self):
183 def flush_all(self):
189 """Flush stdout and stderr"""
184 """Flush stdout and stderr"""
190 self.flush_out()
185 self.flush_out()
191 self.flush_err()
186 self.flush_err()
192
187
193 # a few shorthands
188 # a few shorthands
194 trap = trap_all
189 trap = trap_all
195 release = release_all
190 release = release_all
196 summary = summary_all
191 summary = summary_all
197 flush = flush_all
192 flush = flush_all
198 # end OutputTrap
193 # end OutputTrap
199
194
200
195
201 #****************************************************************************
196 #****************************************************************************
202 # Module testing. Incomplete, I'm lazy...
197 # Module testing. Incomplete, I'm lazy...
203
198
204 def _test_all():
199 def _test_all():
205
200
206 """Module testing functions, activated when the module is called as a
201 """Module testing functions, activated when the module is called as a
207 script (not imported)."""
202 script (not imported)."""
208
203
209 # Put tests for this module in here.
204 # Put tests for this module in here.
210 # Define them as nested functions so they don't clobber the
205 # Define them as nested functions so they don't clobber the
211 # pydoc-generated docs
206 # pydoc-generated docs
212
207
213 def _test_():
208 def _test_():
214 name = ''
209 name = ''
215 print '#'*50+'\nRunning test for ' + name
210 print '#'*50+'\nRunning test for ' + name
216 # ...
211 # ...
217 print 'Finished test for '+ name +'\n'+'#'*50
212 print 'Finished test for '+ name +'\n'+'#'*50
218
213
219 def _test_OutputTrap():
214 def _test_OutputTrap():
220 trap = OutputTrap(name = 'Test Trap', sum_sep = '.'*50+'\n',
215 trap = OutputTrap(name = 'Test Trap', sum_sep = '.'*50+'\n',
221 out_head = 'SOut. ', err_head = 'SErr. ')
216 out_head = 'SOut. ', err_head = 'SErr. ')
222
217
223 name = 'OutputTrap class'
218 name = 'OutputTrap class'
224 print '#'*50+'\nRunning test for ' + name
219 print '#'*50+'\nRunning test for ' + name
225 print 'Trapping out'
220 print 'Trapping out'
226 trap.trap_out()
221 trap.trap_out()
227 print >>sys.stdout, '>>stdout. stdout is trapped.'
222 print >>sys.stdout, '>>stdout. stdout is trapped.'
228 print >>sys.stderr, '>>stderr. stdout is trapped.'
223 print >>sys.stderr, '>>stderr. stdout is trapped.'
229 trap.release_out()
224 trap.release_out()
230 print trap.summary_out()
225 print trap.summary_out()
231
226
232 print 'Trapping err'
227 print 'Trapping err'
233 trap.trap_err()
228 trap.trap_err()
234 print >>sys.stdout, '>>stdout. stderr is trapped.'
229 print >>sys.stdout, '>>stdout. stderr is trapped.'
235 print >>sys.stderr, '>>stderr. stderr is trapped.'
230 print >>sys.stderr, '>>stderr. stderr is trapped.'
236 trap.release_err()
231 trap.release_err()
237 print trap.summary_err()
232 print trap.summary_err()
238
233
239 print 'Trapping all (no flushing)'
234 print 'Trapping all (no flushing)'
240 trap.trap_all()
235 trap.trap_all()
241 print >>sys.stdout, '>>stdout. stdout/err is trapped.'
236 print >>sys.stdout, '>>stdout. stdout/err is trapped.'
242 print >>sys.stderr, '>>stderr. stdout/err is trapped.'
237 print >>sys.stderr, '>>stderr. stdout/err is trapped.'
243 trap.release_all()
238 trap.release_all()
244 print trap.summary_all()
239 print trap.summary_all()
245
240
246 print 'Trapping all (flushing first)'
241 print 'Trapping all (flushing first)'
247 trap.flush()
242 trap.flush()
248 trap.trap_all()
243 trap.trap_all()
249 print >>sys.stdout, '>>stdout. stdout/err is trapped.'
244 print >>sys.stdout, '>>stdout. stdout/err is trapped.'
250 print >>sys.stderr, '>>stderr. stdout/err is trapped.'
245 print >>sys.stderr, '>>stderr. stdout/err is trapped.'
251 trap.release_all()
246 trap.release_all()
252 print trap.summary_all()
247 print trap.summary_all()
253 print 'Finished test for '+ name +'\n'+'#'*50
248 print 'Finished test for '+ name +'\n'+'#'*50
254
249
255 # call the actual tests here:
250 # call the actual tests here:
256 _test_OutputTrap()
251 _test_OutputTrap()
257
252
258
253
259 if __name__=="__main__":
254 if __name__=="__main__":
260 # _test_all() # XXX BROKEN.
255 # _test_all() # XXX BROKEN.
261 pass
256 pass
262
257
263 #************************ end of file <OutputTrap.py> ************************
258 #************************ end of file <OutputTrap.py> ************************
@@ -1,625 +1,624 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Classes for handling input/output prompts.
3 Classes for handling input/output prompts.
4
4 """
5 $Id: Prompts.py 3026 2008-02-07 16:03:16Z vivainio $"""
6
5
7 #*****************************************************************************
6 #*****************************************************************************
8 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
9 #
8 #
10 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
12 #*****************************************************************************
11 #*****************************************************************************
13
12
14 from IPython import Release
13 from IPython import Release
15 __author__ = '%s <%s>' % Release.authors['Fernando']
14 __author__ = '%s <%s>' % Release.authors['Fernando']
16 __license__ = Release.license
15 __license__ = Release.license
17 __version__ = Release.version
16 __version__ = Release.version
18
17
19 #****************************************************************************
18 #****************************************************************************
20 # Required modules
19 # Required modules
21 import __builtin__
20 import __builtin__
22 import os
21 import os
23 import socket
22 import socket
24 import sys
23 import sys
25 import time
24 import time
26
25
27 # IPython's own
26 # IPython's own
28 from IPython import ColorANSI
27 from IPython import ColorANSI
29 from IPython.Itpl import ItplNS
28 from IPython.Itpl import ItplNS
30 from IPython.ipstruct import Struct
29 from IPython.ipstruct import Struct
31 from IPython.macro import Macro
30 from IPython.macro import Macro
32 from IPython.genutils import *
31 from IPython.genutils import *
33 from IPython.ipapi import TryNext
32 from IPython.ipapi import TryNext
34
33
35 #****************************************************************************
34 #****************************************************************************
36 #Color schemes for Prompts.
35 #Color schemes for Prompts.
37
36
38 PromptColors = ColorANSI.ColorSchemeTable()
37 PromptColors = ColorANSI.ColorSchemeTable()
39 InputColors = ColorANSI.InputTermColors # just a shorthand
38 InputColors = ColorANSI.InputTermColors # just a shorthand
40 Colors = ColorANSI.TermColors # just a shorthand
39 Colors = ColorANSI.TermColors # just a shorthand
41
40
42 PromptColors.add_scheme(ColorANSI.ColorScheme(
41 PromptColors.add_scheme(ColorANSI.ColorScheme(
43 'NoColor',
42 'NoColor',
44 in_prompt = InputColors.NoColor, # Input prompt
43 in_prompt = InputColors.NoColor, # Input prompt
45 in_number = InputColors.NoColor, # Input prompt number
44 in_number = InputColors.NoColor, # Input prompt number
46 in_prompt2 = InputColors.NoColor, # Continuation prompt
45 in_prompt2 = InputColors.NoColor, # Continuation prompt
47 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
46 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
48
47
49 out_prompt = Colors.NoColor, # Output prompt
48 out_prompt = Colors.NoColor, # Output prompt
50 out_number = Colors.NoColor, # Output prompt number
49 out_number = Colors.NoColor, # Output prompt number
51
50
52 normal = Colors.NoColor # color off (usu. Colors.Normal)
51 normal = Colors.NoColor # color off (usu. Colors.Normal)
53 ))
52 ))
54
53
55 # make some schemes as instances so we can copy them for modification easily:
54 # make some schemes as instances so we can copy them for modification easily:
56 __PColLinux = ColorANSI.ColorScheme(
55 __PColLinux = ColorANSI.ColorScheme(
57 'Linux',
56 'Linux',
58 in_prompt = InputColors.Green,
57 in_prompt = InputColors.Green,
59 in_number = InputColors.LightGreen,
58 in_number = InputColors.LightGreen,
60 in_prompt2 = InputColors.Green,
59 in_prompt2 = InputColors.Green,
61 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
60 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
62
61
63 out_prompt = Colors.Red,
62 out_prompt = Colors.Red,
64 out_number = Colors.LightRed,
63 out_number = Colors.LightRed,
65
64
66 normal = Colors.Normal
65 normal = Colors.Normal
67 )
66 )
68 # Don't forget to enter it into the table!
67 # Don't forget to enter it into the table!
69 PromptColors.add_scheme(__PColLinux)
68 PromptColors.add_scheme(__PColLinux)
70
69
71 # Slightly modified Linux for light backgrounds
70 # Slightly modified Linux for light backgrounds
72 __PColLightBG = __PColLinux.copy('LightBG')
71 __PColLightBG = __PColLinux.copy('LightBG')
73
72
74 __PColLightBG.colors.update(
73 __PColLightBG.colors.update(
75 in_prompt = InputColors.Blue,
74 in_prompt = InputColors.Blue,
76 in_number = InputColors.LightBlue,
75 in_number = InputColors.LightBlue,
77 in_prompt2 = InputColors.Blue
76 in_prompt2 = InputColors.Blue
78 )
77 )
79 PromptColors.add_scheme(__PColLightBG)
78 PromptColors.add_scheme(__PColLightBG)
80
79
81 del Colors,InputColors
80 del Colors,InputColors
82
81
83 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
84 def multiple_replace(dict, text):
83 def multiple_replace(dict, text):
85 """ Replace in 'text' all occurences of any key in the given
84 """ Replace in 'text' all occurences of any key in the given
86 dictionary by its corresponding value. Returns the new string."""
85 dictionary by its corresponding value. Returns the new string."""
87
86
88 # Function by Xavier Defrang, originally found at:
87 # Function by Xavier Defrang, originally found at:
89 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
88 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
90
89
91 # Create a regular expression from the dictionary keys
90 # Create a regular expression from the dictionary keys
92 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
91 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
93 # For each match, look-up corresponding value in dictionary
92 # For each match, look-up corresponding value in dictionary
94 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
93 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
95
94
96 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
97 # Special characters that can be used in prompt templates, mainly bash-like
96 # Special characters that can be used in prompt templates, mainly bash-like
98
97
99 # If $HOME isn't defined (Windows), make it an absurd string so that it can
98 # If $HOME isn't defined (Windows), make it an absurd string so that it can
100 # never be expanded out into '~'. Basically anything which can never be a
99 # never be expanded out into '~'. Basically anything which can never be a
101 # reasonable directory name will do, we just want the $HOME -> '~' operation
100 # reasonable directory name will do, we just want the $HOME -> '~' operation
102 # to become a no-op. We pre-compute $HOME here so it's not done on every
101 # to become a no-op. We pre-compute $HOME here so it's not done on every
103 # prompt call.
102 # prompt call.
104
103
105 # FIXME:
104 # FIXME:
106
105
107 # - This should be turned into a class which does proper namespace management,
106 # - This should be turned into a class which does proper namespace management,
108 # since the prompt specials need to be evaluated in a certain namespace.
107 # since the prompt specials need to be evaluated in a certain namespace.
109 # Currently it's just globals, which need to be managed manually by code
108 # Currently it's just globals, which need to be managed manually by code
110 # below.
109 # below.
111
110
112 # - I also need to split up the color schemes from the prompt specials
111 # - I also need to split up the color schemes from the prompt specials
113 # somehow. I don't have a clean design for that quite yet.
112 # somehow. I don't have a clean design for that quite yet.
114
113
115 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
114 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
116
115
117 # We precompute a few more strings here for the prompt_specials, which are
116 # We precompute a few more strings here for the prompt_specials, which are
118 # fixed once ipython starts. This reduces the runtime overhead of computing
117 # fixed once ipython starts. This reduces the runtime overhead of computing
119 # prompt strings.
118 # prompt strings.
120 USER = os.environ.get("USER")
119 USER = os.environ.get("USER")
121 HOSTNAME = socket.gethostname()
120 HOSTNAME = socket.gethostname()
122 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
121 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
123 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
122 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
124
123
125 prompt_specials_color = {
124 prompt_specials_color = {
126 # Prompt/history count
125 # Prompt/history count
127 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
126 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
128 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
127 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
129 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
128 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
130 # can get numbers displayed in whatever color they want.
129 # can get numbers displayed in whatever color they want.
131 r'\N': '${self.cache.prompt_count}',
130 r'\N': '${self.cache.prompt_count}',
132 # Prompt/history count, with the actual digits replaced by dots. Used
131 # Prompt/history count, with the actual digits replaced by dots. Used
133 # mainly in continuation prompts (prompt_in2)
132 # mainly in continuation prompts (prompt_in2)
134 r'\D': '${"."*len(str(self.cache.prompt_count))}',
133 r'\D': '${"."*len(str(self.cache.prompt_count))}',
135 # Current working directory
134 # Current working directory
136 r'\w': '${os.getcwd()}',
135 r'\w': '${os.getcwd()}',
137 # Current time
136 # Current time
138 r'\t' : '${time.strftime("%H:%M:%S")}',
137 r'\t' : '${time.strftime("%H:%M:%S")}',
139 # Basename of current working directory.
138 # Basename of current working directory.
140 # (use os.sep to make this portable across OSes)
139 # (use os.sep to make this portable across OSes)
141 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
140 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
142 # These X<N> are an extension to the normal bash prompts. They return
141 # These X<N> are an extension to the normal bash prompts. They return
143 # N terms of the path, after replacing $HOME with '~'
142 # N terms of the path, after replacing $HOME with '~'
144 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
143 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
145 r'\X1': '${self.cwd_filt(1)}',
144 r'\X1': '${self.cwd_filt(1)}',
146 r'\X2': '${self.cwd_filt(2)}',
145 r'\X2': '${self.cwd_filt(2)}',
147 r'\X3': '${self.cwd_filt(3)}',
146 r'\X3': '${self.cwd_filt(3)}',
148 r'\X4': '${self.cwd_filt(4)}',
147 r'\X4': '${self.cwd_filt(4)}',
149 r'\X5': '${self.cwd_filt(5)}',
148 r'\X5': '${self.cwd_filt(5)}',
150 # Y<N> are similar to X<N>, but they show '~' if it's the directory
149 # Y<N> are similar to X<N>, but they show '~' if it's the directory
151 # N+1 in the list. Somewhat like %cN in tcsh.
150 # N+1 in the list. Somewhat like %cN in tcsh.
152 r'\Y0': '${self.cwd_filt2(0)}',
151 r'\Y0': '${self.cwd_filt2(0)}',
153 r'\Y1': '${self.cwd_filt2(1)}',
152 r'\Y1': '${self.cwd_filt2(1)}',
154 r'\Y2': '${self.cwd_filt2(2)}',
153 r'\Y2': '${self.cwd_filt2(2)}',
155 r'\Y3': '${self.cwd_filt2(3)}',
154 r'\Y3': '${self.cwd_filt2(3)}',
156 r'\Y4': '${self.cwd_filt2(4)}',
155 r'\Y4': '${self.cwd_filt2(4)}',
157 r'\Y5': '${self.cwd_filt2(5)}',
156 r'\Y5': '${self.cwd_filt2(5)}',
158 # Hostname up to first .
157 # Hostname up to first .
159 r'\h': HOSTNAME_SHORT,
158 r'\h': HOSTNAME_SHORT,
160 # Full hostname
159 # Full hostname
161 r'\H': HOSTNAME,
160 r'\H': HOSTNAME,
162 # Username of current user
161 # Username of current user
163 r'\u': USER,
162 r'\u': USER,
164 # Escaped '\'
163 # Escaped '\'
165 '\\\\': '\\',
164 '\\\\': '\\',
166 # Newline
165 # Newline
167 r'\n': '\n',
166 r'\n': '\n',
168 # Carriage return
167 # Carriage return
169 r'\r': '\r',
168 r'\r': '\r',
170 # Release version
169 # Release version
171 r'\v': __version__,
170 r'\v': __version__,
172 # Root symbol ($ or #)
171 # Root symbol ($ or #)
173 r'\$': ROOT_SYMBOL,
172 r'\$': ROOT_SYMBOL,
174 }
173 }
175
174
176 # A copy of the prompt_specials dictionary but with all color escapes removed,
175 # A copy of the prompt_specials dictionary but with all color escapes removed,
177 # so we can correctly compute the prompt length for the auto_rewrite method.
176 # so we can correctly compute the prompt length for the auto_rewrite method.
178 prompt_specials_nocolor = prompt_specials_color.copy()
177 prompt_specials_nocolor = prompt_specials_color.copy()
179 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
178 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
180 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
179 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
181
180
182 # Add in all the InputTermColors color escapes as valid prompt characters.
181 # Add in all the InputTermColors color escapes as valid prompt characters.
183 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
182 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
184 # with a color name which may begin with a letter used by any other of the
183 # with a color name which may begin with a letter used by any other of the
185 # allowed specials. This of course means that \\C will never be allowed for
184 # allowed specials. This of course means that \\C will never be allowed for
186 # anything else.
185 # anything else.
187 input_colors = ColorANSI.InputTermColors
186 input_colors = ColorANSI.InputTermColors
188 for _color in dir(input_colors):
187 for _color in dir(input_colors):
189 if _color[0] != '_':
188 if _color[0] != '_':
190 c_name = r'\C_'+_color
189 c_name = r'\C_'+_color
191 prompt_specials_color[c_name] = getattr(input_colors,_color)
190 prompt_specials_color[c_name] = getattr(input_colors,_color)
192 prompt_specials_nocolor[c_name] = ''
191 prompt_specials_nocolor[c_name] = ''
193
192
194 # we default to no color for safety. Note that prompt_specials is a global
193 # we default to no color for safety. Note that prompt_specials is a global
195 # variable used by all prompt objects.
194 # variable used by all prompt objects.
196 prompt_specials = prompt_specials_nocolor
195 prompt_specials = prompt_specials_nocolor
197
196
198 #-----------------------------------------------------------------------------
197 #-----------------------------------------------------------------------------
199 def str_safe(arg):
198 def str_safe(arg):
200 """Convert to a string, without ever raising an exception.
199 """Convert to a string, without ever raising an exception.
201
200
202 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
201 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
203 error message."""
202 error message."""
204
203
205 try:
204 try:
206 out = str(arg)
205 out = str(arg)
207 except UnicodeError:
206 except UnicodeError:
208 try:
207 try:
209 out = arg.encode('utf_8','replace')
208 out = arg.encode('utf_8','replace')
210 except Exception,msg:
209 except Exception,msg:
211 # let's keep this little duplication here, so that the most common
210 # let's keep this little duplication here, so that the most common
212 # case doesn't suffer from a double try wrapping.
211 # case doesn't suffer from a double try wrapping.
213 out = '<ERROR: %s>' % msg
212 out = '<ERROR: %s>' % msg
214 except Exception,msg:
213 except Exception,msg:
215 out = '<ERROR: %s>' % msg
214 out = '<ERROR: %s>' % msg
216 return out
215 return out
217
216
218 class BasePrompt(object):
217 class BasePrompt(object):
219 """Interactive prompt similar to Mathematica's."""
218 """Interactive prompt similar to Mathematica's."""
220
219
221 def _get_p_template(self):
220 def _get_p_template(self):
222 return self._p_template
221 return self._p_template
223
222
224 def _set_p_template(self,val):
223 def _set_p_template(self,val):
225 self._p_template = val
224 self._p_template = val
226 self.set_p_str()
225 self.set_p_str()
227
226
228 p_template = property(_get_p_template,_set_p_template,
227 p_template = property(_get_p_template,_set_p_template,
229 doc='Template for prompt string creation')
228 doc='Template for prompt string creation')
230
229
231 def __init__(self,cache,sep,prompt,pad_left=False):
230 def __init__(self,cache,sep,prompt,pad_left=False):
232
231
233 # Hack: we access information about the primary prompt through the
232 # Hack: we access information about the primary prompt through the
234 # cache argument. We need this, because we want the secondary prompt
233 # cache argument. We need this, because we want the secondary prompt
235 # to be aligned with the primary one. Color table info is also shared
234 # to be aligned with the primary one. Color table info is also shared
236 # by all prompt classes through the cache. Nice OO spaghetti code!
235 # by all prompt classes through the cache. Nice OO spaghetti code!
237 self.cache = cache
236 self.cache = cache
238 self.sep = sep
237 self.sep = sep
239
238
240 # regexp to count the number of spaces at the end of a prompt
239 # regexp to count the number of spaces at the end of a prompt
241 # expression, useful for prompt auto-rewriting
240 # expression, useful for prompt auto-rewriting
242 self.rspace = re.compile(r'(\s*)$')
241 self.rspace = re.compile(r'(\s*)$')
243 # Flag to left-pad prompt strings to match the length of the primary
242 # Flag to left-pad prompt strings to match the length of the primary
244 # prompt
243 # prompt
245 self.pad_left = pad_left
244 self.pad_left = pad_left
246
245
247 # Set template to create each actual prompt (where numbers change).
246 # Set template to create each actual prompt (where numbers change).
248 # Use a property
247 # Use a property
249 self.p_template = prompt
248 self.p_template = prompt
250 self.set_p_str()
249 self.set_p_str()
251
250
252 def set_p_str(self):
251 def set_p_str(self):
253 """ Set the interpolating prompt strings.
252 """ Set the interpolating prompt strings.
254
253
255 This must be called every time the color settings change, because the
254 This must be called every time the color settings change, because the
256 prompt_specials global may have changed."""
255 prompt_specials global may have changed."""
257
256
258 import os,time # needed in locals for prompt string handling
257 import os,time # needed in locals for prompt string handling
259 loc = locals()
258 loc = locals()
260 try:
259 try:
261 self.p_str = ItplNS('%s%s%s' %
260 self.p_str = ItplNS('%s%s%s' %
262 ('${self.sep}${self.col_p}',
261 ('${self.sep}${self.col_p}',
263 multiple_replace(prompt_specials, self.p_template),
262 multiple_replace(prompt_specials, self.p_template),
264 '${self.col_norm}'),self.cache.user_ns,loc)
263 '${self.col_norm}'),self.cache.user_ns,loc)
265
264
266 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
265 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
267 self.p_template),
266 self.p_template),
268 self.cache.user_ns,loc)
267 self.cache.user_ns,loc)
269 except:
268 except:
270 print "Illegal prompt template (check $ usage!):",self.p_template
269 print "Illegal prompt template (check $ usage!):",self.p_template
271 self.p_str = self.p_template
270 self.p_str = self.p_template
272 self.p_str_nocolor = self.p_template
271 self.p_str_nocolor = self.p_template
273
272
274 def write(self,msg): # dbg
273 def write(self,msg): # dbg
275 sys.stdout.write(msg)
274 sys.stdout.write(msg)
276 return ''
275 return ''
277
276
278 def __str__(self):
277 def __str__(self):
279 """Return a string form of the prompt.
278 """Return a string form of the prompt.
280
279
281 This for is useful for continuation and output prompts, since it is
280 This for is useful for continuation and output prompts, since it is
282 left-padded to match lengths with the primary one (if the
281 left-padded to match lengths with the primary one (if the
283 self.pad_left attribute is set)."""
282 self.pad_left attribute is set)."""
284
283
285 out_str = str_safe(self.p_str)
284 out_str = str_safe(self.p_str)
286 if self.pad_left:
285 if self.pad_left:
287 # We must find the amount of padding required to match lengths,
286 # We must find the amount of padding required to match lengths,
288 # taking the color escapes (which are invisible on-screen) into
287 # taking the color escapes (which are invisible on-screen) into
289 # account.
288 # account.
290 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
289 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
291 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
290 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
292 return format % out_str
291 return format % out_str
293 else:
292 else:
294 return out_str
293 return out_str
295
294
296 # these path filters are put in as methods so that we can control the
295 # these path filters are put in as methods so that we can control the
297 # namespace where the prompt strings get evaluated
296 # namespace where the prompt strings get evaluated
298 def cwd_filt(self,depth):
297 def cwd_filt(self,depth):
299 """Return the last depth elements of the current working directory.
298 """Return the last depth elements of the current working directory.
300
299
301 $HOME is always replaced with '~'.
300 $HOME is always replaced with '~'.
302 If depth==0, the full path is returned."""
301 If depth==0, the full path is returned."""
303
302
304 cwd = os.getcwd().replace(HOME,"~")
303 cwd = os.getcwd().replace(HOME,"~")
305 out = os.sep.join(cwd.split(os.sep)[-depth:])
304 out = os.sep.join(cwd.split(os.sep)[-depth:])
306 if out:
305 if out:
307 return out
306 return out
308 else:
307 else:
309 return os.sep
308 return os.sep
310
309
311 def cwd_filt2(self,depth):
310 def cwd_filt2(self,depth):
312 """Return the last depth elements of the current working directory.
311 """Return the last depth elements of the current working directory.
313
312
314 $HOME is always replaced with '~'.
313 $HOME is always replaced with '~'.
315 If depth==0, the full path is returned."""
314 If depth==0, the full path is returned."""
316
315
317 full_cwd = os.getcwd()
316 full_cwd = os.getcwd()
318 cwd = full_cwd.replace(HOME,"~").split(os.sep)
317 cwd = full_cwd.replace(HOME,"~").split(os.sep)
319 if '~' in cwd and len(cwd) == depth+1:
318 if '~' in cwd and len(cwd) == depth+1:
320 depth += 1
319 depth += 1
321 drivepart = ''
320 drivepart = ''
322 if sys.platform == 'win32' and len(cwd) > depth:
321 if sys.platform == 'win32' and len(cwd) > depth:
323 drivepart = os.path.splitdrive(full_cwd)[0]
322 drivepart = os.path.splitdrive(full_cwd)[0]
324 out = drivepart + '/'.join(cwd[-depth:])
323 out = drivepart + '/'.join(cwd[-depth:])
325
324
326 if out:
325 if out:
327 return out
326 return out
328 else:
327 else:
329 return os.sep
328 return os.sep
330
329
331 def __nonzero__(self):
330 def __nonzero__(self):
332 """Implement boolean behavior.
331 """Implement boolean behavior.
333
332
334 Checks whether the p_str attribute is non-empty"""
333 Checks whether the p_str attribute is non-empty"""
335
334
336 return bool(self.p_template)
335 return bool(self.p_template)
337
336
338 class Prompt1(BasePrompt):
337 class Prompt1(BasePrompt):
339 """Input interactive prompt similar to Mathematica's."""
338 """Input interactive prompt similar to Mathematica's."""
340
339
341 def __init__(self,cache,sep='\n',prompt='In [\\#]: ',pad_left=True):
340 def __init__(self,cache,sep='\n',prompt='In [\\#]: ',pad_left=True):
342 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
341 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
343
342
344 def set_colors(self):
343 def set_colors(self):
345 self.set_p_str()
344 self.set_p_str()
346 Colors = self.cache.color_table.active_colors # shorthand
345 Colors = self.cache.color_table.active_colors # shorthand
347 self.col_p = Colors.in_prompt
346 self.col_p = Colors.in_prompt
348 self.col_num = Colors.in_number
347 self.col_num = Colors.in_number
349 self.col_norm = Colors.in_normal
348 self.col_norm = Colors.in_normal
350 # We need a non-input version of these escapes for the '--->'
349 # We need a non-input version of these escapes for the '--->'
351 # auto-call prompts used in the auto_rewrite() method.
350 # auto-call prompts used in the auto_rewrite() method.
352 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
351 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
353 self.col_norm_ni = Colors.normal
352 self.col_norm_ni = Colors.normal
354
353
355 def __str__(self):
354 def __str__(self):
356 self.cache.prompt_count += 1
355 self.cache.prompt_count += 1
357 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
356 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
358 return str_safe(self.p_str)
357 return str_safe(self.p_str)
359
358
360 def auto_rewrite(self):
359 def auto_rewrite(self):
361 """Print a string of the form '--->' which lines up with the previous
360 """Print a string of the form '--->' which lines up with the previous
362 input string. Useful for systems which re-write the user input when
361 input string. Useful for systems which re-write the user input when
363 handling automatically special syntaxes."""
362 handling automatically special syntaxes."""
364
363
365 curr = str(self.cache.last_prompt)
364 curr = str(self.cache.last_prompt)
366 nrspaces = len(self.rspace.search(curr).group())
365 nrspaces = len(self.rspace.search(curr).group())
367 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
366 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
368 ' '*nrspaces,self.col_norm_ni)
367 ' '*nrspaces,self.col_norm_ni)
369
368
370 class PromptOut(BasePrompt):
369 class PromptOut(BasePrompt):
371 """Output interactive prompt similar to Mathematica's."""
370 """Output interactive prompt similar to Mathematica's."""
372
371
373 def __init__(self,cache,sep='',prompt='Out[\\#]: ',pad_left=True):
372 def __init__(self,cache,sep='',prompt='Out[\\#]: ',pad_left=True):
374 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
373 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
375 if not self.p_template:
374 if not self.p_template:
376 self.__str__ = lambda: ''
375 self.__str__ = lambda: ''
377
376
378 def set_colors(self):
377 def set_colors(self):
379 self.set_p_str()
378 self.set_p_str()
380 Colors = self.cache.color_table.active_colors # shorthand
379 Colors = self.cache.color_table.active_colors # shorthand
381 self.col_p = Colors.out_prompt
380 self.col_p = Colors.out_prompt
382 self.col_num = Colors.out_number
381 self.col_num = Colors.out_number
383 self.col_norm = Colors.normal
382 self.col_norm = Colors.normal
384
383
385 class Prompt2(BasePrompt):
384 class Prompt2(BasePrompt):
386 """Interactive continuation prompt."""
385 """Interactive continuation prompt."""
387
386
388 def __init__(self,cache,prompt=' .\\D.: ',pad_left=True):
387 def __init__(self,cache,prompt=' .\\D.: ',pad_left=True):
389 self.cache = cache
388 self.cache = cache
390 self.p_template = prompt
389 self.p_template = prompt
391 self.pad_left = pad_left
390 self.pad_left = pad_left
392 self.set_p_str()
391 self.set_p_str()
393
392
394 def set_p_str(self):
393 def set_p_str(self):
395 import os,time # needed in locals for prompt string handling
394 import os,time # needed in locals for prompt string handling
396 loc = locals()
395 loc = locals()
397 self.p_str = ItplNS('%s%s%s' %
396 self.p_str = ItplNS('%s%s%s' %
398 ('${self.col_p2}',
397 ('${self.col_p2}',
399 multiple_replace(prompt_specials, self.p_template),
398 multiple_replace(prompt_specials, self.p_template),
400 '$self.col_norm'),
399 '$self.col_norm'),
401 self.cache.user_ns,loc)
400 self.cache.user_ns,loc)
402 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
401 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
403 self.p_template),
402 self.p_template),
404 self.cache.user_ns,loc)
403 self.cache.user_ns,loc)
405
404
406 def set_colors(self):
405 def set_colors(self):
407 self.set_p_str()
406 self.set_p_str()
408 Colors = self.cache.color_table.active_colors
407 Colors = self.cache.color_table.active_colors
409 self.col_p2 = Colors.in_prompt2
408 self.col_p2 = Colors.in_prompt2
410 self.col_norm = Colors.in_normal
409 self.col_norm = Colors.in_normal
411 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
410 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
412 # updated their prompt_in2 definitions. Remove eventually.
411 # updated their prompt_in2 definitions. Remove eventually.
413 self.col_p = Colors.out_prompt
412 self.col_p = Colors.out_prompt
414 self.col_num = Colors.out_number
413 self.col_num = Colors.out_number
415
414
416
415
417 #-----------------------------------------------------------------------------
416 #-----------------------------------------------------------------------------
418 class CachedOutput:
417 class CachedOutput:
419 """Class for printing output from calculations while keeping a cache of
418 """Class for printing output from calculations while keeping a cache of
420 reults. It dynamically creates global variables prefixed with _ which
419 reults. It dynamically creates global variables prefixed with _ which
421 contain these results.
420 contain these results.
422
421
423 Meant to be used as a sys.displayhook replacement, providing numbered
422 Meant to be used as a sys.displayhook replacement, providing numbered
424 prompts and cache services.
423 prompts and cache services.
425
424
426 Initialize with initial and final values for cache counter (this defines
425 Initialize with initial and final values for cache counter (this defines
427 the maximum size of the cache."""
426 the maximum size of the cache."""
428
427
429 def __init__(self,shell,cache_size,Pprint,
428 def __init__(self,shell,cache_size,Pprint,
430 colors='NoColor',input_sep='\n',
429 colors='NoColor',input_sep='\n',
431 output_sep='\n',output_sep2='',
430 output_sep='\n',output_sep2='',
432 ps1 = None, ps2 = None,ps_out = None,pad_left=True):
431 ps1 = None, ps2 = None,ps_out = None,pad_left=True):
433
432
434 cache_size_min = 3
433 cache_size_min = 3
435 if cache_size <= 0:
434 if cache_size <= 0:
436 self.do_full_cache = 0
435 self.do_full_cache = 0
437 cache_size = 0
436 cache_size = 0
438 elif cache_size < cache_size_min:
437 elif cache_size < cache_size_min:
439 self.do_full_cache = 0
438 self.do_full_cache = 0
440 cache_size = 0
439 cache_size = 0
441 warn('caching was disabled (min value for cache size is %s).' %
440 warn('caching was disabled (min value for cache size is %s).' %
442 cache_size_min,level=3)
441 cache_size_min,level=3)
443 else:
442 else:
444 self.do_full_cache = 1
443 self.do_full_cache = 1
445
444
446 self.cache_size = cache_size
445 self.cache_size = cache_size
447 self.input_sep = input_sep
446 self.input_sep = input_sep
448
447
449 # we need a reference to the user-level namespace
448 # we need a reference to the user-level namespace
450 self.shell = shell
449 self.shell = shell
451 self.user_ns = shell.user_ns
450 self.user_ns = shell.user_ns
452 # and to the user's input
451 # and to the user's input
453 self.input_hist = shell.input_hist
452 self.input_hist = shell.input_hist
454 # and to the user's logger, for logging output
453 # and to the user's logger, for logging output
455 self.logger = shell.logger
454 self.logger = shell.logger
456
455
457 # Set input prompt strings and colors
456 # Set input prompt strings and colors
458 if cache_size == 0:
457 if cache_size == 0:
459 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
458 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
460 or ps1.find(r'\N') > -1:
459 or ps1.find(r'\N') > -1:
461 ps1 = '>>> '
460 ps1 = '>>> '
462 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
461 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
463 or ps2.find(r'\N') > -1:
462 or ps2.find(r'\N') > -1:
464 ps2 = '... '
463 ps2 = '... '
465 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
464 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
466 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
465 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
467 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
466 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
468
467
469 self.color_table = PromptColors
468 self.color_table = PromptColors
470 self.prompt1 = Prompt1(self,sep=input_sep,prompt=self.ps1_str,
469 self.prompt1 = Prompt1(self,sep=input_sep,prompt=self.ps1_str,
471 pad_left=pad_left)
470 pad_left=pad_left)
472 self.prompt2 = Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
471 self.prompt2 = Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
473 self.prompt_out = PromptOut(self,sep='',prompt=self.ps_out_str,
472 self.prompt_out = PromptOut(self,sep='',prompt=self.ps_out_str,
474 pad_left=pad_left)
473 pad_left=pad_left)
475 self.set_colors(colors)
474 self.set_colors(colors)
476
475
477 # other more normal stuff
476 # other more normal stuff
478 # b/c each call to the In[] prompt raises it by 1, even the first.
477 # b/c each call to the In[] prompt raises it by 1, even the first.
479 self.prompt_count = 0
478 self.prompt_count = 0
480 # Store the last prompt string each time, we need it for aligning
479 # Store the last prompt string each time, we need it for aligning
481 # continuation and auto-rewrite prompts
480 # continuation and auto-rewrite prompts
482 self.last_prompt = ''
481 self.last_prompt = ''
483 self.Pprint = Pprint
482 self.Pprint = Pprint
484 self.output_sep = output_sep
483 self.output_sep = output_sep
485 self.output_sep2 = output_sep2
484 self.output_sep2 = output_sep2
486 self._,self.__,self.___ = '','',''
485 self._,self.__,self.___ = '','',''
487 self.pprint_types = map(type,[(),[],{}])
486 self.pprint_types = map(type,[(),[],{}])
488
487
489 # these are deliberately global:
488 # these are deliberately global:
490 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
489 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
491 self.user_ns.update(to_user_ns)
490 self.user_ns.update(to_user_ns)
492
491
493 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
492 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
494 if p_str is None:
493 if p_str is None:
495 if self.do_full_cache:
494 if self.do_full_cache:
496 return cache_def
495 return cache_def
497 else:
496 else:
498 return no_cache_def
497 return no_cache_def
499 else:
498 else:
500 return p_str
499 return p_str
501
500
502 def set_colors(self,colors):
501 def set_colors(self,colors):
503 """Set the active color scheme and configure colors for the three
502 """Set the active color scheme and configure colors for the three
504 prompt subsystems."""
503 prompt subsystems."""
505
504
506 # FIXME: the prompt_specials global should be gobbled inside this
505 # FIXME: the prompt_specials global should be gobbled inside this
507 # class instead. Do it when cleaning up the whole 3-prompt system.
506 # class instead. Do it when cleaning up the whole 3-prompt system.
508 global prompt_specials
507 global prompt_specials
509 if colors.lower()=='nocolor':
508 if colors.lower()=='nocolor':
510 prompt_specials = prompt_specials_nocolor
509 prompt_specials = prompt_specials_nocolor
511 else:
510 else:
512 prompt_specials = prompt_specials_color
511 prompt_specials = prompt_specials_color
513
512
514 self.color_table.set_active_scheme(colors)
513 self.color_table.set_active_scheme(colors)
515 self.prompt1.set_colors()
514 self.prompt1.set_colors()
516 self.prompt2.set_colors()
515 self.prompt2.set_colors()
517 self.prompt_out.set_colors()
516 self.prompt_out.set_colors()
518
517
519 def __call__(self,arg=None):
518 def __call__(self,arg=None):
520 """Printing with history cache management.
519 """Printing with history cache management.
521
520
522 This is invoked everytime the interpreter needs to print, and is
521 This is invoked everytime the interpreter needs to print, and is
523 activated by setting the variable sys.displayhook to it."""
522 activated by setting the variable sys.displayhook to it."""
524
523
525 # If something injected a '_' variable in __builtin__, delete
524 # If something injected a '_' variable in __builtin__, delete
526 # ipython's automatic one so we don't clobber that. gettext() in
525 # ipython's automatic one so we don't clobber that. gettext() in
527 # particular uses _, so we need to stay away from it.
526 # particular uses _, so we need to stay away from it.
528 if '_' in __builtin__.__dict__:
527 if '_' in __builtin__.__dict__:
529 try:
528 try:
530 del self.user_ns['_']
529 del self.user_ns['_']
531 except KeyError:
530 except KeyError:
532 pass
531 pass
533 if arg is not None:
532 if arg is not None:
534 cout_write = Term.cout.write # fast lookup
533 cout_write = Term.cout.write # fast lookup
535 # first handle the cache and counters
534 # first handle the cache and counters
536
535
537 # do not print output if input ends in ';'
536 # do not print output if input ends in ';'
538 try:
537 try:
539 if self.input_hist[self.prompt_count].endswith(';\n'):
538 if self.input_hist[self.prompt_count].endswith(';\n'):
540 return
539 return
541 except IndexError:
540 except IndexError:
542 # some uses of ipshellembed may fail here
541 # some uses of ipshellembed may fail here
543 pass
542 pass
544 # don't use print, puts an extra space
543 # don't use print, puts an extra space
545 cout_write(self.output_sep)
544 cout_write(self.output_sep)
546 outprompt = self.shell.hooks.generate_output_prompt()
545 outprompt = self.shell.hooks.generate_output_prompt()
547 if self.do_full_cache:
546 if self.do_full_cache:
548 cout_write(outprompt)
547 cout_write(outprompt)
549
548
550 # and now call a possibly user-defined print mechanism
549 # and now call a possibly user-defined print mechanism
551 manipulated_val = self.display(arg)
550 manipulated_val = self.display(arg)
552
551
553 # user display hooks can change the variable to be stored in
552 # user display hooks can change the variable to be stored in
554 # output history
553 # output history
555
554
556 if manipulated_val is not None:
555 if manipulated_val is not None:
557 arg = manipulated_val
556 arg = manipulated_val
558
557
559 # avoid recursive reference when displaying _oh/Out
558 # avoid recursive reference when displaying _oh/Out
560 if arg is not self.user_ns['_oh']:
559 if arg is not self.user_ns['_oh']:
561 self.update(arg)
560 self.update(arg)
562
561
563 if self.logger.log_output:
562 if self.logger.log_output:
564 self.logger.log_write(repr(arg),'output')
563 self.logger.log_write(repr(arg),'output')
565 cout_write(self.output_sep2)
564 cout_write(self.output_sep2)
566 Term.cout.flush()
565 Term.cout.flush()
567
566
568 def _display(self,arg):
567 def _display(self,arg):
569 """Default printer method, uses pprint.
568 """Default printer method, uses pprint.
570
569
571 Do ip.set_hook("result_display", my_displayhook) for custom result
570 Do ip.set_hook("result_display", my_displayhook) for custom result
572 display, e.g. when your own objects need special formatting.
571 display, e.g. when your own objects need special formatting.
573 """
572 """
574 try:
573 try:
575 return IPython.generics.result_display(arg)
574 return IPython.generics.result_display(arg)
576 except TryNext:
575 except TryNext:
577 return self.shell.hooks.result_display(arg)
576 return self.shell.hooks.result_display(arg)
578
577
579 # Assign the default display method:
578 # Assign the default display method:
580 display = _display
579 display = _display
581
580
582 def update(self,arg):
581 def update(self,arg):
583 #print '***cache_count', self.cache_count # dbg
582 #print '***cache_count', self.cache_count # dbg
584 if len(self.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
583 if len(self.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
585 warn('Output cache limit (currently '+
584 warn('Output cache limit (currently '+
586 `self.cache_size`+' entries) hit.\n'
585 `self.cache_size`+' entries) hit.\n'
587 'Flushing cache and resetting history counter...\n'
586 'Flushing cache and resetting history counter...\n'
588 'The only history variables available will be _,__,___ and _1\n'
587 'The only history variables available will be _,__,___ and _1\n'
589 'with the current result.')
588 'with the current result.')
590
589
591 self.flush()
590 self.flush()
592 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
591 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
593 # we cause buggy behavior for things like gettext).
592 # we cause buggy behavior for things like gettext).
594 if '_' not in __builtin__.__dict__:
593 if '_' not in __builtin__.__dict__:
595 self.___ = self.__
594 self.___ = self.__
596 self.__ = self._
595 self.__ = self._
597 self._ = arg
596 self._ = arg
598 self.user_ns.update({'_':self._,'__':self.__,'___':self.___})
597 self.user_ns.update({'_':self._,'__':self.__,'___':self.___})
599
598
600 # hackish access to top-level namespace to create _1,_2... dynamically
599 # hackish access to top-level namespace to create _1,_2... dynamically
601 to_main = {}
600 to_main = {}
602 if self.do_full_cache:
601 if self.do_full_cache:
603 new_result = '_'+`self.prompt_count`
602 new_result = '_'+`self.prompt_count`
604 to_main[new_result] = arg
603 to_main[new_result] = arg
605 self.user_ns.update(to_main)
604 self.user_ns.update(to_main)
606 self.user_ns['_oh'][self.prompt_count] = arg
605 self.user_ns['_oh'][self.prompt_count] = arg
607
606
608 def flush(self):
607 def flush(self):
609 if not self.do_full_cache:
608 if not self.do_full_cache:
610 raise ValueError,"You shouldn't have reached the cache flush "\
609 raise ValueError,"You shouldn't have reached the cache flush "\
611 "if full caching is not enabled!"
610 "if full caching is not enabled!"
612 # delete auto-generated vars from global namespace
611 # delete auto-generated vars from global namespace
613
612
614 for n in range(1,self.prompt_count + 1):
613 for n in range(1,self.prompt_count + 1):
615 key = '_'+`n`
614 key = '_'+`n`
616 try:
615 try:
617 del self.user_ns[key]
616 del self.user_ns[key]
618 except: pass
617 except: pass
619 self.user_ns['_oh'].clear()
618 self.user_ns['_oh'].clear()
620
619
621 if '_' not in __builtin__.__dict__:
620 if '_' not in __builtin__.__dict__:
622 self.user_ns.update({'_':None,'__':None, '___':None})
621 self.user_ns.update({'_':None,'__':None, '___':None})
623 import gc
622 import gc
624 gc.collect() # xxx needed?
623 gc.collect() # xxx needed?
625
624
@@ -1,301 +1,300 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Class and program to colorize python source code for ANSI terminals.
3 Class and program to colorize python source code for ANSI terminals.
4
4
5 Based on an HTML code highlighter by Jurgen Hermann found at:
5 Based on an HTML code highlighter by Jurgen Hermann found at:
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
6 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298
7
7
8 Modifications by Fernando Perez (fperez@colorado.edu).
8 Modifications by Fernando Perez (fperez@colorado.edu).
9
9
10 Information on the original HTML highlighter follows:
10 Information on the original HTML highlighter follows:
11
12 MoinMoin - Python Source Parser
13
11
14 Title: Colorize Python source using the built-in tokenizer
12 MoinMoin - Python Source Parser
15
16 Submitter: Jurgen Hermann
17 Last Updated:2001/04/06
18
19 Version no:1.2
20
13
21 Description:
14 Title: Colorize Python source using the built-in tokenizer
22
15
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
16 Submitter: Jurgen Hermann
24 Python source code to HTML markup, rendering comments, keywords,
17 Last Updated:2001/04/06
25 operators, numeric and string literals in different colors.
26
18
27 It shows how to use the built-in keyword, token and tokenize modules to
19 Version no:1.2
28 scan Python source code and re-emit it with no changes to its original
29 formatting (which is the hard part).
30
20
31 $Id: PyColorize.py 2586 2007-08-06 19:30:09Z vivainio $"""
21 Description:
22
23 This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
24 Python source code to HTML markup, rendering comments, keywords,
25 operators, numeric and string literals in different colors.
26
27 It shows how to use the built-in keyword, token and tokenize modules to
28 scan Python source code and re-emit it with no changes to its original
29 formatting (which is the hard part).
30 """
32
31
33 __all__ = ['ANSICodeColors','Parser']
32 __all__ = ['ANSICodeColors','Parser']
34
33
35 _scheme_default = 'Linux'
34 _scheme_default = 'Linux'
36
35
37 # Imports
36 # Imports
38 import cStringIO
37 import cStringIO
39 import keyword
38 import keyword
40 import os
39 import os
41 import optparse
40 import optparse
42 import string
41 import string
43 import sys
42 import sys
44 import token
43 import token
45 import tokenize
44 import tokenize
46
45
47 from IPython.ColorANSI import *
46 from IPython.ColorANSI import *
48
47
49 #############################################################################
48 #############################################################################
50 ### Python Source Parser (does Hilighting)
49 ### Python Source Parser (does Hilighting)
51 #############################################################################
50 #############################################################################
52
51
53 _KEYWORD = token.NT_OFFSET + 1
52 _KEYWORD = token.NT_OFFSET + 1
54 _TEXT = token.NT_OFFSET + 2
53 _TEXT = token.NT_OFFSET + 2
55
54
56 #****************************************************************************
55 #****************************************************************************
57 # Builtin color schemes
56 # Builtin color schemes
58
57
59 Colors = TermColors # just a shorthand
58 Colors = TermColors # just a shorthand
60
59
61 # Build a few color schemes
60 # Build a few color schemes
62 NoColor = ColorScheme(
61 NoColor = ColorScheme(
63 'NoColor',{
62 'NoColor',{
64 token.NUMBER : Colors.NoColor,
63 token.NUMBER : Colors.NoColor,
65 token.OP : Colors.NoColor,
64 token.OP : Colors.NoColor,
66 token.STRING : Colors.NoColor,
65 token.STRING : Colors.NoColor,
67 tokenize.COMMENT : Colors.NoColor,
66 tokenize.COMMENT : Colors.NoColor,
68 token.NAME : Colors.NoColor,
67 token.NAME : Colors.NoColor,
69 token.ERRORTOKEN : Colors.NoColor,
68 token.ERRORTOKEN : Colors.NoColor,
70
69
71 _KEYWORD : Colors.NoColor,
70 _KEYWORD : Colors.NoColor,
72 _TEXT : Colors.NoColor,
71 _TEXT : Colors.NoColor,
73
72
74 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
73 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
75 } )
74 } )
76
75
77 LinuxColors = ColorScheme(
76 LinuxColors = ColorScheme(
78 'Linux',{
77 'Linux',{
79 token.NUMBER : Colors.LightCyan,
78 token.NUMBER : Colors.LightCyan,
80 token.OP : Colors.Yellow,
79 token.OP : Colors.Yellow,
81 token.STRING : Colors.LightBlue,
80 token.STRING : Colors.LightBlue,
82 tokenize.COMMENT : Colors.LightRed,
81 tokenize.COMMENT : Colors.LightRed,
83 token.NAME : Colors.White,
82 token.NAME : Colors.White,
84 token.ERRORTOKEN : Colors.Red,
83 token.ERRORTOKEN : Colors.Red,
85
84
86 _KEYWORD : Colors.LightGreen,
85 _KEYWORD : Colors.LightGreen,
87 _TEXT : Colors.Yellow,
86 _TEXT : Colors.Yellow,
88
87
89 'normal' : Colors.Normal # color off (usu. Colors.Normal)
88 'normal' : Colors.Normal # color off (usu. Colors.Normal)
90 } )
89 } )
91
90
92 LightBGColors = ColorScheme(
91 LightBGColors = ColorScheme(
93 'LightBG',{
92 'LightBG',{
94 token.NUMBER : Colors.Cyan,
93 token.NUMBER : Colors.Cyan,
95 token.OP : Colors.Blue,
94 token.OP : Colors.Blue,
96 token.STRING : Colors.Blue,
95 token.STRING : Colors.Blue,
97 tokenize.COMMENT : Colors.Red,
96 tokenize.COMMENT : Colors.Red,
98 token.NAME : Colors.Black,
97 token.NAME : Colors.Black,
99 token.ERRORTOKEN : Colors.Red,
98 token.ERRORTOKEN : Colors.Red,
100
99
101 _KEYWORD : Colors.Green,
100 _KEYWORD : Colors.Green,
102 _TEXT : Colors.Blue,
101 _TEXT : Colors.Blue,
103
102
104 'normal' : Colors.Normal # color off (usu. Colors.Normal)
103 'normal' : Colors.Normal # color off (usu. Colors.Normal)
105 } )
104 } )
106
105
107 # Build table of color schemes (needed by the parser)
106 # Build table of color schemes (needed by the parser)
108 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
107 ANSICodeColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
109 _scheme_default)
108 _scheme_default)
110
109
111 class Parser:
110 class Parser:
112 """ Format colored Python source.
111 """ Format colored Python source.
113 """
112 """
114
113
115 def __init__(self, color_table=None,out = sys.stdout):
114 def __init__(self, color_table=None,out = sys.stdout):
116 """ Create a parser with a specified color table and output channel.
115 """ Create a parser with a specified color table and output channel.
117
116
118 Call format() to process code.
117 Call format() to process code.
119 """
118 """
120 self.color_table = color_table and color_table or ANSICodeColors
119 self.color_table = color_table and color_table or ANSICodeColors
121 self.out = out
120 self.out = out
122
121
123 def format(self, raw, out = None, scheme = ''):
122 def format(self, raw, out = None, scheme = ''):
124 return self.format2(raw, out, scheme)[0]
123 return self.format2(raw, out, scheme)[0]
125
124
126 def format2(self, raw, out = None, scheme = ''):
125 def format2(self, raw, out = None, scheme = ''):
127 """ Parse and send the colored source.
126 """ Parse and send the colored source.
128
127
129 If out and scheme are not specified, the defaults (given to
128 If out and scheme are not specified, the defaults (given to
130 constructor) are used.
129 constructor) are used.
131
130
132 out should be a file-type object. Optionally, out can be given as the
131 out should be a file-type object. Optionally, out can be given as the
133 string 'str' and the parser will automatically return the output in a
132 string 'str' and the parser will automatically return the output in a
134 string."""
133 string."""
135
134
136 string_output = 0
135 string_output = 0
137 if out == 'str' or self.out == 'str' or \
136 if out == 'str' or self.out == 'str' or \
138 isinstance(self.out,cStringIO.OutputType):
137 isinstance(self.out,cStringIO.OutputType):
139 # XXX - I don't really like this state handling logic, but at this
138 # XXX - I don't really like this state handling logic, but at this
140 # point I don't want to make major changes, so adding the
139 # point I don't want to make major changes, so adding the
141 # isinstance() check is the simplest I can do to ensure correct
140 # isinstance() check is the simplest I can do to ensure correct
142 # behavior.
141 # behavior.
143 out_old = self.out
142 out_old = self.out
144 self.out = cStringIO.StringIO()
143 self.out = cStringIO.StringIO()
145 string_output = 1
144 string_output = 1
146 elif out is not None:
145 elif out is not None:
147 self.out = out
146 self.out = out
148
147
149 # Fast return of the unmodified input for NoColor scheme
148 # Fast return of the unmodified input for NoColor scheme
150 if scheme == 'NoColor':
149 if scheme == 'NoColor':
151 error = False
150 error = False
152 self.out.write(raw)
151 self.out.write(raw)
153 if string_output:
152 if string_output:
154 return raw,error
153 return raw,error
155 else:
154 else:
156 return None,error
155 return None,error
157
156
158 # local shorthands
157 # local shorthands
159 colors = self.color_table[scheme].colors
158 colors = self.color_table[scheme].colors
160 self.colors = colors # put in object so __call__ sees it
159 self.colors = colors # put in object so __call__ sees it
161
160
162 # Remove trailing whitespace and normalize tabs
161 # Remove trailing whitespace and normalize tabs
163 self.raw = raw.expandtabs().rstrip()
162 self.raw = raw.expandtabs().rstrip()
164
163
165 # store line offsets in self.lines
164 # store line offsets in self.lines
166 self.lines = [0, 0]
165 self.lines = [0, 0]
167 pos = 0
166 pos = 0
168 raw_find = self.raw.find
167 raw_find = self.raw.find
169 lines_append = self.lines.append
168 lines_append = self.lines.append
170 while 1:
169 while 1:
171 pos = raw_find('\n', pos) + 1
170 pos = raw_find('\n', pos) + 1
172 if not pos: break
171 if not pos: break
173 lines_append(pos)
172 lines_append(pos)
174 lines_append(len(self.raw))
173 lines_append(len(self.raw))
175
174
176 # parse the source and write it
175 # parse the source and write it
177 self.pos = 0
176 self.pos = 0
178 text = cStringIO.StringIO(self.raw)
177 text = cStringIO.StringIO(self.raw)
179
178
180 error = False
179 error = False
181 try:
180 try:
182 tokenize.tokenize(text.readline, self)
181 tokenize.tokenize(text.readline, self)
183 except tokenize.TokenError, ex:
182 except tokenize.TokenError, ex:
184 msg = ex[0]
183 msg = ex[0]
185 line = ex[1][0]
184 line = ex[1][0]
186 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
185 self.out.write("%s\n\n*** ERROR: %s%s%s\n" %
187 (colors[token.ERRORTOKEN],
186 (colors[token.ERRORTOKEN],
188 msg, self.raw[self.lines[line]:],
187 msg, self.raw[self.lines[line]:],
189 colors.normal)
188 colors.normal)
190 )
189 )
191 error = True
190 error = True
192 self.out.write(colors.normal+'\n')
191 self.out.write(colors.normal+'\n')
193 if string_output:
192 if string_output:
194 output = self.out.getvalue()
193 output = self.out.getvalue()
195 self.out = out_old
194 self.out = out_old
196 return (output, error)
195 return (output, error)
197 return (None, error)
196 return (None, error)
198
197
199 def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
198 def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
200 """ Token handler, with syntax highlighting."""
199 """ Token handler, with syntax highlighting."""
201
200
202 # local shorthands
201 # local shorthands
203 colors = self.colors
202 colors = self.colors
204 owrite = self.out.write
203 owrite = self.out.write
205
204
206 # line separator, so this works across platforms
205 # line separator, so this works across platforms
207 linesep = os.linesep
206 linesep = os.linesep
208
207
209 # calculate new positions
208 # calculate new positions
210 oldpos = self.pos
209 oldpos = self.pos
211 newpos = self.lines[srow] + scol
210 newpos = self.lines[srow] + scol
212 self.pos = newpos + len(toktext)
211 self.pos = newpos + len(toktext)
213
212
214 # handle newlines
213 # handle newlines
215 if toktype in [token.NEWLINE, tokenize.NL]:
214 if toktype in [token.NEWLINE, tokenize.NL]:
216 owrite(linesep)
215 owrite(linesep)
217 return
216 return
218
217
219 # send the original whitespace, if needed
218 # send the original whitespace, if needed
220 if newpos > oldpos:
219 if newpos > oldpos:
221 owrite(self.raw[oldpos:newpos])
220 owrite(self.raw[oldpos:newpos])
222
221
223 # skip indenting tokens
222 # skip indenting tokens
224 if toktype in [token.INDENT, token.DEDENT]:
223 if toktype in [token.INDENT, token.DEDENT]:
225 self.pos = newpos
224 self.pos = newpos
226 return
225 return
227
226
228 # map token type to a color group
227 # map token type to a color group
229 if token.LPAR <= toktype and toktype <= token.OP:
228 if token.LPAR <= toktype and toktype <= token.OP:
230 toktype = token.OP
229 toktype = token.OP
231 elif toktype == token.NAME and keyword.iskeyword(toktext):
230 elif toktype == token.NAME and keyword.iskeyword(toktext):
232 toktype = _KEYWORD
231 toktype = _KEYWORD
233 color = colors.get(toktype, colors[_TEXT])
232 color = colors.get(toktype, colors[_TEXT])
234
233
235 #print '<%s>' % toktext, # dbg
234 #print '<%s>' % toktext, # dbg
236
235
237 # Triple quoted strings must be handled carefully so that backtracking
236 # Triple quoted strings must be handled carefully so that backtracking
238 # in pagers works correctly. We need color terminators on _each_ line.
237 # in pagers works correctly. We need color terminators on _each_ line.
239 if linesep in toktext:
238 if linesep in toktext:
240 toktext = toktext.replace(linesep, '%s%s%s' %
239 toktext = toktext.replace(linesep, '%s%s%s' %
241 (colors.normal,linesep,color))
240 (colors.normal,linesep,color))
242
241
243 # send text
242 # send text
244 owrite('%s%s%s' % (color,toktext,colors.normal))
243 owrite('%s%s%s' % (color,toktext,colors.normal))
245
244
246 def main(argv=None):
245 def main(argv=None):
247 """Run as a command-line script: colorize a python file or stdin using ANSI
246 """Run as a command-line script: colorize a python file or stdin using ANSI
248 color escapes and print to stdout.
247 color escapes and print to stdout.
249
248
250 Inputs:
249 Inputs:
251
250
252 - argv(None): a list of strings like sys.argv[1:] giving the command-line
251 - argv(None): a list of strings like sys.argv[1:] giving the command-line
253 arguments. If None, use sys.argv[1:].
252 arguments. If None, use sys.argv[1:].
254 """
253 """
255
254
256 usage_msg = """%prog [options] [filename]
255 usage_msg = """%prog [options] [filename]
257
256
258 Colorize a python file or stdin using ANSI color escapes and print to stdout.
257 Colorize a python file or stdin using ANSI color escapes and print to stdout.
259 If no filename is given, or if filename is -, read standard input."""
258 If no filename is given, or if filename is -, read standard input."""
260
259
261 parser = optparse.OptionParser(usage=usage_msg)
260 parser = optparse.OptionParser(usage=usage_msg)
262 newopt = parser.add_option
261 newopt = parser.add_option
263 newopt('-s','--scheme',metavar='NAME',dest='scheme_name',action='store',
262 newopt('-s','--scheme',metavar='NAME',dest='scheme_name',action='store',
264 choices=['Linux','LightBG','NoColor'],default=_scheme_default,
263 choices=['Linux','LightBG','NoColor'],default=_scheme_default,
265 help="give the color scheme to use. Currently only 'Linux'\
264 help="give the color scheme to use. Currently only 'Linux'\
266 (default) and 'LightBG' and 'NoColor' are implemented (give without\
265 (default) and 'LightBG' and 'NoColor' are implemented (give without\
267 quotes)")
266 quotes)")
268
267
269 opts,args = parser.parse_args(argv)
268 opts,args = parser.parse_args(argv)
270
269
271 if len(args) > 1:
270 if len(args) > 1:
272 parser.error("you must give at most one filename.")
271 parser.error("you must give at most one filename.")
273
272
274 if len(args) == 0:
273 if len(args) == 0:
275 fname = '-' # no filename given; setup to read from stdin
274 fname = '-' # no filename given; setup to read from stdin
276 else:
275 else:
277 fname = args[0]
276 fname = args[0]
278
277
279 if fname == '-':
278 if fname == '-':
280 stream = sys.stdin
279 stream = sys.stdin
281 else:
280 else:
282 stream = file(fname)
281 stream = file(fname)
283
282
284 parser = Parser()
283 parser = Parser()
285
284
286 # we need nested try blocks because pre-2.5 python doesn't support unified
285 # we need nested try blocks because pre-2.5 python doesn't support unified
287 # try-except-finally
286 # try-except-finally
288 try:
287 try:
289 try:
288 try:
290 # write colorized version to stdout
289 # write colorized version to stdout
291 parser.format(stream.read(),scheme=opts.scheme_name)
290 parser.format(stream.read(),scheme=opts.scheme_name)
292 except IOError,msg:
291 except IOError,msg:
293 # if user reads through a pager and quits, don't print traceback
292 # if user reads through a pager and quits, don't print traceback
294 if msg.args != (32,'Broken pipe'):
293 if msg.args != (32,'Broken pipe'):
295 raise
294 raise
296 finally:
295 finally:
297 if stream is not sys.stdin:
296 if stream is not sys.stdin:
298 stream.close() # in case a non-handled exception happened above
297 stream.close() # in case a non-handled exception happened above
299
298
300 if __name__ == "__main__":
299 if __name__ == "__main__":
301 main()
300 main()
@@ -1,1238 +1,1233 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Shell classes.
2 """IPython Shell classes.
3
3
4 All the matplotlib support code was co-developed with John Hunter,
4 All the matplotlib support code was co-developed with John Hunter,
5 matplotlib's author.
5 matplotlib's author.
6
6 """
7 $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $"""
8
7
9 #*****************************************************************************
8 #*****************************************************************************
10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
9 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 #
10 #
12 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
14 #*****************************************************************************
13 #*****************************************************************************
15
14
16 from IPython import Release
17 __author__ = '%s <%s>' % Release.authors['Fernando']
18 __license__ = Release.license
19
20 # Code begins
15 # Code begins
21 # Stdlib imports
16 # Stdlib imports
22 import __builtin__
17 import __builtin__
23 import __main__
18 import __main__
24 import Queue
19 import Queue
25 import inspect
20 import inspect
26 import os
21 import os
27 import sys
22 import sys
28 import thread
23 import thread
29 import threading
24 import threading
30 import time
25 import time
31
26
32 from signal import signal, SIGINT
27 from signal import signal, SIGINT
33
28
34 try:
29 try:
35 import ctypes
30 import ctypes
36 HAS_CTYPES = True
31 HAS_CTYPES = True
37 except ImportError:
32 except ImportError:
38 HAS_CTYPES = False
33 HAS_CTYPES = False
39
34
40 # IPython imports
35 # IPython imports
41 import IPython
36 import IPython
42 from IPython import ultraTB, ipapi
37 from IPython import ultraTB, ipapi
43 from IPython.Magic import Magic
38 from IPython.Magic import Magic
44 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
39 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
45 from IPython.iplib import InteractiveShell
40 from IPython.iplib import InteractiveShell
46 from IPython.ipmaker import make_IPython
41 from IPython.ipmaker import make_IPython
47 from IPython.ipstruct import Struct
42 from IPython.ipstruct import Struct
48 from IPython.testing import decorators as testdec
43 from IPython.testing import decorators as testdec
49
44
50 # Globals
45 # Globals
51 # global flag to pass around information about Ctrl-C without exceptions
46 # global flag to pass around information about Ctrl-C without exceptions
52 KBINT = False
47 KBINT = False
53
48
54 # global flag to turn on/off Tk support.
49 # global flag to turn on/off Tk support.
55 USE_TK = False
50 USE_TK = False
56
51
57 # ID for the main thread, used for cross-thread exceptions
52 # ID for the main thread, used for cross-thread exceptions
58 MAIN_THREAD_ID = thread.get_ident()
53 MAIN_THREAD_ID = thread.get_ident()
59
54
60 # Tag when runcode() is active, for exception handling
55 # Tag when runcode() is active, for exception handling
61 CODE_RUN = None
56 CODE_RUN = None
62
57
63 # Default timeout for waiting for multithreaded shells (in seconds)
58 # Default timeout for waiting for multithreaded shells (in seconds)
64 GUI_TIMEOUT = 10
59 GUI_TIMEOUT = 10
65
60
66 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
67 # This class is trivial now, but I want to have it in to publish a clean
62 # This class is trivial now, but I want to have it in to publish a clean
68 # interface. Later when the internals are reorganized, code that uses this
63 # interface. Later when the internals are reorganized, code that uses this
69 # shouldn't have to change.
64 # shouldn't have to change.
70
65
71 class IPShell:
66 class IPShell:
72 """Create an IPython instance."""
67 """Create an IPython instance."""
73
68
74 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
69 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
75 debug=1,shell_class=InteractiveShell):
70 debug=1,shell_class=InteractiveShell):
76 self.IP = make_IPython(argv,user_ns=user_ns,
71 self.IP = make_IPython(argv,user_ns=user_ns,
77 user_global_ns=user_global_ns,
72 user_global_ns=user_global_ns,
78 debug=debug,shell_class=shell_class)
73 debug=debug,shell_class=shell_class)
79
74
80 def mainloop(self,sys_exit=0,banner=None):
75 def mainloop(self,sys_exit=0,banner=None):
81 self.IP.mainloop(banner)
76 self.IP.mainloop(banner)
82 if sys_exit:
77 if sys_exit:
83 sys.exit()
78 sys.exit()
84
79
85 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
86 def kill_embedded(self,parameter_s=''):
81 def kill_embedded(self,parameter_s=''):
87 """%kill_embedded : deactivate for good the current embedded IPython.
82 """%kill_embedded : deactivate for good the current embedded IPython.
88
83
89 This function (after asking for confirmation) sets an internal flag so that
84 This function (after asking for confirmation) sets an internal flag so that
90 an embedded IPython will never activate again. This is useful to
85 an embedded IPython will never activate again. This is useful to
91 permanently disable a shell that is being called inside a loop: once you've
86 permanently disable a shell that is being called inside a loop: once you've
92 figured out what you needed from it, you may then kill it and the program
87 figured out what you needed from it, you may then kill it and the program
93 will then continue to run without the interactive shell interfering again.
88 will then continue to run without the interactive shell interfering again.
94 """
89 """
95
90
96 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
91 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
97 "(y/n)? [y/N] ",'n')
92 "(y/n)? [y/N] ",'n')
98 if kill:
93 if kill:
99 self.shell.embedded_active = False
94 self.shell.embedded_active = False
100 print "This embedded IPython will not reactivate anymore once you exit."
95 print "This embedded IPython will not reactivate anymore once you exit."
101
96
102 class IPShellEmbed:
97 class IPShellEmbed:
103 """Allow embedding an IPython shell into a running program.
98 """Allow embedding an IPython shell into a running program.
104
99
105 Instances of this class are callable, with the __call__ method being an
100 Instances of this class are callable, with the __call__ method being an
106 alias to the embed() method of an InteractiveShell instance.
101 alias to the embed() method of an InteractiveShell instance.
107
102
108 Usage (see also the example-embed.py file for a running example):
103 Usage (see also the example-embed.py file for a running example):
109
104
110 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
105 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
111
106
112 - argv: list containing valid command-line options for IPython, as they
107 - argv: list containing valid command-line options for IPython, as they
113 would appear in sys.argv[1:].
108 would appear in sys.argv[1:].
114
109
115 For example, the following command-line options:
110 For example, the following command-line options:
116
111
117 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
112 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
118
113
119 would be passed in the argv list as:
114 would be passed in the argv list as:
120
115
121 ['-prompt_in1','Input <\\#>','-colors','LightBG']
116 ['-prompt_in1','Input <\\#>','-colors','LightBG']
122
117
123 - banner: string which gets printed every time the interpreter starts.
118 - banner: string which gets printed every time the interpreter starts.
124
119
125 - exit_msg: string which gets printed every time the interpreter exits.
120 - exit_msg: string which gets printed every time the interpreter exits.
126
121
127 - rc_override: a dict or Struct of configuration options such as those
122 - rc_override: a dict or Struct of configuration options such as those
128 used by IPython. These options are read from your ~/.ipython/ipythonrc
123 used by IPython. These options are read from your ~/.ipython/ipythonrc
129 file when the Shell object is created. Passing an explicit rc_override
124 file when the Shell object is created. Passing an explicit rc_override
130 dict with any options you want allows you to override those values at
125 dict with any options you want allows you to override those values at
131 creation time without having to modify the file. This way you can create
126 creation time without having to modify the file. This way you can create
132 embeddable instances configured in any way you want without editing any
127 embeddable instances configured in any way you want without editing any
133 global files (thus keeping your interactive IPython configuration
128 global files (thus keeping your interactive IPython configuration
134 unchanged).
129 unchanged).
135
130
136 Then the ipshell instance can be called anywhere inside your code:
131 Then the ipshell instance can be called anywhere inside your code:
137
132
138 ipshell(header='') -> Opens up an IPython shell.
133 ipshell(header='') -> Opens up an IPython shell.
139
134
140 - header: string printed by the IPython shell upon startup. This can let
135 - header: string printed by the IPython shell upon startup. This can let
141 you know where in your code you are when dropping into the shell. Note
136 you know where in your code you are when dropping into the shell. Note
142 that 'banner' gets prepended to all calls, so header is used for
137 that 'banner' gets prepended to all calls, so header is used for
143 location-specific information.
138 location-specific information.
144
139
145 For more details, see the __call__ method below.
140 For more details, see the __call__ method below.
146
141
147 When the IPython shell is exited with Ctrl-D, normal program execution
142 When the IPython shell is exited with Ctrl-D, normal program execution
148 resumes.
143 resumes.
149
144
150 This functionality was inspired by a posting on comp.lang.python by cmkl
145 This functionality was inspired by a posting on comp.lang.python by cmkl
151 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
146 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
152 by the IDL stop/continue commands."""
147 by the IDL stop/continue commands."""
153
148
154 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
149 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
155 user_ns=None):
150 user_ns=None):
156 """Note that argv here is a string, NOT a list."""
151 """Note that argv here is a string, NOT a list."""
157 self.set_banner(banner)
152 self.set_banner(banner)
158 self.set_exit_msg(exit_msg)
153 self.set_exit_msg(exit_msg)
159 self.set_dummy_mode(0)
154 self.set_dummy_mode(0)
160
155
161 # sys.displayhook is a global, we need to save the user's original
156 # sys.displayhook is a global, we need to save the user's original
162 # Don't rely on __displayhook__, as the user may have changed that.
157 # Don't rely on __displayhook__, as the user may have changed that.
163 self.sys_displayhook_ori = sys.displayhook
158 self.sys_displayhook_ori = sys.displayhook
164
159
165 # save readline completer status
160 # save readline completer status
166 try:
161 try:
167 #print 'Save completer',sys.ipcompleter # dbg
162 #print 'Save completer',sys.ipcompleter # dbg
168 self.sys_ipcompleter_ori = sys.ipcompleter
163 self.sys_ipcompleter_ori = sys.ipcompleter
169 except:
164 except:
170 pass # not nested with IPython
165 pass # not nested with IPython
171
166
172 self.IP = make_IPython(argv,rc_override=rc_override,
167 self.IP = make_IPython(argv,rc_override=rc_override,
173 embedded=True,
168 embedded=True,
174 user_ns=user_ns)
169 user_ns=user_ns)
175
170
176 ip = ipapi.IPApi(self.IP)
171 ip = ipapi.IPApi(self.IP)
177 ip.expose_magic("kill_embedded",kill_embedded)
172 ip.expose_magic("kill_embedded",kill_embedded)
178
173
179 # copy our own displayhook also
174 # copy our own displayhook also
180 self.sys_displayhook_embed = sys.displayhook
175 self.sys_displayhook_embed = sys.displayhook
181 # and leave the system's display hook clean
176 # and leave the system's display hook clean
182 sys.displayhook = self.sys_displayhook_ori
177 sys.displayhook = self.sys_displayhook_ori
183 # don't use the ipython crash handler so that user exceptions aren't
178 # don't use the ipython crash handler so that user exceptions aren't
184 # trapped
179 # trapped
185 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
180 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
186 mode = self.IP.rc.xmode,
181 mode = self.IP.rc.xmode,
187 call_pdb = self.IP.rc.pdb)
182 call_pdb = self.IP.rc.pdb)
188 self.restore_system_completer()
183 self.restore_system_completer()
189
184
190 def restore_system_completer(self):
185 def restore_system_completer(self):
191 """Restores the readline completer which was in place.
186 """Restores the readline completer which was in place.
192
187
193 This allows embedded IPython within IPython not to disrupt the
188 This allows embedded IPython within IPython not to disrupt the
194 parent's completion.
189 parent's completion.
195 """
190 """
196
191
197 try:
192 try:
198 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
193 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
199 sys.ipcompleter = self.sys_ipcompleter_ori
194 sys.ipcompleter = self.sys_ipcompleter_ori
200 except:
195 except:
201 pass
196 pass
202
197
203 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
198 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
204 """Activate the interactive interpreter.
199 """Activate the interactive interpreter.
205
200
206 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
201 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
207 the interpreter shell with the given local and global namespaces, and
202 the interpreter shell with the given local and global namespaces, and
208 optionally print a header string at startup.
203 optionally print a header string at startup.
209
204
210 The shell can be globally activated/deactivated using the
205 The shell can be globally activated/deactivated using the
211 set/get_dummy_mode methods. This allows you to turn off a shell used
206 set/get_dummy_mode methods. This allows you to turn off a shell used
212 for debugging globally.
207 for debugging globally.
213
208
214 However, *each* time you call the shell you can override the current
209 However, *each* time you call the shell you can override the current
215 state of dummy_mode with the optional keyword parameter 'dummy'. For
210 state of dummy_mode with the optional keyword parameter 'dummy'. For
216 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
211 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
217 can still have a specific call work by making it as IPShell(dummy=0).
212 can still have a specific call work by making it as IPShell(dummy=0).
218
213
219 The optional keyword parameter dummy controls whether the call
214 The optional keyword parameter dummy controls whether the call
220 actually does anything. """
215 actually does anything. """
221
216
222 # If the user has turned it off, go away
217 # If the user has turned it off, go away
223 if not self.IP.embedded_active:
218 if not self.IP.embedded_active:
224 return
219 return
225
220
226 # Normal exits from interactive mode set this flag, so the shell can't
221 # Normal exits from interactive mode set this flag, so the shell can't
227 # re-enter (it checks this variable at the start of interactive mode).
222 # re-enter (it checks this variable at the start of interactive mode).
228 self.IP.exit_now = False
223 self.IP.exit_now = False
229
224
230 # Allow the dummy parameter to override the global __dummy_mode
225 # Allow the dummy parameter to override the global __dummy_mode
231 if dummy or (dummy != 0 and self.__dummy_mode):
226 if dummy or (dummy != 0 and self.__dummy_mode):
232 return
227 return
233
228
234 # Set global subsystems (display,completions) to our values
229 # Set global subsystems (display,completions) to our values
235 sys.displayhook = self.sys_displayhook_embed
230 sys.displayhook = self.sys_displayhook_embed
236 if self.IP.has_readline:
231 if self.IP.has_readline:
237 self.IP.set_completer()
232 self.IP.set_completer()
238
233
239 if self.banner and header:
234 if self.banner and header:
240 format = '%s\n%s\n'
235 format = '%s\n%s\n'
241 else:
236 else:
242 format = '%s%s\n'
237 format = '%s%s\n'
243 banner = format % (self.banner,header)
238 banner = format % (self.banner,header)
244
239
245 # Call the embedding code with a stack depth of 1 so it can skip over
240 # Call the embedding code with a stack depth of 1 so it can skip over
246 # our call and get the original caller's namespaces.
241 # our call and get the original caller's namespaces.
247 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
242 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
248
243
249 if self.exit_msg:
244 if self.exit_msg:
250 print self.exit_msg
245 print self.exit_msg
251
246
252 # Restore global systems (display, completion)
247 # Restore global systems (display, completion)
253 sys.displayhook = self.sys_displayhook_ori
248 sys.displayhook = self.sys_displayhook_ori
254 self.restore_system_completer()
249 self.restore_system_completer()
255
250
256 def set_dummy_mode(self,dummy):
251 def set_dummy_mode(self,dummy):
257 """Sets the embeddable shell's dummy mode parameter.
252 """Sets the embeddable shell's dummy mode parameter.
258
253
259 set_dummy_mode(dummy): dummy = 0 or 1.
254 set_dummy_mode(dummy): dummy = 0 or 1.
260
255
261 This parameter is persistent and makes calls to the embeddable shell
256 This parameter is persistent and makes calls to the embeddable shell
262 silently return without performing any action. This allows you to
257 silently return without performing any action. This allows you to
263 globally activate or deactivate a shell you're using with a single call.
258 globally activate or deactivate a shell you're using with a single call.
264
259
265 If you need to manually"""
260 If you need to manually"""
266
261
267 if dummy not in [0,1,False,True]:
262 if dummy not in [0,1,False,True]:
268 raise ValueError,'dummy parameter must be boolean'
263 raise ValueError,'dummy parameter must be boolean'
269 self.__dummy_mode = dummy
264 self.__dummy_mode = dummy
270
265
271 def get_dummy_mode(self):
266 def get_dummy_mode(self):
272 """Return the current value of the dummy mode parameter.
267 """Return the current value of the dummy mode parameter.
273 """
268 """
274 return self.__dummy_mode
269 return self.__dummy_mode
275
270
276 def set_banner(self,banner):
271 def set_banner(self,banner):
277 """Sets the global banner.
272 """Sets the global banner.
278
273
279 This banner gets prepended to every header printed when the shell
274 This banner gets prepended to every header printed when the shell
280 instance is called."""
275 instance is called."""
281
276
282 self.banner = banner
277 self.banner = banner
283
278
284 def set_exit_msg(self,exit_msg):
279 def set_exit_msg(self,exit_msg):
285 """Sets the global exit_msg.
280 """Sets the global exit_msg.
286
281
287 This exit message gets printed upon exiting every time the embedded
282 This exit message gets printed upon exiting every time the embedded
288 shell is called. It is None by default. """
283 shell is called. It is None by default. """
289
284
290 self.exit_msg = exit_msg
285 self.exit_msg = exit_msg
291
286
292 #-----------------------------------------------------------------------------
287 #-----------------------------------------------------------------------------
293 if HAS_CTYPES:
288 if HAS_CTYPES:
294 # Add async exception support. Trick taken from:
289 # Add async exception support. Trick taken from:
295 # http://sebulba.wikispaces.com/recipe+thread2
290 # http://sebulba.wikispaces.com/recipe+thread2
296 def _async_raise(tid, exctype):
291 def _async_raise(tid, exctype):
297 """raises the exception, performs cleanup if needed"""
292 """raises the exception, performs cleanup if needed"""
298 if not inspect.isclass(exctype):
293 if not inspect.isclass(exctype):
299 raise TypeError("Only types can be raised (not instances)")
294 raise TypeError("Only types can be raised (not instances)")
300 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
295 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
301 ctypes.py_object(exctype))
296 ctypes.py_object(exctype))
302 if res == 0:
297 if res == 0:
303 raise ValueError("invalid thread id")
298 raise ValueError("invalid thread id")
304 elif res != 1:
299 elif res != 1:
305 # """if it returns a number greater than one, you're in trouble,
300 # """if it returns a number greater than one, you're in trouble,
306 # and you should call it again with exc=NULL to revert the effect"""
301 # and you should call it again with exc=NULL to revert the effect"""
307 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
302 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
308 raise SystemError("PyThreadState_SetAsyncExc failed")
303 raise SystemError("PyThreadState_SetAsyncExc failed")
309
304
310 def sigint_handler (signum,stack_frame):
305 def sigint_handler (signum,stack_frame):
311 """Sigint handler for threaded apps.
306 """Sigint handler for threaded apps.
312
307
313 This is a horrible hack to pass information about SIGINT _without_
308 This is a horrible hack to pass information about SIGINT _without_
314 using exceptions, since I haven't been able to properly manage
309 using exceptions, since I haven't been able to properly manage
315 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
310 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
316 done (or at least that's my understanding from a c.l.py thread where
311 done (or at least that's my understanding from a c.l.py thread where
317 this was discussed)."""
312 this was discussed)."""
318
313
319 global KBINT
314 global KBINT
320
315
321 if CODE_RUN:
316 if CODE_RUN:
322 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
317 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
323 else:
318 else:
324 KBINT = True
319 KBINT = True
325 print '\nKeyboardInterrupt - Press <Enter> to continue.',
320 print '\nKeyboardInterrupt - Press <Enter> to continue.',
326 Term.cout.flush()
321 Term.cout.flush()
327
322
328 else:
323 else:
329 def sigint_handler (signum,stack_frame):
324 def sigint_handler (signum,stack_frame):
330 """Sigint handler for threaded apps.
325 """Sigint handler for threaded apps.
331
326
332 This is a horrible hack to pass information about SIGINT _without_
327 This is a horrible hack to pass information about SIGINT _without_
333 using exceptions, since I haven't been able to properly manage
328 using exceptions, since I haven't been able to properly manage
334 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
329 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
335 done (or at least that's my understanding from a c.l.py thread where
330 done (or at least that's my understanding from a c.l.py thread where
336 this was discussed)."""
331 this was discussed)."""
337
332
338 global KBINT
333 global KBINT
339
334
340 print '\nKeyboardInterrupt - Press <Enter> to continue.',
335 print '\nKeyboardInterrupt - Press <Enter> to continue.',
341 Term.cout.flush()
336 Term.cout.flush()
342 # Set global flag so that runsource can know that Ctrl-C was hit
337 # Set global flag so that runsource can know that Ctrl-C was hit
343 KBINT = True
338 KBINT = True
344
339
345
340
346 class MTInteractiveShell(InteractiveShell):
341 class MTInteractiveShell(InteractiveShell):
347 """Simple multi-threaded shell."""
342 """Simple multi-threaded shell."""
348
343
349 # Threading strategy taken from:
344 # Threading strategy taken from:
350 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
345 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
351 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
346 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
352 # from the pygtk mailing list, to avoid lockups with system calls.
347 # from the pygtk mailing list, to avoid lockups with system calls.
353
348
354 # class attribute to indicate whether the class supports threads or not.
349 # class attribute to indicate whether the class supports threads or not.
355 # Subclasses with thread support should override this as needed.
350 # Subclasses with thread support should override this as needed.
356 isthreaded = True
351 isthreaded = True
357
352
358 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
353 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
359 user_ns=None,user_global_ns=None,banner2='',
354 user_ns=None,user_global_ns=None,banner2='',
360 gui_timeout=GUI_TIMEOUT,**kw):
355 gui_timeout=GUI_TIMEOUT,**kw):
361 """Similar to the normal InteractiveShell, but with threading control"""
356 """Similar to the normal InteractiveShell, but with threading control"""
362
357
363 InteractiveShell.__init__(self,name,usage,rc,user_ns,
358 InteractiveShell.__init__(self,name,usage,rc,user_ns,
364 user_global_ns,banner2)
359 user_global_ns,banner2)
365
360
366 # Timeout we wait for GUI thread
361 # Timeout we wait for GUI thread
367 self.gui_timeout = gui_timeout
362 self.gui_timeout = gui_timeout
368
363
369 # A queue to hold the code to be executed.
364 # A queue to hold the code to be executed.
370 self.code_queue = Queue.Queue()
365 self.code_queue = Queue.Queue()
371
366
372 # Stuff to do at closing time
367 # Stuff to do at closing time
373 self._kill = None
368 self._kill = None
374 on_kill = kw.get('on_kill', [])
369 on_kill = kw.get('on_kill', [])
375 # Check that all things to kill are callable:
370 # Check that all things to kill are callable:
376 for t in on_kill:
371 for t in on_kill:
377 if not callable(t):
372 if not callable(t):
378 raise TypeError,'on_kill must be a list of callables'
373 raise TypeError,'on_kill must be a list of callables'
379 self.on_kill = on_kill
374 self.on_kill = on_kill
380 # thread identity of the "worker thread" (that may execute code directly)
375 # thread identity of the "worker thread" (that may execute code directly)
381 self.worker_ident = None
376 self.worker_ident = None
382
377
383 def runsource(self, source, filename="<input>", symbol="single"):
378 def runsource(self, source, filename="<input>", symbol="single"):
384 """Compile and run some source in the interpreter.
379 """Compile and run some source in the interpreter.
385
380
386 Modified version of code.py's runsource(), to handle threading issues.
381 Modified version of code.py's runsource(), to handle threading issues.
387 See the original for full docstring details."""
382 See the original for full docstring details."""
388
383
389 global KBINT
384 global KBINT
390
385
391 # If Ctrl-C was typed, we reset the flag and return right away
386 # If Ctrl-C was typed, we reset the flag and return right away
392 if KBINT:
387 if KBINT:
393 KBINT = False
388 KBINT = False
394 return False
389 return False
395
390
396 if self._kill:
391 if self._kill:
397 # can't queue new code if we are being killed
392 # can't queue new code if we are being killed
398 return True
393 return True
399
394
400 try:
395 try:
401 code = self.compile(source, filename, symbol)
396 code = self.compile(source, filename, symbol)
402 except (OverflowError, SyntaxError, ValueError):
397 except (OverflowError, SyntaxError, ValueError):
403 # Case 1
398 # Case 1
404 self.showsyntaxerror(filename)
399 self.showsyntaxerror(filename)
405 return False
400 return False
406
401
407 if code is None:
402 if code is None:
408 # Case 2
403 # Case 2
409 return True
404 return True
410
405
411 # shortcut - if we are in worker thread, or the worker thread is not
406 # shortcut - if we are in worker thread, or the worker thread is not
412 # running, execute directly (to allow recursion and prevent deadlock if
407 # running, execute directly (to allow recursion and prevent deadlock if
413 # code is run early in IPython construction)
408 # code is run early in IPython construction)
414
409
415 if (self.worker_ident is None
410 if (self.worker_ident is None
416 or self.worker_ident == thread.get_ident() ):
411 or self.worker_ident == thread.get_ident() ):
417 InteractiveShell.runcode(self,code)
412 InteractiveShell.runcode(self,code)
418 return
413 return
419
414
420 # Case 3
415 # Case 3
421 # Store code in queue, so the execution thread can handle it.
416 # Store code in queue, so the execution thread can handle it.
422
417
423 completed_ev, received_ev = threading.Event(), threading.Event()
418 completed_ev, received_ev = threading.Event(), threading.Event()
424
419
425 self.code_queue.put((code,completed_ev, received_ev))
420 self.code_queue.put((code,completed_ev, received_ev))
426 # first make sure the message was received, with timeout
421 # first make sure the message was received, with timeout
427 received_ev.wait(self.gui_timeout)
422 received_ev.wait(self.gui_timeout)
428 if not received_ev.isSet():
423 if not received_ev.isSet():
429 # the mainloop is dead, start executing code directly
424 # the mainloop is dead, start executing code directly
430 print "Warning: Timeout for mainloop thread exceeded"
425 print "Warning: Timeout for mainloop thread exceeded"
431 print "switching to nonthreaded mode (until mainloop wakes up again)"
426 print "switching to nonthreaded mode (until mainloop wakes up again)"
432 self.worker_ident = None
427 self.worker_ident = None
433 else:
428 else:
434 completed_ev.wait()
429 completed_ev.wait()
435 return False
430 return False
436
431
437 def runcode(self):
432 def runcode(self):
438 """Execute a code object.
433 """Execute a code object.
439
434
440 Multithreaded wrapper around IPython's runcode()."""
435 Multithreaded wrapper around IPython's runcode()."""
441
436
442 global CODE_RUN
437 global CODE_RUN
443
438
444 # we are in worker thread, stash out the id for runsource()
439 # we are in worker thread, stash out the id for runsource()
445 self.worker_ident = thread.get_ident()
440 self.worker_ident = thread.get_ident()
446
441
447 if self._kill:
442 if self._kill:
448 print >>Term.cout, 'Closing threads...',
443 print >>Term.cout, 'Closing threads...',
449 Term.cout.flush()
444 Term.cout.flush()
450 for tokill in self.on_kill:
445 for tokill in self.on_kill:
451 tokill()
446 tokill()
452 print >>Term.cout, 'Done.'
447 print >>Term.cout, 'Done.'
453 # allow kill() to return
448 # allow kill() to return
454 self._kill.set()
449 self._kill.set()
455 return True
450 return True
456
451
457 # Install sigint handler. We do it every time to ensure that if user
452 # Install sigint handler. We do it every time to ensure that if user
458 # code modifies it, we restore our own handling.
453 # code modifies it, we restore our own handling.
459 try:
454 try:
460 signal(SIGINT,sigint_handler)
455 signal(SIGINT,sigint_handler)
461 except SystemError:
456 except SystemError:
462 # This happens under Windows, which seems to have all sorts
457 # This happens under Windows, which seems to have all sorts
463 # of problems with signal handling. Oh well...
458 # of problems with signal handling. Oh well...
464 pass
459 pass
465
460
466 # Flush queue of pending code by calling the run methood of the parent
461 # Flush queue of pending code by calling the run methood of the parent
467 # class with all items which may be in the queue.
462 # class with all items which may be in the queue.
468 code_to_run = None
463 code_to_run = None
469 while 1:
464 while 1:
470 try:
465 try:
471 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
466 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
472 except Queue.Empty:
467 except Queue.Empty:
473 break
468 break
474 received_ev.set()
469 received_ev.set()
475
470
476 # Exceptions need to be raised differently depending on which
471 # Exceptions need to be raised differently depending on which
477 # thread is active. This convoluted try/except is only there to
472 # thread is active. This convoluted try/except is only there to
478 # protect against asynchronous exceptions, to ensure that a KBINT
473 # protect against asynchronous exceptions, to ensure that a KBINT
479 # at the wrong time doesn't deadlock everything. The global
474 # at the wrong time doesn't deadlock everything. The global
480 # CODE_TO_RUN is set to true/false as close as possible to the
475 # CODE_TO_RUN is set to true/false as close as possible to the
481 # runcode() call, so that the KBINT handler is correctly informed.
476 # runcode() call, so that the KBINT handler is correctly informed.
482 try:
477 try:
483 try:
478 try:
484 CODE_RUN = True
479 CODE_RUN = True
485 InteractiveShell.runcode(self,code_to_run)
480 InteractiveShell.runcode(self,code_to_run)
486 except KeyboardInterrupt:
481 except KeyboardInterrupt:
487 print "Keyboard interrupted in mainloop"
482 print "Keyboard interrupted in mainloop"
488 while not self.code_queue.empty():
483 while not self.code_queue.empty():
489 code, ev1,ev2 = self.code_queue.get_nowait()
484 code, ev1,ev2 = self.code_queue.get_nowait()
490 ev1.set()
485 ev1.set()
491 ev2.set()
486 ev2.set()
492 break
487 break
493 finally:
488 finally:
494 CODE_RUN = False
489 CODE_RUN = False
495 # allow runsource() return from wait
490 # allow runsource() return from wait
496 completed_ev.set()
491 completed_ev.set()
497
492
498
493
499 # This MUST return true for gtk threading to work
494 # This MUST return true for gtk threading to work
500 return True
495 return True
501
496
502 def kill(self):
497 def kill(self):
503 """Kill the thread, returning when it has been shut down."""
498 """Kill the thread, returning when it has been shut down."""
504 self._kill = threading.Event()
499 self._kill = threading.Event()
505 self._kill.wait()
500 self._kill.wait()
506
501
507 class MatplotlibShellBase:
502 class MatplotlibShellBase:
508 """Mixin class to provide the necessary modifications to regular IPython
503 """Mixin class to provide the necessary modifications to regular IPython
509 shell classes for matplotlib support.
504 shell classes for matplotlib support.
510
505
511 Given Python's MRO, this should be used as the FIRST class in the
506 Given Python's MRO, this should be used as the FIRST class in the
512 inheritance hierarchy, so that it overrides the relevant methods."""
507 inheritance hierarchy, so that it overrides the relevant methods."""
513
508
514 def _matplotlib_config(self,name,user_ns,user_global_ns=None):
509 def _matplotlib_config(self,name,user_ns,user_global_ns=None):
515 """Return items needed to setup the user's shell with matplotlib"""
510 """Return items needed to setup the user's shell with matplotlib"""
516
511
517 # Initialize matplotlib to interactive mode always
512 # Initialize matplotlib to interactive mode always
518 import matplotlib
513 import matplotlib
519 from matplotlib import backends
514 from matplotlib import backends
520 matplotlib.interactive(True)
515 matplotlib.interactive(True)
521
516
522 def use(arg):
517 def use(arg):
523 """IPython wrapper for matplotlib's backend switcher.
518 """IPython wrapper for matplotlib's backend switcher.
524
519
525 In interactive use, we can not allow switching to a different
520 In interactive use, we can not allow switching to a different
526 interactive backend, since thread conflicts will most likely crash
521 interactive backend, since thread conflicts will most likely crash
527 the python interpreter. This routine does a safety check first,
522 the python interpreter. This routine does a safety check first,
528 and refuses to perform a dangerous switch. It still allows
523 and refuses to perform a dangerous switch. It still allows
529 switching to non-interactive backends."""
524 switching to non-interactive backends."""
530
525
531 if arg in backends.interactive_bk and arg != self.mpl_backend:
526 if arg in backends.interactive_bk and arg != self.mpl_backend:
532 m=('invalid matplotlib backend switch.\n'
527 m=('invalid matplotlib backend switch.\n'
533 'This script attempted to switch to the interactive '
528 'This script attempted to switch to the interactive '
534 'backend: `%s`\n'
529 'backend: `%s`\n'
535 'Your current choice of interactive backend is: `%s`\n\n'
530 'Your current choice of interactive backend is: `%s`\n\n'
536 'Switching interactive matplotlib backends at runtime\n'
531 'Switching interactive matplotlib backends at runtime\n'
537 'would crash the python interpreter, '
532 'would crash the python interpreter, '
538 'and IPython has blocked it.\n\n'
533 'and IPython has blocked it.\n\n'
539 'You need to either change your choice of matplotlib backend\n'
534 'You need to either change your choice of matplotlib backend\n'
540 'by editing your .matplotlibrc file, or run this script as a \n'
535 'by editing your .matplotlibrc file, or run this script as a \n'
541 'standalone file from the command line, not using IPython.\n' %
536 'standalone file from the command line, not using IPython.\n' %
542 (arg,self.mpl_backend) )
537 (arg,self.mpl_backend) )
543 raise RuntimeError, m
538 raise RuntimeError, m
544 else:
539 else:
545 self.mpl_use(arg)
540 self.mpl_use(arg)
546 self.mpl_use._called = True
541 self.mpl_use._called = True
547
542
548 self.matplotlib = matplotlib
543 self.matplotlib = matplotlib
549 self.mpl_backend = matplotlib.rcParams['backend']
544 self.mpl_backend = matplotlib.rcParams['backend']
550
545
551 # we also need to block switching of interactive backends by use()
546 # we also need to block switching of interactive backends by use()
552 self.mpl_use = matplotlib.use
547 self.mpl_use = matplotlib.use
553 self.mpl_use._called = False
548 self.mpl_use._called = False
554 # overwrite the original matplotlib.use with our wrapper
549 # overwrite the original matplotlib.use with our wrapper
555 matplotlib.use = use
550 matplotlib.use = use
556
551
557 # This must be imported last in the matplotlib series, after
552 # This must be imported last in the matplotlib series, after
558 # backend/interactivity choices have been made
553 # backend/interactivity choices have been made
559 import matplotlib.pylab as pylab
554 import matplotlib.pylab as pylab
560 self.pylab = pylab
555 self.pylab = pylab
561
556
562 self.pylab.show._needmain = False
557 self.pylab.show._needmain = False
563 # We need to detect at runtime whether show() is called by the user.
558 # We need to detect at runtime whether show() is called by the user.
564 # For this, we wrap it into a decorator which adds a 'called' flag.
559 # For this, we wrap it into a decorator which adds a 'called' flag.
565 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
560 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
566
561
567 # Build a user namespace initialized with matplotlib/matlab features.
562 # Build a user namespace initialized with matplotlib/matlab features.
568 user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns,
563 user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns,
569 user_global_ns)
564 user_global_ns)
570
565
571 # Import numpy as np/pyplot as plt are conventions we're trying to
566 # Import numpy as np/pyplot as plt are conventions we're trying to
572 # somewhat standardize on. Making them available to users by default
567 # somewhat standardize on. Making them available to users by default
573 # will greatly help this.
568 # will greatly help this.
574 exec ("import numpy\n"
569 exec ("import numpy\n"
575 "import numpy as np\n"
570 "import numpy as np\n"
576 "import matplotlib\n"
571 "import matplotlib\n"
577 "import matplotlib.pylab as pylab\n"
572 "import matplotlib.pylab as pylab\n"
578 "try:\n"
573 "try:\n"
579 " import matplotlib.pyplot as plt\n"
574 " import matplotlib.pyplot as plt\n"
580 "except ImportError:\n"
575 "except ImportError:\n"
581 " pass\n"
576 " pass\n"
582 ) in user_ns
577 ) in user_ns
583
578
584 # Build matplotlib info banner
579 # Build matplotlib info banner
585 b="""
580 b="""
586 Welcome to pylab, a matplotlib-based Python environment.
581 Welcome to pylab, a matplotlib-based Python environment.
587 For more information, type 'help(pylab)'.
582 For more information, type 'help(pylab)'.
588 """
583 """
589 return user_ns,user_global_ns,b
584 return user_ns,user_global_ns,b
590
585
591 def mplot_exec(self,fname,*where,**kw):
586 def mplot_exec(self,fname,*where,**kw):
592 """Execute a matplotlib script.
587 """Execute a matplotlib script.
593
588
594 This is a call to execfile(), but wrapped in safeties to properly
589 This is a call to execfile(), but wrapped in safeties to properly
595 handle interactive rendering and backend switching."""
590 handle interactive rendering and backend switching."""
596
591
597 #print '*** Matplotlib runner ***' # dbg
592 #print '*** Matplotlib runner ***' # dbg
598 # turn off rendering until end of script
593 # turn off rendering until end of script
599 isInteractive = self.matplotlib.rcParams['interactive']
594 isInteractive = self.matplotlib.rcParams['interactive']
600 self.matplotlib.interactive(False)
595 self.matplotlib.interactive(False)
601 self.safe_execfile(fname,*where,**kw)
596 self.safe_execfile(fname,*where,**kw)
602 self.matplotlib.interactive(isInteractive)
597 self.matplotlib.interactive(isInteractive)
603 # make rendering call now, if the user tried to do it
598 # make rendering call now, if the user tried to do it
604 if self.pylab.draw_if_interactive.called:
599 if self.pylab.draw_if_interactive.called:
605 self.pylab.draw()
600 self.pylab.draw()
606 self.pylab.draw_if_interactive.called = False
601 self.pylab.draw_if_interactive.called = False
607
602
608 # if a backend switch was performed, reverse it now
603 # if a backend switch was performed, reverse it now
609 if self.mpl_use._called:
604 if self.mpl_use._called:
610 self.matplotlib.rcParams['backend'] = self.mpl_backend
605 self.matplotlib.rcParams['backend'] = self.mpl_backend
611
606
612 @testdec.skip_doctest
607 @testdec.skip_doctest
613 def magic_run(self,parameter_s=''):
608 def magic_run(self,parameter_s=''):
614 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
609 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
615
610
616 # Fix the docstring so users see the original as well
611 # Fix the docstring so users see the original as well
617 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
612 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
618 "\n *** Modified %run for Matplotlib,"
613 "\n *** Modified %run for Matplotlib,"
619 " with proper interactive handling ***")
614 " with proper interactive handling ***")
620
615
621 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
616 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
622 # and multithreaded. Note that these are meant for internal use, the IPShell*
617 # and multithreaded. Note that these are meant for internal use, the IPShell*
623 # classes below are the ones meant for public consumption.
618 # classes below are the ones meant for public consumption.
624
619
625 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
620 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
626 """Single-threaded shell with matplotlib support."""
621 """Single-threaded shell with matplotlib support."""
627
622
628 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
623 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
629 user_ns=None,user_global_ns=None,**kw):
624 user_ns=None,user_global_ns=None,**kw):
630 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
625 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
631 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
626 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
632 banner2=b2,**kw)
627 banner2=b2,**kw)
633
628
634 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
629 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
635 """Multi-threaded shell with matplotlib support."""
630 """Multi-threaded shell with matplotlib support."""
636
631
637 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
632 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
638 user_ns=None,user_global_ns=None, **kw):
633 user_ns=None,user_global_ns=None, **kw):
639 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
634 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
640 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
635 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
641 banner2=b2,**kw)
636 banner2=b2,**kw)
642
637
643 #-----------------------------------------------------------------------------
638 #-----------------------------------------------------------------------------
644 # Utility functions for the different GUI enabled IPShell* classes.
639 # Utility functions for the different GUI enabled IPShell* classes.
645
640
646 def get_tk():
641 def get_tk():
647 """Tries to import Tkinter and returns a withdrawn Tkinter root
642 """Tries to import Tkinter and returns a withdrawn Tkinter root
648 window. If Tkinter is already imported or not available, this
643 window. If Tkinter is already imported or not available, this
649 returns None. This function calls `hijack_tk` underneath.
644 returns None. This function calls `hijack_tk` underneath.
650 """
645 """
651 if not USE_TK or sys.modules.has_key('Tkinter'):
646 if not USE_TK or sys.modules.has_key('Tkinter'):
652 return None
647 return None
653 else:
648 else:
654 try:
649 try:
655 import Tkinter
650 import Tkinter
656 except ImportError:
651 except ImportError:
657 return None
652 return None
658 else:
653 else:
659 hijack_tk()
654 hijack_tk()
660 r = Tkinter.Tk()
655 r = Tkinter.Tk()
661 r.withdraw()
656 r.withdraw()
662 return r
657 return r
663
658
664 def hijack_tk():
659 def hijack_tk():
665 """Modifies Tkinter's mainloop with a dummy so when a module calls
660 """Modifies Tkinter's mainloop with a dummy so when a module calls
666 mainloop, it does not block.
661 mainloop, it does not block.
667
662
668 """
663 """
669 def misc_mainloop(self, n=0):
664 def misc_mainloop(self, n=0):
670 pass
665 pass
671 def tkinter_mainloop(n=0):
666 def tkinter_mainloop(n=0):
672 pass
667 pass
673
668
674 import Tkinter
669 import Tkinter
675 Tkinter.Misc.mainloop = misc_mainloop
670 Tkinter.Misc.mainloop = misc_mainloop
676 Tkinter.mainloop = tkinter_mainloop
671 Tkinter.mainloop = tkinter_mainloop
677
672
678 def update_tk(tk):
673 def update_tk(tk):
679 """Updates the Tkinter event loop. This is typically called from
674 """Updates the Tkinter event loop. This is typically called from
680 the respective WX or GTK mainloops.
675 the respective WX or GTK mainloops.
681 """
676 """
682 if tk:
677 if tk:
683 tk.update()
678 tk.update()
684
679
685 def hijack_wx():
680 def hijack_wx():
686 """Modifies wxPython's MainLoop with a dummy so user code does not
681 """Modifies wxPython's MainLoop with a dummy so user code does not
687 block IPython. The hijacked mainloop function is returned.
682 block IPython. The hijacked mainloop function is returned.
688 """
683 """
689 def dummy_mainloop(*args, **kw):
684 def dummy_mainloop(*args, **kw):
690 pass
685 pass
691
686
692 try:
687 try:
693 import wx
688 import wx
694 except ImportError:
689 except ImportError:
695 # For very old versions of WX
690 # For very old versions of WX
696 import wxPython as wx
691 import wxPython as wx
697
692
698 ver = wx.__version__
693 ver = wx.__version__
699 orig_mainloop = None
694 orig_mainloop = None
700 if ver[:3] >= '2.5':
695 if ver[:3] >= '2.5':
701 import wx
696 import wx
702 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
697 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
703 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
698 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
704 else: raise AttributeError('Could not find wx core module')
699 else: raise AttributeError('Could not find wx core module')
705 orig_mainloop = core.PyApp_MainLoop
700 orig_mainloop = core.PyApp_MainLoop
706 core.PyApp_MainLoop = dummy_mainloop
701 core.PyApp_MainLoop = dummy_mainloop
707 elif ver[:3] == '2.4':
702 elif ver[:3] == '2.4':
708 orig_mainloop = wx.wxc.wxPyApp_MainLoop
703 orig_mainloop = wx.wxc.wxPyApp_MainLoop
709 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
704 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
710 else:
705 else:
711 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
706 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
712 return orig_mainloop
707 return orig_mainloop
713
708
714 def hijack_gtk():
709 def hijack_gtk():
715 """Modifies pyGTK's mainloop with a dummy so user code does not
710 """Modifies pyGTK's mainloop with a dummy so user code does not
716 block IPython. This function returns the original `gtk.mainloop`
711 block IPython. This function returns the original `gtk.mainloop`
717 function that has been hijacked.
712 function that has been hijacked.
718 """
713 """
719 def dummy_mainloop(*args, **kw):
714 def dummy_mainloop(*args, **kw):
720 pass
715 pass
721 import gtk
716 import gtk
722 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
717 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
723 else: orig_mainloop = gtk.mainloop
718 else: orig_mainloop = gtk.mainloop
724 gtk.mainloop = dummy_mainloop
719 gtk.mainloop = dummy_mainloop
725 gtk.main = dummy_mainloop
720 gtk.main = dummy_mainloop
726 return orig_mainloop
721 return orig_mainloop
727
722
728 def hijack_qt():
723 def hijack_qt():
729 """Modifies PyQt's mainloop with a dummy so user code does not
724 """Modifies PyQt's mainloop with a dummy so user code does not
730 block IPython. This function returns the original
725 block IPython. This function returns the original
731 `qt.qApp.exec_loop` function that has been hijacked.
726 `qt.qApp.exec_loop` function that has been hijacked.
732 """
727 """
733 def dummy_mainloop(*args, **kw):
728 def dummy_mainloop(*args, **kw):
734 pass
729 pass
735 import qt
730 import qt
736 orig_mainloop = qt.qApp.exec_loop
731 orig_mainloop = qt.qApp.exec_loop
737 qt.qApp.exec_loop = dummy_mainloop
732 qt.qApp.exec_loop = dummy_mainloop
738 qt.QApplication.exec_loop = dummy_mainloop
733 qt.QApplication.exec_loop = dummy_mainloop
739 return orig_mainloop
734 return orig_mainloop
740
735
741 def hijack_qt4():
736 def hijack_qt4():
742 """Modifies PyQt4's mainloop with a dummy so user code does not
737 """Modifies PyQt4's mainloop with a dummy so user code does not
743 block IPython. This function returns the original
738 block IPython. This function returns the original
744 `QtGui.qApp.exec_` function that has been hijacked.
739 `QtGui.qApp.exec_` function that has been hijacked.
745 """
740 """
746 def dummy_mainloop(*args, **kw):
741 def dummy_mainloop(*args, **kw):
747 pass
742 pass
748 from PyQt4 import QtGui, QtCore
743 from PyQt4 import QtGui, QtCore
749 orig_mainloop = QtGui.qApp.exec_
744 orig_mainloop = QtGui.qApp.exec_
750 QtGui.qApp.exec_ = dummy_mainloop
745 QtGui.qApp.exec_ = dummy_mainloop
751 QtGui.QApplication.exec_ = dummy_mainloop
746 QtGui.QApplication.exec_ = dummy_mainloop
752 QtCore.QCoreApplication.exec_ = dummy_mainloop
747 QtCore.QCoreApplication.exec_ = dummy_mainloop
753 return orig_mainloop
748 return orig_mainloop
754
749
755 #-----------------------------------------------------------------------------
750 #-----------------------------------------------------------------------------
756 # The IPShell* classes below are the ones meant to be run by external code as
751 # The IPShell* classes below are the ones meant to be run by external code as
757 # IPython instances. Note that unless a specific threading strategy is
752 # IPython instances. Note that unless a specific threading strategy is
758 # desired, the factory function start() below should be used instead (it
753 # desired, the factory function start() below should be used instead (it
759 # selects the proper threaded class).
754 # selects the proper threaded class).
760
755
761 class IPThread(threading.Thread):
756 class IPThread(threading.Thread):
762 def run(self):
757 def run(self):
763 self.IP.mainloop(self._banner)
758 self.IP.mainloop(self._banner)
764 self.IP.kill()
759 self.IP.kill()
765
760
766 class IPShellGTK(IPThread):
761 class IPShellGTK(IPThread):
767 """Run a gtk mainloop() in a separate thread.
762 """Run a gtk mainloop() in a separate thread.
768
763
769 Python commands can be passed to the thread where they will be executed.
764 Python commands can be passed to the thread where they will be executed.
770 This is implemented by periodically checking for passed code using a
765 This is implemented by periodically checking for passed code using a
771 GTK timeout callback."""
766 GTK timeout callback."""
772
767
773 TIMEOUT = 100 # Millisecond interval between timeouts.
768 TIMEOUT = 100 # Millisecond interval between timeouts.
774
769
775 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
770 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
776 debug=1,shell_class=MTInteractiveShell):
771 debug=1,shell_class=MTInteractiveShell):
777
772
778 import gtk
773 import gtk
779
774
780 self.gtk = gtk
775 self.gtk = gtk
781 self.gtk_mainloop = hijack_gtk()
776 self.gtk_mainloop = hijack_gtk()
782
777
783 # Allows us to use both Tk and GTK.
778 # Allows us to use both Tk and GTK.
784 self.tk = get_tk()
779 self.tk = get_tk()
785
780
786 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
781 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
787 else: mainquit = self.gtk.mainquit
782 else: mainquit = self.gtk.mainquit
788
783
789 self.IP = make_IPython(argv,user_ns=user_ns,
784 self.IP = make_IPython(argv,user_ns=user_ns,
790 user_global_ns=user_global_ns,
785 user_global_ns=user_global_ns,
791 debug=debug,
786 debug=debug,
792 shell_class=shell_class,
787 shell_class=shell_class,
793 on_kill=[mainquit])
788 on_kill=[mainquit])
794
789
795 # HACK: slot for banner in self; it will be passed to the mainloop
790 # HACK: slot for banner in self; it will be passed to the mainloop
796 # method only and .run() needs it. The actual value will be set by
791 # method only and .run() needs it. The actual value will be set by
797 # .mainloop().
792 # .mainloop().
798 self._banner = None
793 self._banner = None
799
794
800 threading.Thread.__init__(self)
795 threading.Thread.__init__(self)
801
796
802 def mainloop(self,sys_exit=0,banner=None):
797 def mainloop(self,sys_exit=0,banner=None):
803
798
804 self._banner = banner
799 self._banner = banner
805
800
806 if self.gtk.pygtk_version >= (2,4,0):
801 if self.gtk.pygtk_version >= (2,4,0):
807 import gobject
802 import gobject
808 gobject.idle_add(self.on_timer)
803 gobject.idle_add(self.on_timer)
809 else:
804 else:
810 self.gtk.idle_add(self.on_timer)
805 self.gtk.idle_add(self.on_timer)
811
806
812 if sys.platform != 'win32':
807 if sys.platform != 'win32':
813 try:
808 try:
814 if self.gtk.gtk_version[0] >= 2:
809 if self.gtk.gtk_version[0] >= 2:
815 self.gtk.gdk.threads_init()
810 self.gtk.gdk.threads_init()
816 except AttributeError:
811 except AttributeError:
817 pass
812 pass
818 except RuntimeError:
813 except RuntimeError:
819 error('Your pyGTK likely has not been compiled with '
814 error('Your pyGTK likely has not been compiled with '
820 'threading support.\n'
815 'threading support.\n'
821 'The exception printout is below.\n'
816 'The exception printout is below.\n'
822 'You can either rebuild pyGTK with threads, or '
817 'You can either rebuild pyGTK with threads, or '
823 'try using \n'
818 'try using \n'
824 'matplotlib with a different backend (like Tk or WX).\n'
819 'matplotlib with a different backend (like Tk or WX).\n'
825 'Note that matplotlib will most likely not work in its '
820 'Note that matplotlib will most likely not work in its '
826 'current state!')
821 'current state!')
827 self.IP.InteractiveTB()
822 self.IP.InteractiveTB()
828
823
829 self.start()
824 self.start()
830 self.gtk.gdk.threads_enter()
825 self.gtk.gdk.threads_enter()
831 self.gtk_mainloop()
826 self.gtk_mainloop()
832 self.gtk.gdk.threads_leave()
827 self.gtk.gdk.threads_leave()
833 self.join()
828 self.join()
834
829
835 def on_timer(self):
830 def on_timer(self):
836 """Called when GTK is idle.
831 """Called when GTK is idle.
837
832
838 Must return True always, otherwise GTK stops calling it"""
833 Must return True always, otherwise GTK stops calling it"""
839
834
840 update_tk(self.tk)
835 update_tk(self.tk)
841 self.IP.runcode()
836 self.IP.runcode()
842 time.sleep(0.01)
837 time.sleep(0.01)
843 return True
838 return True
844
839
845
840
846 class IPShellWX(IPThread):
841 class IPShellWX(IPThread):
847 """Run a wx mainloop() in a separate thread.
842 """Run a wx mainloop() in a separate thread.
848
843
849 Python commands can be passed to the thread where they will be executed.
844 Python commands can be passed to the thread where they will be executed.
850 This is implemented by periodically checking for passed code using a
845 This is implemented by periodically checking for passed code using a
851 GTK timeout callback."""
846 GTK timeout callback."""
852
847
853 TIMEOUT = 100 # Millisecond interval between timeouts.
848 TIMEOUT = 100 # Millisecond interval between timeouts.
854
849
855 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
850 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
856 debug=1,shell_class=MTInteractiveShell):
851 debug=1,shell_class=MTInteractiveShell):
857
852
858 self.IP = make_IPython(argv,user_ns=user_ns,
853 self.IP = make_IPython(argv,user_ns=user_ns,
859 user_global_ns=user_global_ns,
854 user_global_ns=user_global_ns,
860 debug=debug,
855 debug=debug,
861 shell_class=shell_class,
856 shell_class=shell_class,
862 on_kill=[self.wxexit])
857 on_kill=[self.wxexit])
863
858
864 wantedwxversion=self.IP.rc.wxversion
859 wantedwxversion=self.IP.rc.wxversion
865 if wantedwxversion!="0":
860 if wantedwxversion!="0":
866 try:
861 try:
867 import wxversion
862 import wxversion
868 except ImportError:
863 except ImportError:
869 error('The wxversion module is needed for WX version selection')
864 error('The wxversion module is needed for WX version selection')
870 else:
865 else:
871 try:
866 try:
872 wxversion.select(wantedwxversion)
867 wxversion.select(wantedwxversion)
873 except:
868 except:
874 self.IP.InteractiveTB()
869 self.IP.InteractiveTB()
875 error('Requested wxPython version %s could not be loaded' %
870 error('Requested wxPython version %s could not be loaded' %
876 wantedwxversion)
871 wantedwxversion)
877
872
878 import wx
873 import wx
879
874
880 threading.Thread.__init__(self)
875 threading.Thread.__init__(self)
881 self.wx = wx
876 self.wx = wx
882 self.wx_mainloop = hijack_wx()
877 self.wx_mainloop = hijack_wx()
883
878
884 # Allows us to use both Tk and GTK.
879 # Allows us to use both Tk and GTK.
885 self.tk = get_tk()
880 self.tk = get_tk()
886
881
887 # HACK: slot for banner in self; it will be passed to the mainloop
882 # HACK: slot for banner in self; it will be passed to the mainloop
888 # method only and .run() needs it. The actual value will be set by
883 # method only and .run() needs it. The actual value will be set by
889 # .mainloop().
884 # .mainloop().
890 self._banner = None
885 self._banner = None
891
886
892 self.app = None
887 self.app = None
893
888
894 def wxexit(self, *args):
889 def wxexit(self, *args):
895 if self.app is not None:
890 if self.app is not None:
896 self.app.agent.timer.Stop()
891 self.app.agent.timer.Stop()
897 self.app.ExitMainLoop()
892 self.app.ExitMainLoop()
898
893
899 def mainloop(self,sys_exit=0,banner=None):
894 def mainloop(self,sys_exit=0,banner=None):
900
895
901 self._banner = banner
896 self._banner = banner
902
897
903 self.start()
898 self.start()
904
899
905 class TimerAgent(self.wx.MiniFrame):
900 class TimerAgent(self.wx.MiniFrame):
906 wx = self.wx
901 wx = self.wx
907 IP = self.IP
902 IP = self.IP
908 tk = self.tk
903 tk = self.tk
909 def __init__(self, parent, interval):
904 def __init__(self, parent, interval):
910 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
905 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
911 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
906 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
912 size=(100, 100),style=style)
907 size=(100, 100),style=style)
913 self.Show(False)
908 self.Show(False)
914 self.interval = interval
909 self.interval = interval
915 self.timerId = self.wx.NewId()
910 self.timerId = self.wx.NewId()
916
911
917 def StartWork(self):
912 def StartWork(self):
918 self.timer = self.wx.Timer(self, self.timerId)
913 self.timer = self.wx.Timer(self, self.timerId)
919 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
914 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
920 self.timer.Start(self.interval)
915 self.timer.Start(self.interval)
921
916
922 def OnTimer(self, event):
917 def OnTimer(self, event):
923 update_tk(self.tk)
918 update_tk(self.tk)
924 self.IP.runcode()
919 self.IP.runcode()
925
920
926 class App(self.wx.App):
921 class App(self.wx.App):
927 wx = self.wx
922 wx = self.wx
928 TIMEOUT = self.TIMEOUT
923 TIMEOUT = self.TIMEOUT
929 def OnInit(self):
924 def OnInit(self):
930 'Create the main window and insert the custom frame'
925 'Create the main window and insert the custom frame'
931 self.agent = TimerAgent(None, self.TIMEOUT)
926 self.agent = TimerAgent(None, self.TIMEOUT)
932 self.agent.Show(False)
927 self.agent.Show(False)
933 self.agent.StartWork()
928 self.agent.StartWork()
934 return True
929 return True
935
930
936 self.app = App(redirect=False)
931 self.app = App(redirect=False)
937 self.wx_mainloop(self.app)
932 self.wx_mainloop(self.app)
938 self.join()
933 self.join()
939
934
940
935
941 class IPShellQt(IPThread):
936 class IPShellQt(IPThread):
942 """Run a Qt event loop in a separate thread.
937 """Run a Qt event loop in a separate thread.
943
938
944 Python commands can be passed to the thread where they will be executed.
939 Python commands can be passed to the thread where they will be executed.
945 This is implemented by periodically checking for passed code using a
940 This is implemented by periodically checking for passed code using a
946 Qt timer / slot."""
941 Qt timer / slot."""
947
942
948 TIMEOUT = 100 # Millisecond interval between timeouts.
943 TIMEOUT = 100 # Millisecond interval between timeouts.
949
944
950 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
945 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
951 debug=0, shell_class=MTInteractiveShell):
946 debug=0, shell_class=MTInteractiveShell):
952
947
953 import qt
948 import qt
954
949
955 self.exec_loop = hijack_qt()
950 self.exec_loop = hijack_qt()
956
951
957 # Allows us to use both Tk and QT.
952 # Allows us to use both Tk and QT.
958 self.tk = get_tk()
953 self.tk = get_tk()
959
954
960 self.IP = make_IPython(argv,
955 self.IP = make_IPython(argv,
961 user_ns=user_ns,
956 user_ns=user_ns,
962 user_global_ns=user_global_ns,
957 user_global_ns=user_global_ns,
963 debug=debug,
958 debug=debug,
964 shell_class=shell_class,
959 shell_class=shell_class,
965 on_kill=[qt.qApp.exit])
960 on_kill=[qt.qApp.exit])
966
961
967 # HACK: slot for banner in self; it will be passed to the mainloop
962 # HACK: slot for banner in self; it will be passed to the mainloop
968 # method only and .run() needs it. The actual value will be set by
963 # method only and .run() needs it. The actual value will be set by
969 # .mainloop().
964 # .mainloop().
970 self._banner = None
965 self._banner = None
971
966
972 threading.Thread.__init__(self)
967 threading.Thread.__init__(self)
973
968
974 def mainloop(self, sys_exit=0, banner=None):
969 def mainloop(self, sys_exit=0, banner=None):
975
970
976 import qt
971 import qt
977
972
978 self._banner = banner
973 self._banner = banner
979
974
980 if qt.QApplication.startingUp():
975 if qt.QApplication.startingUp():
981 a = qt.QApplication(sys.argv)
976 a = qt.QApplication(sys.argv)
982
977
983 self.timer = qt.QTimer()
978 self.timer = qt.QTimer()
984 qt.QObject.connect(self.timer,
979 qt.QObject.connect(self.timer,
985 qt.SIGNAL('timeout()'),
980 qt.SIGNAL('timeout()'),
986 self.on_timer)
981 self.on_timer)
987
982
988 self.start()
983 self.start()
989 self.timer.start(self.TIMEOUT, True)
984 self.timer.start(self.TIMEOUT, True)
990 while True:
985 while True:
991 if self.IP._kill: break
986 if self.IP._kill: break
992 self.exec_loop()
987 self.exec_loop()
993 self.join()
988 self.join()
994
989
995 def on_timer(self):
990 def on_timer(self):
996 update_tk(self.tk)
991 update_tk(self.tk)
997 result = self.IP.runcode()
992 result = self.IP.runcode()
998 self.timer.start(self.TIMEOUT, True)
993 self.timer.start(self.TIMEOUT, True)
999 return result
994 return result
1000
995
1001
996
1002 class IPShellQt4(IPThread):
997 class IPShellQt4(IPThread):
1003 """Run a Qt event loop in a separate thread.
998 """Run a Qt event loop in a separate thread.
1004
999
1005 Python commands can be passed to the thread where they will be executed.
1000 Python commands can be passed to the thread where they will be executed.
1006 This is implemented by periodically checking for passed code using a
1001 This is implemented by periodically checking for passed code using a
1007 Qt timer / slot."""
1002 Qt timer / slot."""
1008
1003
1009 TIMEOUT = 100 # Millisecond interval between timeouts.
1004 TIMEOUT = 100 # Millisecond interval between timeouts.
1010
1005
1011 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1006 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1012 debug=0, shell_class=MTInteractiveShell):
1007 debug=0, shell_class=MTInteractiveShell):
1013
1008
1014 from PyQt4 import QtCore, QtGui
1009 from PyQt4 import QtCore, QtGui
1015
1010
1016 try:
1011 try:
1017 # present in PyQt4-4.2.1 or later
1012 # present in PyQt4-4.2.1 or later
1018 QtCore.pyqtRemoveInputHook()
1013 QtCore.pyqtRemoveInputHook()
1019 except AttributeError:
1014 except AttributeError:
1020 pass
1015 pass
1021
1016
1022 if QtCore.PYQT_VERSION_STR == '4.3':
1017 if QtCore.PYQT_VERSION_STR == '4.3':
1023 warn('''PyQt4 version 4.3 detected.
1018 warn('''PyQt4 version 4.3 detected.
1024 If you experience repeated threading warnings, please update PyQt4.
1019 If you experience repeated threading warnings, please update PyQt4.
1025 ''')
1020 ''')
1026
1021
1027 self.exec_ = hijack_qt4()
1022 self.exec_ = hijack_qt4()
1028
1023
1029 # Allows us to use both Tk and QT.
1024 # Allows us to use both Tk and QT.
1030 self.tk = get_tk()
1025 self.tk = get_tk()
1031
1026
1032 self.IP = make_IPython(argv,
1027 self.IP = make_IPython(argv,
1033 user_ns=user_ns,
1028 user_ns=user_ns,
1034 user_global_ns=user_global_ns,
1029 user_global_ns=user_global_ns,
1035 debug=debug,
1030 debug=debug,
1036 shell_class=shell_class,
1031 shell_class=shell_class,
1037 on_kill=[QtGui.qApp.exit])
1032 on_kill=[QtGui.qApp.exit])
1038
1033
1039 # HACK: slot for banner in self; it will be passed to the mainloop
1034 # HACK: slot for banner in self; it will be passed to the mainloop
1040 # method only and .run() needs it. The actual value will be set by
1035 # method only and .run() needs it. The actual value will be set by
1041 # .mainloop().
1036 # .mainloop().
1042 self._banner = None
1037 self._banner = None
1043
1038
1044 threading.Thread.__init__(self)
1039 threading.Thread.__init__(self)
1045
1040
1046 def mainloop(self, sys_exit=0, banner=None):
1041 def mainloop(self, sys_exit=0, banner=None):
1047
1042
1048 from PyQt4 import QtCore, QtGui
1043 from PyQt4 import QtCore, QtGui
1049
1044
1050 self._banner = banner
1045 self._banner = banner
1051
1046
1052 if QtGui.QApplication.startingUp():
1047 if QtGui.QApplication.startingUp():
1053 a = QtGui.QApplication(sys.argv)
1048 a = QtGui.QApplication(sys.argv)
1054
1049
1055 self.timer = QtCore.QTimer()
1050 self.timer = QtCore.QTimer()
1056 QtCore.QObject.connect(self.timer,
1051 QtCore.QObject.connect(self.timer,
1057 QtCore.SIGNAL('timeout()'),
1052 QtCore.SIGNAL('timeout()'),
1058 self.on_timer)
1053 self.on_timer)
1059
1054
1060 self.start()
1055 self.start()
1061 self.timer.start(self.TIMEOUT)
1056 self.timer.start(self.TIMEOUT)
1062 while True:
1057 while True:
1063 if self.IP._kill: break
1058 if self.IP._kill: break
1064 self.exec_()
1059 self.exec_()
1065 self.join()
1060 self.join()
1066
1061
1067 def on_timer(self):
1062 def on_timer(self):
1068 update_tk(self.tk)
1063 update_tk(self.tk)
1069 result = self.IP.runcode()
1064 result = self.IP.runcode()
1070 self.timer.start(self.TIMEOUT)
1065 self.timer.start(self.TIMEOUT)
1071 return result
1066 return result
1072
1067
1073
1068
1074 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1069 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1075 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1070 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1076 def _load_pylab(user_ns):
1071 def _load_pylab(user_ns):
1077 """Allow users to disable pulling all of pylab into the top-level
1072 """Allow users to disable pulling all of pylab into the top-level
1078 namespace.
1073 namespace.
1079
1074
1080 This little utility must be called AFTER the actual ipython instance is
1075 This little utility must be called AFTER the actual ipython instance is
1081 running, since only then will the options file have been fully parsed."""
1076 running, since only then will the options file have been fully parsed."""
1082
1077
1083 ip = IPython.ipapi.get()
1078 ip = IPython.ipapi.get()
1084 if ip.options.pylab_import_all:
1079 if ip.options.pylab_import_all:
1085 ip.ex("from matplotlib.pylab import *")
1080 ip.ex("from matplotlib.pylab import *")
1086 ip.IP.user_config_ns.update(ip.user_ns)
1081 ip.IP.user_config_ns.update(ip.user_ns)
1087
1082
1088
1083
1089 class IPShellMatplotlib(IPShell):
1084 class IPShellMatplotlib(IPShell):
1090 """Subclass IPShell with MatplotlibShell as the internal shell.
1085 """Subclass IPShell with MatplotlibShell as the internal shell.
1091
1086
1092 Single-threaded class, meant for the Tk* and FLTK* backends.
1087 Single-threaded class, meant for the Tk* and FLTK* backends.
1093
1088
1094 Having this on a separate class simplifies the external driver code."""
1089 Having this on a separate class simplifies the external driver code."""
1095
1090
1096 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1091 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1097 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1092 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1098 shell_class=MatplotlibShell)
1093 shell_class=MatplotlibShell)
1099 _load_pylab(self.IP.user_ns)
1094 _load_pylab(self.IP.user_ns)
1100
1095
1101 class IPShellMatplotlibGTK(IPShellGTK):
1096 class IPShellMatplotlibGTK(IPShellGTK):
1102 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1097 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1103
1098
1104 Multi-threaded class, meant for the GTK* backends."""
1099 Multi-threaded class, meant for the GTK* backends."""
1105
1100
1106 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1101 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1107 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1102 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1108 shell_class=MatplotlibMTShell)
1103 shell_class=MatplotlibMTShell)
1109 _load_pylab(self.IP.user_ns)
1104 _load_pylab(self.IP.user_ns)
1110
1105
1111 class IPShellMatplotlibWX(IPShellWX):
1106 class IPShellMatplotlibWX(IPShellWX):
1112 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1107 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1113
1108
1114 Multi-threaded class, meant for the WX* backends."""
1109 Multi-threaded class, meant for the WX* backends."""
1115
1110
1116 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1111 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1117 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1112 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1118 shell_class=MatplotlibMTShell)
1113 shell_class=MatplotlibMTShell)
1119 _load_pylab(self.IP.user_ns)
1114 _load_pylab(self.IP.user_ns)
1120
1115
1121 class IPShellMatplotlibQt(IPShellQt):
1116 class IPShellMatplotlibQt(IPShellQt):
1122 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1117 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1123
1118
1124 Multi-threaded class, meant for the Qt* backends."""
1119 Multi-threaded class, meant for the Qt* backends."""
1125
1120
1126 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1121 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1127 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1122 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1128 shell_class=MatplotlibMTShell)
1123 shell_class=MatplotlibMTShell)
1129 _load_pylab(self.IP.user_ns)
1124 _load_pylab(self.IP.user_ns)
1130
1125
1131 class IPShellMatplotlibQt4(IPShellQt4):
1126 class IPShellMatplotlibQt4(IPShellQt4):
1132 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1127 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1133
1128
1134 Multi-threaded class, meant for the Qt4* backends."""
1129 Multi-threaded class, meant for the Qt4* backends."""
1135
1130
1136 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1131 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1137 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1132 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1138 shell_class=MatplotlibMTShell)
1133 shell_class=MatplotlibMTShell)
1139 _load_pylab(self.IP.user_ns)
1134 _load_pylab(self.IP.user_ns)
1140
1135
1141 #-----------------------------------------------------------------------------
1136 #-----------------------------------------------------------------------------
1142 # Factory functions to actually start the proper thread-aware shell
1137 # Factory functions to actually start the proper thread-aware shell
1143
1138
1144 def _select_shell(argv):
1139 def _select_shell(argv):
1145 """Select a shell from the given argv vector.
1140 """Select a shell from the given argv vector.
1146
1141
1147 This function implements the threading selection policy, allowing runtime
1142 This function implements the threading selection policy, allowing runtime
1148 control of the threading mode, both for general users and for matplotlib.
1143 control of the threading mode, both for general users and for matplotlib.
1149
1144
1150 Return:
1145 Return:
1151 Shell class to be instantiated for runtime operation.
1146 Shell class to be instantiated for runtime operation.
1152 """
1147 """
1153
1148
1154 global USE_TK
1149 global USE_TK
1155
1150
1156 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1151 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1157 'wthread' : IPShellMatplotlibWX,
1152 'wthread' : IPShellMatplotlibWX,
1158 'qthread' : IPShellMatplotlibQt,
1153 'qthread' : IPShellMatplotlibQt,
1159 'q4thread' : IPShellMatplotlibQt4,
1154 'q4thread' : IPShellMatplotlibQt4,
1160 'tkthread' : IPShellMatplotlib, # Tk is built-in
1155 'tkthread' : IPShellMatplotlib, # Tk is built-in
1161 }
1156 }
1162
1157
1163 th_shell = {'gthread' : IPShellGTK,
1158 th_shell = {'gthread' : IPShellGTK,
1164 'wthread' : IPShellWX,
1159 'wthread' : IPShellWX,
1165 'qthread' : IPShellQt,
1160 'qthread' : IPShellQt,
1166 'q4thread' : IPShellQt4,
1161 'q4thread' : IPShellQt4,
1167 'tkthread' : IPShell, # Tk is built-in
1162 'tkthread' : IPShell, # Tk is built-in
1168 }
1163 }
1169
1164
1170 backends = {'gthread' : 'GTKAgg',
1165 backends = {'gthread' : 'GTKAgg',
1171 'wthread' : 'WXAgg',
1166 'wthread' : 'WXAgg',
1172 'qthread' : 'QtAgg',
1167 'qthread' : 'QtAgg',
1173 'q4thread' :'Qt4Agg',
1168 'q4thread' :'Qt4Agg',
1174 'tkthread' :'TkAgg',
1169 'tkthread' :'TkAgg',
1175 }
1170 }
1176
1171
1177 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1172 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1178 'tkthread'])
1173 'tkthread'])
1179 user_opts = set([s.replace('-','') for s in argv[:3]])
1174 user_opts = set([s.replace('-','') for s in argv[:3]])
1180 special_opts = user_opts & all_opts
1175 special_opts = user_opts & all_opts
1181
1176
1182 if 'tk' in special_opts:
1177 if 'tk' in special_opts:
1183 USE_TK = True
1178 USE_TK = True
1184 special_opts.remove('tk')
1179 special_opts.remove('tk')
1185
1180
1186 if 'pylab' in special_opts:
1181 if 'pylab' in special_opts:
1187
1182
1188 try:
1183 try:
1189 import matplotlib
1184 import matplotlib
1190 except ImportError:
1185 except ImportError:
1191 error('matplotlib could NOT be imported! Starting normal IPython.')
1186 error('matplotlib could NOT be imported! Starting normal IPython.')
1192 return IPShell
1187 return IPShell
1193
1188
1194 special_opts.remove('pylab')
1189 special_opts.remove('pylab')
1195 # If there's any option left, it means the user wants to force the
1190 # If there's any option left, it means the user wants to force the
1196 # threading backend, else it's auto-selected from the rc file
1191 # threading backend, else it's auto-selected from the rc file
1197 if special_opts:
1192 if special_opts:
1198 th_mode = special_opts.pop()
1193 th_mode = special_opts.pop()
1199 matplotlib.rcParams['backend'] = backends[th_mode]
1194 matplotlib.rcParams['backend'] = backends[th_mode]
1200 else:
1195 else:
1201 backend = matplotlib.rcParams['backend']
1196 backend = matplotlib.rcParams['backend']
1202 if backend.startswith('GTK'):
1197 if backend.startswith('GTK'):
1203 th_mode = 'gthread'
1198 th_mode = 'gthread'
1204 elif backend.startswith('WX'):
1199 elif backend.startswith('WX'):
1205 th_mode = 'wthread'
1200 th_mode = 'wthread'
1206 elif backend.startswith('Qt4'):
1201 elif backend.startswith('Qt4'):
1207 th_mode = 'q4thread'
1202 th_mode = 'q4thread'
1208 elif backend.startswith('Qt'):
1203 elif backend.startswith('Qt'):
1209 th_mode = 'qthread'
1204 th_mode = 'qthread'
1210 else:
1205 else:
1211 # Any other backend, use plain Tk
1206 # Any other backend, use plain Tk
1212 th_mode = 'tkthread'
1207 th_mode = 'tkthread'
1213
1208
1214 return mpl_shell[th_mode]
1209 return mpl_shell[th_mode]
1215 else:
1210 else:
1216 # No pylab requested, just plain threads
1211 # No pylab requested, just plain threads
1217 try:
1212 try:
1218 th_mode = special_opts.pop()
1213 th_mode = special_opts.pop()
1219 except KeyError:
1214 except KeyError:
1220 th_mode = 'tkthread'
1215 th_mode = 'tkthread'
1221 return th_shell[th_mode]
1216 return th_shell[th_mode]
1222
1217
1223
1218
1224 # This is the one which should be called by external code.
1219 # This is the one which should be called by external code.
1225 def start(user_ns = None):
1220 def start(user_ns = None):
1226 """Return a running shell instance, dealing with threading options.
1221 """Return a running shell instance, dealing with threading options.
1227
1222
1228 This is a factory function which will instantiate the proper IPython shell
1223 This is a factory function which will instantiate the proper IPython shell
1229 based on the user's threading choice. Such a selector is needed because
1224 based on the user's threading choice. Such a selector is needed because
1230 different GUI toolkits require different thread handling details."""
1225 different GUI toolkits require different thread handling details."""
1231
1226
1232 shell = _select_shell(sys.argv)
1227 shell = _select_shell(sys.argv)
1233 return shell(user_ns = user_ns)
1228 return shell(user_ns = user_ns)
1234
1229
1235 # Some aliases for backwards compatibility
1230 # Some aliases for backwards compatibility
1236 IPythonShell = IPShell
1231 IPythonShell = IPShell
1237 IPythonShellEmbed = IPShellEmbed
1232 IPythonShellEmbed = IPShellEmbed
1238 #************************ End of file <Shell.py> ***************************
1233 #************************ End of file <Shell.py> ***************************
@@ -1,72 +1,71 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 IPython -- An enhanced Interactive Python
3 IPython -- An enhanced Interactive Python
4
4
5 One of Python's nicest features is its interactive interpreter. This allows
5 One of Python's nicest features is its interactive interpreter. This allows
6 very fast testing of ideas without the overhead of creating test files as is
6 very fast testing of ideas without the overhead of creating test files as is
7 typical in most programming languages. However, the interpreter supplied with
7 typical in most programming languages. However, the interpreter supplied with
8 the standard Python distribution is fairly primitive (and IDLE isn't really
8 the standard Python distribution is fairly primitive (and IDLE isn't really
9 much better).
9 much better).
10
10
11 IPython tries to:
11 IPython tries to:
12
12
13 i - provide an efficient environment for interactive work in Python
13 i - provide an efficient environment for interactive work in Python
14 programming. It tries to address what we see as shortcomings of the standard
14 programming. It tries to address what we see as shortcomings of the standard
15 Python prompt, and adds many features to make interactive work much more
15 Python prompt, and adds many features to make interactive work much more
16 efficient.
16 efficient.
17
17
18 ii - offer a flexible framework so that it can be used as the base
18 ii - offer a flexible framework so that it can be used as the base
19 environment for other projects and problems where Python can be the
19 environment for other projects and problems where Python can be the
20 underlying language. Specifically scientific environments like Mathematica,
20 underlying language. Specifically scientific environments like Mathematica,
21 IDL and Mathcad inspired its design, but similar ideas can be useful in many
21 IDL and Mathcad inspired its design, but similar ideas can be useful in many
22 fields. Python is a fabulous language for implementing this kind of system
22 fields. Python is a fabulous language for implementing this kind of system
23 (due to its dynamic and introspective features), and with suitable libraries
23 (due to its dynamic and introspective features), and with suitable libraries
24 entire systems could be built leveraging Python's power.
24 entire systems could be built leveraging Python's power.
25
25
26 iii - serve as an embeddable, ready to go interpreter for your own programs.
26 iii - serve as an embeddable, ready to go interpreter for your own programs.
27
27
28 IPython requires Python 2.3 or newer.
28 IPython requires Python 2.4 or newer.
29
29 """
30 $Id: __init__.py 2399 2007-05-26 10:23:10Z vivainio $"""
31
30
32 #*****************************************************************************
31 #*****************************************************************************
33 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
32 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
34 #
33 #
35 # Distributed under the terms of the BSD License. The full license is in
34 # Distributed under the terms of the BSD License. The full license is in
36 # the file COPYING, distributed as part of this software.
35 # the file COPYING, distributed as part of this software.
37 #*****************************************************************************
36 #*****************************************************************************
38
37
39 # Enforce proper version requirements
38 # Enforce proper version requirements
40 import sys
39 import sys
41
40
42 if sys.version[0:3] < '2.4':
41 if sys.version[0:3] < '2.4':
43 raise ImportError('Python Version 2.4 or above is required for IPython.')
42 raise ImportError('Python Version 2.4 or above is required for IPython.')
44
43
45 # Make it easy to import extensions - they are always directly on pythonpath.
44 # Make it easy to import extensions - they are always directly on pythonpath.
46 # Therefore, non-IPython modules can be added to Extensions directory
45 # Therefore, non-IPython modules can be added to Extensions directory
47 import os
46 import os
48 sys.path.append(os.path.dirname(__file__) + "/Extensions")
47 sys.path.append(os.path.dirname(__file__) + "/Extensions")
49
48
50 # Define what gets imported with a 'from IPython import *'
49 # Define what gets imported with a 'from IPython import *'
51 __all__ = ['ipapi','generics','ipstruct','Release','Shell']
50 __all__ = ['ipapi','generics','ipstruct','Release','Shell']
52
51
53 # Load __all__ in IPython namespace so that a simple 'import IPython' gives
52 # Load __all__ in IPython namespace so that a simple 'import IPython' gives
54 # access to them via IPython.<name>
53 # access to them via IPython.<name>
55 glob,loc = globals(),locals()
54 glob,loc = globals(),locals()
56 for name in __all__:
55 for name in __all__:
57 #print 'Importing: ',name # dbg
56 #print 'Importing: ',name # dbg
58 __import__(name,glob,loc,[])
57 __import__(name,glob,loc,[])
59
58
60 import Shell
59 import Shell
61
60
62 # Release data
61 # Release data
63 from IPython import Release # do it explicitly so pydoc can see it - pydoc bug
62 from IPython import Release # do it explicitly so pydoc can see it - pydoc bug
64 __author__ = '%s <%s>\n%s <%s>\n%s <%s>' % \
63 __author__ = '%s <%s>\n%s <%s>\n%s <%s>' % \
65 ( Release.authors['Fernando'] + Release.authors['Janko'] + \
64 ( Release.authors['Fernando'] + Release.authors['Janko'] + \
66 Release.authors['Nathan'] )
65 Release.authors['Nathan'] )
67 __license__ = Release.license
66 __license__ = Release.license
68 __version__ = Release.version
67 __version__ = Release.version
69 __revision__ = Release.revision
68 __revision__ = Release.revision
70
69
71 # Namespace cleanup
70 # Namespace cleanup
72 del name,glob,loc
71 del name,glob,loc
@@ -1,496 +1,490 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Manage background (threaded) jobs conveniently from an interactive shell.
2 """Manage background (threaded) jobs conveniently from an interactive shell.
3
3
4 This module provides a BackgroundJobManager class. This is the main class
4 This module provides a BackgroundJobManager class. This is the main class
5 meant for public usage, it implements an object which can create and manage
5 meant for public usage, it implements an object which can create and manage
6 new background jobs.
6 new background jobs.
7
7
8 It also provides the actual job classes managed by these BackgroundJobManager
8 It also provides the actual job classes managed by these BackgroundJobManager
9 objects, see their docstrings below.
9 objects, see their docstrings below.
10
10
11
11
12 This system was inspired by discussions with B. Granger and the
12 This system was inspired by discussions with B. Granger and the
13 BackgroundCommand class described in the book Python Scripting for
13 BackgroundCommand class described in the book Python Scripting for
14 Computational Science, by H. P. Langtangen:
14 Computational Science, by H. P. Langtangen:
15
15
16 http://folk.uio.no/hpl/scripting
16 http://folk.uio.no/hpl/scripting
17
17
18 (although ultimately no code from this text was used, as IPython's system is a
18 (although ultimately no code from this text was used, as IPython's system is a
19 separate implementation).
19 separate implementation).
20
21 $Id: background_jobs.py 994 2006-01-08 08:29:44Z fperez $
22 """
20 """
23
21
24 #*****************************************************************************
22 #*****************************************************************************
25 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
26 #
24 #
27 # Distributed under the terms of the BSD License. The full license is in
25 # Distributed under the terms of the BSD License. The full license is in
28 # the file COPYING, distributed as part of this software.
26 # the file COPYING, distributed as part of this software.
29 #*****************************************************************************
27 #*****************************************************************************
30
28
31 from IPython import Release
32 __author__ = '%s <%s>' % Release.authors['Fernando']
33 __license__ = Release.license
34
35 # Code begins
29 # Code begins
36 import sys
30 import sys
37 import threading
31 import threading
38
32
39 from IPython.ultraTB import AutoFormattedTB
33 from IPython.ultraTB import AutoFormattedTB
40 from IPython.genutils import warn,error
34 from IPython.genutils import warn,error
41
35
42 class BackgroundJobManager:
36 class BackgroundJobManager:
43 """Class to manage a pool of backgrounded threaded jobs.
37 """Class to manage a pool of backgrounded threaded jobs.
44
38
45 Below, we assume that 'jobs' is a BackgroundJobManager instance.
39 Below, we assume that 'jobs' is a BackgroundJobManager instance.
46
40
47 Usage summary (see the method docstrings for details):
41 Usage summary (see the method docstrings for details):
48
42
49 jobs.new(...) -> start a new job
43 jobs.new(...) -> start a new job
50
44
51 jobs() or jobs.status() -> print status summary of all jobs
45 jobs() or jobs.status() -> print status summary of all jobs
52
46
53 jobs[N] -> returns job number N.
47 jobs[N] -> returns job number N.
54
48
55 foo = jobs[N].result -> assign to variable foo the result of job N
49 foo = jobs[N].result -> assign to variable foo the result of job N
56
50
57 jobs[N].traceback() -> print the traceback of dead job N
51 jobs[N].traceback() -> print the traceback of dead job N
58
52
59 jobs.remove(N) -> remove (finished) job N
53 jobs.remove(N) -> remove (finished) job N
60
54
61 jobs.flush_finished() -> remove all finished jobs
55 jobs.flush_finished() -> remove all finished jobs
62
56
63 As a convenience feature, BackgroundJobManager instances provide the
57 As a convenience feature, BackgroundJobManager instances provide the
64 utility result and traceback methods which retrieve the corresponding
58 utility result and traceback methods which retrieve the corresponding
65 information from the jobs list:
59 information from the jobs list:
66
60
67 jobs.result(N) <--> jobs[N].result
61 jobs.result(N) <--> jobs[N].result
68 jobs.traceback(N) <--> jobs[N].traceback()
62 jobs.traceback(N) <--> jobs[N].traceback()
69
63
70 While this appears minor, it allows you to use tab completion
64 While this appears minor, it allows you to use tab completion
71 interactively on the job manager instance.
65 interactively on the job manager instance.
72
66
73 In interactive mode, IPython provides the magic fuction %bg for quick
67 In interactive mode, IPython provides the magic fuction %bg for quick
74 creation of backgrounded expression-based jobs. Type bg? for details."""
68 creation of backgrounded expression-based jobs. Type bg? for details."""
75
69
76 def __init__(self):
70 def __init__(self):
77 # Lists for job management
71 # Lists for job management
78 self.jobs_run = []
72 self.jobs_run = []
79 self.jobs_comp = []
73 self.jobs_comp = []
80 self.jobs_dead = []
74 self.jobs_dead = []
81 # A dict of all jobs, so users can easily access any of them
75 # A dict of all jobs, so users can easily access any of them
82 self.jobs_all = {}
76 self.jobs_all = {}
83 # For reporting
77 # For reporting
84 self._comp_report = []
78 self._comp_report = []
85 self._dead_report = []
79 self._dead_report = []
86 # Store status codes locally for fast lookups
80 # Store status codes locally for fast lookups
87 self._s_created = BackgroundJobBase.stat_created_c
81 self._s_created = BackgroundJobBase.stat_created_c
88 self._s_running = BackgroundJobBase.stat_running_c
82 self._s_running = BackgroundJobBase.stat_running_c
89 self._s_completed = BackgroundJobBase.stat_completed_c
83 self._s_completed = BackgroundJobBase.stat_completed_c
90 self._s_dead = BackgroundJobBase.stat_dead_c
84 self._s_dead = BackgroundJobBase.stat_dead_c
91
85
92 def new(self,func_or_exp,*args,**kwargs):
86 def new(self,func_or_exp,*args,**kwargs):
93 """Add a new background job and start it in a separate thread.
87 """Add a new background job and start it in a separate thread.
94
88
95 There are two types of jobs which can be created:
89 There are two types of jobs which can be created:
96
90
97 1. Jobs based on expressions which can be passed to an eval() call.
91 1. Jobs based on expressions which can be passed to an eval() call.
98 The expression must be given as a string. For example:
92 The expression must be given as a string. For example:
99
93
100 job_manager.new('myfunc(x,y,z=1)'[,glob[,loc]])
94 job_manager.new('myfunc(x,y,z=1)'[,glob[,loc]])
101
95
102 The given expression is passed to eval(), along with the optional
96 The given expression is passed to eval(), along with the optional
103 global/local dicts provided. If no dicts are given, they are
97 global/local dicts provided. If no dicts are given, they are
104 extracted automatically from the caller's frame.
98 extracted automatically from the caller's frame.
105
99
106 A Python statement is NOT a valid eval() expression. Basically, you
100 A Python statement is NOT a valid eval() expression. Basically, you
107 can only use as an eval() argument something which can go on the right
101 can only use as an eval() argument something which can go on the right
108 of an '=' sign and be assigned to a variable.
102 of an '=' sign and be assigned to a variable.
109
103
110 For example,"print 'hello'" is not valid, but '2+3' is.
104 For example,"print 'hello'" is not valid, but '2+3' is.
111
105
112 2. Jobs given a function object, optionally passing additional
106 2. Jobs given a function object, optionally passing additional
113 positional arguments:
107 positional arguments:
114
108
115 job_manager.new(myfunc,x,y)
109 job_manager.new(myfunc,x,y)
116
110
117 The function is called with the given arguments.
111 The function is called with the given arguments.
118
112
119 If you need to pass keyword arguments to your function, you must
113 If you need to pass keyword arguments to your function, you must
120 supply them as a dict named kw:
114 supply them as a dict named kw:
121
115
122 job_manager.new(myfunc,x,y,kw=dict(z=1))
116 job_manager.new(myfunc,x,y,kw=dict(z=1))
123
117
124 The reason for this assymmetry is that the new() method needs to
118 The reason for this assymmetry is that the new() method needs to
125 maintain access to its own keywords, and this prevents name collisions
119 maintain access to its own keywords, and this prevents name collisions
126 between arguments to new() and arguments to your own functions.
120 between arguments to new() and arguments to your own functions.
127
121
128 In both cases, the result is stored in the job.result field of the
122 In both cases, the result is stored in the job.result field of the
129 background job object.
123 background job object.
130
124
131
125
132 Notes and caveats:
126 Notes and caveats:
133
127
134 1. All threads running share the same standard output. Thus, if your
128 1. All threads running share the same standard output. Thus, if your
135 background jobs generate output, it will come out on top of whatever
129 background jobs generate output, it will come out on top of whatever
136 you are currently writing. For this reason, background jobs are best
130 you are currently writing. For this reason, background jobs are best
137 used with silent functions which simply return their output.
131 used with silent functions which simply return their output.
138
132
139 2. Threads also all work within the same global namespace, and this
133 2. Threads also all work within the same global namespace, and this
140 system does not lock interactive variables. So if you send job to the
134 system does not lock interactive variables. So if you send job to the
141 background which operates on a mutable object for a long time, and
135 background which operates on a mutable object for a long time, and
142 start modifying that same mutable object interactively (or in another
136 start modifying that same mutable object interactively (or in another
143 backgrounded job), all sorts of bizarre behaviour will occur.
137 backgrounded job), all sorts of bizarre behaviour will occur.
144
138
145 3. If a background job is spending a lot of time inside a C extension
139 3. If a background job is spending a lot of time inside a C extension
146 module which does not release the Python Global Interpreter Lock
140 module which does not release the Python Global Interpreter Lock
147 (GIL), this will block the IPython prompt. This is simply because the
141 (GIL), this will block the IPython prompt. This is simply because the
148 Python interpreter can only switch between threads at Python
142 Python interpreter can only switch between threads at Python
149 bytecodes. While the execution is inside C code, the interpreter must
143 bytecodes. While the execution is inside C code, the interpreter must
150 simply wait unless the extension module releases the GIL.
144 simply wait unless the extension module releases the GIL.
151
145
152 4. There is no way, due to limitations in the Python threads library,
146 4. There is no way, due to limitations in the Python threads library,
153 to kill a thread once it has started."""
147 to kill a thread once it has started."""
154
148
155 if callable(func_or_exp):
149 if callable(func_or_exp):
156 kw = kwargs.get('kw',{})
150 kw = kwargs.get('kw',{})
157 job = BackgroundJobFunc(func_or_exp,*args,**kw)
151 job = BackgroundJobFunc(func_or_exp,*args,**kw)
158 elif isinstance(func_or_exp,basestring):
152 elif isinstance(func_or_exp,basestring):
159 if not args:
153 if not args:
160 frame = sys._getframe(1)
154 frame = sys._getframe(1)
161 glob, loc = frame.f_globals, frame.f_locals
155 glob, loc = frame.f_globals, frame.f_locals
162 elif len(args)==1:
156 elif len(args)==1:
163 glob = loc = args[0]
157 glob = loc = args[0]
164 elif len(args)==2:
158 elif len(args)==2:
165 glob,loc = args
159 glob,loc = args
166 else:
160 else:
167 raise ValueError,\
161 raise ValueError,\
168 'Expression jobs take at most 2 args (globals,locals)'
162 'Expression jobs take at most 2 args (globals,locals)'
169 job = BackgroundJobExpr(func_or_exp,glob,loc)
163 job = BackgroundJobExpr(func_or_exp,glob,loc)
170 else:
164 else:
171 raise
165 raise
172 jkeys = self.jobs_all.keys()
166 jkeys = self.jobs_all.keys()
173 if jkeys:
167 if jkeys:
174 job.num = max(jkeys)+1
168 job.num = max(jkeys)+1
175 else:
169 else:
176 job.num = 0
170 job.num = 0
177 self.jobs_run.append(job)
171 self.jobs_run.append(job)
178 self.jobs_all[job.num] = job
172 self.jobs_all[job.num] = job
179 print 'Starting job # %s in a separate thread.' % job.num
173 print 'Starting job # %s in a separate thread.' % job.num
180 job.start()
174 job.start()
181 return job
175 return job
182
176
183 def __getitem__(self,key):
177 def __getitem__(self,key):
184 return self.jobs_all[key]
178 return self.jobs_all[key]
185
179
186 def __call__(self):
180 def __call__(self):
187 """An alias to self.status(),
181 """An alias to self.status(),
188
182
189 This allows you to simply call a job manager instance much like the
183 This allows you to simply call a job manager instance much like the
190 Unix jobs shell command."""
184 Unix jobs shell command."""
191
185
192 return self.status()
186 return self.status()
193
187
194 def _update_status(self):
188 def _update_status(self):
195 """Update the status of the job lists.
189 """Update the status of the job lists.
196
190
197 This method moves finished jobs to one of two lists:
191 This method moves finished jobs to one of two lists:
198 - self.jobs_comp: jobs which completed successfully
192 - self.jobs_comp: jobs which completed successfully
199 - self.jobs_dead: jobs which finished but died.
193 - self.jobs_dead: jobs which finished but died.
200
194
201 It also copies those jobs to corresponding _report lists. These lists
195 It also copies those jobs to corresponding _report lists. These lists
202 are used to report jobs completed/dead since the last update, and are
196 are used to report jobs completed/dead since the last update, and are
203 then cleared by the reporting function after each call."""
197 then cleared by the reporting function after each call."""
204
198
205 run,comp,dead = self._s_running,self._s_completed,self._s_dead
199 run,comp,dead = self._s_running,self._s_completed,self._s_dead
206 jobs_run = self.jobs_run
200 jobs_run = self.jobs_run
207 for num in range(len(jobs_run)):
201 for num in range(len(jobs_run)):
208 job = jobs_run[num]
202 job = jobs_run[num]
209 stat = job.stat_code
203 stat = job.stat_code
210 if stat == run:
204 if stat == run:
211 continue
205 continue
212 elif stat == comp:
206 elif stat == comp:
213 self.jobs_comp.append(job)
207 self.jobs_comp.append(job)
214 self._comp_report.append(job)
208 self._comp_report.append(job)
215 jobs_run[num] = False
209 jobs_run[num] = False
216 elif stat == dead:
210 elif stat == dead:
217 self.jobs_dead.append(job)
211 self.jobs_dead.append(job)
218 self._dead_report.append(job)
212 self._dead_report.append(job)
219 jobs_run[num] = False
213 jobs_run[num] = False
220 self.jobs_run = filter(None,self.jobs_run)
214 self.jobs_run = filter(None,self.jobs_run)
221
215
222 def _group_report(self,group,name):
216 def _group_report(self,group,name):
223 """Report summary for a given job group.
217 """Report summary for a given job group.
224
218
225 Return True if the group had any elements."""
219 Return True if the group had any elements."""
226
220
227 if group:
221 if group:
228 print '%s jobs:' % name
222 print '%s jobs:' % name
229 for job in group:
223 for job in group:
230 print '%s : %s' % (job.num,job)
224 print '%s : %s' % (job.num,job)
231 print
225 print
232 return True
226 return True
233
227
234 def _group_flush(self,group,name):
228 def _group_flush(self,group,name):
235 """Flush a given job group
229 """Flush a given job group
236
230
237 Return True if the group had any elements."""
231 Return True if the group had any elements."""
238
232
239 njobs = len(group)
233 njobs = len(group)
240 if njobs:
234 if njobs:
241 plural = {1:''}.setdefault(njobs,'s')
235 plural = {1:''}.setdefault(njobs,'s')
242 print 'Flushing %s %s job%s.' % (njobs,name,plural)
236 print 'Flushing %s %s job%s.' % (njobs,name,plural)
243 group[:] = []
237 group[:] = []
244 return True
238 return True
245
239
246 def _status_new(self):
240 def _status_new(self):
247 """Print the status of newly finished jobs.
241 """Print the status of newly finished jobs.
248
242
249 Return True if any new jobs are reported.
243 Return True if any new jobs are reported.
250
244
251 This call resets its own state every time, so it only reports jobs
245 This call resets its own state every time, so it only reports jobs
252 which have finished since the last time it was called."""
246 which have finished since the last time it was called."""
253
247
254 self._update_status()
248 self._update_status()
255 new_comp = self._group_report(self._comp_report,'Completed')
249 new_comp = self._group_report(self._comp_report,'Completed')
256 new_dead = self._group_report(self._dead_report,
250 new_dead = self._group_report(self._dead_report,
257 'Dead, call job.traceback() for details')
251 'Dead, call job.traceback() for details')
258 self._comp_report[:] = []
252 self._comp_report[:] = []
259 self._dead_report[:] = []
253 self._dead_report[:] = []
260 return new_comp or new_dead
254 return new_comp or new_dead
261
255
262 def status(self,verbose=0):
256 def status(self,verbose=0):
263 """Print a status of all jobs currently being managed."""
257 """Print a status of all jobs currently being managed."""
264
258
265 self._update_status()
259 self._update_status()
266 self._group_report(self.jobs_run,'Running')
260 self._group_report(self.jobs_run,'Running')
267 self._group_report(self.jobs_comp,'Completed')
261 self._group_report(self.jobs_comp,'Completed')
268 self._group_report(self.jobs_dead,'Dead')
262 self._group_report(self.jobs_dead,'Dead')
269 # Also flush the report queues
263 # Also flush the report queues
270 self._comp_report[:] = []
264 self._comp_report[:] = []
271 self._dead_report[:] = []
265 self._dead_report[:] = []
272
266
273 def remove(self,num):
267 def remove(self,num):
274 """Remove a finished (completed or dead) job."""
268 """Remove a finished (completed or dead) job."""
275
269
276 try:
270 try:
277 job = self.jobs_all[num]
271 job = self.jobs_all[num]
278 except KeyError:
272 except KeyError:
279 error('Job #%s not found' % num)
273 error('Job #%s not found' % num)
280 else:
274 else:
281 stat_code = job.stat_code
275 stat_code = job.stat_code
282 if stat_code == self._s_running:
276 if stat_code == self._s_running:
283 error('Job #%s is still running, it can not be removed.' % num)
277 error('Job #%s is still running, it can not be removed.' % num)
284 return
278 return
285 elif stat_code == self._s_completed:
279 elif stat_code == self._s_completed:
286 self.jobs_comp.remove(job)
280 self.jobs_comp.remove(job)
287 elif stat_code == self._s_dead:
281 elif stat_code == self._s_dead:
288 self.jobs_dead.remove(job)
282 self.jobs_dead.remove(job)
289
283
290 def flush_finished(self):
284 def flush_finished(self):
291 """Flush all jobs finished (completed and dead) from lists.
285 """Flush all jobs finished (completed and dead) from lists.
292
286
293 Running jobs are never flushed.
287 Running jobs are never flushed.
294
288
295 It first calls _status_new(), to update info. If any jobs have
289 It first calls _status_new(), to update info. If any jobs have
296 completed since the last _status_new() call, the flush operation
290 completed since the last _status_new() call, the flush operation
297 aborts."""
291 aborts."""
298
292
299 if self._status_new():
293 if self._status_new():
300 error('New jobs completed since last '\
294 error('New jobs completed since last '\
301 '_status_new(), aborting flush.')
295 '_status_new(), aborting flush.')
302 return
296 return
303
297
304 # Remove the finished jobs from the master dict
298 # Remove the finished jobs from the master dict
305 jobs_all = self.jobs_all
299 jobs_all = self.jobs_all
306 for job in self.jobs_comp+self.jobs_dead:
300 for job in self.jobs_comp+self.jobs_dead:
307 del(jobs_all[job.num])
301 del(jobs_all[job.num])
308
302
309 # Now flush these lists completely
303 # Now flush these lists completely
310 fl_comp = self._group_flush(self.jobs_comp,'Completed')
304 fl_comp = self._group_flush(self.jobs_comp,'Completed')
311 fl_dead = self._group_flush(self.jobs_dead,'Dead')
305 fl_dead = self._group_flush(self.jobs_dead,'Dead')
312 if not (fl_comp or fl_dead):
306 if not (fl_comp or fl_dead):
313 print 'No jobs to flush.'
307 print 'No jobs to flush.'
314
308
315 def result(self,num):
309 def result(self,num):
316 """result(N) -> return the result of job N."""
310 """result(N) -> return the result of job N."""
317 try:
311 try:
318 return self.jobs_all[num].result
312 return self.jobs_all[num].result
319 except KeyError:
313 except KeyError:
320 error('Job #%s not found' % num)
314 error('Job #%s not found' % num)
321
315
322 def traceback(self,num):
316 def traceback(self,num):
323 try:
317 try:
324 self.jobs_all[num].traceback()
318 self.jobs_all[num].traceback()
325 except KeyError:
319 except KeyError:
326 error('Job #%s not found' % num)
320 error('Job #%s not found' % num)
327
321
328
322
329 class BackgroundJobBase(threading.Thread):
323 class BackgroundJobBase(threading.Thread):
330 """Base class to build BackgroundJob classes.
324 """Base class to build BackgroundJob classes.
331
325
332 The derived classes must implement:
326 The derived classes must implement:
333
327
334 - Their own __init__, since the one here raises NotImplementedError. The
328 - Their own __init__, since the one here raises NotImplementedError. The
335 derived constructor must call self._init() at the end, to provide common
329 derived constructor must call self._init() at the end, to provide common
336 initialization.
330 initialization.
337
331
338 - A strform attribute used in calls to __str__.
332 - A strform attribute used in calls to __str__.
339
333
340 - A call() method, which will make the actual execution call and must
334 - A call() method, which will make the actual execution call and must
341 return a value to be held in the 'result' field of the job object."""
335 return a value to be held in the 'result' field of the job object."""
342
336
343 # Class constants for status, in string and as numerical codes (when
337 # Class constants for status, in string and as numerical codes (when
344 # updating jobs lists, we don't want to do string comparisons). This will
338 # updating jobs lists, we don't want to do string comparisons). This will
345 # be done at every user prompt, so it has to be as fast as possible
339 # be done at every user prompt, so it has to be as fast as possible
346 stat_created = 'Created'; stat_created_c = 0
340 stat_created = 'Created'; stat_created_c = 0
347 stat_running = 'Running'; stat_running_c = 1
341 stat_running = 'Running'; stat_running_c = 1
348 stat_completed = 'Completed'; stat_completed_c = 2
342 stat_completed = 'Completed'; stat_completed_c = 2
349 stat_dead = 'Dead (Exception), call job.traceback() for details'
343 stat_dead = 'Dead (Exception), call job.traceback() for details'
350 stat_dead_c = -1
344 stat_dead_c = -1
351
345
352 def __init__(self):
346 def __init__(self):
353 raise NotImplementedError, \
347 raise NotImplementedError, \
354 "This class can not be instantiated directly."
348 "This class can not be instantiated directly."
355
349
356 def _init(self):
350 def _init(self):
357 """Common initialization for all BackgroundJob objects"""
351 """Common initialization for all BackgroundJob objects"""
358
352
359 for attr in ['call','strform']:
353 for attr in ['call','strform']:
360 assert hasattr(self,attr), "Missing attribute <%s>" % attr
354 assert hasattr(self,attr), "Missing attribute <%s>" % attr
361
355
362 # The num tag can be set by an external job manager
356 # The num tag can be set by an external job manager
363 self.num = None
357 self.num = None
364
358
365 self.status = BackgroundJobBase.stat_created
359 self.status = BackgroundJobBase.stat_created
366 self.stat_code = BackgroundJobBase.stat_created_c
360 self.stat_code = BackgroundJobBase.stat_created_c
367 self.finished = False
361 self.finished = False
368 self.result = '<BackgroundJob has not completed>'
362 self.result = '<BackgroundJob has not completed>'
369 # reuse the ipython traceback handler if we can get to it, otherwise
363 # reuse the ipython traceback handler if we can get to it, otherwise
370 # make a new one
364 # make a new one
371 try:
365 try:
372 self._make_tb = __IPYTHON__.InteractiveTB.text
366 self._make_tb = __IPYTHON__.InteractiveTB.text
373 except:
367 except:
374 self._make_tb = AutoFormattedTB(mode = 'Context',
368 self._make_tb = AutoFormattedTB(mode = 'Context',
375 color_scheme='NoColor',
369 color_scheme='NoColor',
376 tb_offset = 1).text
370 tb_offset = 1).text
377 # Hold a formatted traceback if one is generated.
371 # Hold a formatted traceback if one is generated.
378 self._tb = None
372 self._tb = None
379
373
380 threading.Thread.__init__(self)
374 threading.Thread.__init__(self)
381
375
382 def __str__(self):
376 def __str__(self):
383 return self.strform
377 return self.strform
384
378
385 def __repr__(self):
379 def __repr__(self):
386 return '<BackgroundJob: %s>' % self.strform
380 return '<BackgroundJob: %s>' % self.strform
387
381
388 def traceback(self):
382 def traceback(self):
389 print self._tb
383 print self._tb
390
384
391 def run(self):
385 def run(self):
392 try:
386 try:
393 self.status = BackgroundJobBase.stat_running
387 self.status = BackgroundJobBase.stat_running
394 self.stat_code = BackgroundJobBase.stat_running_c
388 self.stat_code = BackgroundJobBase.stat_running_c
395 self.result = self.call()
389 self.result = self.call()
396 except:
390 except:
397 self.status = BackgroundJobBase.stat_dead
391 self.status = BackgroundJobBase.stat_dead
398 self.stat_code = BackgroundJobBase.stat_dead_c
392 self.stat_code = BackgroundJobBase.stat_dead_c
399 self.finished = None
393 self.finished = None
400 self.result = ('<BackgroundJob died, call job.traceback() for details>')
394 self.result = ('<BackgroundJob died, call job.traceback() for details>')
401 self._tb = self._make_tb()
395 self._tb = self._make_tb()
402 else:
396 else:
403 self.status = BackgroundJobBase.stat_completed
397 self.status = BackgroundJobBase.stat_completed
404 self.stat_code = BackgroundJobBase.stat_completed_c
398 self.stat_code = BackgroundJobBase.stat_completed_c
405 self.finished = True
399 self.finished = True
406
400
407 class BackgroundJobExpr(BackgroundJobBase):
401 class BackgroundJobExpr(BackgroundJobBase):
408 """Evaluate an expression as a background job (uses a separate thread)."""
402 """Evaluate an expression as a background job (uses a separate thread)."""
409
403
410 def __init__(self,expression,glob=None,loc=None):
404 def __init__(self,expression,glob=None,loc=None):
411 """Create a new job from a string which can be fed to eval().
405 """Create a new job from a string which can be fed to eval().
412
406
413 global/locals dicts can be provided, which will be passed to the eval
407 global/locals dicts can be provided, which will be passed to the eval
414 call."""
408 call."""
415
409
416 # fail immediately if the given expression can't be compiled
410 # fail immediately if the given expression can't be compiled
417 self.code = compile(expression,'<BackgroundJob compilation>','eval')
411 self.code = compile(expression,'<BackgroundJob compilation>','eval')
418
412
419 if glob is None:
413 if glob is None:
420 glob = {}
414 glob = {}
421 if loc is None:
415 if loc is None:
422 loc = {}
416 loc = {}
423
417
424 self.expression = self.strform = expression
418 self.expression = self.strform = expression
425 self.glob = glob
419 self.glob = glob
426 self.loc = loc
420 self.loc = loc
427 self._init()
421 self._init()
428
422
429 def call(self):
423 def call(self):
430 return eval(self.code,self.glob,self.loc)
424 return eval(self.code,self.glob,self.loc)
431
425
432 class BackgroundJobFunc(BackgroundJobBase):
426 class BackgroundJobFunc(BackgroundJobBase):
433 """Run a function call as a background job (uses a separate thread)."""
427 """Run a function call as a background job (uses a separate thread)."""
434
428
435 def __init__(self,func,*args,**kwargs):
429 def __init__(self,func,*args,**kwargs):
436 """Create a new job from a callable object.
430 """Create a new job from a callable object.
437
431
438 Any positional arguments and keyword args given to this constructor
432 Any positional arguments and keyword args given to this constructor
439 after the initial callable are passed directly to it."""
433 after the initial callable are passed directly to it."""
440
434
441 assert callable(func),'first argument must be callable'
435 assert callable(func),'first argument must be callable'
442
436
443 if args is None:
437 if args is None:
444 args = []
438 args = []
445 if kwargs is None:
439 if kwargs is None:
446 kwargs = {}
440 kwargs = {}
447
441
448 self.func = func
442 self.func = func
449 self.args = args
443 self.args = args
450 self.kwargs = kwargs
444 self.kwargs = kwargs
451 # The string form will only include the function passed, because
445 # The string form will only include the function passed, because
452 # generating string representations of the arguments is a potentially
446 # generating string representations of the arguments is a potentially
453 # _very_ expensive operation (e.g. with large arrays).
447 # _very_ expensive operation (e.g. with large arrays).
454 self.strform = str(func)
448 self.strform = str(func)
455 self._init()
449 self._init()
456
450
457 def call(self):
451 def call(self):
458 return self.func(*self.args,**self.kwargs)
452 return self.func(*self.args,**self.kwargs)
459
453
460
454
461 if __name__=='__main__':
455 if __name__=='__main__':
462
456
463 import time
457 import time
464
458
465 def sleepfunc(interval=2,*a,**kw):
459 def sleepfunc(interval=2,*a,**kw):
466 args = dict(interval=interval,
460 args = dict(interval=interval,
467 args=a,
461 args=a,
468 kwargs=kw)
462 kwargs=kw)
469 time.sleep(interval)
463 time.sleep(interval)
470 return args
464 return args
471
465
472 def diefunc(interval=2,*a,**kw):
466 def diefunc(interval=2,*a,**kw):
473 time.sleep(interval)
467 time.sleep(interval)
474 die
468 die
475
469
476 def printfunc(interval=1,reps=5):
470 def printfunc(interval=1,reps=5):
477 for n in range(reps):
471 for n in range(reps):
478 time.sleep(interval)
472 time.sleep(interval)
479 print 'In the background...'
473 print 'In the background...'
480
474
481 jobs = BackgroundJobManager()
475 jobs = BackgroundJobManager()
482 # first job will have # 0
476 # first job will have # 0
483 jobs.new(sleepfunc,4)
477 jobs.new(sleepfunc,4)
484 jobs.new(sleepfunc,kw={'reps':2})
478 jobs.new(sleepfunc,kw={'reps':2})
485 # This makes a job which will die
479 # This makes a job which will die
486 jobs.new(diefunc,1)
480 jobs.new(diefunc,1)
487 jobs.new('printfunc(1,3)')
481 jobs.new('printfunc(1,3)')
488
482
489 # after a while, you can get the traceback of a dead job. Run the line
483 # after a while, you can get the traceback of a dead job. Run the line
490 # below again interactively until it prints a traceback (check the status
484 # below again interactively until it prints a traceback (check the status
491 # of the job):
485 # of the job):
492 print jobs[1].status
486 print jobs[1].status
493 jobs[1].traceback()
487 jobs[1].traceback()
494
488
495 # Run this line again until the printed result changes
489 # Run this line again until the printed result changes
496 print "The result of job #0 is:",jobs[0].result
490 print "The result of job #0 is:",jobs[0].result
@@ -1,189 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 A module to change reload() so that it acts recursively.
3 A module to change reload() so that it acts recursively.
4 To enable it type:
4 To enable it type:
5 >>> import __builtin__, deep_reload
5 >>> import __builtin__, deep_reload
6 >>> __builtin__.reload = deep_reload.reload
6 >>> __builtin__.reload = deep_reload.reload
7
7
8 You can then disable it with:
8 You can then disable it with:
9 >>> __builtin__.reload = deep_reload.original_reload
9 >>> __builtin__.reload = deep_reload.original_reload
10
10
11 Alternatively, you can add a dreload builtin alongside normal reload with:
11 Alternatively, you can add a dreload builtin alongside normal reload with:
12 >>> __builtin__.dreload = deep_reload.reload
12 >>> __builtin__.dreload = deep_reload.reload
13
13
14 This code is almost entirely based on knee.py from the standard library.
14 This code is almost entirely based on knee.py from the standard library.
15
15 """
16 $Id: deep_reload.py 958 2005-12-27 23:17:51Z fperez $"""
17
16
18 #*****************************************************************************
17 #*****************************************************************************
19 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
18 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
20 #
19 #
21 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
23 #*****************************************************************************
22 #*****************************************************************************
24
23
25 from IPython import Release # do it explicitly so pydoc can see it - pydoc bug
26 __author__ = '%s <%s>' % Release.authors['Nathan']
27 __license__ = Release.license
28 __version__ = "0.5"
29 __date__ = "21 August 2001"
30
31 import __builtin__
24 import __builtin__
32 import imp
25 import imp
33 import sys
26 import sys
34
27
35 # Replacement for __import__()
28 # Replacement for __import__()
36 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
29 def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1):
37 # For now level is ignored, it's just there to prevent crash
30 # For now level is ignored, it's just there to prevent crash
38 # with from __future__ import absolute_import
31 # with from __future__ import absolute_import
39 parent = determine_parent(globals)
32 parent = determine_parent(globals)
40 q, tail = find_head_package(parent, name)
33 q, tail = find_head_package(parent, name)
41 m = load_tail(q, tail)
34 m = load_tail(q, tail)
42 if not fromlist:
35 if not fromlist:
43 return q
36 return q
44 if hasattr(m, "__path__"):
37 if hasattr(m, "__path__"):
45 ensure_fromlist(m, fromlist)
38 ensure_fromlist(m, fromlist)
46 return m
39 return m
47
40
48 def determine_parent(globals):
41 def determine_parent(globals):
49 if not globals or not globals.has_key("__name__"):
42 if not globals or not globals.has_key("__name__"):
50 return None
43 return None
51 pname = globals['__name__']
44 pname = globals['__name__']
52 if globals.has_key("__path__"):
45 if globals.has_key("__path__"):
53 parent = sys.modules[pname]
46 parent = sys.modules[pname]
54 assert globals is parent.__dict__
47 assert globals is parent.__dict__
55 return parent
48 return parent
56 if '.' in pname:
49 if '.' in pname:
57 i = pname.rfind('.')
50 i = pname.rfind('.')
58 pname = pname[:i]
51 pname = pname[:i]
59 parent = sys.modules[pname]
52 parent = sys.modules[pname]
60 assert parent.__name__ == pname
53 assert parent.__name__ == pname
61 return parent
54 return parent
62 return None
55 return None
63
56
64 def find_head_package(parent, name):
57 def find_head_package(parent, name):
65 # Import the first
58 # Import the first
66 if '.' in name:
59 if '.' in name:
67 # 'some.nested.package' -> head = 'some', tail = 'nested.package'
60 # 'some.nested.package' -> head = 'some', tail = 'nested.package'
68 i = name.find('.')
61 i = name.find('.')
69 head = name[:i]
62 head = name[:i]
70 tail = name[i+1:]
63 tail = name[i+1:]
71 else:
64 else:
72 # 'packagename' -> head = 'packagename', tail = ''
65 # 'packagename' -> head = 'packagename', tail = ''
73 head = name
66 head = name
74 tail = ""
67 tail = ""
75 if parent:
68 if parent:
76 # If this is a subpackage then qname = parent's name + head
69 # If this is a subpackage then qname = parent's name + head
77 qname = "%s.%s" % (parent.__name__, head)
70 qname = "%s.%s" % (parent.__name__, head)
78 else:
71 else:
79 qname = head
72 qname = head
80 q = import_module(head, qname, parent)
73 q = import_module(head, qname, parent)
81 if q: return q, tail
74 if q: return q, tail
82 if parent:
75 if parent:
83 qname = head
76 qname = head
84 parent = None
77 parent = None
85 q = import_module(head, qname, parent)
78 q = import_module(head, qname, parent)
86 if q: return q, tail
79 if q: return q, tail
87 raise ImportError, "No module named " + qname
80 raise ImportError, "No module named " + qname
88
81
89 def load_tail(q, tail):
82 def load_tail(q, tail):
90 m = q
83 m = q
91 while tail:
84 while tail:
92 i = tail.find('.')
85 i = tail.find('.')
93 if i < 0: i = len(tail)
86 if i < 0: i = len(tail)
94 head, tail = tail[:i], tail[i+1:]
87 head, tail = tail[:i], tail[i+1:]
95
88
96 # fperez: fix dotted.name reloading failures by changing:
89 # fperez: fix dotted.name reloading failures by changing:
97 #mname = "%s.%s" % (m.__name__, head)
90 #mname = "%s.%s" % (m.__name__, head)
98 # to:
91 # to:
99 mname = m.__name__
92 mname = m.__name__
100 # This needs more testing!!! (I don't understand this module too well)
93 # This needs more testing!!! (I don't understand this module too well)
101
94
102 #print '** head,tail=|%s|->|%s|, mname=|%s|' % (head,tail,mname) # dbg
95 #print '** head,tail=|%s|->|%s|, mname=|%s|' % (head,tail,mname) # dbg
103 m = import_module(head, mname, m)
96 m = import_module(head, mname, m)
104 if not m:
97 if not m:
105 raise ImportError, "No module named " + mname
98 raise ImportError, "No module named " + mname
106 return m
99 return m
107
100
108 def ensure_fromlist(m, fromlist, recursive=0):
101 def ensure_fromlist(m, fromlist, recursive=0):
109 for sub in fromlist:
102 for sub in fromlist:
110 if sub == "*":
103 if sub == "*":
111 if not recursive:
104 if not recursive:
112 try:
105 try:
113 all = m.__all__
106 all = m.__all__
114 except AttributeError:
107 except AttributeError:
115 pass
108 pass
116 else:
109 else:
117 ensure_fromlist(m, all, 1)
110 ensure_fromlist(m, all, 1)
118 continue
111 continue
119 if sub != "*" and not hasattr(m, sub):
112 if sub != "*" and not hasattr(m, sub):
120 subname = "%s.%s" % (m.__name__, sub)
113 subname = "%s.%s" % (m.__name__, sub)
121 submod = import_module(sub, subname, m)
114 submod = import_module(sub, subname, m)
122 if not submod:
115 if not submod:
123 raise ImportError, "No module named " + subname
116 raise ImportError, "No module named " + subname
124
117
125 # Need to keep track of what we've already reloaded to prevent cyclic evil
118 # Need to keep track of what we've already reloaded to prevent cyclic evil
126 found_now = {}
119 found_now = {}
127
120
128 def import_module(partname, fqname, parent):
121 def import_module(partname, fqname, parent):
129 global found_now
122 global found_now
130 if found_now.has_key(fqname):
123 if found_now.has_key(fqname):
131 try:
124 try:
132 return sys.modules[fqname]
125 return sys.modules[fqname]
133 except KeyError:
126 except KeyError:
134 pass
127 pass
135
128
136 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
129 print 'Reloading', fqname #, sys.excepthook is sys.__excepthook__, \
137 #sys.displayhook is sys.__displayhook__
130 #sys.displayhook is sys.__displayhook__
138
131
139 found_now[fqname] = 1
132 found_now[fqname] = 1
140 try:
133 try:
141 fp, pathname, stuff = imp.find_module(partname,
134 fp, pathname, stuff = imp.find_module(partname,
142 parent and parent.__path__)
135 parent and parent.__path__)
143 except ImportError:
136 except ImportError:
144 return None
137 return None
145
138
146 try:
139 try:
147 m = imp.load_module(fqname, fp, pathname, stuff)
140 m = imp.load_module(fqname, fp, pathname, stuff)
148 finally:
141 finally:
149 if fp: fp.close()
142 if fp: fp.close()
150
143
151 if parent:
144 if parent:
152 setattr(parent, partname, m)
145 setattr(parent, partname, m)
153
146
154 return m
147 return m
155
148
156 def deep_reload_hook(module):
149 def deep_reload_hook(module):
157 name = module.__name__
150 name = module.__name__
158 if '.' not in name:
151 if '.' not in name:
159 return import_module(name, name, None)
152 return import_module(name, name, None)
160 i = name.rfind('.')
153 i = name.rfind('.')
161 pname = name[:i]
154 pname = name[:i]
162 parent = sys.modules[pname]
155 parent = sys.modules[pname]
163 return import_module(name[i+1:], name, parent)
156 return import_module(name[i+1:], name, parent)
164
157
165 # Save the original hooks
158 # Save the original hooks
166 original_reload = __builtin__.reload
159 original_reload = __builtin__.reload
167
160
168 # Replacement for reload()
161 # Replacement for reload()
169 def reload(module, exclude=['sys', '__builtin__', '__main__']):
162 def reload(module, exclude=['sys', '__builtin__', '__main__']):
170 """Recursively reload all modules used in the given module. Optionally
163 """Recursively reload all modules used in the given module. Optionally
171 takes a list of modules to exclude from reloading. The default exclude
164 takes a list of modules to exclude from reloading. The default exclude
172 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
165 list contains sys, __main__, and __builtin__, to prevent, e.g., resetting
173 display, exception, and io hooks.
166 display, exception, and io hooks.
174 """
167 """
175 global found_now
168 global found_now
176 for i in exclude:
169 for i in exclude:
177 found_now[i] = 1
170 found_now[i] = 1
178 original_import = __builtin__.__import__
171 original_import = __builtin__.__import__
179 __builtin__.__import__ = deep_import_hook
172 __builtin__.__import__ = deep_import_hook
180 try:
173 try:
181 ret = deep_reload_hook(module)
174 ret = deep_reload_hook(module)
182 finally:
175 finally:
183 __builtin__.__import__ = original_import
176 __builtin__.__import__ = original_import
184 found_now = {}
177 found_now = {}
185 return ret
178 return ret
186
179
187 # Uncomment the following to automatically activate deep reloading whenever
180 # Uncomment the following to automatically activate deep reloading whenever
188 # this module is imported
181 # this module is imported
189 #__builtin__.reload = reload
182 #__builtin__.reload = reload
@@ -1,143 +1,137 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Color schemes for exception handling code in IPython.
3 Color schemes for exception handling code in IPython.
4
4 """
5 $Id: Prompts.py 638 2005-07-18 03:01:41Z fperez $"""
6
5
7 #*****************************************************************************
6 #*****************************************************************************
8 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
9 #
8 #
10 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
12 #*****************************************************************************
11 #*****************************************************************************
13
12
14 from IPython import Release
15 __author__ = '%s <%s>' % Release.authors['Fernando']
16 __license__ = Release.license
17 __version__ = Release.version
18
19 #****************************************************************************
13 #****************************************************************************
20 # Required modules
14 # Required modules
21 from IPython.ColorANSI import ColorSchemeTable, TermColors, ColorScheme
15 from IPython.ColorANSI import ColorSchemeTable, TermColors, ColorScheme
22
16
23 def exception_colors():
17 def exception_colors():
24 """Return a color table with fields for exception reporting.
18 """Return a color table with fields for exception reporting.
25
19
26 The table is an instance of ColorSchemeTable with schemes added for
20 The table is an instance of ColorSchemeTable with schemes added for
27 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
21 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
28 in.
22 in.
29
23
30 Examples:
24 Examples:
31
25
32 >>> ec = exception_colors()
26 >>> ec = exception_colors()
33 >>> ec.active_scheme_name
27 >>> ec.active_scheme_name
34 ''
28 ''
35 >>> print ec.active_colors
29 >>> print ec.active_colors
36 None
30 None
37
31
38 Now we activate a color scheme:
32 Now we activate a color scheme:
39 >>> ec.set_active_scheme('NoColor')
33 >>> ec.set_active_scheme('NoColor')
40 >>> ec.active_scheme_name
34 >>> ec.active_scheme_name
41 'NoColor'
35 'NoColor'
42 >>> ec.active_colors.keys()
36 >>> ec.active_colors.keys()
43 ['em', 'caret', '__allownew', 'name', 'val', 'vName', 'Normal', 'normalEm',
37 ['em', 'caret', '__allownew', 'name', 'val', 'vName', 'Normal', 'normalEm',
44 'filename', 'linenoEm', 'excName', 'lineno', 'valEm', 'filenameEm',
38 'filename', 'linenoEm', 'excName', 'lineno', 'valEm', 'filenameEm',
45 'nameEm', 'line', 'topline']
39 'nameEm', 'line', 'topline']
46 """
40 """
47
41
48 ex_colors = ColorSchemeTable()
42 ex_colors = ColorSchemeTable()
49
43
50 # Populate it with color schemes
44 # Populate it with color schemes
51 C = TermColors # shorthand and local lookup
45 C = TermColors # shorthand and local lookup
52 ex_colors.add_scheme(ColorScheme(
46 ex_colors.add_scheme(ColorScheme(
53 'NoColor',
47 'NoColor',
54 # The color to be used for the top line
48 # The color to be used for the top line
55 topline = C.NoColor,
49 topline = C.NoColor,
56
50
57 # The colors to be used in the traceback
51 # The colors to be used in the traceback
58 filename = C.NoColor,
52 filename = C.NoColor,
59 lineno = C.NoColor,
53 lineno = C.NoColor,
60 name = C.NoColor,
54 name = C.NoColor,
61 vName = C.NoColor,
55 vName = C.NoColor,
62 val = C.NoColor,
56 val = C.NoColor,
63 em = C.NoColor,
57 em = C.NoColor,
64
58
65 # Emphasized colors for the last frame of the traceback
59 # Emphasized colors for the last frame of the traceback
66 normalEm = C.NoColor,
60 normalEm = C.NoColor,
67 filenameEm = C.NoColor,
61 filenameEm = C.NoColor,
68 linenoEm = C.NoColor,
62 linenoEm = C.NoColor,
69 nameEm = C.NoColor,
63 nameEm = C.NoColor,
70 valEm = C.NoColor,
64 valEm = C.NoColor,
71
65
72 # Colors for printing the exception
66 # Colors for printing the exception
73 excName = C.NoColor,
67 excName = C.NoColor,
74 line = C.NoColor,
68 line = C.NoColor,
75 caret = C.NoColor,
69 caret = C.NoColor,
76 Normal = C.NoColor
70 Normal = C.NoColor
77 ))
71 ))
78
72
79 # make some schemes as instances so we can copy them for modification easily
73 # make some schemes as instances so we can copy them for modification easily
80 ex_colors.add_scheme(ColorScheme(
74 ex_colors.add_scheme(ColorScheme(
81 'Linux',
75 'Linux',
82 # The color to be used for the top line
76 # The color to be used for the top line
83 topline = C.LightRed,
77 topline = C.LightRed,
84
78
85 # The colors to be used in the traceback
79 # The colors to be used in the traceback
86 filename = C.Green,
80 filename = C.Green,
87 lineno = C.Green,
81 lineno = C.Green,
88 name = C.Purple,
82 name = C.Purple,
89 vName = C.Cyan,
83 vName = C.Cyan,
90 val = C.Green,
84 val = C.Green,
91 em = C.LightCyan,
85 em = C.LightCyan,
92
86
93 # Emphasized colors for the last frame of the traceback
87 # Emphasized colors for the last frame of the traceback
94 normalEm = C.LightCyan,
88 normalEm = C.LightCyan,
95 filenameEm = C.LightGreen,
89 filenameEm = C.LightGreen,
96 linenoEm = C.LightGreen,
90 linenoEm = C.LightGreen,
97 nameEm = C.LightPurple,
91 nameEm = C.LightPurple,
98 valEm = C.LightBlue,
92 valEm = C.LightBlue,
99
93
100 # Colors for printing the exception
94 # Colors for printing the exception
101 excName = C.LightRed,
95 excName = C.LightRed,
102 line = C.Yellow,
96 line = C.Yellow,
103 caret = C.White,
97 caret = C.White,
104 Normal = C.Normal
98 Normal = C.Normal
105 ))
99 ))
106
100
107 # For light backgrounds, swap dark/light colors
101 # For light backgrounds, swap dark/light colors
108 ex_colors.add_scheme(ColorScheme(
102 ex_colors.add_scheme(ColorScheme(
109 'LightBG',
103 'LightBG',
110 # The color to be used for the top line
104 # The color to be used for the top line
111 topline = C.Red,
105 topline = C.Red,
112
106
113 # The colors to be used in the traceback
107 # The colors to be used in the traceback
114 filename = C.LightGreen,
108 filename = C.LightGreen,
115 lineno = C.LightGreen,
109 lineno = C.LightGreen,
116 name = C.LightPurple,
110 name = C.LightPurple,
117 vName = C.Cyan,
111 vName = C.Cyan,
118 val = C.LightGreen,
112 val = C.LightGreen,
119 em = C.Cyan,
113 em = C.Cyan,
120
114
121 # Emphasized colors for the last frame of the traceback
115 # Emphasized colors for the last frame of the traceback
122 normalEm = C.Cyan,
116 normalEm = C.Cyan,
123 filenameEm = C.Green,
117 filenameEm = C.Green,
124 linenoEm = C.Green,
118 linenoEm = C.Green,
125 nameEm = C.Purple,
119 nameEm = C.Purple,
126 valEm = C.Blue,
120 valEm = C.Blue,
127
121
128 # Colors for printing the exception
122 # Colors for printing the exception
129 excName = C.Red,
123 excName = C.Red,
130 #line = C.Brown, # brown often is displayed as yellow
124 #line = C.Brown, # brown often is displayed as yellow
131 line = C.Red,
125 line = C.Red,
132 caret = C.Normal,
126 caret = C.Normal,
133 Normal = C.Normal
127 Normal = C.Normal
134 ))
128 ))
135
129
136 return ex_colors
130 return ex_colors
137
131
138
132
139 # For backwards compatibility, keep around a single global object. Note that
133 # For backwards compatibility, keep around a single global object. Note that
140 # this should NOT be used, the factory function should be used instead, since
134 # this should NOT be used, the factory function should be used instead, since
141 # these objects are stateful and it's very easy to get strange bugs if any code
135 # these objects are stateful and it's very easy to get strange bugs if any code
142 # modifies the module-level object's state.
136 # modifies the module-level object's state.
143 ExceptionColors = exception_colors()
137 ExceptionColors = exception_colors()
@@ -1,278 +1,276 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
3
3
4 This module lets you quickly and conveniently interpolate values into
4 This module lets you quickly and conveniently interpolate values into
5 strings (in the flavour of Perl or Tcl, but with less extraneous
5 strings (in the flavour of Perl or Tcl, but with less extraneous
6 punctuation). You get a bit more power than in the other languages,
6 punctuation). You get a bit more power than in the other languages,
7 because this module allows subscripting, slicing, function calls,
7 because this module allows subscripting, slicing, function calls,
8 attribute lookup, or arbitrary expressions. Variables and expressions
8 attribute lookup, or arbitrary expressions. Variables and expressions
9 are evaluated in the namespace of the caller.
9 are evaluated in the namespace of the caller.
10
10
11 The itpl() function returns the result of interpolating a string, and
11 The itpl() function returns the result of interpolating a string, and
12 printpl() prints out an interpolated string. Here are some examples:
12 printpl() prints out an interpolated string. Here are some examples:
13
13
14 from Itpl import printpl
14 from Itpl import printpl
15 printpl("Here is a $string.")
15 printpl("Here is a $string.")
16 printpl("Here is a $module.member.")
16 printpl("Here is a $module.member.")
17 printpl("Here is an $object.member.")
17 printpl("Here is an $object.member.")
18 printpl("Here is a $functioncall(with, arguments).")
18 printpl("Here is a $functioncall(with, arguments).")
19 printpl("Here is an ${arbitrary + expression}.")
19 printpl("Here is an ${arbitrary + expression}.")
20 printpl("Here is an $array[3] member.")
20 printpl("Here is an $array[3] member.")
21 printpl("Here is a $dictionary['member'].")
21 printpl("Here is a $dictionary['member'].")
22
22
23 The filter() function filters a file object so that output through it
23 The filter() function filters a file object so that output through it
24 is interpolated. This lets you produce the illusion that Python knows
24 is interpolated. This lets you produce the illusion that Python knows
25 how to do interpolation:
25 how to do interpolation:
26
26
27 import Itpl
27 import Itpl
28 sys.stdout = Itpl.filter()
28 sys.stdout = Itpl.filter()
29 f = "fancy"
29 f = "fancy"
30 print "Isn't this $f?"
30 print "Is this not $f?"
31 print "Standard output has been replaced with a $sys.stdout object."
31 print "Standard output has been replaced with a $sys.stdout object."
32 sys.stdout = Itpl.unfilter()
32 sys.stdout = Itpl.unfilter()
33 print "Okay, back $to $normal."
33 print "Okay, back $to $normal."
34
34
35 Under the hood, the Itpl class represents a string that knows how to
35 Under the hood, the Itpl class represents a string that knows how to
36 interpolate values. An instance of the class parses the string once
36 interpolate values. An instance of the class parses the string once
37 upon initialization; the evaluation and substitution can then be done
37 upon initialization; the evaluation and substitution can then be done
38 each time the instance is evaluated with str(instance). For example:
38 each time the instance is evaluated with str(instance). For example:
39
39
40 from Itpl import Itpl
40 from Itpl import Itpl
41 s = Itpl("Here is $foo.")
41 s = Itpl("Here is $foo.")
42 foo = 5
42 foo = 5
43 print str(s)
43 print str(s)
44 foo = "bar"
44 foo = "bar"
45 print str(s)
45 print str(s)
46
46 """
47 $Id: Itpl.py 2305 2007-05-04 05:34:42Z bgranger $
48 """ # ' -> close an open quote for stupid emacs
49
47
50 #*****************************************************************************
48 #*****************************************************************************
51 #
49 #
52 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
50 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
53 #
51 #
54 #
52 #
55 # Published under the terms of the MIT license, hereby reproduced:
53 # Published under the terms of the MIT license, hereby reproduced:
56 #
54 #
57 # Permission is hereby granted, free of charge, to any person obtaining a copy
55 # Permission is hereby granted, free of charge, to any person obtaining a copy
58 # of this software and associated documentation files (the "Software"), to
56 # of this software and associated documentation files (the "Software"), to
59 # deal in the Software without restriction, including without limitation the
57 # deal in the Software without restriction, including without limitation the
60 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
58 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
61 # sell copies of the Software, and to permit persons to whom the Software is
59 # sell copies of the Software, and to permit persons to whom the Software is
62 # furnished to do so, subject to the following conditions:
60 # furnished to do so, subject to the following conditions:
63 #
61 #
64 # The above copyright notice and this permission notice shall be included in
62 # The above copyright notice and this permission notice shall be included in
65 # all copies or substantial portions of the Software.
63 # all copies or substantial portions of the Software.
66 #
64 #
67 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
69 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
72 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
70 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
73 # IN THE SOFTWARE.
71 # IN THE SOFTWARE.
74 #
72 #
75 #*****************************************************************************
73 #*****************************************************************************
76
74
77 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
75 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
78 __license__ = 'MIT'
76 __license__ = 'MIT'
79
77
80 import string
78 import string
81 import sys
79 import sys
82 from tokenize import tokenprog
80 from tokenize import tokenprog
83 from types import StringType
81 from types import StringType
84
82
85 class ItplError(ValueError):
83 class ItplError(ValueError):
86 def __init__(self, text, pos):
84 def __init__(self, text, pos):
87 self.text = text
85 self.text = text
88 self.pos = pos
86 self.pos = pos
89 def __str__(self):
87 def __str__(self):
90 return "unfinished expression in %s at char %d" % (
88 return "unfinished expression in %s at char %d" % (
91 repr(self.text), self.pos)
89 repr(self.text), self.pos)
92
90
93 def matchorfail(text, pos):
91 def matchorfail(text, pos):
94 match = tokenprog.match(text, pos)
92 match = tokenprog.match(text, pos)
95 if match is None:
93 if match is None:
96 raise ItplError(text, pos)
94 raise ItplError(text, pos)
97 return match, match.end()
95 return match, match.end()
98
96
99 class Itpl:
97 class Itpl:
100 """Class representing a string with interpolation abilities.
98 """Class representing a string with interpolation abilities.
101
99
102 Upon creation, an instance works out what parts of the format
100 Upon creation, an instance works out what parts of the format
103 string are literal and what parts need to be evaluated. The
101 string are literal and what parts need to be evaluated. The
104 evaluation and substitution happens in the namespace of the
102 evaluation and substitution happens in the namespace of the
105 caller when str(instance) is called."""
103 caller when str(instance) is called."""
106
104
107 def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'):
105 def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'):
108 """The single mandatory argument to this constructor is a format
106 """The single mandatory argument to this constructor is a format
109 string.
107 string.
110
108
111 The format string is parsed according to the following rules:
109 The format string is parsed according to the following rules:
112
110
113 1. A dollar sign and a name, possibly followed by any of:
111 1. A dollar sign and a name, possibly followed by any of:
114 - an open-paren, and anything up to the matching paren
112 - an open-paren, and anything up to the matching paren
115 - an open-bracket, and anything up to the matching bracket
113 - an open-bracket, and anything up to the matching bracket
116 - a period and a name
114 - a period and a name
117 any number of times, is evaluated as a Python expression.
115 any number of times, is evaluated as a Python expression.
118
116
119 2. A dollar sign immediately followed by an open-brace, and
117 2. A dollar sign immediately followed by an open-brace, and
120 anything up to the matching close-brace, is evaluated as
118 anything up to the matching close-brace, is evaluated as
121 a Python expression.
119 a Python expression.
122
120
123 3. Outside of the expressions described in the above two rules,
121 3. Outside of the expressions described in the above two rules,
124 two dollar signs in a row give you one literal dollar sign.
122 two dollar signs in a row give you one literal dollar sign.
125
123
126 Optional arguments:
124 Optional arguments:
127
125
128 - codec('utf_8'): a string containing the name of a valid Python
126 - codec('utf_8'): a string containing the name of a valid Python
129 codec.
127 codec.
130
128
131 - encoding_errors('backslashreplace'): a string with a valid error handling
129 - encoding_errors('backslashreplace'): a string with a valid error handling
132 policy. See the codecs module documentation for details.
130 policy. See the codecs module documentation for details.
133
131
134 These are used to encode the format string if a call to str() fails on
132 These are used to encode the format string if a call to str() fails on
135 the expanded result."""
133 the expanded result."""
136
134
137 if not isinstance(format,basestring):
135 if not isinstance(format,basestring):
138 raise TypeError, "needs string initializer"
136 raise TypeError, "needs string initializer"
139 self.format = format
137 self.format = format
140 self.codec = codec
138 self.codec = codec
141 self.encoding_errors = encoding_errors
139 self.encoding_errors = encoding_errors
142
140
143 namechars = "abcdefghijklmnopqrstuvwxyz" \
141 namechars = "abcdefghijklmnopqrstuvwxyz" \
144 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
142 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
145 chunks = []
143 chunks = []
146 pos = 0
144 pos = 0
147
145
148 while 1:
146 while 1:
149 dollar = string.find(format, "$", pos)
147 dollar = string.find(format, "$", pos)
150 if dollar < 0: break
148 if dollar < 0: break
151 nextchar = format[dollar+1]
149 nextchar = format[dollar+1]
152
150
153 if nextchar == "{":
151 if nextchar == "{":
154 chunks.append((0, format[pos:dollar]))
152 chunks.append((0, format[pos:dollar]))
155 pos, level = dollar+2, 1
153 pos, level = dollar+2, 1
156 while level:
154 while level:
157 match, pos = matchorfail(format, pos)
155 match, pos = matchorfail(format, pos)
158 tstart, tend = match.regs[3]
156 tstart, tend = match.regs[3]
159 token = format[tstart:tend]
157 token = format[tstart:tend]
160 if token == "{": level = level+1
158 if token == "{": level = level+1
161 elif token == "}": level = level-1
159 elif token == "}": level = level-1
162 chunks.append((1, format[dollar+2:pos-1]))
160 chunks.append((1, format[dollar+2:pos-1]))
163
161
164 elif nextchar in namechars:
162 elif nextchar in namechars:
165 chunks.append((0, format[pos:dollar]))
163 chunks.append((0, format[pos:dollar]))
166 match, pos = matchorfail(format, dollar+1)
164 match, pos = matchorfail(format, dollar+1)
167 while pos < len(format):
165 while pos < len(format):
168 if format[pos] == "." and \
166 if format[pos] == "." and \
169 pos+1 < len(format) and format[pos+1] in namechars:
167 pos+1 < len(format) and format[pos+1] in namechars:
170 match, pos = matchorfail(format, pos+1)
168 match, pos = matchorfail(format, pos+1)
171 elif format[pos] in "([":
169 elif format[pos] in "([":
172 pos, level = pos+1, 1
170 pos, level = pos+1, 1
173 while level:
171 while level:
174 match, pos = matchorfail(format, pos)
172 match, pos = matchorfail(format, pos)
175 tstart, tend = match.regs[3]
173 tstart, tend = match.regs[3]
176 token = format[tstart:tend]
174 token = format[tstart:tend]
177 if token[0] in "([": level = level+1
175 if token[0] in "([": level = level+1
178 elif token[0] in ")]": level = level-1
176 elif token[0] in ")]": level = level-1
179 else: break
177 else: break
180 chunks.append((1, format[dollar+1:pos]))
178 chunks.append((1, format[dollar+1:pos]))
181
179
182 else:
180 else:
183 chunks.append((0, format[pos:dollar+1]))
181 chunks.append((0, format[pos:dollar+1]))
184 pos = dollar + 1 + (nextchar == "$")
182 pos = dollar + 1 + (nextchar == "$")
185
183
186 if pos < len(format): chunks.append((0, format[pos:]))
184 if pos < len(format): chunks.append((0, format[pos:]))
187 self.chunks = chunks
185 self.chunks = chunks
188
186
189 def __repr__(self):
187 def __repr__(self):
190 return "<Itpl %s >" % repr(self.format)
188 return "<Itpl %s >" % repr(self.format)
191
189
192 def _str(self,glob,loc):
190 def _str(self,glob,loc):
193 """Evaluate to a string in the given globals/locals.
191 """Evaluate to a string in the given globals/locals.
194
192
195 The final output is built by calling str(), but if this fails, the
193 The final output is built by calling str(), but if this fails, the
196 result is encoded with the instance's codec and error handling policy,
194 result is encoded with the instance's codec and error handling policy,
197 via a call to out.encode(self.codec,self.encoding_errors)"""
195 via a call to out.encode(self.codec,self.encoding_errors)"""
198 result = []
196 result = []
199 app = result.append
197 app = result.append
200 for live, chunk in self.chunks:
198 for live, chunk in self.chunks:
201 if live: app(str(eval(chunk,glob,loc)))
199 if live: app(str(eval(chunk,glob,loc)))
202 else: app(chunk)
200 else: app(chunk)
203 out = ''.join(result)
201 out = ''.join(result)
204 try:
202 try:
205 return str(out)
203 return str(out)
206 except UnicodeError:
204 except UnicodeError:
207 return out.encode(self.codec,self.encoding_errors)
205 return out.encode(self.codec,self.encoding_errors)
208
206
209 def __str__(self):
207 def __str__(self):
210 """Evaluate and substitute the appropriate parts of the string."""
208 """Evaluate and substitute the appropriate parts of the string."""
211
209
212 # We need to skip enough frames to get to the actual caller outside of
210 # We need to skip enough frames to get to the actual caller outside of
213 # Itpl.
211 # Itpl.
214 frame = sys._getframe(1)
212 frame = sys._getframe(1)
215 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
213 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
216 loc, glob = frame.f_locals, frame.f_globals
214 loc, glob = frame.f_locals, frame.f_globals
217
215
218 return self._str(glob,loc)
216 return self._str(glob,loc)
219
217
220 class ItplNS(Itpl):
218 class ItplNS(Itpl):
221 """Class representing a string with interpolation abilities.
219 """Class representing a string with interpolation abilities.
222
220
223 This inherits from Itpl, but at creation time a namespace is provided
221 This inherits from Itpl, but at creation time a namespace is provided
224 where the evaluation will occur. The interpolation becomes a bit more
222 where the evaluation will occur. The interpolation becomes a bit more
225 efficient, as no traceback needs to be extracte. It also allows the
223 efficient, as no traceback needs to be extracte. It also allows the
226 caller to supply a different namespace for the interpolation to occur than
224 caller to supply a different namespace for the interpolation to occur than
227 its own."""
225 its own."""
228
226
229 def __init__(self, format,globals,locals=None,
227 def __init__(self, format,globals,locals=None,
230 codec='utf_8',encoding_errors='backslashreplace'):
228 codec='utf_8',encoding_errors='backslashreplace'):
231 """ItplNS(format,globals[,locals]) -> interpolating string instance.
229 """ItplNS(format,globals[,locals]) -> interpolating string instance.
232
230
233 This constructor, besides a format string, takes a globals dictionary
231 This constructor, besides a format string, takes a globals dictionary
234 and optionally a locals (which defaults to globals if not provided).
232 and optionally a locals (which defaults to globals if not provided).
235
233
236 For further details, see the Itpl constructor."""
234 For further details, see the Itpl constructor."""
237
235
238 if locals is None:
236 if locals is None:
239 locals = globals
237 locals = globals
240 self.globals = globals
238 self.globals = globals
241 self.locals = locals
239 self.locals = locals
242 Itpl.__init__(self,format,codec,encoding_errors)
240 Itpl.__init__(self,format,codec,encoding_errors)
243
241
244 def __str__(self):
242 def __str__(self):
245 """Evaluate and substitute the appropriate parts of the string."""
243 """Evaluate and substitute the appropriate parts of the string."""
246 return self._str(self.globals,self.locals)
244 return self._str(self.globals,self.locals)
247
245
248 def __repr__(self):
246 def __repr__(self):
249 return "<ItplNS %s >" % repr(self.format)
247 return "<ItplNS %s >" % repr(self.format)
250
248
251 # utilities for fast printing
249 # utilities for fast printing
252 def itpl(text): return str(Itpl(text))
250 def itpl(text): return str(Itpl(text))
253 def printpl(text): print itpl(text)
251 def printpl(text): print itpl(text)
254 # versions with namespace
252 # versions with namespace
255 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
253 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
256 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
254 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
257
255
258 class ItplFile:
256 class ItplFile:
259 """A file object that filters each write() through an interpolator."""
257 """A file object that filters each write() through an interpolator."""
260 def __init__(self, file): self.file = file
258 def __init__(self, file): self.file = file
261 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
259 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
262 def __getattr__(self, attr): return getattr(self.file, attr)
260 def __getattr__(self, attr): return getattr(self.file, attr)
263 def write(self, text): self.file.write(str(Itpl(text)))
261 def write(self, text): self.file.write(str(Itpl(text)))
264
262
265 def filter(file=sys.stdout):
263 def filter(file=sys.stdout):
266 """Return an ItplFile that filters writes to the given file object.
264 """Return an ItplFile that filters writes to the given file object.
267
265
268 'file = filter(file)' replaces 'file' with a filtered object that
266 'file = filter(file)' replaces 'file' with a filtered object that
269 has a write() method. When called with no argument, this creates
267 has a write() method. When called with no argument, this creates
270 a filter to sys.stdout."""
268 a filter to sys.stdout."""
271 return ItplFile(file)
269 return ItplFile(file)
272
270
273 def unfilter(ifile=None):
271 def unfilter(ifile=None):
274 """Return the original file that corresponds to the given ItplFile.
272 """Return the original file that corresponds to the given ItplFile.
275
273
276 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
274 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
277 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
275 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
278 return ifile and ifile.file or sys.stdout.file
276 return ifile and ifile.file or sys.stdout.file
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now