[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services - Configuration.py:1.3.2.1 ConfigurationInterfaces.py:1.5.2.1

Marius Gedminas mgedmin@codeworks.lt
Tue, 10 Dec 2002 14:16:30 -0500


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

Modified Files:
      Tag: named-component-configuration-branch
	Configuration.py ConfigurationInterfaces.py 
Log Message:
Refactoring of configuration views:
  - new interfaces INamedComponentConfiguration, INameConfigurable,
    implemented in NamedComponentConfiguration, NameConfigurable, simplify
    the case where configurations are identified by a name (service types,
    connections, caches, queries, etc)
  - common views for INamedComponentConfiguration, INameConfigurable
  - refactored ServiceManager and ConnectionService to take advantage of the
    new infrastructure
  - incidentally wrote several unit tests for configuration classes
  - removed caching from ComponentConnection.getComponent; this exposed a bug
    in LocalEventService tests



=== Zope3/lib/python/Zope/App/OFS/Services/Configuration.py 1.3 => 1.3.2.1 ===
--- Zope3/lib/python/Zope/App/OFS/Services/Configuration.py:1.3	Thu Dec  5 12:00:44 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/Configuration.py	Tue Dec 10 14:15:59 2002
@@ -2,14 +2,14 @@
 #
 # 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
 
@@ -22,20 +22,26 @@
 
 from Persistence import Persistent
 from ConfigurationInterfaces import IConfigurationRegistry, IConfiguration
+from ConfigurationInterfaces import IComponentConfiguration
+from ConfigurationInterfaces import INamedComponentConfiguration
+from ConfigurationInterfaces import INameConfigurable
 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.ComponentArchitecture import getAdapter
 from Zope.ContextWrapper import ContextMethod
+from Zope.Proxy.ContextWrapper import ContextWrapper
+from Zope.Proxy.ProxyIntrospection import removeAllProxies
+from Zope.Security.Proxy import Proxy
+from Zope.Security.Checker import InterfaceChecker
 from Zope.App.OFS.Container.IAddNotifiable import IAddNotifiable
 from Zope.App.OFS.Container.IDeleteNotifiable import IDeleteNotifiable
 from Zope.App.DependencyFramework.IDependable import IDependable
 from Zope.App.DependencyFramework.Exceptions import DependencyError
-from Zope.ComponentArchitecture import getServiceManager, getAdapter
-from Zope.Proxy.ProxyIntrospection import removeAllProxies
-from Zope.App.Traversing import getPhysicalRoot, traverse
+from Zope.App.Traversing import getPhysicalPathString, traverse
+from Zope.App.Traversing import getPhysicalRoot
+from Zope.App.OFS.Services.ConfigurationInterfaces \
+     import Unregistered, Registered, Active
+
 
 class ConfigurationStatusProperty:
 
@@ -75,7 +81,7 @@
 
             if registry is None:
                 registry = service.createConfigurationsFor(configuration)
-            
+
             if value == Registered:
                 if registry.active() == configuration:
                     registry.deactivate(configuration)
@@ -140,14 +146,14 @@
 
                 # 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)
@@ -161,12 +167,12 @@
     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:]
@@ -175,7 +181,7 @@
                 sm = getServiceManager(self)
                 old = traverse(sm, 'Packages/'+data[0])
                 old.deactivated()
