View of /PEAK/src/peak/config/interfaces.py
Parent Directory
| Revision Log
Revision:
555 -
(
download)
(
as text)
Fri Oct 11 00:53:50 2002 UTC (21 years, 5 months ago) by
pje
File size: 5351 byte(s)
Properties and utilities are unified at last! Both properties and
utilities are now handled by the __instance_provides__ and
__class_provides__ attributes of component classes. This simplified the
code and interfaces for both PropertyMap and for registering dynamic
utility providers.
Property names now have an additional syntax form: 'some.prop?' which is
used as a default value that is checked after 'some.prop', 'some.*', and
'some.prop' (a second time). This is handled transparently for you by
way of the 'setDefault()' method of 'IPropertyMap', so effectively it's an
implementation detail.
Documentation is still a little sketchy, but it should now be
straightforward to get the naming system using properties and utilities in
a harmonious way.
ALSO:
Fixed config.getLocal() to ensure it's working with a root component.
binding.New() can now use an import string instead of a
class/type/callable. The object referenced by the import string will be
loaded and called as though the object itself had been used. GlobalConfig
and LocalConfig now have 'setup()' methods that can be overridden to
create default rule and provider registrations. This'll be handy for
making LocalConfig register a default transaction service provider, for
example.
from __future__ import generators
from Interface import Interface
from Interface.Attribute import Attribute
from peak.api import exceptions
__all__ = [
'IRuleFactory', 'IRule', 'IPropertyMap', 'IConfigKey', 'Property',
]
class IRuleFactory(Interface):
"""Thing that instantiates a rule for a propertyMap and name"""
def __call__(propertyMap, propName):
"""Return an IRule instance suitable for the given IPropertyMap"""
class IRule(Interface):
"""Rule to compute a property value for a target object"""
def __call__(propertyMap, configKey, targetObject):
"""Retrieve 'configKey' for 'targetObject' or return 'NOT_FOUND'
The rule object is allowed to call any 'IPropertyMap' methods on the
'propertyMap' that is requesting computation of this rule. It is
also allowed to call 'config.getProperty()' relative to 'targetObject'
or 'propertyMap'.
What an IRule must *not* do, however, is return different results over
time for the same input parameters. If it cannot guarantee this
algorithmically, it must cache its results keyed by the parameters it
used, and not compute the results a second time.
"""
class IPropertyMap(Interface):
def setRule(propName, ruleFactory):
"""Add IRuleFactory's rule to rules for computing a property
Note that if the specified property (or any more-specific form)
has already been accessed, an 'AlreadyRead' exception results.
'propName' may be a "wildcard", of the form '"part.of.a.name.*"'
or '"*"' by itself in the degenerate case. Wildcard rules are
checked in most-specific-first order, after the non-wildcard name,
and before the property's default."""
def setDefault(propName, ruleObj):
"""Set 'IRule' 'defaultObj' as function to compute 'propName' default
Note that if a default for the specified property has already been
accessed, an 'AlreadyRead' exception results. Also, if a value has
already been set for the property, the default will be ignored. The
default will also be ignored if a rule exists for the same 'propName'
(or parent wildcard thereof), unless the rule returns 'NOT_FOUND' or
'NOT_GIVEN'. Note: like values and unlike rules, defaults can *not*
be registered for a wildcard 'propName'."""
def setValue(propName, value):
"""Set property 'propName' to 'value'
No wildcards allowed. 'AlreadyRead' is raised if the property
has already been accessed for the target object."""
def getValueFor(propName, forObj=None):
"""Return value of property for 'forObj' or return 'NOT_FOUND'"""
def registerProvider(ifaces, ruleObj):
"""Register 'IRule' 'ruleObj' as a provider for 'ifaces'"""
class IConfigKey(Interface):
"""Marker interface for configuration data keys
This is effectively just the subset of 'Interface.IInterface' that's
needed by PropertyMap and EigenRegistry instances to use as interface-like
utility keys."""
def getBases():
"""Return a sequence of the base interfaces, or empty sequence
if object is a property name"""
def extends(other, strict=1):
"""Test whether the interface extends another interface
(Meaningless for property names)"""
from Interface.Implements import implements
implements(Interface, IConfigKey)
import re
validChars = re.compile( r"([-+*?._a-z0-9]+)", re.I ).match
class Property(str):
__implements__ = IConfigKey
def __new__(klass, *args):
self = super(Property,klass).__new__(klass,*args)
valid = validChars(self)
if valid.end()<len(self):
raise exceptions.InvalidName(
"Invalid characters in property name", self
)
parts = self.split('.')
if '' in parts or not parts:
raise exceptions.InvalidName(
"Empty part in property name", self
)
if '*' in self:
if '*' not in parts or parts.index('*') < (len(parts)-1):
raise exceptions.InvalidName(
"'*' must be last part of wildcard property name", self
)
if '?' in self:
if '?' in parts or self.index('?') < (len(self)-1):
raise exceptions.InvalidName(
"'?' must be at end of a non-empty part", self
)
return self
def isWildcard(self):
return self.endswith('*')
def isDefault(self):
return self.endswith('*')
def isPlain(self):
return self[-1:] not in '?*'
def matchPatterns(self):
if not self.isPlain():
raise exceptions.InvalidName(
"Can't match patterns against special property names", self
)
yield self
name = self
while '.' in name:
name = name[:name.rindex('.')]
yield name+'.*'
yield '*'
yield self
yield self+'?'
def getBases(self):
return ()
def extends(self, other, strict=1):
return not strict and self==other