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