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