[Subversion] / PEAK / src / peak / config / interfaces.py  

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 (18 years, 6 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.


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
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

ViewCVS and CVS Help