-            
+
 
             self._data = (cid, ) + tuple(
                 [item for item in data if item != cid]
@@ -185,7 +191,7 @@
 
         else:
             raise ValueError(
-                "Configuration to be activated is not regsistered",
+                "Configuration to be activated is not registered",
                 configuration)
 
     activate = ContextMethod(activate)
@@ -205,7 +211,7 @@
 
         else:
             raise ValueError(
-                "Configuration to be deactivated is not regsistered",
+                "Configuration to be deactivated is not registered",
                 configuration)
 
     deactivate = ContextMethod(deactivate)
@@ -218,7 +224,7 @@
                 sm = getServiceManager(self)
                 configuration = traverse(sm, 'Packages/'+path)
                 return configuration
-                
+
         return None
 
     active = ContextMethod(active)
@@ -241,15 +247,17 @@
                 del result[0]
             else:
                 result[0]['active'] = True
-        
+
         return result
 
     info = ContextMethod(info)
 
+
+
 class SimpleConfiguration(Persistent):
-    """Configutaion objects that just contain configuration data
+    """Configuration objects that just contain configuration data
     """
-    
+
     __implements__ = IConfiguration, IDeleteNotifiable
 
     title = description = u''
@@ -259,73 +267,68 @@
 
     def deactivated(self):
         pass
-        
+
     def manage_beforeDelete(self, configuration, container):
         "See Zope.App.OFS.Container.IDeleteNotifiable"
-        
+
         objectstatus = configuration.status
-        
+
         if objectstatus == Active:
-            raise DependencyError("Can't delete active configurations")
+            try: objectpath = getPhysicalPathString(configuration)
+            except: objectpath = str(configuration)
+            raise DependencyError("Can't delete active configuration (%s)"
+                                  % objectpath)
         elif objectstatus == Registered:
             configuration.status = Unregistered
 
+
 class ComponentConfiguration(SimpleConfiguration):
     """Component configuration
+
+    Subclasses should define a getInterface() method returning the interface
+    of the component.
     """
 
-    __implements__ = SimpleConfiguration.__implements__, IAddNotifiable
+    __implements__ = (IComponentConfiguration,
+                      SimpleConfiguration.__implements__, IAddNotifiable)
 
     def __init__(self, component_path, permission=None):
         self.componentPath = component_path
         if permission == 'Zope.Public':
             permission = CheckerPublic
-            
-        self.permission = permission
 
-    ############################################################
-    # Implementation methods for interface
-    # Zope.App.OFS.Services.ServiceManager.IServiceConfiguration.
+        self.permission = permission
 
     def getComponent(self):
         service_manager = getServiceManager(self)
-        
-        service = getattr(self, '_v_service', None)
-        if service is None:
-            
-            # We have to be clever here. We need to do an honest to
-            # god unrestricted traveral, which means we have to
-            # traverse from an unproxied object. But, it's not enough
-            # for the service manager to be unproxies, because the
-            # path is an absolute path. When absolute paths are
-            # traversed, the traverser finds the physical root and
-            # traverses from there, so we need to make sure the
-            # physical root isn;t proxied.
-
-            # get the root and unproxy it.
-            root = removeAllProxies(getPhysicalRoot(service_manager))            
-            service = traverse(root, self.componentPath)
-
-            if self.permission:
-                if type(service) is Proxy:
-                    # XXX what is this?
-                    service = removeSecurityProxy(service)
-
-                interface = service_manager.getInterfaceFor(self.serviceType)
-
-                checker = InterfaceChecker(interface, self.permission)
 
-                service = Proxy(service, checker)
+        # We have to be clever here. We need to do an honest to
+        # god unrestricted traveral, which means we have to
+        # traverse from an unproxied object. But, it's not enough
+        # for the service manager to be unproxies, because the
+        # path is an absolute path. When absolute paths are
+        # traversed, the traverser finds the physical root and
+        # traverses from there, so we need to make sure the
+        # physical root isn't proxied.
+
+        # get the root and unproxy it.
+        root = removeAllProxies(getPhysicalRoot(service_manager))
+        component = traverse(root, self.componentPath)
+
+        if self.permission:
+            if type(component) is Proxy:
+                # XXX what is this?
+                component = removeSecurityProxy(component)
 
-            
-            self._v_service = service
+            interface = self.getInterface()
 
+            checker = InterfaceChecker(interface, self.permission)
 
-        return service
+            component = Proxy(component, checker)
 
-    getComponent = ContextMethod(getComponent)
+        return component
 
-    ############################################################
+    getComponent = ContextMethod(getComponent)
 
     def manage_afterAdd(self, configuration, container):
         "See Zope.App.OFS.Container.IAddNotifiable"
@@ -333,12 +336,64 @@
         dependents = getAdapter(component, IDependable)
         objectpath = getPhysicalPathString(configuration)
         dependents.addDependent(objectpath)
-        
+
     def manage_beforeDelete(self, configuration, container):
         "See Zope.App.OFS.Container.IDeleteNotifiable"
         super(ComponentConfiguration, self
-              ).manage_beforeDelete(configuration, container)        
+              ).manage_beforeDelete(configuration, container)
         component = configuration.getComponent()
         dependents = getAdapter(component, IDependable)
         objectpath = getPhysicalPathString(configuration)
         dependents.removeDependent(objectpath)
+
+
+class NamedComponentConfiguration(ComponentConfiguration):
+    """Named component configuration
+    """
+
+    __implements__ = (INamedComponentConfiguration,
+                      ComponentConfiguration.__implements__)
+
+    def __init__(self, name, *args, **kw):
+        self.name = name
+        super(NamedComponentConfiguration, self).__init__(*args, **kw)
+
+
+class NameConfigurable:
+    """Mixin for implementing INameConfigurable
+    """
+
+    __implements__ = INameConfigurable
+
+    def __init__(self):
+        self._bindings = {}
+
+    def queryConfigurationsFor(self, cfg, default=None):
+        return self.queryConfigurations(cfg.name, default)
+
+    queryConfigurationsFor = ContextMethod(queryConfigurationsFor)
+
+    def queryConfigurations(self, name, default=None):
+        registry = self._bindings.get(name, default)
+        return ContextWrapper(registry, self)
+
+    queryConfigurations = ContextMethod(queryConfigurations)
+
+    def createConfigurationsFor(self, cfg):
+        return self.createConfigurations(cfg.name)
+
+    createConfigurationsFor = ContextMethod(createConfigurationsFor)
+
+    def createConfigurations(self, name):
+        try:
+            registry = self._bindings[name]
+        except KeyError:
+            self._bindings[name] = registry = ConfigurationRegistry()
+            self._p_changed = 1
+        return ContextWrapper(registry, self)
+
+    createConfigurations = ContextMethod(createConfigurations)
+
+    def listConfigurationNames(self):
+        return self._bindings.keys()
+


=== Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py 1.5 => 1.5.2.1 ===
--- Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py:1.5	Mon Dec  9 10:14:03 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/ConfigurationInterfaces.py	Tue Dec 10 14:15:59 2002
@@ -2,14 +2,14 @@
 #
 # 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
 
@@ -54,7 +54,7 @@
     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. 
+    interface.
     """
 
     description = Text(title = u"Description",
@@ -70,6 +70,8 @@
         """
 
 class IComponentConfiguration(IConfiguration):
+    """Configuration object that configures a component
+    """
 
     componentPath = Attribute("The physical path to the component")
 
@@ -77,6 +79,17 @@
         """Return the component named in the configuration.
         """
 
+class INamedComponentConfiguration(IComponentConfiguration):
+    """Configuration object that configures a component associated with a name
+    """
+
+    name = Attribute("The name of the component")
+
+    label = TextLine(title=u"Label",
+                     description=u"Descriptive label of the configuration type"
+                                 u" (e.g. Service, Connection)")
+
+
 
 class IConfigurationRegistry(Interface):
     """A registry of configurations for a set of parameters
@@ -85,7 +98,6 @@
     for specific parameters. For example, an adapter service will have
     a configuration registry for each given used-for and provided
     interface.
-
     """
 
     def register(configuration):
@@ -118,7 +130,7 @@
 
     def deactivate(configuration):
         """Make the configuration inactive.
-        
+
         Id the configuration is active, the deactivated method is called
         on the configuration.
 
@@ -152,6 +164,7 @@
         """The registry is true if it is non-empty
         """
 
+
 class IConfigurable(Interface):
 
     def queryConfigurationsFor(configuration, default=None):
@@ -159,14 +172,13 @@
 
         Data on the configuration is used to decide which registry to
         return. For example, a service manager will use the
-        configuration serviceType attribute to decide which registry
+        configuration name attribute to decide which registry
         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):
@@ -174,11 +186,44 @@
 
         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
+        configuration name 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.
+
+        Calling createConfigurationsFor twice for the same configuration
+        returns the same registry.
+        """
+
+
+class INameConfigurable(IConfigurable):
+    # XXX docstring
+
+    def queryConfigurations(name, default=None):
+        """Return an IConfigurationRegistry for the configuration name
+
+        queryConfigurationsFor(cfg, default) is equivalent to
+        queryConfigurations(cfg.name, default)
+        """
+
+    def createConfigurationsFor(configuration):
+        """Create and return an IConfigurationRegistry for the configuration
+        name
+
+        createConfigurationsFor(cfg, default) is equivalent to
+        createConfigurations(cfg.name, default)
         """
+
+    def listConfigurationNames():
+        """Return a list of all registered configuration names
+        """
+
+    # XXX It might be useful to abstract out common parts from
+    # ServiceManager.getBoundService and ConnectionService.getConnection
+    # into a method declared in INameConfigurable.  That would also mean that
+    # INameConfigurable relies on configurations implementing
+    # INamedComponentConfiguration, while now it is sufficient for a
+    # configuration to have a name attribute.