[PEAK] Config like metadata

Radek Kanovsky rk at dat.cz
Tue Dec 14 05:19:39 EST 2004


Hi all,

I have put together a few simple classes which allow declare and obtain
general metadata (see metadata module at the end). Purpose will be
obvious from example.

Suppose we need title or description for every feature of our
model.Element subclass (for generating HTML or GUI form for example).
model.Feature doesn't count with titles so we should declare titles
otherwise.

  >>> from peak.core import *
  >>> class Contact (model.Element) :
  ...   class name (model.Attribute) : referencedType = model.String
  ...   class city (model.Attribute) : referencedType = model.String
  ...   class phone (model.Attribute) : referencedType = model.String
  ... 
  >>> from metadata import *
  >>> class Title (Metadata) : pass
  ...

'Title' class is kind of property that can be declared over class
attributes via standard binding.declareAttribute function
or binding.metadata advisor:

  >>> binding.declareAttribute(Contact,'name',Title('Your name'))

or

  >>> class Contact (model.Element) :
  ...   [...]
  ...   binding.metadata(name=Title('Your name'))

or 

  >>> class Contact (model.Element) :
  ...   class name (model.Attribute) :
  ...     referencedType = model.String
  ...     metadata = Title('Your name')

Now, titles can be obtained by using 'of' classmethod of Metadata
class. Signature is 'of(klass, attribute=None, default=NOT_FOUND)'.

  >>> Title.of(Contact, 'name')
  'Your name'
  >>> Title.of(Contact, 'city')
  NOT_FOUND


It is possible to declare MetaRule that will generate titles
programaticaly if we are lazy and don't want to declare every title
"manually":

  >>> binding.declareAttribute(
  ...      Contact, '*', Title(MetaRule(lambda m,c,a: a.capitalize()))
  ... )

The asterisk works here as default declaration. It is used if no
explicit declaration is found for given attribute in Contact class or
its base classes. And MetaRule is special case. Instance of MetaRule
is not returned directly but result of calling its argument is returned
instead. Every feature has Title now:

  >>> [(a,Title.of(Contact,a,None)) for a in Contact.mdl_featureNames]
  [('city', 'City'), ('name', 'Your name'), ('phone', 'Phone')]


Metadata are inherited so MetaRule can be declared over 'object' class
for setting system-wide rule for title generation:

  >>> binding.declareAttribute(
  ...      object, '*', Title(MetaRule(lambda m,c,a: a.capitalize()))
  ... )
  >>> Title.of(file, 'open')
  'Open'



It is also possible to declare metadata that are not bound to any
particular attribute but to class itself by using None as attribute.

  >>> class PrimaryKey (Metadata) : pass
  ...
  >>> class Person (model.Element) : pass
  ...
  >>> binding.declareAttribute(Person, None, PrimaryKey(('pid',)))
  >>> PrimaryKey.of(Person)
  ('pid',)


Current limitations:

    * metadata registry is global and is not thread-safe
    * works only with new-style classes for now
    * it would be better to raise exception when metadata is not found
      instead of returning NOT_FOUND (if default is not given)
    * registry allows redeclare metadata after they have been looked up

RadekK





------------------------ metadata.py -------------------------------

from peak.binding.attributes import declareAttribute
from peak.util.symbols import NOT_FOUND


__all__ = ['MetaRule', 'Metadata']


class _metadata_registry (dict) :
    
    __slots__ = ()
    
    def matchingItems (self, meta, subj, attr) :
        mro = subj.__mro__
        get = self.get
        if attr in (None, '*') :
            for b in mro :
                rule = get((meta, b, attr), NOT_FOUND)
                if rule is not NOT_FOUND :
                    yield meta, b, attr, rule
        else :
            for a in (attr, '*') :
                for b in mro :
                    rule = get((meta, b, a), NOT_FOUND)
                    if rule is not NOT_FOUND :
                        yield meta, b, a, rule

_metadata_registry = _metadata_registry()




            
class MetaRule :

    def __init__ (self, rule) :
        self.rule = rule

    def __call__ (self, meta, subj, attrName) :
        return self.rule(meta, subj, attrName)




class Metadata (object) :
    
    def __init__ (self, rule) :
        self.rule = rule
    
    def __call__ (self,  subj, attrName, key) :
        rule = self.rule
        if isinstance(rule, MetaRule) :
            return rule(subj, attrName, key)
        else :
            return rule

    def register (self, subj, attrName=None) :
        _metadata_registry[(self.__class__, subj, attrName)] = self
    
    def setDefault (klass, subj, *args, **kw) :
        rule = klass(*args, **kw)
        rule.register(subj, '*')
    setDefault = classmethod(setDefault)

    def of (klass, subj, attrName=None, default=NOT_FOUND) :
        for p,c,a,r in _metadata_registry.matchingItems(klass,subj,attrName) :
            return r(klass, subj, attrName)
        return default
    of = classmethod(of)


declareAttribute.addMethod(Metadata, lambda c, a, m: m.register(c,a))



More information about the PEAK mailing list