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