[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services - Configuration.py:1.2 ConfigurationInterfaces.py:1.2 IConfigureFor.py:1.2 RegistrationState.py:1.2 configure.zcml:1.15

Jim Fulton jim@zope.com
Sat, 30 Nov 2002 13:36:25 -0500


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

Modified Files:
	configure.zcml 
Added Files:
	Configuration.py ConfigurationInterfaces.py IConfigureFor.py 
	RegistrationState.py 
Log Message:
Added a framework for managing configuration registration. This should
make it much easier to implement configurable services.



=== Zope3/lib/python/Zope/App/OFS/Services/Configuration.py 1.1 => 1.2 ===
--- /dev/null	Sat Nov 30 13:36:25 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/Configuration.py	Sat Nov 30 13:35:55 2002
@@ -0,0 +1,240 @@
+##############################################################################
+#
+# 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$
+"""
+__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)


=== Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py 1.1 => 1.2 ===
--- /dev/null	Sat Nov 30 13:36:25 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py	Sat Nov 30 13:35:55 2002
@@ -0,0 +1,176 @@
+##############################################################################
+#
+# 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$
+"""
+
+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/IConfigureFor.py 1.1 => 1.2 ===
--- /dev/null	Sat Nov 30 13:36:25 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/IConfigureFor.py	Sat Nov 30 13:35:55 2002
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2001, 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.
+# 
+##############################################################################
+"""
+
+$Id$
+"""
+
+from Interface import Interface
+
+class IConfigureFor(Interface):
+    """Services that configure component for interfaces
+
+    This interface is used to find out if there is configuration for a given
+    interface.
+
+    Services that implements this interface must provide a view named "ConfigurationFor"
+    that displays the configuration for a given interface. For browser views the interface
+    will be given in a form variable named "forInterface".
+    """
+    def hasConfigurationFor(interface):
+        """Check for configuration information
+        
+        Return a Boolean indicating wether there is configuration information for
+        the given interface.
+        """
+    
+    


=== Zope3/lib/python/Zope/App/OFS/Services/RegistrationState.py 1.1 => 1.2 ===
--- /dev/null	Sat Nov 30 13:36:25 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/RegistrationState.py	Sat Nov 30 13:35:55 2002
@@ -0,0 +1,24 @@
+##############################################################################
+#
+# 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.
+# 
+##############################################################################
+"""Standard registration states
+
+This module provides constant definitions for the three registration states,
+Unregistered, Registered, and Active.
+
+$Id$
+"""
+
+Unregistered = 'Unregistered'
+Registered = 'Registered'
+Active = 'Active'


=== Zope3/lib/python/Zope/App/OFS/Services/configure.zcml 1.14 => 1.15 ===
--- Zope3/lib/python/Zope/App/OFS/Services/configure.zcml:1.14	Tue Oct 29 22:47:45 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/configure.zcml	Sat Nov 30 13:35:55 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=".ObjectHub" />
 <include package=".ErrorReportingService" />
+<include package=".Browser" />
 
 </zopeConfigure>