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
Powered by ViewCVS 1.0-dev