[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services - Configuration.py:1.1.2.1 ConfigurationInterfaces.py:1.1.2.1 configure.zcml:1.9.6.2

Jim Fulton jim@zope.com
Sat, 30 Nov 2002 07:44:56 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services
In directory cvs.zope.org:/tmp/cvs-serv30047/lib/python/Zope/App/OFS/Services

Modified Files:
      Tag: Zope3-Bangalore-TTW-Branch
	configure.zcml 
Added Files:
      Tag: Zope3-Bangalore-TTW-Branch
	Configuration.py ConfigurationInterfaces.py 
Log Message:
Refactored the way TTW component registration is done.  There are now
separate registry objects that abstract the machinery for registering
multiple conflicting configurations and deciding which, if any are
active.  Also provided a new field and widget for the status
information.

Along the way, cleaned up and streamlined placeful testing
infrastructure a bit.

Now checking into branch. Will give file-by-file (or at least more
specific logs) when the changes are merged into the head.


=== Added File Zope3/lib/python/Zope/App/OFS/Services/Configuration.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Component registration support for services

This module provides constant definitions for the three registration states,
Unregistered, Registered, and Active.

$Id: Configuration.py,v 1.1.2.1 2002/11/30 12:44:26 jim Exp $
"""
__metaclass__ = type

from Persistence import Persistent
from ConfigurationInterfaces import IConfigurationRegistry
from Zope.Schema import Text
from Zope.ComponentArchitecture import getService, getServiceManager
from Zope.App.Traversing import getPhysicalPathString, traverse
from Zope.App.OFS.Services.ConfigurationInterfaces \
     import Unregistered, Registered, Active
from Zope.Proxy.ContextWrapper import ContextWrapper
from Zope.ContextWrapper import ContextMethod

class ConfigurationStatusProperty:

    __Zope_ContextWrapper_contextful_get__ = True
    __Zope_ContextWrapper_contextful_set__ = True

    def __init__(self, service):
        self.service = service

    def __get__(self, inst, klass):
        if inst is None:
            return self

        configuration = inst
        service = getService(configuration, self.service)
        registry = service.queryConfigurationsFor(configuration)

        if registry:

            if registry.active() == configuration:
                return Active
            if registry.registered(configuration):
                return Registered

        return Unregistered

    def __set__(self, inst, value):
        configuration = inst
        service = getService(configuration, self.service)
        registry = service.queryConfigurationsFor(configuration)

        if value == Unregistered:
            if registry:
                registry.unregister(configuration)

        else:

            if registry is None:
                registry = service.createConfigurationsFor(configuration)
            
            if value == Registered:
                if registry.active() == configuration:
                    registry.deactivate(configuration)
                else:
                    registry.register(configuration)

            elif value == Active:
                if not registry.registered(configuration):
                    registry.register(configuration)
                registry.activate(configuration)



class ConfigurationRegistry(Persistent):

    __implements__ = IConfigurationRegistry

    _data = ()

    def _id(self, ob):

        # Get and check relative path
        prefix = "/++etc++Services/Packages/"
        path = getPhysicalPathString(ob)
        lpackages = path.rfind(prefix)
        if lpackages < 0:
            raise ValueError("Configuration object is in an invalid location",
                             path)

        rpath = path[lpackages+len(prefix):]
        if not rpath or (".." in rpath.split("/")):
            raise ValueError("Configuration object is in an invalid location",
                             path)

        return rpath

    _id = ContextMethod(_id)

    def register(self, configuration):
        cid = self._id(configuration)

        if self._data:
            if cid in self._data:
                return # already registered
        else:
            # Nothing registered. Need to stick None in front so that nothing
            # is active.
            self._data = (None, )

        self._data += (cid, )

    register = ContextMethod(register)

    def unregister(self, configuration):
        cid = self._id(configuration)

        data = self._data
        if data:
            if data[0] == cid:
                # It's active, we need to switch in None
                data = (None, ) + data[1:]

                # we need to notify it that it's inactive.
                configuration.deactivated()
                
            else:
                data = tuple([item for item in data if item != cid])

        # Check for empty registry
        if len(data) == 1 and data[0] is None:
            data = ()
        
        self._data = data

    unregister = ContextMethod(unregister)

    def registered(self, configuration):
        cid = self._id(configuration)
        return cid in self._data

    registered = ContextMethod(registered)

    def activate(self, configuration):
        cid = self._id(configuration)
        data = self._data
        
        if cid in data:

            if data[0] == cid:
                return # already active
            
            if data[0] is None:
                # Remove leading None marker
                data = data[1:]
            else:
                # We need to deactivate the currently active component
                sm = getServiceManager(self)
                old = traverse(sm, 'Packages/'+data[0])
                old.deactivated()
            

            self._data = (cid, ) + tuple(
                [item for item in data if item != cid]
                )

            configuration.activated()

        else:
            raise ValueError(
                "Configuration to be activated is not regsistered",
                configuration)

    activate = ContextMethod(activate)

    def deactivate(self, configuration):
        cid = self._id(configuration)

        if cid in self._data:

            if self._data[0] != cid:
                return # already inactive

            # Just stick None on the front
            self._data = (None, ) + self._data

            configuration.deactivated()

        else:
            raise ValueError(
                "Configuration to be deactivated is not regsistered",
                configuration)

    deactivate = ContextMethod(deactivate)

    def active(self):
        if self._data:
            path = self._data[0]
            if path is not None:
                # Make sure we can traverse to it.
                sm = getServiceManager(self)
                configuration = traverse(sm, 'Packages/'+path)
                return configuration
                
        return None

    active = ContextMethod(active)

    def __nonzero__(self):
        return bool(self._data)

    def info(self):
        sm = getServiceManager(self)

        result = [{'id': path,
                   'active': False,
                   'configuration': (path and traverse(sm, 'Packages/'+path))
                   }
                  for path in self._data
                  ]

        if result:
            if result[0]['configuration'] is None:
                del result[0]
            else:
                result[0]['active'] = True
        
        return result

    info = ContextMethod(info)


=== Added File Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Interfaces for objects supporting configuration registration

$Id: ConfigurationInterfaces.py,v 1.1.2.1 2002/11/30 12:44:26 jim Exp $
"""

from Interface import Interface
from Zope.Schema import Text, TextLine
from Zope.Schema.IField import ITextLine

Unregistered = u'Unregistered'
Registered = u'Registered'
Active = u'Active'

class IConfigurationStatus(ITextLine):
    """The registration status of a configuration
    """

class ConfigurationStatus(TextLine):
    __implements__ = IConfigurationStatus
    allowed_values = Unregistered, Registered, Active

class IConfigurationSummary(Interface):
    """Configuration summary data
    """

    title = TextLine(title = u"Title",
                 description = u"Descriptive title",
                 required = True)

    status = ConfigurationStatus(title = u"Configuration status")

class IConfiguration(IConfigurationSummary):
    """Configuration object

    A configuration object represents a specific configuration
    decision, such as registering an adapter or defining a permission.

    In addition to the attributes or methods defined here,
    configuration objecvts will include additional attributes
    identifying how they should be used. For example, a service
    configuration will provide a service type. An adapter
    configuration will specify a used-for interface and a provided
    interface. 
    """

    description = Text(title = u"Description",
                       description = u"Detailed description",
                       )

    def activated():
        """Method called when a configuration is made active
        """

    def deactivated():
        """Method called when a configuration is made inactive
        """


class IConfigurationRegistry(Interface):
    """A registry of configurations for a set of parameters

    A service will have a regsitry containing configuration registries
    for specific parameters. For example, an adapter service will have
    a configuration registry for each given used-for and provided
    interface.

    """

    def register(configuration):
        """Register the given configuration

        Do nothing if the configuration is already registered.
        """

    def unregister(configuration):
        """Unregister the given configuration

        Do nothing if the configuration is not registered.
        """

    def registered(configuration):
        """Is the configuration registered

        Return a boolean indicating whether the configuration has been
        registered.

        """

    def activate(configuration):
        """Make the configuration active.

        The activated method is called on the configuration.

        Raises a ValueError if the given configuration is not registered.
        """

    def deactivate(configuration):
        """Make the configuration inactive.
        
        Id the configuration is active, the deactivated method is called
        on the configuration.

        Raises a ValueError if the given configuration is not registered.

        The call has no effect if the configuration is registered but
        not active.
        """

    def active():
        """Return the active configuration, if any

        Otherwise, returns None.
        """

    def info():
        """Return a sequence of configuration information

        The sequence items are mapping objects with keys:

        id -- A string that can be used to uniquely identify the
              configuration

        active -- A boolean indicating whether the configuration is
                  active

        configuration -- The configuration object.
        """

    def __nonzero__(self):
        """The registry is true if it is non-empty
        """

class IConfigurable(Interface):

    def queryConfigurationsFor(configuration):
        """Return an IConfigurationRegistry for the configuration

        Data on the configuration is used to decide which regsitry to
        return. For example, a service manager will use the
        configuration serviceType attribute to decide which regsitry
        to return.

        Typically, an object that implements this method will also
        implement a method named queryConfigurations, which takes
        arguments for each of the parameters needed to specify a set
        of configurations.
        
        """

    def createConfigurationsFor(configuration):
        """Create and return an IConfigurationRegistry for the configuration

        Data on the configuration is used to decide which regsitry to
        create. For example, a service manager will use the
        configuration serviceType attribute to decide which regsitry
        to create.

        Typically, an object that implements this method will also
        implement a method named createConfigurations, which takes
        arguments for each of the parameters needed to specify a set
        of configurations.

        """


=== Zope3/lib/python/Zope/App/OFS/Services/configure.zcml 1.9.6.1 => 1.9.6.2 ===
--- Zope3/lib/python/Zope/App/OFS/Services/configure.zcml:1.9.6.1	Wed Oct 16 11:52:24 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/configure.zcml	Sat Nov 30 07:44:26 2002
@@ -1,4 +1,13 @@
-<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
+<zopeConfigure
+   xmlns='http://namespaces.zope.org/zope'
+   xmlns:browser="http://namespaces.zope.org/browser"
+   >
+
+<content class=".Configuration.ConfigurationRegistry">
+   <require permission="Zope.ManageServices"
+            interface=".ConfigurationInterfaces.IConfigurationRegistry"
+            />
+</content>
 
 <include package=".ServiceManager" />
 <include package=".AuthenticationService" />
@@ -9,5 +18,6 @@
 <include package=".CachingService" />
 <include package=".AdapterService" />
 
+<include package=".Browser" />
 
 </zopeConfigure>