Show More
@@ -1,177 +1,177 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Tools for coloring text in ANSI terminals. |
|
2 | """Tools for coloring text in ANSI terminals. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | #***************************************************************************** |
|
5 | #***************************************************************************** | |
6 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> |
|
6 | # Copyright (C) 2002-2006 Fernando Perez. <fperez@colorado.edu> | |
7 | # |
|
7 | # | |
8 | # Distributed under the terms of the BSD License. The full license is in |
|
8 | # Distributed under the terms of the BSD License. The full license is in | |
9 | # the file COPYING, distributed as part of this software. |
|
9 | # the file COPYING, distributed as part of this software. | |
10 | #***************************************************************************** |
|
10 | #***************************************************************************** | |
11 |
|
11 | |||
12 | __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] |
|
12 | __all__ = ['TermColors','InputTermColors','ColorScheme','ColorSchemeTable'] | |
13 |
|
13 | |||
14 | import os |
|
14 | import os | |
15 |
|
15 | |||
16 | from IPython.utils.ipstruct import Struct |
|
16 | from IPython.utils.ipstruct import Struct | |
17 |
|
17 | |||
18 | def make_color_table(in_class): |
|
18 | def make_color_table(in_class): | |
19 | """Build a set of color attributes in a class. |
|
19 | """Build a set of color attributes in a class. | |
20 |
|
20 | |||
21 | Helper function for building the *TermColors classes.""" |
|
21 | Helper function for building the *TermColors classes.""" | |
22 |
|
22 | |||
23 | color_templates = ( |
|
23 | color_templates = ( | |
24 | # Dark colors |
|
24 | # Dark colors | |
25 | ("Black" , "0;30"), |
|
25 | ("Black" , "0;30"), | |
26 | ("Red" , "0;31"), |
|
26 | ("Red" , "0;31"), | |
27 | ("Green" , "0;32"), |
|
27 | ("Green" , "0;32"), | |
28 | ("Brown" , "0;33"), |
|
28 | ("Brown" , "0;33"), | |
29 | ("Blue" , "0;34"), |
|
29 | ("Blue" , "0;34"), | |
30 | ("Purple" , "0;35"), |
|
30 | ("Purple" , "0;35"), | |
31 | ("Cyan" , "0;36"), |
|
31 | ("Cyan" , "0;36"), | |
32 | ("LightGray" , "0;37"), |
|
32 | ("LightGray" , "0;37"), | |
33 | # Light colors |
|
33 | # Light colors | |
34 | ("DarkGray" , "1;30"), |
|
34 | ("DarkGray" , "1;30"), | |
35 | ("LightRed" , "1;31"), |
|
35 | ("LightRed" , "1;31"), | |
36 | ("LightGreen" , "1;32"), |
|
36 | ("LightGreen" , "1;32"), | |
37 | ("Yellow" , "1;33"), |
|
37 | ("Yellow" , "1;33"), | |
38 | ("LightBlue" , "1;34"), |
|
38 | ("LightBlue" , "1;34"), | |
39 | ("LightPurple" , "1;35"), |
|
39 | ("LightPurple" , "1;35"), | |
40 | ("LightCyan" , "1;36"), |
|
40 | ("LightCyan" , "1;36"), | |
41 | ("White" , "1;37"), |
|
41 | ("White" , "1;37"), | |
42 | # Blinking colors. Probably should not be used in anything serious. |
|
42 | # Blinking colors. Probably should not be used in anything serious. | |
43 | ("BlinkBlack" , "5;30"), |
|
43 | ("BlinkBlack" , "5;30"), | |
44 | ("BlinkRed" , "5;31"), |
|
44 | ("BlinkRed" , "5;31"), | |
45 | ("BlinkGreen" , "5;32"), |
|
45 | ("BlinkGreen" , "5;32"), | |
46 | ("BlinkYellow" , "5;33"), |
|
46 | ("BlinkYellow" , "5;33"), | |
47 | ("BlinkBlue" , "5;34"), |
|
47 | ("BlinkBlue" , "5;34"), | |
48 | ("BlinkPurple" , "5;35"), |
|
48 | ("BlinkPurple" , "5;35"), | |
49 | ("BlinkCyan" , "5;36"), |
|
49 | ("BlinkCyan" , "5;36"), | |
50 | ("BlinkLightGray", "5;37"), |
|
50 | ("BlinkLightGray", "5;37"), | |
51 | ) |
|
51 | ) | |
52 |
|
52 | |||
53 | for name,value in color_templates: |
|
53 | for name,value in color_templates: | |
54 | setattr(in_class,name,in_class._base % value) |
|
54 | setattr(in_class,name,in_class._base % value) | |
55 |
|
55 | |||
56 | class TermColors: |
|
56 | class TermColors: | |
57 | """Color escape sequences. |
|
57 | """Color escape sequences. | |
58 |
|
58 | |||
59 | This class defines the escape sequences for all the standard (ANSI?) |
|
59 | This class defines the escape sequences for all the standard (ANSI?) | |
60 | colors in terminals. Also defines a NoColor escape which is just the null |
|
60 | colors in terminals. Also defines a NoColor escape which is just the null | |
61 | string, suitable for defining 'dummy' color schemes in terminals which get |
|
61 | string, suitable for defining 'dummy' color schemes in terminals which get | |
62 | confused by color escapes. |
|
62 | confused by color escapes. | |
63 |
|
63 | |||
64 | This class should be used as a mixin for building color schemes.""" |
|
64 | This class should be used as a mixin for building color schemes.""" | |
65 |
|
65 | |||
66 | NoColor = '' # for color schemes in color-less terminals. |
|
66 | NoColor = '' # for color schemes in color-less terminals. | |
67 | Normal = '\033[0m' # Reset normal coloring |
|
67 | Normal = '\033[0m' # Reset normal coloring | |
68 | _base = '\033[%sm' # Template for all other colors |
|
68 | _base = '\033[%sm' # Template for all other colors | |
69 |
|
69 | |||
70 | # Build the actual color table as a set of class attributes: |
|
70 | # Build the actual color table as a set of class attributes: | |
71 | make_color_table(TermColors) |
|
71 | make_color_table(TermColors) | |
72 |
|
72 | |||
73 | class InputTermColors: |
|
73 | class InputTermColors: | |
74 | """Color escape sequences for input prompts. |
|
74 | """Color escape sequences for input prompts. | |
75 |
|
75 | |||
76 | This class is similar to TermColors, but the escapes are wrapped in \001 |
|
76 | This class is similar to TermColors, but the escapes are wrapped in \001 | |
77 | and \002 so that readline can properly know the length of each line and |
|
77 | and \002 so that readline can properly know the length of each line and | |
78 | can wrap lines accordingly. Use this class for any colored text which |
|
78 | can wrap lines accordingly. Use this class for any colored text which | |
79 | needs to be used in input prompts, such as in calls to raw_input(). |
|
79 | needs to be used in input prompts, such as in calls to raw_input(). | |
80 |
|
80 | |||
81 | This class defines the escape sequences for all the standard (ANSI?) |
|
81 | This class defines the escape sequences for all the standard (ANSI?) | |
82 | colors in terminals. Also defines a NoColor escape which is just the null |
|
82 | colors in terminals. Also defines a NoColor escape which is just the null | |
83 | string, suitable for defining 'dummy' color schemes in terminals which get |
|
83 | string, suitable for defining 'dummy' color schemes in terminals which get | |
84 | confused by color escapes. |
|
84 | confused by color escapes. | |
85 |
|
85 | |||
86 | This class should be used as a mixin for building color schemes.""" |
|
86 | This class should be used as a mixin for building color schemes.""" | |
87 |
|
87 | |||
88 | NoColor = '' # for color schemes in color-less terminals. |
|
88 | NoColor = '' # for color schemes in color-less terminals. | |
89 |
|
89 | |||
90 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': |
|
90 | if os.name == 'nt' and os.environ.get('TERM','dumb') == 'emacs': | |
91 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them |
|
91 | # (X)emacs on W32 gets confused with \001 and \002 so we remove them | |
92 | Normal = '\033[0m' # Reset normal coloring |
|
92 | Normal = '\033[0m' # Reset normal coloring | |
93 | _base = '\033[%sm' # Template for all other colors |
|
93 | _base = '\033[%sm' # Template for all other colors | |
94 | else: |
|
94 | else: | |
95 | Normal = '\001\033[0m\002' # Reset normal coloring |
|
95 | Normal = '\001\033[0m\002' # Reset normal coloring | |
96 | _base = '\001\033[%sm\002' # Template for all other colors |
|
96 | _base = '\001\033[%sm\002' # Template for all other colors | |
97 |
|
97 | |||
98 | # Build the actual color table as a set of class attributes: |
|
98 | # Build the actual color table as a set of class attributes: | |
99 | make_color_table(InputTermColors) |
|
99 | make_color_table(InputTermColors) | |
100 |
|
100 | |||
101 | class ColorScheme: |
|
101 | class ColorScheme: | |
102 | """Generic color scheme class. Just a name and a Struct.""" |
|
102 | """Generic color scheme class. Just a name and a Struct.""" | |
103 | def __init__(self,__scheme_name_,colordict=None,**colormap): |
|
103 | def __init__(self,__scheme_name_,colordict=None,**colormap): | |
104 | self.name = __scheme_name_ |
|
104 | self.name = __scheme_name_ | |
105 | if colordict is None: |
|
105 | if colordict is None: | |
106 | self.colors = Struct(**colormap) |
|
106 | self.colors = Struct(**colormap) | |
107 | else: |
|
107 | else: | |
108 | self.colors = Struct(colordict) |
|
108 | self.colors = Struct(colordict) | |
109 |
|
109 | |||
110 | def copy(self,name=None): |
|
110 | def copy(self,name=None): | |
111 | """Return a full copy of the object, optionally renaming it.""" |
|
111 | """Return a full copy of the object, optionally renaming it.""" | |
112 | if name is None: |
|
112 | if name is None: | |
113 | name = self.name |
|
113 | name = self.name | |
114 |
return ColorScheme(name,self.colors. |
|
114 | return ColorScheme(name, self.colors.dict()) | |
115 |
|
115 | |||
116 | class ColorSchemeTable(dict): |
|
116 | class ColorSchemeTable(dict): | |
117 | """General class to handle tables of color schemes. |
|
117 | """General class to handle tables of color schemes. | |
118 |
|
118 | |||
119 | It's basically a dict of color schemes with a couple of shorthand |
|
119 | It's basically a dict of color schemes with a couple of shorthand | |
120 | attributes and some convenient methods. |
|
120 | attributes and some convenient methods. | |
121 |
|
121 | |||
122 | active_scheme_name -> obvious |
|
122 | active_scheme_name -> obvious | |
123 | active_colors -> actual color table of the active scheme""" |
|
123 | active_colors -> actual color table of the active scheme""" | |
124 |
|
124 | |||
125 | def __init__(self,scheme_list=None,default_scheme=''): |
|
125 | def __init__(self,scheme_list=None,default_scheme=''): | |
126 | """Create a table of color schemes. |
|
126 | """Create a table of color schemes. | |
127 |
|
127 | |||
128 | The table can be created empty and manually filled or it can be |
|
128 | The table can be created empty and manually filled or it can be | |
129 | created with a list of valid color schemes AND the specification for |
|
129 | created with a list of valid color schemes AND the specification for | |
130 | the default active scheme. |
|
130 | the default active scheme. | |
131 | """ |
|
131 | """ | |
132 |
|
132 | |||
133 | # create object attributes to be set later |
|
133 | # create object attributes to be set later | |
134 | self.active_scheme_name = '' |
|
134 | self.active_scheme_name = '' | |
135 | self.active_colors = None |
|
135 | self.active_colors = None | |
136 |
|
136 | |||
137 | if scheme_list: |
|
137 | if scheme_list: | |
138 | if default_scheme == '': |
|
138 | if default_scheme == '': | |
139 | raise ValueError,'you must specify the default color scheme' |
|
139 | raise ValueError,'you must specify the default color scheme' | |
140 | for scheme in scheme_list: |
|
140 | for scheme in scheme_list: | |
141 | self.add_scheme(scheme) |
|
141 | self.add_scheme(scheme) | |
142 | self.set_active_scheme(default_scheme) |
|
142 | self.set_active_scheme(default_scheme) | |
143 |
|
143 | |||
144 | def copy(self): |
|
144 | def copy(self): | |
145 | """Return full copy of object""" |
|
145 | """Return full copy of object""" | |
146 | return ColorSchemeTable(self.values(),self.active_scheme_name) |
|
146 | return ColorSchemeTable(self.values(),self.active_scheme_name) | |
147 |
|
147 | |||
148 | def add_scheme(self,new_scheme): |
|
148 | def add_scheme(self,new_scheme): | |
149 | """Add a new color scheme to the table.""" |
|
149 | """Add a new color scheme to the table.""" | |
150 | if not isinstance(new_scheme,ColorScheme): |
|
150 | if not isinstance(new_scheme,ColorScheme): | |
151 | raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' |
|
151 | raise ValueError,'ColorSchemeTable only accepts ColorScheme instances' | |
152 | self[new_scheme.name] = new_scheme |
|
152 | self[new_scheme.name] = new_scheme | |
153 |
|
153 | |||
154 | def set_active_scheme(self,scheme,case_sensitive=0): |
|
154 | def set_active_scheme(self,scheme,case_sensitive=0): | |
155 | """Set the currently active scheme. |
|
155 | """Set the currently active scheme. | |
156 |
|
156 | |||
157 | Names are by default compared in a case-insensitive way, but this can |
|
157 | Names are by default compared in a case-insensitive way, but this can | |
158 | be changed by setting the parameter case_sensitive to true.""" |
|
158 | be changed by setting the parameter case_sensitive to true.""" | |
159 |
|
159 | |||
160 | scheme_names = self.keys() |
|
160 | scheme_names = self.keys() | |
161 | if case_sensitive: |
|
161 | if case_sensitive: | |
162 | valid_schemes = scheme_names |
|
162 | valid_schemes = scheme_names | |
163 | scheme_test = scheme |
|
163 | scheme_test = scheme | |
164 | else: |
|
164 | else: | |
165 | valid_schemes = [s.lower() for s in scheme_names] |
|
165 | valid_schemes = [s.lower() for s in scheme_names] | |
166 | scheme_test = scheme.lower() |
|
166 | scheme_test = scheme.lower() | |
167 | try: |
|
167 | try: | |
168 | scheme_idx = valid_schemes.index(scheme_test) |
|
168 | scheme_idx = valid_schemes.index(scheme_test) | |
169 | except ValueError: |
|
169 | except ValueError: | |
170 | raise ValueError,'Unrecognized color scheme: ' + scheme + \ |
|
170 | raise ValueError,'Unrecognized color scheme: ' + scheme + \ | |
171 | '\nValid schemes: '+str(scheme_names).replace("'', ",'') |
|
171 | '\nValid schemes: '+str(scheme_names).replace("'', ",'') | |
172 | else: |
|
172 | else: | |
173 | active = scheme_names[scheme_idx] |
|
173 | active = scheme_names[scheme_idx] | |
174 | self.active_scheme_name = active |
|
174 | self.active_scheme_name = active | |
175 | self.active_colors = self[active].colors |
|
175 | self.active_colors = self[active].colors | |
176 | # Now allow using '' as an index for the current active scheme |
|
176 | # Now allow using '' as an index for the current active scheme | |
177 | self[''] = self[active] |
|
177 | self[''] = self[active] |
@@ -1,416 +1,417 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """Mimic C structs with lots of extra functionality. |
|
2 | """Mimic C structs with lots of extra functionality. | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | #***************************************************************************** |
|
5 | #***************************************************************************** | |
6 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> |
|
6 | # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | |
7 | # |
|
7 | # | |
8 | # Distributed under the terms of the BSD License. The full license is in |
|
8 | # Distributed under the terms of the BSD License. The full license is in | |
9 | # the file COPYING, distributed as part of this software. |
|
9 | # the file COPYING, distributed as part of this software. | |
10 | #***************************************************************************** |
|
10 | #***************************************************************************** | |
11 |
|
11 | |||
12 | __all__ = ['Struct'] |
|
12 | __all__ = ['Struct'] | |
13 |
|
13 | |||
|
14 | import inspect | |||
14 | import types |
|
15 | import types | |
15 | import pprint |
|
16 | import pprint | |
16 |
|
17 | |||
17 | from IPython.utils.genutils import list2dict2 |
|
18 | from IPython.utils.genutils import list2dict2 | |
18 |
|
19 | |||
19 | class Struct: |
|
20 | class Struct(object): | |
20 | """Class to mimic C structs but also provide convenient dictionary-like |
|
21 | """Class to mimic C structs but also provide convenient dictionary-like | |
21 | functionality. |
|
22 | functionality. | |
22 |
|
23 | |||
23 | Instances can be initialized with a dictionary, a list of key=value pairs |
|
24 | Instances can be initialized with a dictionary, a list of key=value pairs | |
24 | or both. If both are present, the dictionary must come first. |
|
25 | or both. If both are present, the dictionary must come first. | |
25 |
|
26 | |||
26 | Because Python classes provide direct assignment to their members, it's |
|
27 | Because Python classes provide direct assignment to their members, it's | |
27 | easy to overwrite normal methods (S.copy = 1 would destroy access to |
|
28 | easy to overwrite normal methods (S.copy = 1 would destroy access to | |
28 | S.copy()). For this reason, all builtin method names are protected and |
|
29 | S.copy()). For this reason, all builtin method names are protected and | |
29 | can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise |
|
30 | can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise | |
30 | a KeyError exception. If you really want to, you can bypass this |
|
31 | a KeyError exception. If you really want to, you can bypass this | |
31 | protection by directly assigning to __dict__: s.__dict__['copy']=1 will |
|
32 | protection by directly assigning to __dict__: s.__dict__['copy']=1 will | |
32 | still work. Doing this will break functionality, though. As in most of |
|
33 | still work. Doing this will break functionality, though. As in most of | |
33 | Python, namespace protection is weakly enforced, so feel free to shoot |
|
34 | Python, namespace protection is weakly enforced, so feel free to shoot | |
34 | yourself if you really want to. |
|
35 | yourself if you really want to. | |
35 |
|
36 | |||
36 | Note that this class uses more memory and is *much* slower than a regular |
|
37 | Note that this class uses more memory and is *much* slower than a regular | |
37 | dictionary, so be careful in situations where memory or performance are |
|
38 | dictionary, so be careful in situations where memory or performance are | |
38 | critical. But for day to day use it should behave fine. It is particularly |
|
39 | critical. But for day to day use it should behave fine. It is particularly | |
39 | convenient for storing configuration data in programs. |
|
40 | convenient for storing configuration data in programs. | |
40 |
|
41 | |||
41 | +,+=,- and -= are implemented. +/+= do merges (non-destructive updates), |
|
42 | +,+=,- and -= are implemented. +/+= do merges (non-destructive updates), | |
42 | -/-= remove keys from the original. See the method descripitions. |
|
43 | -/-= remove keys from the original. See the method descripitions. | |
43 |
|
44 | |||
44 | This class allows a quick access syntax: both s.key and s['key'] are |
|
45 | This class allows a quick access syntax: both s.key and s['key'] are | |
45 | valid. This syntax has a limitation: each 'key' has to be explicitly |
|
46 | valid. This syntax has a limitation: each 'key' has to be explicitly | |
46 | accessed by its original name. The normal s.key syntax doesn't provide |
|
47 | accessed by its original name. The normal s.key syntax doesn't provide | |
47 | access to the keys via variables whose values evaluate to the desired |
|
48 | access to the keys via variables whose values evaluate to the desired | |
48 | keys. An example should clarify this: |
|
49 | keys. An example should clarify this: | |
49 |
|
50 | |||
50 | Define a dictionary and initialize both with dict and k=v pairs: |
|
51 | Define a dictionary and initialize both with dict and k=v pairs: | |
51 | >>> d={'a':1,'b':2} |
|
52 | >>> d={'a':1,'b':2} | |
52 | >>> s=Struct(d,hi=10,ho=20) |
|
53 | >>> s=Struct(d,hi=10,ho=20) | |
53 |
|
54 | |||
54 | The return of __repr__ can be used to create a new instance: |
|
55 | The return of __repr__ can be used to create a new instance: | |
55 | >>> s |
|
56 | >>> s | |
56 | Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) |
|
57 | Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) | |
57 |
|
58 | |||
58 | Note: the special '__allownew' key is used for internal purposes. |
|
59 | Note: the special '__allownew' key is used for internal purposes. | |
59 |
|
60 | |||
60 | __str__ (called by print) shows it's not quite a regular dictionary: |
|
61 | __str__ (called by print) shows it's not quite a regular dictionary: | |
61 | >>> print s |
|
62 | >>> print s | |
62 | Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) |
|
63 | Struct({'__allownew': True, 'a': 1, 'b': 2, 'hi': 10, 'ho': 20}) | |
63 |
|
64 | |||
64 | Access by explicitly named key with dot notation: |
|
65 | Access by explicitly named key with dot notation: | |
65 | >>> s.a |
|
66 | >>> s.a | |
66 | 1 |
|
67 | 1 | |
67 |
|
68 | |||
68 | Or like a dictionary: |
|
69 | Or like a dictionary: | |
69 | >>> s['a'] |
|
70 | >>> s['a'] | |
70 | 1 |
|
71 | 1 | |
71 |
|
72 | |||
72 | If you want a variable to hold the key value, only dictionary access works: |
|
73 | If you want a variable to hold the key value, only dictionary access works: | |
73 | >>> key='hi' |
|
74 | >>> key='hi' | |
74 | >>> s.key |
|
75 | >>> s.key | |
75 | Traceback (most recent call last): |
|
76 | Traceback (most recent call last): | |
76 | File "<stdin>", line 1, in ? |
|
77 | File "<stdin>", line 1, in ? | |
77 | AttributeError: Struct instance has no attribute 'key' |
|
78 | AttributeError: Struct instance has no attribute 'key' | |
78 |
|
79 | |||
79 | >>> s[key] |
|
80 | >>> s[key] | |
80 | 10 |
|
81 | 10 | |
81 |
|
82 | |||
82 | Another limitation of the s.key syntax (and Struct(key=val) |
|
83 | Another limitation of the s.key syntax (and Struct(key=val) | |
83 | initialization): keys can't be numbers. But numeric keys can be used and |
|
84 | initialization): keys can't be numbers. But numeric keys can be used and | |
84 | accessed using the dictionary syntax. Again, an example: |
|
85 | accessed using the dictionary syntax. Again, an example: | |
85 |
|
86 | |||
86 | This doesn't work (prompt changed to avoid confusing the test system): |
|
87 | This doesn't work (prompt changed to avoid confusing the test system): | |
87 | ->> s=Struct(4='hi') |
|
88 | ->> s=Struct(4='hi') | |
88 | Traceback (most recent call last): |
|
89 | Traceback (most recent call last): | |
89 | ... |
|
90 | ... | |
90 | SyntaxError: keyword can't be an expression |
|
91 | SyntaxError: keyword can't be an expression | |
91 |
|
92 | |||
92 | But this does: |
|
93 | But this does: | |
93 | >>> s=Struct() |
|
94 | >>> s=Struct() | |
94 | >>> s[4]='hi' |
|
95 | >>> s[4]='hi' | |
95 | >>> s |
|
96 | >>> s | |
96 | Struct({4: 'hi', '__allownew': True}) |
|
97 | Struct({4: 'hi', '__allownew': True}) | |
97 | >>> s[4] |
|
98 | >>> s[4] | |
98 | 'hi' |
|
99 | 'hi' | |
99 | """ |
|
100 | """ | |
100 |
|
101 | |||
101 | # Attributes to which __setitem__ and __setattr__ will block access. |
|
102 | # Attributes to which __setitem__ and __setattr__ will block access. | |
102 | # Note: much of this will be moot in Python 2.2 and will be done in a much |
|
103 | # Note: much of this will be moot in Python 2.2 and will be done in a much | |
103 | # cleaner way. |
|
104 | # cleaner way. | |
104 | __protected = ('copy dict dictcopy get has_attr has_key items keys ' |
|
105 | __protected = ('copy dict dictcopy get has_attr has_key items keys ' | |
105 | 'merge popitem setdefault update values ' |
|
106 | 'merge popitem setdefault update values ' | |
106 | '__make_dict __dict_invert ').split() |
|
107 | '__make_dict __dict_invert ').split() | |
107 |
|
108 | |||
108 |
def __init__(self,d |
|
109 | def __init__(self,data=None,**kw): | |
109 |
"""Initialize with a dictionary, another Struct, or |
|
110 | """Initialize with a dictionary, another Struct, or data. | |
110 | explicitly the list of attributes. |
|
|||
111 |
|
111 | |||
112 | Both can be used, but the dictionary must come first: |
|
112 | Parameters | |
113 | Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2). |
|
113 | ---------- | |
|
114 | data : dict, Struct | |||
|
115 | Initialize with this data. | |||
|
116 | kw : dict | |||
|
117 | Initialize with key, value pairs. | |||
|
118 | ||||
|
119 | Examples | |||
|
120 | -------- | |||
|
121 | ||||
114 | """ |
|
122 | """ | |
115 |
self |
|
123 | object.__setattr__(self, '_allownew', True) | |
116 | if dict is None: |
|
124 | object.__setattr__(self, '_data',{}) | |
117 | dict = {} |
|
125 | if data is None: | |
118 | if isinstance(dict,Struct): |
|
126 | data = {} | |
119 | dict = dict.dict() |
|
127 | if isinstance(data, Struct): | |
120 | elif dict and type(dict) is not types.DictType: |
|
128 | data = data.dict() | |
121 | raise TypeError,\ |
|
129 | elif data and not isinstance(data, dict): | |
122 |
|
|
130 | raise TypeError('initialize with a dict, Struct or key=val pairs') | |
123 |
d |
|
131 | data.update(kw) | |
124 | # do the updating by hand to guarantee that we go through the |
|
132 | # do the updating by hand to guarantee that we go through the | |
125 | # safety-checked __setitem__ |
|
133 | # safety-checked __setitem__ | |
126 |
for k,v in d |
|
134 | for k, v in data.items(): | |
127 | self[k] = v |
|
135 | self[k] = v | |
128 |
|
||||
129 |
|
136 | |||
130 | def __setitem__(self,key,value): |
|
137 | def __setitem__(self, key, value): | |
131 | """Used when struct[key] = val calls are made.""" |
|
138 | """Used when struct[key] = val calls are made.""" | |
132 | if key in Struct.__protected: |
|
139 | if isinstance(key, str): | |
133 | raise KeyError,'Key '+`key`+' is a protected key of class Struct.' |
|
140 | # I can't simply call hasattr here because it calls getattr, which | |
134 | if not self['__allownew'] and key not in self.__dict__: |
|
141 | # calls self.__getattr__, which returns True for keys in | |
|
142 | # self._data. But I only want keys in the class and in | |||
|
143 | # self.__dict__ | |||
|
144 | if key in self.__dict__ or hasattr(Struct, key): | |||
|
145 | raise KeyError( | |||
|
146 | 'key %s is a protected key of class Struct.' % key | |||
|
147 | ) | |||
|
148 | if not self._allownew and key not in self._data: | |||
135 | raise KeyError( |
|
149 | raise KeyError( | |
136 |
" |
|
150 | "can't create unknown attribute %s. Check for typos, or use allow_new_attr" % key) | |
137 | key) |
|
151 | self._data[key] = value | |
138 |
|
||||
139 | self.__dict__[key] = value |
|
|||
140 |
|
152 | |||
141 | def __setattr__(self, key, value): |
|
153 | def __setattr__(self, key, value): | |
142 | """Used when struct.key = val calls are made.""" |
|
154 | self.__setitem__(key, value) | |
143 | self.__setitem__(key,value) |
|
155 | ||
|
156 | def __getattr__(self, key): | |||
|
157 | try: | |||
|
158 | result = self._data[key] | |||
|
159 | except KeyError: | |||
|
160 | raise AttributeError(key) | |||
|
161 | else: | |||
|
162 | return result | |||
|
163 | ||||
|
164 | def __getitem__(self, key): | |||
|
165 | return self._data[key] | |||
144 |
|
166 | |||
145 | def __str__(self): |
|
167 | def __str__(self): | |
146 | """Gets called by print.""" |
|
168 | return 'Struct('+ pprint.pformat(self._data)+')' | |
147 |
|
||||
148 | return 'Struct('+ pprint.pformat(self.__dict__)+')' |
|
|||
149 |
|
169 | |||
150 | def __repr__(self): |
|
170 | def __repr__(self): | |
151 | """Gets called by repr. |
|
|||
152 |
|
||||
153 | A Struct can be recreated with S_new=eval(repr(S_old)).""" |
|
|||
154 | return self.__str__() |
|
171 | return self.__str__() | |
155 |
|
172 | |||
156 |
def __ |
|
173 | def __contains__(self, key): | |
157 | """Allows struct[key] access.""" |
|
174 | return key in self._data | |
158 | return self.__dict__[key] |
|
|||
159 |
|
||||
160 | def __contains__(self,key): |
|
|||
161 | """Allows use of the 'in' operator. |
|
|||
162 |
|
||||
163 | Examples: |
|
|||
164 | >>> s = Struct(x=1) |
|
|||
165 | >>> 'x' in s |
|
|||
166 | True |
|
|||
167 | >>> 'y' in s |
|
|||
168 | False |
|
|||
169 | >>> s[4] = None |
|
|||
170 | >>> 4 in s |
|
|||
171 | True |
|
|||
172 | >>> s.z = None |
|
|||
173 | >>> 'z' in s |
|
|||
174 | True |
|
|||
175 | """ |
|
|||
176 | return key in self.__dict__ |
|
|||
177 |
|
175 | |||
178 | def __iadd__(self,other): |
|
176 | def __iadd__(self, other): | |
179 | """S += S2 is a shorthand for S.merge(S2).""" |
|
177 | """S += S2 is a shorthand for S.merge(S2).""" | |
180 | self.merge(other) |
|
178 | self.merge(other) | |
181 | return self |
|
179 | return self | |
182 |
|
180 | |||
183 | def __add__(self,other): |
|
181 | def __add__(self,other): | |
184 |
"""S + S2 -> New Struct made f |
|
182 | """S + S2 -> New Struct made from S.merge(S2)""" | |
185 | Sout = self.copy() |
|
183 | Sout = self.copy() | |
186 | Sout.merge(other) |
|
184 | Sout.merge(other) | |
187 | return Sout |
|
185 | return Sout | |
188 |
|
186 | |||
189 | def __sub__(self,other): |
|
187 | def __sub__(self,other): | |
190 | """Return S1-S2, where all keys in S2 have been deleted (if present) |
|
188 | """Out of place remove keys from self that are in other.""" | |
191 | from S1.""" |
|
|||
192 | Sout = self.copy() |
|
189 | Sout = self.copy() | |
193 | Sout -= other |
|
190 | Sout -= other | |
194 | return Sout |
|
191 | return Sout | |
195 |
|
192 | |||
196 | def __isub__(self,other): |
|
193 | def __isub__(self,other): | |
197 | """Do in place S = S - S2, meaning all keys in S2 have been deleted |
|
194 | """Inplace remove keys from self that are in other.""" | |
198 | (if present) from S1.""" |
|
|||
199 |
|
||||
200 | for k in other.keys(): |
|
195 | for k in other.keys(): | |
201 | if self.has_key(k): |
|
196 | if self.has_key(k): | |
202 |
del self._ |
|
197 | del self._data[k] | |
203 |
|
198 | |||
204 | def __make_dict(self,__loc_data__,**kw): |
|
199 | def __make_dict(self,__loc_data__,**kw): | |
205 |
"Helper function for update and merge. Return a dict from data. |
|
200 | """Helper function for update and merge. Return a dict from data. | |
206 |
|
201 | """ | ||
207 | if __loc_data__ == None: |
|
202 | if __loc_data__ == None: | |
208 |
d |
|
203 | data = {} | |
209 |
elif |
|
204 | elif isinstance(__loc_data__, dict): | |
210 |
d |
|
205 | data = __loc_data__ | |
211 | elif isinstance(__loc_data__,Struct): |
|
206 | elif isinstance(__loc_data__, Struct): | |
212 |
d |
|
207 | data = __loc_data__._data | |
213 | else: |
|
208 | else: | |
214 |
raise TypeError |
|
209 | raise TypeError('update with a dict, Struct or key=val pairs') | |
215 | if kw: |
|
210 | if kw: | |
216 |
d |
|
211 | data.update(kw) | |
217 |
return d |
|
212 | return data | |
218 |
|
213 | |||
219 |
def __dict_invert(self, |
|
214 | def __dict_invert(self, data): | |
220 |
"""Helper function for merge. |
|
215 | """Helper function for merge. | |
221 | lists and returns a dict. with the elements of each list as keys and |
|
216 | ||
222 | the original keys as values.""" |
|
217 | Takes a dictionary whose values are lists and returns a dict with | |
223 |
|
218 | the elements of each list as keys and the original keys as values. | ||
|
219 | """ | |||
224 | outdict = {} |
|
220 | outdict = {} | |
225 |
for k,lst in d |
|
221 | for k,lst in data.items(): | |
226 |
if |
|
222 | if isinstance(lst, str): | |
227 | lst = lst.split() |
|
223 | lst = lst.split() | |
228 | for entry in lst: |
|
224 | for entry in lst: | |
229 | outdict[entry] = k |
|
225 | outdict[entry] = k | |
230 | return outdict |
|
226 | return outdict | |
231 |
|
227 | |||
232 | def clear(self): |
|
228 | def clear(self): | |
233 | """Clear all attributes.""" |
|
229 | """Clear all attributes.""" | |
234 |
self._ |
|
230 | self._data.clear() | |
235 |
|
231 | |||
236 | def copy(self): |
|
232 | def copy(self): | |
237 | """Return a (shallow) copy of a Struct.""" |
|
233 | """Return a (shallow) copy of a Struct.""" | |
238 |
return Struct(self._ |
|
234 | return Struct(self._data.copy()) | |
239 |
|
235 | |||
240 | def dict(self): |
|
236 | def dict(self): | |
241 | """Return the Struct's dictionary.""" |
|
237 | """Return the Struct's dictionary.""" | |
242 |
return self._ |
|
238 | return self._data | |
243 |
|
239 | |||
244 | def dictcopy(self): |
|
240 | def dictcopy(self): | |
245 | """Return a (shallow) copy of the Struct's dictionary.""" |
|
241 | """Return a (shallow) copy of the Struct's dictionary.""" | |
246 |
return self._ |
|
242 | return self._data.copy() | |
247 |
|
243 | |||
248 | def popitem(self): |
|
244 | def popitem(self): | |
249 | """S.popitem() -> (k, v), remove and return some (key, value) pair as |
|
245 | """Return (key, value) tuple and remove from Struct. | |
250 | a 2-tuple; but raise KeyError if S is empty.""" |
|
246 | ||
251 | return self.__dict__.popitem() |
|
247 | If key is not present raise KeyError. | |
252 |
|
248 | """ | ||
|
249 | return self._data.popitem() | |||
|
250 | ||||
253 | def update(self,__loc_data__=None,**kw): |
|
251 | def update(self,__loc_data__=None,**kw): | |
254 |
"""Update (merge) with data from another Struct or |
|
252 | """Update (merge) with data from another Struct or dict. | |
255 | Optionally, one or more key=value pairs can be given at the end for |
|
253 | ||
256 | direct update.""" |
|
254 | Parameters | |
257 |
|
255 | ---------- | ||
|
256 | __loc_data : dict, Struct | |||
|
257 | The new data to add to self. | |||
|
258 | kw : dict | |||
|
259 | Key, value pairs to add to self. | |||
|
260 | """ | |||
258 | # The funny name __loc_data__ is to prevent a common variable name |
|
261 | # The funny name __loc_data__ is to prevent a common variable name | |
259 | # which could be a fieled of a Struct to collide with this |
|
262 | # which could be a fieled of a Struct to collide with this | |
260 | # parameter. The problem would arise if the function is called with a |
|
263 | # parameter. The problem would arise if the function is called with a | |
261 | # keyword with this same name that a user means to add as a Struct |
|
264 | # keyword with this same name that a user means to add as a Struct | |
262 | # field. |
|
265 | # field. | |
263 |
newdict = |
|
266 | newdict = self.__make_dict(__loc_data__, **kw) | |
264 | for k,v in newdict.iteritems(): |
|
267 | for k, v in newdict.iteritems(): | |
265 | self[k] = v |
|
268 | self[k] = v | |
266 |
|
269 | |||
267 | def merge(self,__loc_data__=None,__conflict_solve=None,**kw): |
|
270 | def merge(self, __loc_data__=None, __conflict_solve=None, **kw): | |
268 | """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S. |
|
271 | """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S. | |
269 |
|
272 | |||
270 | This is similar to update(), but much more flexible. First, a dict is |
|
273 | This is similar to update(), but much more flexible. First, a dict is | |
271 | made from data+key=value pairs. When merging this dict with the Struct |
|
274 | made from data+key=value pairs. When merging this dict with the Struct | |
272 | S, the optional dictionary 'conflict' is used to decide what to do. |
|
275 | S, the optional dictionary 'conflict' is used to decide what to do. | |
273 |
|
276 | |||
274 | If conflict is not given, the default behavior is to preserve any keys |
|
277 | If conflict is not given, the default behavior is to preserve any keys | |
275 | with their current value (the opposite of the update method's |
|
278 | with their current value (the opposite of the update method's | |
276 | behavior). |
|
279 | behavior). | |
277 |
|
280 | |||
278 | conflict is a dictionary of binary functions which will be used to |
|
281 | conflict is a dictionary of binary functions which will be used to | |
279 | solve key conflicts. It must have the following structure: |
|
282 | solve key conflicts. It must have the following structure: | |
280 |
|
283 | |||
281 | conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc } |
|
284 | conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc } | |
282 |
|
285 | |||
283 | Values must be lists or whitespace separated strings which are |
|
286 | Values must be lists or whitespace separated strings which are | |
284 | automatically converted to lists of strings by calling string.split(). |
|
287 | automatically converted to lists of strings by calling string.split(). | |
285 |
|
288 | |||
286 | Each key of conflict is a function which defines a policy for |
|
289 | Each key of conflict is a function which defines a policy for | |
287 | resolving conflicts when merging with the input data. Each fn must be |
|
290 | resolving conflicts when merging with the input data. Each fn must be | |
288 | a binary function which returns the desired outcome for a key |
|
291 | a binary function which returns the desired outcome for a key | |
289 | conflict. These functions will be called as fn(old,new). |
|
292 | conflict. These functions will be called as fn(old,new). | |
290 |
|
293 | |||
291 | An example is probably in order. Suppose you are merging the struct S |
|
294 | An example is probably in order. Suppose you are merging the struct S | |
292 | with a dict D and the following conflict policy dict: |
|
295 | with a dict D and the following conflict policy dict: | |
293 |
|
296 | |||
294 | S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'}) |
|
297 | S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'}) | |
295 |
|
298 | |||
296 | If the key 'a' is found in both S and D, the merge method will call: |
|
299 | If the key 'a' is found in both S and D, the merge method will call: | |
297 |
|
300 | |||
298 | S['a'] = fn1(S['a'],D['a']) |
|
301 | S['a'] = fn1(S['a'],D['a']) | |
299 |
|
302 | |||
300 | As a convenience, merge() provides five (the most commonly needed) |
|
303 | As a convenience, merge() provides five (the most commonly needed) | |
301 | pre-defined policies: preserve, update, add, add_flip and add_s. The |
|
304 | pre-defined policies: preserve, update, add, add_flip and add_s. The | |
302 | easiest explanation is their implementation: |
|
305 | easiest explanation is their implementation: | |
303 |
|
306 | |||
304 | preserve = lambda old,new: old |
|
307 | preserve = lambda old,new: old | |
305 | update = lambda old,new: new |
|
308 | update = lambda old,new: new | |
306 | add = lambda old,new: old + new |
|
309 | add = lambda old,new: old + new | |
307 | add_flip = lambda old,new: new + old # note change of order! |
|
310 | add_flip = lambda old,new: new + old # note change of order! | |
308 | add_s = lambda old,new: old + ' ' + new # only works for strings! |
|
311 | add_s = lambda old,new: old + ' ' + new # only works for strings! | |
309 |
|
312 | |||
310 | You can use those four words (as strings) as keys in conflict instead |
|
313 | You can use those four words (as strings) as keys in conflict instead | |
311 | of defining them as functions, and the merge method will substitute |
|
314 | of defining them as functions, and the merge method will substitute | |
312 | the appropriate functions for you. That is, the call |
|
315 | the appropriate functions for you. That is, the call | |
313 |
|
316 | |||
314 | S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]}) |
|
317 | S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]}) | |
315 |
|
318 | |||
316 | will automatically substitute the functions preserve and add for the |
|
319 | will automatically substitute the functions preserve and add for the | |
317 | names 'preserve' and 'add' before making any function calls. |
|
320 | names 'preserve' and 'add' before making any function calls. | |
318 |
|
321 | |||
319 | For more complicated conflict resolution policies, you still need to |
|
322 | For more complicated conflict resolution policies, you still need to | |
320 | construct your own functions. """ |
|
323 | construct your own functions. """ | |
321 |
|
324 | |||
322 |
data_dict = |
|
325 | data_dict = self.__make_dict(__loc_data__,**kw) | |
323 |
|
326 | |||
324 | # policies for conflict resolution: two argument functions which return |
|
327 | # policies for conflict resolution: two argument functions which return | |
325 | # the value that will go in the new struct |
|
328 | # the value that will go in the new struct | |
326 | preserve = lambda old,new: old |
|
329 | preserve = lambda old,new: old | |
327 | update = lambda old,new: new |
|
330 | update = lambda old,new: new | |
328 | add = lambda old,new: old + new |
|
331 | add = lambda old,new: old + new | |
329 | add_flip = lambda old,new: new + old # note change of order! |
|
332 | add_flip = lambda old,new: new + old # note change of order! | |
330 | add_s = lambda old,new: old + ' ' + new |
|
333 | add_s = lambda old,new: old + ' ' + new | |
331 |
|
334 | |||
332 | # default policy is to keep current keys when there's a conflict |
|
335 | # default policy is to keep current keys when there's a conflict | |
333 | conflict_solve = list2dict2(self.keys(),default = preserve) |
|
336 | conflict_solve = list2dict2(self.keys(), default = preserve) | |
334 |
|
337 | |||
335 | # the conflict_solve dictionary is given by the user 'inverted': we |
|
338 | # the conflict_solve dictionary is given by the user 'inverted': we | |
336 | # need a name-function mapping, it comes as a function -> names |
|
339 | # need a name-function mapping, it comes as a function -> names | |
337 | # dict. Make a local copy (b/c we'll make changes), replace user |
|
340 | # dict. Make a local copy (b/c we'll make changes), replace user | |
338 | # strings for the three builtin policies and invert it. |
|
341 | # strings for the three builtin policies and invert it. | |
339 | if __conflict_solve: |
|
342 | if __conflict_solve: | |
340 | inv_conflict_solve_user = __conflict_solve.copy() |
|
343 | inv_conflict_solve_user = __conflict_solve.copy() | |
341 | for name, func in [('preserve',preserve), ('update',update), |
|
344 | for name, func in [('preserve',preserve), ('update',update), | |
342 | ('add',add), ('add_flip',add_flip), |
|
345 | ('add',add), ('add_flip',add_flip), | |
343 | ('add_s',add_s)]: |
|
346 | ('add_s',add_s)]: | |
344 | if name in inv_conflict_solve_user.keys(): |
|
347 | if name in inv_conflict_solve_user.keys(): | |
345 | inv_conflict_solve_user[func] = inv_conflict_solve_user[name] |
|
348 | inv_conflict_solve_user[func] = inv_conflict_solve_user[name] | |
346 | del inv_conflict_solve_user[name] |
|
349 | del inv_conflict_solve_user[name] | |
347 |
conflict_solve.update( |
|
350 | conflict_solve.update(self.__dict_invert(inv_conflict_solve_user)) | |
348 | #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg |
|
351 | #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg | |
349 | #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve) |
|
352 | #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve) | |
350 | for key in data_dict: |
|
353 | for key in data_dict: | |
351 | if key not in self: |
|
354 | if key not in self: | |
352 | self[key] = data_dict[key] |
|
355 | self[key] = data_dict[key] | |
353 | else: |
|
356 | else: | |
354 | self[key] = conflict_solve[key](self[key],data_dict[key]) |
|
357 | self[key] = conflict_solve[key](self[key],data_dict[key]) | |
355 |
|
358 | |||
356 | def has_key(self,key): |
|
359 | def has_key(self,key): | |
357 | """Like has_key() dictionary method.""" |
|
360 | """Like has_key() dictionary method.""" | |
358 |
return self._ |
|
361 | return self._data.has_key(key) | |
359 |
|
362 | |||
360 | def hasattr(self,key): |
|
363 | def hasattr(self,key): | |
361 | """hasattr function available as a method. |
|
364 | """hasattr function available as a method. | |
362 |
|
365 | |||
363 | Implemented like has_key, to make sure that all available keys in the |
|
366 | Implemented like has_key, to make sure that all available keys in the | |
364 | internal dictionary of the Struct appear also as attributes (even |
|
367 | internal dictionary of the Struct appear also as attributes (even | |
365 | numeric keys).""" |
|
368 | numeric keys).""" | |
366 |
return self._ |
|
369 | return self._data.has_key(key) | |
367 |
|
370 | |||
368 | def items(self): |
|
371 | def items(self): | |
369 |
"""Return the items in the Struct's dictionary |
|
372 | """Return the items in the Struct's dictionary as (key, value)'s.""" | |
370 | as a call to {}.items().""" |
|
373 | return self._data.items() | |
371 | return self.__dict__.items() |
|
374 | ||
372 |
|
||||
373 | def keys(self): |
|
375 | def keys(self): | |
374 |
"""Return the keys in the Struct's dictionary |
|
376 | """Return the keys in the Struct's dictionary..""" | |
375 | as a call to {}.keys().""" |
|
377 | return self._data.keys() | |
376 | return self.__dict__.keys() |
|
378 | ||
377 |
|
379 | def values(self, keys=None): | ||
378 | def values(self,keys=None): |
|
380 | """Return the values in the Struct's dictionary. | |
379 | """Return the values in the Struct's dictionary, in the same format |
|
381 | ||
380 | as a call to {}.values(). |
|
|||
381 |
|
||||
382 | Can be called with an optional argument keys, which must be a list or |
|
382 | Can be called with an optional argument keys, which must be a list or | |
383 | tuple of keys. In this case it returns only the values corresponding |
|
383 | tuple of keys. In this case it returns only the values corresponding | |
384 |
to those keys (allowing a form of 'slicing' for Structs). |
|
384 | to those keys (allowing a form of 'slicing' for Structs). | |
|
385 | """ | |||
385 | if not keys: |
|
386 | if not keys: | |
386 |
return self._ |
|
387 | return self._data.values() | |
387 | else: |
|
388 | else: | |
388 | ret=[] |
|
389 | result=[] | |
389 | for k in keys: |
|
390 | for k in keys: | |
390 | ret.append(self[k]) |
|
391 | result.append(self[k]) | |
391 | return ret |
|
392 | return result | |
392 |
|
393 | |||
393 | def get(self,attr,val=None): |
|
394 | def get(self, attr, val=None): | |
394 | """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None.""" |
|
395 | """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None.""" | |
395 | try: |
|
396 | try: | |
396 | return self[attr] |
|
397 | return self[attr] | |
397 | except KeyError: |
|
398 | except KeyError: | |
398 | return val |
|
399 | return val | |
399 |
|
400 | |||
400 | def setdefault(self,attr,val=None): |
|
401 | def setdefault(self, attr, val=None): | |
401 | """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S""" |
|
402 | """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if k not in S""" | |
402 | if not self.has_key(attr): |
|
403 | if not self._data.has_key(attr): | |
403 | self[attr] = val |
|
404 | self[attr] = val | |
404 | return self.get(attr,val) |
|
405 | return self.get(attr, val) | |
405 |
|
406 | |||
406 | def allow_new_attr(self, allow = True): |
|
407 | def allow_new_attr(self, allow = True): | |
407 |
""" |
|
408 | """Set whether new attributes can be created in this Struct. | |
408 |
|
409 | |||
409 | This can be used to catch typos by verifying that the attribute user |
|
410 | This can be used to catch typos by verifying that the attribute user | |
410 | tries to change already exists in this Struct. |
|
411 | tries to change already exists in this Struct. | |
411 | """ |
|
412 | """ | |
412 |
self |
|
413 | object.__setattr__(self, '_allownew', allow) | |
413 |
|
414 | |||
414 |
|
415 | |||
415 | # end class Struct |
|
416 | # end class Struct | |
416 |
|
417 |
General Comments 0
You need to be logged in to leave comments.
Login now