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