Show More
@@ -111,7 +111,7 b' class ColorScheme:' | |||
|
111 | 111 | """Return a full copy of the object, optionally renaming it.""" |
|
112 | 112 | if name is None: |
|
113 | 113 | name = self.name |
|
114 |
return ColorScheme(name,self.colors. |
|
|
114 | return ColorScheme(name, self.colors.dict()) | |
|
115 | 115 | |
|
116 | 116 | class ColorSchemeTable(dict): |
|
117 | 117 | """General class to handle tables of color schemes. |
@@ -11,12 +11,13 b'' | |||
|
11 | 11 | |
|
12 | 12 | __all__ = ['Struct'] |
|
13 | 13 | |
|
14 | import inspect | |
|
14 | 15 | import types |
|
15 | 16 | import pprint |
|
16 | 17 | |
|
17 | 18 | from IPython.utils.genutils import list2dict2 |
|
18 | 19 | |
|
19 | class Struct: | |
|
20 | class Struct(object): | |
|
20 | 21 | """Class to mimic C structs but also provide convenient dictionary-like |
|
21 | 22 | functionality. |
|
22 | 23 | |
@@ -105,222 +106,224 b' class Struct:' | |||
|
105 | 106 | 'merge popitem setdefault update values ' |
|
106 | 107 | '__make_dict __dict_invert ').split() |
|
107 | 108 | |
|
108 |
def __init__(self,d |
|
|
109 |
"""Initialize with a dictionary, another Struct, or |
|
|
110 | explicitly the list of attributes. | |
|
109 | def __init__(self,data=None,**kw): | |
|
110 | """Initialize with a dictionary, another Struct, or data. | |
|
111 | 111 | |
|
112 | Both can be used, but the dictionary must come first: | |
|
113 | Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2). | |
|
112 | Parameters | |
|
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 |
|
|
116 | if dict is None: | |
|
117 | dict = {} | |
|
118 | if isinstance(dict,Struct): | |
|
119 | dict = dict.dict() | |
|
120 | elif dict and type(dict) is not types.DictType: | |
|
121 | raise TypeError,\ | |
|
122 |
|
|
|
123 |
d |
|
|
123 | object.__setattr__(self, '_allownew', True) | |
|
124 | object.__setattr__(self, '_data',{}) | |
|
125 | if data is None: | |
|
126 | data = {} | |
|
127 | if isinstance(data, Struct): | |
|
128 | data = data.dict() | |
|
129 | elif data and not isinstance(data, dict): | |
|
130 | raise TypeError('initialize with a dict, Struct or key=val pairs') | |
|
131 | data.update(kw) | |
|
124 | 132 | # do the updating by hand to guarantee that we go through the |
|
125 | 133 | # safety-checked __setitem__ |
|
126 |
for k,v in d |
|
|
134 | for k, v in data.items(): | |
|
127 | 135 | self[k] = v |
|
128 | ||
|
129 | 136 | |
|
130 | def __setitem__(self,key,value): | |
|
137 | def __setitem__(self, key, value): | |
|
131 | 138 | """Used when struct[key] = val calls are made.""" |
|
132 | if key in Struct.__protected: | |
|
133 | raise KeyError,'Key '+`key`+' is a protected key of class Struct.' | |
|
134 | if not self['__allownew'] and key not in self.__dict__: | |
|
139 | if isinstance(key, str): | |
|
140 | # I can't simply call hasattr here because it calls getattr, which | |
|
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 | 149 | raise KeyError( |
|
136 |
" |
|
|
137 | key) | |
|
138 | ||
|
139 | self.__dict__[key] = value | |
|
150 | "can't create unknown attribute %s. Check for typos, or use allow_new_attr" % key) | |
|
151 | self._data[key] = value | |
|
140 | 152 | |
|
141 | 153 | def __setattr__(self, key, value): |
|
142 | """Used when struct.key = val calls are made.""" | |
|
143 | self.__setitem__(key,value) | |
|
154 | 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 | 167 | def __str__(self): |
|
146 | """Gets called by print.""" | |
|
147 | ||
|
148 | return 'Struct('+ pprint.pformat(self.__dict__)+')' | |
|
168 | return 'Struct('+ pprint.pformat(self._data)+')' | |
|
149 | 169 | |
|
150 | 170 | def __repr__(self): |
|
151 | """Gets called by repr. | |
|
152 | ||
|
153 | A Struct can be recreated with S_new=eval(repr(S_old)).""" | |
|
154 | 171 | return self.__str__() |
|
155 | 172 | |
|
156 |
def __ |
|
|
157 | """Allows struct[key] access.""" | |
|
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__ | |
|
173 | def __contains__(self, key): | |
|
174 | return key in self._data | |
|
177 | 175 | |
|
178 | def __iadd__(self,other): | |
|
176 | def __iadd__(self, other): | |
|
179 | 177 | """S += S2 is a shorthand for S.merge(S2).""" |
|
180 | 178 | self.merge(other) |
|
181 | 179 | return self |
|
182 | 180 | |
|
183 | 181 | def __add__(self,other): |
|
184 |
"""S + S2 -> New Struct made f |
|
|
182 | """S + S2 -> New Struct made from S.merge(S2)""" | |
|
185 | 183 | Sout = self.copy() |
|
186 | 184 | Sout.merge(other) |
|
187 | 185 | return Sout |
|
188 | 186 | |
|
189 | 187 | def __sub__(self,other): |
|
190 | """Return S1-S2, where all keys in S2 have been deleted (if present) | |
|
191 | from S1.""" | |
|
188 | """Out of place remove keys from self that are in other.""" | |
|
192 | 189 | Sout = self.copy() |
|
193 | 190 | Sout -= other |
|
194 | 191 | return Sout |
|
195 | 192 | |
|
196 | 193 | def __isub__(self,other): |
|
197 | """Do in place S = S - S2, meaning all keys in S2 have been deleted | |
|
198 | (if present) from S1.""" | |
|
199 | ||
|
194 | """Inplace remove keys from self that are in other.""" | |
|
200 | 195 | for k in other.keys(): |
|
201 | 196 | if self.has_key(k): |
|
202 |
del self._ |
|
|
197 | del self._data[k] | |
|
203 | 198 | |
|
204 | 199 | def __make_dict(self,__loc_data__,**kw): |
|
205 |
"Helper function for update and merge. Return a dict from data. |
|
|
206 | ||
|
200 | """Helper function for update and merge. Return a dict from data. | |
|
201 | """ | |
|
207 | 202 | if __loc_data__ == None: |
|
208 |
d |
|
|
209 |
elif |
|
|
210 |
d |
|
|
211 | elif isinstance(__loc_data__,Struct): | |
|
212 |
d |
|
|
203 | data = {} | |
|
204 | elif isinstance(__loc_data__, dict): | |
|
205 | data = __loc_data__ | |
|
206 | elif isinstance(__loc_data__, Struct): | |
|
207 | data = __loc_data__._data | |
|
213 | 208 | else: |
|
214 |
raise TypeError |
|
|
209 | raise TypeError('update with a dict, Struct or key=val pairs') | |
|
215 | 210 | if kw: |
|
216 |
d |
|
|
217 |
return d |
|
|
218 | ||
|
219 |
def __dict_invert(self, |
|
|
220 |
"""Helper function for merge. |
|
|
221 | lists and returns a dict. with the elements of each list as keys and | |
|
222 | the original keys as values.""" | |
|
223 | ||
|
211 | data.update(kw) | |
|
212 | return data | |
|
213 | ||
|
214 | def __dict_invert(self, data): | |
|
215 | """Helper function for merge. | |
|
216 | ||
|
217 | Takes a dictionary whose values are lists and returns a dict with | |
|
218 | the elements of each list as keys and the original keys as values. | |
|
219 | """ | |
|
224 | 220 | outdict = {} |
|
225 |
for k,lst in d |
|
|
226 |
if |
|
|
221 | for k,lst in data.items(): | |
|
222 | if isinstance(lst, str): | |
|
227 | 223 | lst = lst.split() |
|
228 | 224 | for entry in lst: |
|
229 | 225 | outdict[entry] = k |
|
230 | 226 | return outdict |
|
231 | ||
|
227 | ||
|
232 | 228 | def clear(self): |
|
233 | 229 | """Clear all attributes.""" |
|
234 |
self._ |
|
|
235 | ||
|
230 | self._data.clear() | |
|
231 | ||
|
236 | 232 | def copy(self): |
|
237 | 233 | """Return a (shallow) copy of a Struct.""" |
|
238 |
return Struct(self._ |
|
|
239 | ||
|
234 | return Struct(self._data.copy()) | |
|
235 | ||
|
240 | 236 | def dict(self): |
|
241 | 237 | """Return the Struct's dictionary.""" |
|
242 |
return self._ |
|
|
243 | ||
|
238 | return self._data | |
|
239 | ||
|
244 | 240 | def dictcopy(self): |
|
245 | 241 | """Return a (shallow) copy of the Struct's dictionary.""" |
|
246 |
return self._ |
|
|
242 | return self._data.copy() | |
|
247 | 243 | |
|
248 | 244 | def popitem(self): |
|
249 | """S.popitem() -> (k, v), remove and return some (key, value) pair as | |
|
250 | a 2-tuple; but raise KeyError if S is empty.""" | |
|
251 | return self.__dict__.popitem() | |
|
252 | ||
|
245 | """Return (key, value) tuple and remove from Struct. | |
|
246 | ||
|
247 | If key is not present raise KeyError. | |
|
248 | """ | |
|
249 | return self._data.popitem() | |
|
250 | ||
|
253 | 251 | def update(self,__loc_data__=None,**kw): |
|
254 |
"""Update (merge) with data from another Struct or |
|
|
255 | Optionally, one or more key=value pairs can be given at the end for | |
|
256 | direct update.""" | |
|
257 | ||
|
252 | """Update (merge) with data from another Struct or dict. | |
|
253 | ||
|
254 | Parameters | |
|
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 | 261 | # The funny name __loc_data__ is to prevent a common variable name |
|
259 | 262 | # which could be a fieled of a Struct to collide with this |
|
260 | 263 | # parameter. The problem would arise if the function is called with a |
|
261 | 264 | # keyword with this same name that a user means to add as a Struct |
|
262 | 265 | # field. |
|
263 |
newdict = |
|
|
264 | for k,v in newdict.iteritems(): | |
|
266 | newdict = self.__make_dict(__loc_data__, **kw) | |
|
267 | for k, v in newdict.iteritems(): | |
|
265 | 268 | self[k] = v |
|
266 | ||
|
267 | def merge(self,__loc_data__=None,__conflict_solve=None,**kw): | |
|
269 | ||
|
270 | def merge(self, __loc_data__=None, __conflict_solve=None, **kw): | |
|
268 | 271 | """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S. |
|
269 | ||
|
272 | ||
|
270 | 273 | This is similar to update(), but much more flexible. First, a dict is |
|
271 | 274 | made from data+key=value pairs. When merging this dict with the Struct |
|
272 | 275 | S, the optional dictionary 'conflict' is used to decide what to do. |
|
273 | ||
|
276 | ||
|
274 | 277 | If conflict is not given, the default behavior is to preserve any keys |
|
275 | 278 | with their current value (the opposite of the update method's |
|
276 | 279 | behavior). |
|
277 | ||
|
280 | ||
|
278 | 281 | conflict is a dictionary of binary functions which will be used to |
|
279 | 282 | solve key conflicts. It must have the following structure: |
|
280 | ||
|
283 | ||
|
281 | 284 | conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc } |
|
282 | ||
|
285 | ||
|
283 | 286 | Values must be lists or whitespace separated strings which are |
|
284 | 287 | automatically converted to lists of strings by calling string.split(). |
|
285 | ||
|
288 | ||
|
286 | 289 | Each key of conflict is a function which defines a policy for |
|
287 | 290 | resolving conflicts when merging with the input data. Each fn must be |
|
288 | 291 | a binary function which returns the desired outcome for a key |
|
289 | 292 | conflict. These functions will be called as fn(old,new). |
|
290 | ||
|
293 | ||
|
291 | 294 | An example is probably in order. Suppose you are merging the struct S |
|
292 | 295 | with a dict D and the following conflict policy dict: |
|
293 | ||
|
296 | ||
|
294 | 297 | S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'}) |
|
295 | ||
|
298 | ||
|
296 | 299 | If the key 'a' is found in both S and D, the merge method will call: |
|
297 | ||
|
300 | ||
|
298 | 301 | S['a'] = fn1(S['a'],D['a']) |
|
299 | ||
|
302 | ||
|
300 | 303 | As a convenience, merge() provides five (the most commonly needed) |
|
301 | 304 | pre-defined policies: preserve, update, add, add_flip and add_s. The |
|
302 | 305 | easiest explanation is their implementation: |
|
303 | ||
|
306 | ||
|
304 | 307 | preserve = lambda old,new: old |
|
305 | 308 | update = lambda old,new: new |
|
306 | 309 | add = lambda old,new: old + new |
|
307 | 310 | add_flip = lambda old,new: new + old # note change of order! |
|
308 | 311 | add_s = lambda old,new: old + ' ' + new # only works for strings! |
|
309 | ||
|
312 | ||
|
310 | 313 | You can use those four words (as strings) as keys in conflict instead |
|
311 | 314 | of defining them as functions, and the merge method will substitute |
|
312 | 315 | the appropriate functions for you. That is, the call |
|
313 | ||
|
316 | ||
|
314 | 317 | S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]}) |
|
315 | ||
|
318 | ||
|
316 | 319 | will automatically substitute the functions preserve and add for the |
|
317 | 320 | names 'preserve' and 'add' before making any function calls. |
|
318 | ||
|
321 | ||
|
319 | 322 | For more complicated conflict resolution policies, you still need to |
|
320 | 323 | construct your own functions. """ |
|
321 | ||
|
322 |
data_dict = |
|
|
323 | ||
|
324 | ||
|
325 | data_dict = self.__make_dict(__loc_data__,**kw) | |
|
326 | ||
|
324 | 327 | # policies for conflict resolution: two argument functions which return |
|
325 | 328 | # the value that will go in the new struct |
|
326 | 329 | preserve = lambda old,new: old |
@@ -328,10 +331,10 b' class Struct:' | |||
|
328 | 331 | add = lambda old,new: old + new |
|
329 | 332 | add_flip = lambda old,new: new + old # note change of order! |
|
330 | 333 | add_s = lambda old,new: old + ' ' + new |
|
331 | ||
|
334 | ||
|
332 | 335 | # default policy is to keep current keys when there's a conflict |
|
333 | conflict_solve = list2dict2(self.keys(),default = preserve) | |
|
334 | ||
|
336 | conflict_solve = list2dict2(self.keys(), default = preserve) | |
|
337 | ||
|
335 | 338 | # the conflict_solve dictionary is given by the user 'inverted': we |
|
336 | 339 | # need a name-function mapping, it comes as a function -> names |
|
337 | 340 | # dict. Make a local copy (b/c we'll make changes), replace user |
@@ -344,7 +347,7 b' class Struct:' | |||
|
344 | 347 | if name in inv_conflict_solve_user.keys(): |
|
345 | 348 | inv_conflict_solve_user[func] = inv_conflict_solve_user[name] |
|
346 | 349 | del inv_conflict_solve_user[name] |
|
347 |
conflict_solve.update( |
|
|
350 | conflict_solve.update(self.__dict_invert(inv_conflict_solve_user)) | |
|
348 | 351 | #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg |
|
349 | 352 | #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve) |
|
350 | 353 | for key in data_dict: |
@@ -352,64 +355,62 b' class Struct:' | |||
|
352 | 355 | self[key] = data_dict[key] |
|
353 | 356 | else: |
|
354 | 357 | self[key] = conflict_solve[key](self[key],data_dict[key]) |
|
355 | ||
|
358 | ||
|
356 | 359 | def has_key(self,key): |
|
357 | 360 | """Like has_key() dictionary method.""" |
|
358 |
return self._ |
|
|
359 | ||
|
361 | return self._data.has_key(key) | |
|
362 | ||
|
360 | 363 | def hasattr(self,key): |
|
361 | 364 | """hasattr function available as a method. |
|
362 | ||
|
365 | ||
|
363 | 366 | Implemented like has_key, to make sure that all available keys in the |
|
364 | 367 | internal dictionary of the Struct appear also as attributes (even |
|
365 | 368 | numeric keys).""" |
|
366 |
return self._ |
|
|
367 | ||
|
369 | return self._data.has_key(key) | |
|
370 | ||
|
368 | 371 | def items(self): |
|
369 |
"""Return the items in the Struct's dictionary |
|
|
370 | as a call to {}.items().""" | |
|
371 | return self.__dict__.items() | |
|
372 | ||
|
372 | """Return the items in the Struct's dictionary as (key, value)'s.""" | |
|
373 | return self._data.items() | |
|
374 | ||
|
373 | 375 | def keys(self): |
|
374 |
"""Return the keys in the Struct's dictionary |
|
|
375 | as a call to {}.keys().""" | |
|
376 | return self.__dict__.keys() | |
|
377 | ||
|
378 | def values(self,keys=None): | |
|
379 | """Return the values in the Struct's dictionary, in the same format | |
|
380 | as a call to {}.values(). | |
|
381 | ||
|
376 | """Return the keys in the Struct's dictionary..""" | |
|
377 | return self._data.keys() | |
|
378 | ||
|
379 | def values(self, keys=None): | |
|
380 | """Return the values in the Struct's dictionary. | |
|
381 | ||
|
382 | 382 | Can be called with an optional argument keys, which must be a list or |
|
383 | 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 | 386 | if not keys: |
|
386 |
return self._ |
|
|
387 | return self._data.values() | |
|
387 | 388 | else: |
|
388 | ret=[] | |
|
389 | result=[] | |
|
389 | 390 | for k in keys: |
|
390 | ret.append(self[k]) | |
|
391 | return ret | |
|
392 | ||
|
393 | def get(self,attr,val=None): | |
|
391 | result.append(self[k]) | |
|
392 | return result | |
|
393 | ||
|
394 | def get(self, attr, val=None): | |
|
394 | 395 | """S.get(k[,d]) -> S[k] if k in S, else d. d defaults to None.""" |
|
395 | 396 | try: |
|
396 | 397 | return self[attr] |
|
397 | 398 | except KeyError: |
|
398 | 399 | return val |
|
399 | ||
|
400 | def setdefault(self,attr,val=None): | |
|
400 | ||
|
401 | def setdefault(self, attr, val=None): | |
|
401 | 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 | 404 | self[attr] = val |
|
404 | return self.get(attr,val) | |
|
405 | return self.get(attr, val) | |
|
405 | 406 | |
|
406 | 407 | def allow_new_attr(self, allow = True): |
|
407 |
""" |
|
|
408 | """Set whether new attributes can be created in this Struct. | |
|
408 | 409 | |
|
409 | 410 | This can be used to catch typos by verifying that the attribute user |
|
410 | 411 | tries to change already exists in this Struct. |
|
411 | 412 | """ |
|
412 |
self |
|
|
413 | object.__setattr__(self, '_allownew', allow) | |
|
413 | 414 | |
|
414 | 415 | |
|
415 | 416 | # end class Struct |
General Comments 0
You need to be logged in to leave comments.
Login now