Mercurial > MadButterfly
comparison pyink/trait.py @ 1348:22a79dcbaec6
Change structure of provide_traits and method_map_traits
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Sat, 12 Feb 2011 19:22:21 +0800 |
parents | aaa88d805aac |
children | b0e54ae756f8 |
comparison
equal
deleted
inserted
replaced
1347:aaa88d805aac | 1348:22a79dcbaec6 |
---|---|
2 # | 2 # |
3 # The instances of require map attributes of traits to corresponding | 3 # The instances of require map attributes of traits to corresponding |
4 # attributes of the instance of the composition class. | 4 # attributes of the instance of the composition class. |
5 # | 5 # |
6 class require(object): | 6 class require(object): |
7 def __init__(self, trait_clazz): | |
8 self.trait_clazz = trait_clazz | |
9 pass | |
10 | |
7 def __get__(self, instance, owner): | 11 def __get__(self, instance, owner): |
8 if not instance: # from a class object | 12 if not instance: # from a class object |
9 return self | 13 return self |
10 | 14 |
11 attrname = instance._trait_attrname_map[self] | 15 attrname = instance._trait_attrname_map[self] |
30 for attr in dir(trait_clazz): | 34 for attr in dir(trait_clazz): |
31 value = getattr(trait_clazz, attr) | 35 value = getattr(trait_clazz, attr) |
32 if value != require: | 36 if value != require: |
33 continue | 37 continue |
34 | 38 |
35 require_o = require() | 39 require_o = require(trait_clazz) |
36 setattr(trait_clazz, attr, require_o) | 40 setattr(trait_clazz, attr, require_o) |
37 attrname_map[require_o] = attr | 41 attrname_map[require_o] = attr |
38 pass | 42 pass |
39 | 43 |
40 trait_clazz._is_trait = True | 44 trait_clazz._is_trait = True |
67 | 71 |
68 | 72 |
69 ## \brief Derive and modify an existing trait. | 73 ## \brief Derive and modify an existing trait. |
70 # | 74 # |
71 def derive_trait(a_trait, composite_clazz): | 75 def derive_trait(a_trait, composite_clazz): |
76 # | |
77 # Set a map mamping requires to attribute names. | |
78 # | |
79 # Search provide_traits for requires of a_trait, and patch | |
80 # attrname_map for it. | |
81 # | |
72 attrname_map = None | 82 attrname_map = None |
73 if hasattr(composite_clazz, 'provide_traits'): | 83 if hasattr(composite_clazz, 'provide_traits'): |
74 provide_traits = composite_clazz.provide_traits | 84 provide_traits = composite_clazz.provide_traits |
75 if a_trait in provide_traits: | 85 attrname_map = dict(a_trait._trait_attrname_map) |
76 provide_trait = provide_traits[a_trait] | 86 for req in provide_traits: |
77 attrname_map = dict(a_trait._trait_attrname_map) | 87 if req.trait_clazz == a_trait: |
78 attrname_map.update(provide_trait) | 88 attrname_map[req] = provide_traits[req] |
89 pass | |
79 pass | 90 pass |
80 pass | 91 pass |
81 | 92 |
82 dic = {} | 93 dic = {} |
83 if attrname_map: | 94 if attrname_map: |
87 derived = type('derived_trait', (a_trait,), dic) | 98 derived = type('derived_trait', (a_trait,), dic) |
88 | 99 |
89 return derived | 100 return derived |
90 | 101 |
91 | 102 |
92 ## \brief Handle explicity providing for requires. | 103 ## \brief Check explicity providing for requires. |
93 # | 104 # |
94 # Composite maps require attributes of traits to the attribute, with | 105 # Composite maps require attributes of traits to the attribute, with |
95 # the same name, of composition class by default. But, composition | 106 # the same name, of composition class by default. But, composition |
96 # class can specify name of the attribute that will satisfy a require. | 107 # class can specify name of the attribute that will satisfy a require. |
97 # | 108 # |
98 def _handle_provide_traits(clazz): | 109 def _check_provide_traits(clazz): |
99 traits = clazz.use_traits | 110 traits = clazz.use_traits |
100 | 111 |
101 # | 112 # |
102 # Check content of clazz.provide_traits | 113 # Check content of clazz.provide_traits |
103 # | 114 # |
104 if hasattr(clazz, 'provide_traits'): | 115 if hasattr(clazz, 'provide_traits'): |
105 if not isinstance(clazz.provide_traits, dict): | 116 if not isinstance(clazz.provide_traits, dict): |
106 raise TypeError, \ | 117 raise TypeError, \ |
107 'provide_traits of a composite must be a dictionary' | 118 'provide_traits of a composite must be a dictionary' |
108 | 119 |
109 provide_set = set(clazz.provide_traits.keys()) | 120 provide_set = set([req.trait_clazz |
121 for req in clazz.provide_traits.keys()]) | |
110 trait_set = set(traits) | 122 trait_set = set(traits) |
111 unused_set = provide_set - trait_set | 123 unused_set = provide_set - trait_set |
112 if unused_set: | 124 if unused_set: |
113 raise ValueError, \ | 125 raise ValueError, \ |
114 'can not find %s in provide_traits' % (repr(unused_set.pop())) | 126 'can not find %s in provide_traits' % (repr(unused_set.pop())) |
115 | 127 |
116 for trait, attrname_map in clazz.provide_traits.items(): | 128 for req in clazz.provide_traits: |
117 for req in attrname_map: | 129 if not isinstance(req, require): |
118 if not isinstance(req, require): | 130 raise TypeError, \ |
119 raise TypeError, \ | 131 '%s is not a require: key of an ' \ |
120 '%s is not a require: key of an ' \ | 132 'attribute name map must be a require' % (repr(req)) |
121 'attribute name map must be a require' % (repr(req)) | |
122 pass | |
123 pass | 133 pass |
124 pass | 134 pass |
125 pass | 135 pass |
126 | 136 |
127 | 137 |
159 derived_traits = clazz._derived_traits = {} | 169 derived_traits = clazz._derived_traits = {} |
160 for a_trait in traits: | 170 for a_trait in traits: |
161 derived = derive_trait(a_trait, clazz) | 171 derived = derive_trait(a_trait, clazz) |
162 derived_traits[a_trait] = derived | 172 derived_traits[a_trait] = derived |
163 | 173 |
164 if a_trait in method_map_traits: | |
165 method_map_trait = method_map_traits[a_trait] | |
166 else: | |
167 method_map_trait = {} | |
168 | |
169 for attr in dir(derived): | 174 for attr in dir(derived): |
170 if attr not in attrname_cnts: # hidden | 175 if attr not in attrname_cnts: # hidden |
171 continue | 176 continue |
172 if attrname_cnts[attr] > 1: # conflict | 177 if attrname_cnts[attr] > 1: # conflict |
173 continue | 178 continue |
174 | 179 |
175 if hasattr(clazz, attr): # override | 180 if hasattr(clazz, attr): # override |
176 continue | 181 continue |
177 | 182 |
178 value = getattr(a_trait, attr) | 183 value = getattr(a_trait, attr) |
179 if value in method_map_trait: # do it later | 184 if value in method_map_traits: # do it later |
180 continue | 185 continue |
181 | 186 |
182 value = getattr(derived, attr) | 187 value = getattr(derived, attr) |
183 | 188 |
184 if not callable(value): | 189 if not callable(value): |
190 setattr(clazz, attr, proxy) | 195 setattr(clazz, attr, proxy) |
191 pass | 196 pass |
192 pass | 197 pass |
193 | 198 |
194 # | 199 # |
195 # Map methods specified in method_map_traits. | 200 # Set a proxy for methods specified in method_map_traits. |
196 # | 201 # |
197 for a_trait, method_map_trait in method_map_traits.items(): | 202 for method, attrname in method_map_traits.items(): |
203 if not callable(method): | |
204 raise TypeError, \ | |
205 '%s.%s is not a callable' % (repr(a_trait), repr(method)) | |
206 | |
207 a_trait = method.im_class | |
198 if a_trait not in derived_traits: | 208 if a_trait not in derived_traits: |
199 raise TypeError, \ | 209 raise TypeError, \ |
200 '%s is not a trait used by the composition class' % \ | 210 '%s is not a trait used by the composition class' % \ |
201 (repr(a_trait)) | 211 (repr(a_trait)) |
202 | 212 |
203 derived = derived_traits[a_trait] | 213 derived = derived_traits[a_trait] |
204 for method, attrname in method_map_trait.items(): | 214 func = method.im_func |
205 if not callable(method): | 215 proxy = trait_method_proxy(derived, func) |
206 raise TypeError, \ | 216 setattr(clazz, attrname, proxy) |
207 '%s.%s is not a callable' % (repr(a_trait), repr(method)) | |
208 func = method.im_func | |
209 proxy = trait_method_proxy(derived, func) | |
210 setattr(clazz, attrname, proxy) | |
211 pass | |
212 pass | 217 pass |
213 pass | 218 pass |
214 | 219 |
215 | 220 |
216 ## \brief A decorator to make class composited from traits. | 221 ## \brief A decorator to make class composited from traits. |
251 # the value of 'var_a' of 'obj', an instance of class foo. | 256 # the value of 'var_a' of 'obj', an instance of class foo. |
252 # | 257 # |
253 # By default, traits map attribute 'var_a' to 'var_a' of instances of | 258 # By default, traits map attribute 'var_a' to 'var_a' of instances of |
254 # composition classes. But, you can change it by specifying the map | 259 # composition classes. But, you can change it by specifying the map |
255 # in an attribute, named 'provide_traits', defined in composition | 260 # in an attribute, named 'provide_traits', defined in composition |
256 # class. The attribute provide_traits is a dictionary mapping from | 261 # class. The attribute provide_traits of a composition class is a |
257 # trait class to a dictionary, named 'attrname_map' for the trait. | 262 # dictionary mapping from require attributes of used traits to names |
258 # The attrname_map maps require attributes of the trait to names of | 263 # of attributes of the composition class. |
259 # attributes of instances of the composition class. | |
260 # | 264 # |
261 def composite(clazz): | 265 def composite(clazz): |
262 if not hasattr(clazz, 'use_traits'): | 266 if not hasattr(clazz, 'use_traits'): |
263 raise KeyError, \ | 267 raise KeyError, \ |
264 '%s has no use_trait: it must be a list of traits' % (repr(clazz)) | 268 '%s has no use_trait: it must be a list of traits' % (repr(clazz)) |
267 for a_trait in traits: | 271 for a_trait in traits: |
268 if not hasattr(a_trait, '_is_trait'): | 272 if not hasattr(a_trait, '_is_trait'): |
269 raise TypeError, '%s is not a trait' % (repr(a_trait)) | 273 raise TypeError, '%s is not a trait' % (repr(a_trait)) |
270 pass | 274 pass |
271 | 275 |
272 _handle_provide_traits(clazz) | 276 _check_provide_traits(clazz) |
273 | 277 |
274 _include_methods(clazz) | 278 _include_methods(clazz) |
275 | 279 |
276 return clazz | 280 return clazz |
277 | 281 |
294 pass | 298 pass |
295 | 299 |
296 @composite | 300 @composite |
297 class hello_bye(object): | 301 class hello_bye(object): |
298 use_traits = (hello, bye) | 302 use_traits = (hello, bye) |
299 | 303 provide_traits = {hello.msg: 'msg1'} |
300 provide_hello = {hello.msg: 'msg1'} | 304 method_map_traits = {hello.hello: 'hello1'} |
301 provide_traits = {hello: provide_hello} | |
302 | |
303 method_map_hello = {hello.hello: 'hello1'} | |
304 method_map_traits = {hello: method_map_hello} | |
305 | 305 |
306 msg = 'hello_bye' | 306 msg = 'hello_bye' |
307 msg1 = 'hello_bye_msg1' | 307 msg1 = 'hello_bye_msg1' |
308 pass | 308 pass |
309 | 309 |