[Zope3-checkins] CVS: Zope3/src/zope/app/registration - __init__.py:1.1 configure.zcml:1.1 interfaces.py:1.1 registration.py:1.1

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Mar 13 13:01:19 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/registration
In directory cvs.zope.org:/tmp/cvs-serv27233/src/zope/app/registration

Added Files:
	__init__.py configure.zcml interfaces.py registration.py 
Log Message:


Moved registration code to zope.app.registration. Created module aliases, so
that old ZODBs work.


=== Added File Zope3/src/zope/app/registration/__init__.py ===
# Import this.


=== Added File Zope3/src/zope/app/registration/configure.zcml ===
<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:fssync="http://namespaces.zope.org/fssync"
    >

  <!-- For backward compatibility -->

  <modulealias
      module=".registration"
      alias="zope.app.services.registration"
      />

  <modulealias
      module=".interfaces"
      alias="zope.app.interfaces.services.registration"
      />

  <!-- Registration registries -->
  
  <content class=".registration.RegistrationStack">
    <require
        permission="zope.ManageServices"
        interface=".interfaces.IRegistrationStack"
        />
  </content>
  
  <adapter
    for=".interfaces.IRegisterable"
    provides=".interfaces.IRegistered"
    factory=".registration.Registered"
    />
  
  <!-- Registration Manager -->

  <content class=".registration.RegistrationManager">
    <factory
        id = "zope.app.services.RegistrationManager"
        title = "Registration Manager" />
    <require
        permission="zope.View"
        interface="zope.app.container.interfaces.IReadContainer" />
    <require
        permission="zope.ManageServices"
        interface="
        zope.app.container.interfaces.IWriteContainer
        .interfaces.IOrderedContainer
        zope.app.container.interfaces.IRemoveNotifiable
        zope.app.container.interfaces.INameChooser
        " 
        />
    <implements
        interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
        />
  </content>

  <!-- Filesystem synchronization support -->
  
  <fssync:adapter
      class=".registration.RegistrationManager"
      factory="zope.fssync.server.entryadapter.DirectoryAdapter"
      />

</configure>


=== Added File Zope3/src/zope/app/registration/interfaces.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 registration

$Id: interfaces.py,v 1.1 2004/03/13 18:01:16 srichter Exp $
"""
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.interfaces.annotation import IAnnotatable
from zope.app.interfaces.annotation import IAttributeAnnotatable
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.container.interfaces import IContained, IContainer
from zope.app.security.permission import PermissionField
from zope.interface import Interface, Attribute, implements
from zope.schema import TextLine, Field
from zope.schema.interfaces import ITextLine
from zope.app.container.constraints import ItemTypePrecondition
from zope.app.container.constraints import ContainerTypesConstraint

UnregisteredStatus = _('Unregistered')
RegisteredStatus = _('Registered')
ActiveStatus = _('Active')

class IRegistrationStatus(ITextLine):
    """The status of a registration
    """

class RegistrationStatus(TextLine):
    implements(IRegistrationStatus)

    def __init__(self, *args, **kw):
        super(RegistrationStatus, self).__init__(*args, **kw)
        self.allowed_values = (UnregisteredStatus,
                               RegisteredStatus,
                               ActiveStatus)

class INoLocalServiceError(Interface):
    """No local service to register with.
    """

class NoLocalServiceError(Exception):
    """No local service to configure

    An attempt was made to register a registration for which there is
    no local service.
    """

    implements(INoLocalServiceError)

class IRegistration(Interface):
    """Registration object

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

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

    serviceType = Attribute("service type that manages "
                            "this registration type")
    # A string; typically a class attribute

    status = RegistrationStatus(
        title=_("Registration status")
        )

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

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

    def usageSummary():
        """Single-line usage summary.

        This should include the registrayion keys and the kind of
        registration. For example, a service registration will have a
        usage summary that indicates a registration for a service of
        some type.  (e.g. "View Service")

        """

    def implementationSummary():
        """Single-line implementation summary.

        This summarizes about the implementation of the thing being
        registered. For example, for local-component registrations,
        this will include the component path. For a page registration,
        this might include a template path and a dotted class name.
        """


