from peak.api import exceptions |
from peak.api import exceptions |
|
|
__all__ = [ |
__all__ = [ |
'IRuleFactory', 'IRule', 'IDefault', 'IPropertyMap', 'IConfigKey', |
'IRuleFactory', 'IRule', 'IPropertyMap', 'IConfigKey', 'Property', |
'Property', |
|
] |
] |
|
|
|
|
|
|
|
|
|
|
|
|
class IRuleFactory(Interface): |
class IRuleFactory(Interface): |
|
|
|
"""Thing that instantiates a rule for a propertyMap and name""" |
|
|
def __call__(propertyMap, propName): |
def __call__(propertyMap, propName): |
"""Return an IRule instance suitable for the given IPropertyMap""" |
"""Return an IRule instance suitable for the given IPropertyMap""" |
|
|
|
|
"""Rule to compute a property value for a target object""" |
"""Rule to compute a property value for a target object""" |
|
|
def __call__(propName, targetObject): |
def __call__(propertyMap, configKey, targetObject): |
|
|
"""Compute property 'propName' for 'targetObject' or return 'NOT_FOUND' |
"""Retrieve 'configKey' for 'targetObject' or return 'NOT_FOUND' |
|
|
The rule object is allowed to call any 'IPropertyMap' methods on the |
The rule object is allowed to call any 'IPropertyMap' methods on the |
'propertyMap' that is requesting computation of this rule. It is |
'propertyMap' that is requesting computation of this rule. It is |
time for the same input parameters. If it cannot guarantee this |
time for the same input parameters. If it cannot guarantee this |
algorithmically, it must cache its results keyed by the parameters it |
algorithmically, it must cache its results keyed by the parameters it |
used, and not compute the results a second time. |
used, and not compute the results a second time. |
|
|
In the event a rule cannot speak to the presence or absence of a |
|
property value for a given name and target, it may return the |
|
'NOT_GIVEN' singleton. In this case, the 'propertyMap' will continue |
|
the search for a value, but will not consider the rule to have |
|
participated in the determination of the value. This is important |
|
for rules that load configuration values, rules, or defaults, but do |
|
not directly involve themselves in the computation of property values. |
|
Such rules should return 'NOT_GIVEN' for every call after the first |
|
one, in order to "bow out" of the property calculation process once |
|
they've done their job. They can also return 'NOT_GIVEN' on the first |
|
call, so long as all the data they load is either values or defaults. |
|
""" |
""" |
|
|
|
|
|
|
|
|
|
|
class IDefault(Interface): |
|
|
|
"""Rule to compute a default property value, irrespective of target""" |
|
|
|
def __call__(propertyMap, propName): |
|
"""Compute default for property 'propName' or return 'NOT_FOUND'""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setRule(propName, ruleFactory): |
def setRule(propName, ruleFactory): |
"""Add IRuleFactory's rule to rules for computing a property |
"""Add IRuleFactory's rule to rules for computing a property |
|
|
Note that if a rule or value for the specified property has already |
Note that if the specified property (or any more-specific form) |
been accessed for any target object, an 'AlreadyRead' exception |
has already been accessed, an 'AlreadyRead' exception results. |
results. Also, if a value has already been set for the property, |
|
the rule will be ignored. |
|
|
|
Note also that '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, defaultObj): |
'propName' may be a "wildcard", of the form '"part.of.a.name.*"' |
"""Set IDefault 'defaultObj' as function to compute 'propName' default |
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 |
Note that if a default for the specified property has already been |
accessed, an 'AlreadyRead' exception results. Also, if a value has |
accessed, an 'AlreadyRead' exception results. Also, if a value has |
'NOT_GIVEN'. Note: like values and unlike rules, defaults can *not* |
'NOT_GIVEN'. Note: like values and unlike rules, defaults can *not* |
be registered for a wildcard 'propName'.""" |
be registered for a wildcard 'propName'.""" |
|
|
def setPropertyFor(obj, propName, value): |
|
"""Set property 'propName' to 'value' for 'obj' |
def setValue(propName, value): |
|
"""Set property 'propName' to 'value' |
|
|
No wildcards allowed. 'AlreadyRead' is raised if the property |
No wildcards allowed. 'AlreadyRead' is raised if the property |
has already been accessed for the target object. If 'obj' is |
has already been accessed for the target object.""" |
outside the property map's scope or it only manages properties for |
|
its owner, an 'ObjectOutOfScope' exception will be raised. |
|
'obj' may be 'None', in which case it is assumed that the property |
|
should be set for all objects within the map's scope, unless they |
|
have properties more specifically set upon them.""" |
|
|
|
def getPropertyFor(obj, propName): |
def getValueFor(propName, forObj=None): |
"""Return value of property for 'obj' or return 'NOT_FOUND'""" |
"""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): |
class IConfigKey(Interface): |
|
|
"""This is a marker interface used to indicate that XXX""" |
"""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(): |
def getBases(): |
"""Return a sequence of the base interfaces, or empty sequence |
"""Return a sequence of the base interfaces, or empty sequence |
for a |
if object is a property name""" |
""" |
|
|
|
def extends(other, strict=1): |
def extends(other, strict=1): |
"""Test whether the interface extends another interface""" |
"""Test whether the interface extends another interface |
|
(Meaningless for property names)""" |
|
|
|
|
from Interface.Implements import implements |
from Interface.Implements import implements |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import re |
import re |
|
|
validChars = re.compile( r"([-+*._a-z0-9]+)", re.I ).match |
validChars = re.compile( r"([-+*?._a-z0-9]+)", re.I ).match |
|
|
class Property(str): |
class Property(str): |
|
|
"'*' must be last part of wildcard property name", self |
"'*' must be last part of wildcard property name", self |
) |
) |
|
|
return 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): |
def isWildcard(self): |
return self.endswith('*') |
return self.endswith('*') |
|
|
|
def isDefault(self): |
|
return self.endswith('*') |
|
|
|
def isPlain(self): |
|
return self[-1:] not in '?*' |
|
|
|
|
def matchPatterns(self): |
def matchPatterns(self): |
|
|
|
if not self.isPlain(): |
|
raise exceptions.InvalidName( |
|
"Can't match patterns against special property names", self |
|
) |
|
|
yield self |
yield self |
|
|
while '.' in self: |
name = self |
self = self[:self.rindex('.')] |
|
yield self+'.*' |
while '.' in name: |
|
name = name[:name.rindex('.')] |
|
yield name+'.*' |
|
|
yield '*' |
yield '*' |
|
yield self |
|
yield self+'?' |
|
|
|
|
def getBases(self): |
def getBases(self): |