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