Mercurial > MadButterfly
view pyink/trait.py @ 1434:4be04f29fa70
Add functions to search for the text recursively inside coord_t tree. Once we find the first instance, we change the text of it. We need to think about how to manage the multiple segment texts, which is composed of several tspan.
author | wycc |
---|---|
date | Mon, 11 Apr 2011 12:52:09 +0800 |
parents | 1a4d15fe2c62 |
children | 28769a82a72d |
line wrap: on
line source
## \brief Implement descriptors for mapping attributes for traits. # # The instances of require map attributes of traits to corresponding # attributes of the instance of the composition class. # class require(object): def __init__(self, trait_clazz): self.trait_clazz = trait_clazz pass def __get__(self, instance, owner): if not instance: # from a class object return self attrname = instance._trait_attrname_map[self] composite_obj = instance._trait_composite_obj val = getattr(composite_obj, attrname) return val def __set__(self, instance, value): attrname = instance._trait_attrname_map[self] composite_obj = instance._trait_composite_obj setattr(composite_obj, attrname, value) pass pass ## \brief Decorator for making a class being a trait. # def trait(trait_clazz): attrname_map = {} trait_clazz._trait_attrname_map = attrname_map for attr in dir(trait_clazz): value = getattr(trait_clazz, attr) if value != require: continue require_o = require(trait_clazz) setattr(trait_clazz, attr, require_o) attrname_map[require_o] = attr pass trait_clazz._is_trait = True return trait_clazz ## \brief The function to return a proxy for a method of a trait. # def trait_method_proxy(trait_clazz, method): def trait_method_proxy_real(self, *args, **kws): if not hasattr(self, '_all_trait_objs'): # self is an instance of the class composed from traits. self._all_trait_objs = {} pass try: trait_obj = self._all_trait_objs[trait_clazz] except KeyError: trait_obj = trait_clazz() trait_obj._trait_composite_obj = self self._all_trait_objs[trait_clazz] = trait_obj pass r = method(trait_obj, *args, **kws) return r return trait_method_proxy_real ## \brief Derive and modify an existing trait. # def derive_trait(a_trait, composite_clazz): # # Set a map mamping requires to attribute names. # # Search provide_traits for requires of a_trait, and patch # attrname_map for it. # attrname_map = None if hasattr(composite_clazz, 'provide_traits'): provide_traits = composite_clazz.provide_traits attrname_map = dict(a_trait._trait_attrname_map) for req in provide_traits: if req.trait_clazz == a_trait: attrname_map[req] = provide_traits[req] pass pass pass dic = {} if attrname_map: dic['_trait_attrname_map'] = attrname_map pass derived = type('derived_trait', (a_trait,), dic) return derived ## \brief Check explicity providing for requires. # # Composite maps require attributes of traits to the attribute, with # the same name, of composition class by default. But, composition # class can specify name of the attribute that will satisfy a require. # def _check_provide_traits(clazz): traits = clazz.use_traits # # Check content of clazz.provide_traits # if hasattr(clazz, 'provide_traits'): if not isinstance(clazz.provide_traits, dict): raise TypeError, \ 'provide_traits of a composite must be a dictionary' provide_set = set([req.trait_clazz for req in clazz.provide_traits.keys()]) trait_set = set(traits) unused_set = provide_set - trait_set if unused_set: raise ValueError, \ 'can not find %s in provide_traits' % (repr(unused_set.pop())) for req in clazz.provide_traits: if not isinstance(req, require): raise TypeError, \ '%s is not a require: key of an ' \ 'attribute name map must be a require' % (repr(req)) pass pass pass ## \brief Include methods from trait for a composition class. # def _include_methods(clazz): traits = clazz.use_traits # # Count number of appearing in all traits for every attribute name. # attrname_cnts = {} for a_trait in traits: for attr in dir(a_trait): if attr.startswith('_'): continue value = getattr(a_trait, attr) if isinstance(value, require) or not callable(value): continue attrname_cnts[attr] = attrname_cnts.setdefault(attr, 0) + 1 pass pass if hasattr(clazz, 'method_map_traits'): method_map_traits = clazz.method_map_traits else: method_map_traits = {} pass # # Set a proxy for every exported methods. # derived_traits = clazz._derived_traits = {} for a_trait in traits: derived = derive_trait(a_trait, clazz) derived_traits[a_trait] = derived for attr in dir(derived): if attr not in attrname_cnts: # hidden continue if attrname_cnts[attr] > 1: # conflict continue if hasattr(clazz, attr): # override continue value = getattr(a_trait, attr) if value in method_map_traits: # do it later continue value = getattr(derived, attr) if not callable(value): continue func = value.im_func proxy = trait_method_proxy(derived, func) setattr(clazz, attr, proxy) pass pass # # Set a proxy for methods specified in method_map_traits. # for method, attrname in method_map_traits.items(): if not callable(method): raise TypeError, \ '%s.%s is not a callable' % (repr(a_trait), repr(method)) a_trait = method.im_class if a_trait not in derived_traits: raise TypeError, \ '%s is not a trait used by the composition class' % \ (repr(a_trait)) derived = derived_traits[a_trait] func = method.im_func proxy = trait_method_proxy(derived, func) setattr(clazz, attrname, proxy) pass pass ## \brief A decorator to make class composited from traits. # # The class decorated by composite must own a use_traits attribute. # # \verbatim # @trait # class trait_a(object): # var_a = require # def xxx(self): return self.var_a # # @trait # class trait_b(object): # def ooo(self): pass # # @composite # class foo(object): # use_traits = (trait_a, trait_b) # # var_a = 'value of var_a' # pass # # obj = foo() # \endverbatim # # To make a class from a set of traits. You must decorate the class # with the decorator 'composite'. The class must has an attribute, # named use_traits, to provide a list or tuple of traits. # # Class that defines a trait must decorated with the decorator # 'trait'. If the trait need to access state (varaibles) of the # intances of composition class, it must define attributes with value # 'require', likes what 'var_a' of trait_a does. Then, the attributes # would be mapped to corresponding attributes of instances of # composition class. For example, when you call obj.xxx(), it returns # value of 'var_a', and attribute 'var_a' is a property that returns # the value of 'var_a' of 'obj', an instance of class foo. # # By default, traits map attribute 'var_a' to 'var_a' of instances of # composition classes. But, you can change it by specifying the map # in an attribute, named 'provide_traits', defined in composition # class. The attribute provide_traits of a composition class is a # dictionary mapping from require attributes of used traits to names # of attributes of the composition class. # # \verbatim # @composite # class foo(object): # use_trait = (trait_a, trait_b) # provide_traits = {trait_a.var_a: 'var_foo'} # # var_foo = 'value of var_foo' # pass # \endverbatim # # Like mapping require attributes of used traits, there is a map, # named method_map_traits, for methods of used traits. # # \verbatim # @composite # class foo(object): # use_trait = (trait_a, trait_b) # provide_traits = {trait_a.var_a: 'var_foo'} # method_map_traits = {trait_a.xxx: 'hello') # # var_foo = 'value of var_foo' # pass # \endverbatim # # Previous example maps trait_a.xxx method to foo.hello method. # composite does not include methods that has a name prefixed by a '_' # charater. But, you can still force it, by an explicity mapping in # method_map_traits, to include a method prefixed by a '_' character. # def composite(clazz): if not hasattr(clazz, 'use_traits'): raise KeyError, \ '%s has no use_trait: it must be a list of traits' % (repr(clazz)) traits = clazz.use_traits for a_trait in traits: if not hasattr(a_trait, '_is_trait'): raise TypeError, '%s is not a trait' % (repr(a_trait)) pass _check_provide_traits(clazz) _include_methods(clazz) return clazz if __name__ == '__main__': @trait class hello(object): msg = require bye_provide = require def hello(self, name): return self.msg + ' hello ' + name def hello_provide(self): return 'hello provide' def require_bye(self): return self.bye_provide() pass @trait class bye(object): msg = require hello_provide = require def bye(self, name): return self.msg + ' bye ' + name def bye_provide(self): return 'bye provide' def require_hello(self): return self.hello_provide() pass @composite class hello_bye(object): use_traits = (hello, bye) provide_traits = {hello.msg: 'msg1'} method_map_traits = {hello.hello: 'hello1'} msg = 'hello_bye' msg1 = 'hello_bye_msg1' pass o = hello_bye() assert o.hello1('Miky') == 'hello_bye_msg1 hello Miky' assert o.bye('Miky') == 'hello_bye bye Miky' assert o.require_bye() == 'bye provide' assert o.require_hello() == 'hello provide' print 'OK' pass