[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