class IComponentPath(ITextLine):
    """A component path
    """
    # This is juse the interface for the ComponentPath field below.
    # We'll use this as the basis for looking up an appriate widget.

class ComponentPath(TextLine):
    """A component path

    Values of the field are absolute unicode path strings that can be
    traversed to get an object.
    """
    implements(IComponentPath)


class IComponentRegistration(IRegistration):
    """Registration object that uses a component path and a permission."""

    componentPath = ComponentPath(
        title=_("Component path"),
        description=_("The path to the component; this may be absolute, "
                      "or relative to the nearest site management folder"),
        required=True)

    permission = PermissionField(
        title=_("The permission needed to use the component"),
        required=False,
        )

    def getComponent():
        """Return the component named in the registration.
        """


class IRegistrationStack(Interface):
    """A stack of registrations for a set of parameters

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

    The registry stack works like a stack: the first element is
    active; when it is removed, the element after it is automatically
    activated.  An explicit None may be present (at most once) to
    signal that nothing is active.  To deactivate an element, it is
    moved to the end.
    """

    def register(registration):
        """Register the given registration without activating it.

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

    def unregister(registration):
        """Unregister the given registration.

        Do nothing if the registration is not registered.

        Implies deactivate() if the registration is active.
        """

    def registered(registration):
        """Is the registration registered?

        Return a boolean indicating whether the registration has been
        registered.
        """

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

        The activated() method is called on the registration.  If
        another registration was previously active, its deactivated()
        method is called first.

        If the argument is None, the currently active registration if
        any is disabled and no new registration is activated.

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

    def deactivate(registration):
        """Make the registration inactive.

        If the registration is active, the deactivated() method is
        called on the registration.  If this reveals a registration
        that was previously active, that registration's activated()
        method is called.

        Raises a ValueError if the given registration is not registered.

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

    def active():
        """Return the active registration, if any.

        Otherwise, returns None.
        """

    def info(keep_dummy=False):
        """Return a sequence of registration information.

        The sequence items are mapping objects with keys:

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

        active -- A boolean indicating whether the registration is
                  active.

        registration -- The registration object.

        If keep_dummy is true, an entry corresponding to the dummy
        entry's position is returned whose value is
        {id: '',
         active: (True iff it is the first entry),
         registration: None}.
        """

    def __nonzero__(self):
        """The registry is true iff it has no registrations."""


class IRegistry(Interface):
    """A component that can be configured using a registration manager."""

    def queryRegistrationsFor(registration, default=None):
        """Return an IRegistrationStack for the registration.

        Data on the registration is used to decide which registry to
        return. For example, a service manager will use the
        registration name attribute to decide which registry
        to return.

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

        The registry must be in the context of the registry.

        """

    def createRegistrationsFor(registration):
        """Create and return an IRegistrationStack for the registration.

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

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

        Calling createRegistrationsFor twice for the same registration
        returns the same registry.

        The registry must be in the context of the registry.

        """


class IOrderedContainer(Interface):
    """Containers whose items can be reorderd.

    XXX This is likely to go.
    """

    def moveTop(names):
        """Move the objects corresponding to the given names to the top.
        """

    def moveUp(names):
        """Move the objects corresponding to the given names up.
        """

    def moveBottom(names):
        """Move the objects corresponding to the given names to the bottom.
        """

    def moveDown(names):
        """Move the objects corresponding to the given names down.
        """

class IRegistrationManager(IContainerNamesContainer, IOrderedContainer):
    """Manage Registrations
    """

