Show More
The requested changes are too big and content was truncated. Show full diff
@@ -1,170 +1,170 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 $""" |
|
4 | $Id: ColorANSI.py 2167 2007-03-21 06:57:50Z fperez $""" | |
5 |
|
5 | |||
6 | #***************************************************************************** |
|
6 | #***************************************************************************** | |
7 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> |
|
7 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> | |
8 | # |
|
8 | # | |
9 | # 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 | |
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #***************************************************************************** |
|
11 | #***************************************************************************** | |
12 |
|
12 | |||
13 | from IPython import Release |
|
13 | from IPython import Release | |
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
15 | __license__ = Release.license |
|
15 | __license__ = Release.license | |
16 |
|
16 | |||
17 | __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] |
|
17 | __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] | |
18 |
|
18 | |||
19 | import os |
|
19 | import os | |
20 |
|
20 | |||
21 | from IPython.ipstruct import Struct |
|
21 | from IPython.ipstruct import Struct | |
22 |
|
22 | |||
23 | def make_color_table(in_class): |
|
23 | def make_color_table(in_class): | |
24 | """Build a set of color attributes in a class. |
|
24 | """Build a set of color attributes in a class. | |
25 |
|
25 | |||
26 | Helper function for building the *TermColors classes.""" |
|
26 | Helper function for building the *TermColors classes.""" | |
27 |
|
27 | |||
28 | color_templates = ( |
|
28 | color_templates = ( | |
29 | ("Black" , "0;30"), |
|
29 | ("Black" , "0;30"), | |
30 | ("Red" , "0;31"), |
|
30 | ("Red" , "0;31"), | |
31 | ("Green" , "0;32"), |
|
31 | ("Green" , "0;32"), | |
32 | ("Brown" , "0;33"), |
|
32 | ("Brown" , "0;33"), | |
33 | ("Blue" , "0;34"), |
|
33 | ("Blue" , "0;34"), | |
34 | ("Purple" , "0;35"), |
|
34 | ("Purple" , "0;35"), | |
35 | ("Cyan" , "0;36"), |
|
35 | ("Cyan" , "0;36"), | |
36 | ("LightGray" , "0;37"), |
|
36 | ("LightGray" , "0;37"), | |
37 | ("DarkGray" , "1;30"), |
|
37 | ("DarkGray" , "1;30"), | |
38 | ("LightRed" , "1;31"), |
|
38 | ("LightRed" , "1;31"), | |
39 | ("LightGreen" , "1;32"), |
|
39 | ("LightGreen" , "1;32"), | |
40 | ("Yellow" , "1;33"), |
|
40 | ("Yellow" , "1;33"), | |
41 | ("LightBlue" , "1;34"), |
|
41 | ("LightBlue" , "1;34"), | |
42 | ("LightPurple" , "1;35"), |
|
42 | ("LightPurple" , "1;35"), | |
43 | ("LightCyan" , "1;36"), |
|
43 | ("LightCyan" , "1;36"), | |
44 | ("White" , "1;37"), ) |
|
44 | ("White" , "1;37"), ) | |
45 |
|
45 | |||
46 | for name,value in color_templates: |
|
46 | for name,value in color_templates: | |
47 | setattr(in_class,name,in_class._base % value) |
|
47 | setattr(in_class,name,in_class._base % value) | |
48 |
|
48 | |||
49 | class TermColors: |
|
49 | class TermColors: | |
50 | """Color escape sequences. |
|
50 | """Color escape sequences. | |
51 |
|
51 | |||
52 | This class defines the escape sequences for all the standard (ANSI?) |
|
52 | This class defines the escape sequences for all the standard (ANSI?) | |
53 | colors in terminals. Also defines a NoColor escape which is just the null |
|
53 | colors in terminals. Also defines a NoColor escape which is just the null | |
54 | string, suitable for defining 'dummy' color schemes in terminals which get |
|
54 | string, suitable for defining 'dummy' color schemes in terminals which get | |
55 | confused by color escapes. |
|
55 | confused by color escapes. | |
56 |
|
56 | |||
57 | This class should be used as a mixin for building color schemes.""" |
|
57 | This class should be used as a mixin for building color schemes.""" | |
58 |
|
58 | |||
59 | NoColor = '' # for color schemes in color-less terminals. |
|
59 | NoColor = '' # for color schemes in color-less terminals. | |
60 | Normal = '\033[0m' # Reset normal coloring |
|
60 | Normal = '\033[0m' # Reset normal coloring | |
61 | _base = '\033[%sm' # Template for all other colors |
|
61 | _base = '\033[%sm' # Template for all other colors | |
62 |
|
62 | |||
63 | # Build the actual color table as a set of class attributes: |
|
63 | # Build the actual color table as a set of class attributes: | |
64 | make_color_table(TermColors) |
|
64 | make_color_table(TermColors) | |
65 |
|
65 | |||
66 | class InputTermColors: |
|
66 | class InputTermColors: | |
67 | """Color escape sequences for input prompts. |
|
67 | """Color escape sequences for input prompts. | |
68 |
|
68 | |||
69 | This class is similar to TermColors, but the escapes are wrapped in \001 |
|
69 | This class is similar to TermColors, but the escapes are wrapped in \001 | |
70 | and \002 so that readline can properly know the length of each line and |
|
70 | and \002 so that readline can properly know the length of each line and | |
71 | can wrap lines accordingly. Use this class for any colored text which |
|
71 | can wrap lines accordingly. Use this class for any colored text which | |
72 | needs to be used in input prompts, such as in calls to raw_input(). |
|
72 | needs to be used in input prompts, such as in calls to raw_input(). | |
73 |
|
73 | |||
74 | This class defines the escape sequences for all the standard (ANSI?) |
|
74 | This class defines the escape sequences for all the standard (ANSI?) | |
75 | colors in terminals. Also defines a NoColor escape which is just the null |
|
75 | colors in terminals. Also defines a NoColor escape which is just the null | |
76 | string, suitable for defining 'dummy' color schemes in terminals which get |
|
76 | string, suitable for defining 'dummy' color schemes in terminals which get | |
77 | confused by color escapes. |
|
77 | confused by color escapes. | |
78 |
|
78 | |||
79 | This class should be used as a mixin for building color schemes.""" |
|
79 | This class should be used as a mixin for building color schemes.""" | |
80 |
|
80 | |||
81 | NoColor = '' # for color schemes in color-less terminals. |
|
81 | NoColor = '' # for color schemes in color-less terminals. | |
82 |
|
82 | |||
83 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': |
|
83 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': | |
84 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them |
|
84 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them | |
85 | Normal = '\033[0m' # Reset normal coloring |
|
85 | Normal = '\033[0m' # Reset normal coloring | |
86 | _base = '\033[%sm' # Template for all other colors |
|
86 | _base = '\033[%sm' # Template for all other colors | |
87 | else: |
|
87 | else: | |
88 | Normal = '\001\033[0m\002' # Reset normal coloring |
|
88 | Normal = '\001\033[0m\002' # Reset normal coloring | |
89 | _base = '\001\033[%sm\002' # Template for all other colors |
|
89 | _base = '\001\033[%sm\002' # Template for all other colors | |
90 |
|
90 | |||
91 | # Build the actual color table as a set of class attributes: |
|
91 | # Build the actual color table as a set of class attributes: | |
92 | make_color_table(InputTermColors) |
|
92 | make_color_table(InputTermColors) | |
93 |
|
93 | |||
94 | class ColorScheme: |
|
94 | class ColorScheme: | |
95 | """Generic color scheme class. Just a name and a Struct.""" |
|
95 | """Generic color scheme class. Just a name and a Struct.""" | |
96 | def __init__(self,__scheme_name_,colordict=None,**colormap): |
|
96 | def __init__(self,__scheme_name_,colordict=None,**colormap): | |
97 | self.name = __scheme_name_ |
|
97 | self.name = __scheme_name_ | |
98 | if colordict is None: |
|
98 | if colordict is None: | |
99 | self.colors = Struct(**colormap) |
|
99 | self.colors = Struct(**colormap) | |
100 | else: |
|
100 | else: | |
101 | self.colors = Struct(colordict) |
|
101 | self.colors = Struct(colordict) | |
102 |
|
102 | |||
103 | def copy(self,name=None): |
|
103 | def copy(self,name=None): | |
104 | """Return a full copy of the object, optionally renaming it.""" |
|
104 | """Return a full copy of the object, optionally renaming it.""" | |
105 | if name is None: |
|
105 | if name is None: | |
106 | name = self.name |
|
106 | name = self.name | |
107 | return ColorScheme(name,self.colors.__dict__) |
|
107 | return ColorScheme(name,self.colors.__dict__) | |
108 |
|
108 | |||
109 | class ColorSchemeTable(dict): |
|
109 | class ColorSchemeTable(dict): | |
110 | """General class to handle tables of color schemes. |
|
110 | """General class to handle tables of color schemes. | |
111 |
|
111 | |||
112 | It's basically a dict of color schemes with a couple of shorthand |
|
112 | It's basically a dict of color schemes with a couple of shorthand | |
113 | attributes and some convenient methods. |
|
113 | attributes and some convenient methods. | |
114 |
|
114 | |||
115 | active_scheme_name -> obvious |
|
115 | active_scheme_name -> obvious | |
116 | active_colors -> actual color table of the active scheme""" |
|
116 | active_colors -> actual color table of the active scheme""" | |
117 |
|
117 | |||
118 | def __init__(self,scheme_list=None,default_scheme=''): |
|
118 | def __init__(self,scheme_list=None,default_scheme=''): | |
119 | """Create a table of color schemes. |
|
119 | """Create a table of color schemes. | |
120 |
|
120 | |||
121 | The table can be created empty and manually filled or it can be |
|
121 | The table can be created empty and manually filled or it can be | |
122 | created with a list of valid color schemes AND the specification for |
|
122 | created with a list of valid color schemes AND the specification for | |
123 | the default active scheme. |
|
123 | the default active scheme. | |
124 | """ |
|
124 | """ | |
125 |
|
125 | |||
126 | # create object attributes to be set later |
|
126 | # create object attributes to be set later | |
127 | self.active_scheme_name = '' |
|
127 | self.active_scheme_name = '' | |
128 | self.active_colors = None |
|
128 | self.active_colors = None | |
129 |
|
129 | |||
130 | if scheme_list: |
|
130 | if scheme_list: | |
131 | if default_scheme == '': |
|
131 | if default_scheme == '': | |
132 | raise ValueError,'you must specify the default color scheme' |
|
132 | raise ValueError,'you must specify the default color scheme' | |
133 | for scheme in scheme_list: |
|
133 | for scheme in scheme_list: | |
134 | self.add_scheme(scheme) |
|
134 | self.add_scheme(scheme) | |
135 | self.set_active_scheme(default_scheme) |
|
135 | self.set_active_scheme(default_scheme) | |
136 |
|
136 | |||
137 | def copy(self): |
|
137 | def copy(self): | |
138 | """Return full copy of object""" |
|
138 | """Return full copy of object""" | |
139 | return ColorSchemeTable(self.values(),self.active_scheme_name) |
|
139 | return ColorSchemeTable(self.values(),self.active_scheme_name) | |
140 |
|
140 | |||
141 | def add_scheme(self,new_scheme): |
|
141 | def add_scheme(self,new_scheme): | |
142 | """Add a new color scheme to the table.""" |
|
142 | """Add a new color scheme to the table.""" | |
143 | if not isinstance(new_scheme,ColorScheme): |
|
143 | if not isinstance(new_scheme,ColorScheme): | |
144 | raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' |
|
144 | raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' | |
145 | self[new_scheme.name] = new_scheme |
|
145 | self[new_scheme.name] = new_scheme | |
146 |
|
146 | |||
147 | def set_active_scheme(self,scheme,case_sensitive=0): |
|
147 | def set_active_scheme(self,scheme,case_sensitive=0): | |
148 | """Set the currently active scheme. |
|
148 | """Set the currently active scheme. | |
149 |
|
149 | |||
150 | Names are by default compared in a case-insensitive way, but this can |
|
150 | Names are by default compared in a case-insensitive way, but this can | |
151 | be changed by setting the parameter case_sensitive to true.""" |
|
151 | be changed by setting the parameter case_sensitive to true.""" | |
152 |
|
152 | |||
153 | scheme_names = self.keys() |
|
153 | scheme_names = self.keys() | |
154 | if case_sensitive: |
|
154 | if case_sensitive: | |
155 | valid_schemes = scheme_names |
|
155 | valid_schemes = scheme_names | |
156 | scheme_test = scheme |
|
156 | scheme_test = scheme | |
157 | else: |
|
157 | else: | |
158 | valid_schemes = [s.lower() for s in scheme_names] |
|
158 | valid_schemes = [s.lower() for s in scheme_names] | |
159 | scheme_test = scheme.lower() |
|
159 | scheme_test = scheme.lower() | |
160 | try: |
|
160 | try: | |
161 | scheme_idx = valid_schemes.index(scheme_test) |
|
161 | scheme_idx = valid_schemes.index(scheme_test) | |
162 | except ValueError: |
|
162 | except ValueError: | |
163 | raise ValueError,'Unrecognized color scheme: ' + scheme + \ |
|
163 | raise ValueError,'Unrecognized color scheme: ' + scheme + \ | |
164 | '\nValid schemes: '+str(scheme_names).replace("'', ",'') |
|
164 | '\nValid schemes: '+str(scheme_names).replace("'', ",'') | |
165 | else: |
|
165 | else: | |
166 | active = scheme_names[scheme_idx] |
|
166 | active = scheme_names[scheme_idx] | |
167 | self.active_scheme_name = active |
|
167 | self.active_scheme_name = active | |
168 | self.active_colors = self[active].colors |
|
168 | self.active_colors = self[active].colors | |
169 | # Now allow using '' as an index for the current active scheme |
|
169 | # Now allow using '' as an index for the current active scheme | |
170 | self[''] = self[active] |
|
170 | self[''] = self[active] |
@@ -1,116 +1,116 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 $""" |
|
4 | $Id: ConfigLoader.py 1005 2006-01-12 08:39:26Z fperez $""" | |
5 |
|
5 | |||
6 | #***************************************************************************** |
|
6 | #***************************************************************************** | |
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> |
|
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> | |
8 | # |
|
8 | # | |
9 | # 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 | |
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #***************************************************************************** |
|
11 | #***************************************************************************** | |
12 |
|
12 | |||
13 | from IPython import Release |
|
13 | from IPython import Release | |
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
15 | __license__ = Release.license |
|
15 | __license__ = Release.license | |
16 |
|
16 | |||
17 | import exceptions |
|
17 | import exceptions | |
18 | import os |
|
18 | import os | |
19 | from pprint import pprint |
|
19 | from pprint import pprint | |
20 |
|
20 | |||
21 | from IPython import ultraTB |
|
21 | from IPython import ultraTB | |
22 | from IPython.ipstruct import Struct |
|
22 | from IPython.ipstruct import Struct | |
23 | from IPython.genutils import * |
|
23 | from IPython.genutils import * | |
24 |
|
24 | |||
25 | class ConfigLoaderError(exceptions.Exception): |
|
25 | class ConfigLoaderError(exceptions.Exception): | |
26 | """Exception for ConfigLoader class.""" |
|
26 | """Exception for ConfigLoader class.""" | |
27 |
|
27 | |||
28 | def __init__(self,args=None): |
|
28 | def __init__(self,args=None): | |
29 | self.args = args |
|
29 | self.args = args | |
30 |
|
30 | |||
31 | class ConfigLoader: |
|
31 | class ConfigLoader: | |
32 |
|
32 | |||
33 | """Configuration file loader capable of handling recursive inclusions and |
|
33 | """Configuration file loader capable of handling recursive inclusions and | |
34 | with parametrized conflict resolution for multiply found keys.""" |
|
34 | with parametrized conflict resolution for multiply found keys.""" | |
35 |
|
35 | |||
36 | def __init__(self,conflict=None,field_sep=None,reclimit=15): |
|
36 | def __init__(self,conflict=None,field_sep=None,reclimit=15): | |
37 |
|
37 | |||
38 | """The reclimit parameter controls the number of recursive |
|
38 | """The reclimit parameter controls the number of recursive | |
39 | configuration file inclusions. This way we can stop early on (before |
|
39 | configuration file inclusions. This way we can stop early on (before | |
40 | python's own recursion limit is hit) if there is a circular |
|
40 | python's own recursion limit is hit) if there is a circular | |
41 | inclusion. |
|
41 | inclusion. | |
42 |
|
42 | |||
43 | - conflict: dictionary for conflict resolutions (see Struct.merge()) |
|
43 | - conflict: dictionary for conflict resolutions (see Struct.merge()) | |
44 |
|
44 | |||
45 | """ |
|
45 | """ | |
46 | self.conflict = conflict |
|
46 | self.conflict = conflict | |
47 | self.field_sep = field_sep |
|
47 | self.field_sep = field_sep | |
48 | self.reset(reclimit) |
|
48 | self.reset(reclimit) | |
49 |
|
49 | |||
50 | def reset(self,reclimit=15): |
|
50 | def reset(self,reclimit=15): | |
51 | self.reclimit = reclimit |
|
51 | self.reclimit = reclimit | |
52 | self.recdepth = 0 |
|
52 | self.recdepth = 0 | |
53 | self.included = [] |
|
53 | self.included = [] | |
54 |
|
54 | |||
55 | def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw): |
|
55 | def load(self,fname,convert=None,recurse_key='',incpath = '.',**kw): | |
56 | """Load a configuration file, return the resulting Struct. |
|
56 | """Load a configuration file, return the resulting Struct. | |
57 |
|
57 | |||
58 | Call: load_config(fname,convert=None,conflict=None,recurse_key='') |
|
58 | Call: load_config(fname,convert=None,conflict=None,recurse_key='') | |
59 |
|
59 | |||
60 | - fname: file to load from. |
|
60 | - fname: file to load from. | |
61 | - convert: dictionary of type conversions (see read_dict()) |
|
61 | - convert: dictionary of type conversions (see read_dict()) | |
62 | - recurse_key: keyword in dictionary to trigger recursive file |
|
62 | - recurse_key: keyword in dictionary to trigger recursive file | |
63 | inclusions. |
|
63 | inclusions. | |
64 | """ |
|
64 | """ | |
65 |
|
65 | |||
66 | if self.recdepth > self.reclimit: |
|
66 | if self.recdepth > self.reclimit: | |
67 | raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\ |
|
67 | raise ConfigLoaderError, 'maximum recursive inclusion of rcfiles '+\ | |
68 | 'exceeded: ' + `self.recdepth` + \ |
|
68 | 'exceeded: ' + `self.recdepth` + \ | |
69 | '.\nMaybe you have a circular chain of inclusions?' |
|
69 | '.\nMaybe you have a circular chain of inclusions?' | |
70 | self.recdepth += 1 |
|
70 | self.recdepth += 1 | |
71 | fname = filefind(fname,incpath) |
|
71 | fname = filefind(fname,incpath) | |
72 | data = Struct() |
|
72 | data = Struct() | |
73 | # avoid including the same file more than once |
|
73 | # avoid including the same file more than once | |
74 | if fname in self.included: |
|
74 | if fname in self.included: | |
75 | return data |
|
75 | return data | |
76 | Xinfo = ultraTB.AutoFormattedTB() |
|
76 | Xinfo = ultraTB.AutoFormattedTB() | |
77 | if convert==None and recurse_key : convert = {qwflat:recurse_key} |
|
77 | if convert==None and recurse_key : convert = {qwflat:recurse_key} | |
78 | # for production, change warn to 0: |
|
78 | # for production, change warn to 0: | |
79 | data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1, |
|
79 | data.merge(read_dict(fname,convert,fs=self.field_sep,strip=1, | |
80 | warn=0,no_empty=0,**kw)) |
|
80 | warn=0,no_empty=0,**kw)) | |
81 | # keep track of successfully loaded files |
|
81 | # keep track of successfully loaded files | |
82 | self.included.append(fname) |
|
82 | self.included.append(fname) | |
83 | if recurse_key in data.keys(): |
|
83 | if recurse_key in data.keys(): | |
84 | for incfilename in data[recurse_key]: |
|
84 | for incfilename in data[recurse_key]: | |
85 | found=0 |
|
85 | found=0 | |
86 | try: |
|
86 | try: | |
87 | incfile = filefind(incfilename,incpath) |
|
87 | incfile = filefind(incfilename,incpath) | |
88 | except IOError: |
|
88 | except IOError: | |
89 | if os.name in ['nt','dos']: |
|
89 | if os.name in ['nt','dos']: | |
90 | try: |
|
90 | try: | |
91 | # Try again with '.ini' extension |
|
91 | # Try again with '.ini' extension | |
92 | incfilename += '.ini' |
|
92 | incfilename += '.ini' | |
93 | incfile = filefind(incfilename,incpath) |
|
93 | incfile = filefind(incfilename,incpath) | |
94 | except IOError: |
|
94 | except IOError: | |
95 | found = 0 |
|
95 | found = 0 | |
96 | else: |
|
96 | else: | |
97 | found = 1 |
|
97 | found = 1 | |
98 | else: |
|
98 | else: | |
99 | found = 0 |
|
99 | found = 0 | |
100 | else: |
|
100 | else: | |
101 | found = 1 |
|
101 | found = 1 | |
102 | if found: |
|
102 | if found: | |
103 | try: |
|
103 | try: | |
104 | data.merge(self.load(incfile,convert,recurse_key, |
|
104 | data.merge(self.load(incfile,convert,recurse_key, | |
105 | incpath,**kw), |
|
105 | incpath,**kw), | |
106 | self.conflict) |
|
106 | self.conflict) | |
107 | except: |
|
107 | except: | |
108 | Xinfo() |
|
108 | Xinfo() | |
109 | warn('Problem loading included file: '+ |
|
109 | warn('Problem loading included file: '+ | |
110 | `incfilename` + '. Ignoring it...') |
|
110 | `incfilename` + '. Ignoring it...') | |
111 | else: |
|
111 | else: | |
112 | warn('File `%s` not found. Included by %s' % (incfilename,fname)) |
|
112 | warn('File `%s` not found. Included by %s' % (incfilename,fname)) | |
113 |
|
113 | |||
114 | return data |
|
114 | return data | |
115 |
|
115 | |||
116 | # end ConfigLoader |
|
116 | # end ConfigLoader |
@@ -1,228 +1,228 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 $""" |
|
4 | $Id: CrashHandler.py 2908 2007-12-30 21:07:46Z vivainio $""" | |
5 |
|
5 | |||
6 | #***************************************************************************** |
|
6 | #***************************************************************************** | |
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> |
|
7 | # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu> | |
8 | # |
|
8 | # | |
9 | # 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 | |
10 | # the file COPYING, distributed as part of this software. |
|
10 | # the file COPYING, distributed as part of this software. | |
11 | #***************************************************************************** |
|
11 | #***************************************************************************** | |
12 |
|
12 | |||
13 | from IPython import Release |
|
13 | from IPython import Release | |
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
14 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
15 | __license__ = Release.license |
|
15 | __license__ = Release.license | |
16 | __version__ = Release.version |
|
16 | __version__ = Release.version | |
17 |
|
17 | |||
18 | #**************************************************************************** |
|
18 | #**************************************************************************** | |
19 | # Required modules |
|
19 | # Required modules | |
20 |
|
20 | |||
21 | # From the standard library |
|
21 | # From the standard library | |
22 | import os |
|
22 | import os | |
23 | import sys |
|
23 | import sys | |
24 | from pprint import pprint,pformat |
|
24 | from pprint import pprint,pformat | |
25 |
|
25 | |||
26 | # Homebrewed |
|
26 | # Homebrewed | |
27 | from IPython.Itpl import Itpl,itpl,printpl |
|
27 | from IPython.Itpl import Itpl,itpl,printpl | |
28 | from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names |
|
28 | from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names | |
29 | from IPython import ultraTB |
|
29 | from IPython import ultraTB | |
30 | from IPython.genutils import * |
|
30 | from IPython.genutils import * | |
31 |
|
31 | |||
32 | #**************************************************************************** |
|
32 | #**************************************************************************** | |
33 | class CrashHandler: |
|
33 | class CrashHandler: | |
34 | """Customizable crash handlers for IPython-based systems. |
|
34 | """Customizable crash handlers for IPython-based systems. | |
35 |
|
35 | |||
36 | Instances of this class provide a __call__ method which can be used as a |
|
36 | Instances of this class provide a __call__ method which can be used as a | |
37 | sys.excepthook, i.e., the __call__ signature is: |
|
37 | sys.excepthook, i.e., the __call__ signature is: | |
38 |
|
38 | |||
39 | def __call__(self,etype, evalue, etb) |
|
39 | def __call__(self,etype, evalue, etb) | |
40 |
|
40 | |||
41 | """ |
|
41 | """ | |
42 |
|
42 | |||
43 | def __init__(self,IP,app_name,contact_name,contact_email, |
|
43 | def __init__(self,IP,app_name,contact_name,contact_email, | |
44 | bug_tracker,crash_report_fname, |
|
44 | bug_tracker,crash_report_fname, | |
45 | show_crash_traceback=True): |
|
45 | show_crash_traceback=True): | |
46 | """New crash handler. |
|
46 | """New crash handler. | |
47 |
|
47 | |||
48 | Inputs: |
|
48 | Inputs: | |
49 |
|
49 | |||
50 | - IP: a running IPython instance, which will be queried at crash time |
|
50 | - IP: a running IPython instance, which will be queried at crash time | |
51 | for internal information. |
|
51 | for internal information. | |
52 |
|
52 | |||
53 | - app_name: a string containing the name of your application. |
|
53 | - app_name: a string containing the name of your application. | |
54 |
|
54 | |||
55 | - contact_name: a string with the name of the person to contact. |
|
55 | - contact_name: a string with the name of the person to contact. | |
56 |
|
56 | |||
57 | - contact_email: a string with the email address of the contact. |
|
57 | - contact_email: a string with the email address of the contact. | |
58 |
|
58 | |||
59 | - bug_tracker: a string with the URL for your project's bug tracker. |
|
59 | - bug_tracker: a string with the URL for your project's bug tracker. | |
60 |
|
60 | |||
61 | - crash_report_fname: a string with the filename for the crash report |
|
61 | - 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 |
|
62 | to be saved in. These reports are left in the ipython user directory | |
63 | as determined by the running IPython instance. |
|
63 | as determined by the running IPython instance. | |
64 |
|
64 | |||
65 | Optional inputs: |
|
65 | Optional inputs: | |
66 |
|
66 | |||
67 | - show_crash_traceback(True): if false, don't print the crash |
|
67 | - show_crash_traceback(True): if false, don't print the crash | |
68 | traceback on stderr, only generate the on-disk report |
|
68 | traceback on stderr, only generate the on-disk report | |
69 |
|
69 | |||
70 |
|
70 | |||
71 | Non-argument instance attributes: |
|
71 | Non-argument instance attributes: | |
72 |
|
72 | |||
73 | These instances contain some non-argument attributes which allow for |
|
73 | These instances contain some non-argument attributes which allow for | |
74 | further customization of the crash handler's behavior. Please see the |
|
74 | further customization of the crash handler's behavior. Please see the | |
75 | source for further details. |
|
75 | source for further details. | |
76 | """ |
|
76 | """ | |
77 |
|
77 | |||
78 | # apply args into instance |
|
78 | # apply args into instance | |
79 | self.IP = IP # IPython instance |
|
79 | self.IP = IP # IPython instance | |
80 | self.app_name = app_name |
|
80 | self.app_name = app_name | |
81 | self.contact_name = contact_name |
|
81 | self.contact_name = contact_name | |
82 | self.contact_email = contact_email |
|
82 | self.contact_email = contact_email | |
83 | self.bug_tracker = bug_tracker |
|
83 | self.bug_tracker = bug_tracker | |
84 | self.crash_report_fname = crash_report_fname |
|
84 | self.crash_report_fname = crash_report_fname | |
85 | self.show_crash_traceback = show_crash_traceback |
|
85 | self.show_crash_traceback = show_crash_traceback | |
86 |
|
86 | |||
87 | # Hardcoded defaults, which can be overridden either by subclasses or |
|
87 | # Hardcoded defaults, which can be overridden either by subclasses or | |
88 | # at runtime for the instance. |
|
88 | # at runtime for the instance. | |
89 |
|
89 | |||
90 | # Template for the user message. Subclasses which completely override |
|
90 | # Template for the user message. Subclasses which completely override | |
91 | # this, or user apps, can modify it to suit their tastes. It gets |
|
91 | # 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. |
|
92 | # expanded using itpl, so calls of the kind $self.foo are valid. | |
93 | self.user_message_template = """ |
|
93 | self.user_message_template = """ | |
94 | Oops, $self.app_name crashed. We do our best to make it stable, but... |
|
94 | Oops, $self.app_name crashed. We do our best to make it stable, but... | |
95 |
|
95 | |||
96 | A crash report was automatically generated with the following information: |
|
96 | A crash report was automatically generated with the following information: | |
97 | - A verbatim copy of the crash traceback. |
|
97 | - A verbatim copy of the crash traceback. | |
98 | - A copy of your input history during this session. |
|
98 | - A copy of your input history during this session. | |
99 | - Data on your current $self.app_name configuration. |
|
99 | - Data on your current $self.app_name configuration. | |
100 |
|
100 | |||
101 | It was left in the file named: |
|
101 | It was left in the file named: | |
102 | \t'$self.crash_report_fname' |
|
102 | \t'$self.crash_report_fname' | |
103 | If you can email this file to the developers, the information in it will help |
|
103 | If you can email this file to the developers, the information in it will help | |
104 | them in understanding and correcting the problem. |
|
104 | them in understanding and correcting the problem. | |
105 |
|
105 | |||
106 | You can mail it to: $self.contact_name at $self.contact_email |
|
106 | You can mail it to: $self.contact_name at $self.contact_email | |
107 | with the subject '$self.app_name Crash Report'. |
|
107 | with the subject '$self.app_name Crash Report'. | |
108 |
|
108 | |||
109 | If you want to do it now, the following command will work (under Unix): |
|
109 | 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 |
|
110 | mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname | |
111 |
|
111 | |||
112 | To ensure accurate tracking of this issue, please file a report about it at: |
|
112 | To ensure accurate tracking of this issue, please file a report about it at: | |
113 | $self.bug_tracker |
|
113 | $self.bug_tracker | |
114 | """ |
|
114 | """ | |
115 |
|
115 | |||
116 | def __call__(self,etype, evalue, etb): |
|
116 | def __call__(self,etype, evalue, etb): | |
117 | """Handle an exception, call for compatible with sys.excepthook""" |
|
117 | """Handle an exception, call for compatible with sys.excepthook""" | |
118 |
|
118 | |||
119 | # Report tracebacks shouldn't use color in general (safer for users) |
|
119 | # Report tracebacks shouldn't use color in general (safer for users) | |
120 | color_scheme = 'NoColor' |
|
120 | color_scheme = 'NoColor' | |
121 |
|
121 | |||
122 | # Use this ONLY for developer debugging (keep commented out for release) |
|
122 | # Use this ONLY for developer debugging (keep commented out for release) | |
123 | #color_scheme = 'Linux' # dbg |
|
123 | #color_scheme = 'Linux' # dbg | |
124 |
|
124 | |||
125 | try: |
|
125 | try: | |
126 | rptdir = self.IP.rc.ipythondir |
|
126 | rptdir = self.IP.rc.ipythondir | |
127 | except: |
|
127 | except: | |
128 | rptdir = os.getcwd() |
|
128 | rptdir = os.getcwd() | |
129 | if not os.path.isdir(rptdir): |
|
129 | if not os.path.isdir(rptdir): | |
130 | rptdir = os.getcwd() |
|
130 | rptdir = os.getcwd() | |
131 | report_name = os.path.join(rptdir,self.crash_report_fname) |
|
131 | report_name = os.path.join(rptdir,self.crash_report_fname) | |
132 | # write the report filename into the instance dict so it can get |
|
132 | # write the report filename into the instance dict so it can get | |
133 | # properly expanded out in the user message template |
|
133 | # properly expanded out in the user message template | |
134 | self.crash_report_fname = report_name |
|
134 | self.crash_report_fname = report_name | |
135 | TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme, |
|
135 | TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme, | |
136 | long_header=1) |
|
136 | long_header=1) | |
137 | traceback = TBhandler.text(etype,evalue,etb,context=31) |
|
137 | traceback = TBhandler.text(etype,evalue,etb,context=31) | |
138 |
|
138 | |||
139 | # print traceback to screen |
|
139 | # print traceback to screen | |
140 | if self.show_crash_traceback: |
|
140 | if self.show_crash_traceback: | |
141 | print >> sys.stderr, traceback |
|
141 | print >> sys.stderr, traceback | |
142 |
|
142 | |||
143 | # and generate a complete report on disk |
|
143 | # and generate a complete report on disk | |
144 | try: |
|
144 | try: | |
145 | report = open(report_name,'w') |
|
145 | report = open(report_name,'w') | |
146 | except: |
|
146 | except: | |
147 | print >> sys.stderr, 'Could not create crash report on disk.' |
|
147 | print >> sys.stderr, 'Could not create crash report on disk.' | |
148 | return |
|
148 | return | |
149 |
|
149 | |||
150 | # Inform user on stderr of what happened |
|
150 | # Inform user on stderr of what happened | |
151 | msg = itpl('\n'+'*'*70+'\n'+self.user_message_template) |
|
151 | msg = itpl('\n'+'*'*70+'\n'+self.user_message_template) | |
152 | print >> sys.stderr, msg |
|
152 | print >> sys.stderr, msg | |
153 |
|
153 | |||
154 | # Construct report on disk |
|
154 | # Construct report on disk | |
155 | report.write(self.make_report(traceback)) |
|
155 | report.write(self.make_report(traceback)) | |
156 | report.close() |
|
156 | report.close() | |
157 | raw_input("Press enter to exit:") |
|
157 | raw_input("Press enter to exit:") | |
158 |
|
158 | |||
159 | def make_report(self,traceback): |
|
159 | def make_report(self,traceback): | |
160 | """Return a string containing a crash report.""" |
|
160 | """Return a string containing a crash report.""" | |
161 |
|
161 | |||
162 | sec_sep = '\n\n'+'*'*75+'\n\n' |
|
162 | sec_sep = '\n\n'+'*'*75+'\n\n' | |
163 |
|
163 | |||
164 | report = [] |
|
164 | report = [] | |
165 | rpt_add = report.append |
|
165 | rpt_add = report.append | |
166 |
|
166 | |||
167 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') |
|
167 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') | |
168 | rpt_add('IPython version: %s \n\n' % Release.version) |
|
168 | rpt_add('IPython version: %s \n\n' % Release.version) | |
169 | rpt_add('SVN revision : %s \n\n' % Release.revision) |
|
169 | rpt_add('SVN revision : %s \n\n' % Release.revision) | |
170 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % |
|
170 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % | |
171 | (os.name,sys.platform) ) |
|
171 | (os.name,sys.platform) ) | |
172 | rpt_add(sec_sep+'Current user configuration structure:\n\n') |
|
172 | rpt_add(sec_sep+'Current user configuration structure:\n\n') | |
173 | rpt_add(pformat(self.IP.rc.dict())) |
|
173 | rpt_add(pformat(self.IP.rc.dict())) | |
174 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) |
|
174 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) | |
175 | try: |
|
175 | try: | |
176 | rpt_add(sec_sep+"History of session input:") |
|
176 | rpt_add(sec_sep+"History of session input:") | |
177 | for line in self.IP.user_ns['_ih']: |
|
177 | for line in self.IP.user_ns['_ih']: | |
178 | rpt_add(line) |
|
178 | rpt_add(line) | |
179 | rpt_add('\n*** Last line of input (may not be in above history):\n') |
|
179 | rpt_add('\n*** Last line of input (may not be in above history):\n') | |
180 | rpt_add(self.IP._last_input_line+'\n') |
|
180 | rpt_add(self.IP._last_input_line+'\n') | |
181 | except: |
|
181 | except: | |
182 | pass |
|
182 | pass | |
183 |
|
183 | |||
184 | return ''.join(report) |
|
184 | return ''.join(report) | |
185 |
|
185 | |||
186 | class IPythonCrashHandler(CrashHandler): |
|
186 | class IPythonCrashHandler(CrashHandler): | |
187 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" |
|
187 | """sys.excepthook for IPython itself, leaves a detailed report on disk.""" | |
188 |
|
188 | |||
189 | def __init__(self,IP): |
|
189 | def __init__(self,IP): | |
190 |
|
190 | |||
191 | # Set here which of the IPython authors should be listed as contact |
|
191 | # Set here which of the IPython authors should be listed as contact | |
192 | AUTHOR_CONTACT = 'Ville' |
|
192 | AUTHOR_CONTACT = 'Ville' | |
193 |
|
193 | |||
194 | # Set argument defaults |
|
194 | # Set argument defaults | |
195 | app_name = 'IPython' |
|
195 | app_name = 'IPython' | |
196 | bug_tracker = 'http://projects.scipy.org/ipython/ipython/report' |
|
196 | bug_tracker = 'http://projects.scipy.org/ipython/ipython/report' | |
197 | contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2] |
|
197 | contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2] | |
198 | crash_report_fname = 'IPython_crash_report.txt' |
|
198 | crash_report_fname = 'IPython_crash_report.txt' | |
199 | # Call parent constructor |
|
199 | # Call parent constructor | |
200 | CrashHandler.__init__(self,IP,app_name,contact_name,contact_email, |
|
200 | CrashHandler.__init__(self,IP,app_name,contact_name,contact_email, | |
201 | bug_tracker,crash_report_fname) |
|
201 | bug_tracker,crash_report_fname) | |
202 |
|
202 | |||
203 | def make_report(self,traceback): |
|
203 | def make_report(self,traceback): | |
204 | """Return a string containing a crash report.""" |
|
204 | """Return a string containing a crash report.""" | |
205 |
|
205 | |||
206 | sec_sep = '\n\n'+'*'*75+'\n\n' |
|
206 | sec_sep = '\n\n'+'*'*75+'\n\n' | |
207 |
|
207 | |||
208 | report = [] |
|
208 | report = [] | |
209 | rpt_add = report.append |
|
209 | rpt_add = report.append | |
210 |
|
210 | |||
211 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') |
|
211 | rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n') | |
212 | rpt_add('IPython version: %s \n\n' % Release.version) |
|
212 | rpt_add('IPython version: %s \n\n' % Release.version) | |
213 | rpt_add('SVN revision : %s \n\n' % Release.revision) |
|
213 | rpt_add('SVN revision : %s \n\n' % Release.revision) | |
214 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % |
|
214 | rpt_add('Platform info : os.name -> %s, sys.platform -> %s' % | |
215 | (os.name,sys.platform) ) |
|
215 | (os.name,sys.platform) ) | |
216 | rpt_add(sec_sep+'Current user configuration structure:\n\n') |
|
216 | rpt_add(sec_sep+'Current user configuration structure:\n\n') | |
217 | rpt_add(pformat(self.IP.rc.dict())) |
|
217 | rpt_add(pformat(self.IP.rc.dict())) | |
218 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) |
|
218 | rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) | |
219 | try: |
|
219 | try: | |
220 | rpt_add(sec_sep+"History of session input:") |
|
220 | rpt_add(sec_sep+"History of session input:") | |
221 | for line in self.IP.user_ns['_ih']: |
|
221 | for line in self.IP.user_ns['_ih']: | |
222 | rpt_add(line) |
|
222 | rpt_add(line) | |
223 | rpt_add('\n*** Last line of input (may not be in above history):\n') |
|
223 | rpt_add('\n*** Last line of input (may not be in above history):\n') | |
224 | rpt_add(self.IP._last_input_line+'\n') |
|
224 | rpt_add(self.IP._last_input_line+'\n') | |
225 | except: |
|
225 | except: | |
226 | pass |
|
226 | pass | |
227 |
|
227 | |||
228 | return ''.join(report) |
|
228 | return ''.join(report) |
This diff has been collapsed as it changes many lines, (1386 lines changed) Show them Hide them | |||||
@@ -1,693 +1,693 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 $ |
|
4 | $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $ | |
5 |
|
5 | |||
6 | This module is modeled after perl's Getopt::Long module-- which |
|
6 | This module is modeled after perl's Getopt::Long module-- which | |
7 | is, in turn, modeled after GNU's extended getopt() function. |
|
7 | is, in turn, modeled after GNU's extended getopt() function. | |
8 |
|
8 | |||
9 | Upon instantiation, the option specification should be a sequence |
|
9 | Upon instantiation, the option specification should be a sequence | |
10 | (list) of option definitions. |
|
10 | (list) of option definitions. | |
11 |
|
11 | |||
12 | Options that take no arguments should simply contain the name of |
|
12 | 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 |
|
13 | the option. If a ! is post-pended, the option can be negated by | |
14 | prepending 'no'; ie 'debug!' specifies that -debug and -nodebug |
|
14 | prepending 'no'; ie 'debug!' specifies that -debug and -nodebug | |
15 | should be accepted. |
|
15 | should be accepted. | |
16 |
|
16 | |||
17 | Mandatory arguments to options are specified using a postpended |
|
17 | Mandatory arguments to options are specified using a postpended | |
18 | '=' + a type specifier. '=s' specifies a mandatory string |
|
18 | '=' + a type specifier. '=s' specifies a mandatory string | |
19 | argument, '=i' specifies a mandatory integer argument, and '=f' |
|
19 | argument, '=i' specifies a mandatory integer argument, and '=f' | |
20 | specifies a mandatory real number. In all cases, the '=' can be |
|
20 | specifies a mandatory real number. In all cases, the '=' can be | |
21 | substituted with ':' to specify that the argument is optional. |
|
21 | substituted with ':' to specify that the argument is optional. | |
22 |
|
22 | |||
23 | Dashes '-' in option names are allowed. |
|
23 | Dashes '-' in option names are allowed. | |
24 |
|
24 | |||
25 | If an option has the character '@' postpended (after the |
|
25 | If an option has the character '@' postpended (after the | |
26 | argumentation specification), it can appear multiple times within |
|
26 | argumentation specification), it can appear multiple times within | |
27 | each argument list that is processed. The results will be stored |
|
27 | each argument list that is processed. The results will be stored | |
28 | in a list. |
|
28 | in a list. | |
29 |
|
29 | |||
30 | The option name can actually be a list of names separated by '|' |
|
30 | The option name can actually be a list of names separated by '|' | |
31 | characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar, |
|
31 | characters; ie-- 'foo|bar|baz=f@' specifies that all -foo, -bar, | |
32 | and -baz options that appear on within the parsed argument list |
|
32 | and -baz options that appear on within the parsed argument list | |
33 | must have a real number argument and that the accumulated list |
|
33 | must have a real number argument and that the accumulated list | |
34 | of values will be available under the name 'foo' |
|
34 | of values will be available under the name 'foo' | |
35 |
|
35 | |||
36 | $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $""" |
|
36 | $Id: DPyGetOpt.py 2872 2007-11-25 17:58:05Z fperez $""" | |
37 |
|
37 | |||
38 | #***************************************************************************** |
|
38 | #***************************************************************************** | |
39 | # |
|
39 | # | |
40 | # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com> |
|
40 | # Copyright (c) 2001 Bill Bumgarner <bbum@friday.com> | |
41 | # |
|
41 | # | |
42 | # |
|
42 | # | |
43 | # Published under the terms of the MIT license, hereby reproduced: |
|
43 | # Published under the terms of the MIT license, hereby reproduced: | |
44 | # |
|
44 | # | |
45 | # Permission is hereby granted, free of charge, to any person obtaining a copy |
|
45 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
46 | # of this software and associated documentation files (the "Software"), to |
|
46 | # of this software and associated documentation files (the "Software"), to | |
47 | # deal in the Software without restriction, including without limitation the |
|
47 | # deal in the Software without restriction, including without limitation the | |
48 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|
48 | # 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 |
|
49 | # sell copies of the Software, and to permit persons to whom the Software is | |
50 | # furnished to do so, subject to the following conditions: |
|
50 | # furnished to do so, subject to the following conditions: | |
51 | # |
|
51 | # | |
52 | # The above copyright notice and this permission notice shall be included in |
|
52 | # The above copyright notice and this permission notice shall be included in | |
53 | # all copies or substantial portions of the Software. |
|
53 | # all copies or substantial portions of the Software. | |
54 | # |
|
54 | # | |
55 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
55 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
56 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
56 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
57 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
57 | # 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 |
|
58 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
59 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
59 | # 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 |
|
60 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
61 | # IN THE SOFTWARE. |
|
61 | # IN THE SOFTWARE. | |
62 | # |
|
62 | # | |
63 | #***************************************************************************** |
|
63 | #***************************************************************************** | |
64 |
|
64 | |||
65 | __author__ = 'Bill Bumgarner <bbum@friday.com>' |
|
65 | __author__ = 'Bill Bumgarner <bbum@friday.com>' | |
66 | __license__ = 'MIT' |
|
66 | __license__ = 'MIT' | |
67 | __version__ = '1.2' |
|
67 | __version__ = '1.2' | |
68 |
|
68 | |||
69 | # Modified to use re instead of regex and regsub modules. |
|
69 | # Modified to use re instead of regex and regsub modules. | |
70 | # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com> |
|
70 | # 2001/5/7, Jonathan Hogg <jonathan@onegoodidea.com> | |
71 |
|
71 | |||
72 | import re |
|
72 | import re | |
73 | import string |
|
73 | import string | |
74 | import sys |
|
74 | import sys | |
75 | import types |
|
75 | import types | |
76 |
|
76 | |||
77 | class Error(Exception): |
|
77 | class Error(Exception): | |
78 | """Base class for exceptions in the DPyGetOpt module.""" |
|
78 | """Base class for exceptions in the DPyGetOpt module.""" | |
79 |
|
79 | |||
80 | class ArgumentError(Error): |
|
80 | class ArgumentError(Error): | |
81 | """Exception indicating an error in the arguments passed to |
|
81 | """Exception indicating an error in the arguments passed to | |
82 | DPyGetOpt.processArguments.""" |
|
82 | DPyGetOpt.processArguments.""" | |
83 |
|
83 | |||
84 | class SpecificationError(Error): |
|
84 | class SpecificationError(Error): | |
85 | """Exception indicating an error with an option specification.""" |
|
85 | """Exception indicating an error with an option specification.""" | |
86 |
|
86 | |||
87 | class TerminationError(Error): |
|
87 | class TerminationError(Error): | |
88 | """Exception indicating an error with an option processing terminator.""" |
|
88 | """Exception indicating an error with an option processing terminator.""" | |
89 |
|
89 | |||
90 | specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)') |
|
90 | specificationExpr = re.compile('(?P<required>.)(?P<type>.)(?P<multi>@?)') | |
91 |
|
91 | |||
92 | ArgRequired = 'Requires an Argument' |
|
92 | ArgRequired = 'Requires an Argument' | |
93 | ArgOptional = 'Argument Optional' |
|
93 | ArgOptional = 'Argument Optional' | |
94 |
|
94 | |||
95 | # The types modules is not used for these identifiers because there |
|
95 | # The types modules is not used for these identifiers because there | |
96 | # is no identifier for 'boolean' or 'generic' |
|
96 | # is no identifier for 'boolean' or 'generic' | |
97 | StringArgType = 'String Argument Type' |
|
97 | StringArgType = 'String Argument Type' | |
98 | IntegerArgType = 'Integer Argument Type' |
|
98 | IntegerArgType = 'Integer Argument Type' | |
99 | RealArgType = 'Real Argument Type' |
|
99 | RealArgType = 'Real Argument Type' | |
100 | BooleanArgType = 'Boolean Argument Type' |
|
100 | BooleanArgType = 'Boolean Argument Type' | |
101 | GenericArgType = 'Generic Argument Type' |
|
101 | GenericArgType = 'Generic Argument Type' | |
102 |
|
102 | |||
103 | # dictionary of conversion functions-- boolean and generic options |
|
103 | # dictionary of conversion functions-- boolean and generic options | |
104 | # do not accept arguments and do not need conversion functions; |
|
104 | # do not accept arguments and do not need conversion functions; | |
105 | # the identity function is used purely for convenience. |
|
105 | # the identity function is used purely for convenience. | |
106 | ConversionFunctions = { |
|
106 | ConversionFunctions = { | |
107 | StringArgType : lambda x: x, |
|
107 | StringArgType : lambda x: x, | |
108 | IntegerArgType : string.atoi, |
|
108 | IntegerArgType : string.atoi, | |
109 | RealArgType : string.atof, |
|
109 | RealArgType : string.atof, | |
110 | BooleanArgType : lambda x: x, |
|
110 | BooleanArgType : lambda x: x, | |
111 | GenericArgType : lambda x: x, |
|
111 | GenericArgType : lambda x: x, | |
112 | } |
|
112 | } | |
113 |
|
113 | |||
114 | class DPyGetOpt: |
|
114 | class DPyGetOpt: | |
115 |
|
115 | |||
116 | def __init__(self, spec = None, terminators = ['--']): |
|
116 | def __init__(self, spec = None, terminators = ['--']): | |
117 | """ |
|
117 | """ | |
118 | Declare and intialize instance variables |
|
118 | Declare and intialize instance variables | |
119 |
|
119 | |||
120 | Yes, declaration is not necessary... but one of the things |
|
120 | Yes, declaration is not necessary... but one of the things | |
121 | I sorely miss from C/Obj-C is the concept of having an |
|
121 | I sorely miss from C/Obj-C is the concept of having an | |
122 | interface definition that clearly declares all instance |
|
122 | interface definition that clearly declares all instance | |
123 | variables and methods without providing any implementation |
|
123 | variables and methods without providing any implementation | |
124 | details. it is a useful reference! |
|
124 | details. it is a useful reference! | |
125 |
|
125 | |||
126 | all instance variables are initialized to 0/Null/None of |
|
126 | all instance variables are initialized to 0/Null/None of | |
127 | the appropriate type-- not even the default value... |
|
127 | the appropriate type-- not even the default value... | |
128 | """ |
|
128 | """ | |
129 |
|
129 | |||
130 | # sys.stderr.write(string.join(spec) + "\n") |
|
130 | # sys.stderr.write(string.join(spec) + "\n") | |
131 |
|
131 | |||
132 | self.allowAbbreviations = 1 # boolean, 1 if abbreviations will |
|
132 | self.allowAbbreviations = 1 # boolean, 1 if abbreviations will | |
133 | # be expanded |
|
133 | # be expanded | |
134 | self.freeValues = [] # list, contains free values |
|
134 | self.freeValues = [] # list, contains free values | |
135 | self.ignoreCase = 0 # boolean, YES if ignoring case |
|
135 | self.ignoreCase = 0 # boolean, YES if ignoring case | |
136 | self.needsParse = 0 # boolean, YES if need to reparse parameter spec |
|
136 | self.needsParse = 0 # boolean, YES if need to reparse parameter spec | |
137 | self.optionNames = {} # dict, all option names-- value is index of tuple |
|
137 | self.optionNames = {} # dict, all option names-- value is index of tuple | |
138 | self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--') |
|
138 | self.optionStartExpr = None # regexp defining the start of an option (ie; '-', '--') | |
139 | self.optionTuples = [] # list o' tuples containing defn of options AND aliases |
|
139 | self.optionTuples = [] # list o' tuples containing defn of options AND aliases | |
140 | self.optionValues = {} # dict, option names (after alias expansion) -> option value(s) |
|
140 | self.optionValues = {} # dict, option names (after alias expansion) -> option value(s) | |
141 | self.orderMixed = 0 # boolean, YES if options can be mixed with args |
|
141 | self.orderMixed = 0 # boolean, YES if options can be mixed with args | |
142 | self.posixCompliance = 0 # boolean, YES indicates posix like behaviour |
|
142 | self.posixCompliance = 0 # boolean, YES indicates posix like behaviour | |
143 | self.spec = [] # list, raw specs (in case it must be reparsed) |
|
143 | self.spec = [] # list, raw specs (in case it must be reparsed) | |
144 | self.terminators = terminators # list, strings that terminate argument processing |
|
144 | self.terminators = terminators # list, strings that terminate argument processing | |
145 | self.termValues = [] # list, values after terminator |
|
145 | self.termValues = [] # list, values after terminator | |
146 | self.terminator = None # full name of terminator that ended |
|
146 | self.terminator = None # full name of terminator that ended | |
147 | # option processing |
|
147 | # option processing | |
148 |
|
148 | |||
149 | # set up defaults |
|
149 | # set up defaults | |
150 | self.setPosixCompliance() |
|
150 | self.setPosixCompliance() | |
151 | self.setIgnoreCase() |
|
151 | self.setIgnoreCase() | |
152 | self.setAllowAbbreviations() |
|
152 | self.setAllowAbbreviations() | |
153 |
|
153 | |||
154 | # parse spec-- if present |
|
154 | # parse spec-- if present | |
155 | if spec: |
|
155 | if spec: | |
156 | self.parseConfiguration(spec) |
|
156 | self.parseConfiguration(spec) | |
157 |
|
157 | |||
158 | def setPosixCompliance(self, aFlag = 0): |
|
158 | def setPosixCompliance(self, aFlag = 0): | |
159 | """ |
|
159 | """ | |
160 | Enables and disables posix compliance. |
|
160 | Enables and disables posix compliance. | |
161 |
|
161 | |||
162 | When enabled, '+' can be used as an option prefix and free |
|
162 | When enabled, '+' can be used as an option prefix and free | |
163 | values can be mixed with options. |
|
163 | values can be mixed with options. | |
164 | """ |
|
164 | """ | |
165 | self.posixCompliance = aFlag |
|
165 | self.posixCompliance = aFlag | |
166 | self.needsParse = 1 |
|
166 | self.needsParse = 1 | |
167 |
|
167 | |||
168 | if self.posixCompliance: |
|
168 | if self.posixCompliance: | |
169 | 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>=.*)?') | |
170 | self.orderMixed = 0 |
|
170 | self.orderMixed = 0 | |
171 | else: |
|
171 | else: | |
172 | self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') |
|
172 | self.optionStartExpr = re.compile('(--|-|\+)(?P<option>[A-Za-z0-9_-]+)(?P<arg>=.*)?') | |
173 | self.orderMixed = 1 |
|
173 | self.orderMixed = 1 | |
174 |
|
174 | |||
175 | def isPosixCompliant(self): |
|
175 | def isPosixCompliant(self): | |
176 | """ |
|
176 | """ | |
177 | Returns the value of the posix compliance flag. |
|
177 | Returns the value of the posix compliance flag. | |
178 | """ |
|
178 | """ | |
179 | return self.posixCompliance |
|
179 | return self.posixCompliance | |
180 |
|
180 | |||
181 | def setIgnoreCase(self, aFlag = 1): |
|
181 | def setIgnoreCase(self, aFlag = 1): | |
182 | """ |
|
182 | """ | |
183 | Enables and disables ignoring case during option processing. |
|
183 | Enables and disables ignoring case during option processing. | |
184 | """ |
|
184 | """ | |
185 | self.needsParse = 1 |
|
185 | self.needsParse = 1 | |
186 | self.ignoreCase = aFlag |
|
186 | self.ignoreCase = aFlag | |
187 |
|
187 | |||
188 | def ignoreCase(self): |
|
188 | def ignoreCase(self): | |
189 | """ |
|
189 | """ | |
190 | Returns 1 if the option processor will ignore case when |
|
190 | Returns 1 if the option processor will ignore case when | |
191 | processing options. |
|
191 | processing options. | |
192 | """ |
|
192 | """ | |
193 | return self.ignoreCase |
|
193 | return self.ignoreCase | |
194 |
|
194 | |||
195 | def setAllowAbbreviations(self, aFlag = 1): |
|
195 | def setAllowAbbreviations(self, aFlag = 1): | |
196 | """ |
|
196 | """ | |
197 | Enables and disables the expansion of abbreviations during |
|
197 | Enables and disables the expansion of abbreviations during | |
198 | option processing. |
|
198 | option processing. | |
199 | """ |
|
199 | """ | |
200 | self.allowAbbreviations = aFlag |
|
200 | self.allowAbbreviations = aFlag | |
201 |
|
201 | |||
202 | def willAllowAbbreviations(self): |
|
202 | def willAllowAbbreviations(self): | |
203 | """ |
|
203 | """ | |
204 | Returns 1 if abbreviated options will be automatically |
|
204 | Returns 1 if abbreviated options will be automatically | |
205 | expanded to the non-abbreviated form (instead of causing an |
|
205 | expanded to the non-abbreviated form (instead of causing an | |
206 | unrecognized option error). |
|
206 | unrecognized option error). | |
207 | """ |
|
207 | """ | |
208 | return self.allowAbbreviations |
|
208 | return self.allowAbbreviations | |
209 |
|
209 | |||
210 | def addTerminator(self, newTerm): |
|
210 | def addTerminator(self, newTerm): | |
211 | """ |
|
211 | """ | |
212 | Adds newTerm as terminator of option processing. |
|
212 | Adds newTerm as terminator of option processing. | |
213 |
|
213 | |||
214 | Whenever the option processor encounters one of the terminators |
|
214 | Whenever the option processor encounters one of the terminators | |
215 | during option processing, the processing of options terminates |
|
215 | during option processing, the processing of options terminates | |
216 | immediately, all remaining options are stored in the termValues |
|
216 | immediately, all remaining options are stored in the termValues | |
217 | instance variable and the full name of the terminator is stored |
|
217 | instance variable and the full name of the terminator is stored | |
218 | in the terminator instance variable. |
|
218 | in the terminator instance variable. | |
219 | """ |
|
219 | """ | |
220 | self.terminators = self.terminators + [newTerm] |
|
220 | self.terminators = self.terminators + [newTerm] | |
221 |
|
221 | |||
222 | def _addOption(self, oTuple): |
|
222 | def _addOption(self, oTuple): | |
223 | """ |
|
223 | """ | |
224 | Adds the option described by oTuple (name, (type, mode, |
|
224 | Adds the option described by oTuple (name, (type, mode, | |
225 | default), alias) to optionTuples. Adds index keyed under name |
|
225 | default), alias) to optionTuples. Adds index keyed under name | |
226 | to optionNames. Raises SpecificationError if name already in |
|
226 | to optionNames. Raises SpecificationError if name already in | |
227 | optionNames |
|
227 | optionNames | |
228 | """ |
|
228 | """ | |
229 | (name, (type, mode, default, multi), realName) = oTuple |
|
229 | (name, (type, mode, default, multi), realName) = oTuple | |
230 |
|
230 | |||
231 | # verify name and add to option names dictionary |
|
231 | # verify name and add to option names dictionary | |
232 | if self.optionNames.has_key(name): |
|
232 | if self.optionNames.has_key(name): | |
233 | if realName: |
|
233 | if realName: | |
234 | raise SpecificationError('Alias \'' + name + '\' for \'' + |
|
234 | raise SpecificationError('Alias \'' + name + '\' for \'' + | |
235 | realName + |
|
235 | realName + | |
236 | '\' already used for another option or alias.') |
|
236 | '\' already used for another option or alias.') | |
237 | else: |
|
237 | else: | |
238 | raise SpecificationError('Option named \'' + name + |
|
238 | raise SpecificationError('Option named \'' + name + | |
239 | '\' specified more than once. Specification: ' |
|
239 | '\' specified more than once. Specification: ' | |
240 | + option) |
|
240 | + option) | |
241 |
|
241 | |||
242 | # validated. add to optionNames |
|
242 | # validated. add to optionNames | |
243 | self.optionNames[name] = self.tupleIndex |
|
243 | self.optionNames[name] = self.tupleIndex | |
244 | self.tupleIndex = self.tupleIndex + 1 |
|
244 | self.tupleIndex = self.tupleIndex + 1 | |
245 |
|
245 | |||
246 | # add to optionTuples |
|
246 | # add to optionTuples | |
247 | self.optionTuples = self.optionTuples + [oTuple] |
|
247 | self.optionTuples = self.optionTuples + [oTuple] | |
248 |
|
248 | |||
249 | # if type is boolean, add negation |
|
249 | # if type is boolean, add negation | |
250 | if type == BooleanArgType: |
|
250 | if type == BooleanArgType: | |
251 | alias = 'no' + name |
|
251 | alias = 'no' + name | |
252 | specTuple = (type, mode, 0, multi) |
|
252 | specTuple = (type, mode, 0, multi) | |
253 | oTuple = (alias, specTuple, name) |
|
253 | oTuple = (alias, specTuple, name) | |
254 |
|
254 | |||
255 | # verify name and add to option names dictionary |
|
255 | # verify name and add to option names dictionary | |
256 | if self.optionNames.has_key(alias): |
|
256 | if self.optionNames.has_key(alias): | |
257 | if realName: |
|
257 | if realName: | |
258 | raise SpecificationError('Negated alias \'' + name + |
|
258 | raise SpecificationError('Negated alias \'' + name + | |
259 | '\' for \'' + realName + |
|
259 | '\' for \'' + realName + | |
260 | '\' already used for another option or alias.') |
|
260 | '\' already used for another option or alias.') | |
261 | else: |
|
261 | else: | |
262 | raise SpecificationError('Negated option named \'' + name + |
|
262 | raise SpecificationError('Negated option named \'' + name + | |
263 | '\' specified more than once. Specification: ' |
|
263 | '\' specified more than once. Specification: ' | |
264 | + option) |
|
264 | + option) | |
265 |
|
265 | |||
266 | # validated. add to optionNames |
|
266 | # validated. add to optionNames | |
267 | self.optionNames[alias] = self.tupleIndex |
|
267 | self.optionNames[alias] = self.tupleIndex | |
268 | self.tupleIndex = self.tupleIndex + 1 |
|
268 | self.tupleIndex = self.tupleIndex + 1 | |
269 |
|
269 | |||
270 | # add to optionTuples |
|
270 | # add to optionTuples | |
271 | self.optionTuples = self.optionTuples + [oTuple] |
|
271 | self.optionTuples = self.optionTuples + [oTuple] | |
272 |
|
272 | |||
273 | def addOptionConfigurationTuple(self, oTuple): |
|
273 | def addOptionConfigurationTuple(self, oTuple): | |
274 | (name, argSpec, realName) = oTuple |
|
274 | (name, argSpec, realName) = oTuple | |
275 | if self.ignoreCase: |
|
275 | if self.ignoreCase: | |
276 | name = string.lower(name) |
|
276 | name = string.lower(name) | |
277 | if realName: |
|
277 | if realName: | |
278 | realName = string.lower(realName) |
|
278 | realName = string.lower(realName) | |
279 | else: |
|
279 | else: | |
280 | realName = name |
|
280 | realName = name | |
281 |
|
281 | |||
282 | oTuple = (name, argSpec, realName) |
|
282 | oTuple = (name, argSpec, realName) | |
283 |
|
283 | |||
284 | # add option |
|
284 | # add option | |
285 | self._addOption(oTuple) |
|
285 | self._addOption(oTuple) | |
286 |
|
286 | |||
287 | def addOptionConfigurationTuples(self, oTuple): |
|
287 | def addOptionConfigurationTuples(self, oTuple): | |
288 | if type(oTuple) is ListType: |
|
288 | if type(oTuple) is ListType: | |
289 | for t in oTuple: |
|
289 | for t in oTuple: | |
290 | self.addOptionConfigurationTuple(t) |
|
290 | self.addOptionConfigurationTuple(t) | |
291 | else: |
|
291 | else: | |
292 | self.addOptionConfigurationTuple(oTuple) |
|
292 | self.addOptionConfigurationTuple(oTuple) | |
293 |
|
293 | |||
294 | def parseConfiguration(self, spec): |
|
294 | def parseConfiguration(self, spec): | |
295 | # destroy previous stored information + store raw spec |
|
295 | # destroy previous stored information + store raw spec | |
296 | self.spec = spec |
|
296 | self.spec = spec | |
297 | self.optionTuples = [] |
|
297 | self.optionTuples = [] | |
298 | self.optionNames = {} |
|
298 | self.optionNames = {} | |
299 | self.tupleIndex = 0 |
|
299 | self.tupleIndex = 0 | |
300 |
|
300 | |||
301 | tupleIndex = 0 |
|
301 | tupleIndex = 0 | |
302 |
|
302 | |||
303 | # create some regex's for parsing each spec |
|
303 | # create some regex's for parsing each spec | |
304 | splitExpr = \ |
|
304 | splitExpr = \ | |
305 | re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?') |
|
305 | re.compile('(?P<names>\w+[-A-Za-z0-9|]*)?(?P<spec>!|[=:][infs]@?)?') | |
306 | for option in spec: |
|
306 | for option in spec: | |
307 | # push to lower case (does not negatively affect |
|
307 | # push to lower case (does not negatively affect | |
308 | # specification) |
|
308 | # specification) | |
309 | if self.ignoreCase: |
|
309 | if self.ignoreCase: | |
310 | option = string.lower(option) |
|
310 | option = string.lower(option) | |
311 |
|
311 | |||
312 | # break into names, specification |
|
312 | # break into names, specification | |
313 | match = splitExpr.match(option) |
|
313 | match = splitExpr.match(option) | |
314 | if match is None: |
|
314 | if match is None: | |
315 | raise SpecificationError('Invalid specification {' + option + |
|
315 | raise SpecificationError('Invalid specification {' + option + | |
316 | '}') |
|
316 | '}') | |
317 |
|
317 | |||
318 | names = match.group('names') |
|
318 | names = match.group('names') | |
319 | specification = match.group('spec') |
|
319 | specification = match.group('spec') | |
320 |
|
320 | |||
321 | # break name into name, aliases |
|
321 | # break name into name, aliases | |
322 | nlist = string.split(names, '|') |
|
322 | nlist = string.split(names, '|') | |
323 |
|
323 | |||
324 | # get name |
|
324 | # get name | |
325 | name = nlist[0] |
|
325 | name = nlist[0] | |
326 | aliases = nlist[1:] |
|
326 | aliases = nlist[1:] | |
327 |
|
327 | |||
328 | # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)') |
|
328 | # specificationExpr = regex.symcomp('\(<required>.\)\(<type>.\)\(<multi>@?\)') | |
329 | if not specification: |
|
329 | if not specification: | |
330 | #spec tuple is ('type', 'arg mode', 'default value', 'multiple') |
|
330 | #spec tuple is ('type', 'arg mode', 'default value', 'multiple') | |
331 | argType = GenericArgType |
|
331 | argType = GenericArgType | |
332 | argMode = None |
|
332 | argMode = None | |
333 | argDefault = 1 |
|
333 | argDefault = 1 | |
334 | argMultiple = 0 |
|
334 | argMultiple = 0 | |
335 | elif specification == '!': |
|
335 | elif specification == '!': | |
336 | argType = BooleanArgType |
|
336 | argType = BooleanArgType | |
337 | argMode = None |
|
337 | argMode = None | |
338 | argDefault = 1 |
|
338 | argDefault = 1 | |
339 | argMultiple = 0 |
|
339 | argMultiple = 0 | |
340 | else: |
|
340 | else: | |
341 | # parse |
|
341 | # parse | |
342 | match = specificationExpr.match(specification) |
|
342 | match = specificationExpr.match(specification) | |
343 | if match is None: |
|
343 | if match is None: | |
344 | # failed to parse, die |
|
344 | # failed to parse, die | |
345 | raise SpecificationError('Invalid configuration for option \'' |
|
345 | raise SpecificationError('Invalid configuration for option \'' | |
346 | + option + '\'') |
|
346 | + option + '\'') | |
347 |
|
347 | |||
348 | # determine mode |
|
348 | # determine mode | |
349 | required = match.group('required') |
|
349 | required = match.group('required') | |
350 | if required == '=': |
|
350 | if required == '=': | |
351 | argMode = ArgRequired |
|
351 | argMode = ArgRequired | |
352 | elif required == ':': |
|
352 | elif required == ':': | |
353 | argMode = ArgOptional |
|
353 | argMode = ArgOptional | |
354 | else: |
|
354 | else: | |
355 | raise SpecificationError('Unknown requirement configuration \'' |
|
355 | raise SpecificationError('Unknown requirement configuration \'' | |
356 | + required + '\'') |
|
356 | + required + '\'') | |
357 |
|
357 | |||
358 | # determine type |
|
358 | # determine type | |
359 | type = match.group('type') |
|
359 | type = match.group('type') | |
360 | if type == 's': |
|
360 | if type == 's': | |
361 | argType = StringArgType |
|
361 | argType = StringArgType | |
362 | argDefault = '' |
|
362 | argDefault = '' | |
363 | elif type == 'i': |
|
363 | elif type == 'i': | |
364 | argType = IntegerArgType |
|
364 | argType = IntegerArgType | |
365 | argDefault = 1 |
|
365 | argDefault = 1 | |
366 | elif type == 'f' or type == 'n': |
|
366 | elif type == 'f' or type == 'n': | |
367 | argType = RealArgType |
|
367 | argType = RealArgType | |
368 | argDefault = 1 |
|
368 | argDefault = 1 | |
369 | else: |
|
369 | else: | |
370 | raise SpecificationError('Unknown type specifier \'' + |
|
370 | raise SpecificationError('Unknown type specifier \'' + | |
371 | type + '\'') |
|
371 | type + '\'') | |
372 |
|
372 | |||
373 | # determine quantity |
|
373 | # determine quantity | |
374 | if match.group('multi') == '@': |
|
374 | if match.group('multi') == '@': | |
375 | argMultiple = 1 |
|
375 | argMultiple = 1 | |
376 | else: |
|
376 | else: | |
377 | argMultiple = 0 |
|
377 | argMultiple = 0 | |
378 | ## end else (of not specification) |
|
378 | ## end else (of not specification) | |
379 |
|
379 | |||
380 | # construct specification tuple |
|
380 | # construct specification tuple | |
381 | specTuple = (argType, argMode, argDefault, argMultiple) |
|
381 | specTuple = (argType, argMode, argDefault, argMultiple) | |
382 |
|
382 | |||
383 | # add the option-- option tuple is (name, specTuple, real name) |
|
383 | # add the option-- option tuple is (name, specTuple, real name) | |
384 | oTuple = (name, specTuple, name) |
|
384 | oTuple = (name, specTuple, name) | |
385 | self._addOption(oTuple) |
|
385 | self._addOption(oTuple) | |
386 |
|
386 | |||
387 | for alias in aliases: |
|
387 | for alias in aliases: | |
388 | # drop to all lower (if configured to do so) |
|
388 | # drop to all lower (if configured to do so) | |
389 | if self.ignoreCase: |
|
389 | if self.ignoreCase: | |
390 | alias = string.lower(alias) |
|
390 | alias = string.lower(alias) | |
391 | # create configuration tuple |
|
391 | # create configuration tuple | |
392 | oTuple = (alias, specTuple, name) |
|
392 | oTuple = (alias, specTuple, name) | |
393 | # add |
|
393 | # add | |
394 | self._addOption(oTuple) |
|
394 | self._addOption(oTuple) | |
395 |
|
395 | |||
396 | # successfully parsed.... |
|
396 | # successfully parsed.... | |
397 | self.needsParse = 0 |
|
397 | self.needsParse = 0 | |
398 |
|
398 | |||
399 | def _getArgTuple(self, argName): |
|
399 | def _getArgTuple(self, argName): | |
400 | """ |
|
400 | """ | |
401 | Returns a list containing all the specification tuples that |
|
401 | Returns a list containing all the specification tuples that | |
402 | match argName. If none match, None is returned. If one |
|
402 | match argName. If none match, None is returned. If one | |
403 | matches, a list with one tuple is returned. If more than one |
|
403 | matches, a list with one tuple is returned. If more than one | |
404 | match, a list containing all the tuples that matched is |
|
404 | match, a list containing all the tuples that matched is | |
405 | returned. |
|
405 | returned. | |
406 |
|
406 | |||
407 | In other words, this function does not pass judgement upon the |
|
407 | In other words, this function does not pass judgement upon the | |
408 | validity of multiple matches. |
|
408 | validity of multiple matches. | |
409 | """ |
|
409 | """ | |
410 | # is it in the optionNames dict? |
|
410 | # is it in the optionNames dict? | |
411 |
|
411 | |||
412 | try: |
|
412 | try: | |
413 | # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n") |
|
413 | # sys.stderr.write(argName + string.join(self.optionNames.keys()) + "\n") | |
414 |
|
414 | |||
415 | # yes, get index |
|
415 | # yes, get index | |
416 | tupleIndex = self.optionNames[argName] |
|
416 | tupleIndex = self.optionNames[argName] | |
417 | # and return tuple as element of list |
|
417 | # and return tuple as element of list | |
418 | return [self.optionTuples[tupleIndex]] |
|
418 | return [self.optionTuples[tupleIndex]] | |
419 | except KeyError: |
|
419 | except KeyError: | |
420 | # are abbreviations allowed? |
|
420 | # are abbreviations allowed? | |
421 | if not self.allowAbbreviations: |
|
421 | if not self.allowAbbreviations: | |
422 | # No! terefore, this cannot be valid argument-- nothing found |
|
422 | # No! terefore, this cannot be valid argument-- nothing found | |
423 | return None |
|
423 | return None | |
424 |
|
424 | |||
425 | # argName might be an abbreviation (and, abbreviations must |
|
425 | # argName might be an abbreviation (and, abbreviations must | |
426 | # be allowed... or this would not have been reached!) |
|
426 | # be allowed... or this would not have been reached!) | |
427 |
|
427 | |||
428 | # create regex for argName |
|
428 | # create regex for argName | |
429 | argExpr = re.compile('^' + argName) |
|
429 | argExpr = re.compile('^' + argName) | |
430 |
|
430 | |||
431 | tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None, |
|
431 | tuples = filter(lambda x, argExpr=argExpr: argExpr.search(x[0]) is not None, | |
432 | self.optionTuples) |
|
432 | self.optionTuples) | |
433 |
|
433 | |||
434 | if not len(tuples): |
|
434 | if not len(tuples): | |
435 | return None |
|
435 | return None | |
436 | else: |
|
436 | else: | |
437 | return tuples |
|
437 | return tuples | |
438 |
|
438 | |||
439 | def _isTerminator(self, optionName): |
|
439 | def _isTerminator(self, optionName): | |
440 | """ |
|
440 | """ | |
441 | Returns the full name of the terminator if optionName is a valid |
|
441 | 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 |
|
442 | terminator. If it is, sets self.terminator to the full name of | |
443 | the terminator. |
|
443 | the terminator. | |
444 |
|
444 | |||
445 | If more than one terminator matched, raises a TerminationError with a |
|
445 | If more than one terminator matched, raises a TerminationError with a | |
446 | string describing the ambiguity. |
|
446 | string describing the ambiguity. | |
447 | """ |
|
447 | """ | |
448 |
|
448 | |||
449 | # sys.stderr.write(optionName + "\n") |
|
449 | # sys.stderr.write(optionName + "\n") | |
450 | # sys.stderr.write(repr(self.terminators)) |
|
450 | # sys.stderr.write(repr(self.terminators)) | |
451 |
|
451 | |||
452 | if optionName in self.terminators: |
|
452 | if optionName in self.terminators: | |
453 | self.terminator = optionName |
|
453 | self.terminator = optionName | |
454 | elif not self.allowAbbreviations: |
|
454 | elif not self.allowAbbreviations: | |
455 | return None |
|
455 | return None | |
456 |
|
456 | |||
457 | # regex thing in bogus |
|
457 | # regex thing in bogus | |
458 | # termExpr = regex.compile('^' + optionName) |
|
458 | # termExpr = regex.compile('^' + optionName) | |
459 |
|
459 | |||
460 | terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators) |
|
460 | terms = filter(lambda x, on=optionName: string.find(x,on) == 0, self.terminators) | |
461 |
|
461 | |||
462 | if not len(terms): |
|
462 | if not len(terms): | |
463 | return None |
|
463 | return None | |
464 | elif len(terms) > 1: |
|
464 | elif len(terms) > 1: | |
465 | raise TerminationError('Ambiguous terminator \'' + optionName + |
|
465 | raise TerminationError('Ambiguous terminator \'' + optionName + | |
466 | '\' matches ' + repr(terms)) |
|
466 | '\' matches ' + repr(terms)) | |
467 |
|
467 | |||
468 | self.terminator = terms[0] |
|
468 | self.terminator = terms[0] | |
469 | return self.terminator |
|
469 | return self.terminator | |
470 |
|
470 | |||
471 | def processArguments(self, args = None): |
|
471 | def processArguments(self, args = None): | |
472 | """ |
|
472 | """ | |
473 | Processes args, a list of arguments (including options). |
|
473 | Processes args, a list of arguments (including options). | |
474 |
|
474 | |||
475 | If args is the same as sys.argv, automatically trims the first |
|
475 | If args is the same as sys.argv, automatically trims the first | |
476 | argument (the executable name/path). |
|
476 | argument (the executable name/path). | |
477 |
|
477 | |||
478 | If an exception is not raised, the argument list was parsed |
|
478 | If an exception is not raised, the argument list was parsed | |
479 | correctly. |
|
479 | correctly. | |
480 |
|
480 | |||
481 | Upon successful completion, the freeValues instance variable |
|
481 | Upon successful completion, the freeValues instance variable | |
482 | will contain all the arguments that were not associated with an |
|
482 | will contain all the arguments that were not associated with an | |
483 | option in the order they were encountered. optionValues is a |
|
483 | option in the order they were encountered. optionValues is a | |
484 | dictionary containing the value of each option-- the method |
|
484 | dictionary containing the value of each option-- the method | |
485 | valueForOption() can be used to query this dictionary. |
|
485 | valueForOption() can be used to query this dictionary. | |
486 | terminator will contain the argument encountered that terminated |
|
486 | terminator will contain the argument encountered that terminated | |
487 | option processing (or None, if a terminator was never |
|
487 | option processing (or None, if a terminator was never | |
488 | encountered) and termValues will contain all of the options that |
|
488 | encountered) and termValues will contain all of the options that | |
489 | appeared after the Terminator (or an empty list). |
|
489 | appeared after the Terminator (or an empty list). | |
490 | """ |
|
490 | """ | |
491 |
|
491 | |||
492 | if hasattr(sys, "argv") and args == sys.argv: |
|
492 | if hasattr(sys, "argv") and args == sys.argv: | |
493 | args = sys.argv[1:] |
|
493 | args = sys.argv[1:] | |
494 |
|
494 | |||
495 | max = len(args) # maximum index + 1 |
|
495 | max = len(args) # maximum index + 1 | |
496 | self.freeValues = [] # array to hold return values |
|
496 | self.freeValues = [] # array to hold return values | |
497 | self.optionValues= {} |
|
497 | self.optionValues= {} | |
498 | index = 0 # initial index |
|
498 | index = 0 # initial index | |
499 | self.terminator = None |
|
499 | self.terminator = None | |
500 | self.termValues = [] |
|
500 | self.termValues = [] | |
501 |
|
501 | |||
502 | while index < max: |
|
502 | while index < max: | |
503 | # obtain argument |
|
503 | # obtain argument | |
504 | arg = args[index] |
|
504 | arg = args[index] | |
505 | # increment index -- REMEMBER; it is NOW incremented |
|
505 | # increment index -- REMEMBER; it is NOW incremented | |
506 | index = index + 1 |
|
506 | index = index + 1 | |
507 |
|
507 | |||
508 | # terminate immediately if option terminator encountered |
|
508 | # terminate immediately if option terminator encountered | |
509 | if self._isTerminator(arg): |
|
509 | if self._isTerminator(arg): | |
510 | self.freeValues = self.freeValues + args[index:] |
|
510 | self.freeValues = self.freeValues + args[index:] | |
511 | self.termValues = args[index:] |
|
511 | self.termValues = args[index:] | |
512 | return |
|
512 | return | |
513 |
|
513 | |||
514 | # is this possibly an option? |
|
514 | # is this possibly an option? | |
515 | match = self.optionStartExpr.match(arg) |
|
515 | match = self.optionStartExpr.match(arg) | |
516 | if match is None: |
|
516 | if match is None: | |
517 | # not an option-- add to freeValues |
|
517 | # not an option-- add to freeValues | |
518 | self.freeValues = self.freeValues + [arg] |
|
518 | self.freeValues = self.freeValues + [arg] | |
519 | if not self.orderMixed: |
|
519 | if not self.orderMixed: | |
520 | # mixing not allowed; add rest of args as freeValues |
|
520 | # mixing not allowed; add rest of args as freeValues | |
521 | self.freeValues = self.freeValues + args[index:] |
|
521 | self.freeValues = self.freeValues + args[index:] | |
522 | # return to caller |
|
522 | # return to caller | |
523 | return |
|
523 | return | |
524 | else: |
|
524 | else: | |
525 | continue |
|
525 | continue | |
526 |
|
526 | |||
527 | # grab name |
|
527 | # grab name | |
528 | optName = match.group('option') |
|
528 | optName = match.group('option') | |
529 |
|
529 | |||
530 | # obtain next argument-- index has already been incremented |
|
530 | # obtain next argument-- index has already been incremented | |
531 | nextArg = match.group('arg') |
|
531 | nextArg = match.group('arg') | |
532 | if nextArg: |
|
532 | if nextArg: | |
533 | nextArg = nextArg[1:] |
|
533 | nextArg = nextArg[1:] | |
534 | index = index - 1 # put it back |
|
534 | index = index - 1 # put it back | |
535 | else: |
|
535 | else: | |
536 | try: |
|
536 | try: | |
537 | nextArg = args[index] |
|
537 | nextArg = args[index] | |
538 | except: |
|
538 | except: | |
539 | nextArg = None |
|
539 | nextArg = None | |
540 |
|
540 | |||
541 | # transpose to lower case, if necessary |
|
541 | # transpose to lower case, if necessary | |
542 | if self.ignoreCase: |
|
542 | if self.ignoreCase: | |
543 | optName = string.lower(optName) |
|
543 | optName = string.lower(optName) | |
544 |
|
544 | |||
545 | # obtain defining tuple |
|
545 | # obtain defining tuple | |
546 | tuples = self._getArgTuple(optName) |
|
546 | tuples = self._getArgTuple(optName) | |
547 |
|
547 | |||
548 | if tuples == None: |
|
548 | if tuples == None: | |
549 | raise ArgumentError('Illegal option \'' + arg + '\'') |
|
549 | raise ArgumentError('Illegal option \'' + arg + '\'') | |
550 | elif len(tuples) > 1: |
|
550 | elif len(tuples) > 1: | |
551 | raise ArgumentError('Ambiguous option \'' + arg + |
|
551 | raise ArgumentError('Ambiguous option \'' + arg + | |
552 | '\'; matches ' + |
|
552 | '\'; matches ' + | |
553 | repr(map(lambda x: x[0], tuples))) |
|
553 | repr(map(lambda x: x[0], tuples))) | |
554 | else: |
|
554 | else: | |
555 | config = tuples[0] |
|
555 | config = tuples[0] | |
556 |
|
556 | |||
557 | # config is now set to the configuration tuple for the |
|
557 | # config is now set to the configuration tuple for the | |
558 | # argument |
|
558 | # argument | |
559 | (fullName, spec, realName) = config |
|
559 | (fullName, spec, realName) = config | |
560 | (optType, optMode, optDefault, optMultiple) = spec |
|
560 | (optType, optMode, optDefault, optMultiple) = spec | |
561 |
|
561 | |||
562 | # if opt mode required, but nextArg is none, raise an error |
|
562 | # if opt mode required, but nextArg is none, raise an error | |
563 | if (optMode == ArgRequired): |
|
563 | if (optMode == ArgRequired): | |
564 | if (not nextArg) or self._isTerminator(nextArg): |
|
564 | if (not nextArg) or self._isTerminator(nextArg): | |
565 | # print nextArg |
|
565 | # print nextArg | |
566 | raise ArgumentError('Option \'' + arg + |
|
566 | raise ArgumentError('Option \'' + arg + | |
567 | '\' requires an argument of type ' + |
|
567 | '\' requires an argument of type ' + | |
568 | optType) |
|
568 | optType) | |
569 |
|
569 | |||
570 | if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)): |
|
570 | if (not optMode == None) and nextArg and (not self._isTerminator(nextArg)): | |
571 | # nextArg defined, option configured to possibly consume arg |
|
571 | # nextArg defined, option configured to possibly consume arg | |
572 | try: |
|
572 | try: | |
573 | # grab conversion function-- the try is more for internal diagnostics |
|
573 | # grab conversion function-- the try is more for internal diagnostics | |
574 | func = ConversionFunctions[optType] |
|
574 | func = ConversionFunctions[optType] | |
575 | try: |
|
575 | try: | |
576 | optionValue = func(nextArg) |
|
576 | optionValue = func(nextArg) | |
577 | index = index + 1 |
|
577 | index = index + 1 | |
578 | except: |
|
578 | except: | |
579 | # only raise conversion error if REQUIRED to consume argument |
|
579 | # only raise conversion error if REQUIRED to consume argument | |
580 | if optMode == ArgRequired: |
|
580 | if optMode == ArgRequired: | |
581 | raise ArgumentError('Invalid argument to option \'' |
|
581 | raise ArgumentError('Invalid argument to option \'' | |
582 | + arg + '\'; should be \'' + |
|
582 | + arg + '\'; should be \'' + | |
583 | optType + '\'') |
|
583 | optType + '\'') | |
584 | else: |
|
584 | else: | |
585 | optionValue = optDefault |
|
585 | optionValue = optDefault | |
586 | except ArgumentError: |
|
586 | except ArgumentError: | |
587 | raise |
|
587 | raise | |
588 | except: |
|
588 | except: | |
589 | raise ArgumentError('(' + arg + |
|
589 | raise ArgumentError('(' + arg + | |
590 | ') Conversion function for \'' + |
|
590 | ') Conversion function for \'' + | |
591 | optType + '\' not found.') |
|
591 | optType + '\' not found.') | |
592 | else: |
|
592 | else: | |
593 | optionValue = optDefault |
|
593 | optionValue = optDefault | |
594 |
|
594 | |||
595 | # add value to options dictionary |
|
595 | # add value to options dictionary | |
596 | if optMultiple: |
|
596 | if optMultiple: | |
597 | # can be multiple values |
|
597 | # can be multiple values | |
598 | try: |
|
598 | try: | |
599 | # try to append element |
|
599 | # try to append element | |
600 | self.optionValues[realName] = self.optionValues[realName] + [optionValue] |
|
600 | self.optionValues[realName] = self.optionValues[realName] + [optionValue] | |
601 | except: |
|
601 | except: | |
602 | # failed-- must not exist; add it |
|
602 | # failed-- must not exist; add it | |
603 | self.optionValues[realName] = [optionValue] |
|
603 | self.optionValues[realName] = [optionValue] | |
604 | else: |
|
604 | else: | |
605 | # only one value per |
|
605 | # only one value per | |
606 | if self.isPosixCompliant and self.optionValues.has_key(realName): |
|
606 | if self.isPosixCompliant and self.optionValues.has_key(realName): | |
607 | raise ArgumentError('Argument \'' + arg + |
|
607 | raise ArgumentError('Argument \'' + arg + | |
608 | '\' occurs multiple times.') |
|
608 | '\' occurs multiple times.') | |
609 |
|
609 | |||
610 | self.optionValues[realName] = optionValue |
|
610 | self.optionValues[realName] = optionValue | |
611 |
|
611 | |||
612 | def valueForOption(self, optionName, defaultValue = None): |
|
612 | def valueForOption(self, optionName, defaultValue = None): | |
613 | """ |
|
613 | """ | |
614 | Return the value associated with optionName. If optionName was |
|
614 | Return the value associated with optionName. If optionName was | |
615 | not encountered during parsing of the arguments, returns the |
|
615 | not encountered during parsing of the arguments, returns the | |
616 | defaultValue (which defaults to None). |
|
616 | defaultValue (which defaults to None). | |
617 | """ |
|
617 | """ | |
618 | try: |
|
618 | try: | |
619 | optionValue = self.optionValues[optionName] |
|
619 | optionValue = self.optionValues[optionName] | |
620 | except: |
|
620 | except: | |
621 | optionValue = defaultValue |
|
621 | optionValue = defaultValue | |
622 |
|
622 | |||
623 | return optionValue |
|
623 | return optionValue | |
624 |
|
624 | |||
625 | ## |
|
625 | ## | |
626 | ## test/example section |
|
626 | ## test/example section | |
627 | ## |
|
627 | ## | |
628 | test_error = 'Test Run Amok!' |
|
628 | test_error = 'Test Run Amok!' | |
629 | def _test(): |
|
629 | def _test(): | |
630 | """ |
|
630 | """ | |
631 | A relatively complete test suite. |
|
631 | A relatively complete test suite. | |
632 | """ |
|
632 | """ | |
633 | try: |
|
633 | try: | |
634 | DPyGetOpt(['foo', 'bar=s', 'foo']) |
|
634 | DPyGetOpt(['foo', 'bar=s', 'foo']) | |
635 | except Error, exc: |
|
635 | except Error, exc: | |
636 | print 'EXCEPTION (should be \'foo\' already used..): %s' % exc |
|
636 | print 'EXCEPTION (should be \'foo\' already used..): %s' % exc | |
637 |
|
637 | |||
638 | try: |
|
638 | try: | |
639 | DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!']) |
|
639 | DPyGetOpt(['foo|bar|apple=s@', 'baz|apple!']) | |
640 | except Error, exc: |
|
640 | except Error, exc: | |
641 | print 'EXCEPTION (should be duplicate alias/name error): %s' % exc |
|
641 | print 'EXCEPTION (should be duplicate alias/name error): %s' % exc | |
642 |
|
642 | |||
643 | x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@']) |
|
643 | x = DPyGetOpt(['apple|atlas=i@', 'application|executable=f@']) | |
644 | try: |
|
644 | try: | |
645 | x.processArguments(['-app', '29.3']) |
|
645 | x.processArguments(['-app', '29.3']) | |
646 | except Error, exc: |
|
646 | except Error, exc: | |
647 | print 'EXCEPTION (should be ambiguous argument): %s' % exc |
|
647 | print 'EXCEPTION (should be ambiguous argument): %s' % exc | |
648 |
|
648 | |||
649 | x = DPyGetOpt(['foo'], ['antigravity', 'antithesis']) |
|
649 | x = DPyGetOpt(['foo'], ['antigravity', 'antithesis']) | |
650 | try: |
|
650 | try: | |
651 | x.processArguments(['-foo', 'anti']) |
|
651 | x.processArguments(['-foo', 'anti']) | |
652 | except Error, exc: |
|
652 | except Error, exc: | |
653 | print 'EXCEPTION (should be ambiguous terminator): %s' % exc |
|
653 | print 'EXCEPTION (should be ambiguous terminator): %s' % exc | |
654 |
|
654 | |||
655 | profile = ['plain-option', |
|
655 | profile = ['plain-option', | |
656 | 'boolean-option!', |
|
656 | 'boolean-option!', | |
657 | 'list-of-integers=i@', |
|
657 | 'list-of-integers=i@', | |
658 | 'list-real-option|list-real-alias|list-real-pseudonym=f@', |
|
658 | 'list-real-option|list-real-alias|list-real-pseudonym=f@', | |
659 | 'optional-string-option:s', |
|
659 | 'optional-string-option:s', | |
660 | 'abbreviated-string-list=s@'] |
|
660 | 'abbreviated-string-list=s@'] | |
661 |
|
661 | |||
662 | terminators = ['terminator'] |
|
662 | terminators = ['terminator'] | |
663 |
|
663 | |||
664 | args = ['-plain-option', |
|
664 | args = ['-plain-option', | |
665 | '+noboolean-option', |
|
665 | '+noboolean-option', | |
666 | '--list-of-integers', '1', |
|
666 | '--list-of-integers', '1', | |
667 | '+list-of-integers', '2', |
|
667 | '+list-of-integers', '2', | |
668 | '-list-of-integers', '3', |
|
668 | '-list-of-integers', '3', | |
669 | 'freeargone', |
|
669 | 'freeargone', | |
670 | '-list-real-option', '1.1', |
|
670 | '-list-real-option', '1.1', | |
671 | '+list-real-alias', '1.2', |
|
671 | '+list-real-alias', '1.2', | |
672 | '--list-real-pseudonym', '1.3', |
|
672 | '--list-real-pseudonym', '1.3', | |
673 | 'freeargtwo', |
|
673 | 'freeargtwo', | |
674 | '-abbreviated-string-list', 'String1', |
|
674 | '-abbreviated-string-list', 'String1', | |
675 | '--abbreviated-s', 'String2', |
|
675 | '--abbreviated-s', 'String2', | |
676 | '-abbrev', 'String3', |
|
676 | '-abbrev', 'String3', | |
677 | '-a', 'String4', |
|
677 | '-a', 'String4', | |
678 | '-optional-string-option', |
|
678 | '-optional-string-option', | |
679 | 'term', |
|
679 | 'term', | |
680 | 'next option should look like an invalid arg', |
|
680 | 'next option should look like an invalid arg', | |
681 | '-a'] |
|
681 | '-a'] | |
682 |
|
682 | |||
683 |
|
683 | |||
684 | print 'Using profile: ' + repr(profile) |
|
684 | print 'Using profile: ' + repr(profile) | |
685 | print 'With terminator: ' + repr(terminators) |
|
685 | print 'With terminator: ' + repr(terminators) | |
686 | print 'Processing arguments: ' + repr(args) |
|
686 | print 'Processing arguments: ' + repr(args) | |
687 |
|
687 | |||
688 | go = DPyGetOpt(profile, terminators) |
|
688 | go = DPyGetOpt(profile, terminators) | |
689 | go.processArguments(args) |
|
689 | go.processArguments(args) | |
690 |
|
690 | |||
691 | print 'Options (and values): ' + repr(go.optionValues) |
|
691 | print 'Options (and values): ' + repr(go.optionValues) | |
692 | print 'free args: ' + repr(go.freeValues) |
|
692 | print 'free args: ' + repr(go.freeValues) | |
693 | print 'term args: ' + repr(go.termValues) |
|
693 | print 'term args: ' + repr(go.termValues) |
This diff has been collapsed as it changes many lines, (1034 lines changed) Show them Hide them | |||||
@@ -1,517 +1,517 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 |
|
17 | |||
18 | $Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $""" |
|
18 | $Id: Debugger.py 2913 2007-12-31 12:42:14Z vivainio $""" | |
19 |
|
19 | |||
20 | #***************************************************************************** |
|
20 | #***************************************************************************** | |
21 | # |
|
21 | # | |
22 | # This file is licensed under the PSF license. |
|
22 | # This file is licensed under the PSF license. | |
23 | # |
|
23 | # | |
24 | # Copyright (C) 2001 Python Software Foundation, www.python.org |
|
24 | # Copyright (C) 2001 Python Software Foundation, www.python.org | |
25 | # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu> |
|
25 | # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu> | |
26 | # |
|
26 | # | |
27 | # |
|
27 | # | |
28 | #***************************************************************************** |
|
28 | #***************************************************************************** | |
29 |
|
29 | |||
30 | from IPython import Release |
|
30 | from IPython import Release | |
31 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
31 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
32 | __license__ = 'Python' |
|
32 | __license__ = 'Python' | |
33 |
|
33 | |||
34 | import bdb |
|
34 | import bdb | |
35 | import cmd |
|
35 | import cmd | |
36 | import linecache |
|
36 | import linecache | |
37 | import os |
|
37 | import os | |
38 | import sys |
|
38 | import sys | |
39 |
|
39 | |||
40 | from IPython import PyColorize, ColorANSI, ipapi |
|
40 | from IPython import PyColorize, ColorANSI, ipapi | |
41 | from IPython.genutils import Term |
|
41 | from IPython.genutils import Term | |
42 | from IPython.excolors import ExceptionColors |
|
42 | from IPython.excolors import ExceptionColors | |
43 |
|
43 | |||
44 | # See if we can use pydb. |
|
44 | # See if we can use pydb. | |
45 | has_pydb = False |
|
45 | has_pydb = False | |
46 | prompt = 'ipdb> ' |
|
46 | prompt = 'ipdb> ' | |
47 | #We have to check this directly from sys.argv, config struct not yet available |
|
47 | #We have to check this directly from sys.argv, config struct not yet available | |
48 | if '-pydb' in sys.argv: |
|
48 | if '-pydb' in sys.argv: | |
49 | try: |
|
49 | try: | |
50 | import pydb |
|
50 | import pydb | |
51 | if hasattr(pydb.pydb, "runl") and pydb.version>'1.17': |
|
51 | 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 |
|
52 | # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we | |
53 | # better protect against it. |
|
53 | # better protect against it. | |
54 | has_pydb = True |
|
54 | has_pydb = True | |
55 | except ImportError: |
|
55 | except ImportError: | |
56 | print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available" |
|
56 | print "Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available" | |
57 |
|
57 | |||
58 | if has_pydb: |
|
58 | if has_pydb: | |
59 | from pydb import Pdb as OldPdb |
|
59 | from pydb import Pdb as OldPdb | |
60 | #print "Using pydb for %run -d and post-mortem" #dbg |
|
60 | #print "Using pydb for %run -d and post-mortem" #dbg | |
61 | prompt = 'ipydb> ' |
|
61 | prompt = 'ipydb> ' | |
62 | else: |
|
62 | else: | |
63 | from pdb import Pdb as OldPdb |
|
63 | from pdb import Pdb as OldPdb | |
64 |
|
64 | |||
65 | # Allow the set_trace code to operate outside of an ipython instance, even if |
|
65 | # 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 |
|
66 | # it does so with some limitations. The rest of this support is implemented in | |
67 | # the Tracer constructor. |
|
67 | # the Tracer constructor. | |
68 | def BdbQuit_excepthook(et,ev,tb): |
|
68 | def BdbQuit_excepthook(et,ev,tb): | |
69 | if et==bdb.BdbQuit: |
|
69 | if et==bdb.BdbQuit: | |
70 | print 'Exiting Debugger.' |
|
70 | print 'Exiting Debugger.' | |
71 | else: |
|
71 | else: | |
72 | BdbQuit_excepthook.excepthook_ori(et,ev,tb) |
|
72 | BdbQuit_excepthook.excepthook_ori(et,ev,tb) | |
73 |
|
73 | |||
74 | def BdbQuit_IPython_excepthook(self,et,ev,tb): |
|
74 | def BdbQuit_IPython_excepthook(self,et,ev,tb): | |
75 | print 'Exiting Debugger.' |
|
75 | print 'Exiting Debugger.' | |
76 |
|
76 | |||
77 | class Tracer(object): |
|
77 | class Tracer(object): | |
78 | """Class for local debugging, similar to pdb.set_trace. |
|
78 | """Class for local debugging, similar to pdb.set_trace. | |
79 |
|
79 | |||
80 | Instances of this class, when called, behave like pdb.set_trace, but |
|
80 | Instances of this class, when called, behave like pdb.set_trace, but | |
81 | providing IPython's enhanced capabilities. |
|
81 | providing IPython's enhanced capabilities. | |
82 |
|
82 | |||
83 | This is implemented as a class which must be initialized in your own code |
|
83 | 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 |
|
84 | 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 |
|
85 | 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, |
|
86 | constructor, ensuring that this code plays nicely with a running IPython, | |
87 | while functioning acceptably (though with limitations) if outside of it. |
|
87 | while functioning acceptably (though with limitations) if outside of it. | |
88 | """ |
|
88 | """ | |
89 |
|
89 | |||
90 | def __init__(self,colors=None): |
|
90 | def __init__(self,colors=None): | |
91 | """Create a local debugger instance. |
|
91 | """Create a local debugger instance. | |
92 |
|
92 | |||
93 | :Parameters: |
|
93 | :Parameters: | |
94 |
|
94 | |||
95 | - `colors` (None): a string containing the name of the color scheme to |
|
95 | - `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 |
|
96 | 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 |
|
97 | function will default to the current IPython scheme when running inside | |
98 | IPython, and to 'NoColor' otherwise. |
|
98 | IPython, and to 'NoColor' otherwise. | |
99 |
|
99 | |||
100 | Usage example: |
|
100 | Usage example: | |
101 |
|
101 | |||
102 | from IPython.Debugger import Tracer; debug_here = Tracer() |
|
102 | from IPython.Debugger import Tracer; debug_here = Tracer() | |
103 |
|
103 | |||
104 | ... later in your code |
|
104 | ... later in your code | |
105 | debug_here() # -> will open up the debugger at that point. |
|
105 | debug_here() # -> will open up the debugger at that point. | |
106 |
|
106 | |||
107 | Once the debugger activates, you can use all of its regular commands to |
|
107 | Once the debugger activates, you can use all of its regular commands to | |
108 | step through code, set breakpoints, etc. See the pdb documentation |
|
108 | step through code, set breakpoints, etc. See the pdb documentation | |
109 | from the Python standard library for usage details. |
|
109 | from the Python standard library for usage details. | |
110 | """ |
|
110 | """ | |
111 |
|
111 | |||
112 | global __IPYTHON__ |
|
112 | global __IPYTHON__ | |
113 | try: |
|
113 | try: | |
114 | __IPYTHON__ |
|
114 | __IPYTHON__ | |
115 | except NameError: |
|
115 | except NameError: | |
116 | # Outside of ipython, we set our own exception hook manually |
|
116 | # Outside of ipython, we set our own exception hook manually | |
117 | __IPYTHON__ = ipapi.get(True,False) |
|
117 | __IPYTHON__ = ipapi.get(True,False) | |
118 | BdbQuit_excepthook.excepthook_ori = sys.excepthook |
|
118 | BdbQuit_excepthook.excepthook_ori = sys.excepthook | |
119 | sys.excepthook = BdbQuit_excepthook |
|
119 | sys.excepthook = BdbQuit_excepthook | |
120 | def_colors = 'NoColor' |
|
120 | def_colors = 'NoColor' | |
121 | try: |
|
121 | try: | |
122 | # Limited tab completion support |
|
122 | # Limited tab completion support | |
123 | import rlcompleter,readline |
|
123 | import rlcompleter,readline | |
124 | readline.parse_and_bind('tab: complete') |
|
124 | readline.parse_and_bind('tab: complete') | |
125 | except ImportError: |
|
125 | except ImportError: | |
126 | pass |
|
126 | pass | |
127 | else: |
|
127 | else: | |
128 | # In ipython, we use its custom exception handler mechanism |
|
128 | # In ipython, we use its custom exception handler mechanism | |
129 | ip = ipapi.get() |
|
129 | ip = ipapi.get() | |
130 | def_colors = ip.options.colors |
|
130 | def_colors = ip.options.colors | |
131 | ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook) |
|
131 | ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook) | |
132 |
|
132 | |||
133 | if colors is None: |
|
133 | if colors is None: | |
134 | colors = def_colors |
|
134 | colors = def_colors | |
135 | self.debugger = Pdb(colors) |
|
135 | self.debugger = Pdb(colors) | |
136 |
|
136 | |||
137 | def __call__(self): |
|
137 | def __call__(self): | |
138 | """Starts an interactive debugger at the point where called. |
|
138 | """Starts an interactive debugger at the point where called. | |
139 |
|
139 | |||
140 | This is similar to the pdb.set_trace() function from the std lib, but |
|
140 | This is similar to the pdb.set_trace() function from the std lib, but | |
141 | using IPython's enhanced debugger.""" |
|
141 | using IPython's enhanced debugger.""" | |
142 |
|
142 | |||
143 | self.debugger.set_trace(sys._getframe().f_back) |
|
143 | self.debugger.set_trace(sys._getframe().f_back) | |
144 |
|
144 | |||
145 | def decorate_fn_with_doc(new_fn, old_fn, additional_text=""): |
|
145 | 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 |
|
146 | """Make new_fn have old_fn's doc string. This is particularly useful | |
147 | for the do_... commands that hook into the help system. |
|
147 | for the do_... commands that hook into the help system. | |
148 | Adapted from from a comp.lang.python posting |
|
148 | Adapted from from a comp.lang.python posting | |
149 | by Duncan Booth.""" |
|
149 | by Duncan Booth.""" | |
150 | def wrapper(*args, **kw): |
|
150 | def wrapper(*args, **kw): | |
151 | return new_fn(*args, **kw) |
|
151 | return new_fn(*args, **kw) | |
152 | if old_fn.__doc__: |
|
152 | if old_fn.__doc__: | |
153 | wrapper.__doc__ = old_fn.__doc__ + additional_text |
|
153 | wrapper.__doc__ = old_fn.__doc__ + additional_text | |
154 | return wrapper |
|
154 | return wrapper | |
155 |
|
155 | |||
156 | def _file_lines(fname): |
|
156 | def _file_lines(fname): | |
157 | """Return the contents of a named file as a list of lines. |
|
157 | """Return the contents of a named file as a list of lines. | |
158 |
|
158 | |||
159 | This function never raises an IOError exception: if the file can't be |
|
159 | This function never raises an IOError exception: if the file can't be | |
160 | read, it simply returns an empty list.""" |
|
160 | read, it simply returns an empty list.""" | |
161 |
|
161 | |||
162 | try: |
|
162 | try: | |
163 | outfile = open(fname) |
|
163 | outfile = open(fname) | |
164 | except IOError: |
|
164 | except IOError: | |
165 | return [] |
|
165 | return [] | |
166 | else: |
|
166 | else: | |
167 | out = outfile.readlines() |
|
167 | out = outfile.readlines() | |
168 | outfile.close() |
|
168 | outfile.close() | |
169 | return out |
|
169 | return out | |
170 |
|
170 | |||
171 | class Pdb(OldPdb): |
|
171 | class Pdb(OldPdb): | |
172 | """Modified Pdb class, does not load readline.""" |
|
172 | """Modified Pdb class, does not load readline.""" | |
173 |
|
173 | |||
174 | if sys.version[:3] >= '2.5' or has_pydb: |
|
174 | if sys.version[:3] >= '2.5' or has_pydb: | |
175 | def __init__(self,color_scheme='NoColor',completekey=None, |
|
175 | def __init__(self,color_scheme='NoColor',completekey=None, | |
176 | stdin=None, stdout=None): |
|
176 | stdin=None, stdout=None): | |
177 |
|
177 | |||
178 | # Parent constructor: |
|
178 | # Parent constructor: | |
179 | if has_pydb and completekey is None: |
|
179 | if has_pydb and completekey is None: | |
180 | OldPdb.__init__(self,stdin=stdin,stdout=Term.cout) |
|
180 | OldPdb.__init__(self,stdin=stdin,stdout=Term.cout) | |
181 | else: |
|
181 | else: | |
182 | OldPdb.__init__(self,completekey,stdin,stdout) |
|
182 | OldPdb.__init__(self,completekey,stdin,stdout) | |
183 |
|
183 | |||
184 | self.prompt = prompt # The default prompt is '(Pdb)' |
|
184 | self.prompt = prompt # The default prompt is '(Pdb)' | |
185 |
|
185 | |||
186 | # IPython changes... |
|
186 | # IPython changes... | |
187 | self.is_pydb = has_pydb |
|
187 | self.is_pydb = has_pydb | |
188 |
|
188 | |||
189 | if self.is_pydb: |
|
189 | if self.is_pydb: | |
190 |
|
190 | |||
191 | # iplib.py's ipalias seems to want pdb's checkline |
|
191 | # iplib.py's ipalias seems to want pdb's checkline | |
192 | # which located in pydb.fn |
|
192 | # which located in pydb.fn | |
193 | import pydb.fns |
|
193 | import pydb.fns | |
194 | self.checkline = lambda filename, lineno: \ |
|
194 | self.checkline = lambda filename, lineno: \ | |
195 | pydb.fns.checkline(self, filename, lineno) |
|
195 | pydb.fns.checkline(self, filename, lineno) | |
196 |
|
196 | |||
197 | self.curframe = None |
|
197 | self.curframe = None | |
198 | self.do_restart = self.new_do_restart |
|
198 | self.do_restart = self.new_do_restart | |
199 |
|
199 | |||
200 | self.old_all_completions = __IPYTHON__.Completer.all_completions |
|
200 | self.old_all_completions = __IPYTHON__.Completer.all_completions | |
201 | __IPYTHON__.Completer.all_completions=self.all_completions |
|
201 | __IPYTHON__.Completer.all_completions=self.all_completions | |
202 |
|
202 | |||
203 | self.do_list = decorate_fn_with_doc(self.list_command_pydb, |
|
203 | self.do_list = decorate_fn_with_doc(self.list_command_pydb, | |
204 | OldPdb.do_list) |
|
204 | OldPdb.do_list) | |
205 | self.do_l = self.do_list |
|
205 | self.do_l = self.do_list | |
206 | self.do_frame = decorate_fn_with_doc(self.new_do_frame, |
|
206 | self.do_frame = decorate_fn_with_doc(self.new_do_frame, | |
207 | OldPdb.do_frame) |
|
207 | OldPdb.do_frame) | |
208 |
|
208 | |||
209 | self.aliases = {} |
|
209 | self.aliases = {} | |
210 |
|
210 | |||
211 | # Create color table: we copy the default one from the traceback |
|
211 | # Create color table: we copy the default one from the traceback | |
212 | # module and add a few attributes needed for debugging |
|
212 | # module and add a few attributes needed for debugging | |
213 | self.color_scheme_table = ExceptionColors.copy() |
|
213 | self.color_scheme_table = ExceptionColors.copy() | |
214 |
|
214 | |||
215 | # shorthands |
|
215 | # shorthands | |
216 | C = ColorANSI.TermColors |
|
216 | C = ColorANSI.TermColors | |
217 | cst = self.color_scheme_table |
|
217 | cst = self.color_scheme_table | |
218 |
|
218 | |||
219 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor |
|
219 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor | |
220 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor |
|
220 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor | |
221 |
|
221 | |||
222 | cst['Linux'].colors.breakpoint_enabled = C.LightRed |
|
222 | cst['Linux'].colors.breakpoint_enabled = C.LightRed | |
223 | cst['Linux'].colors.breakpoint_disabled = C.Red |
|
223 | cst['Linux'].colors.breakpoint_disabled = C.Red | |
224 |
|
224 | |||
225 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed |
|
225 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed | |
226 | cst['LightBG'].colors.breakpoint_disabled = C.Red |
|
226 | cst['LightBG'].colors.breakpoint_disabled = C.Red | |
227 |
|
227 | |||
228 | self.set_colors(color_scheme) |
|
228 | self.set_colors(color_scheme) | |
229 |
|
229 | |||
230 | # Add a python parser so we can syntax highlight source while |
|
230 | # Add a python parser so we can syntax highlight source while | |
231 | # debugging. |
|
231 | # debugging. | |
232 | self.parser = PyColorize.Parser() |
|
232 | self.parser = PyColorize.Parser() | |
233 |
|
233 | |||
234 |
|
234 | |||
235 | else: |
|
235 | else: | |
236 | # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor, |
|
236 | # 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 |
|
237 | # because it binds readline and breaks tab-completion. This means we | |
238 | # have to COPY the constructor here. |
|
238 | # have to COPY the constructor here. | |
239 | def __init__(self,color_scheme='NoColor'): |
|
239 | def __init__(self,color_scheme='NoColor'): | |
240 | bdb.Bdb.__init__(self) |
|
240 | bdb.Bdb.__init__(self) | |
241 | cmd.Cmd.__init__(self,completekey=None) # don't load readline |
|
241 | cmd.Cmd.__init__(self,completekey=None) # don't load readline | |
242 | self.prompt = 'ipdb> ' # The default prompt is '(Pdb)' |
|
242 | self.prompt = 'ipdb> ' # The default prompt is '(Pdb)' | |
243 | self.aliases = {} |
|
243 | self.aliases = {} | |
244 |
|
244 | |||
245 | # These two lines are part of the py2.4 constructor, let's put them |
|
245 | # 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. |
|
246 | # unconditionally here as they won't cause any problems in 2.3. | |
247 | self.mainpyfile = '' |
|
247 | self.mainpyfile = '' | |
248 | self._wait_for_mainpyfile = 0 |
|
248 | self._wait_for_mainpyfile = 0 | |
249 |
|
249 | |||
250 | # Read $HOME/.pdbrc and ./.pdbrc |
|
250 | # Read $HOME/.pdbrc and ./.pdbrc | |
251 | try: |
|
251 | try: | |
252 | self.rcLines = _file_lines(os.path.join(os.environ['HOME'], |
|
252 | self.rcLines = _file_lines(os.path.join(os.environ['HOME'], | |
253 | ".pdbrc")) |
|
253 | ".pdbrc")) | |
254 | except KeyError: |
|
254 | except KeyError: | |
255 | self.rcLines = [] |
|
255 | self.rcLines = [] | |
256 | self.rcLines.extend(_file_lines(".pdbrc")) |
|
256 | self.rcLines.extend(_file_lines(".pdbrc")) | |
257 |
|
257 | |||
258 | # Create color table: we copy the default one from the traceback |
|
258 | # Create color table: we copy the default one from the traceback | |
259 | # module and add a few attributes needed for debugging |
|
259 | # module and add a few attributes needed for debugging | |
260 | ExceptionColors.set_active_scheme(color_scheme) |
|
260 | ExceptionColors.set_active_scheme(color_scheme) | |
261 | self.color_scheme_table = ExceptionColors.copy() |
|
261 | self.color_scheme_table = ExceptionColors.copy() | |
262 |
|
262 | |||
263 | # shorthands |
|
263 | # shorthands | |
264 | C = ColorANSI.TermColors |
|
264 | C = ColorANSI.TermColors | |
265 | cst = self.color_scheme_table |
|
265 | cst = self.color_scheme_table | |
266 |
|
266 | |||
267 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor |
|
267 | cst['NoColor'].colors.breakpoint_enabled = C.NoColor | |
268 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor |
|
268 | cst['NoColor'].colors.breakpoint_disabled = C.NoColor | |
269 |
|
269 | |||
270 | cst['Linux'].colors.breakpoint_enabled = C.LightRed |
|
270 | cst['Linux'].colors.breakpoint_enabled = C.LightRed | |
271 | cst['Linux'].colors.breakpoint_disabled = C.Red |
|
271 | cst['Linux'].colors.breakpoint_disabled = C.Red | |
272 |
|
272 | |||
273 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed |
|
273 | cst['LightBG'].colors.breakpoint_enabled = C.LightRed | |
274 | cst['LightBG'].colors.breakpoint_disabled = C.Red |
|
274 | cst['LightBG'].colors.breakpoint_disabled = C.Red | |
275 |
|
275 | |||
276 | self.set_colors(color_scheme) |
|
276 | self.set_colors(color_scheme) | |
277 |
|
277 | |||
278 | # Add a python parser so we can syntax highlight source while |
|
278 | # Add a python parser so we can syntax highlight source while | |
279 | # debugging. |
|
279 | # debugging. | |
280 | self.parser = PyColorize.Parser() |
|
280 | self.parser = PyColorize.Parser() | |
281 |
|
281 | |||
282 | def set_colors(self, scheme): |
|
282 | def set_colors(self, scheme): | |
283 | """Shorthand access to the color table scheme selector method.""" |
|
283 | """Shorthand access to the color table scheme selector method.""" | |
284 | self.color_scheme_table.set_active_scheme(scheme) |
|
284 | self.color_scheme_table.set_active_scheme(scheme) | |
285 |
|
285 | |||
286 | def interaction(self, frame, traceback): |
|
286 | def interaction(self, frame, traceback): | |
287 | __IPYTHON__.set_completer_frame(frame) |
|
287 | __IPYTHON__.set_completer_frame(frame) | |
288 | OldPdb.interaction(self, frame, traceback) |
|
288 | OldPdb.interaction(self, frame, traceback) | |
289 |
|
289 | |||
290 | def new_do_up(self, arg): |
|
290 | def new_do_up(self, arg): | |
291 | OldPdb.do_up(self, arg) |
|
291 | OldPdb.do_up(self, arg) | |
292 | __IPYTHON__.set_completer_frame(self.curframe) |
|
292 | __IPYTHON__.set_completer_frame(self.curframe) | |
293 | do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up) |
|
293 | do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up) | |
294 |
|
294 | |||
295 | def new_do_down(self, arg): |
|
295 | def new_do_down(self, arg): | |
296 | OldPdb.do_down(self, arg) |
|
296 | OldPdb.do_down(self, arg) | |
297 | __IPYTHON__.set_completer_frame(self.curframe) |
|
297 | __IPYTHON__.set_completer_frame(self.curframe) | |
298 |
|
298 | |||
299 | do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down) |
|
299 | do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down) | |
300 |
|
300 | |||
301 | def new_do_frame(self, arg): |
|
301 | def new_do_frame(self, arg): | |
302 | OldPdb.do_frame(self, arg) |
|
302 | OldPdb.do_frame(self, arg) | |
303 | __IPYTHON__.set_completer_frame(self.curframe) |
|
303 | __IPYTHON__.set_completer_frame(self.curframe) | |
304 |
|
304 | |||
305 | def new_do_quit(self, arg): |
|
305 | def new_do_quit(self, arg): | |
306 |
|
306 | |||
307 | if hasattr(self, 'old_all_completions'): |
|
307 | if hasattr(self, 'old_all_completions'): | |
308 | __IPYTHON__.Completer.all_completions=self.old_all_completions |
|
308 | __IPYTHON__.Completer.all_completions=self.old_all_completions | |
309 |
|
309 | |||
310 |
|
310 | |||
311 | return OldPdb.do_quit(self, arg) |
|
311 | return OldPdb.do_quit(self, arg) | |
312 |
|
312 | |||
313 | do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit) |
|
313 | do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit) | |
314 |
|
314 | |||
315 | def new_do_restart(self, arg): |
|
315 | def new_do_restart(self, arg): | |
316 | """Restart command. In the context of ipython this is exactly the same |
|
316 | """Restart command. In the context of ipython this is exactly the same | |
317 | thing as 'quit'.""" |
|
317 | thing as 'quit'.""" | |
318 | self.msg("Restart doesn't make sense here. Using 'quit' instead.") |
|
318 | self.msg("Restart doesn't make sense here. Using 'quit' instead.") | |
319 | return self.do_quit(arg) |
|
319 | return self.do_quit(arg) | |
320 |
|
320 | |||
321 | def postloop(self): |
|
321 | def postloop(self): | |
322 | __IPYTHON__.set_completer_frame(None) |
|
322 | __IPYTHON__.set_completer_frame(None) | |
323 |
|
323 | |||
324 | def print_stack_trace(self): |
|
324 | def print_stack_trace(self): | |
325 | try: |
|
325 | try: | |
326 | for frame_lineno in self.stack: |
|
326 | for frame_lineno in self.stack: | |
327 | self.print_stack_entry(frame_lineno, context = 5) |
|
327 | self.print_stack_entry(frame_lineno, context = 5) | |
328 | except KeyboardInterrupt: |
|
328 | except KeyboardInterrupt: | |
329 | pass |
|
329 | pass | |
330 |
|
330 | |||
331 | def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ', |
|
331 | def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ', | |
332 | context = 3): |
|
332 | context = 3): | |
333 | #frame, lineno = frame_lineno |
|
333 | #frame, lineno = frame_lineno | |
334 | print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) |
|
334 | print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) | |
335 |
|
335 | |||
336 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): |
|
336 | def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3): | |
337 | import linecache, repr |
|
337 | import linecache, repr | |
338 |
|
338 | |||
339 | ret = [] |
|
339 | ret = [] | |
340 |
|
340 | |||
341 | Colors = self.color_scheme_table.active_colors |
|
341 | Colors = self.color_scheme_table.active_colors | |
342 | ColorsNormal = Colors.Normal |
|
342 | ColorsNormal = Colors.Normal | |
343 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) |
|
343 | tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) | |
344 | tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) |
|
344 | tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) | |
345 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) |
|
345 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
346 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, |
|
346 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, | |
347 | ColorsNormal) |
|
347 | ColorsNormal) | |
348 |
|
348 | |||
349 | frame, lineno = frame_lineno |
|
349 | frame, lineno = frame_lineno | |
350 |
|
350 | |||
351 | return_value = '' |
|
351 | return_value = '' | |
352 | if '__return__' in frame.f_locals: |
|
352 | if '__return__' in frame.f_locals: | |
353 | rv = frame.f_locals['__return__'] |
|
353 | rv = frame.f_locals['__return__'] | |
354 | #return_value += '->' |
|
354 | #return_value += '->' | |
355 | return_value += repr.repr(rv) + '\n' |
|
355 | return_value += repr.repr(rv) + '\n' | |
356 | ret.append(return_value) |
|
356 | ret.append(return_value) | |
357 |
|
357 | |||
358 | #s = filename + '(' + `lineno` + ')' |
|
358 | #s = filename + '(' + `lineno` + ')' | |
359 | filename = self.canonic(frame.f_code.co_filename) |
|
359 | filename = self.canonic(frame.f_code.co_filename) | |
360 | link = tpl_link % filename |
|
360 | link = tpl_link % filename | |
361 |
|
361 | |||
362 | if frame.f_code.co_name: |
|
362 | if frame.f_code.co_name: | |
363 | func = frame.f_code.co_name |
|
363 | func = frame.f_code.co_name | |
364 | else: |
|
364 | else: | |
365 | func = "<lambda>" |
|
365 | func = "<lambda>" | |
366 |
|
366 | |||
367 | call = '' |
|
367 | call = '' | |
368 | if func != '?': |
|
368 | if func != '?': | |
369 | if '__args__' in frame.f_locals: |
|
369 | if '__args__' in frame.f_locals: | |
370 | args = repr.repr(frame.f_locals['__args__']) |
|
370 | args = repr.repr(frame.f_locals['__args__']) | |
371 | else: |
|
371 | else: | |
372 | args = '()' |
|
372 | args = '()' | |
373 | call = tpl_call % (func, args) |
|
373 | call = tpl_call % (func, args) | |
374 |
|
374 | |||
375 | # The level info should be generated in the same format pdb uses, to |
|
375 | # The level info should be generated in the same format pdb uses, to | |
376 | # avoid breaking the pdbtrack functionality of python-mode in *emacs. |
|
376 | # avoid breaking the pdbtrack functionality of python-mode in *emacs. | |
377 | if frame is self.curframe: |
|
377 | if frame is self.curframe: | |
378 | ret.append('> ') |
|
378 | ret.append('> ') | |
379 | else: |
|
379 | else: | |
380 | ret.append(' ') |
|
380 | ret.append(' ') | |
381 | ret.append('%s(%s)%s\n' % (link,lineno,call)) |
|
381 | ret.append('%s(%s)%s\n' % (link,lineno,call)) | |
382 |
|
382 | |||
383 | start = lineno - 1 - context//2 |
|
383 | start = lineno - 1 - context//2 | |
384 | lines = linecache.getlines(filename) |
|
384 | lines = linecache.getlines(filename) | |
385 | start = max(start, 0) |
|
385 | start = max(start, 0) | |
386 | start = min(start, len(lines) - context) |
|
386 | start = min(start, len(lines) - context) | |
387 | lines = lines[start : start + context] |
|
387 | lines = lines[start : start + context] | |
388 |
|
388 | |||
389 | for i,line in enumerate(lines): |
|
389 | for i,line in enumerate(lines): | |
390 | show_arrow = (start + 1 + i == lineno) |
|
390 | show_arrow = (start + 1 + i == lineno) | |
391 | linetpl = (frame is self.curframe or show_arrow) \ |
|
391 | linetpl = (frame is self.curframe or show_arrow) \ | |
392 | and tpl_line_em \ |
|
392 | and tpl_line_em \ | |
393 | or tpl_line |
|
393 | or tpl_line | |
394 | ret.append(self.__format_line(linetpl, filename, |
|
394 | ret.append(self.__format_line(linetpl, filename, | |
395 | start + 1 + i, line, |
|
395 | start + 1 + i, line, | |
396 | arrow = show_arrow) ) |
|
396 | arrow = show_arrow) ) | |
397 |
|
397 | |||
398 | return ''.join(ret) |
|
398 | return ''.join(ret) | |
399 |
|
399 | |||
400 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): |
|
400 | def __format_line(self, tpl_line, filename, lineno, line, arrow = False): | |
401 | bp_mark = "" |
|
401 | bp_mark = "" | |
402 | bp_mark_color = "" |
|
402 | bp_mark_color = "" | |
403 |
|
403 | |||
404 | scheme = self.color_scheme_table.active_scheme_name |
|
404 | scheme = self.color_scheme_table.active_scheme_name | |
405 | new_line, err = self.parser.format2(line, 'str', scheme) |
|
405 | new_line, err = self.parser.format2(line, 'str', scheme) | |
406 | if not err: line = new_line |
|
406 | if not err: line = new_line | |
407 |
|
407 | |||
408 | bp = None |
|
408 | bp = None | |
409 | if lineno in self.get_file_breaks(filename): |
|
409 | if lineno in self.get_file_breaks(filename): | |
410 | bps = self.get_breaks(filename, lineno) |
|
410 | bps = self.get_breaks(filename, lineno) | |
411 | bp = bps[-1] |
|
411 | bp = bps[-1] | |
412 |
|
412 | |||
413 | if bp: |
|
413 | if bp: | |
414 | Colors = self.color_scheme_table.active_colors |
|
414 | Colors = self.color_scheme_table.active_colors | |
415 | bp_mark = str(bp.number) |
|
415 | bp_mark = str(bp.number) | |
416 | bp_mark_color = Colors.breakpoint_enabled |
|
416 | bp_mark_color = Colors.breakpoint_enabled | |
417 | if not bp.enabled: |
|
417 | if not bp.enabled: | |
418 | bp_mark_color = Colors.breakpoint_disabled |
|
418 | bp_mark_color = Colors.breakpoint_disabled | |
419 |
|
419 | |||
420 | numbers_width = 7 |
|
420 | numbers_width = 7 | |
421 | if arrow: |
|
421 | if arrow: | |
422 | # This is the line with the error |
|
422 | # This is the line with the error | |
423 | pad = numbers_width - len(str(lineno)) - len(bp_mark) |
|
423 | pad = numbers_width - len(str(lineno)) - len(bp_mark) | |
424 | if pad >= 3: |
|
424 | if pad >= 3: | |
425 | marker = '-'*(pad-3) + '-> ' |
|
425 | marker = '-'*(pad-3) + '-> ' | |
426 | elif pad == 2: |
|
426 | elif pad == 2: | |
427 | marker = '> ' |
|
427 | marker = '> ' | |
428 | elif pad == 1: |
|
428 | elif pad == 1: | |
429 | marker = '>' |
|
429 | marker = '>' | |
430 | else: |
|
430 | else: | |
431 | marker = '' |
|
431 | marker = '' | |
432 | num = '%s%s' % (marker, str(lineno)) |
|
432 | num = '%s%s' % (marker, str(lineno)) | |
433 | line = tpl_line % (bp_mark_color + bp_mark, num, line) |
|
433 | line = tpl_line % (bp_mark_color + bp_mark, num, line) | |
434 | else: |
|
434 | else: | |
435 | num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) |
|
435 | num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) | |
436 | line = tpl_line % (bp_mark_color + bp_mark, num, line) |
|
436 | line = tpl_line % (bp_mark_color + bp_mark, num, line) | |
437 |
|
437 | |||
438 | return line |
|
438 | return line | |
439 |
|
439 | |||
440 | def list_command_pydb(self, arg): |
|
440 | def list_command_pydb(self, arg): | |
441 | """List command to use if we have a newer pydb installed""" |
|
441 | """List command to use if we have a newer pydb installed""" | |
442 | filename, first, last = OldPdb.parse_list_cmd(self, arg) |
|
442 | filename, first, last = OldPdb.parse_list_cmd(self, arg) | |
443 | if filename is not None: |
|
443 | if filename is not None: | |
444 | self.print_list_lines(filename, first, last) |
|
444 | self.print_list_lines(filename, first, last) | |
445 |
|
445 | |||
446 | def print_list_lines(self, filename, first, last): |
|
446 | def print_list_lines(self, filename, first, last): | |
447 | """The printing (as opposed to the parsing part of a 'list' |
|
447 | """The printing (as opposed to the parsing part of a 'list' | |
448 | command.""" |
|
448 | command.""" | |
449 | try: |
|
449 | try: | |
450 | Colors = self.color_scheme_table.active_colors |
|
450 | Colors = self.color_scheme_table.active_colors | |
451 | ColorsNormal = Colors.Normal |
|
451 | ColorsNormal = Colors.Normal | |
452 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) |
|
452 | tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) | |
453 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) |
|
453 | tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) | |
454 | src = [] |
|
454 | src = [] | |
455 | for lineno in range(first, last+1): |
|
455 | for lineno in range(first, last+1): | |
456 | line = linecache.getline(filename, lineno) |
|
456 | line = linecache.getline(filename, lineno) | |
457 | if not line: |
|
457 | if not line: | |
458 | break |
|
458 | break | |
459 |
|
459 | |||
460 | if lineno == self.curframe.f_lineno: |
|
460 | if lineno == self.curframe.f_lineno: | |
461 | line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True) |
|
461 | line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True) | |
462 | else: |
|
462 | else: | |
463 | line = self.__format_line(tpl_line, filename, lineno, line, arrow = False) |
|
463 | line = self.__format_line(tpl_line, filename, lineno, line, arrow = False) | |
464 |
|
464 | |||
465 | src.append(line) |
|
465 | src.append(line) | |
466 | self.lineno = lineno |
|
466 | self.lineno = lineno | |
467 |
|
467 | |||
468 | print >>Term.cout, ''.join(src) |
|
468 | print >>Term.cout, ''.join(src) | |
469 |
|
469 | |||
470 | except KeyboardInterrupt: |
|
470 | except KeyboardInterrupt: | |
471 | pass |
|
471 | pass | |
472 |
|
472 | |||
473 | def do_list(self, arg): |
|
473 | def do_list(self, arg): | |
474 | self.lastcmd = 'list' |
|
474 | self.lastcmd = 'list' | |
475 | last = None |
|
475 | last = None | |
476 | if arg: |
|
476 | if arg: | |
477 | try: |
|
477 | try: | |
478 | x = eval(arg, {}, {}) |
|
478 | x = eval(arg, {}, {}) | |
479 | if type(x) == type(()): |
|
479 | if type(x) == type(()): | |
480 | first, last = x |
|
480 | first, last = x | |
481 | first = int(first) |
|
481 | first = int(first) | |
482 | last = int(last) |
|
482 | last = int(last) | |
483 | if last < first: |
|
483 | if last < first: | |
484 | # Assume it's a count |
|
484 | # Assume it's a count | |
485 | last = first + last |
|
485 | last = first + last | |
486 | else: |
|
486 | else: | |
487 | first = max(1, int(x) - 5) |
|
487 | first = max(1, int(x) - 5) | |
488 | except: |
|
488 | except: | |
489 | print '*** Error in argument:', `arg` |
|
489 | print '*** Error in argument:', `arg` | |
490 | return |
|
490 | return | |
491 | elif self.lineno is None: |
|
491 | elif self.lineno is None: | |
492 | first = max(1, self.curframe.f_lineno - 5) |
|
492 | first = max(1, self.curframe.f_lineno - 5) | |
493 | else: |
|
493 | else: | |
494 | first = self.lineno + 1 |
|
494 | first = self.lineno + 1 | |
495 | if last is None: |
|
495 | if last is None: | |
496 | last = first + 10 |
|
496 | last = first + 10 | |
497 | self.print_list_lines(self.curframe.f_code.co_filename, first, last) |
|
497 | self.print_list_lines(self.curframe.f_code.co_filename, first, last) | |
498 |
|
498 | |||
499 | do_l = do_list |
|
499 | do_l = do_list | |
500 |
|
500 | |||
501 | def do_pdef(self, arg): |
|
501 | def do_pdef(self, arg): | |
502 | """The debugger interface to magic_pdef""" |
|
502 | """The debugger interface to magic_pdef""" | |
503 | namespaces = [('Locals', self.curframe.f_locals), |
|
503 | namespaces = [('Locals', self.curframe.f_locals), | |
504 | ('Globals', self.curframe.f_globals)] |
|
504 | ('Globals', self.curframe.f_globals)] | |
505 | __IPYTHON__.magic_pdef(arg, namespaces=namespaces) |
|
505 | __IPYTHON__.magic_pdef(arg, namespaces=namespaces) | |
506 |
|
506 | |||
507 | def do_pdoc(self, arg): |
|
507 | def do_pdoc(self, arg): | |
508 | """The debugger interface to magic_pdoc""" |
|
508 | """The debugger interface to magic_pdoc""" | |
509 | namespaces = [('Locals', self.curframe.f_locals), |
|
509 | namespaces = [('Locals', self.curframe.f_locals), | |
510 | ('Globals', self.curframe.f_globals)] |
|
510 | ('Globals', self.curframe.f_globals)] | |
511 | __IPYTHON__.magic_pdoc(arg, namespaces=namespaces) |
|
511 | __IPYTHON__.magic_pdoc(arg, namespaces=namespaces) | |
512 |
|
512 | |||
513 | def do_pinfo(self, arg): |
|
513 | def do_pinfo(self, arg): | |
514 | """The debugger equivalant of ?obj""" |
|
514 | """The debugger equivalant of ?obj""" | |
515 | namespaces = [('Locals', self.curframe.f_locals), |
|
515 | namespaces = [('Locals', self.curframe.f_locals), | |
516 | ('Globals', self.curframe.f_globals)] |
|
516 | ('Globals', self.curframe.f_globals)] | |
517 | __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces) |
|
517 | __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces) |
This diff has been collapsed as it changes many lines, (518 lines changed) Show them Hide them | |||||
@@ -1,259 +1,259 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 $""" |
|
8 | $Id: InterpreterExec.py 2724 2007-09-07 08:05:38Z fperez $""" | |
9 |
|
9 | |||
10 | #***************************************************************************** |
|
10 | #***************************************************************************** | |
11 | # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl> |
|
11 | # Copyright (C) 2004 W.J. van der Laan <gnufnork@hetdigitalegat.nl> | |
12 | # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu> |
|
12 | # Copyright (C) 2004-2006 Fernando Perez <fperez@colorado.edu> | |
13 | # |
|
13 | # | |
14 | # Distributed under the terms of the BSD License. The full license is in |
|
14 | # Distributed under the terms of the BSD License. The full license is in | |
15 | # the file COPYING, distributed as part of this software. |
|
15 | # the file COPYING, distributed as part of this software. | |
16 | #***************************************************************************** |
|
16 | #***************************************************************************** | |
17 |
|
17 | |||
18 | from IPython import Release |
|
18 | from IPython import Release | |
19 | __author__ = 'W.J. van der Laan <gnufnork@hetdigitalegat.nl>, '\ |
|
19 | __author__ = 'W.J. van der Laan <gnufnork@hetdigitalegat.nl>, '\ | |
20 | '%s <%s>' % Release.authors['Fernando'] |
|
20 | '%s <%s>' % Release.authors['Fernando'] | |
21 | __license__ = Release.license |
|
21 | __license__ = Release.license | |
22 |
|
22 | |||
23 | # TODO: deprecated |
|
23 | # TODO: deprecated | |
24 | def prefilter_shell(self,line,continuation): |
|
24 | def prefilter_shell(self,line,continuation): | |
25 | """Alternate prefilter, modified for shell-like functionality. |
|
25 | """Alternate prefilter, modified for shell-like functionality. | |
26 |
|
26 | |||
27 | - Execute all lines beginning with '~', '/' or '.' |
|
27 | - Execute all lines beginning with '~', '/' or '.' | |
28 | - $var=cmd <=> %sc var=cmd |
|
28 | - $var=cmd <=> %sc var=cmd | |
29 | - $$var=cmd <=> %sc -l var=cmd |
|
29 | - $$var=cmd <=> %sc -l var=cmd | |
30 | """ |
|
30 | """ | |
31 |
|
31 | |||
32 | if line: |
|
32 | if line: | |
33 | l0 = line[0] |
|
33 | l0 = line[0] | |
34 | if l0 in '~/.': |
|
34 | if l0 in '~/.': | |
35 | return self._prefilter("!%s"%line,continuation) |
|
35 | return self._prefilter("!%s"%line,continuation) | |
36 | elif l0=='$': |
|
36 | elif l0=='$': | |
37 | lrest = line[1:] |
|
37 | lrest = line[1:] | |
38 | if lrest.startswith('$'): |
|
38 | if lrest.startswith('$'): | |
39 | # $$var=cmd <=> %sc -l var=cmd |
|
39 | # $$var=cmd <=> %sc -l var=cmd | |
40 | return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]), |
|
40 | return self._prefilter("%ssc -l %s" % (self.ESC_MAGIC,lrest[1:]), | |
41 | continuation) |
|
41 | continuation) | |
42 | else: |
|
42 | else: | |
43 | # $var=cmd <=> %sc var=cmd |
|
43 | # $var=cmd <=> %sc var=cmd | |
44 | return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest), |
|
44 | return self._prefilter("%ssc %s" % (self.ESC_MAGIC,lrest), | |
45 | continuation) |
|
45 | continuation) | |
46 | else: |
|
46 | else: | |
47 | return self._prefilter(line,continuation) |
|
47 | return self._prefilter(line,continuation) | |
48 | else: |
|
48 | else: | |
49 | return self._prefilter(line,continuation) |
|
49 | return self._prefilter(line,continuation) | |
50 |
|
50 | |||
51 | # Rebind this to be the new IPython prefilter: |
|
51 | # Rebind this to be the new IPython prefilter: | |
52 | from IPython.iplib import InteractiveShell |
|
52 | from IPython.iplib import InteractiveShell | |
53 | InteractiveShell.prefilter = prefilter_shell |
|
53 | InteractiveShell.prefilter = prefilter_shell | |
54 | # Clean up the namespace. |
|
54 | # Clean up the namespace. | |
55 | del InteractiveShell,prefilter_shell |
|
55 | del InteractiveShell,prefilter_shell | |
56 |
|
56 | |||
57 | # Provide pysh and further shell-oriented services |
|
57 | # Provide pysh and further shell-oriented services | |
58 | import os,sys,shutil |
|
58 | import os,sys,shutil | |
59 | from IPython.genutils import system,shell,getoutput,getoutputerror |
|
59 | from IPython.genutils import system,shell,getoutput,getoutputerror | |
60 |
|
60 | |||
61 | # Short aliases for getting shell output as a string and a list |
|
61 | # Short aliases for getting shell output as a string and a list | |
62 | sout = getoutput |
|
62 | sout = getoutput | |
63 | lout = lambda cmd: getoutput(cmd,split=1) |
|
63 | lout = lambda cmd: getoutput(cmd,split=1) | |
64 |
|
64 | |||
65 | # Empty function, meant as a docstring holder so help(pysh) works. |
|
65 | # Empty function, meant as a docstring holder so help(pysh) works. | |
66 | def pysh(): |
|
66 | def pysh(): | |
67 | """Pysh is a set of modules and extensions to IPython which make shell-like |
|
67 | """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 |
|
68 | 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! |
|
69 | full-blown shell, so don't try to make it your /etc/passwd entry! | |
70 |
|
70 | |||
71 | In particular, it has no job control, so if you type Ctrl-Z (under Unix), |
|
71 | 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. |
|
72 | you'll suspend pysh itself, not the process you just started. | |
73 |
|
73 | |||
74 | Since pysh is really nothing but a customized IPython, you should |
|
74 | Since pysh is really nothing but a customized IPython, you should | |
75 | familiarize yourself with IPython's features. This brief help mainly |
|
75 | familiarize yourself with IPython's features. This brief help mainly | |
76 | documents areas in which pysh differs from the normal IPython. |
|
76 | documents areas in which pysh differs from the normal IPython. | |
77 |
|
77 | |||
78 | ALIASES |
|
78 | ALIASES | |
79 | ------- |
|
79 | ------- | |
80 | All of your $PATH has been loaded as IPython aliases, so you should be |
|
80 | 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? |
|
81 | able to type any normal system command and have it executed. See %alias? | |
82 | and %unalias? for details on the alias facilities. |
|
82 | and %unalias? for details on the alias facilities. | |
83 |
|
83 | |||
84 | SPECIAL SYNTAX |
|
84 | SPECIAL SYNTAX | |
85 | -------------- |
|
85 | -------------- | |
86 | Any lines which begin with '~', '/' and '.' will be executed as shell |
|
86 | Any lines which begin with '~', '/' and '.' will be executed as shell | |
87 | commands instead of as Python code. The special escapes below are also |
|
87 | 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 |
|
88 | recognized. !cmd is valid in single or multi-line input, all others are | |
89 | only valid in single-line input: |
|
89 | only valid in single-line input: | |
90 |
|
90 | |||
91 | !cmd - pass 'cmd' directly to the shell |
|
91 | !cmd - pass 'cmd' directly to the shell | |
92 | !!cmd - execute 'cmd' and return output as a list (split on '\\n') |
|
92 | !!cmd - execute 'cmd' and return output as a list (split on '\\n') | |
93 | $var=cmd - capture output of cmd into var, as a string |
|
93 | $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') |
|
94 | $$var=cmd - capture output of cmd into var, as a list (split on '\\n') | |
95 |
|
95 | |||
96 | The $/$$ syntaxes make Python variables from system output, which you can |
|
96 | The $/$$ syntaxes make Python variables from system output, which you can | |
97 | later use for further scripting. The converse is also possible: when |
|
97 | 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 |
|
98 | 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 |
|
99 | python variable or expression by prepending it with $. Full details of | |
100 | the allowed syntax can be found in Python's PEP 215. |
|
100 | the allowed syntax can be found in Python's PEP 215. | |
101 |
|
101 | |||
102 | A few brief examples will illustrate these: |
|
102 | A few brief examples will illustrate these: | |
103 |
|
103 | |||
104 | fperez[~/test]|3> !ls *s.py |
|
104 | fperez[~/test]|3> !ls *s.py | |
105 | scopes.py strings.py |
|
105 | scopes.py strings.py | |
106 |
|
106 | |||
107 | ls is an internal alias, so there's no need to use !: |
|
107 | ls is an internal alias, so there's no need to use !: | |
108 | fperez[~/test]|4> ls *s.py |
|
108 | fperez[~/test]|4> ls *s.py | |
109 | scopes.py* strings.py |
|
109 | scopes.py* strings.py | |
110 |
|
110 | |||
111 | !!ls will return the output into a Python variable: |
|
111 | !!ls will return the output into a Python variable: | |
112 | fperez[~/test]|5> !!ls *s.py |
|
112 | fperez[~/test]|5> !!ls *s.py | |
113 | <5> ['scopes.py', 'strings.py'] |
|
113 | <5> ['scopes.py', 'strings.py'] | |
114 | fperez[~/test]|6> print _5 |
|
114 | fperez[~/test]|6> print _5 | |
115 | ['scopes.py', 'strings.py'] |
|
115 | ['scopes.py', 'strings.py'] | |
116 |
|
116 | |||
117 | $ and $$ allow direct capture to named variables: |
|
117 | $ and $$ allow direct capture to named variables: | |
118 | fperez[~/test]|7> $astr = ls *s.py |
|
118 | fperez[~/test]|7> $astr = ls *s.py | |
119 | fperez[~/test]|8> astr |
|
119 | fperez[~/test]|8> astr | |
120 | <8> 'scopes.py\\nstrings.py' |
|
120 | <8> 'scopes.py\\nstrings.py' | |
121 |
|
121 | |||
122 | fperez[~/test]|9> $$alist = ls *s.py |
|
122 | fperez[~/test]|9> $$alist = ls *s.py | |
123 | fperez[~/test]|10> alist |
|
123 | fperez[~/test]|10> alist | |
124 | <10> ['scopes.py', 'strings.py'] |
|
124 | <10> ['scopes.py', 'strings.py'] | |
125 |
|
125 | |||
126 | alist is now a normal python list you can loop over. Using $ will expand |
|
126 | alist is now a normal python list you can loop over. Using $ will expand | |
127 | back the python values when alias calls are made: |
|
127 | back the python values when alias calls are made: | |
128 | fperez[~/test]|11> for f in alist: |
|
128 | fperez[~/test]|11> for f in alist: | |
129 | |..> print 'file',f, |
|
129 | |..> print 'file',f, | |
130 | |..> wc -l $f |
|
130 | |..> wc -l $f | |
131 | |..> |
|
131 | |..> | |
132 | file scopes.py 13 scopes.py |
|
132 | file scopes.py 13 scopes.py | |
133 | file strings.py 4 strings.py |
|
133 | file strings.py 4 strings.py | |
134 |
|
134 | |||
135 | Note that you may need to protect your variables with braces if you want |
|
135 | 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 |
|
136 | to append strings to their names. To copy all files in alist to .bak | |
137 | extensions, you must use: |
|
137 | extensions, you must use: | |
138 | fperez[~/test]|12> for f in alist: |
|
138 | fperez[~/test]|12> for f in alist: | |
139 | |..> cp $f ${f}.bak |
|
139 | |..> cp $f ${f}.bak | |
140 |
|
140 | |||
141 | If you try using $f.bak, you'll get an AttributeError exception saying |
|
141 | 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 |
|
142 | that your string object doesn't have a .bak attribute. This is because | |
143 | the $ expansion mechanism allows you to expand full Python expressions: |
|
143 | the $ expansion mechanism allows you to expand full Python expressions: | |
144 | fperez[~/test]|13> echo "sys.platform is: $sys.platform" |
|
144 | fperez[~/test]|13> echo "sys.platform is: $sys.platform" | |
145 | sys.platform is: linux2 |
|
145 | sys.platform is: linux2 | |
146 |
|
146 | |||
147 | IPython's input history handling is still active, which allows you to |
|
147 | 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: |
|
148 | rerun a single block of multi-line input by simply using exec: | |
149 | fperez[~/test]|14> $$alist = ls *.eps |
|
149 | fperez[~/test]|14> $$alist = ls *.eps | |
150 | fperez[~/test]|15> exec _i11 |
|
150 | fperez[~/test]|15> exec _i11 | |
151 | file image2.eps 921 image2.eps |
|
151 | file image2.eps 921 image2.eps | |
152 | file image.eps 921 image.eps |
|
152 | file image.eps 921 image.eps | |
153 |
|
153 | |||
154 | While these are new special-case syntaxes, they are designed to allow very |
|
154 | 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 |
|
155 | efficient use of the shell with minimal typing. At an interactive shell | |
156 | prompt, conciseness of expression wins over readability. |
|
156 | prompt, conciseness of expression wins over readability. | |
157 |
|
157 | |||
158 | USEFUL FUNCTIONS AND MODULES |
|
158 | USEFUL FUNCTIONS AND MODULES | |
159 | ---------------------------- |
|
159 | ---------------------------- | |
160 | The os, sys and shutil modules from the Python standard library are |
|
160 | The os, sys and shutil modules from the Python standard library are | |
161 | automatically loaded. Some additional functions, useful for shell usage, |
|
161 | automatically loaded. Some additional functions, useful for shell usage, | |
162 | are listed below. You can request more help about them with '?'. |
|
162 | are listed below. You can request more help about them with '?'. | |
163 |
|
163 | |||
164 | shell - execute a command in the underlying system shell |
|
164 | shell - execute a command in the underlying system shell | |
165 | system - like shell(), but return the exit status of the command |
|
165 | system - like shell(), but return the exit status of the command | |
166 | sout - capture the output of a command as a string |
|
166 | sout - capture the output of a command as a string | |
167 | lout - capture the output of a command as a list (split on '\\n') |
|
167 | lout - capture the output of a command as a list (split on '\\n') | |
168 | getoutputerror - capture (output,error) of a shell command |
|
168 | getoutputerror - capture (output,error) of a shell command | |
169 |
|
169 | |||
170 | sout/lout are the functional equivalents of $/$$. They are provided to |
|
170 | 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, |
|
171 | allow you to capture system output in the middle of true python code, | |
172 | function definitions, etc (where $ and $$ are invalid). |
|
172 | function definitions, etc (where $ and $$ are invalid). | |
173 |
|
173 | |||
174 | DIRECTORY MANAGEMENT |
|
174 | DIRECTORY MANAGEMENT | |
175 | -------------------- |
|
175 | -------------------- | |
176 | Since each command passed by pysh to the underlying system is executed in |
|
176 | 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 |
|
177 | a subshell which exits immediately, you can NOT use !cd to navigate the | |
178 | filesystem. |
|
178 | filesystem. | |
179 |
|
179 | |||
180 | Pysh provides its own builtin '%cd' magic command to move in the |
|
180 | 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 |
|
181 | 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 |
|
182 | list of visited directories (use %dhist to see it) and allows direct | |
183 | switching to any of them. Type 'cd?' for more details. |
|
183 | switching to any of them. Type 'cd?' for more details. | |
184 |
|
184 | |||
185 | %pushd, %popd and %dirs are provided for directory stack handling. |
|
185 | %pushd, %popd and %dirs are provided for directory stack handling. | |
186 |
|
186 | |||
187 | PROMPT CUSTOMIZATION |
|
187 | PROMPT CUSTOMIZATION | |
188 | -------------------- |
|
188 | -------------------- | |
189 |
|
189 | |||
190 | The supplied ipythonrc-pysh profile comes with an example of a very |
|
190 | The supplied ipythonrc-pysh profile comes with an example of a very | |
191 | colored and detailed prompt, mainly to serve as an illustration. The |
|
191 | colored and detailed prompt, mainly to serve as an illustration. The | |
192 | valid escape sequences, besides color names, are: |
|
192 | valid escape sequences, besides color names, are: | |
193 |
|
193 | |||
194 | \\# - Prompt number. |
|
194 | \\# - Prompt number. | |
195 | \\D - Dots, as many as there are digits in \\# (so they align). |
|
195 | \\D - Dots, as many as there are digits in \\# (so they align). | |
196 | \\w - Current working directory (cwd). |
|
196 | \\w - Current working directory (cwd). | |
197 | \\W - Basename of current working directory. |
|
197 | \\W - Basename of current working directory. | |
198 | \\XN - Where N=0..5. N terms of the cwd, with $HOME written as ~. |
|
198 | \\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. |
|
199 | \\YN - Where N=0..5. Like XN, but if ~ is term N+1 it's also shown. | |
200 | \\u - Username. |
|
200 | \\u - Username. | |
201 | \\H - Full hostname. |
|
201 | \\H - Full hostname. | |
202 | \\h - Hostname up to first '.' |
|
202 | \\h - Hostname up to first '.' | |
203 | \\$ - Root symbol ($ or #). |
|
203 | \\$ - Root symbol ($ or #). | |
204 | \\t - Current time, in H:M:S format. |
|
204 | \\t - Current time, in H:M:S format. | |
205 | \\v - IPython release version. |
|
205 | \\v - IPython release version. | |
206 | \\n - Newline. |
|
206 | \\n - Newline. | |
207 | \\r - Carriage return. |
|
207 | \\r - Carriage return. | |
208 | \\\\ - An explicitly escaped '\\'. |
|
208 | \\\\ - An explicitly escaped '\\'. | |
209 |
|
209 | |||
210 | You can configure your prompt colors using any ANSI color escape. Each |
|
210 | 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 |
|
211 | color escape sets the color for any subsequent text, until another escape | |
212 | comes in and changes things. The valid color escapes are: |
|
212 | comes in and changes things. The valid color escapes are: | |
213 |
|
213 | |||
214 | \\C_Black |
|
214 | \\C_Black | |
215 | \\C_Blue |
|
215 | \\C_Blue | |
216 | \\C_Brown |
|
216 | \\C_Brown | |
217 | \\C_Cyan |
|
217 | \\C_Cyan | |
218 | \\C_DarkGray |
|
218 | \\C_DarkGray | |
219 | \\C_Green |
|
219 | \\C_Green | |
220 | \\C_LightBlue |
|
220 | \\C_LightBlue | |
221 | \\C_LightCyan |
|
221 | \\C_LightCyan | |
222 | \\C_LightGray |
|
222 | \\C_LightGray | |
223 | \\C_LightGreen |
|
223 | \\C_LightGreen | |
224 | \\C_LightPurple |
|
224 | \\C_LightPurple | |
225 | \\C_LightRed |
|
225 | \\C_LightRed | |
226 | \\C_Purple |
|
226 | \\C_Purple | |
227 | \\C_Red |
|
227 | \\C_Red | |
228 | \\C_White |
|
228 | \\C_White | |
229 | \\C_Yellow |
|
229 | \\C_Yellow | |
230 | \\C_Normal - Stop coloring, defaults to your terminal settings. |
|
230 | \\C_Normal - Stop coloring, defaults to your terminal settings. | |
231 | """ |
|
231 | """ | |
232 | pass |
|
232 | pass | |
233 |
|
233 | |||
234 | # Configure a few things. Much of this is fairly hackish, since IPython |
|
234 | # 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 |
|
235 | # doesn't really expose a clean API for it. Be careful if you start making | |
236 | # many modifications here. |
|
236 | # many modifications here. | |
237 |
|
237 | |||
238 |
|
238 | |||
239 | # Set the 'cd' command to quiet mode, a more shell-like behavior |
|
239 | # Set the 'cd' command to quiet mode, a more shell-like behavior | |
240 | __IPYTHON__.default_option('cd','-q') |
|
240 | __IPYTHON__.default_option('cd','-q') | |
241 |
|
241 | |||
242 | # This is redundant, ipy_user_conf.py will determine this |
|
242 | # This is redundant, ipy_user_conf.py will determine this | |
243 | # Load all of $PATH as aliases |
|
243 | # Load all of $PATH as aliases | |
244 | __IPYTHON__.magic_rehashx() |
|
244 | __IPYTHON__.magic_rehashx() | |
245 |
|
245 | |||
246 | # Remove %sc,%sx if present as aliases |
|
246 | # Remove %sc,%sx if present as aliases | |
247 | __IPYTHON__.magic_unalias('sc') |
|
247 | __IPYTHON__.magic_unalias('sc') | |
248 | __IPYTHON__.magic_unalias('sx') |
|
248 | __IPYTHON__.magic_unalias('sx') | |
249 |
|
249 | |||
250 | # We need different criteria for line-splitting, so that aliases such as |
|
250 | # We need different criteria for line-splitting, so that aliases such as | |
251 | # 'gnome-terminal' are interpreted as a single alias instead of variable |
|
251 | # 'gnome-terminal' are interpreted as a single alias instead of variable | |
252 | # 'gnome' minus variable 'terminal'. |
|
252 | # 'gnome' minus variable 'terminal'. | |
253 | import re |
|
253 | import re | |
254 | __IPYTHON__.line_split = re.compile(r'^([\s*,;/])' |
|
254 | __IPYTHON__.line_split = re.compile(r'^([\s*,;/])' | |
255 | r'([\?\w\.\-\+]+\w*\s*)' |
|
255 | r'([\?\w\.\-\+]+\w*\s*)' | |
256 | r'(\(?.*$)') |
|
256 | r'(\(?.*$)') | |
257 |
|
257 | |||
258 | # Namespace cleanup |
|
258 | # Namespace cleanup | |
259 | del re |
|
259 | del re |
@@ -1,122 +1,122 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Modified input prompt for entering text with >>> or ... at the start. |
|
2 | """Modified input prompt for entering text with >>> or ... at the start. | |
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 '... '. These two strings, if present at the start of the input |
|
5 | '>>> ' or '... '. These two strings, if present at the start of the input | |
6 | line, are stripped. This allows for direct pasting of code from examples such |
|
6 | line, are stripped. This allows for direct pasting of code from examples such | |
7 | as those available in the standard Python tutorial. |
|
7 | as those available in the standard Python tutorial. | |
8 |
|
8 | |||
9 | Normally pasting such code is one chunk is impossible because of the |
|
9 | Normally pasting such code is one chunk is impossible because of the | |
10 | extraneous >>> and ..., requiring one to do a line by line paste with careful |
|
10 | extraneous >>> and ..., requiring one to do a line by line paste with careful | |
11 | removal of those characters. This module allows pasting that kind of |
|
11 | removal of those characters. This module allows pasting that kind of | |
12 | multi-line examples in one pass. |
|
12 | multi-line examples in one pass. | |
13 |
|
13 | |||
14 | Here is an 'screenshot' of a section of the tutorial pasted into IPython with |
|
14 | Here is an 'screenshot' of a section of the tutorial pasted into IPython with | |
15 | this feature enabled: |
|
15 | this feature enabled: | |
16 |
|
16 | |||
17 | In [1]: >>> def fib2(n): # return Fibonacci series up to n |
|
17 | In [1]: >>> def fib2(n): # return Fibonacci series up to n | |
18 | ...: ... '''Return a list containing the Fibonacci series up to n.''' |
|
18 | ...: ... '''Return a list containing the Fibonacci series up to n.''' | |
19 | ...: ... result = [] |
|
19 | ...: ... result = [] | |
20 | ...: ... a, b = 0, 1 |
|
20 | ...: ... a, b = 0, 1 | |
21 | ...: ... while b < n: |
|
21 | ...: ... while b < n: | |
22 | ...: ... result.append(b) # see below |
|
22 | ...: ... result.append(b) # see below | |
23 | ...: ... a, b = b, a+b |
|
23 | ...: ... a, b = b, a+b | |
24 | ...: ... return result |
|
24 | ...: ... return result | |
25 | ...: |
|
25 | ...: | |
26 |
|
26 | |||
27 | In [2]: fib2(10) |
|
27 | In [2]: fib2(10) | |
28 | Out[2]: [1, 1, 2, 3, 5, 8] |
|
28 | Out[2]: [1, 1, 2, 3, 5, 8] | |
29 |
|
29 | |||
30 | The >>> and ... are stripped from the input so that the python interpreter |
|
30 | The >>> and ... are stripped from the input so that the python interpreter | |
31 | only sees the real part of the code. |
|
31 | only sees the real part of the code. | |
32 |
|
32 | |||
33 | All other input is processed normally. |
|
33 | All other input is processed normally. | |
34 |
|
34 | |||
35 | Notes |
|
35 | Notes | |
36 | ===== |
|
36 | ===== | |
37 |
|
37 | |||
38 | * You can even paste code that has extra initial spaces, such as is common in |
|
38 | * You can even paste code that has extra initial spaces, such as is common in | |
39 | doctests: |
|
39 | doctests: | |
40 |
|
40 | |||
41 | In [3]: >>> a = ['Mary', 'had', 'a', 'little', 'lamb'] |
|
41 | In [3]: >>> a = ['Mary', 'had', 'a', 'little', 'lamb'] | |
42 |
|
42 | |||
43 | In [4]: >>> for i in range(len(a)): |
|
43 | In [4]: >>> for i in range(len(a)): | |
44 | ...: ... print i, a[i] |
|
44 | ...: ... print i, a[i] | |
45 | ...: ... |
|
45 | ...: ... | |
46 | 0 Mary |
|
46 | 0 Mary | |
47 | 1 had |
|
47 | 1 had | |
48 | 2 a |
|
48 | 2 a | |
49 | 3 little |
|
49 | 3 little | |
50 | 4 lamb |
|
50 | 4 lamb | |
51 | """ |
|
51 | """ | |
52 |
|
52 | |||
53 | #***************************************************************************** |
|
53 | #***************************************************************************** | |
54 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
54 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
55 | # |
|
55 | # | |
56 | # Distributed under the terms of the BSD License. The full license is in |
|
56 | # Distributed under the terms of the BSD License. The full license is in | |
57 | # the file COPYING, distributed as part of this software. |
|
57 | # the file COPYING, distributed as part of this software. | |
58 | #***************************************************************************** |
|
58 | #***************************************************************************** | |
59 |
|
59 | |||
60 | from IPython import Release |
|
60 | from IPython import Release | |
61 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
61 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
62 | __license__ = Release.license |
|
62 | __license__ = Release.license | |
63 |
|
63 | |||
64 | # This file is an example of how to modify IPython's line-processing behavior |
|
64 | # This file is an example of how to modify IPython's line-processing behavior | |
65 | # without touching the internal code. We'll define an alternate pre-processing |
|
65 | # without touching the internal code. We'll define an alternate pre-processing | |
66 | # stage which allows a special form of input (which is invalid Python syntax) |
|
66 | # stage which allows a special form of input (which is invalid Python syntax) | |
67 | # for certain quantities, rewrites a line of proper Python in those cases, and |
|
67 | # for certain quantities, rewrites a line of proper Python in those cases, and | |
68 | # then passes it off to IPython's normal processor for further work. |
|
68 | # then passes it off to IPython's normal processor for further work. | |
69 |
|
69 | |||
70 | # With this kind of customization, IPython can be adapted for many |
|
70 | # With this kind of customization, IPython can be adapted for many | |
71 | # special-purpose scenarios providing alternate input syntaxes. |
|
71 | # special-purpose scenarios providing alternate input syntaxes. | |
72 |
|
72 | |||
73 | # This file can be imported like a regular module. |
|
73 | # This file can be imported like a regular module. | |
74 |
|
74 | |||
75 | # IPython has a prefilter() function that analyzes each input line. We redefine |
|
75 | # IPython has a prefilter() function that analyzes each input line. We redefine | |
76 | # it here to first pre-process certain forms of input |
|
76 | # it here to first pre-process certain forms of input | |
77 |
|
77 | |||
78 | # The prototype of any alternate prefilter must be like this one (the name |
|
78 | # The prototype of any alternate prefilter must be like this one (the name | |
79 | # doesn't matter): |
|
79 | # doesn't matter): | |
80 | # - line is a string containing the user input line. |
|
80 | # - line is a string containing the user input line. | |
81 | # - continuation is a parameter which tells us if we are processing a first |
|
81 | # - continuation is a parameter which tells us if we are processing a first | |
82 | # line of user input or the second or higher of a multi-line statement. |
|
82 | # line of user input or the second or higher of a multi-line statement. | |
83 |
|
83 | |||
84 | import re |
|
84 | import re | |
85 |
|
85 | |||
86 | from IPython.iplib import InteractiveShell |
|
86 | from IPython.iplib import InteractiveShell | |
87 |
|
87 | |||
88 | PROMPT_RE = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )') |
|
88 | PROMPT_RE = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )') | |
89 |
|
89 | |||
90 | def prefilter_paste(self,line,continuation): |
|
90 | def prefilter_paste(self,line,continuation): | |
91 | """Alternate prefilter for input of pasted code from an interpreter. |
|
91 | """Alternate prefilter for input of pasted code from an interpreter. | |
92 | """ |
|
92 | """ | |
93 | if not line: |
|
93 | if not line: | |
94 | return '' |
|
94 | return '' | |
95 | m = PROMPT_RE.match(line) |
|
95 | m = PROMPT_RE.match(line) | |
96 | if m: |
|
96 | if m: | |
97 | # In the end, always call the default IPython _prefilter() function. |
|
97 | # In the end, always call the default IPython _prefilter() function. | |
98 | # Note that self must be passed explicitly, b/c we're calling the |
|
98 | # Note that self must be passed explicitly, b/c we're calling the | |
99 | # unbound class method (since this method will overwrite the instance |
|
99 | # unbound class method (since this method will overwrite the instance | |
100 | # prefilter()) |
|
100 | # prefilter()) | |
101 | return self._prefilter(line[len(m.group(0)):],continuation) |
|
101 | return self._prefilter(line[len(m.group(0)):],continuation) | |
102 | elif line.strip() == '...': |
|
102 | elif line.strip() == '...': | |
103 | return self._prefilter('',continuation) |
|
103 | return self._prefilter('',continuation) | |
104 | elif line.isspace(): |
|
104 | elif line.isspace(): | |
105 | # This allows us to recognize multiple input prompts separated by blank |
|
105 | # This allows us to recognize multiple input prompts separated by blank | |
106 | # lines and pasted in a single chunk, very common when pasting doctests |
|
106 | # lines and pasted in a single chunk, very common when pasting doctests | |
107 | # or long tutorial passages. |
|
107 | # or long tutorial passages. | |
108 | return '' |
|
108 | return '' | |
109 | else: |
|
109 | else: | |
110 | return self._prefilter(line,continuation) |
|
110 | return self._prefilter(line,continuation) | |
111 |
|
111 | |||
112 | def activate_prefilter(): |
|
112 | def activate_prefilter(): | |
113 | """Rebind the input-pasting filter to be the new IPython prefilter""" |
|
113 | """Rebind the input-pasting filter to be the new IPython prefilter""" | |
114 | InteractiveShell.prefilter = prefilter_paste |
|
114 | InteractiveShell.prefilter = prefilter_paste | |
115 |
|
115 | |||
116 | def deactivate_prefilter(): |
|
116 | def deactivate_prefilter(): | |
117 | """Reset the filter.""" |
|
117 | """Reset the filter.""" | |
118 | InteractiveShell.prefilter = InteractiveShell._prefilter |
|
118 | InteractiveShell.prefilter = InteractiveShell._prefilter | |
119 |
|
119 | |||
120 | # Just a heads up at the console |
|
120 | # Just a heads up at the console | |
121 | activate_prefilter() |
|
121 | activate_prefilter() | |
122 | print '*** Pasting of code with ">>>" or "..." has been enabled.' |
|
122 | print '*** Pasting of code with ">>>" or "..." has been enabled.' |
@@ -1,83 +1,83 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Modified input prompt for entering quantities with units. |
|
2 | """Modified input prompt for entering quantities with units. | |
3 |
|
3 | |||
4 | Modify the behavior of the interactive interpreter to allow direct input of |
|
4 | Modify the behavior of the interactive interpreter to allow direct input of | |
5 | quantities with units without having to make a function call. |
|
5 | quantities with units without having to make a function call. | |
6 |
|
6 | |||
7 | Now the following forms are accepted: |
|
7 | Now the following forms are accepted: | |
8 |
|
8 | |||
9 | x = 4 m |
|
9 | x = 4 m | |
10 | y = -.45e3 m/s |
|
10 | y = -.45e3 m/s | |
11 | g = 9.8 m/s**2 |
|
11 | g = 9.8 m/s**2 | |
12 | a = 2.3 m/s^2 # ^ -> ** automatically |
|
12 | a = 2.3 m/s^2 # ^ -> ** automatically | |
13 |
|
13 | |||
14 | All other input is processed normally. |
|
14 | All other input is processed normally. | |
15 | """ |
|
15 | """ | |
16 | #***************************************************************************** |
|
16 | #***************************************************************************** | |
17 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
17 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |
18 | # |
|
18 | # | |
19 | # Distributed under the terms of the BSD License. The full license is in |
|
19 | # Distributed under the terms of the BSD License. The full license is in | |
20 | # the file COPYING, distributed as part of this software. |
|
20 | # the file COPYING, distributed as part of this software. | |
21 | #***************************************************************************** |
|
21 | #***************************************************************************** | |
22 |
|
22 | |||
23 | from IPython import Release |
|
23 | from IPython import Release | |
24 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
24 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
25 | __license__ = Release.license |
|
25 | __license__ = Release.license | |
26 |
|
26 | |||
27 | # This file is an example of how to modify IPython's line-processing behavior |
|
27 | # This file is an example of how to modify IPython's line-processing behavior | |
28 | # without touching the internal code. We'll define an alternate pre-processing |
|
28 | # without touching the internal code. We'll define an alternate pre-processing | |
29 | # stage which allows a special form of input (which is invalid Python syntax) |
|
29 | # stage which allows a special form of input (which is invalid Python syntax) | |
30 | # for certain quantities, rewrites a line of proper Python in those cases, and |
|
30 | # for certain quantities, rewrites a line of proper Python in those cases, and | |
31 | # then passes it off to IPython's normal processor for further work. |
|
31 | # then passes it off to IPython's normal processor for further work. | |
32 |
|
32 | |||
33 | # With this kind of customization, IPython can be adapted for many |
|
33 | # With this kind of customization, IPython can be adapted for many | |
34 | # special-purpose scenarios providing alternate input syntaxes. |
|
34 | # special-purpose scenarios providing alternate input syntaxes. | |
35 |
|
35 | |||
36 | # This file can be imported like a regular module. |
|
36 | # This file can be imported like a regular module. | |
37 |
|
37 | |||
38 | # IPython has a prefilter() function that analyzes each input line. We redefine |
|
38 | # IPython has a prefilter() function that analyzes each input line. We redefine | |
39 | # it here to first pre-process certain forms of input |
|
39 | # it here to first pre-process certain forms of input | |
40 |
|
40 | |||
41 | # The prototype of any alternate prefilter must be like this one (the name |
|
41 | # The prototype of any alternate prefilter must be like this one (the name | |
42 | # doesn't matter): |
|
42 | # doesn't matter): | |
43 | # - line is a string containing the user input line. |
|
43 | # - line is a string containing the user input line. | |
44 | # - continuation is a parameter which tells us if we are processing a first line of |
|
44 | # - continuation is a parameter which tells us if we are processing a first line of | |
45 | # user input or the second or higher of a multi-line statement. |
|
45 | # user input or the second or higher of a multi-line statement. | |
46 |
|
46 | |||
47 | def prefilter_PQ(self,line,continuation): |
|
47 | def prefilter_PQ(self,line,continuation): | |
48 | """Alternate prefilter for input of PhysicalQuantityInteractive objects. |
|
48 | """Alternate prefilter for input of PhysicalQuantityInteractive objects. | |
49 |
|
49 | |||
50 | This assumes that the function PhysicalQuantityInteractive() has been |
|
50 | This assumes that the function PhysicalQuantityInteractive() has been | |
51 | imported.""" |
|
51 | imported.""" | |
52 |
|
52 | |||
53 | from re import match |
|
53 | from re import match | |
54 | from IPython.iplib import InteractiveShell |
|
54 | from IPython.iplib import InteractiveShell | |
55 |
|
55 | |||
56 | # This regexp is what does the real work |
|
56 | # This regexp is what does the real work | |
57 | unit_split = match(r'\s*(\w+)\s*=\s*(-?\d*\.?\d*[eE]?-?\d*)\s+([a-zA-Z].*)', |
|
57 | unit_split = match(r'\s*(\w+)\s*=\s*(-?\d*\.?\d*[eE]?-?\d*)\s+([a-zA-Z].*)', | |
58 | line) |
|
58 | line) | |
59 |
|
59 | |||
60 | # If special input was ecnountered, process it: |
|
60 | # If special input was ecnountered, process it: | |
61 | if unit_split: |
|
61 | if unit_split: | |
62 | var,val,units = unit_split.groups() |
|
62 | var,val,units = unit_split.groups() | |
63 | if var and val and units: |
|
63 | if var and val and units: | |
64 | units = units.replace('^','**') |
|
64 | units = units.replace('^','**') | |
65 | # Now a valid line needs to be constructed for IPython to process: |
|
65 | # Now a valid line needs to be constructed for IPython to process: | |
66 | line = var +" = PhysicalQuantityInteractive(" + val + ", '" + \ |
|
66 | line = var +" = PhysicalQuantityInteractive(" + val + ", '" + \ | |
67 | units + "')" |
|
67 | units + "')" | |
68 | #print 'New line:',line # dbg |
|
68 | #print 'New line:',line # dbg | |
69 |
|
69 | |||
70 | # In the end, always call the default IPython _prefilter() function. Note |
|
70 | # In the end, always call the default IPython _prefilter() function. Note | |
71 | # that self must be passed explicitly, b/c we're calling the unbound class |
|
71 | # that self must be passed explicitly, b/c we're calling the unbound class | |
72 | # method (since this method will overwrite the instance prefilter()) |
|
72 | # method (since this method will overwrite the instance prefilter()) | |
73 | return InteractiveShell._prefilter(self,line,continuation) |
|
73 | return InteractiveShell._prefilter(self,line,continuation) | |
74 |
|
74 | |||
75 | # Rebind this to be the new IPython prefilter: |
|
75 | # Rebind this to be the new IPython prefilter: | |
76 | from IPython.iplib import InteractiveShell |
|
76 | from IPython.iplib import InteractiveShell | |
77 | InteractiveShell.prefilter = prefilter_PQ |
|
77 | InteractiveShell.prefilter = prefilter_PQ | |
78 |
|
78 | |||
79 | # Clean up the namespace. |
|
79 | # Clean up the namespace. | |
80 | del InteractiveShell,prefilter_PQ |
|
80 | del InteractiveShell,prefilter_PQ | |
81 |
|
81 | |||
82 | # Just a heads up at the console |
|
82 | # Just a heads up at the console | |
83 | print '*** Simplified input for physical quantities enabled.' |
|
83 | print '*** Simplified input for physical quantities enabled.' |
@@ -1,88 +1,88 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Modify the PhysicalQuantities class for more convenient interactive use. |
|
2 | """Modify the PhysicalQuantities class for more convenient interactive use. | |
3 |
|
3 | |||
4 | Also redefine some math functions to operate on PhysQties with no need for |
|
4 | Also redefine some math functions to operate on PhysQties with no need for | |
5 | special method syntax. This just means moving them out to the global |
|
5 | special method syntax. This just means moving them out to the global | |
6 | namespace. |
|
6 | namespace. | |
7 |
|
7 | |||
8 | This module should always be loaded *after* math or Numeric, so it can |
|
8 | This module should always be loaded *after* math or Numeric, so it can | |
9 | overwrite math functions with the versions that handle units.""" |
|
9 | overwrite math functions with the versions that handle units.""" | |
10 |
|
10 | |||
11 | #***************************************************************************** |
|
11 | #***************************************************************************** | |
12 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
12 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |
13 | # |
|
13 | # | |
14 | # Distributed under the terms of the BSD License. The full license is in |
|
14 | # Distributed under the terms of the BSD License. The full license is in | |
15 | # the file COPYING, distributed as part of this software. |
|
15 | # the file COPYING, distributed as part of this software. | |
16 | #***************************************************************************** |
|
16 | #***************************************************************************** | |
17 |
|
17 | |||
18 | from IPython import Release |
|
18 | from IPython import Release | |
19 | __author__ = '%s <%s>' % Release.authors['Fernando'] |
|
19 | __author__ = '%s <%s>' % Release.authors['Fernando'] | |
20 | __license__ = Release.license |
|
20 | __license__ = Release.license | |
21 |
|
21 | |||
22 | from Scientific.Physics.PhysicalQuantities import PhysicalQuantity |
|
22 | from Scientific.Physics.PhysicalQuantities import PhysicalQuantity | |
23 |
|
23 | |||
24 | # This code can be set up to work with Numeric or with math for providing the |
|
24 | # This code can be set up to work with Numeric or with math for providing the | |
25 | # mathematical functions. Uncomment the one you prefer to use below. |
|
25 | # mathematical functions. Uncomment the one you prefer to use below. | |
26 |
|
26 | |||
27 | # If you use math, sin(x) won't work for x an array, only float or PhysQty |
|
27 | # If you use math, sin(x) won't work for x an array, only float or PhysQty | |
28 | import math |
|
28 | import math | |
29 |
|
29 | |||
30 | # If you use Numeric, sin(x) works for x a float, PhysQty an array. |
|
30 | # If you use Numeric, sin(x) works for x a float, PhysQty an array. | |
31 | #import Numeric as math |
|
31 | #import Numeric as math | |
32 |
|
32 | |||
33 | class PhysicalQuantityFunction: |
|
33 | class PhysicalQuantityFunction: | |
34 | """Generic function wrapper for PhysicalQuantity instances. |
|
34 | """Generic function wrapper for PhysicalQuantity instances. | |
35 |
|
35 | |||
36 | Calls functions from either the math library or the instance's methods as |
|
36 | Calls functions from either the math library or the instance's methods as | |
37 | required. Allows using sin(theta) or sqrt(v**2) syntax irrespective of |
|
37 | required. Allows using sin(theta) or sqrt(v**2) syntax irrespective of | |
38 | whether theta is a pure number or a PhysicalQuantity. |
|
38 | whether theta is a pure number or a PhysicalQuantity. | |
39 |
|
39 | |||
40 | This is *slow*. It's meant for convenient interactive use, not for |
|
40 | This is *slow*. It's meant for convenient interactive use, not for | |
41 | speed.""" |
|
41 | speed.""" | |
42 |
|
42 | |||
43 | def __init__(self,name): |
|
43 | def __init__(self,name): | |
44 | self.name = name |
|
44 | self.name = name | |
45 |
|
45 | |||
46 | def __call__(self,x): |
|
46 | def __call__(self,x): | |
47 | if isinstance(x,PhysicalQuantity): |
|
47 | if isinstance(x,PhysicalQuantity): | |
48 | return PhysicalQuantity.__dict__[self.name](x) |
|
48 | return PhysicalQuantity.__dict__[self.name](x) | |
49 | else: |
|
49 | else: | |
50 | return math.__dict__[self.name](x) |
|
50 | return math.__dict__[self.name](x) | |
51 |
|
51 | |||
52 | class PhysicalQuantityInteractive(PhysicalQuantity): |
|
52 | class PhysicalQuantityInteractive(PhysicalQuantity): | |
53 | """Physical quantity with units - modified for Interactive use. |
|
53 | """Physical quantity with units - modified for Interactive use. | |
54 |
|
54 | |||
55 | Basically, the __str__ and __repr__ methods have been swapped for more |
|
55 | Basically, the __str__ and __repr__ methods have been swapped for more | |
56 | convenient interactive use. Powers are shown as ^ instead of ** and only 4 |
|
56 | convenient interactive use. Powers are shown as ^ instead of ** and only 4 | |
57 | significant figures are shown. |
|
57 | significant figures are shown. | |
58 |
|
58 | |||
59 | Also adds the following aliases for commonly used methods: |
|
59 | Also adds the following aliases for commonly used methods: | |
60 | b = PhysicalQuantity.inBaseUnits |
|
60 | b = PhysicalQuantity.inBaseUnits | |
61 | u = PhysicalQuantity.inUnitsOf |
|
61 | u = PhysicalQuantity.inUnitsOf | |
62 |
|
62 | |||
63 | These are useful when doing a lot of interactive calculations. |
|
63 | These are useful when doing a lot of interactive calculations. | |
64 | """ |
|
64 | """ | |
65 |
|
65 | |||
66 | # shorthands for the most useful unit conversions |
|
66 | # shorthands for the most useful unit conversions | |
67 | b = PhysicalQuantity.inBaseUnits # so you can just type x.b to get base units |
|
67 | b = PhysicalQuantity.inBaseUnits # so you can just type x.b to get base units | |
68 | u = PhysicalQuantity.inUnitsOf |
|
68 | u = PhysicalQuantity.inUnitsOf | |
69 |
|
69 | |||
70 | # This can be done, but it can get dangerous when coupled with IPython's |
|
70 | # This can be done, but it can get dangerous when coupled with IPython's | |
71 | # auto-calling. Everything ends up shown in baseunits and things like x*2 |
|
71 | # auto-calling. Everything ends up shown in baseunits and things like x*2 | |
72 | # get automatically converted to k(*2), which doesn't work. |
|
72 | # get automatically converted to k(*2), which doesn't work. | |
73 | # Probably not a good idea in general... |
|
73 | # Probably not a good idea in general... | |
74 | #__call__ = b |
|
74 | #__call__ = b | |
75 |
|
75 | |||
76 | def __str__(self): |
|
76 | def __str__(self): | |
77 | return PhysicalQuantity.__repr__(self) |
|
77 | return PhysicalQuantity.__repr__(self) | |
78 |
|
78 | |||
79 | def __repr__(self): |
|
79 | def __repr__(self): | |
80 | value = '%.4G' % self.value |
|
80 | value = '%.4G' % self.value | |
81 | units = self.unit.name().replace('**','^') |
|
81 | units = self.unit.name().replace('**','^') | |
82 | return value + ' ' + units |
|
82 | return value + ' ' + units | |
83 |
|
83 | |||
84 | # implement the methods defined in PhysicalQuantity as PhysicalQuantityFunctions |
|
84 | # implement the methods defined in PhysicalQuantity as PhysicalQuantityFunctions | |
85 | sin = PhysicalQuantityFunction('sin') |
|
85 | sin = PhysicalQuantityFunction('sin') | |
86 | cos = PhysicalQuantityFunction('cos') |
|
86 | cos = PhysicalQuantityFunction('cos') | |
87 | tan = PhysicalQuantityFunction('tan') |
|
87 | tan = PhysicalQuantityFunction('tan') | |
88 | sqrt = PhysicalQuantityFunction('sqrt') |
|
88 | sqrt = PhysicalQuantityFunction('sqrt') |
@@ -1,13 +1,13 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """This directory is meant for special-purpose extensions to IPython. |
|
2 | """This directory is meant for special-purpose extensions to IPython. | |
3 |
|
3 | |||
4 | This can include things which alter the syntax processing stage (see |
|
4 | This can include things which alter the syntax processing stage (see | |
5 | PhysicalQ_Input for an example of how to do this). |
|
5 | PhysicalQ_Input for an example of how to do this). | |
6 |
|
6 | |||
7 | Any file located here can be called with an 'execfile =' option as |
|
7 | Any file located here can be called with an 'execfile =' option as | |
8 |
|
8 | |||
9 | execfile = Extensions/filename.py |
|
9 | execfile = Extensions/filename.py | |
10 |
|
10 | |||
11 | since the IPython directory itself is already part of the search path for |
|
11 | since the IPython directory itself is already part of the search path for | |
12 | files listed as 'execfile ='. |
|
12 | files listed as 'execfile ='. | |
13 | """ |
|
13 | """ |
@@ -1,86 +1,86 b'' | |||||
1 | <?xml version='1.0' encoding='iso-8859-1'?> |
|
1 | <?xml version='1.0' encoding='iso-8859-1'?> | |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
3 | <html> |
|
3 | <html> | |
4 | <head> |
|
4 | <head> | |
5 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> |
|
5 | <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> | |
6 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> |
|
6 | <link rel="stylesheet" href="igrid_help.css" type="text/css" /> | |
7 | <title>igrid help</title> |
|
7 | <title>igrid help</title> | |
8 | </head> |
|
8 | </head> | |
9 | <body> |
|
9 | <body> | |
10 | <h1>igrid help</h1> |
|
10 | <h1>igrid help</h1> | |
11 |
|
11 | |||
12 |
|
12 | |||
13 | <h2>Commands</h2> |
|
13 | <h2>Commands</h2> | |
14 |
|
14 | |||
15 |
|
15 | |||
16 | <h3>pick (P)</h3> |
|
16 | <h3>pick (P)</h3> | |
17 | <p>Pick the whole row (object is available as "_")</p> |
|
17 | <p>Pick the whole row (object is available as "_")</p> | |
18 |
|
18 | |||
19 | <h3>pickattr (Shift-P)</h3> |
|
19 | <h3>pickattr (Shift-P)</h3> | |
20 | <p>Pick the attribute under the cursor</p> |
|
20 | <p>Pick the attribute under the cursor</p> | |
21 |
|
21 | |||
22 | <h3>pickallattrs (Shift-C)</h3> |
|
22 | <h3>pickallattrs (Shift-C)</h3> | |
23 | <p>Pick' the complete column under the cursor (i.e. the attribute under the |
|
23 | <p>Pick' the complete column under the cursor (i.e. the attribute under the | |
24 | cursor) from all currently fetched objects. These attributes will be returned |
|
24 | cursor) from all currently fetched objects. These attributes will be returned | |
25 | as a list.</p> |
|
25 | as a list.</p> | |
26 |
|
26 | |||
27 | <h3>enter (E)</h3> |
|
27 | <h3>enter (E)</h3> | |
28 | <p>Enter the object under the cursor. (what this mean depends on the object |
|
28 | <p>Enter the object under the cursor. (what this mean depends on the object | |
29 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> |
|
29 | itself, i.e. how it implements iteration). This opens a new browser 'level'.</p> | |
30 |
|
30 | |||
31 | <h3>enterattr (Shift-E)</h3> |
|
31 | <h3>enterattr (Shift-E)</h3> | |
32 | <p>Enter the attribute under the cursor.</p> |
|
32 | <p>Enter the attribute under the cursor.</p> | |
33 |
|
33 | |||
34 | <h3>detail (D)</h3> |
|
34 | <h3>detail (D)</h3> | |
35 | <p>Show a detail view of the object under the cursor. This shows the name, |
|
35 | <p>Show a detail view of the object under the cursor. This shows the name, | |
36 | type, doc string and value of the object attributes (and it might show more |
|
36 | type, doc string and value of the object attributes (and it might show more | |
37 | attributes than in the list view, depending on the object).</p> |
|
37 | attributes than in the list view, depending on the object).</p> | |
38 |
|
38 | |||
39 | <h3>detailattr (Shift-D)</h3> |
|
39 | <h3>detailattr (Shift-D)</h3> | |
40 | <p>Show a detail view of the attribute under the cursor.</p> |
|
40 | <p>Show a detail view of the attribute under the cursor.</p> | |
41 |
|
41 | |||
42 | <h3>pickrows (M)</h3> |
|
42 | <h3>pickrows (M)</h3> | |
43 | <p>Pick multiple selected rows (M)</p> |
|
43 | <p>Pick multiple selected rows (M)</p> | |
44 |
|
44 | |||
45 | <h3>pickrowsattr (CTRL-M)</h3> |
|
45 | <h3>pickrowsattr (CTRL-M)</h3> | |
46 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> |
|
46 | <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p> | |
47 |
|
47 | |||
48 | <h3>find (CTRL-F)</h3> |
|
48 | <h3>find (CTRL-F)</h3> | |
49 | <p>Find text</p> |
|
49 | <p>Find text</p> | |
50 |
|
50 | |||
51 | <h3>find_next (F3)</h3> |
|
51 | <h3>find_next (F3)</h3> | |
52 | <p>Find next occurrence of the searchtext</p> |
|
52 | <p>Find next occurrence of the searchtext</p> | |
53 |
|
53 | |||
54 | <h3>find_previous (Shift-F3)</h3> |
|
54 | <h3>find_previous (Shift-F3)</h3> | |
55 | <p>Find previous occurrence of the searchtext </p> |
|
55 | <p>Find previous occurrence of the searchtext </p> | |
56 |
|
56 | |||
57 | <h3>sortattrasc (V)</h3> |
|
57 | <h3>sortattrasc (V)</h3> | |
58 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> |
|
58 | <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p> | |
59 |
|
59 | |||
60 | <h3>sortattrdesc (Shift-V)</h3> |
|
60 | <h3>sortattrdesc (Shift-V)</h3> | |
61 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> |
|
61 | <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p> | |
62 |
|
62 | |||
63 | <h3>leave (Backspace, DEL, X)</h3> |
|
63 | <h3>leave (Backspace, DEL, X)</h3> | |
64 | <p>Close current tab (and all the tabs to the right of the current one).</h3> |
|
64 | <p>Close current tab (and all the tabs to the right of the current one).</h3> | |
65 |
|
65 | |||
66 | <h3>quit (ESC,Q)</h3> |
|
66 | <h3>quit (ESC,Q)</h3> | |
67 | <p>Quit igrid and return to the IPython prompt.</p> |
|
67 | <p>Quit igrid and return to the IPython prompt.</p> | |
68 |
|
68 | |||
69 |
|
69 | |||
70 | <h2>Navigation</h2> |
|
70 | <h2>Navigation</h2> | |
71 |
|
71 | |||
72 |
|
72 | |||
73 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> |
|
73 | <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3> | |
74 |
|
74 | |||
75 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> |
|
75 | <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3> | |
76 |
|
76 | |||
77 | <h3>Move the cursor one column to the left (<)</h3> |
|
77 | <h3>Move the cursor one column to the left (<)</h3> | |
78 |
|
78 | |||
79 | <h3>Move the cursor one column to the right (>)</h3> |
|
79 | <h3>Move the cursor one column to the right (>)</h3> | |
80 |
|
80 | |||
81 | <h3>Jump to the first row in the current column (CTRL-Up)</h3> |
|
81 | <h3>Jump to the first row in the current column (CTRL-Up)</h3> | |
82 |
|
82 | |||
83 | <h3>Jump to the last row in the current column (CTRL-Down)</h3> |
|
83 | <h3>Jump to the last row in the current column (CTRL-Down)</h3> | |
84 |
|
84 | |||
85 | </body> |
|
85 | </body> | |
86 | </html> |
|
86 | </html> |
This diff has been collapsed as it changes many lines, (1150 lines changed) Show them Hide them | |||||
@@ -1,575 +1,575 b'' | |||||
1 | """ ILeo - Leo plugin for IPython |
|
1 | """ ILeo - Leo plugin for IPython | |
2 |
|
2 | |||
3 |
|
3 | |||
4 | """ |
|
4 | """ | |
5 | import IPython.ipapi |
|
5 | import IPython.ipapi | |
6 | import IPython.genutils |
|
6 | import IPython.genutils | |
7 | import IPython.generics |
|
7 | import IPython.generics | |
8 | from IPython.hooks import CommandChainDispatcher |
|
8 | from IPython.hooks import CommandChainDispatcher | |
9 | import re |
|
9 | import re | |
10 | import UserDict |
|
10 | import UserDict | |
11 | from IPython.ipapi import TryNext |
|
11 | from IPython.ipapi import TryNext | |
12 | import IPython.macro |
|
12 | import IPython.macro | |
13 |
|
13 | |||
14 | def init_ipython(ipy): |
|
14 | def init_ipython(ipy): | |
15 | """ This will be run by _ip.load('ipy_leo') |
|
15 | """ This will be run by _ip.load('ipy_leo') | |
16 |
|
16 | |||
17 | Leo still needs to run update_commander() after this. |
|
17 | Leo still needs to run update_commander() after this. | |
18 |
|
18 | |||
19 | """ |
|
19 | """ | |
20 | global ip |
|
20 | global ip | |
21 | ip = ipy |
|
21 | ip = ipy | |
22 | ip.set_hook('complete_command', mb_completer, str_key = '%mb') |
|
22 | ip.set_hook('complete_command', mb_completer, str_key = '%mb') | |
23 | ip.expose_magic('mb',mb_f) |
|
23 | ip.expose_magic('mb',mb_f) | |
24 | ip.expose_magic('lee',lee_f) |
|
24 | ip.expose_magic('lee',lee_f) | |
25 | ip.expose_magic('leoref',leoref_f) |
|
25 | ip.expose_magic('leoref',leoref_f) | |
26 | expose_ileo_push(push_cl_node,100) |
|
26 | expose_ileo_push(push_cl_node,100) | |
27 | # this should be the LAST one that will be executed, and it will never raise TryNext |
|
27 | # this should be the LAST one that will be executed, and it will never raise TryNext | |
28 | expose_ileo_push(push_ipython_script, 1000) |
|
28 | expose_ileo_push(push_ipython_script, 1000) | |
29 | expose_ileo_push(push_plain_python, 100) |
|
29 | expose_ileo_push(push_plain_python, 100) | |
30 | expose_ileo_push(push_ev_node, 100) |
|
30 | expose_ileo_push(push_ev_node, 100) | |
31 | global wb |
|
31 | global wb | |
32 | wb = LeoWorkbook() |
|
32 | wb = LeoWorkbook() | |
33 | ip.user_ns['wb'] = wb |
|
33 | ip.user_ns['wb'] = wb | |
34 |
|
34 | |||
35 | show_welcome() |
|
35 | show_welcome() | |
36 |
|
36 | |||
37 |
|
37 | |||
38 | def update_commander(new_leox): |
|
38 | def update_commander(new_leox): | |
39 | """ Set the Leo commander to use |
|
39 | """ Set the Leo commander to use | |
40 |
|
40 | |||
41 | This will be run every time Leo does ipython-launch; basically, |
|
41 | This will be run every time Leo does ipython-launch; basically, | |
42 | when the user switches the document he is focusing on, he should do |
|
42 | when the user switches the document he is focusing on, he should do | |
43 | ipython-launch to tell ILeo what document the commands apply to. |
|
43 | ipython-launch to tell ILeo what document the commands apply to. | |
44 |
|
44 | |||
45 | """ |
|
45 | """ | |
46 |
|
46 | |||
47 | global c,g |
|
47 | global c,g | |
48 | c,g = new_leox.c, new_leox.g |
|
48 | c,g = new_leox.c, new_leox.g | |
49 | print "Set Leo Commander:",c.frame.getTitle() |
|
49 | print "Set Leo Commander:",c.frame.getTitle() | |
50 |
|
50 | |||
51 | # will probably be overwritten by user, but handy for experimentation early on |
|
51 | # will probably be overwritten by user, but handy for experimentation early on | |
52 | ip.user_ns['c'] = c |
|
52 | ip.user_ns['c'] = c | |
53 | ip.user_ns['g'] = g |
|
53 | ip.user_ns['g'] = g | |
54 | ip.user_ns['_leo'] = new_leox |
|
54 | ip.user_ns['_leo'] = new_leox | |
55 |
|
55 | |||
56 | new_leox.push = push_position_from_leo |
|
56 | new_leox.push = push_position_from_leo | |
57 | run_leo_startup_node() |
|
57 | run_leo_startup_node() | |
58 |
|
58 | |||
59 | from IPython.external.simplegeneric import generic |
|
59 | from IPython.external.simplegeneric import generic | |
60 | import pprint |
|
60 | import pprint | |
61 |
|
61 | |||
62 | def es(s): |
|
62 | def es(s): | |
63 | g.es(s, tabName = 'IPython') |
|
63 | g.es(s, tabName = 'IPython') | |
64 | pass |
|
64 | pass | |
65 |
|
65 | |||
66 | @generic |
|
66 | @generic | |
67 | def format_for_leo(obj): |
|
67 | def format_for_leo(obj): | |
68 | """ Convert obj to string representiation (for editing in Leo)""" |
|
68 | """ Convert obj to string representiation (for editing in Leo)""" | |
69 | return pprint.pformat(obj) |
|
69 | return pprint.pformat(obj) | |
70 |
|
70 | |||
71 | @format_for_leo.when_type(list) |
|
71 | @format_for_leo.when_type(list) | |
72 | def format_list(obj): |
|
72 | def format_list(obj): | |
73 | return "\n".join(str(s) for s in obj) |
|
73 | return "\n".join(str(s) for s in obj) | |
74 |
|
74 | |||
75 |
|
75 | |||
76 | attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
|
76 | attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | |
77 | def valid_attribute(s): |
|
77 | def valid_attribute(s): | |
78 | return attribute_re.match(s) |
|
78 | return attribute_re.match(s) | |
79 |
|
79 | |||
80 | _rootnode = None |
|
80 | _rootnode = None | |
81 | def rootnode(): |
|
81 | def rootnode(): | |
82 | """ Get ileo root node (@ipy-root) |
|
82 | """ Get ileo root node (@ipy-root) | |
83 |
|
83 | |||
84 | if node has become invalid or has not been set, return None |
|
84 | if node has become invalid or has not been set, return None | |
85 |
|
85 | |||
86 | Note that the root is the *first* @ipy-root item found |
|
86 | Note that the root is the *first* @ipy-root item found | |
87 | """ |
|
87 | """ | |
88 | global _rootnode |
|
88 | global _rootnode | |
89 | if _rootnode is None: |
|
89 | if _rootnode is None: | |
90 | return None |
|
90 | return None | |
91 | if c.positionExists(_rootnode.p): |
|
91 | if c.positionExists(_rootnode.p): | |
92 | return _rootnode |
|
92 | return _rootnode | |
93 | _rootnode = None |
|
93 | _rootnode = None | |
94 | return None |
|
94 | return None | |
95 |
|
95 | |||
96 | def all_cells(): |
|
96 | def all_cells(): | |
97 | global _rootnode |
|
97 | global _rootnode | |
98 | d = {} |
|
98 | d = {} | |
99 | r = rootnode() |
|
99 | r = rootnode() | |
100 | if r is not None: |
|
100 | if r is not None: | |
101 | nodes = r.p.children_iter() |
|
101 | nodes = r.p.children_iter() | |
102 | else: |
|
102 | else: | |
103 | nodes = c.allNodes_iter() |
|
103 | nodes = c.allNodes_iter() | |
104 |
|
104 | |||
105 | for p in nodes: |
|
105 | for p in nodes: | |
106 | h = p.headString() |
|
106 | h = p.headString() | |
107 | if h.strip() == '@ipy-root': |
|
107 | if h.strip() == '@ipy-root': | |
108 | # update root node (found it for the first time) |
|
108 | # update root node (found it for the first time) | |
109 | _rootnode = LeoNode(p) |
|
109 | _rootnode = LeoNode(p) | |
110 | # the next recursive call will use the children of new root |
|
110 | # the next recursive call will use the children of new root | |
111 | return all_cells() |
|
111 | return all_cells() | |
112 |
|
112 | |||
113 | if h.startswith('@a '): |
|
113 | if h.startswith('@a '): | |
114 | d[h.lstrip('@a ').strip()] = p.parent().copy() |
|
114 | d[h.lstrip('@a ').strip()] = p.parent().copy() | |
115 | elif not valid_attribute(h): |
|
115 | elif not valid_attribute(h): | |
116 | continue |
|
116 | continue | |
117 | d[h] = p.copy() |
|
117 | d[h] = p.copy() | |
118 | return d |
|
118 | return d | |
119 |
|
119 | |||
120 | def eval_node(n): |
|
120 | def eval_node(n): | |
121 | body = n.b |
|
121 | body = n.b | |
122 | if not body.startswith('@cl'): |
|
122 | if not body.startswith('@cl'): | |
123 | # plain python repr node, just eval it |
|
123 | # plain python repr node, just eval it | |
124 | return ip.ev(n.b) |
|
124 | return ip.ev(n.b) | |
125 | # @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body |
|
125 | # @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body | |
126 | first, rest = body.split('\n',1) |
|
126 | first, rest = body.split('\n',1) | |
127 | tup = first.split(None, 1) |
|
127 | tup = first.split(None, 1) | |
128 | # @cl alone SPECIAL USE-> dump var to user_ns |
|
128 | # @cl alone SPECIAL USE-> dump var to user_ns | |
129 | if len(tup) == 1: |
|
129 | if len(tup) == 1: | |
130 | val = ip.ev(rest) |
|
130 | val = ip.ev(rest) | |
131 | ip.user_ns[n.h] = val |
|
131 | ip.user_ns[n.h] = val | |
132 | es("%s = %s" % (n.h, repr(val)[:20] )) |
|
132 | es("%s = %s" % (n.h, repr(val)[:20] )) | |
133 | return val |
|
133 | return val | |
134 |
|
134 | |||
135 | cl, hd = tup |
|
135 | cl, hd = tup | |
136 |
|
136 | |||
137 | xformer = ip.ev(hd.strip()) |
|
137 | xformer = ip.ev(hd.strip()) | |
138 | es('Transform w/ %s' % repr(xformer)) |
|
138 | es('Transform w/ %s' % repr(xformer)) | |
139 | return xformer(rest, n) |
|
139 | return xformer(rest, n) | |
140 |
|
140 | |||
141 | class LeoNode(object, UserDict.DictMixin): |
|
141 | class LeoNode(object, UserDict.DictMixin): | |
142 | """ Node in Leo outline |
|
142 | """ Node in Leo outline | |
143 |
|
143 | |||
144 | Most important attributes (getters/setters available: |
|
144 | Most important attributes (getters/setters available: | |
145 | .v - evaluate node, can also be alligned |
|
145 | .v - evaluate node, can also be alligned | |
146 | .b, .h - body string, headline string |
|
146 | .b, .h - body string, headline string | |
147 | .l - value as string list |
|
147 | .l - value as string list | |
148 |
|
148 | |||
149 | Also supports iteration, |
|
149 | Also supports iteration, | |
150 |
|
150 | |||
151 | setitem / getitem (indexing): |
|
151 | setitem / getitem (indexing): | |
152 | wb.foo['key'] = 12 |
|
152 | wb.foo['key'] = 12 | |
153 | assert wb.foo['key'].v == 12 |
|
153 | assert wb.foo['key'].v == 12 | |
154 |
|
154 | |||
155 | Note the asymmetry on setitem and getitem! Also other |
|
155 | Note the asymmetry on setitem and getitem! Also other | |
156 | dict methods are available. |
|
156 | dict methods are available. | |
157 |
|
157 | |||
158 | .ipush() - run push-to-ipython |
|
158 | .ipush() - run push-to-ipython | |
159 |
|
159 | |||
160 | Minibuffer command access (tab completion works): |
|
160 | Minibuffer command access (tab completion works): | |
161 |
|
161 | |||
162 | mb save-to-file |
|
162 | mb save-to-file | |
163 |
|
163 | |||
164 | """ |
|
164 | """ | |
165 | def __init__(self,p): |
|
165 | def __init__(self,p): | |
166 | self.p = p.copy() |
|
166 | self.p = p.copy() | |
167 |
|
167 | |||
168 | def __str__(self): |
|
168 | def __str__(self): | |
169 | return "<LeoNode %s>" % str(self.p) |
|
169 | return "<LeoNode %s>" % str(self.p) | |
170 |
|
170 | |||
171 | __repr__ = __str__ |
|
171 | __repr__ = __str__ | |
172 |
|
172 | |||
173 | def __get_h(self): return self.p.headString() |
|
173 | def __get_h(self): return self.p.headString() | |
174 | def __set_h(self,val): |
|
174 | def __set_h(self,val): | |
175 | print "set head",val |
|
175 | print "set head",val | |
176 | c.beginUpdate() |
|
176 | c.beginUpdate() | |
177 | try: |
|
177 | try: | |
178 | c.setHeadString(self.p,val) |
|
178 | c.setHeadString(self.p,val) | |
179 | finally: |
|
179 | finally: | |
180 | c.endUpdate() |
|
180 | c.endUpdate() | |
181 |
|
181 | |||
182 | h = property( __get_h, __set_h, doc = "Node headline string") |
|
182 | h = property( __get_h, __set_h, doc = "Node headline string") | |
183 |
|
183 | |||
184 | def __get_b(self): return self.p.bodyString() |
|
184 | def __get_b(self): return self.p.bodyString() | |
185 | def __set_b(self,val): |
|
185 | def __set_b(self,val): | |
186 | print "set body",val |
|
186 | print "set body",val | |
187 | c.beginUpdate() |
|
187 | c.beginUpdate() | |
188 | try: |
|
188 | try: | |
189 | c.setBodyString(self.p, val) |
|
189 | c.setBodyString(self.p, val) | |
190 | finally: |
|
190 | finally: | |
191 | c.endUpdate() |
|
191 | c.endUpdate() | |
192 |
|
192 | |||
193 | b = property(__get_b, __set_b, doc = "Nody body string") |
|
193 | b = property(__get_b, __set_b, doc = "Nody body string") | |
194 |
|
194 | |||
195 | def __set_val(self, val): |
|
195 | def __set_val(self, val): | |
196 | self.b = format_for_leo(val) |
|
196 | self.b = format_for_leo(val) | |
197 |
|
197 | |||
198 | v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value") |
|
198 | v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value") | |
199 |
|
199 | |||
200 | def __set_l(self,val): |
|
200 | def __set_l(self,val): | |
201 | self.b = '\n'.join(val ) |
|
201 | self.b = '\n'.join(val ) | |
202 | l = property(lambda self : IPython.genutils.SList(self.b.splitlines()), |
|
202 | l = property(lambda self : IPython.genutils.SList(self.b.splitlines()), | |
203 | __set_l, doc = "Node value as string list") |
|
203 | __set_l, doc = "Node value as string list") | |
204 |
|
204 | |||
205 | def __iter__(self): |
|
205 | def __iter__(self): | |
206 | """ Iterate through nodes direct children """ |
|
206 | """ Iterate through nodes direct children """ | |
207 |
|
207 | |||
208 | return (LeoNode(p) for p in self.p.children_iter()) |
|
208 | return (LeoNode(p) for p in self.p.children_iter()) | |
209 |
|
209 | |||
210 | def __children(self): |
|
210 | def __children(self): | |
211 | d = {} |
|
211 | d = {} | |
212 | for child in self: |
|
212 | for child in self: | |
213 | head = child.h |
|
213 | head = child.h | |
214 | tup = head.split(None,1) |
|
214 | tup = head.split(None,1) | |
215 | if len(tup) > 1 and tup[0] == '@k': |
|
215 | if len(tup) > 1 and tup[0] == '@k': | |
216 | d[tup[1]] = child |
|
216 | d[tup[1]] = child | |
217 | continue |
|
217 | continue | |
218 |
|
218 | |||
219 | if not valid_attribute(head): |
|
219 | if not valid_attribute(head): | |
220 | d[head] = child |
|
220 | d[head] = child | |
221 | continue |
|
221 | continue | |
222 | return d |
|
222 | return d | |
223 | def keys(self): |
|
223 | def keys(self): | |
224 | d = self.__children() |
|
224 | d = self.__children() | |
225 | return d.keys() |
|
225 | return d.keys() | |
226 | def __getitem__(self, key): |
|
226 | def __getitem__(self, key): | |
227 | """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff' |
|
227 | """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff' | |
228 |
|
228 | |||
229 | If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well |
|
229 | If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well | |
230 | """ |
|
230 | """ | |
231 | key = str(key) |
|
231 | key = str(key) | |
232 | d = self.__children() |
|
232 | d = self.__children() | |
233 | return d[key] |
|
233 | return d[key] | |
234 | def __setitem__(self, key, val): |
|
234 | def __setitem__(self, key, val): | |
235 | """ You can do wb.foo['My Stuff'] = 12 to create children |
|
235 | """ You can do wb.foo['My Stuff'] = 12 to create children | |
236 |
|
236 | |||
237 | This will create 'My Stuff' as a child of foo (if it does not exist), and |
|
237 | This will create 'My Stuff' as a child of foo (if it does not exist), and | |
238 | do .v = 12 assignment. |
|
238 | do .v = 12 assignment. | |
239 |
|
239 | |||
240 | Exception: |
|
240 | Exception: | |
241 |
|
241 | |||
242 | wb.foo['bar'] = 12 |
|
242 | wb.foo['bar'] = 12 | |
243 |
|
243 | |||
244 | will create a child with headline '@k bar', because bar is a valid python name |
|
244 | will create a child with headline '@k bar', because bar is a valid python name | |
245 | and we don't want to crowd the WorkBook namespace with (possibly numerous) entries |
|
245 | and we don't want to crowd the WorkBook namespace with (possibly numerous) entries | |
246 | """ |
|
246 | """ | |
247 | key = str(key) |
|
247 | key = str(key) | |
248 | d = self.__children() |
|
248 | d = self.__children() | |
249 | if key in d: |
|
249 | if key in d: | |
250 | d[key].v = val |
|
250 | d[key].v = val | |
251 | return |
|
251 | return | |
252 |
|
252 | |||
253 | if not valid_attribute(key): |
|
253 | if not valid_attribute(key): | |
254 | head = key |
|
254 | head = key | |
255 | else: |
|
255 | else: | |
256 | head = '@k ' + key |
|
256 | head = '@k ' + key | |
257 | p = c.createLastChildNode(self.p, head, '') |
|
257 | p = c.createLastChildNode(self.p, head, '') | |
258 | LeoNode(p).v = val |
|
258 | LeoNode(p).v = val | |
259 |
|
259 | |||
260 | def ipush(self): |
|
260 | def ipush(self): | |
261 | """ Does push-to-ipython on the node """ |
|
261 | """ Does push-to-ipython on the node """ | |
262 | push_from_leo(self) |
|
262 | push_from_leo(self) | |
263 |
|
263 | |||
264 | def go(self): |
|
264 | def go(self): | |
265 | """ Set node as current node (to quickly see it in Outline) """ |
|
265 | """ Set node as current node (to quickly see it in Outline) """ | |
266 | c.beginUpdate() |
|
266 | c.beginUpdate() | |
267 | try: |
|
267 | try: | |
268 | c.setCurrentPosition(self.p) |
|
268 | c.setCurrentPosition(self.p) | |
269 | finally: |
|
269 | finally: | |
270 | c.endUpdate() |
|
270 | c.endUpdate() | |
271 |
|
271 | |||
272 | def script(self): |
|
272 | def script(self): | |
273 | """ Method to get the 'tangled' contents of the node |
|
273 | """ Method to get the 'tangled' contents of the node | |
274 |
|
274 | |||
275 | (parse @others, << section >> references etc.) |
|
275 | (parse @others, << section >> references etc.) | |
276 | """ |
|
276 | """ | |
277 | return g.getScript(c,self.p,useSelectedText=False,useSentinels=False) |
|
277 | return g.getScript(c,self.p,useSelectedText=False,useSentinels=False) | |
278 |
|
278 | |||
279 | def __get_uA(self): |
|
279 | def __get_uA(self): | |
280 | p = self.p |
|
280 | p = self.p | |
281 | # Create the uA if necessary. |
|
281 | # Create the uA if necessary. | |
282 | if not hasattr(p.v.t,'unknownAttributes'): |
|
282 | if not hasattr(p.v.t,'unknownAttributes'): | |
283 | p.v.t.unknownAttributes = {} |
|
283 | p.v.t.unknownAttributes = {} | |
284 |
|
284 | |||
285 | d = p.v.t.unknownAttributes.setdefault('ipython', {}) |
|
285 | d = p.v.t.unknownAttributes.setdefault('ipython', {}) | |
286 | return d |
|
286 | return d | |
287 |
|
287 | |||
288 | uA = property(__get_uA, doc = "Access persistent unknownAttributes of node") |
|
288 | uA = property(__get_uA, doc = "Access persistent unknownAttributes of node") | |
289 |
|
289 | |||
290 |
|
290 | |||
291 | class LeoWorkbook: |
|
291 | class LeoWorkbook: | |
292 | """ class for 'advanced' node access |
|
292 | """ class for 'advanced' node access | |
293 |
|
293 | |||
294 | Has attributes for all "discoverable" nodes. Node is discoverable if it |
|
294 | Has attributes for all "discoverable" nodes. Node is discoverable if it | |
295 | either |
|
295 | either | |
296 |
|
296 | |||
297 | - has a valid python name (Foo, bar_12) |
|
297 | - has a valid python name (Foo, bar_12) | |
298 | - is a parent of an anchor node (if it has a child '@a foo', it is visible as foo) |
|
298 | - is a parent of an anchor node (if it has a child '@a foo', it is visible as foo) | |
299 |
|
299 | |||
300 | """ |
|
300 | """ | |
301 | def __getattr__(self, key): |
|
301 | def __getattr__(self, key): | |
302 | if key.startswith('_') or key == 'trait_names' or not valid_attribute(key): |
|
302 | if key.startswith('_') or key == 'trait_names' or not valid_attribute(key): | |
303 | raise AttributeError |
|
303 | raise AttributeError | |
304 | cells = all_cells() |
|
304 | cells = all_cells() | |
305 | p = cells.get(key, None) |
|
305 | p = cells.get(key, None) | |
306 | if p is None: |
|
306 | if p is None: | |
307 | return add_var(key) |
|
307 | return add_var(key) | |
308 |
|
308 | |||
309 | return LeoNode(p) |
|
309 | return LeoNode(p) | |
310 |
|
310 | |||
311 | def __str__(self): |
|
311 | def __str__(self): | |
312 | return "<LeoWorkbook>" |
|
312 | return "<LeoWorkbook>" | |
313 | def __setattr__(self,key, val): |
|
313 | def __setattr__(self,key, val): | |
314 | raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val)) |
|
314 | raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val)) | |
315 |
|
315 | |||
316 | __repr__ = __str__ |
|
316 | __repr__ = __str__ | |
317 |
|
317 | |||
318 | def __iter__(self): |
|
318 | def __iter__(self): | |
319 | """ Iterate all (even non-exposed) nodes """ |
|
319 | """ Iterate all (even non-exposed) nodes """ | |
320 | cells = all_cells() |
|
320 | cells = all_cells() | |
321 | return (LeoNode(p) for p in c.allNodes_iter()) |
|
321 | return (LeoNode(p) for p in c.allNodes_iter()) | |
322 |
|
322 | |||
323 | current = property(lambda self: LeoNode(c.currentPosition()), doc = "Currently selected node") |
|
323 | current = property(lambda self: LeoNode(c.currentPosition()), doc = "Currently selected node") | |
324 |
|
324 | |||
325 | def match_h(self, regex): |
|
325 | def match_h(self, regex): | |
326 | cmp = re.compile(regex) |
|
326 | cmp = re.compile(regex) | |
327 | for node in self: |
|
327 | for node in self: | |
328 | if re.match(cmp, node.h, re.IGNORECASE): |
|
328 | if re.match(cmp, node.h, re.IGNORECASE): | |
329 | yield node |
|
329 | yield node | |
330 | return |
|
330 | return | |
331 |
|
331 | |||
332 | @IPython.generics.complete_object.when_type(LeoWorkbook) |
|
332 | @IPython.generics.complete_object.when_type(LeoWorkbook) | |
333 | def workbook_complete(obj, prev): |
|
333 | def workbook_complete(obj, prev): | |
334 | return all_cells().keys() + [s for s in prev if not s.startswith('_')] |
|
334 | return all_cells().keys() + [s for s in prev if not s.startswith('_')] | |
335 |
|
335 | |||
336 |
|
336 | |||
337 | def add_var(varname): |
|
337 | def add_var(varname): | |
338 | c.beginUpdate() |
|
338 | c.beginUpdate() | |
339 | r = rootnode() |
|
339 | r = rootnode() | |
340 | try: |
|
340 | try: | |
341 | if r is None: |
|
341 | if r is None: | |
342 | p2 = g.findNodeAnywhere(c,varname) |
|
342 | p2 = g.findNodeAnywhere(c,varname) | |
343 | else: |
|
343 | else: | |
344 | p2 = g.findNodeInChildren(c, r.p, varname) |
|
344 | p2 = g.findNodeInChildren(c, r.p, varname) | |
345 | if p2: |
|
345 | if p2: | |
346 | return LeoNode(p2) |
|
346 | return LeoNode(p2) | |
347 |
|
347 | |||
348 | if r is not None: |
|
348 | if r is not None: | |
349 | p2 = r.p.insertAsLastChild() |
|
349 | p2 = r.p.insertAsLastChild() | |
350 |
|
350 | |||
351 | else: |
|
351 | else: | |
352 | p2 = c.currentPosition().insertAfter() |
|
352 | p2 = c.currentPosition().insertAfter() | |
353 |
|
353 | |||
354 | c.setHeadString(p2,varname) |
|
354 | c.setHeadString(p2,varname) | |
355 | return LeoNode(p2) |
|
355 | return LeoNode(p2) | |
356 | finally: |
|
356 | finally: | |
357 | c.endUpdate() |
|
357 | c.endUpdate() | |
358 |
|
358 | |||
359 | def add_file(self,fname): |
|
359 | def add_file(self,fname): | |
360 | p2 = c.currentPosition().insertAfter() |
|
360 | p2 = c.currentPosition().insertAfter() | |
361 |
|
361 | |||
362 | push_from_leo = CommandChainDispatcher() |
|
362 | push_from_leo = CommandChainDispatcher() | |
363 |
|
363 | |||
364 | def expose_ileo_push(f, prio = 0): |
|
364 | def expose_ileo_push(f, prio = 0): | |
365 | push_from_leo.add(f, prio) |
|
365 | push_from_leo.add(f, prio) | |
366 |
|
366 | |||
367 | def push_ipython_script(node): |
|
367 | def push_ipython_script(node): | |
368 | """ Execute the node body in IPython, as if it was entered in interactive prompt """ |
|
368 | """ Execute the node body in IPython, as if it was entered in interactive prompt """ | |
369 | c.beginUpdate() |
|
369 | c.beginUpdate() | |
370 | try: |
|
370 | try: | |
371 | ohist = ip.IP.output_hist |
|
371 | ohist = ip.IP.output_hist | |
372 | hstart = len(ip.IP.input_hist) |
|
372 | hstart = len(ip.IP.input_hist) | |
373 | script = node.script() |
|
373 | script = node.script() | |
374 |
|
374 | |||
375 | script = g.splitLines(script + '\n') |
|
375 | script = g.splitLines(script + '\n') | |
376 | ip.user_ns['_p'] = node |
|
376 | ip.user_ns['_p'] = node | |
377 | ip.runlines(script) |
|
377 | ip.runlines(script) | |
378 | ip.user_ns.pop('_p',None) |
|
378 | ip.user_ns.pop('_p',None) | |
379 |
|
379 | |||
380 | has_output = False |
|
380 | has_output = False | |
381 | for idx in range(hstart,len(ip.IP.input_hist)): |
|
381 | for idx in range(hstart,len(ip.IP.input_hist)): | |
382 | val = ohist.get(idx,None) |
|
382 | val = ohist.get(idx,None) | |
383 | if val is None: |
|
383 | if val is None: | |
384 | continue |
|
384 | continue | |
385 | has_output = True |
|
385 | has_output = True | |
386 | inp = ip.IP.input_hist[idx] |
|
386 | inp = ip.IP.input_hist[idx] | |
387 | if inp.strip(): |
|
387 | if inp.strip(): | |
388 | es('In: %s' % (inp[:40], )) |
|
388 | es('In: %s' % (inp[:40], )) | |
389 |
|
389 | |||
390 | es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40))) |
|
390 | es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40))) | |
391 |
|
391 | |||
392 | if not has_output: |
|
392 | if not has_output: | |
393 | es('ipy run: %s (%d LL)' %( node.h,len(script))) |
|
393 | es('ipy run: %s (%d LL)' %( node.h,len(script))) | |
394 | finally: |
|
394 | finally: | |
395 | c.endUpdate() |
|
395 | c.endUpdate() | |
396 |
|
396 | |||
397 |
|
397 | |||
398 | def eval_body(body): |
|
398 | def eval_body(body): | |
399 | try: |
|
399 | try: | |
400 | val = ip.ev(body) |
|
400 | val = ip.ev(body) | |
401 | except: |
|
401 | except: | |
402 | # just use stringlist if it's not completely legal python expression |
|
402 | # just use stringlist if it's not completely legal python expression | |
403 | val = IPython.genutils.SList(body.splitlines()) |
|
403 | val = IPython.genutils.SList(body.splitlines()) | |
404 | return val |
|
404 | return val | |
405 |
|
405 | |||
406 | def push_plain_python(node): |
|
406 | def push_plain_python(node): | |
407 | if not node.h.endswith('P'): |
|
407 | if not node.h.endswith('P'): | |
408 | raise TryNext |
|
408 | raise TryNext | |
409 | script = node.script() |
|
409 | script = node.script() | |
410 | lines = script.count('\n') |
|
410 | lines = script.count('\n') | |
411 | try: |
|
411 | try: | |
412 | exec script in ip.user_ns |
|
412 | exec script in ip.user_ns | |
413 | except: |
|
413 | except: | |
414 | print " -- Exception in script:\n"+script + "\n --" |
|
414 | print " -- Exception in script:\n"+script + "\n --" | |
415 | raise |
|
415 | raise | |
416 | es('ipy plain: %s (%d LL)' % (node.h,lines)) |
|
416 | es('ipy plain: %s (%d LL)' % (node.h,lines)) | |
417 |
|
417 | |||
418 |
|
418 | |||
419 | def push_cl_node(node): |
|
419 | def push_cl_node(node): | |
420 | """ If node starts with @cl, eval it |
|
420 | """ If node starts with @cl, eval it | |
421 |
|
421 | |||
422 | The result is put as last child of @ipy-results node, if it exists |
|
422 | The result is put as last child of @ipy-results node, if it exists | |
423 | """ |
|
423 | """ | |
424 | if not node.b.startswith('@cl'): |
|
424 | if not node.b.startswith('@cl'): | |
425 | raise TryNext |
|
425 | raise TryNext | |
426 |
|
426 | |||
427 | p2 = g.findNodeAnywhere(c,'@ipy-results') |
|
427 | p2 = g.findNodeAnywhere(c,'@ipy-results') | |
428 | val = node.v |
|
428 | val = node.v | |
429 | if p2: |
|
429 | if p2: | |
430 | es("=> @ipy-results") |
|
430 | es("=> @ipy-results") | |
431 | LeoNode(p2).v = val |
|
431 | LeoNode(p2).v = val | |
432 | es(val) |
|
432 | es(val) | |
433 |
|
433 | |||
434 | def push_ev_node(node): |
|
434 | def push_ev_node(node): | |
435 | """ If headline starts with @ev, eval it and put result in body """ |
|
435 | """ If headline starts with @ev, eval it and put result in body """ | |
436 | if not node.h.startswith('@ev '): |
|
436 | if not node.h.startswith('@ev '): | |
437 | raise TryNext |
|
437 | raise TryNext | |
438 | expr = node.h.lstrip('@ev ') |
|
438 | expr = node.h.lstrip('@ev ') | |
439 | es('ipy eval ' + expr) |
|
439 | es('ipy eval ' + expr) | |
440 | res = ip.ev(expr) |
|
440 | res = ip.ev(expr) | |
441 | node.v = res |
|
441 | node.v = res | |
442 |
|
442 | |||
443 |
|
443 | |||
444 | def push_position_from_leo(p): |
|
444 | def push_position_from_leo(p): | |
445 | push_from_leo(LeoNode(p)) |
|
445 | push_from_leo(LeoNode(p)) | |
446 |
|
446 | |||
447 | @generic |
|
447 | @generic | |
448 | def edit_object_in_leo(obj, varname): |
|
448 | def edit_object_in_leo(obj, varname): | |
449 | """ Make it @cl node so it can be pushed back directly by alt+I """ |
|
449 | """ Make it @cl node so it can be pushed back directly by alt+I """ | |
450 | node = add_var(varname) |
|
450 | node = add_var(varname) | |
451 | formatted = format_for_leo(obj) |
|
451 | formatted = format_for_leo(obj) | |
452 | if not formatted.startswith('@cl'): |
|
452 | if not formatted.startswith('@cl'): | |
453 | formatted = '@cl\n' + formatted |
|
453 | formatted = '@cl\n' + formatted | |
454 | node.b = formatted |
|
454 | node.b = formatted | |
455 | node.go() |
|
455 | node.go() | |
456 |
|
456 | |||
457 | @edit_object_in_leo.when_type(IPython.macro.Macro) |
|
457 | @edit_object_in_leo.when_type(IPython.macro.Macro) | |
458 | def edit_macro(obj,varname): |
|
458 | def edit_macro(obj,varname): | |
459 | bod = '_ip.defmacro("""\\\n' + obj.value + '""")' |
|
459 | bod = '_ip.defmacro("""\\\n' + obj.value + '""")' | |
460 | node = add_var('Macro_' + varname) |
|
460 | node = add_var('Macro_' + varname) | |
461 | node.b = bod |
|
461 | node.b = bod | |
462 | node.go() |
|
462 | node.go() | |
463 |
|
463 | |||
464 | def get_history(hstart = 0): |
|
464 | def get_history(hstart = 0): | |
465 | res = [] |
|
465 | res = [] | |
466 | ohist = ip.IP.output_hist |
|
466 | ohist = ip.IP.output_hist | |
467 |
|
467 | |||
468 | for idx in range(hstart, len(ip.IP.input_hist)): |
|
468 | for idx in range(hstart, len(ip.IP.input_hist)): | |
469 | val = ohist.get(idx,None) |
|
469 | val = ohist.get(idx,None) | |
470 | has_output = True |
|
470 | has_output = True | |
471 | inp = ip.IP.input_hist_raw[idx] |
|
471 | inp = ip.IP.input_hist_raw[idx] | |
472 | if inp.strip(): |
|
472 | if inp.strip(): | |
473 | res.append('In [%d]: %s' % (idx, inp)) |
|
473 | res.append('In [%d]: %s' % (idx, inp)) | |
474 | if val: |
|
474 | if val: | |
475 | res.append(pprint.pformat(val)) |
|
475 | res.append(pprint.pformat(val)) | |
476 | res.append('\n') |
|
476 | res.append('\n') | |
477 | return ''.join(res) |
|
477 | return ''.join(res) | |
478 |
|
478 | |||
479 |
|
479 | |||
480 | def lee_f(self,s): |
|
480 | def lee_f(self,s): | |
481 | """ Open file(s)/objects in Leo |
|
481 | """ Open file(s)/objects in Leo | |
482 |
|
482 | |||
483 | - %lee hist -> open full session history in leo |
|
483 | - %lee hist -> open full session history in leo | |
484 | - Takes an object |
|
484 | - Takes an object | |
485 | - Takes an mglob pattern, e.g. '%lee *.cpp' or %leo 'rec:*.cpp' |
|
485 | - Takes an mglob pattern, e.g. '%lee *.cpp' or %leo 'rec:*.cpp' | |
486 | """ |
|
486 | """ | |
487 | import os |
|
487 | import os | |
488 |
|
488 | |||
489 | c.beginUpdate() |
|
489 | c.beginUpdate() | |
490 | try: |
|
490 | try: | |
491 | if s == 'hist': |
|
491 | if s == 'hist': | |
492 | wb.ipython_history.b = get_history() |
|
492 | wb.ipython_history.b = get_history() | |
493 | wb.ipython_history.go() |
|
493 | wb.ipython_history.go() | |
494 | return |
|
494 | return | |
495 |
|
495 | |||
496 |
|
496 | |||
497 |
|
497 | |||
498 | # try editing the object directly |
|
498 | # try editing the object directly | |
499 | obj = ip.user_ns.get(s, None) |
|
499 | obj = ip.user_ns.get(s, None) | |
500 | if obj is not None: |
|
500 | if obj is not None: | |
501 | edit_object_in_leo(obj,s) |
|
501 | edit_object_in_leo(obj,s) | |
502 | return |
|
502 | return | |
503 |
|
503 | |||
504 | # if it's not object, it's a file name / mglob pattern |
|
504 | # if it's not object, it's a file name / mglob pattern | |
505 | from IPython.external import mglob |
|
505 | from IPython.external import mglob | |
506 |
|
506 | |||
507 | files = (os.path.abspath(f) for f in mglob.expand(s)) |
|
507 | files = (os.path.abspath(f) for f in mglob.expand(s)) | |
508 | for fname in files: |
|
508 | for fname in files: | |
509 | p = g.findNodeAnywhere(c,'@auto ' + fname) |
|
509 | p = g.findNodeAnywhere(c,'@auto ' + fname) | |
510 | if not p: |
|
510 | if not p: | |
511 | p = c.currentPosition().insertAfter() |
|
511 | p = c.currentPosition().insertAfter() | |
512 |
|
512 | |||
513 | p.setHeadString('@auto ' + fname) |
|
513 | p.setHeadString('@auto ' + fname) | |
514 | if os.path.isfile(fname): |
|
514 | if os.path.isfile(fname): | |
515 | c.setBodyString(p,open(fname).read()) |
|
515 | c.setBodyString(p,open(fname).read()) | |
516 | c.selectPosition(p) |
|
516 | c.selectPosition(p) | |
517 | print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes" |
|
517 | print "Editing file(s), press ctrl+shift+w in Leo to write @auto nodes" | |
518 | finally: |
|
518 | finally: | |
519 | c.endUpdate() |
|
519 | c.endUpdate() | |
520 |
|
520 | |||
521 |
|
521 | |||
522 |
|
522 | |||
523 | def leoref_f(self,s): |
|
523 | def leoref_f(self,s): | |
524 | """ Quick reference for ILeo """ |
|
524 | """ Quick reference for ILeo """ | |
525 | import textwrap |
|
525 | import textwrap | |
526 | print textwrap.dedent("""\ |
|
526 | print textwrap.dedent("""\ | |
527 | %leoe file/object - open file / object in leo |
|
527 | %leoe file/object - open file / object in leo | |
528 | wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo') |
|
528 | wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo') | |
529 | wb.foo.v = 12 - assign to body of node foo |
|
529 | wb.foo.v = 12 - assign to body of node foo | |
530 | wb.foo.b - read or write the body of node foo |
|
530 | wb.foo.b - read or write the body of node foo | |
531 | wb.foo.l - body of node foo as string list |
|
531 | wb.foo.l - body of node foo as string list | |
532 |
|
532 | |||
533 | for el in wb.foo: |
|
533 | for el in wb.foo: | |
534 | print el.v |
|
534 | print el.v | |
535 |
|
535 | |||
536 | """ |
|
536 | """ | |
537 | ) |
|
537 | ) | |
538 |
|
538 | |||
539 |
|
539 | |||
540 |
|
540 | |||
541 | def mb_f(self, arg): |
|
541 | def mb_f(self, arg): | |
542 | """ Execute leo minibuffer commands |
|
542 | """ Execute leo minibuffer commands | |
543 |
|
543 | |||
544 | Example: |
|
544 | Example: | |
545 | mb save-to-file |
|
545 | mb save-to-file | |
546 | """ |
|
546 | """ | |
547 | c.executeMinibufferCommand(arg) |
|
547 | c.executeMinibufferCommand(arg) | |
548 |
|
548 | |||
549 | def mb_completer(self,event): |
|
549 | def mb_completer(self,event): | |
550 | """ Custom completer for minibuffer """ |
|
550 | """ Custom completer for minibuffer """ | |
551 | cmd_param = event.line.split() |
|
551 | cmd_param = event.line.split() | |
552 | if event.line.endswith(' '): |
|
552 | if event.line.endswith(' '): | |
553 | cmd_param.append('') |
|
553 | cmd_param.append('') | |
554 | if len(cmd_param) > 2: |
|
554 | if len(cmd_param) > 2: | |
555 | return ip.IP.Completer.file_matches(event.symbol) |
|
555 | return ip.IP.Completer.file_matches(event.symbol) | |
556 | cmds = c.commandsDict.keys() |
|
556 | cmds = c.commandsDict.keys() | |
557 | cmds.sort() |
|
557 | cmds.sort() | |
558 | return cmds |
|
558 | return cmds | |
559 |
|
559 | |||
560 | def show_welcome(): |
|
560 | def show_welcome(): | |
561 | print "------------------" |
|
561 | print "------------------" | |
562 | print "Welcome to Leo-enabled IPython session!" |
|
562 | print "Welcome to Leo-enabled IPython session!" | |
563 | print "Try %leoref for quick reference." |
|
563 | print "Try %leoref for quick reference." | |
564 | import IPython.platutils |
|
564 | import IPython.platutils | |
565 | IPython.platutils.set_term_title('ILeo') |
|
565 | IPython.platutils.set_term_title('ILeo') | |
566 | IPython.platutils.freeze_term_title() |
|
566 | IPython.platutils.freeze_term_title() | |
567 |
|
567 | |||
568 | def run_leo_startup_node(): |
|
568 | def run_leo_startup_node(): | |
569 | p = g.findNodeAnywhere(c,'@ipy-startup') |
|
569 | p = g.findNodeAnywhere(c,'@ipy-startup') | |
570 | if p: |
|
570 | if p: | |
571 | print "Running @ipy-startup nodes" |
|
571 | print "Running @ipy-startup nodes" | |
572 | for n in LeoNode(p): |
|
572 | for n in LeoNode(p): | |
573 | push_from_leo(n) |
|
573 | push_from_leo(n) | |
574 |
|
574 | |||
575 |
|
575 |
@@ -1,43 +1,43 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | Extension for printing Numeric Arrays in flexible ways. |
|
3 | Extension for printing Numeric Arrays in flexible ways. | |
4 | """ |
|
4 | """ | |
5 |
|
5 | |||
6 | from Numeric import ArrayType |
|
6 | from Numeric import ArrayType | |
7 |
|
7 | |||
8 | def num_display(self,arg): |
|
8 | def num_display(self,arg): | |
9 | """Display method for printing which treats Numeric arrays specially. |
|
9 | """Display method for printing which treats Numeric arrays specially. | |
10 | """ |
|
10 | """ | |
11 |
|
11 | |||
12 | # Non-numpy variables are printed using the system default |
|
12 | # Non-numpy variables are printed using the system default | |
13 | if type(arg) != ArrayType: |
|
13 | if type(arg) != ArrayType: | |
14 | self._display(arg) |
|
14 | self._display(arg) | |
15 | return |
|
15 | return | |
16 | # Otherwise, we do work. |
|
16 | # Otherwise, we do work. | |
17 | format = __IPYTHON__.runtime_rc.numarray_print_format |
|
17 | format = __IPYTHON__.runtime_rc.numarray_print_format | |
18 | print 'NumPy array, format:',format |
|
18 | print 'NumPy array, format:',format | |
19 | # Here is where all the printing logic needs to be implemented |
|
19 | # Here is where all the printing logic needs to be implemented | |
20 | print arg # nothing yet :) |
|
20 | print arg # nothing yet :) | |
21 |
|
21 | |||
22 |
|
22 | |||
23 | def magic_format(self,parameter_s=''): |
|
23 | def magic_format(self,parameter_s=''): | |
24 | """Specifies format of numerical output. |
|
24 | """Specifies format of numerical output. | |
25 |
|
25 | |||
26 | This command is similar to Ocave's format command. |
|
26 | This command is similar to Ocave's format command. | |
27 | """ |
|
27 | """ | |
28 |
|
28 | |||
29 | valid_formats = ['long','short'] |
|
29 | valid_formats = ['long','short'] | |
30 |
|
30 | |||
31 | if parameter_s in valid_formats: |
|
31 | if parameter_s in valid_formats: | |
32 | self.runtime_rc.numarray_print_format = parameter_s |
|
32 | self.runtime_rc.numarray_print_format = parameter_s | |
33 | print 'Numeric output format is now:',parameter_s |
|
33 | print 'Numeric output format is now:',parameter_s | |
34 | else: |
|
34 | else: | |
35 | print 'Invalid format:',parameter_s |
|
35 | print 'Invalid format:',parameter_s | |
36 | print 'Valid formats:',valid_formats |
|
36 | print 'Valid formats:',valid_formats | |
37 |
|
37 | |||
38 | # setup default format |
|
38 | # setup default format | |
39 | __IPYTHON__.runtime_rc.numarray_print_format = 'long' |
|
39 | __IPYTHON__.runtime_rc.numarray_print_format = 'long' | |
40 |
|
40 | |||
41 | # Bind our new functions to the interpreter |
|
41 | # Bind our new functions to the interpreter | |
42 | __IPYTHON__.__class__.magic_format = magic_format |
|
42 | __IPYTHON__.__class__.magic_format = magic_format | |
43 | __IPYTHON__.hooks.display = num_display |
|
43 | __IPYTHON__.hooks.display = num_display |
@@ -1,43 +1,43 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 $""" |
|
8 | $Id: FakeModule.py 2754 2007-09-09 10:16:59Z fperez $""" | |
9 |
|
9 | |||
10 | #***************************************************************************** |
|
10 | #***************************************************************************** | |
11 | # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu> |
|
11 | # Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu> | |
12 | # |
|
12 | # | |
13 | # 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 | |
14 | # the file COPYING, distributed as part of this software. |
|
14 | # the file COPYING, distributed as part of this software. | |
15 | #***************************************************************************** |
|
15 | #***************************************************************************** | |
16 |
|
16 | |||
17 | import types |
|
17 | import types | |
18 |
|
18 | |||
19 | class FakeModule(types.ModuleType): |
|
19 | class FakeModule(types.ModuleType): | |
20 | """Simple class with attribute access to fake a module. |
|
20 | """Simple class with attribute access to fake a module. | |
21 |
|
21 | |||
22 | This is not meant to replace a module, but to allow inserting a fake |
|
22 | 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 |
|
23 | module in sys.modules so that systems which rely on run-time module | |
24 | importing (like shelve and pickle) work correctly in interactive IPython |
|
24 | importing (like shelve and pickle) work correctly in interactive IPython | |
25 | sessions. |
|
25 | sessions. | |
26 |
|
26 | |||
27 | Do NOT use this code for anything other than this IPython private hack.""" |
|
27 | Do NOT use this code for anything other than this IPython private hack.""" | |
28 |
|
28 | |||
29 | def __init__(self,adict=None): |
|
29 | def __init__(self,adict=None): | |
30 |
|
30 | |||
31 | # tmp to force __dict__ instance creation, else self.__dict__ fails |
|
31 | # tmp to force __dict__ instance creation, else self.__dict__ fails | |
32 | self.__iptmp = None |
|
32 | self.__iptmp = None | |
33 |
|
33 | |||
34 | # It seems pydoc (and perhaps others) needs any module instance to |
|
34 | # It seems pydoc (and perhaps others) needs any module instance to | |
35 | # implement a __nonzero__ method, so we add it if missing: |
|
35 | # implement a __nonzero__ method, so we add it if missing: | |
36 | self.__dict__.setdefault('__nonzero__',lambda : True) |
|
36 | self.__dict__.setdefault('__nonzero__',lambda : True) | |
37 | self.__dict__.setdefault('__file__',__file__) |
|
37 | self.__dict__.setdefault('__file__',__file__) | |
38 |
|
38 | |||
39 | # cleanup our temp trick |
|
39 | # cleanup our temp trick | |
40 | del self.__iptmp |
|
40 | del self.__iptmp | |
41 |
|
41 | |||
42 | if adict is not None: |
|
42 | if adict is not None: | |
43 | self.__dict__.update(adict) |
|
43 | self.__dict__.update(adict) |
This diff has been collapsed as it changes many lines, (1332 lines changed) Show them Hide them | |||||
@@ -1,666 +1,666 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 $""" |
|
16 | $Id: Gnuplot2.py 1210 2006-03-13 01:19:31Z fperez $""" | |
17 |
|
17 | |||
18 | import cStringIO |
|
18 | import cStringIO | |
19 | import os |
|
19 | import os | |
20 | import string |
|
20 | import string | |
21 | import sys |
|
21 | import sys | |
22 | import tempfile |
|
22 | import tempfile | |
23 | import time |
|
23 | import time | |
24 | import types |
|
24 | import types | |
25 |
|
25 | |||
26 | import Gnuplot as Gnuplot_ori |
|
26 | import Gnuplot as Gnuplot_ori | |
27 | import Numeric |
|
27 | import Numeric | |
28 |
|
28 | |||
29 | from IPython.genutils import popkey,xsys |
|
29 | from IPython.genutils import popkey,xsys | |
30 |
|
30 | |||
31 | # needed by hardcopy(): |
|
31 | # needed by hardcopy(): | |
32 | gp = Gnuplot_ori.gp |
|
32 | gp = Gnuplot_ori.gp | |
33 |
|
33 | |||
34 | # Patch for Gnuplot.py 1.6 compatibility. |
|
34 | # Patch for Gnuplot.py 1.6 compatibility. | |
35 | # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz> |
|
35 | # Thanks to Hayden Callow <h.callow@elec.canterbury.ac.nz> | |
36 | try: |
|
36 | try: | |
37 | OptionException = Gnuplot_ori.PlotItems.OptionException |
|
37 | OptionException = Gnuplot_ori.PlotItems.OptionException | |
38 | except AttributeError: |
|
38 | except AttributeError: | |
39 | OptionException = Gnuplot_ori.Errors.OptionError |
|
39 | OptionException = Gnuplot_ori.Errors.OptionError | |
40 |
|
40 | |||
41 | # exhibit a similar interface to Gnuplot so it can be somewhat drop-in |
|
41 | # exhibit a similar interface to Gnuplot so it can be somewhat drop-in | |
42 | Data = Gnuplot_ori.Data |
|
42 | Data = Gnuplot_ori.Data | |
43 | Func = Gnuplot_ori.Func |
|
43 | Func = Gnuplot_ori.Func | |
44 | GridData = Gnuplot_ori.GridData |
|
44 | GridData = Gnuplot_ori.GridData | |
45 | PlotItem = Gnuplot_ori.PlotItem |
|
45 | PlotItem = Gnuplot_ori.PlotItem | |
46 | PlotItems = Gnuplot_ori.PlotItems |
|
46 | PlotItems = Gnuplot_ori.PlotItems | |
47 |
|
47 | |||
48 | # Modify some of Gnuplot's functions with improved versions (or bugfixed, in |
|
48 | # 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 |
|
49 | # hardcopy's case). In order to preserve the docstrings at runtime, I've | |
50 | # copied them from the original code. |
|
50 | # copied them from the original code. | |
51 |
|
51 | |||
52 | # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit |
|
52 | # After some significant changes in v 1.7 of Gnuplot.py, we need to do a bit | |
53 | # of version checking. |
|
53 | # of version checking. | |
54 |
|
54 | |||
55 | if Gnuplot_ori.__version__ <= '1.6': |
|
55 | if Gnuplot_ori.__version__ <= '1.6': | |
56 | _BaseFileItem = PlotItems.File |
|
56 | _BaseFileItem = PlotItems.File | |
57 | _BaseTempFileItem = PlotItems.TempFile |
|
57 | _BaseTempFileItem = PlotItems.TempFile | |
58 |
|
58 | |||
59 | # Fix the File class to add the 'index' option for Gnuplot versions < 1.7 |
|
59 | # Fix the File class to add the 'index' option for Gnuplot versions < 1.7 | |
60 | class File(_BaseFileItem): |
|
60 | class File(_BaseFileItem): | |
61 |
|
61 | |||
62 | _option_list = _BaseFileItem._option_list.copy() |
|
62 | _option_list = _BaseFileItem._option_list.copy() | |
63 | _option_list.update({ |
|
63 | _option_list.update({ | |
64 | 'index' : lambda self, index: self.set_option_index(index), |
|
64 | 'index' : lambda self, index: self.set_option_index(index), | |
65 | }) |
|
65 | }) | |
66 |
|
66 | |||
67 | # A new initializer is needed b/c we want to add a modified |
|
67 | # A new initializer is needed b/c we want to add a modified | |
68 | # _option_sequence list which includes 'index' in the right place. |
|
68 | # _option_sequence list which includes 'index' in the right place. | |
69 | def __init__(self,*args,**kw): |
|
69 | def __init__(self,*args,**kw): | |
70 | self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes', |
|
70 | self._option_sequence = ['binary', 'index', 'using', 'smooth', 'axes', | |
71 | 'title', 'with'] |
|
71 | 'title', 'with'] | |
72 |
|
72 | |||
73 | _BaseFileItem.__init__(self,*args,**kw) |
|
73 | _BaseFileItem.__init__(self,*args,**kw) | |
74 |
|
74 | |||
75 | # Let's fix the constructor docstring |
|
75 | # Let's fix the constructor docstring | |
76 | __newdoc = \ |
|
76 | __newdoc = \ | |
77 | """Additional Keyword arguments added by IPython: |
|
77 | """Additional Keyword arguments added by IPython: | |
78 |
|
78 | |||
79 | 'index=<int>' -- similar to the `index` keyword in Gnuplot. |
|
79 | 'index=<int>' -- similar to the `index` keyword in Gnuplot. | |
80 | This allows only some of the datasets in a file to be |
|
80 | This allows only some of the datasets in a file to be | |
81 | plotted. Datasets within a file are assumed to be separated |
|
81 | plotted. Datasets within a file are assumed to be separated | |
82 | by _pairs_ of blank lines, and the first one is numbered as |
|
82 | by _pairs_ of blank lines, and the first one is numbered as | |
83 | 0 (similar to C/Python usage).""" |
|
83 | 0 (similar to C/Python usage).""" | |
84 | __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc |
|
84 | __init__.__doc__ = PlotItems.File.__init__.__doc__ + __newdoc | |
85 |
|
85 | |||
86 | def set_option_index(self, index): |
|
86 | def set_option_index(self, index): | |
87 | if index is None: |
|
87 | if index is None: | |
88 | self.clear_option('index') |
|
88 | self.clear_option('index') | |
89 | elif type(index) in [type(''), type(1)]: |
|
89 | elif type(index) in [type(''), type(1)]: | |
90 | self._options['index'] = (index, 'index %s' % index) |
|
90 | self._options['index'] = (index, 'index %s' % index) | |
91 | elif type(index) is type(()): |
|
91 | elif type(index) is type(()): | |
92 | self._options['index'] = (index,'index %s' % |
|
92 | self._options['index'] = (index,'index %s' % | |
93 | string.join(map(repr, index), ':')) |
|
93 | string.join(map(repr, index), ':')) | |
94 | else: |
|
94 | else: | |
95 | raise OptionException('index=%s' % (index,)) |
|
95 | raise OptionException('index=%s' % (index,)) | |
96 |
|
96 | |||
97 | # We need a FileClass with a different name from 'File', which is a |
|
97 | # 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 |
|
98 | # factory function in 1.7, so that our String class can subclass FileClass | |
99 | # in any version. |
|
99 | # in any version. | |
100 | _FileClass = File |
|
100 | _FileClass = File | |
101 |
|
101 | |||
102 | elif Gnuplot_ori.__version__ =='1.7': |
|
102 | elif Gnuplot_ori.__version__ =='1.7': | |
103 | _FileClass = _BaseFileItem = PlotItems._FileItem |
|
103 | _FileClass = _BaseFileItem = PlotItems._FileItem | |
104 | _BaseTempFileItem = PlotItems._TempFileItem |
|
104 | _BaseTempFileItem = PlotItems._TempFileItem | |
105 | File = PlotItems.File |
|
105 | File = PlotItems.File | |
106 |
|
106 | |||
107 | else: # changes in the newer version (svn as of March'06) |
|
107 | else: # changes in the newer version (svn as of March'06) | |
108 | _FileClass = _BaseFileItem = PlotItems._FileItem |
|
108 | _FileClass = _BaseFileItem = PlotItems._FileItem | |
109 | _BaseTempFileItem = PlotItems._NewFileItem |
|
109 | _BaseTempFileItem = PlotItems._NewFileItem | |
110 | File = PlotItems.File |
|
110 | File = PlotItems.File | |
111 |
|
111 | |||
112 |
|
112 | |||
113 | # Now, we can add our generic code which is version independent |
|
113 | # Now, we can add our generic code which is version independent | |
114 |
|
114 | |||
115 | # First some useful utilities |
|
115 | # First some useful utilities | |
116 | def eps_fix_bbox(fname): |
|
116 | def eps_fix_bbox(fname): | |
117 | """Fix the bounding box of an eps file by running ps2eps on it. |
|
117 | """Fix the bounding box of an eps file by running ps2eps on it. | |
118 |
|
118 | |||
119 | If its name ends in .eps, the original file is removed. |
|
119 | If its name ends in .eps, the original file is removed. | |
120 |
|
120 | |||
121 | This is particularly useful for plots made by Gnuplot with square aspect |
|
121 | 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 |
|
122 | ratio: there is a bug in Gnuplot which makes it generate a bounding box | |
123 | which is far wider than the actual plot. |
|
123 | which is far wider than the actual plot. | |
124 |
|
124 | |||
125 | This function assumes that ps2eps is installed in your system.""" |
|
125 | This function assumes that ps2eps is installed in your system.""" | |
126 |
|
126 | |||
127 | # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The |
|
127 | # note: ps2ps and eps2eps do NOT work, ONLY ps2eps works correctly. The | |
128 | # others make output with bitmapped fonts, which looks horrible. |
|
128 | # others make output with bitmapped fonts, which looks horrible. | |
129 | print 'Fixing eps file: <%s>' % fname |
|
129 | print 'Fixing eps file: <%s>' % fname | |
130 | xsys('ps2eps -f -q -l %s' % fname) |
|
130 | xsys('ps2eps -f -q -l %s' % fname) | |
131 | if fname.endswith('.eps'): |
|
131 | if fname.endswith('.eps'): | |
132 | os.rename(fname+'.eps',fname) |
|
132 | os.rename(fname+'.eps',fname) | |
133 |
|
133 | |||
134 | def is_list1d(x,containers = [types.ListType,types.TupleType]): |
|
134 | def is_list1d(x,containers = [types.ListType,types.TupleType]): | |
135 | """Returns true if x appears to be a 1d list/tuple/array. |
|
135 | """Returns true if x appears to be a 1d list/tuple/array. | |
136 |
|
136 | |||
137 | The heuristics are: identify Numeric arrays, or lists/tuples whose first |
|
137 | 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 |
|
138 | 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 |
|
139 | 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 |
|
140 | 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. |
|
141 | will produce an error. But it should work well in most cases. | |
142 | """ |
|
142 | """ | |
143 | x_type = type(x) |
|
143 | x_type = type(x) | |
144 |
|
144 | |||
145 | return x_type == Numeric.ArrayType and len(x.shape)==1 or \ |
|
145 | return x_type == Numeric.ArrayType and len(x.shape)==1 or \ | |
146 | (x_type in containers and |
|
146 | (x_type in containers and | |
147 | type(x[0]) not in containers + [Numeric.ArrayType]) |
|
147 | type(x[0]) not in containers + [Numeric.ArrayType]) | |
148 |
|
148 | |||
149 | def zip_items(items,titles=None): |
|
149 | def zip_items(items,titles=None): | |
150 | """zip together neighboring 1-d arrays, and zip standalone ones |
|
150 | """zip together neighboring 1-d arrays, and zip standalone ones | |
151 | with their index. Leave other plot items alone.""" |
|
151 | with their index. Leave other plot items alone.""" | |
152 |
|
152 | |||
153 | class StandaloneItem(Exception): pass |
|
153 | class StandaloneItem(Exception): pass | |
154 |
|
154 | |||
155 | def get_titles(titles): |
|
155 | def get_titles(titles): | |
156 | """Return the next title and the input titles array. |
|
156 | """Return the next title and the input titles array. | |
157 |
|
157 | |||
158 | The input array may be changed to None when no titles are left to |
|
158 | The input array may be changed to None when no titles are left to | |
159 | prevent extra unnecessary calls to this function.""" |
|
159 | prevent extra unnecessary calls to this function.""" | |
160 |
|
160 | |||
161 | try: |
|
161 | try: | |
162 | title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope |
|
162 | title = titles[tit_ct[0]] # tit_ct[0] is in zip_items'scope | |
163 | except IndexError: |
|
163 | except IndexError: | |
164 | titles = None # so we don't enter again |
|
164 | titles = None # so we don't enter again | |
165 | title = None |
|
165 | title = None | |
166 | else: |
|
166 | else: | |
167 | tit_ct[0] += 1 |
|
167 | tit_ct[0] += 1 | |
168 | return title,titles |
|
168 | return title,titles | |
169 |
|
169 | |||
170 | new_items = [] |
|
170 | new_items = [] | |
171 |
|
171 | |||
172 | if titles: |
|
172 | if titles: | |
173 | # Initialize counter. It was put in a list as a hack to allow the |
|
173 | # 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. |
|
174 | # nested get_titles to modify it without raising a NameError. | |
175 | tit_ct = [0] |
|
175 | tit_ct = [0] | |
176 |
|
176 | |||
177 | n = 0 # this loop needs to be done by hand |
|
177 | n = 0 # this loop needs to be done by hand | |
178 | while n < len(items): |
|
178 | while n < len(items): | |
179 | item = items[n] |
|
179 | item = items[n] | |
180 | try: |
|
180 | try: | |
181 | if is_list1d(item): |
|
181 | if is_list1d(item): | |
182 | if n==len(items)-1: # last in list |
|
182 | if n==len(items)-1: # last in list | |
183 | raise StandaloneItem |
|
183 | raise StandaloneItem | |
184 | else: # check the next item and zip together if needed |
|
184 | else: # check the next item and zip together if needed | |
185 | next_item = items[n+1] |
|
185 | next_item = items[n+1] | |
186 | if next_item is None: |
|
186 | if next_item is None: | |
187 | n += 1 |
|
187 | n += 1 | |
188 | raise StandaloneItem |
|
188 | raise StandaloneItem | |
189 | elif is_list1d(next_item): |
|
189 | elif is_list1d(next_item): | |
190 | # this would be best done with an iterator |
|
190 | # this would be best done with an iterator | |
191 | if titles: |
|
191 | if titles: | |
192 | title,titles = get_titles(titles) |
|
192 | title,titles = get_titles(titles) | |
193 | else: |
|
193 | else: | |
194 | title = None |
|
194 | title = None | |
195 | new_items.append(Data(zip(item,next_item), |
|
195 | new_items.append(Data(zip(item,next_item), | |
196 | title=title)) |
|
196 | title=title)) | |
197 | n += 1 # avoid double-inclusion of next item |
|
197 | n += 1 # avoid double-inclusion of next item | |
198 | else: # can't zip with next, zip with own index list |
|
198 | else: # can't zip with next, zip with own index list | |
199 | raise StandaloneItem |
|
199 | raise StandaloneItem | |
200 | else: # not 1-d array |
|
200 | else: # not 1-d array | |
201 | new_items.append(item) |
|
201 | new_items.append(item) | |
202 | except StandaloneItem: |
|
202 | except StandaloneItem: | |
203 | if titles: |
|
203 | if titles: | |
204 | title,titles = get_titles(titles) |
|
204 | title,titles = get_titles(titles) | |
205 | else: |
|
205 | else: | |
206 | title = None |
|
206 | title = None | |
207 | new_items.append(Data(zip(range(len(item)),item),title=title)) |
|
207 | new_items.append(Data(zip(range(len(item)),item),title=title)) | |
208 | except AttributeError: |
|
208 | except AttributeError: | |
209 | new_items.append(item) |
|
209 | new_items.append(item) | |
210 | n+=1 |
|
210 | n+=1 | |
211 |
|
211 | |||
212 | return new_items |
|
212 | return new_items | |
213 |
|
213 | |||
214 | # And some classes with enhanced functionality. |
|
214 | # And some classes with enhanced functionality. | |
215 | class String(_FileClass): |
|
215 | class String(_FileClass): | |
216 | """Make a PlotItem from data in a string with the same format as a File. |
|
216 | """Make a PlotItem from data in a string with the same format as a File. | |
217 |
|
217 | |||
218 | This allows writing data directly inside python scripts using the exact |
|
218 | This allows writing data directly inside python scripts using the exact | |
219 | same format and manipulation options which would be used for external |
|
219 | same format and manipulation options which would be used for external | |
220 | files.""" |
|
220 | files.""" | |
221 |
|
221 | |||
222 | def __init__(self, data_str, **keyw): |
|
222 | def __init__(self, data_str, **keyw): | |
223 | """Construct a String object. |
|
223 | """Construct a String object. | |
224 |
|
224 | |||
225 | <data_str> is a string formatted exactly like a valid Gnuplot data |
|
225 | <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. |
|
226 | file would be. All options from the File constructor are valid here. | |
227 |
|
227 | |||
228 | Warning: when used for interactive plotting in scripts which exit |
|
228 | Warning: when used for interactive plotting in scripts which exit | |
229 | immediately, you may get an error because the temporary file used to |
|
229 | 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 |
|
230 | 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 |
|
231 | it. You can work around this problem by putting a raw_input() call at | |
232 | the end of the script. |
|
232 | the end of the script. | |
233 |
|
233 | |||
234 | This problem does not appear when generating PostScript output, only |
|
234 | This problem does not appear when generating PostScript output, only | |
235 | with Gnuplot windows.""" |
|
235 | with Gnuplot windows.""" | |
236 |
|
236 | |||
237 | self.tmpfile = _BaseTempFileItem() |
|
237 | self.tmpfile = _BaseTempFileItem() | |
238 | tmpfile = file(self.tmpfile.filename,'w') |
|
238 | tmpfile = file(self.tmpfile.filename,'w') | |
239 | tmpfile.write(data_str) |
|
239 | tmpfile.write(data_str) | |
240 | _BaseFileItem.__init__(self,self.tmpfile,**keyw) |
|
240 | _BaseFileItem.__init__(self,self.tmpfile,**keyw) | |
241 |
|
241 | |||
242 |
|
242 | |||
243 | class Gnuplot(Gnuplot_ori.Gnuplot): |
|
243 | class Gnuplot(Gnuplot_ori.Gnuplot): | |
244 | """Improved Gnuplot class. |
|
244 | """Improved Gnuplot class. | |
245 |
|
245 | |||
246 | Enhancements: better plot,replot and hardcopy methods. New methods for |
|
246 | Enhancements: better plot,replot and hardcopy methods. New methods for | |
247 | quick range setting. |
|
247 | quick range setting. | |
248 | """ |
|
248 | """ | |
249 |
|
249 | |||
250 | def xrange(self,min='*',max='*'): |
|
250 | def xrange(self,min='*',max='*'): | |
251 | """Set xrange. If min/max is omitted, it is set to '*' (auto). |
|
251 | """Set xrange. If min/max is omitted, it is set to '*' (auto). | |
252 |
|
252 | |||
253 | Note that this is different from the regular Gnuplot behavior, where |
|
253 | Note that this is different from the regular Gnuplot behavior, where | |
254 | an unspecified limit means no change. Here any unspecified limit is |
|
254 | an unspecified limit means no change. Here any unspecified limit is | |
255 | set to autoscaling, allowing these functions to be used for full |
|
255 | set to autoscaling, allowing these functions to be used for full | |
256 | autoscaling when called with no arguments. |
|
256 | autoscaling when called with no arguments. | |
257 |
|
257 | |||
258 | To preserve one limit's current value while changing the other, an |
|
258 | To preserve one limit's current value while changing the other, an | |
259 | explicit '' argument must be given as the limit to be kept. |
|
259 | explicit '' argument must be given as the limit to be kept. | |
260 |
|
260 | |||
261 | Similar functions exist for [y{2}z{2}rtuv]range.""" |
|
261 | Similar functions exist for [y{2}z{2}rtuv]range.""" | |
262 |
|
262 | |||
263 | self('set xrange [%s:%s]' % (min,max)) |
|
263 | self('set xrange [%s:%s]' % (min,max)) | |
264 |
|
264 | |||
265 | def yrange(self,min='*',max='*'): |
|
265 | def yrange(self,min='*',max='*'): | |
266 | self('set yrange [%s:%s]' % (min,max)) |
|
266 | self('set yrange [%s:%s]' % (min,max)) | |
267 |
|
267 | |||
268 | def zrange(self,min='*',max='*'): |
|
268 | def zrange(self,min='*',max='*'): | |
269 | self('set zrange [%s:%s]' % (min,max)) |
|
269 | self('set zrange [%s:%s]' % (min,max)) | |
270 |
|
270 | |||
271 | def x2range(self,min='*',max='*'): |
|
271 | def x2range(self,min='*',max='*'): | |
272 | self('set xrange [%s:%s]' % (min,max)) |
|
272 | self('set xrange [%s:%s]' % (min,max)) | |
273 |
|
273 | |||
274 | def y2range(self,min='*',max='*'): |
|
274 | def y2range(self,min='*',max='*'): | |
275 | self('set yrange [%s:%s]' % (min,max)) |
|
275 | self('set yrange [%s:%s]' % (min,max)) | |
276 |
|
276 | |||
277 | def z2range(self,min='*',max='*'): |
|
277 | def z2range(self,min='*',max='*'): | |
278 | self('set zrange [%s:%s]' % (min,max)) |
|
278 | self('set zrange [%s:%s]' % (min,max)) | |
279 |
|
279 | |||
280 | def rrange(self,min='*',max='*'): |
|
280 | def rrange(self,min='*',max='*'): | |
281 | self('set rrange [%s:%s]' % (min,max)) |
|
281 | self('set rrange [%s:%s]' % (min,max)) | |
282 |
|
282 | |||
283 | def trange(self,min='*',max='*'): |
|
283 | def trange(self,min='*',max='*'): | |
284 | self('set trange [%s:%s]' % (min,max)) |
|
284 | self('set trange [%s:%s]' % (min,max)) | |
285 |
|
285 | |||
286 | def urange(self,min='*',max='*'): |
|
286 | def urange(self,min='*',max='*'): | |
287 | self('set urange [%s:%s]' % (min,max)) |
|
287 | self('set urange [%s:%s]' % (min,max)) | |
288 |
|
288 | |||
289 | def vrange(self,min='*',max='*'): |
|
289 | def vrange(self,min='*',max='*'): | |
290 | self('set vrange [%s:%s]' % (min,max)) |
|
290 | self('set vrange [%s:%s]' % (min,max)) | |
291 |
|
291 | |||
292 | def set_ps(self,option): |
|
292 | def set_ps(self,option): | |
293 | """Set an option for the PostScript terminal and reset default term.""" |
|
293 | """Set an option for the PostScript terminal and reset default term.""" | |
294 |
|
294 | |||
295 | self('set terminal postscript %s ' % option) |
|
295 | self('set terminal postscript %s ' % option) | |
296 | self('set terminal %s' % gp.GnuplotOpts.default_term) |
|
296 | self('set terminal %s' % gp.GnuplotOpts.default_term) | |
297 |
|
297 | |||
298 | def __plot_ps(self, plot_method,*items, **keyw): |
|
298 | def __plot_ps(self, plot_method,*items, **keyw): | |
299 | """Wrapper for plot/splot/replot, with processing of hardcopy options. |
|
299 | """Wrapper for plot/splot/replot, with processing of hardcopy options. | |
300 |
|
300 | |||
301 | For internal use only.""" |
|
301 | For internal use only.""" | |
302 |
|
302 | |||
303 | # Filter out PostScript options which will crash the normal plot/replot |
|
303 | # Filter out PostScript options which will crash the normal plot/replot | |
304 | psargs = {'filename':None, |
|
304 | psargs = {'filename':None, | |
305 | 'mode':None, |
|
305 | 'mode':None, | |
306 | 'eps':None, |
|
306 | 'eps':None, | |
307 | 'enhanced':None, |
|
307 | 'enhanced':None, | |
308 | 'color':None, |
|
308 | 'color':None, | |
309 | 'solid':None, |
|
309 | 'solid':None, | |
310 | 'duplexing':None, |
|
310 | 'duplexing':None, | |
311 | 'fontname':None, |
|
311 | 'fontname':None, | |
312 | 'fontsize':None, |
|
312 | 'fontsize':None, | |
313 | 'debug':0 } |
|
313 | 'debug':0 } | |
314 |
|
314 | |||
315 | for k in psargs.keys(): |
|
315 | for k in psargs.keys(): | |
316 | if keyw.has_key(k): |
|
316 | if keyw.has_key(k): | |
317 | psargs[k] = keyw[k] |
|
317 | psargs[k] = keyw[k] | |
318 | del keyw[k] |
|
318 | del keyw[k] | |
319 |
|
319 | |||
320 | # Filter out other options the original plot doesn't know |
|
320 | # Filter out other options the original plot doesn't know | |
321 | hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None) |
|
321 | hardcopy = popkey(keyw,'hardcopy',psargs['filename'] is not None) | |
322 | titles = popkey(keyw,'titles',0) |
|
322 | titles = popkey(keyw,'titles',0) | |
323 |
|
323 | |||
324 | # the filename keyword should control hardcopy generation, this is an |
|
324 | # the filename keyword should control hardcopy generation, this is an | |
325 | # override switch only which needs to be explicitly set to zero |
|
325 | # override switch only which needs to be explicitly set to zero | |
326 | if hardcopy: |
|
326 | if hardcopy: | |
327 | if psargs['filename'] is None: |
|
327 | if psargs['filename'] is None: | |
328 | raise ValueError, \ |
|
328 | raise ValueError, \ | |
329 | 'If you request hardcopy, you must give a filename.' |
|
329 | 'If you request hardcopy, you must give a filename.' | |
330 |
|
330 | |||
331 | # set null output so nothing goes to screen. hardcopy() restores output |
|
331 | # set null output so nothing goes to screen. hardcopy() restores output | |
332 | self('set term dumb') |
|
332 | self('set term dumb') | |
333 | # I don't know how to prevent screen output in Windows |
|
333 | # I don't know how to prevent screen output in Windows | |
334 | if os.name == 'posix': |
|
334 | if os.name == 'posix': | |
335 | self('set output "/dev/null"') |
|
335 | self('set output "/dev/null"') | |
336 |
|
336 | |||
337 | new_items = zip_items(items,titles) |
|
337 | new_items = zip_items(items,titles) | |
338 | # plot_method is either plot or replot from the original Gnuplot class: |
|
338 | # plot_method is either plot or replot from the original Gnuplot class: | |
339 | plot_method(self,*new_items,**keyw) |
|
339 | plot_method(self,*new_items,**keyw) | |
340 |
|
340 | |||
341 | # Do hardcopy if requested |
|
341 | # Do hardcopy if requested | |
342 | if hardcopy: |
|
342 | if hardcopy: | |
343 | if psargs['filename'].endswith('.eps'): |
|
343 | if psargs['filename'].endswith('.eps'): | |
344 | psargs['eps'] = 1 |
|
344 | psargs['eps'] = 1 | |
345 | self.hardcopy(**psargs) |
|
345 | self.hardcopy(**psargs) | |
346 |
|
346 | |||
347 | def plot(self, *items, **keyw): |
|
347 | def plot(self, *items, **keyw): | |
348 | """Draw a new plot. |
|
348 | """Draw a new plot. | |
349 |
|
349 | |||
350 | Clear the current plot and create a new 2-d plot containing |
|
350 | Clear the current plot and create a new 2-d plot containing | |
351 | the specified items. Each arguments should be of the |
|
351 | the specified items. Each arguments should be of the | |
352 | following types: |
|
352 | following types: | |
353 |
|
353 | |||
354 | 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most |
|
354 | 'PlotItem' (e.g., 'Data', 'File', 'Func') -- This is the most | |
355 | flexible way to call plot because the PlotItems can |
|
355 | flexible way to call plot because the PlotItems can | |
356 | contain suboptions. Moreover, PlotItems can be saved to |
|
356 | contain suboptions. Moreover, PlotItems can be saved to | |
357 | variables so that their lifetime is longer than one plot |
|
357 | variables so that their lifetime is longer than one plot | |
358 | command; thus they can be replotted with minimal overhead. |
|
358 | command; thus they can be replotted with minimal overhead. | |
359 |
|
359 | |||
360 | 'string' (e.g., 'sin(x)') -- The string is interpreted as |
|
360 | 'string' (e.g., 'sin(x)') -- The string is interpreted as | |
361 | 'Func(string)' (a function that is computed by gnuplot). |
|
361 | 'Func(string)' (a function that is computed by gnuplot). | |
362 |
|
362 | |||
363 | Anything else -- The object, which should be convertible to an |
|
363 | Anything else -- The object, which should be convertible to an | |
364 | array, is passed to the 'Data' constructor, and thus |
|
364 | array, is passed to the 'Data' constructor, and thus | |
365 | plotted as data. If the conversion fails, an exception is |
|
365 | plotted as data. If the conversion fails, an exception is | |
366 | raised. |
|
366 | raised. | |
367 |
|
367 | |||
368 |
|
368 | |||
369 | This is a modified version of plot(). Compared to the original in |
|
369 | This is a modified version of plot(). Compared to the original in | |
370 | Gnuplot.py, this version has several enhancements, listed below. |
|
370 | Gnuplot.py, this version has several enhancements, listed below. | |
371 |
|
371 | |||
372 |
|
372 | |||
373 | Modifications to the input arguments |
|
373 | Modifications to the input arguments | |
374 | ------------------------------------ |
|
374 | ------------------------------------ | |
375 |
|
375 | |||
376 | (1-d array means Numeric array, list or tuple): |
|
376 | (1-d array means Numeric array, list or tuple): | |
377 |
|
377 | |||
378 | (i) Any 1-d array which is NOT followed by another 1-d array, is |
|
378 | (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 |
|
379 | automatically zipped with range(len(array_1d)). Typing g.plot(y) will | |
380 | plot y against its indices. |
|
380 | plot y against its indices. | |
381 |
|
381 | |||
382 | (ii) If two 1-d arrays are contiguous in the argument list, they are |
|
382 | (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 |
|
383 | 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. |
|
384 | g.plot(x1,y1,x2,y2) plots y1 vs. x1 and y2 vs. x2. | |
385 |
|
385 | |||
386 | (iii) Any 1-d array which is followed by None is automatically zipped |
|
386 | (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) |
|
387 | 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 |
|
388 | 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 |
|
389 | 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) |
|
390 | since y2 is unpaired it is automatically zipped to its indices by (i) | |
391 |
|
391 | |||
392 | (iv) Any other arguments which don't match these cases are left alone and |
|
392 | (iv) Any other arguments which don't match these cases are left alone and | |
393 | passed to the code below. |
|
393 | passed to the code below. | |
394 |
|
394 | |||
395 | For lists or tuples, the heuristics used to determine whether they are |
|
395 | 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 |
|
396 | 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 |
|
397 | if it is not a list or tuple itself, it is assumed that the whole | |
398 | object is one-dimensional. |
|
398 | object is one-dimensional. | |
399 |
|
399 | |||
400 | An additional optional keyword 'titles' has been added: it must be a |
|
400 | 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 |
|
401 | 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 |
|
402 | are NOT PlotItem objects (since those objects carry their own labels | |
403 | within). |
|
403 | within). | |
404 |
|
404 | |||
405 |
|
405 | |||
406 | PostScript generation |
|
406 | PostScript generation | |
407 | --------------------- |
|
407 | --------------------- | |
408 |
|
408 | |||
409 | This version of plot() also handles automatically the production of |
|
409 | This version of plot() also handles automatically the production of | |
410 | PostScript output. The main options are (given as keyword arguments): |
|
410 | PostScript output. The main options are (given as keyword arguments): | |
411 |
|
411 | |||
412 | - filename: a string, typically ending in .eps. If given, the plot is |
|
412 | - filename: a string, typically ending in .eps. If given, the plot is | |
413 | sent to this file in PostScript format. |
|
413 | sent to this file in PostScript format. | |
414 |
|
414 | |||
415 | - hardcopy: this can be set to 0 to override 'filename'. It does not |
|
415 | - 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 |
|
416 | need to be given to produce PostScript, its purpose is to allow | |
417 | switching PostScript output off globally in scripts without having to |
|
417 | switching PostScript output off globally in scripts without having to | |
418 | manually change 'filename' values in multiple calls. |
|
418 | manually change 'filename' values in multiple calls. | |
419 |
|
419 | |||
420 | All other keywords accepted by Gnuplot.hardcopy() are transparently |
|
420 | All other keywords accepted by Gnuplot.hardcopy() are transparently | |
421 | passed, and safely ignored if output is sent to the screen instead of |
|
421 | passed, and safely ignored if output is sent to the screen instead of | |
422 | PostScript. |
|
422 | PostScript. | |
423 |
|
423 | |||
424 | For example: |
|
424 | For example: | |
425 |
|
425 | |||
426 | In [1]: x=frange(0,2*pi,npts=100) |
|
426 | In [1]: x=frange(0,2*pi,npts=100) | |
427 |
|
427 | |||
428 | Generate a plot in file 'sin.eps': |
|
428 | Generate a plot in file 'sin.eps': | |
429 |
|
429 | |||
430 | In [2]: plot(x,sin(x),filename = 'sin.eps') |
|
430 | In [2]: plot(x,sin(x),filename = 'sin.eps') | |
431 |
|
431 | |||
432 | Plot to screen instead, without having to change the filename: |
|
432 | Plot to screen instead, without having to change the filename: | |
433 |
|
433 | |||
434 | In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0) |
|
434 | In [3]: plot(x,sin(x),filename = 'sin.eps',hardcopy=0) | |
435 |
|
435 | |||
436 | Pass the 'color=0' option to hardcopy for monochrome output: |
|
436 | Pass the 'color=0' option to hardcopy for monochrome output: | |
437 |
|
437 | |||
438 | In [4]: plot(x,sin(x),filename = 'sin.eps',color=0) |
|
438 | In [4]: plot(x,sin(x),filename = 'sin.eps',color=0) | |
439 |
|
439 | |||
440 | PostScript generation through plot() is useful mainly for scripting |
|
440 | PostScript generation through plot() is useful mainly for scripting | |
441 | uses where you are not interested in interactive plotting. For |
|
441 | uses where you are not interested in interactive plotting. For | |
442 | interactive use, the hardcopy() function is typically more convenient: |
|
442 | interactive use, the hardcopy() function is typically more convenient: | |
443 |
|
443 | |||
444 | In [5]: plot(x,sin(x)) |
|
444 | In [5]: plot(x,sin(x)) | |
445 |
|
445 | |||
446 | In [6]: hardcopy('sin.eps') """ |
|
446 | In [6]: hardcopy('sin.eps') """ | |
447 |
|
447 | |||
448 | self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw) |
|
448 | self.__plot_ps(Gnuplot_ori.Gnuplot.plot,*items,**keyw) | |
449 |
|
449 | |||
450 | def plot2(self,arg,**kw): |
|
450 | def plot2(self,arg,**kw): | |
451 | """Plot the entries of a dictionary or a list/tuple of arrays. |
|
451 | """Plot the entries of a dictionary or a list/tuple of arrays. | |
452 |
|
452 | |||
453 | This simple utility calls plot() with a list of Gnuplot.Data objects |
|
453 | 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 |
|
454 | 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 |
|
455 | in it if it is a tuple or list. Each item gets labeled with the key/index | |
456 | in the Gnuplot legend. |
|
456 | in the Gnuplot legend. | |
457 |
|
457 | |||
458 | Each item is plotted by zipping it with a list of its indices. |
|
458 | Each item is plotted by zipping it with a list of its indices. | |
459 |
|
459 | |||
460 | Any keywords are passed directly to plot().""" |
|
460 | Any keywords are passed directly to plot().""" | |
461 |
|
461 | |||
462 | if hasattr(arg,'keys'): |
|
462 | if hasattr(arg,'keys'): | |
463 | keys = arg.keys() |
|
463 | keys = arg.keys() | |
464 | keys.sort() |
|
464 | keys.sort() | |
465 | else: |
|
465 | else: | |
466 | keys = range(len(arg)) |
|
466 | keys = range(len(arg)) | |
467 |
|
467 | |||
468 | pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys] |
|
468 | pitems = [Data(zip(range(len(arg[k])),arg[k]),title=`k`) for k in keys] | |
469 | self.plot(*pitems,**kw) |
|
469 | self.plot(*pitems,**kw) | |
470 |
|
470 | |||
471 | def splot(self, *items, **keyw): |
|
471 | def splot(self, *items, **keyw): | |
472 | """Draw a new three-dimensional plot. |
|
472 | """Draw a new three-dimensional plot. | |
473 |
|
473 | |||
474 | Clear the current plot and create a new 3-d plot containing |
|
474 | Clear the current plot and create a new 3-d plot containing | |
475 | the specified items. Arguments can be of the following types: |
|
475 | the specified items. Arguments can be of the following types: | |
476 |
|
476 | |||
477 | 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This |
|
477 | 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData' ) -- This | |
478 | is the most flexible way to call plot because the |
|
478 | is the most flexible way to call plot because the | |
479 | PlotItems can contain suboptions. Moreover, PlotItems can |
|
479 | PlotItems can contain suboptions. Moreover, PlotItems can | |
480 | be saved to variables so that their lifetime is longer |
|
480 | be saved to variables so that their lifetime is longer | |
481 | than one plot command--thus they can be replotted with |
|
481 | than one plot command--thus they can be replotted with | |
482 | minimal overhead. |
|
482 | minimal overhead. | |
483 |
|
483 | |||
484 | 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a |
|
484 | 'string' (e.g., 'sin(x*y)') -- The string is interpreted as a | |
485 | 'Func()' (a function that is computed by gnuplot). |
|
485 | 'Func()' (a function that is computed by gnuplot). | |
486 |
|
486 | |||
487 | Anything else -- The object is converted to a Data() item, and |
|
487 | Anything else -- The object is converted to a Data() item, and | |
488 | thus plotted as data. Note that each data point should |
|
488 | thus plotted as data. Note that each data point should | |
489 | normally have at least three values associated with it |
|
489 | normally have at least three values associated with it | |
490 | (i.e., x, y, and z). If the conversion fails, an |
|
490 | (i.e., x, y, and z). If the conversion fails, an | |
491 | exception is raised. |
|
491 | exception is raised. | |
492 |
|
492 | |||
493 | This is a modified version of splot(). Compared to the original in |
|
493 | This is a modified version of splot(). Compared to the original in | |
494 | Gnuplot.py, this version has several enhancements, listed in the |
|
494 | Gnuplot.py, this version has several enhancements, listed in the | |
495 | plot() documentation. |
|
495 | plot() documentation. | |
496 | """ |
|
496 | """ | |
497 |
|
497 | |||
498 | self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw) |
|
498 | self.__plot_ps(Gnuplot_ori.Gnuplot.splot,*items,**keyw) | |
499 |
|
499 | |||
500 | def replot(self, *items, **keyw): |
|
500 | def replot(self, *items, **keyw): | |
501 | """Replot the data, possibly adding new 'PlotItem's. |
|
501 | """Replot the data, possibly adding new 'PlotItem's. | |
502 |
|
502 | |||
503 | Replot the existing graph, using the items in the current |
|
503 | Replot the existing graph, using the items in the current | |
504 | itemlist. If arguments are specified, they are interpreted as |
|
504 | itemlist. If arguments are specified, they are interpreted as | |
505 | additional items to be plotted alongside the existing items on |
|
505 | additional items to be plotted alongside the existing items on | |
506 | the same graph. See 'plot' for details. |
|
506 | the same graph. See 'plot' for details. | |
507 |
|
507 | |||
508 | If you want to replot to a postscript file, you MUST give the |
|
508 | 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 |
|
509 | 'filename' keyword argument in each call to replot. The Gnuplot python | |
510 | interface has no way of knowing that your previous call to |
|
510 | interface has no way of knowing that your previous call to | |
511 | Gnuplot.plot() was meant for PostScript output.""" |
|
511 | Gnuplot.plot() was meant for PostScript output.""" | |
512 |
|
512 | |||
513 | self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw) |
|
513 | self.__plot_ps(Gnuplot_ori.Gnuplot.replot,*items,**keyw) | |
514 |
|
514 | |||
515 | # The original hardcopy has a bug. See fix at the end. The rest of the code |
|
515 | # 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 |
|
516 | # was lifted verbatim from the original, so that people using IPython get the | |
517 | # benefits without having to manually patch Gnuplot.py |
|
517 | # benefits without having to manually patch Gnuplot.py | |
518 | def hardcopy(self, filename=None, |
|
518 | def hardcopy(self, filename=None, | |
519 | mode=None, |
|
519 | mode=None, | |
520 | eps=None, |
|
520 | eps=None, | |
521 | enhanced=None, |
|
521 | enhanced=None, | |
522 | color=None, |
|
522 | color=None, | |
523 | solid=None, |
|
523 | solid=None, | |
524 | duplexing=None, |
|
524 | duplexing=None, | |
525 | fontname=None, |
|
525 | fontname=None, | |
526 | fontsize=None, |
|
526 | fontsize=None, | |
527 | debug = 0, |
|
527 | debug = 0, | |
528 | ): |
|
528 | ): | |
529 | """Create a hardcopy of the current plot. |
|
529 | """Create a hardcopy of the current plot. | |
530 |
|
530 | |||
531 | Create a postscript hardcopy of the current plot to the |
|
531 | Create a postscript hardcopy of the current plot to the | |
532 | default printer (if configured) or to the specified filename. |
|
532 | default printer (if configured) or to the specified filename. | |
533 |
|
533 | |||
534 | Note that gnuplot remembers the postscript suboptions across |
|
534 | Note that gnuplot remembers the postscript suboptions across | |
535 | terminal changes. Therefore if you set, for example, color=1 |
|
535 | terminal changes. Therefore if you set, for example, color=1 | |
536 | for one hardcopy then the next hardcopy will also be color |
|
536 | for one hardcopy then the next hardcopy will also be color | |
537 | unless you explicitly choose color=0. Alternately you can |
|
537 | unless you explicitly choose color=0. Alternately you can | |
538 | force all of the options to their defaults by setting |
|
538 | force all of the options to their defaults by setting | |
539 | mode='default'. I consider this to be a bug in gnuplot. |
|
539 | mode='default'. I consider this to be a bug in gnuplot. | |
540 |
|
540 | |||
541 | Keyword arguments: |
|
541 | Keyword arguments: | |
542 |
|
542 | |||
543 | 'filename=<string>' -- if a filename is specified, save the |
|
543 | 'filename=<string>' -- if a filename is specified, save the | |
544 | output in that file; otherwise print it immediately |
|
544 | output in that file; otherwise print it immediately | |
545 | using the 'default_lpr' configuration option. If the |
|
545 | using the 'default_lpr' configuration option. If the | |
546 | filename ends in '.eps', EPS mode is automatically |
|
546 | filename ends in '.eps', EPS mode is automatically | |
547 | selected (like manually specifying eps=1 or mode='eps'). |
|
547 | selected (like manually specifying eps=1 or mode='eps'). | |
548 |
|
548 | |||
549 | 'mode=<string>' -- set the postscript submode ('landscape', |
|
549 | 'mode=<string>' -- set the postscript submode ('landscape', | |
550 | 'portrait', 'eps', or 'default'). The default is |
|
550 | 'portrait', 'eps', or 'default'). The default is | |
551 | to leave this option unspecified. |
|
551 | to leave this option unspecified. | |
552 |
|
552 | |||
553 | 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to |
|
553 | 'eps=<bool>' -- shorthand for 'mode="eps"'; asks gnuplot to | |
554 | generate encapsulated postscript. |
|
554 | generate encapsulated postscript. | |
555 |
|
555 | |||
556 | 'enhanced=<bool>' -- if set (the default), then generate |
|
556 | 'enhanced=<bool>' -- if set (the default), then generate | |
557 | enhanced postscript, which allows extra features like |
|
557 | enhanced postscript, which allows extra features like | |
558 | font-switching, superscripts, and subscripts in axis |
|
558 | font-switching, superscripts, and subscripts in axis | |
559 | labels. (Some old gnuplot versions do not support |
|
559 | labels. (Some old gnuplot versions do not support | |
560 | enhanced postscript; if this is the case set |
|
560 | enhanced postscript; if this is the case set | |
561 | gp.GnuplotOpts.prefer_enhanced_postscript=None.) |
|
561 | gp.GnuplotOpts.prefer_enhanced_postscript=None.) | |
562 |
|
562 | |||
563 | 'color=<bool>' -- if set, create a plot with color. Default |
|
563 | 'color=<bool>' -- if set, create a plot with color. Default | |
564 | is to leave this option unchanged. |
|
564 | is to leave this option unchanged. | |
565 |
|
565 | |||
566 | 'solid=<bool>' -- if set, force lines to be solid (i.e., not |
|
566 | 'solid=<bool>' -- if set, force lines to be solid (i.e., not | |
567 | dashed). |
|
567 | dashed). | |
568 |
|
568 | |||
569 | 'duplexing=<string>' -- set duplexing option ('defaultplex', |
|
569 | 'duplexing=<string>' -- set duplexing option ('defaultplex', | |
570 | 'simplex', or 'duplex'). Only request double-sided |
|
570 | 'simplex', or 'duplex'). Only request double-sided | |
571 | printing if your printer can handle it. Actually this |
|
571 | printing if your printer can handle it. Actually this | |
572 | option is probably meaningless since hardcopy() can only |
|
572 | option is probably meaningless since hardcopy() can only | |
573 | print a single plot at a time. |
|
573 | print a single plot at a time. | |
574 |
|
574 | |||
575 | 'fontname=<string>' -- set the default font to <string>, |
|
575 | 'fontname=<string>' -- set the default font to <string>, | |
576 | which must be a valid postscript font. The default is |
|
576 | which must be a valid postscript font. The default is | |
577 | to leave this option unspecified. |
|
577 | to leave this option unspecified. | |
578 |
|
578 | |||
579 | 'fontsize=<double>' -- set the default font size, in |
|
579 | 'fontsize=<double>' -- set the default font size, in | |
580 | postscript points. |
|
580 | postscript points. | |
581 |
|
581 | |||
582 | 'debug=<bool>' -- print extra debugging information (useful if |
|
582 | 'debug=<bool>' -- print extra debugging information (useful if | |
583 | your PostScript files are misteriously not being created). |
|
583 | your PostScript files are misteriously not being created). | |
584 | """ |
|
584 | """ | |
585 |
|
585 | |||
586 | if filename is None: |
|
586 | if filename is None: | |
587 | assert gp.GnuplotOpts.default_lpr is not None, \ |
|
587 | assert gp.GnuplotOpts.default_lpr is not None, \ | |
588 | OptionException('default_lpr is not set, so you can only ' |
|
588 | OptionException('default_lpr is not set, so you can only ' | |
589 | 'print to a file.') |
|
589 | 'print to a file.') | |
590 | filename = gp.GnuplotOpts.default_lpr |
|
590 | filename = gp.GnuplotOpts.default_lpr | |
591 | lpr_output = 1 |
|
591 | lpr_output = 1 | |
592 | else: |
|
592 | else: | |
593 | if filename.endswith('.eps'): |
|
593 | if filename.endswith('.eps'): | |
594 | eps = 1 |
|
594 | eps = 1 | |
595 | lpr_output = 0 |
|
595 | lpr_output = 0 | |
596 |
|
596 | |||
597 | # Be careful processing the options. If the user didn't |
|
597 | # Be careful processing the options. If the user didn't | |
598 | # request an option explicitly, do not specify it on the 'set |
|
598 | # request an option explicitly, do not specify it on the 'set | |
599 | # terminal' line (don't even specify the default value for the |
|
599 | # terminal' line (don't even specify the default value for the | |
600 | # option). This is to avoid confusing older versions of |
|
600 | # option). This is to avoid confusing older versions of | |
601 | # gnuplot that do not support all of these options. The |
|
601 | # gnuplot that do not support all of these options. The | |
602 | # exception is 'enhanced', which is just too useful to have to |
|
602 | # exception is 'enhanced', which is just too useful to have to | |
603 | # specify each time! |
|
603 | # specify each time! | |
604 |
|
604 | |||
605 | setterm = ['set', 'terminal', 'postscript'] |
|
605 | setterm = ['set', 'terminal', 'postscript'] | |
606 | if eps: |
|
606 | if eps: | |
607 | assert mode is None or mode=='eps', \ |
|
607 | assert mode is None or mode=='eps', \ | |
608 | OptionException('eps option and mode are incompatible') |
|
608 | OptionException('eps option and mode are incompatible') | |
609 | setterm.append('eps') |
|
609 | setterm.append('eps') | |
610 | else: |
|
610 | else: | |
611 | if mode is not None: |
|
611 | if mode is not None: | |
612 | assert mode in ['landscape', 'portrait', 'eps', 'default'], \ |
|
612 | assert mode in ['landscape', 'portrait', 'eps', 'default'], \ | |
613 | OptionException('illegal mode "%s"' % mode) |
|
613 | OptionException('illegal mode "%s"' % mode) | |
614 | setterm.append(mode) |
|
614 | setterm.append(mode) | |
615 | if enhanced is None: |
|
615 | if enhanced is None: | |
616 | enhanced = gp.GnuplotOpts.prefer_enhanced_postscript |
|
616 | enhanced = gp.GnuplotOpts.prefer_enhanced_postscript | |
617 | if enhanced is not None: |
|
617 | if enhanced is not None: | |
618 | if enhanced: setterm.append('enhanced') |
|
618 | if enhanced: setterm.append('enhanced') | |
619 | else: setterm.append('noenhanced') |
|
619 | else: setterm.append('noenhanced') | |
620 | if color is not None: |
|
620 | if color is not None: | |
621 | if color: setterm.append('color') |
|
621 | if color: setterm.append('color') | |
622 | else: setterm.append('monochrome') |
|
622 | else: setterm.append('monochrome') | |
623 | if solid is not None: |
|
623 | if solid is not None: | |
624 | if solid: setterm.append('solid') |
|
624 | if solid: setterm.append('solid') | |
625 | else: setterm.append('dashed') |
|
625 | else: setterm.append('dashed') | |
626 | if duplexing is not None: |
|
626 | if duplexing is not None: | |
627 | assert duplexing in ['defaultplex', 'simplex', 'duplex'], \ |
|
627 | assert duplexing in ['defaultplex', 'simplex', 'duplex'], \ | |
628 | OptionException('illegal duplexing mode "%s"' % duplexing) |
|
628 | OptionException('illegal duplexing mode "%s"' % duplexing) | |
629 | setterm.append(duplexing) |
|
629 | setterm.append(duplexing) | |
630 | if fontname is not None: |
|
630 | if fontname is not None: | |
631 | setterm.append('"%s"' % fontname) |
|
631 | setterm.append('"%s"' % fontname) | |
632 | if fontsize is not None: |
|
632 | if fontsize is not None: | |
633 | setterm.append('%s' % fontsize) |
|
633 | setterm.append('%s' % fontsize) | |
634 |
|
634 | |||
635 | self(string.join(setterm)) |
|
635 | self(string.join(setterm)) | |
636 | self.set_string('output', filename) |
|
636 | self.set_string('output', filename) | |
637 | # replot the current figure (to the printer): |
|
637 | # replot the current figure (to the printer): | |
638 | self.refresh() |
|
638 | self.refresh() | |
639 |
|
639 | |||
640 | # fperez. Ugly kludge: often for some reason the file is NOT created |
|
640 | # 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! |
|
641 | # and we must reissue the creation commands. I have no idea why! | |
642 | if not lpr_output: |
|
642 | if not lpr_output: | |
643 | #print 'Hardcopy <%s>' % filename # dbg |
|
643 | #print 'Hardcopy <%s>' % filename # dbg | |
644 | maxtries = 20 |
|
644 | maxtries = 20 | |
645 | delay = 0.1 # delay (in seconds) between print attempts |
|
645 | delay = 0.1 # delay (in seconds) between print attempts | |
646 | for i in range(maxtries): |
|
646 | for i in range(maxtries): | |
647 | time.sleep(0.05) # safety, very small delay |
|
647 | time.sleep(0.05) # safety, very small delay | |
648 | if os.path.isfile(filename): |
|
648 | if os.path.isfile(filename): | |
649 | if debug: |
|
649 | if debug: | |
650 | print 'Hardcopy to file <%s> success at attempt #%s.' \ |
|
650 | print 'Hardcopy to file <%s> success at attempt #%s.' \ | |
651 | % (filename,i+1) |
|
651 | % (filename,i+1) | |
652 | break |
|
652 | break | |
653 | time.sleep(delay) |
|
653 | time.sleep(delay) | |
654 | # try again, issue all commands just in case |
|
654 | # try again, issue all commands just in case | |
655 | self(string.join(setterm)) |
|
655 | self(string.join(setterm)) | |
656 | self.set_string('output', filename) |
|
656 | self.set_string('output', filename) | |
657 | self.refresh() |
|
657 | self.refresh() | |
658 | if not os.path.isfile(filename): |
|
658 | if not os.path.isfile(filename): | |
659 | print >> sys.stderr,'ERROR: Tried %s times and failed to '\ |
|
659 | print >> sys.stderr,'ERROR: Tried %s times and failed to '\ | |
660 | 'create hardcopy file `%s`' % (maxtries,filename) |
|
660 | 'create hardcopy file `%s`' % (maxtries,filename) | |
661 |
|
661 | |||
662 | # reset the terminal to its `default' setting: |
|
662 | # reset the terminal to its `default' setting: | |
663 | self('set terminal %s' % gp.GnuplotOpts.default_term) |
|
663 | self('set terminal %s' % gp.GnuplotOpts.default_term) | |
664 | self.set_string('output') |
|
664 | self.set_string('output') | |
665 |
|
665 | |||
666 | #********************** End of file <Gnuplot2.py> ************************ |
|
666 | #********************** End of file <Gnuplot2.py> ************************ |
@@ -1,148 +1,148 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 $""" |
|
13 | $Id: GnuplotInteractive.py 389 2004-10-09 07:59:30Z fperez $""" | |
14 |
|
14 | |||
15 | __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot', |
|
15 | __all__ = ['Gnuplot','gp','gp_new','plot','plot2','splot','replot', | |
16 | 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid', |
|
16 | 'hardcopy','gpdata','gpfile','gpstring','gpfunc','gpgrid', | |
17 | 'gphelp'] |
|
17 | 'gphelp'] | |
18 |
|
18 | |||
19 | import IPython.GnuplotRuntime as GRun |
|
19 | import IPython.GnuplotRuntime as GRun | |
20 | from IPython.genutils import page,warn |
|
20 | from IPython.genutils import page,warn | |
21 |
|
21 | |||
22 | # Set global names for interactive use |
|
22 | # Set global names for interactive use | |
23 | Gnuplot = GRun.Gnuplot |
|
23 | Gnuplot = GRun.Gnuplot | |
24 | gp_new = GRun.gp_new |
|
24 | gp_new = GRun.gp_new | |
25 | gp = GRun.gp |
|
25 | gp = GRun.gp | |
26 | plot = gp.plot |
|
26 | plot = gp.plot | |
27 | plot2 = gp.plot2 |
|
27 | plot2 = gp.plot2 | |
28 | splot = gp.splot |
|
28 | splot = gp.splot | |
29 | replot = gp.replot |
|
29 | replot = gp.replot | |
30 | hardcopy = gp.hardcopy |
|
30 | hardcopy = gp.hardcopy | |
31 |
|
31 | |||
32 | # Accessors for the main plot object constructors: |
|
32 | # Accessors for the main plot object constructors: | |
33 | gpdata = Gnuplot.Data |
|
33 | gpdata = Gnuplot.Data | |
34 | gpfile = Gnuplot.File |
|
34 | gpfile = Gnuplot.File | |
35 | gpstring = Gnuplot.String |
|
35 | gpstring = Gnuplot.String | |
36 | gpfunc = Gnuplot.Func |
|
36 | gpfunc = Gnuplot.Func | |
37 | gpgrid = Gnuplot.GridData |
|
37 | gpgrid = Gnuplot.GridData | |
38 |
|
38 | |||
39 | def gphelp(): |
|
39 | def gphelp(): | |
40 | """Print information about the Gnuplot facilities in IPython.""" |
|
40 | """Print information about the Gnuplot facilities in IPython.""" | |
41 |
|
41 | |||
42 | page(""" |
|
42 | page(""" | |
43 | IPython provides an interface to access the Gnuplot scientific plotting |
|
43 | IPython provides an interface to access the Gnuplot scientific plotting | |
44 | system, in an environment similar to that of Mathematica or Matlab. |
|
44 | system, in an environment similar to that of Mathematica or Matlab. | |
45 |
|
45 | |||
46 | New top-level global objects |
|
46 | New top-level global objects | |
47 | ---------------------------- |
|
47 | ---------------------------- | |
48 |
|
48 | |||
49 | Please see their respective docstrings for further details. |
|
49 | Please see their respective docstrings for further details. | |
50 |
|
50 | |||
51 | - gp: a running Gnuplot instance. You can access its methods as |
|
51 | - 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 |
|
52 | gp.<method>. gp(`a string`) will execute the given string as if it had been | |
53 | typed in an interactive gnuplot window. |
|
53 | typed in an interactive gnuplot window. | |
54 |
|
54 | |||
55 | - plot, splot, replot and hardcopy: aliases to the methods of the same name in |
|
55 | - 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: |
|
56 | the global running Gnuplot instance gp. These allow you to simply type: | |
57 |
|
57 | |||
58 | In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array |
|
58 | In [1]: plot(x,sin(x),title='Sin(x)') # assuming x is a Numeric array | |
59 |
|
59 | |||
60 | and obtain a plot of sin(x) vs x with the title 'Sin(x)'. |
|
60 | and obtain a plot of sin(x) vs x with the title 'Sin(x)'. | |
61 |
|
61 | |||
62 | - gp_new: a function which returns a new Gnuplot instance. This can be used to |
|
62 | - 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 |
|
63 | have multiple Gnuplot instances running in your session to compare different | |
64 | plots, each in a separate window. |
|
64 | plots, each in a separate window. | |
65 |
|
65 | |||
66 | - Gnuplot: alias to the Gnuplot2 module, an improved drop-in replacement for |
|
66 | - 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 |
|
67 | the original Gnuplot.py. Gnuplot2 needs Gnuplot but redefines several of its | |
68 | functions with improved versions (Gnuplot2 comes with IPython). |
|
68 | functions with improved versions (Gnuplot2 comes with IPython). | |
69 |
|
69 | |||
70 | - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data, |
|
70 | - gpdata, gpfile, gpstring, gpfunc, gpgrid: aliases to Gnuplot.Data, | |
71 | Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData |
|
71 | Gnuplot.File, Gnuplot.String, Gnuplot.Func and Gnuplot.GridData | |
72 | respectively. These functions create objects which can then be passed to the |
|
72 | respectively. These functions create objects which can then be passed to the | |
73 | plotting commands. See the Gnuplot.py documentation for details. |
|
73 | plotting commands. See the Gnuplot.py documentation for details. | |
74 |
|
74 | |||
75 | Keep in mind that all commands passed to a Gnuplot instance are executed in |
|
75 | 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 |
|
76 | the Gnuplot namespace, where no Python variables exist. For example, for | |
77 | plotting sin(x) vs x as above, typing |
|
77 | plotting sin(x) vs x as above, typing | |
78 |
|
78 | |||
79 | In [2]: gp('plot x,sin(x)') |
|
79 | In [2]: gp('plot x,sin(x)') | |
80 |
|
80 | |||
81 | would not work. Instead, you would get the plot of BOTH the functions 'x' and |
|
81 | 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() |
|
82 | '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. |
|
83 | method lives in python and does know about these variables. | |
84 |
|
84 | |||
85 |
|
85 | |||
86 | New magic functions |
|
86 | New magic functions | |
87 | ------------------- |
|
87 | ------------------- | |
88 |
|
88 | |||
89 | %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where |
|
89 | %gpc: pass one command to Gnuplot and execute it or open a Gnuplot shell where | |
90 | each line of input is executed. |
|
90 | each line of input is executed. | |
91 |
|
91 | |||
92 | %gp_set_default: reset the value of IPython's global Gnuplot instance.""") |
|
92 | %gp_set_default: reset the value of IPython's global Gnuplot instance.""") | |
93 |
|
93 | |||
94 | # Code below is all for IPython use |
|
94 | # Code below is all for IPython use | |
95 | # Define the magic functions for communicating with the above gnuplot instance. |
|
95 | # Define the magic functions for communicating with the above gnuplot instance. | |
96 | def magic_gpc(self,parameter_s=''): |
|
96 | def magic_gpc(self,parameter_s=''): | |
97 | """Execute a gnuplot command or open a gnuplot shell. |
|
97 | """Execute a gnuplot command or open a gnuplot shell. | |
98 |
|
98 | |||
99 | Usage (omit the % if automagic is on). There are two ways to use it: |
|
99 | Usage (omit the % if automagic is on). There are two ways to use it: | |
100 |
|
100 | |||
101 | 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance. |
|
101 | 1) %gpc 'command' -> passes 'command' directly to the gnuplot instance. | |
102 |
|
102 | |||
103 | 2) %gpc -> will open up a prompt (gnuplot>>>) which takes input like the |
|
103 | 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 |
|
104 | standard gnuplot interactive prompt. If you need to type a multi-line | |
105 | command, use \\ at the end of each intermediate line. |
|
105 | command, use \\ at the end of each intermediate line. | |
106 |
|
106 | |||
107 | Upon exiting of the gnuplot sub-shell, you return to your IPython |
|
107 | 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). |
|
108 | session (the gnuplot sub-shell can be invoked as many times as needed). | |
109 | """ |
|
109 | """ | |
110 |
|
110 | |||
111 | if parameter_s.strip(): |
|
111 | if parameter_s.strip(): | |
112 | self.shell.gnuplot(parameter_s) |
|
112 | self.shell.gnuplot(parameter_s) | |
113 | else: |
|
113 | else: | |
114 | self.shell.gnuplot.interact() |
|
114 | self.shell.gnuplot.interact() | |
115 |
|
115 | |||
116 | def magic_gp_set_default(self,parameter_s=''): |
|
116 | def magic_gp_set_default(self,parameter_s=''): | |
117 | """Set the default gnuplot instance accessed by the %gp magic function. |
|
117 | """Set the default gnuplot instance accessed by the %gp magic function. | |
118 |
|
118 | |||
119 | %gp_set_default name |
|
119 | %gp_set_default name | |
120 |
|
120 | |||
121 | Call with the name of the new instance at the command line. If you want to |
|
121 | 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 |
|
122 | set this instance in your own code (using an embedded IPython, for | |
123 | example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot |
|
123 | example), simply set the variable __IPYTHON__.gnuplot to your own gnuplot | |
124 | instance object.""" |
|
124 | instance object.""" | |
125 |
|
125 | |||
126 | gname = parameter_s.strip() |
|
126 | gname = parameter_s.strip() | |
127 | G = eval(gname,self.shell.user_ns) |
|
127 | G = eval(gname,self.shell.user_ns) | |
128 | self.shell.gnuplot = G |
|
128 | self.shell.gnuplot = G | |
129 | self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2, |
|
129 | self.shell.user_ns.update({'plot':G.plot,'splot':G.splot,'plot2':G.plot2, | |
130 | 'replot':G.replot,'hardcopy':G.hardcopy}) |
|
130 | 'replot':G.replot,'hardcopy':G.hardcopy}) | |
131 |
|
131 | |||
132 | try: |
|
132 | try: | |
133 | __IPYTHON__ |
|
133 | __IPYTHON__ | |
134 | except NameError: |
|
134 | except NameError: | |
135 | pass |
|
135 | pass | |
136 | else: |
|
136 | else: | |
137 | # make the global Gnuplot instance known to IPython |
|
137 | # make the global Gnuplot instance known to IPython | |
138 | __IPYTHON__.gnuplot = GRun.gp |
|
138 | __IPYTHON__.gnuplot = GRun.gp | |
139 | __IPYTHON__.gnuplot.shell_first_time = 1 |
|
139 | __IPYTHON__.gnuplot.shell_first_time = 1 | |
140 |
|
140 | |||
141 | print """*** Type `gphelp` for help on the Gnuplot integration features.""" |
|
141 | print """*** Type `gphelp` for help on the Gnuplot integration features.""" | |
142 |
|
142 | |||
143 | # Add the new magic functions to the class dict |
|
143 | # Add the new magic functions to the class dict | |
144 | from IPython.iplib import InteractiveShell |
|
144 | from IPython.iplib import InteractiveShell | |
145 | InteractiveShell.magic_gpc = magic_gpc |
|
145 | InteractiveShell.magic_gpc = magic_gpc | |
146 | InteractiveShell.magic_gp_set_default = magic_gp_set_default |
|
146 | InteractiveShell.magic_gp_set_default = magic_gp_set_default | |
147 |
|
147 | |||
148 | #********************** End of file <GnuplotInteractive.py> ******************* |
|
148 | #********************** End of file <GnuplotInteractive.py> ******************* |
@@ -1,147 +1,147 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 $""" |
|
51 | $Id: GnuplotRuntime.py 389 2004-10-09 07:59:30Z fperez $""" | |
52 |
|
52 | |||
53 | __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData', |
|
53 | __all__ = ['Gnuplot','gp','gp_new','Data','File','Func','GridData', | |
54 | 'pm3d_config','eps_fix_bbox'] |
|
54 | 'pm3d_config','eps_fix_bbox'] | |
55 |
|
55 | |||
56 | import os,tempfile,sys |
|
56 | import os,tempfile,sys | |
57 | from IPython.genutils import getoutput |
|
57 | from IPython.genutils import getoutput | |
58 |
|
58 | |||
59 | #--------------------------------------------------------------------------- |
|
59 | #--------------------------------------------------------------------------- | |
60 | # Notes on mouse support for Gnuplot.py |
|
60 | # Notes on mouse support for Gnuplot.py | |
61 |
|
61 | |||
62 | # If you do not have a mouse-enabled gnuplot, set gnuplot_mouse to 0. If you |
|
62 | # 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 |
|
63 | # 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, |
|
64 | # extremely useful feature. Mouse support is official as of gnuplot 4.0, | |
65 | # released in April 2004. |
|
65 | # released in April 2004. | |
66 |
|
66 | |||
67 | # For the mouse features to work correctly, you MUST set your Gnuplot.py |
|
67 | # 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 |
|
68 | # module to use temporary files instead of 'inline data' for data | |
69 | # communication. Note that this is the default, so unless you've manually |
|
69 | # 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 |
|
70 | # 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 |
|
71 | # 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 |
|
72 | # 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 |
|
73 | # 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 |
|
74 | # confuses the mouse control system (even though it may be a bit faster than | |
75 | # using temp files). |
|
75 | # using temp files). | |
76 |
|
76 | |||
77 | # As of Gnuplot.py v1.7, a new option was added to use FIFOs (pipes). This |
|
77 | # 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 |
|
78 | # mechanism, while fast, also breaks the mouse system. You must therefore set | |
79 | # the variable prefer_fifo_data to 0 in gp_unix.py. |
|
79 | # the variable prefer_fifo_data to 0 in gp_unix.py. | |
80 |
|
80 | |||
81 | tmpname = tempfile.mktemp() |
|
81 | tmpname = tempfile.mktemp() | |
82 | open(tmpname,'w').write('set mouse') |
|
82 | open(tmpname,'w').write('set mouse') | |
83 | gnu_out = getoutput('gnuplot '+ tmpname) |
|
83 | gnu_out = getoutput('gnuplot '+ tmpname) | |
84 | os.unlink(tmpname) |
|
84 | os.unlink(tmpname) | |
85 | if gnu_out: # Gnuplot won't print anything if it has mouse support |
|
85 | 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." |
|
86 | print "*** Your version of Gnuplot appears not to have mouse support." | |
87 | gnuplot_mouse = 0 |
|
87 | gnuplot_mouse = 0 | |
88 | else: |
|
88 | else: | |
89 | gnuplot_mouse = 1 |
|
89 | gnuplot_mouse = 1 | |
90 | del tmpname,gnu_out |
|
90 | del tmpname,gnu_out | |
91 |
|
91 | |||
92 | # Default state for persistence of new gnuplot instances |
|
92 | # Default state for persistence of new gnuplot instances | |
93 | if os.name in ['nt','dos'] or sys.platform == 'cygwin': |
|
93 | if os.name in ['nt','dos'] or sys.platform == 'cygwin': | |
94 | gnuplot_persist = 0 |
|
94 | gnuplot_persist = 0 | |
95 | else: |
|
95 | else: | |
96 | gnuplot_persist = 1 |
|
96 | gnuplot_persist = 1 | |
97 |
|
97 | |||
98 | import IPython.Gnuplot2 as Gnuplot |
|
98 | import IPython.Gnuplot2 as Gnuplot | |
99 |
|
99 | |||
100 | class NotGiven: pass |
|
100 | class NotGiven: pass | |
101 |
|
101 | |||
102 | def gp_new(mouse=NotGiven,persist=NotGiven): |
|
102 | def gp_new(mouse=NotGiven,persist=NotGiven): | |
103 | """Return a new Gnuplot instance. |
|
103 | """Return a new Gnuplot instance. | |
104 |
|
104 | |||
105 | The instance returned uses the improved methods defined in Gnuplot2. |
|
105 | The instance returned uses the improved methods defined in Gnuplot2. | |
106 |
|
106 | |||
107 | Options (boolean): |
|
107 | Options (boolean): | |
108 |
|
108 | |||
109 | - mouse: if unspecified, the module global gnuplot_mouse is used. |
|
109 | - mouse: if unspecified, the module global gnuplot_mouse is used. | |
110 |
|
110 | |||
111 | - persist: if unspecified, the module global gnuplot_persist is used.""" |
|
111 | - persist: if unspecified, the module global gnuplot_persist is used.""" | |
112 |
|
112 | |||
113 | if mouse is NotGiven: |
|
113 | if mouse is NotGiven: | |
114 | mouse = gnuplot_mouse |
|
114 | mouse = gnuplot_mouse | |
115 | if persist is NotGiven: |
|
115 | if persist is NotGiven: | |
116 | persist = gnuplot_persist |
|
116 | persist = gnuplot_persist | |
117 | g = Gnuplot.Gnuplot(persist=persist) |
|
117 | g = Gnuplot.Gnuplot(persist=persist) | |
118 | if mouse: |
|
118 | if mouse: | |
119 | g('set mouse') |
|
119 | g('set mouse') | |
120 | return g |
|
120 | return g | |
121 |
|
121 | |||
122 | # Global-level names. |
|
122 | # Global-level names. | |
123 |
|
123 | |||
124 | # A global Gnuplot instance for interactive use: |
|
124 | # A global Gnuplot instance for interactive use: | |
125 | gp = gp_new() |
|
125 | gp = gp_new() | |
126 |
|
126 | |||
127 | # Accessors for the main plot object constructors: |
|
127 | # Accessors for the main plot object constructors: | |
128 | Data = Gnuplot.Data |
|
128 | Data = Gnuplot.Data | |
129 | File = Gnuplot.File |
|
129 | File = Gnuplot.File | |
130 | Func = Gnuplot.Func |
|
130 | Func = Gnuplot.Func | |
131 | String = Gnuplot.String |
|
131 | String = Gnuplot.String | |
132 | GridData = Gnuplot.GridData |
|
132 | GridData = Gnuplot.GridData | |
133 |
|
133 | |||
134 | # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot |
|
134 | # A Unix-only function to fix eps files with bad bounding boxes (which Gnuplot | |
135 | # generates when the plot size is set to square): |
|
135 | # generates when the plot size is set to square): | |
136 | eps_fix_bbox = Gnuplot.eps_fix_bbox |
|
136 | eps_fix_bbox = Gnuplot.eps_fix_bbox | |
137 |
|
137 | |||
138 | # String for configuring pm3d. Simply call g(pm3d_config) to execute it. pm3d |
|
138 | # 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 |
|
139 | # is a very nice mode for plotting colormaps on surfaces. Modify the defaults | |
140 | # below to suit your taste. |
|
140 | # below to suit your taste. | |
141 | pm3d_config = """ |
|
141 | pm3d_config = """ | |
142 | set pm3d solid |
|
142 | set pm3d solid | |
143 | set hidden3d |
|
143 | set hidden3d | |
144 | unset surface |
|
144 | unset surface | |
145 | set isosamples 50 |
|
145 | set isosamples 50 | |
146 | """ |
|
146 | """ | |
147 | #******************** End of file <GnuplotRuntime.py> ****************** |
|
147 | #******************** End of file <GnuplotRuntime.py> ****************** |
This diff has been collapsed as it changes many lines, (568 lines changed) Show them Hide them | |||||
@@ -1,284 +1,284 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 "Isn't this $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 $ |
|
47 | $Id: Itpl.py 2918 2007-12-31 14:34:47Z vivainio $ | |
48 | """ # ' -> close an open quote for stupid emacs |
|
48 | """ # ' -> close an open quote for stupid emacs | |
49 |
|
49 | |||
50 | #***************************************************************************** |
|
50 | #***************************************************************************** | |
51 | # |
|
51 | # | |
52 | # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org> |
|
52 | # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org> | |
53 | # |
|
53 | # | |
54 | # |
|
54 | # | |
55 | # Published under the terms of the MIT license, hereby reproduced: |
|
55 | # Published under the terms of the MIT license, hereby reproduced: | |
56 | # |
|
56 | # | |
57 | # Permission is hereby granted, free of charge, to any person obtaining a copy |
|
57 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
58 | # of this software and associated documentation files (the "Software"), to |
|
58 | # of this software and associated documentation files (the "Software"), to | |
59 | # deal in the Software without restriction, including without limitation the |
|
59 | # deal in the Software without restriction, including without limitation the | |
60 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|
60 | # 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 |
|
61 | # sell copies of the Software, and to permit persons to whom the Software is | |
62 | # furnished to do so, subject to the following conditions: |
|
62 | # furnished to do so, subject to the following conditions: | |
63 | # |
|
63 | # | |
64 | # The above copyright notice and this permission notice shall be included in |
|
64 | # The above copyright notice and this permission notice shall be included in | |
65 | # all copies or substantial portions of the Software. |
|
65 | # all copies or substantial portions of the Software. | |
66 | # |
|
66 | # | |
67 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
67 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
68 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
68 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
69 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
69 | # 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 |
|
70 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
71 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
71 | # 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 |
|
72 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
73 | # IN THE SOFTWARE. |
|
73 | # IN THE SOFTWARE. | |
74 | # |
|
74 | # | |
75 | #***************************************************************************** |
|
75 | #***************************************************************************** | |
76 |
|
76 | |||
77 | __author__ = 'Ka-Ping Yee <ping@lfw.org>' |
|
77 | __author__ = 'Ka-Ping Yee <ping@lfw.org>' | |
78 | __license__ = 'MIT' |
|
78 | __license__ = 'MIT' | |
79 |
|
79 | |||
80 | import string |
|
80 | import string | |
81 | import sys |
|
81 | import sys | |
82 | from tokenize import tokenprog |
|
82 | from tokenize import tokenprog | |
83 | from types import StringType |
|
83 | from types import StringType | |
84 |
|
84 | |||
85 | class ItplError(ValueError): |
|
85 | class ItplError(ValueError): | |
86 | def __init__(self, text, pos): |
|
86 | def __init__(self, text, pos): | |
87 | self.text = text |
|
87 | self.text = text | |
88 | self.pos = pos |
|
88 | self.pos = pos | |
89 | def __str__(self): |
|
89 | def __str__(self): | |
90 | return "unfinished expression in %s at char %d" % ( |
|
90 | return "unfinished expression in %s at char %d" % ( | |
91 | repr(self.text), self.pos) |
|
91 | repr(self.text), self.pos) | |
92 |
|
92 | |||
93 | def matchorfail(text, pos): |
|
93 | def matchorfail(text, pos): | |
94 | match = tokenprog.match(text, pos) |
|
94 | match = tokenprog.match(text, pos) | |
95 | if match is None: |
|
95 | if match is None: | |
96 | raise ItplError(text, pos) |
|
96 | raise ItplError(text, pos) | |
97 | return match, match.end() |
|
97 | return match, match.end() | |
98 |
|
98 | |||
99 | class Itpl: |
|
99 | class Itpl: | |
100 | """Class representing a string with interpolation abilities. |
|
100 | """Class representing a string with interpolation abilities. | |
101 |
|
101 | |||
102 | Upon creation, an instance works out what parts of the format |
|
102 | Upon creation, an instance works out what parts of the format | |
103 | string are literal and what parts need to be evaluated. The |
|
103 | string are literal and what parts need to be evaluated. The | |
104 | evaluation and substitution happens in the namespace of the |
|
104 | evaluation and substitution happens in the namespace of the | |
105 | caller when str(instance) is called.""" |
|
105 | caller when str(instance) is called.""" | |
106 |
|
106 | |||
107 | def __init__(self, format,codec=sys.stdin.encoding,encoding_errors='backslashreplace'): |
|
107 | def __init__(self, format,codec=sys.stdin.encoding,encoding_errors='backslashreplace'): | |
108 | """The single mandatory argument to this constructor is a format |
|
108 | """The single mandatory argument to this constructor is a format | |
109 | string. |
|
109 | string. | |
110 |
|
110 | |||
111 | The format string is parsed according to the following rules: |
|
111 | The format string is parsed according to the following rules: | |
112 |
|
112 | |||
113 | 1. A dollar sign and a name, possibly followed by any of: |
|
113 | 1. A dollar sign and a name, possibly followed by any of: | |
114 | - an open-paren, and anything up to the matching paren |
|
114 | - an open-paren, and anything up to the matching paren | |
115 | - an open-bracket, and anything up to the matching bracket |
|
115 | - an open-bracket, and anything up to the matching bracket | |
116 | - a period and a name |
|
116 | - a period and a name | |
117 | any number of times, is evaluated as a Python expression. |
|
117 | any number of times, is evaluated as a Python expression. | |
118 |
|
118 | |||
119 | 2. A dollar sign immediately followed by an open-brace, and |
|
119 | 2. A dollar sign immediately followed by an open-brace, and | |
120 | anything up to the matching close-brace, is evaluated as |
|
120 | anything up to the matching close-brace, is evaluated as | |
121 | a Python expression. |
|
121 | a Python expression. | |
122 |
|
122 | |||
123 | 3. Outside of the expressions described in the above two rules, |
|
123 | 3. Outside of the expressions described in the above two rules, | |
124 | two dollar signs in a row give you one literal dollar sign. |
|
124 | two dollar signs in a row give you one literal dollar sign. | |
125 |
|
125 | |||
126 | Optional arguments: |
|
126 | Optional arguments: | |
127 |
|
127 | |||
128 | - codec('utf_8'): a string containing the name of a valid Python |
|
128 | - codec('utf_8'): a string containing the name of a valid Python | |
129 | codec. |
|
129 | codec. | |
130 |
|
130 | |||
131 | - encoding_errors('backslashreplace'): a string with a valid error handling |
|
131 | - encoding_errors('backslashreplace'): a string with a valid error handling | |
132 | policy. See the codecs module documentation for details. |
|
132 | policy. See the codecs module documentation for details. | |
133 |
|
133 | |||
134 | These are used to encode the format string if a call to str() fails on |
|
134 | These are used to encode the format string if a call to str() fails on | |
135 | the expanded result.""" |
|
135 | the expanded result.""" | |
136 |
|
136 | |||
137 | if not isinstance(format,basestring): |
|
137 | if not isinstance(format,basestring): | |
138 | raise TypeError, "needs string initializer" |
|
138 | raise TypeError, "needs string initializer" | |
139 | self.format = format |
|
139 | self.format = format | |
140 | self.codec = codec |
|
140 | self.codec = codec | |
141 | self.encoding_errors = encoding_errors |
|
141 | self.encoding_errors = encoding_errors | |
142 |
|
142 | |||
143 | namechars = "abcdefghijklmnopqrstuvwxyz" \ |
|
143 | namechars = "abcdefghijklmnopqrstuvwxyz" \ | |
144 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; |
|
144 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; | |
145 | chunks = [] |
|
145 | chunks = [] | |
146 | pos = 0 |
|
146 | pos = 0 | |
147 |
|
147 | |||
148 | while 1: |
|
148 | while 1: | |
149 | dollar = string.find(format, "$", pos) |
|
149 | dollar = string.find(format, "$", pos) | |
150 | if dollar < 0: break |
|
150 | if dollar < 0: break | |
151 | nextchar = format[dollar+1] |
|
151 | nextchar = format[dollar+1] | |
152 |
|
152 | |||
153 | if nextchar == "{": |
|
153 | if nextchar == "{": | |
154 | chunks.append((0, format[pos:dollar])) |
|
154 | chunks.append((0, format[pos:dollar])) | |
155 | pos, level = dollar+2, 1 |
|
155 | pos, level = dollar+2, 1 | |
156 | while level: |
|
156 | while level: | |
157 | match, pos = matchorfail(format, pos) |
|
157 | match, pos = matchorfail(format, pos) | |
158 | tstart, tend = match.regs[3] |
|
158 | tstart, tend = match.regs[3] | |
159 | token = format[tstart:tend] |
|
159 | token = format[tstart:tend] | |
160 | if token == "{": level = level+1 |
|
160 | if token == "{": level = level+1 | |
161 | elif token == "}": level = level-1 |
|
161 | elif token == "}": level = level-1 | |
162 | chunks.append((1, format[dollar+2:pos-1])) |
|
162 | chunks.append((1, format[dollar+2:pos-1])) | |
163 |
|
163 | |||
164 | elif nextchar in namechars: |
|
164 | elif nextchar in namechars: | |
165 | chunks.append((0, format[pos:dollar])) |
|
165 | chunks.append((0, format[pos:dollar])) | |
166 | match, pos = matchorfail(format, dollar+1) |
|
166 | match, pos = matchorfail(format, dollar+1) | |
167 | while pos < len(format): |
|
167 | while pos < len(format): | |
168 | if format[pos] == "." and \ |
|
168 | if format[pos] == "." and \ | |
169 | pos+1 < len(format) and format[pos+1] in namechars: |
|
169 | pos+1 < len(format) and format[pos+1] in namechars: | |
170 | match, pos = matchorfail(format, pos+1) |
|
170 | match, pos = matchorfail(format, pos+1) | |
171 | elif format[pos] in "([": |
|
171 | elif format[pos] in "([": | |
172 | pos, level = pos+1, 1 |
|
172 | pos, level = pos+1, 1 | |
173 | while level: |
|
173 | while level: | |
174 | match, pos = matchorfail(format, pos) |
|
174 | match, pos = matchorfail(format, pos) | |
175 | tstart, tend = match.regs[3] |
|
175 | tstart, tend = match.regs[3] | |
176 | token = format[tstart:tend] |
|
176 | token = format[tstart:tend] | |
177 | if token[0] in "([": level = level+1 |
|
177 | if token[0] in "([": level = level+1 | |
178 | elif token[0] in ")]": level = level-1 |
|
178 | elif token[0] in ")]": level = level-1 | |
179 | else: break |
|
179 | else: break | |
180 | chunks.append((1, format[dollar+1:pos])) |
|
180 | chunks.append((1, format[dollar+1:pos])) | |
181 |
|
181 | |||
182 | else: |
|
182 | else: | |
183 | chunks.append((0, format[pos:dollar+1])) |
|
183 | chunks.append((0, format[pos:dollar+1])) | |
184 | pos = dollar + 1 + (nextchar == "$") |
|
184 | pos = dollar + 1 + (nextchar == "$") | |
185 |
|
185 | |||
186 | if pos < len(format): chunks.append((0, format[pos:])) |
|
186 | if pos < len(format): chunks.append((0, format[pos:])) | |
187 | self.chunks = chunks |
|
187 | self.chunks = chunks | |
188 |
|
188 | |||
189 | def __repr__(self): |
|
189 | def __repr__(self): | |
190 | return "<Itpl %s >" % repr(self.format) |
|
190 | return "<Itpl %s >" % repr(self.format) | |
191 |
|
191 | |||
192 | def _str(self,glob,loc): |
|
192 | def _str(self,glob,loc): | |
193 | """Evaluate to a string in the given globals/locals. |
|
193 | """Evaluate to a string in the given globals/locals. | |
194 |
|
194 | |||
195 | The final output is built by calling str(), but if this fails, the |
|
195 | 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, |
|
196 | result is encoded with the instance's codec and error handling policy, | |
197 | via a call to out.encode(self.codec,self.encoding_errors)""" |
|
197 | via a call to out.encode(self.codec,self.encoding_errors)""" | |
198 | result = [] |
|
198 | result = [] | |
199 | app = result.append |
|
199 | app = result.append | |
200 | for live, chunk in self.chunks: |
|
200 | for live, chunk in self.chunks: | |
201 | if live: |
|
201 | if live: | |
202 | val = eval(chunk,glob,loc) |
|
202 | val = eval(chunk,glob,loc) | |
203 | try: |
|
203 | try: | |
204 | app(str(val)) |
|
204 | app(str(val)) | |
205 | except UnicodeEncodeError: |
|
205 | except UnicodeEncodeError: | |
206 | app(unicode(val)) |
|
206 | app(unicode(val)) | |
207 |
|
207 | |||
208 | else: app(chunk) |
|
208 | else: app(chunk) | |
209 | out = ''.join(result) |
|
209 | out = ''.join(result) | |
210 | try: |
|
210 | try: | |
211 | return str(out) |
|
211 | return str(out) | |
212 | except UnicodeError: |
|
212 | except UnicodeError: | |
213 | return out.encode(self.codec,self.encoding_errors) |
|
213 | return out.encode(self.codec,self.encoding_errors) | |
214 |
|
214 | |||
215 | def __str__(self): |
|
215 | def __str__(self): | |
216 | """Evaluate and substitute the appropriate parts of the string.""" |
|
216 | """Evaluate and substitute the appropriate parts of the string.""" | |
217 |
|
217 | |||
218 | # We need to skip enough frames to get to the actual caller outside of |
|
218 | # We need to skip enough frames to get to the actual caller outside of | |
219 | # Itpl. |
|
219 | # Itpl. | |
220 | frame = sys._getframe(1) |
|
220 | frame = sys._getframe(1) | |
221 | while frame.f_globals["__name__"] == __name__: frame = frame.f_back |
|
221 | while frame.f_globals["__name__"] == __name__: frame = frame.f_back | |
222 | loc, glob = frame.f_locals, frame.f_globals |
|
222 | loc, glob = frame.f_locals, frame.f_globals | |
223 |
|
223 | |||
224 | return self._str(glob,loc) |
|
224 | return self._str(glob,loc) | |
225 |
|
225 | |||
226 | class ItplNS(Itpl): |
|
226 | class ItplNS(Itpl): | |
227 | """Class representing a string with interpolation abilities. |
|
227 | """Class representing a string with interpolation abilities. | |
228 |
|
228 | |||
229 | This inherits from Itpl, but at creation time a namespace is provided |
|
229 | This inherits from Itpl, but at creation time a namespace is provided | |
230 | where the evaluation will occur. The interpolation becomes a bit more |
|
230 | where the evaluation will occur. The interpolation becomes a bit more | |
231 | efficient, as no traceback needs to be extracte. It also allows the |
|
231 | efficient, as no traceback needs to be extracte. It also allows the | |
232 | caller to supply a different namespace for the interpolation to occur than |
|
232 | caller to supply a different namespace for the interpolation to occur than | |
233 | its own.""" |
|
233 | its own.""" | |
234 |
|
234 | |||
235 | def __init__(self, format,globals,locals=None, |
|
235 | def __init__(self, format,globals,locals=None, | |
236 | codec='utf_8',encoding_errors='backslashreplace'): |
|
236 | codec='utf_8',encoding_errors='backslashreplace'): | |
237 | """ItplNS(format,globals[,locals]) -> interpolating string instance. |
|
237 | """ItplNS(format,globals[,locals]) -> interpolating string instance. | |
238 |
|
238 | |||
239 | This constructor, besides a format string, takes a globals dictionary |
|
239 | This constructor, besides a format string, takes a globals dictionary | |
240 | and optionally a locals (which defaults to globals if not provided). |
|
240 | and optionally a locals (which defaults to globals if not provided). | |
241 |
|
241 | |||
242 | For further details, see the Itpl constructor.""" |
|
242 | For further details, see the Itpl constructor.""" | |
243 |
|
243 | |||
244 | if locals is None: |
|
244 | if locals is None: | |
245 | locals = globals |
|
245 | locals = globals | |
246 | self.globals = globals |
|
246 | self.globals = globals | |
247 | self.locals = locals |
|
247 | self.locals = locals | |
248 | Itpl.__init__(self,format,codec,encoding_errors) |
|
248 | Itpl.__init__(self,format,codec,encoding_errors) | |
249 |
|
249 | |||
250 | def __str__(self): |
|
250 | def __str__(self): | |
251 | """Evaluate and substitute the appropriate parts of the string.""" |
|
251 | """Evaluate and substitute the appropriate parts of the string.""" | |
252 | return self._str(self.globals,self.locals) |
|
252 | return self._str(self.globals,self.locals) | |
253 |
|
253 | |||
254 | def __repr__(self): |
|
254 | def __repr__(self): | |
255 | return "<ItplNS %s >" % repr(self.format) |
|
255 | return "<ItplNS %s >" % repr(self.format) | |
256 |
|
256 | |||
257 | # utilities for fast printing |
|
257 | # utilities for fast printing | |
258 | def itpl(text): return str(Itpl(text)) |
|
258 | def itpl(text): return str(Itpl(text)) | |
259 | def printpl(text): print itpl(text) |
|
259 | def printpl(text): print itpl(text) | |
260 | # versions with namespace |
|
260 | # versions with namespace | |
261 | def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals)) |
|
261 | def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals)) | |
262 | def printplns(text,globals,locals=None): print itplns(text,globals,locals) |
|
262 | def printplns(text,globals,locals=None): print itplns(text,globals,locals) | |
263 |
|
263 | |||
264 | class ItplFile: |
|
264 | class ItplFile: | |
265 | """A file object that filters each write() through an interpolator.""" |
|
265 | """A file object that filters each write() through an interpolator.""" | |
266 | def __init__(self, file): self.file = file |
|
266 | def __init__(self, file): self.file = file | |
267 | def __repr__(self): return "<interpolated " + repr(self.file) + ">" |
|
267 | def __repr__(self): return "<interpolated " + repr(self.file) + ">" | |
268 | def __getattr__(self, attr): return getattr(self.file, attr) |
|
268 | def __getattr__(self, attr): return getattr(self.file, attr) | |
269 | def write(self, text): self.file.write(str(Itpl(text))) |
|
269 | def write(self, text): self.file.write(str(Itpl(text))) | |
270 |
|
270 | |||
271 | def filter(file=sys.stdout): |
|
271 | def filter(file=sys.stdout): | |
272 | """Return an ItplFile that filters writes to the given file object. |
|
272 | """Return an ItplFile that filters writes to the given file object. | |
273 |
|
273 | |||
274 | 'file = filter(file)' replaces 'file' with a filtered object that |
|
274 | 'file = filter(file)' replaces 'file' with a filtered object that | |
275 | has a write() method. When called with no argument, this creates |
|
275 | has a write() method. When called with no argument, this creates | |
276 | a filter to sys.stdout.""" |
|
276 | a filter to sys.stdout.""" | |
277 | return ItplFile(file) |
|
277 | return ItplFile(file) | |
278 |
|
278 | |||
279 | def unfilter(ifile=None): |
|
279 | def unfilter(ifile=None): | |
280 | """Return the original file that corresponds to the given ItplFile. |
|
280 | """Return the original file that corresponds to the given ItplFile. | |
281 |
|
281 | |||
282 | 'file = unfilter(file)' undoes the effect of 'file = filter(file)'. |
|
282 | 'file = unfilter(file)' undoes the effect of 'file = filter(file)'. | |
283 | 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'.""" |
|
283 | 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'.""" | |
284 | return ifile and ifile.file or sys.stdout.file |
|
284 | return ifile and ifile.file or sys.stdout.file |
This diff has been collapsed as it changes many lines, (540 lines changed) Show them Hide them | |||||
@@ -1,270 +1,270 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 |
|
4 | |||
5 | $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $ |
|
5 | $Id: Logger.py 2875 2007-11-26 08:37:39Z fperez $ | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
9 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and |
|
9 | # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and | |
10 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
10 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
11 | # |
|
11 | # | |
12 | # Distributed under the terms of the BSD License. The full license is in |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
13 | # the file COPYING, distributed as part of this software. |
|
13 | # the file COPYING, distributed as part of this software. | |
14 | #***************************************************************************** |
|
14 | #***************************************************************************** | |
15 |
|
15 | |||
16 | #**************************************************************************** |
|
16 | #**************************************************************************** | |
17 | # Modules and globals |
|
17 | # Modules and globals | |
18 |
|
18 | |||
19 | from IPython import Release |
|
19 | from IPython import Release | |
20 | __author__ = '%s <%s>\n%s <%s>' % \ |
|
20 | __author__ = '%s <%s>\n%s <%s>' % \ | |
21 | ( Release.authors['Janko'] + Release.authors['Fernando'] ) |
|
21 | ( Release.authors['Janko'] + Release.authors['Fernando'] ) | |
22 | __license__ = Release.license |
|
22 | __license__ = Release.license | |
23 |
|
23 | |||
24 | # Python standard modules |
|
24 | # Python standard modules | |
25 | import glob |
|
25 | import glob | |
26 | import os |
|
26 | import os | |
27 | import time |
|
27 | import time | |
28 |
|
28 | |||
29 | #**************************************************************************** |
|
29 | #**************************************************************************** | |
30 | # FIXME: This class isn't a mixin anymore, but it still needs attributes from |
|
30 | # FIXME: This class isn't a mixin anymore, but it still needs attributes from | |
31 | # ipython and does input cache management. Finish cleanup later... |
|
31 | # ipython and does input cache management. Finish cleanup later... | |
32 |
|
32 | |||
33 | class Logger(object): |
|
33 | class Logger(object): | |
34 | """A Logfile class with different policies for file creation""" |
|
34 | """A Logfile class with different policies for file creation""" | |
35 |
|
35 | |||
36 | def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'): |
|
36 | def __init__(self,shell,logfname='Logger.log',loghead='',logmode='over'): | |
37 |
|
37 | |||
38 | self._i00,self._i,self._ii,self._iii = '','','','' |
|
38 | self._i00,self._i,self._ii,self._iii = '','','','' | |
39 |
|
39 | |||
40 | # this is the full ipython instance, we need some attributes from it |
|
40 | # 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... |
|
41 | # which won't exist until later. What a mess, clean up later... | |
42 | self.shell = shell |
|
42 | self.shell = shell | |
43 |
|
43 | |||
44 | self.logfname = logfname |
|
44 | self.logfname = logfname | |
45 | self.loghead = loghead |
|
45 | self.loghead = loghead | |
46 | self.logmode = logmode |
|
46 | self.logmode = logmode | |
47 | self.logfile = None |
|
47 | self.logfile = None | |
48 |
|
48 | |||
49 | # Whether to log raw or processed input |
|
49 | # Whether to log raw or processed input | |
50 | self.log_raw_input = False |
|
50 | self.log_raw_input = False | |
51 |
|
51 | |||
52 | # whether to also log output |
|
52 | # whether to also log output | |
53 | self.log_output = False |
|
53 | self.log_output = False | |
54 |
|
54 | |||
55 | # whether to put timestamps before each log entry |
|
55 | # whether to put timestamps before each log entry | |
56 | self.timestamp = False |
|
56 | self.timestamp = False | |
57 |
|
57 | |||
58 | # activity control flags |
|
58 | # activity control flags | |
59 | self.log_active = False |
|
59 | self.log_active = False | |
60 |
|
60 | |||
61 | # logmode is a validated property |
|
61 | # logmode is a validated property | |
62 | def _set_mode(self,mode): |
|
62 | def _set_mode(self,mode): | |
63 | if mode not in ['append','backup','global','over','rotate']: |
|
63 | if mode not in ['append','backup','global','over','rotate']: | |
64 | raise ValueError,'invalid log mode %s given' % mode |
|
64 | raise ValueError,'invalid log mode %s given' % mode | |
65 | self._logmode = mode |
|
65 | self._logmode = mode | |
66 |
|
66 | |||
67 | def _get_mode(self): |
|
67 | def _get_mode(self): | |
68 | return self._logmode |
|
68 | return self._logmode | |
69 |
|
69 | |||
70 | logmode = property(_get_mode,_set_mode) |
|
70 | logmode = property(_get_mode,_set_mode) | |
71 |
|
71 | |||
72 | def logstart(self,logfname=None,loghead=None,logmode=None, |
|
72 | def logstart(self,logfname=None,loghead=None,logmode=None, | |
73 | log_output=False,timestamp=False,log_raw_input=False): |
|
73 | log_output=False,timestamp=False,log_raw_input=False): | |
74 | """Generate a new log-file with a default header. |
|
74 | """Generate a new log-file with a default header. | |
75 |
|
75 | |||
76 | Raises RuntimeError if the log has already been started""" |
|
76 | Raises RuntimeError if the log has already been started""" | |
77 |
|
77 | |||
78 | if self.logfile is not None: |
|
78 | if self.logfile is not None: | |
79 | raise RuntimeError('Log file is already active: %s' % |
|
79 | raise RuntimeError('Log file is already active: %s' % | |
80 | self.logfname) |
|
80 | self.logfname) | |
81 |
|
81 | |||
82 | self.log_active = True |
|
82 | self.log_active = True | |
83 |
|
83 | |||
84 | # The parameters can override constructor defaults |
|
84 | # The parameters can override constructor defaults | |
85 | if logfname is not None: self.logfname = logfname |
|
85 | if logfname is not None: self.logfname = logfname | |
86 | if loghead is not None: self.loghead = loghead |
|
86 | if loghead is not None: self.loghead = loghead | |
87 | if logmode is not None: self.logmode = logmode |
|
87 | if logmode is not None: self.logmode = logmode | |
88 |
|
88 | |||
89 | # Parameters not part of the constructor |
|
89 | # Parameters not part of the constructor | |
90 | self.timestamp = timestamp |
|
90 | self.timestamp = timestamp | |
91 | self.log_output = log_output |
|
91 | self.log_output = log_output | |
92 | self.log_raw_input = log_raw_input |
|
92 | self.log_raw_input = log_raw_input | |
93 |
|
93 | |||
94 | # init depending on the log mode requested |
|
94 | # init depending on the log mode requested | |
95 | isfile = os.path.isfile |
|
95 | isfile = os.path.isfile | |
96 | logmode = self.logmode |
|
96 | logmode = self.logmode | |
97 |
|
97 | |||
98 | if logmode == 'append': |
|
98 | if logmode == 'append': | |
99 | self.logfile = open(self.logfname,'a') |
|
99 | self.logfile = open(self.logfname,'a') | |
100 |
|
100 | |||
101 | elif logmode == 'backup': |
|
101 | elif logmode == 'backup': | |
102 | if isfile(self.logfname): |
|
102 | if isfile(self.logfname): | |
103 | backup_logname = self.logfname+'~' |
|
103 | backup_logname = self.logfname+'~' | |
104 | # Manually remove any old backup, since os.rename may fail |
|
104 | # Manually remove any old backup, since os.rename may fail | |
105 | # under Windows. |
|
105 | # under Windows. | |
106 | if isfile(backup_logname): |
|
106 | if isfile(backup_logname): | |
107 | os.remove(backup_logname) |
|
107 | os.remove(backup_logname) | |
108 | os.rename(self.logfname,backup_logname) |
|
108 | os.rename(self.logfname,backup_logname) | |
109 | self.logfile = open(self.logfname,'w') |
|
109 | self.logfile = open(self.logfname,'w') | |
110 |
|
110 | |||
111 | elif logmode == 'global': |
|
111 | elif logmode == 'global': | |
112 | self.logfname = os.path.join(self.shell.home_dir,self.logfname) |
|
112 | self.logfname = os.path.join(self.shell.home_dir,self.logfname) | |
113 | self.logfile = open(self.logfname, 'a') |
|
113 | self.logfile = open(self.logfname, 'a') | |
114 |
|
114 | |||
115 | elif logmode == 'over': |
|
115 | elif logmode == 'over': | |
116 | if isfile(self.logfname): |
|
116 | if isfile(self.logfname): | |
117 | os.remove(self.logfname) |
|
117 | os.remove(self.logfname) | |
118 | self.logfile = open(self.logfname,'w') |
|
118 | self.logfile = open(self.logfname,'w') | |
119 |
|
119 | |||
120 | elif logmode == 'rotate': |
|
120 | elif logmode == 'rotate': | |
121 | if isfile(self.logfname): |
|
121 | if isfile(self.logfname): | |
122 | if isfile(self.logfname+'.001~'): |
|
122 | if isfile(self.logfname+'.001~'): | |
123 | old = glob.glob(self.logfname+'.*~') |
|
123 | old = glob.glob(self.logfname+'.*~') | |
124 | old.sort() |
|
124 | old.sort() | |
125 | old.reverse() |
|
125 | old.reverse() | |
126 | for f in old: |
|
126 | for f in old: | |
127 | root, ext = os.path.splitext(f) |
|
127 | root, ext = os.path.splitext(f) | |
128 | num = int(ext[1:-1])+1 |
|
128 | num = int(ext[1:-1])+1 | |
129 | os.rename(f, root+'.'+`num`.zfill(3)+'~') |
|
129 | os.rename(f, root+'.'+`num`.zfill(3)+'~') | |
130 | os.rename(self.logfname, self.logfname+'.001~') |
|
130 | os.rename(self.logfname, self.logfname+'.001~') | |
131 | self.logfile = open(self.logfname,'w') |
|
131 | self.logfile = open(self.logfname,'w') | |
132 |
|
132 | |||
133 | if logmode != 'append': |
|
133 | if logmode != 'append': | |
134 | self.logfile.write(self.loghead) |
|
134 | self.logfile.write(self.loghead) | |
135 |
|
135 | |||
136 | self.logfile.flush() |
|
136 | self.logfile.flush() | |
137 |
|
137 | |||
138 | def switch_log(self,val): |
|
138 | def switch_log(self,val): | |
139 | """Switch logging on/off. val should be ONLY a boolean.""" |
|
139 | """Switch logging on/off. val should be ONLY a boolean.""" | |
140 |
|
140 | |||
141 | if val not in [False,True,0,1]: |
|
141 | if val not in [False,True,0,1]: | |
142 | raise ValueError, \ |
|
142 | raise ValueError, \ | |
143 | 'Call switch_log ONLY with a boolean argument, not with:',val |
|
143 | 'Call switch_log ONLY with a boolean argument, not with:',val | |
144 |
|
144 | |||
145 | label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} |
|
145 | label = {0:'OFF',1:'ON',False:'OFF',True:'ON'} | |
146 |
|
146 | |||
147 | if self.logfile is None: |
|
147 | if self.logfile is None: | |
148 | print """ |
|
148 | print """ | |
149 | Logging hasn't been started yet (use logstart for that). |
|
149 | Logging hasn't been started yet (use logstart for that). | |
150 |
|
150 | |||
151 | %logon/%logoff are for temporarily starting and stopping logging for a logfile |
|
151 | %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 |
|
152 | which already exists. But you must first start the logging process with | |
153 | %logstart (optionally giving a logfile name).""" |
|
153 | %logstart (optionally giving a logfile name).""" | |
154 |
|
154 | |||
155 | else: |
|
155 | else: | |
156 | if self.log_active == val: |
|
156 | if self.log_active == val: | |
157 | print 'Logging is already',label[val] |
|
157 | print 'Logging is already',label[val] | |
158 | else: |
|
158 | else: | |
159 | print 'Switching logging',label[val] |
|
159 | print 'Switching logging',label[val] | |
160 | self.log_active = not self.log_active |
|
160 | self.log_active = not self.log_active | |
161 | self.log_active_out = self.log_active |
|
161 | self.log_active_out = self.log_active | |
162 |
|
162 | |||
163 | def logstate(self): |
|
163 | def logstate(self): | |
164 | """Print a status message about the logger.""" |
|
164 | """Print a status message about the logger.""" | |
165 | if self.logfile is None: |
|
165 | if self.logfile is None: | |
166 | print 'Logging has not been activated.' |
|
166 | print 'Logging has not been activated.' | |
167 | else: |
|
167 | else: | |
168 | state = self.log_active and 'active' or 'temporarily suspended' |
|
168 | state = self.log_active and 'active' or 'temporarily suspended' | |
169 | print 'Filename :',self.logfname |
|
169 | print 'Filename :',self.logfname | |
170 | print 'Mode :',self.logmode |
|
170 | print 'Mode :',self.logmode | |
171 | print 'Output logging :',self.log_output |
|
171 | print 'Output logging :',self.log_output | |
172 | print 'Raw input log :',self.log_raw_input |
|
172 | print 'Raw input log :',self.log_raw_input | |
173 | print 'Timestamping :',self.timestamp |
|
173 | print 'Timestamping :',self.timestamp | |
174 | print 'State :',state |
|
174 | print 'State :',state | |
175 |
|
175 | |||
176 | def log(self,line_ori,line_mod,continuation=None): |
|
176 | def log(self,line_ori,line_mod,continuation=None): | |
177 | """Write the line to a log and create input cache variables _i*. |
|
177 | """Write the line to a log and create input cache variables _i*. | |
178 |
|
178 | |||
179 | Inputs: |
|
179 | Inputs: | |
180 |
|
180 | |||
181 | - line_ori: unmodified input line from the user. This is not |
|
181 | - line_ori: unmodified input line from the user. This is not | |
182 | necessarily valid Python. |
|
182 | necessarily valid Python. | |
183 |
|
183 | |||
184 | - line_mod: possibly modified input, such as the transformations made |
|
184 | - line_mod: possibly modified input, such as the transformations made | |
185 | by input prefilters or input handlers of various kinds. This should |
|
185 | by input prefilters or input handlers of various kinds. This should | |
186 | always be valid Python. |
|
186 | always be valid Python. | |
187 |
|
187 | |||
188 | - continuation: if True, indicates this is part of multi-line input.""" |
|
188 | - continuation: if True, indicates this is part of multi-line input.""" | |
189 |
|
189 | |||
190 | # update the auto _i tables |
|
190 | # update the auto _i tables | |
191 | #print '***logging line',line_mod # dbg |
|
191 | #print '***logging line',line_mod # dbg | |
192 | #print '***cache_count', self.shell.outputcache.prompt_count # dbg |
|
192 | #print '***cache_count', self.shell.outputcache.prompt_count # dbg | |
193 | try: |
|
193 | try: | |
194 | input_hist = self.shell.user_ns['_ih'] |
|
194 | input_hist = self.shell.user_ns['_ih'] | |
195 | except: |
|
195 | except: | |
196 | print 'userns:',self.shell.user_ns.keys() |
|
196 | print 'userns:',self.shell.user_ns.keys() | |
197 | return |
|
197 | return | |
198 |
|
198 | |||
199 | out_cache = self.shell.outputcache |
|
199 | out_cache = self.shell.outputcache | |
200 |
|
200 | |||
201 | # add blank lines if the input cache fell out of sync. |
|
201 | # add blank lines if the input cache fell out of sync. | |
202 | if out_cache.do_full_cache and \ |
|
202 | if out_cache.do_full_cache and \ | |
203 | out_cache.prompt_count +1 > len(input_hist): |
|
203 | out_cache.prompt_count +1 > len(input_hist): | |
204 | input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist))) |
|
204 | input_hist.extend(['\n'] * (out_cache.prompt_count - len(input_hist))) | |
205 |
|
205 | |||
206 | if not continuation and line_mod: |
|
206 | if not continuation and line_mod: | |
207 | self._iii = self._ii |
|
207 | self._iii = self._ii | |
208 | self._ii = self._i |
|
208 | self._ii = self._i | |
209 | self._i = self._i00 |
|
209 | self._i = self._i00 | |
210 | # put back the final \n of every input line |
|
210 | # put back the final \n of every input line | |
211 | self._i00 = line_mod+'\n' |
|
211 | self._i00 = line_mod+'\n' | |
212 | #print 'Logging input:<%s>' % line_mod # dbg |
|
212 | #print 'Logging input:<%s>' % line_mod # dbg | |
213 | input_hist.append(self._i00) |
|
213 | input_hist.append(self._i00) | |
214 | #print '---[%s]' % (len(input_hist)-1,) # dbg |
|
214 | #print '---[%s]' % (len(input_hist)-1,) # dbg | |
215 |
|
215 | |||
216 | # hackish access to top-level namespace to create _i1,_i2... dynamically |
|
216 | # hackish access to top-level namespace to create _i1,_i2... dynamically | |
217 | to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii} |
|
217 | to_main = {'_i':self._i,'_ii':self._ii,'_iii':self._iii} | |
218 | if self.shell.outputcache.do_full_cache: |
|
218 | if self.shell.outputcache.do_full_cache: | |
219 | in_num = self.shell.outputcache.prompt_count |
|
219 | in_num = self.shell.outputcache.prompt_count | |
220 |
|
220 | |||
221 | # but if the opposite is true (a macro can produce multiple inputs |
|
221 | # but if the opposite is true (a macro can produce multiple inputs | |
222 | # with no output display called), then bring the output counter in |
|
222 | # with no output display called), then bring the output counter in | |
223 | # sync: |
|
223 | # sync: | |
224 | last_num = len(input_hist)-1 |
|
224 | last_num = len(input_hist)-1 | |
225 | if in_num != last_num: |
|
225 | if in_num != last_num: | |
226 | in_num = self.shell.outputcache.prompt_count = last_num |
|
226 | in_num = self.shell.outputcache.prompt_count = last_num | |
227 | new_i = '_i%s' % in_num |
|
227 | new_i = '_i%s' % in_num | |
228 | if continuation: |
|
228 | if continuation: | |
229 | self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod) |
|
229 | self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod) | |
230 | input_hist[in_num] = self._i00 |
|
230 | input_hist[in_num] = self._i00 | |
231 | to_main[new_i] = self._i00 |
|
231 | to_main[new_i] = self._i00 | |
232 | self.shell.user_ns.update(to_main) |
|
232 | self.shell.user_ns.update(to_main) | |
233 |
|
233 | |||
234 | # Write the log line, but decide which one according to the |
|
234 | # Write the log line, but decide which one according to the | |
235 | # log_raw_input flag, set when the log is started. |
|
235 | # log_raw_input flag, set when the log is started. | |
236 | if self.log_raw_input: |
|
236 | if self.log_raw_input: | |
237 | self.log_write(line_ori) |
|
237 | self.log_write(line_ori) | |
238 | else: |
|
238 | else: | |
239 | self.log_write(line_mod) |
|
239 | self.log_write(line_mod) | |
240 |
|
240 | |||
241 | def log_write(self,data,kind='input'): |
|
241 | def log_write(self,data,kind='input'): | |
242 | """Write data to the log file, if active""" |
|
242 | """Write data to the log file, if active""" | |
243 |
|
243 | |||
244 | #print 'data: %r' % data # dbg |
|
244 | #print 'data: %r' % data # dbg | |
245 | if self.log_active and data: |
|
245 | if self.log_active and data: | |
246 | write = self.logfile.write |
|
246 | write = self.logfile.write | |
247 | if kind=='input': |
|
247 | if kind=='input': | |
248 | if self.timestamp: |
|
248 | if self.timestamp: | |
249 | write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', |
|
249 | write(time.strftime('# %a, %d %b %Y %H:%M:%S\n', | |
250 | time.localtime())) |
|
250 | time.localtime())) | |
251 | write('%s\n' % data) |
|
251 | write('%s\n' % data) | |
252 | elif kind=='output' and self.log_output: |
|
252 | elif kind=='output' and self.log_output: | |
253 | odata = '\n'.join(['#[Out]# %s' % s |
|
253 | odata = '\n'.join(['#[Out]# %s' % s | |
254 | for s in data.split('\n')]) |
|
254 | for s in data.split('\n')]) | |
255 | write('%s\n' % odata) |
|
255 | write('%s\n' % odata) | |
256 | self.logfile.flush() |
|
256 | self.logfile.flush() | |
257 |
|
257 | |||
258 | def logstop(self): |
|
258 | def logstop(self): | |
259 | """Fully stop logging and close log file. |
|
259 | """Fully stop logging and close log file. | |
260 |
|
260 | |||
261 | In order to start logging again, a new logstart() call needs to be |
|
261 | 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 |
|
262 | made, possibly (though not necessarily) with a new filename, mode and | |
263 | other options.""" |
|
263 | other options.""" | |
264 |
|
264 | |||
265 | self.logfile.close() |
|
265 | self.logfile.close() | |
266 | self.logfile = None |
|
266 | self.logfile = None | |
267 | self.log_active = False |
|
267 | self.log_active = False | |
268 |
|
268 | |||
269 | # For backwards compatibility, in case anyone was using this. |
|
269 | # For backwards compatibility, in case anyone was using this. | |
270 | close_log = logstop |
|
270 | 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 | 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 |
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 |
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 |
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