class IRegistrationManagerContainer(IContainer):
    """Containers with registration managers

    These are site-management folders of one sort or another.

    The container allows clients to access the registration manager
    without knowing it's name.

    XXX at this point, it doesn't really make sense for regsitration
    managers to be items.  It would probably be better to expose the
    registrations as a separate tab.

    The container prevents deletion of the last registration manager.

    The container may allow more than one registration manager. If it
    has more than one, the one returned from an unnamed access is
    undefined. XXX the container should allow one and only one.

    The registration manager container *also* supports local-module
    lookup.

    """

    def getRegistrationManager():
        """get a registration manager.

        Find a registration manager.  Clients can get the
        registration manager without knowing it's name. Normally,
        folders have one registration manager. If there is more than
        one, this method will return one; which one is undefined.

        An error is raised if no registration manager can be found.
        """

    def findModule(name):
        """Find the module of the given name.

        If the module can be find in the folder or a parent folder
        (within the site manager), then return it, otherwise, delegate
        to the module service.

        This must return None when the module is not found.

        """

    def resolve(name):
        """Resolve a dotted object name.

        A dotted object name is a dotted module name and an object
        name within the module.

        XXX We really should switch to using some other character than
        a dot for the delimiter between the module and the object
        name.

        """

    def __setitem__(name, object):
        """Add to object"""

class IRegisterable(IAnnotatable, IContained):
    """A marker interface."""
    
    __parent__ = Field(
        constraint = ContainerTypesConstraint(IRegistrationManagerContainer))

IRegistrationManagerContainer['__setitem__'].setTaggedValue(
    'precondition',
    ItemTypePrecondition(IRegisterable, IRegistrationManagerContainer))
    

class IRegistered(Interface):
    """An object that can keep track of its configured uses.

    The object need not implement this functionality itself, but must at
    least support doing so via an adapter.
    """

    def addUsage(location):
        """Add a usage by location.

        The location is the physical path to the registration object that
        configures the usage.
        """
    def removeUsage(location):
        """Remove a usage by location.

        The location is the physical path to the registration object that
        configures the usage.
        """
    def usages():
        """Return a sequence of locations.

        A location is a physical path to a registration object that
        configures a usage.
        """

    def registrations():
        """Return a sequence of registration objects for this object."""

class IAttributeRegisterable(IAttributeAnnotatable, IRegisterable):
    """A marker interface."""

class INoRegistrationManagerError(Interface):
    """No registration manager error
    """

class NoRegistrationManagerError(Exception):
    """No registration manager

    There is no registration manager in a site-management folder, or
    an operation would result in no registration manager in a
    site-management folder.

    """
    implements(INoRegistrationManagerError)



# XXX Pickle backward compatability
IUseConfigurable = IRegisterable
import sys
sys.modules['zope.app.interfaces.services.configuration'
            ] = sys.modules['zope.app.registration.interfaces']


=== Added File Zope3/src/zope/app/registration/registration.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

$Id: registration.py,v 1.1 2004/03/13 18:01:16 srichter Exp $
"""
from persistent import Persistent
from zope.app.container.contained import Contained
from zope.app.container.contained import setitem, contained, uncontained
from zope.app.container.interfaces import IAddNotifiable, IRemoveNotifiable
from zope.app import zapi
from zope.app.interfaces.annotation import IAttributeAnnotatable
from zope.app.interfaces.dependable import IDependable, DependencyError
from zope.app.module.interfaces import IModuleManager
from zope.exceptions import DuplicationError
from zope.fssync.server.entryadapter import ObjectEntryAdapter
from zope.fssync.server.interfaces import IObjectFile
from zope.interface import implements
from zope.proxy import removeAllProxies, getProxiedObject
from zope.security.checker import InterfaceChecker, CheckerPublic
from zope.security.proxy import Proxy, trustedRemoveSecurityProxy
from zope.xmlpickle import dumps, loads
import interfaces


class RegistrationStatusProperty(object):

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

        registration = inst
        service = self._get_service(registration)
        registry = service and service.queryRegistrationsFor(registration)

        if registry:

            if registry.active() == registration:
                return interfaces.ActiveStatus
            if registry.registered(registration):
                return interfaces.RegisteredStatus

        return interfaces.UnregisteredStatus

    def __set__(self, inst, value):
        registration = inst
        service = self._get_service(registration)
        registry = service and service.queryRegistrationsFor(registration)

        if value == interfaces.UnregisteredStatus:
            if registry:
                registry.unregister(registration)

        else:
            if not service:
                raise interfaces.NoLocalServiceError(
                    "This registration change cannot be performed because "
                    "there isn't a corresponding %s service defined in this "
                    "site. To proceed, first add a local %s service."
                    % (registration.serviceType, registration.serviceType))

            if registry is None:
                registry = service.createRegistrationsFor(registration)

            if value == interfaces.RegisteredStatus:
                if registry.active() == registration:
                    registry.deactivate(registration)
                else:
                    registry.register(registration)

            elif value == interfaces.ActiveStatus:
                if not registry.registered(registration):
                    registry.register(registration)
                registry.activate(registration)

    def _get_service(self, registration):
        # how we get the service is factored out so subclasses can
        # approach this differently
        sm = zapi.getServiceManager(registration)
        return sm.queryLocalService(registration.serviceType)


class RegistrationStack(Persistent, Contained):

    """Registration registry implementation.

    The invariants for _data are as follows:

        (1) The last element (if any) is not None

        (2) No value occurs more than once

        (3) Each value except None is a relative path from the nearest
            service manager to an object implementing IRegistration
    """

    implements(interfaces.IRegistrationStack)

    _data = ()  # tuple of strings (ivar)

    def __init__(self, container):
        self.__parent__ = container

    def _id(self, ob):
        """Turn ob into a path relative to the site management folder."""
        # Get and check relative path
        path = zapi.getPath(ob)
        prefix = "/++etc++site/"
        lpackages = path.rfind(prefix)
        if lpackages < 0:
            # XXX Backward compatability
            prefix = "/++etc++Services/"
            lpackages = path.rfind(prefix)

        if lpackages < 0:
            raise ValueError("Registration object is in an invalid location",
                             path)

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

        return rpath

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

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

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

        data = self._data
        if data:
            if data[0] == cid:
                data = data[1:]
                self._data = data

                # Tell it that it is no longer active
                registration.deactivated()

                if data and data[0] is not None:
                    # Activate the newly active component
                    sm = zapi.getServiceManager(self)
                    new = zapi.traverse(sm, data[0])
                    new.activated()
            else:
                # Remove it from our data
                data = tuple([item for item in data if item != cid])

                # Check for trailing None
                if data and data[-1] is None:
                    data = data[:-1]

                self._data = data

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

    def activate(self, registration):
        if registration is None:
            cid = None
        else:
            cid = self._id(registration)
        data = self._data

        if cid is None and not data:
            return # already in the state we want

        if cid is None or cid in data:
            old = data[0]
            if old == cid:
                return # already active

            # Insert it in front, removing it from back
            data = (cid, ) + tuple([item for item in data if item != cid])

            # Check for trailing None
            if data[-1] == None:
                data = data[:-1]

            # Write data back
            self._data = data

            if old is not None:
                # Deactivated the currently active component
                sm = zapi.getServiceManager(self)
                old = zapi.traverse(sm, old)
                old.deactivated()

            if registration is not None:
                # Tell it that it is now active
                registration.activated()

        else:
            raise ValueError(
                "Registration to be activated is not registered",
                registration)

    def deactivate(self, registration):
        cid = self._id(registration)
        data = self._data

        if cid not in data:
            raise ValueError(
                "Registration to be deactivated is not registered",
                registration)

        if data[0] != cid:
            return # already inactive

        if None not in data:
            # Append None
            data += (None,)

        # Move it to the end
        data = data[1:] + data[:1]

        # Write data back
        self._data = data

        # Tell it that it is no longer active
        registration.deactivated()

        if data[0] is not None:
            # Activate the newly active component
            sm = zapi.getServiceManager(self)
            new = zapi.traverse(sm, data[0])
            new.activated()

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

        return None

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

    def info(self, keep_dummy=False):
        sm = zapi.getServiceManager(self)

        data = self._data
        if None not in data:
            data += (None,)

        result = [{'id': path or "",
                   'active': False,
                   'registration': (path and zapi.traverse(sm, path))
                  }
                  for path in data
                 ]

        result[0]['active'] = True

        if not keep_dummy:
            # Throw away dummy:
            result = [x for x in result if x['id']]

        return result

class NotifyingRegistrationStack(RegistrationStack):

    def activate(self, registration):
        RegistrationStack.activate(self, registration)
        self.__parent__.notifyActivated(self, registration)

    def deactivate(self, registration):
        RegistrationStack.deactivate(self, registration)
        self.__parent__.notifyDeactivated(self, registration)

class SimpleRegistration(Persistent, Contained):
    """Registration objects that just contain registration data

    Classes that derive from this must make sure they implement
    IRemoveNotifiable either by implementing
    implementedBy(SimpleRegistration) or explicitly implementing
    IRemoveNotifiable.
    """

    implements(interfaces.IRegistration, IRemoveNotifiable,
               # We are including this here because we want all of the
               # subclasses to get it and we don't really need to be
               # flexible about the policy here. At least we don't
               # *think* we do. :)
               IAttributeAnnotatable,
               )

    status = RegistrationStatusProperty()

    # Methods from IRegistration

    def activated(self):
        pass

    def deactivated(self):
        pass

    def usageSummary(self):
        return self.__class__.__name__

    def implementationSummary(self):
        return ""

    # Methods from IRemoveNotifiable

    def removeNotify(self, event):
        "See IRemoveNotifiable"

        objectstatus = self.status

        if objectstatus == interfaces.ActiveStatus:
            try:
                objectpath = zapi.getPath(self)
            except: # XXX
                objectpath = str(self)
            raise DependencyError("Can't delete active registration (%s)"
                                  % objectpath)
        elif objectstatus == interfaces.RegisteredStatus:
            self.status = interfaces.UnregisteredStatus


class ComponentRegistration(SimpleRegistration):
    """Component registration.

    Subclasses should define a getInterface() method returning the interface
    of the component.
    """

    # SimpleRegistration implements IRemoveNotifiable, so we don't need
    # it below.
    implements(interfaces.IComponentRegistration, IAddNotifiable)

    def __init__(self, component_path, permission=None):
        self.componentPath = component_path
        if permission == 'zope.Public':
            permission = CheckerPublic
        self.permission = permission

    def implementationSummary(self):
        return self.componentPath

    def getComponent(self):
        service_manager = zapi.getServiceManager(self)

        # The user of the registration object may not have permission
        # to traverse to the component.  Yet they should be able to
        # get it by calling getComponent() on a registration object
        # for which they do have permission.  What they get will be
        # wrapped in a security proxy of course.  Hence:

        # 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 unproxied, 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.

        path = self.componentPath
        # Get the root and unproxy it
        if path.startswith("/"):
            # Absolute path
            root = removeAllProxies(zapi.getRoot(service_manager))
            component = zapi.traverse(root, path)
        else:
            # Relative path.
            ancestor = self.__parent__.__parent__
            component = zapi.traverse(ancestor, path)

        if self.permission:
            if type(component) is Proxy:
                # There should be at most one security Proxy around an object.
                # So, if we're going to add a new security proxy, we need to
                # remove any existing one.
                component = trustedRemoveSecurityProxy(component)

            interface = self.getInterface()

            checker = InterfaceChecker(interface, self.permission)

            component = Proxy(component, checker)

        return component

    def addNotify(self, event):
        "See IAddNotifiable"
        component = self.getComponent()
        dependents = IDependable(component)
        objectpath = zapi.getPath(self)
        dependents.addDependent(objectpath)
        # Also update usage, if supported
        adapter = interfaces.IRegistered(component, None)
        if adapter is not None:
            adapter.addUsage(objectpath)

    def removeNotify(self, event):
        "See IRemoveNotifiable"
        super(ComponentRegistration, self).removeNotify(event)
        component = self.getComponent()
        dependents = IDependable(component)
        objectpath = zapi.getPath(self)
        dependents.removeDependent(objectpath)
        # Also update usage, if supported
        adapter = interfaces.IRegistered(component, None)
        if adapter is not None:
            adapter.removeUsage(zapi.getPath(self))


from zope.app.dependable import PathSetAnnotation

class Registered(PathSetAnnotation):
    """An adapter from IRegisterable to IRegistered.

    This class is the only place that knows how 'Registered'
    data is represented.
    """

    implements(interfaces.IRegistered)

    # We want to use this key:
    #   key = "zope.app.registration.Registered"
    # But we have existing annotations with the following key, so we'll keep
    # it. :(
    key = "zope.app.services.configuration.UseConfiguration"

    addUsage = PathSetAnnotation.addPath
    removeUsage = PathSetAnnotation.removePath
    usages = PathSetAnnotation.getPaths

    def registrations(self):
        return [zapi.traverse(self.context, path)
                for path in self.getPaths()]


class RegistrationManager(Persistent, Contained):
    """Registration manager

    Manages registrations within a package.
    """

    implements(interfaces.IRegistrationManager, IRemoveNotifiable)

    def __init__(self):
        self._data = ()

    def __getitem__(self, key):
        "See IItemContainer"
        v = self.get(key)
        if v is None:
            raise KeyError, key
        return v

    def get(self, key, default=None):
        "See IReadMapping"
        for k, v in self._data:
            if k == key:
                return v
        return default

    def __contains__(self, key):
        "See IReadMapping"
        return self.get(key) is not None

    def keys(self):
        "See IEnumerableMapping"
        return [k for k, v in self._data]

    def __iter__(self):
        return iter(self.keys())

    def values(self):
        "See IEnumerableMapping"
        return [v for k, v in self._data]

    def items(self):
        "See IEnumerableMapping"
        return self._data

    def __len__(self):
        "See IEnumerableMapping"
        return len(self._data)

    def __setitem__(self, key, v):
        setitem(self, self.__setitem, key, v)

    def __setitem(self, key, v):
        if key in self:
            raise DuplicationError(key)
        self._data += ((key, v), )

    def addRegistration(self, object):
        "See IWriteContainer"
        key = self._chooseName('', object)
        self[key] = object
        return key

    def _chooseName(self, name, object):
        if not name:
            name = object.__class__.__name__

        i = 1
        n = name
        while n in self:
            i += 1
            n = name + str(i)

        return n

    def __delitem__(self, key):
        "See IWriteContainer"
        uncontained(self[key], self, key)
        self._data = tuple(
            [item
             for item in self._data
             if item[0] != key]
            )

    def moveTop(self, names):
        self._data = tuple(
            [item for item in self._data if (item[0] in names)]
            +
            [item for item in self._data if (item[0] not in names)]
            )

    def moveBottom(self, names):
        self._data = tuple(
            [item for item in self._data if (item[0] not in names)]
            +
            [item for item in self._data if (item[0] in names)]
            )

    def _moveUpOrDown(self, names, direction):
        # Move each named item by one position. Note that this
        # might require moving some unnamed objects by more than
        # one position.

        indexes = {}

        # Copy named items to positions one less than they currently have
        i = -1
        for item in self._data:
            i += 1
            if item[0] in names:
                j = max(i + direction, 0)
                while j in indexes:
                    j += 1

                indexes[j] = item

        # Fill in the rest where there's room.
        i = 0
        for item in self._data:
            if item[0] not in names:
                while i in indexes:
                    i += 1
                indexes[i] = item

        items = indexes.items()
        items.sort()

        self._data = tuple([item[1] for item in items])

    def moveUp(self, names):
        self._moveUpOrDown(names, -1)

    def moveDown(self, names):
        self._moveUpOrDown(names, 1)

    def removeNotify(self, event):
        assert event.object == self
        for name in self:
            del self[name]


class RegistrationManagerContainer(object):
    """Mix-in to implement IRegistrationManagerContainer
    """

    implements(interfaces.IRegistrationManagerContainer)

    def __init__(self):
        super(RegistrationManagerContainer, self).__init__()
        rm = RegistrationManager()
        rm.__parent__ = self
        rm.__name__ = 'RegistrationManager'
        self[rm.__name__] = rm

    def __delitem__(self, name):
        """Delete an item, but not if it's the last registration manager
        """
        item = self[name]
        if interfaces.IRegistrationManager.providedBy(item):
            # Check to make sure it's not the last one
            if len([i for i in self.values()
                    if interfaces.IRegistrationManager.providedBy(i)
                    ]
                   ) < 2:
                raise interfaces.NoRegistrationManagerError(
                    "Can't delete the last registration manager")
        super(RegistrationManagerContainer, self).__delitem__(name)

    def getRegistrationManager(self):
        """Get a registration manager
        """
        # Get the registration manager for this folder
        for name in self:
            item = self[name]
            if interfaces.IRegistrationManager.providedBy(item):
                # We found one. Get it in context
                return item
        else:
            raise interfaces.NoRegistrationManagerError(
                "Couldn't find an registration manager")

    def findModule(self, name):
        # Used by the persistent modules import hook

        # Look for a .py file first:
        manager = self.get(name+'.py')
        if manager is not None:
            # found an item with that name, make sure it's a module(manager):
            if IModuleManager.providedBy(manager):
                return manager.getModule()

        # Look for the module in this folder:
        manager = self.get(name)
        if manager is not None:
            # found an item with that name, make sure it's a module(manager):
            if IModuleManager.providedBy(manager):
                return manager.getModule()


        # See if out container is a RegistrationManagerContainer:
        c = self.__parent__
        if interfaces.IRegistrationManagerContainer.providedBy(c):
            return c.findModule(name)

        # Use sys.modules in lieu of module service:
        module = sys.modules.get(name)
        if module is not None:
            return module

        raise ImportError(name)


    def resolve(self, name):
        l = name.rfind('.')
        mod = self.findModule(name[:l])
        return getattr(mod, name[l+1:])


class ComponentRegistrationAdapter(ObjectEntryAdapter):

    """Fssync adapter for ComponentRegistration objects and subclasses.

    This is fairly generic -- it should apply to most subclasses of
    ComponentRegistration.  But in order for it to work for a
    specific subclass (say, UtilityRegistration), you have to (a) add
    an entry to configure.zcml, like this:

        <fssync:adapter
            class=".utility.UtilityRegistration"
            factory=".registration.ComponentRegistrationAdapter"
            />

    and (b) add a function to factories.py, like this:

        def UtilityRegistration():
            from zope.app.services.utility import UtilityRegistration
            return UtilityRegistration("", None, "")

    The file representation of a registration object is an XML pickle
    for a modified version of the instance dict.  In this version of
    the instance dict, the __annotations__ attribute is omitted,
    because annotations are already stored on the filesystem in a
    different way (in @@Zope/Annotations/<file>).
    """

    implements(IObjectFile)

    def factory(self):
        """See IObjectEntry."""
        name = self.context.__class__.__name__
        return "zope.app.services.factories." + name

    def getBody(self):
        """See IObjectEntry."""
        obj = removeAllProxies(self.context)
        ivars = {}
        ivars.update(obj.__getstate__())
        aname = "__annotations__"
        if aname in ivars:
            del ivars[aname]
        return dumps(ivars)

    def setBody(self, body):
        """See IObjectEntry."""
        obj = removeAllProxies(self.context)
        ivars = loads(body)
        obj.__setstate__(ivars)



# XXX Pickle backward compatability
ConfigurationRegistry = RegistrationStack
ConfigurationManager = RegistrationManager
import sys
sys.modules['zope.app.services.registrationmanager'
            ] = sys.modules['zope.app.registration']
sys.modules['zope.app.services.configuration'
            ] = sys.modules['zope.app.registration']




More information about the Zope3-Checkins mailing list