[Zope-CVS] CVS: Products/Ape/apelib/core - __init__.py:1.1 classifiers.py:1.1 events.py:1.1 exceptions.py:1.1 gateways.py:1.1 interfaces.py:1.1 keygen.py:1.1 mapper.py:1.1 schemas.py:1.1 serializers.py:1.1

Shane Hathaway shane@zope.com
Sat, 15 Mar 2003 19:37:41 -0500


Update of /cvs-repository/Products/Ape/apelib/core
In directory cvs.zope.org:/tmp/cvs-serv7402

Added Files:
	__init__.py classifiers.py events.py exceptions.py gateways.py 
	interfaces.py keygen.py mapper.py schemas.py serializers.py 
Log Message:
An overzealous heuristic caused 'cvs import' to ignore the core package.


=== Added File Products/Ape/apelib/core/__init__.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""General object mapping framework.

The names are influenced by Martin Fowler's O/R mapping patterns.
"""


=== Added File Products/Ape/apelib/core/classifiers.py ===
##############################################################################
#
# Copyright (c) 2003 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 classifiers.

$Id: classifiers.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from apelib.core.interfaces import IClassifier
from apelib.core.exceptions import SerializationError, DeserializationError



class FixedClassifier:
    """Classifies objects based purely on the keychain."""

    __implements__ = IClassifier

    def __init__(self):
        self._key_to_res = {}
        self._default_res = None

    def register(self, key, mapper_name):
        self._key_to_res[key] = ({}, mapper_name)

    def registerDefault(self, mapper_name):
        self._default_res = ({}, mapper_name)

    def getResult(self, k):
        res = self._key_to_res.get(k)
        if res is None:
            res = self._default_res
            if res is None:
                raise KeyError("Key %s is not known to fixed classifier %s" %
                               (repr(k), repr(self)))
        return res

    def classifyObject(self, value, keychain):
        return self.getResult(keychain[-1])

    def classifyState(self, event):
        return self.getResult(event.getKeychain()[-1])

    def store(self, event, classification):
        pass


class NullClassifier:
    """A null classifier refuses to classify anything."""

    __implements__ = IClassifier

    def classifyObject(self, value, keychain):
        raise SerializationError("Null classifier")

    def classifyState(self, event):
        raise DeserializationError("Null classifier")

    def store(self, event, classification):
        pass



=== Added File Products/Ape/apelib/core/events.py ===
##############################################################################
#
# Copyright (c) 2003 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 event implementations

$Id: events.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from interfaces \
     import IMapperEvent, ILoadEvent, IStoreEvent, ISDEvent, \
     IFullDeserializationEvent, IFullSerializationEvent


SIMPLE_IMMUTABLE_OBJECTS = (None, (), 0, 1, '', u'')

try:
    True
except NameError:
    pass
else:
    SIMPLE_IMMUTABLE_OBJECTS += (False, True)


class MapperEvent:

    __implements__ = IMapperEvent

    def __init__(self, mapper, keychain):
        self._mapper = mapper
        self._keychain = keychain

    def getMapper(self):
        """Returns the mapper for the object being (de)serialized."""
        return self._mapper

    def getKeychain(self):
        """Returns the keychain of the object being (de)serialized."""
        return self._keychain

    def getKey(self):
        """Returns the last element of the keychain."""
        return self._keychain[-1]

    def makeKeychain(self, name, stored):
        kcg = self.getMapper().getKeychainGenerator()
        return kcg.makeKeychain(self, name, stored)


class LoadEvent (MapperEvent):
    """Object loading event."""

    __implements__ = ILoadEvent

    hash_only = 0


class StoreEvent (MapperEvent):
    """Object storing event."""

    __implements__ = IStoreEvent


class SDEvent (MapperEvent):

    __implements__ = ISDEvent

    _serializer_name = ''

    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
        MapperEvent.__init__(self, object_mapper, keychain)
        self._keyed_ob_sys = keyed_ob_sys
        self._object = object
        self._unmanaged = []

    def getKeyedObjectSystem(self):
        """Returns the IKeyedObjectSystem that generated this event.

        It is needed for loading and identifying other objects.
        """
        return self._keyed_ob_sys

    def setSerializerName(self, name):
        """Sets the name of the next serializer."""
        assert ':' not in name
        self._serializer_name = name

    def getSerializerName(self):
        """Returns the name of the serializer in use."""
        return self._serializer_name

    def getObject(self):
        """Returns the object being serialized."""
        return self._object

    def addUnmanagedPersistentObjects(self, obs):
        """Notifies that there are unmanaged persistent objects in the object.
        """
        self._unmanaged.extend(obs)

    def getUnmanagedPersistentObjects(self):
        """Returns the list of unmanaged persistent objects."""
        return self._unmanaged


class DeserializationEvent (SDEvent):

    __implements__ = IFullDeserializationEvent

    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
        SDEvent.__init__(
            self, keyed_ob_sys, object_mapper, keychain, object)
        self._loaded_refs = {}  # { (serializer_name, name) -> object }

    # IDeserializationEvent interface methods:

    def notifyDeserialized(self, name, value):
        """See the IDeserializationEvent interface."""
        assert self._serializer_name is not None
        self._loaded_refs['%s:%s' % (self._serializer_name, name)] = value

    def dereference(self, name, keychain, hints=None):
        """Retrieves a referenced subobject (usually ghosted initially).
        """
        kos = self.getKeyedObjectSystem()
        ob = kos.loadStub(keychain, hints)
        self.notifyDeserialized(name, ob)
        return ob


    # IFullDeserializationEvent interface methods:

    def loadInternalRef(self, ref):
        """Returns an object already deserialized by another serializer.

        'ref' is a tuple containing (serializer_name, name).
        """
        return self._loaded_refs[ref]


class SerializationEvent (SDEvent):

    __implements__ = IFullSerializationEvent

    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
        SDEvent.__init__(
            self, keyed_ob_sys, object_mapper, keychain, object)
        self._attrs = {}

        # _refs is the list of externally referenced objects.
        # It has the form [(keychain, value)]
        self._refs = []
        # _internal_refs:
        # id(ob) -> (serializer_name, name)
        self._internal_refs = {}
        # _internal_ref_list contains all objects that may be referenced
        # internally.  This only ensures that id(ob) stays consistent.
        self._internal_ref_list = []


    # ISerializationEvent interface methods:

    def notifySerialized(self, name, value, is_attribute):
        """See the ISerializationEvent interface."""
        assert self._serializer_name is not None
        for ob in SIMPLE_IMMUTABLE_OBJECTS:
            # If value is a simple immutable object, don't make a
            # reference to it.  Compare by identity rather than
            # equality, otherwise rich comparison leads to surprises.
            if value is ob:
                break
        else:
            # Make internal references only for mutable or complex objects.
            idx = id(value)
            if not self._internal_refs.has_key(idx):
                self._internal_ref_list.append(value)
                if name is not None:
                    self._internal_refs[idx] = (
                        '%s:%s' % (self._serializer_name, name))
                else:
                    self._internal_refs[idx] = None
        if is_attribute and name is not None:
            self._attrs[name] = 1

    def identifyObject(self, value):
        kos = self.getKeyedObjectSystem()
        return kos.identifyObject(value)

    def notifySerializedRef(self, name, value, is_attribute, keychain):
        assert keychain is not None
        self._refs.append((keychain, value))
        self.notifySerialized(name, value, is_attribute)

    def ignoreAttribute(self, name):
        """See the ISerializationEvent interface."""
        self._attrs[name] = 1

    def ignoreAttributes(self, names):
        """See the ISerializationEvent interface."""
        for name in names:
            self._attrs[name] = 1

    def getExternalRefs(self):
        """Returns the list of external references"""
        return self._refs


    # IFullSerializationEvent interface methods:

    def getSerializedAttributeNames(self):
        """Returns the name of all attributes serialized."""
        return self._attrs.keys()

    def getInternalRef(self, ob):
        """Returns (serializer_name, name) or None."""
        return self._internal_refs.get(id(ob))




=== Added File Products/Ape/apelib/core/exceptions.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Serializer exception types.

$Id: exceptions.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

class MappingError(Exception):
    """Object mapping exception"""

class SerializationError(MappingError):
    """Error during serialization"""

class DeserializationError(MappingError):
    """Error during deserialization"""

class NoStateFoundError(MappingError):
    """No state is there to load"""



=== Added File Products/Ape/apelib/core/gateways.py ===
##############################################################################
#
# Copyright (c) 2003 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 gateway classes.

$Id: gateways.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

import time

from interfaces import IGateway
from exceptions import NoStateFoundError


class CompositeGateway:
    """Gateway that delegates to multiple smaller gateways."""

    __implements__ = IGateway

    def __init__(self, base=None):
        self._gws = {}
        if base is not None:
            self._gws.update(base._gws)

    def addGateway(self, name, gw, force=0):
        if not force and self._gws.has_key(name):
            raise KeyError, "Gateway name %s in use" % name
        self._gws[name] = gw

    def removeGateway(self, name):
        del self._gws[name]  # raise KeyError if not in use

    def hasGateway(self, name):
        return self._gws.has_key(name)

    def getSchema(self):
        """Returns the ISchema of data stored by this gateway.

        See interfaces.ISchema.
        """
        res = {}
        for name, gw in self._gws.items():
            s = gw.getSchema()
            if s is not None:
                res[name] = s
        return res

    def load(self, event):
        """Loads data.

        Returns a pair containing the data and an object
        that acts as a serial number or a hash of the data.
        The serial number is either a time stamp or some other object
        that can be consistently compared to detect conflicts.
        """
        full_state = {}
        serials = {}
        for name, gw in self._gws.items():
            state, serial = gw.load(event)
            if state is not None:
                full_state[name] = state
                if serial is not None:
                    serials[name] = serial
        serials = serials.items()
        serials.sort()
        return full_state, tuple(serials)

    def store(self, event, full_state):
        """Stores data.

        Returns a new serial.
        """
        serials = {}
        for name, gw in self._gws.items():
            state = full_state.get(name)
            # print 'gateway storing', keychain, name, state
            serial = gw.store(event, state)
            if serial is not None:
                serials[name] = serial
        serials = serials.items()
        serials.sort()
        return tuple(serials)


class MappingGateway:
    """Gateway to a simple dictionary (primarily for testing).
    """

    __implements__ = IGateway

    def __init__(self, schema):
        self.schema = schema
        self.data = {}

    def getSchema(self):
        return self.schema

    def load(self, event):
        # Returns (data, serial)
        keychain = event.getKeychain()
        try:
            return self.data[keychain]
        except KeyError:
            raise NoStateFoundError(keychain)

    def store(self, event, data):
        h = time.time()
        self.data[event.getKeychain()] = (data, h)
        return h



=== Added File Products/Ape/apelib/core/interfaces.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Public interfaces

$Id: interfaces.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from Interface import Interface, Attribute


class ISchema(Interface):
    """Data schema"""

    def __eq__(other):
        """Checks equality with another schema.

        This is the only method common to all schema objects.  The
        idea is that serializers and data gateways need only agree on
        schemas, and this method allows you to check compatibility.
        """


class IKeyedObjectSystem (Interface):
    """A collection of objects identifiable by keychain.

    In apelib.zodb3, the ZODB Connection object (the _p_jar) is
    an IKeyedObjectSystem.
    """

    def loadStub(keychain, hints=None):
        """Returns a class instance, possibly ghosted.

        hints, a mapping, may be provided as an optimization.
        Without it, implementations of this method may have to load a
        full object rather than a ghosted object.

        Some hints may be:

        classification
        mapper_name (the mapper name for the last item in the keychain)
        mapper_names (a list of all mapper names)
        """

    def identifyObject(object):
        """Returns the keychain of an object.

        Returns None if the object is not in the keyed object system.
        """

    def newKey():
        """Returns a new, unique key (which might be used in a keychain)."""



class IMapperEvent (Interface):
    """The base interface for events in this package."""

    def getMapper():
        """Returns the mapper for the object being (de)serialized."""

    def getKeychain():
        """Returns the keychain of the object being (de)serialized."""

    def getKey():
        """Returns the last element of the keychain.

        For most purposes, the key can be used as an OID.
        """

    def makeKeychain(name, stored):
        """Generates a keychain for a subobject.
        """


class ILoadEvent (IMapperEvent):
    """Interface for events involved in loading objects."""

    hash_only = Attribute(
        'hash_only', """Set when only the hash is needed.

        Sometimes the system only needs the hash value for an object
        and not the full state.  When this attribute is set, the
        gateway's load() method can choose to return None as the
        state.
        """)


class IStoreEvent (IMapperEvent):
    """Interface for events involved in storing objects."""


class ISDEvent (IMapperEvent):
    """Base for serialization and deserialization events."""

    def getKeyedObjectSystem():
        """Returns the IKeyedObjectSystem that generated the event."""

    def getObject():
        """Returns the object being (de)serialized."""

    def setSerializerName(name):
        """Sets the name of the next serializer to be used."""

    def getSerializerName():
        """Returns the name of the serializer in use."""

    def addUnmanagedPersistentObjects(obs):
        """Notifies that there are unmanaged persistent objects in the object.

        If no attention is paid to unmanaged persistent objects
        (UPOs), they will not notify ZODB when they are changed, and
        hence can be a challenge for the application programmer.  Use
        this method to tell ZODB about the UPOs so that ZODB will see
        changes made to them and save the corresponding managed
        persistent object.
        """

    def getUnmanagedPersistentObjects():
        """Returns the list of unmanaged persistent objects."""


class IDeserializationEvent(ISDEvent):
    """A helper in the object deserialization process.

    Implementations of ISerializer.deserialize() call
    methods of this interface to restore internal and external
    references.
    """

    def notifyDeserialized(name, value):
        """Indicates that a named internal subobject was deserialized.
        """

    def dereference(name, keychain, mapper_names=None):
        """Retrieves a referenced subobject (usually ghosted initially).
        """


class IFullDeserializationEvent(IDeserializationEvent):
    """Deserialization event with features for deserializing remainder data.
    """

    def loadInternalRef(ref):
        """Returns an object already deserialized by another serializer.

        'ref' is a tuple containing (serializer_name, name).
        """


class ISerializationEvent(ISDEvent):
    """A helper in the object serialization process.

    Implementations of ISerializer.serialize() call
    methods of this interface to create internal and external
    references.
    """

    def notifySerialized(name, value, is_attribute):
        """Indicates that a named internal subobject was serialized.

        This allows a 'remainder pickler' to refer to subobjects by name.
        Be careful to unwrap acquisition and/or context wrappers around
        subob before calling this method.
        """

    def identifyObject(value):
        """Returns the keychain of an existing object.

        Returns None if the object is not yet known.
        """

    def notifySerializedRef(name, value, is_attribute, keychain):
        """Indicates that a reference to an external subobject was serialized.

        TODO: write more here. ;-)
        """

    def ignoreAttribute(name):
        """Indicates that an attribute should be ignored when storing."""

    def ignoreAttributes(names):
        """Indicates that several attributes should be ignored."""

    def getExternalRefs():
        """Returns the list of external references.

        The returned list is of the form [(keychain, subobject)].
        """


class IFullSerializationEvent(ISerializationEvent):
    """Serialization event with features for ensuring complete serialization.

    Used for generating a 'remainder pickle'.
    """

    def getSerializedAttributeNames():
        """Returns the name of all attributes serialized."""

    def getInternalRef(ob):
        """Returns (serializer_name, name) or None."""


class ISerializer(Interface):
    """Object serializer / deserializer"""

    def getSchema():
        """Returns the schema of records (de)serialized by this component.
        """

    def canSerialize(object):
        """Returns true if this serializer can serialize the given object.
        """

    def serialize(object, event):
        """Returns the state of this part of the object.

        Use the ISerializationEvent to set up internal and external
        references.
        """

    def deserialize(object, event, state):
        """Fills in the state of this part of the object.

        Use the IDeserializationEvent to resolve external references.
        Returns nothing.
        """


class IFullObjectSerializer(ISerializer):
    """Serializes/deserializes the complete state of objects.

    The serialized state does not need to include the class of the object,
    which is maintained separately.

    IFullObjectSerializers usually delegate to multiple ISerializers
    to do the actual work of (de)serialization.  The schema of
    IFullObjectSerializers is usually a dictionary containing the name
    and schema of its constituent ISerializers.
    """

    def createEmptyInstance(classification=None, class_factory=None):
        """Returns a new instance.

        If this serializer works with instances of only one class,
        createEmptyInstance() should not require the use of the
        classification argument.  Implementations that need the
        classification argument can return None when classification is
        None, but doing so may incur a performance penalty.

        If a class factory is provided, the serializer may use it
        instead of the standard Python import mechanism to find a
        class.  The class_factory will be called with a module and
        class name.
        """


class IGateway (Interface):
    """Loads and stores data by keychain.

    Implementations can store in entire tables, pieces of tables, translate
    for storage in joined tables, or store in some entirely different way.

    Based on _Patterns of Enterprise Application Architecture_
    by Martin Fowler.
    """

    def getSchema():
        """Returns the ISchema of data stored by this gateway.

        See serial.interfaces.ISchema.
        """

    def load(event):
        """Loads data.

        event is an ILoadEvent.

        Returns a pair containing the data and a hash of the data.
        The hash value is either an integer or an object that is
        hashable using the Python hash() function.  The hashable
        object is used to detect storage conflicts.

        If the hash_only attribute of the event is true, the system
        only needs the hash value and the load() method can return
        None as the state.
        """

    def store(event, data):
        """Stores data.

        event is an IStoreEvent.

        Returns a new hash value.
        """


class IClassifier(Interface):
    """Object classifier

    Implementations of this interface are a little like biologists.
    During serialization, the classifyObject() method returns a
    mapping containing the classification of subob (like a biologist
    identifying a creature's genus and species).  During
    deserialization, the classifyState() method decides what kind of
    objects to create for a stored state (like a biologist showing you
    a creature of a certain genus and species).

    The keys in classifications are implementation-dependent.
    """

    def classifyObject(value, keychain):
        """Returns a classification and mapper_name.
        """

    def classifyState(event):
        """Returns a classification and mapper_name.

        event is an ILoadEvent.

        May load the classification from storage.
        """

    def store(event, classification):
        """Stores the classification of an object.

        event is an IStoreEvent.
        """


class IKeychainGenerator (Interface):
    """A utility for generating keychains.

    Many serializers encode references to other objects, but it is
    desirable to not hard code the format of keychains into
    ISerializer implementations.  This utility, normally accessed
    through an IMapperEvent, allows keychain knowledge to be kept in
    one place.
    """

    def makeKeychain(event, name, stored):
        """Returns a new keychain.

        event is an IMapperEvent.

        name is the name of the subobject.

        stored is a flag indicating whether the returned keychain will
        be stored.  If it will not be stored, the generated keychain
        can not be a random label.
        """


class IMapper (Interface):
    """A hub for mapping a certain kind of object.
    """

    def getSerializer():
        """Returns the IObjectSerializer for this mapper."""

    def getGateway():
        """Returns the IGateway for this mapper."""

    def getClassifier():
        """Returns the IClassifier for objects referenced by this mapper.

        If this mapper references no other objects, it's safe to
        return None.
        """

    def getSubMapper(name):
        """Returns the named sub-IMapper.

        The name of a sub-mapper is chosen by either a classifier or
        a serializer.
        """

    def getKeychainGenerator():
        """Returns the IKeychainGenerator for subobjects.

        Some serializers and gateways make use of this.  If no serializer
        or gateway needs this, it's safe to return None.
        """


class IConfigurableMapper (IMapper):
    """Adds operations to IMapper for configuration.
    """

    def setSerializer(s):
        """Sets the IFullObjectSerializer for this mapper."""

    def setGateway(g):
        """Returns the IGateway for this mapper."""

    def setClassifier(c):
        """Sets the IClassifier for this mapper."""

    def setKeychainGenerator(k):
        """Sets the IKeychainGenerator for subobjects."""

    def addSubMapper(name, m=None, replace=0):
        """Adds a named sub-IMapper, returning the mapper added.

        If m is None, a default IConfigurableMapper
        implementation is created.  If replace is not set,
        the implemenation prevents overriding an existing mapper.
        """

    def checkConfiguration(path='root', recursive=1):
        """Verifies the mapper configuration is sane.

        Raises an exception if there are errors.

        'path' gives the path to the mapper, for debugging purposes.
        'recursive' can be turned off to not descend into sub-mappers.
        """


class ITPCConnection(Interface):

    def connect():
        """Opens any resources needed for transactions.  Called only once."""

    def sortKey():
        """Returns a sort key for consistent ordering."""

    def getName():
        """Returns a human-readable name."""

    def begin():
        """Called before the first phase of two-phase commit."""

    def vote():
        """Called upon transition to the second phase of two-phase commit."""

    def abort():
        """Aborts the transaction."""

    def finish():
        """Commits the transaction."""

    def close():
        """Closes resources.  Called only once."""




=== Added File Products/Ape/apelib/core/keygen.py ===
##############################################################################
#
# Copyright (c) 2003 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 keychain generators.

$Id: keygen.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from apelib.core.interfaces import IKeychainGenerator
from apelib.core.exceptions import MappingError


class NullKeychainGenerator:
    """A null keychain generator refuses to generate any keychains."""

    __implements__ = IKeychainGenerator

    def makeKeychain(self, event, name, stored):
        raise MappingError("Null keychain generator")


class PathKeychainGenerator:
    """Path-based keychain generator
    """
    __implements__ = IKeychainGenerator

    def makeKeychain(self, event, name, stored):
        if name is None:
            raise MappingError('Path keychains require a name')
        if '/' in name:
            raise ValueError, '%s is not a legal name' % name
        parent_keychain = event.getKeychain()
        k = parent_keychain[-1]
        if k.endswith('/'):
            k = k + name
        else:
            k = '%s/%s' % (k, name)
        return parent_keychain[:-1] + (k,)



=== Added File Products/Ape/apelib/core/mapper.py ===
##############################################################################
#
# Copyright (c) 2003 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 mapper class.

$Id: mapper.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from interfaces \
     import IConfigurableMapper, IMapper, IFullObjectSerializer, \
     IGateway, IClassifier, IKeychainGenerator


class Mapper:
    """Standard mapper class."""

    __implements__ = IConfigurableMapper

    def __init__(self,
                 parent=None,
                 serializer=None,
                 gateway=None,
                 classifier=None,
                 kgen=None):
        self._sub_mappers = {}
        self._parent = parent
        self._serializer = serializer
        self._gateway = gateway
        self._classifier = classifier
        self._kgen = kgen

    # IConfigurableMapper implementation

    def setSerializer(self, s):
        self._serializer = s

    def setGateway(self, g):
        self._gateway = g

    def setClassifier(self, c):
        self._classifier = c

    def setKeychainGenerator(self, k):
        self._kgen = k

    def addSubMapper(self, name, m=None, replace=0):
        if not replace and self._sub_mappers.has_key(name):
            raise KeyError('mapper name %s already in use' % name)
        if m is None:
            m = Mapper(self)
        self._sub_mappers[name] = m
        return m

    def checkConfiguration(self, path='root', recursive=1):
        s = self._serializer
        if s is None:
            raise RuntimeError(
                'No serializer configured for mapper %s' % repr(path))
        if not IFullObjectSerializer.isImplementedBy(s):
            raise RuntimeError(
                'Not an IFullObjectSerializer: %s' % repr(s))
        g = self._gateway
        if g is None:
            raise RuntimeError(
                'No gateway configured for mapper %s' % repr(path))
        if not IGateway.isImplementedBy(g):
            raise RuntimeError(
                'Not an IGateway: %s' % repr(g))
        if s.getSchema() != g.getSchema():
            raise RuntimeError('Mismatched schemas in mapper %s: %s != %s' % (
                repr(path), s.getSchema(), g.getSchema()))
        if self._parent is None:
            if self._classifier is None:
                raise RuntimeError('No root classifier configured')
            if not IClassifier.isImplementedBy(self._classifier):
                raise RuntimeError(
                    'Not an IClassifier: %s' % repr(self._classifier))
            if self._kgen is None:
                raise RuntimeError('No root keychain generator configured')
            if not IKeychainGenerator.isImplementedBy(self._kgen):
                raise RuntimeError(
                    'Not an IKeychainGenerator: %s' % repr(self._kgen))
        else:
            if not IMapper.isImplementedBy(self._parent):
                raise RuntimeError(
                    'Not an IMapper: %s' % repr(self._parent))

        if recursive:
            for n, m in self._sub_mappers.items():
                m.checkConfiguration(('%s/%s' % (path, n)), recursive)

    # IMapper implementation

    def getSerializer(self):
        return self._serializer

    def getGateway(self):
        return self._gateway

    def getSubMapper(self, name):
        return self._sub_mappers[name]

    def getClassifier(self):
        if self._classifier is not None:
            return self._classifier
        if self._parent is not None:
            return self._parent.getClassifier()
        return None

    def getKeychainGenerator(self):
        if self._kgen is not None:
            return self._kgen
        if self._parent is not None:
            return self._parent.getKeychainGenerator()
        return None



=== Added File Products/Ape/apelib/core/schemas.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Basic schema implementations.

$Id: schemas.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from types import StringType

from interfaces import ISchema

ok_types = ['unicode', 'string', 'int', 'float', 'bool', 'object',
            'classification', 'keychain', 'string:list']


def addFieldType(t):
    """Adds an allowable field type."""
    assert isinstance(t, StringType)
    if t not in ok_types:
        ok_types.append(t)


class FieldSchema:
    """Defines the schema of one field."""
    __implements__ = ISchema

    def __init__(self, name, type='string', unique=0):
        assert type in ok_types, type
        self.name = name
        self.type = type
        self.unique = not not unique

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            if (other.name == self.name) and (other.type == self.type) and (
                other.unique == self.unique):
                return 1  # Same
        return 0  # Different

    def __repr__(self):
        return 'FieldSchema(name=%s, type=%s, unique=%s)' % (
            repr(self.name), repr(self.type), repr(self.unique))


class RowSchema:
    """Defines an ordered set of fields for exactly one row.
    """
    __implements__ = ISchema

    def __init__(self, fields=()):
        self.fields = []
        self.field_names = {}
        for c in fields:
            self._add(c)

    def _add(self, c):
        if self.field_names.has_key(c.name):
            raise KeyError, 'Duplicate field name: %s' % c.name
        self.field_names[c.name] = 1
        self.fields.append(c)

    def addField(self, name, type='string', unique=0):
        self._add(FieldSchema(name, type, unique))

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            if (self.fields == other.fields):
                return 1  # Same
        return 0  # Different

    def __repr__(self):
        return 'RowSchema(%s)' % repr(self.fields)


class RowSequenceSchema (RowSchema):
    """Defines a schema for a sequence of rows, including row count limits.
    """
    __implements__ = ISchema

    def __init__(self, fields=(), min_rows=0, max_rows=0):
        # max_rows == 0 means unlimited.
        assert (max_rows == 0 or max_rows >= min_rows)
        RowSchema.__init__(self, fields)
        self.min_rows = min_rows
        self.max_rows = max_rows

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            if (self.fields == other.fields) and (
                self.min_rows == other.min_rows) and (
                self.max_rows == other.max_rows):
                return 1  # Same
        return 0  # Different

    def __repr__(self):
        return 'RowSequenceSchema(%s, min_rows=%s, max_rows=%s)' % (
            repr(self.fields), repr(self.min_rows), repr(self.max_rows))



=== Added File Products/Ape/apelib/core/serializers.py ===
##############################################################################
#
# Copyright (c) 2003 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 serializers.

$Id: serializers.py,v 1.1 2003/03/16 00:37:40 shane Exp $
"""

from types import StringType

from interfaces import ISerializer, IFullObjectSerializer
from exceptions import DeserializationError, SerializationError
from schemas import FieldSchema


class CompositeSerializer:
    """Full serializer based on partial serializers.
    """

    __implements__ = IFullObjectSerializer

    def __init__(self, module, name, base=None):
        self._module = module
        self._name = name
        self.init(base)

    def init(self, base=None):
        self._part_names = {}  # { name -> 1 }
        self._parts = []       # [(name, serializer)] -- Order matters.
        self._final_parts = [] # [(name, serializer)]
        if base is not None:
            self._part_names.update(base._part_names)
            self._parts[:] = base._parts
            self._final_parts[:] = base._final_parts

    def addSerializer(self, name, serializer, force=0, final=0):
        if self._part_names.has_key(name):
            if not force:
                raise KeyError, "Serializer name %s in use" % repr(name)
            self.removeSerializer(name)
        if final:
            self._final_parts.append((name, serializer))
        else:
            self._parts.append((name, serializer))
        self._part_names[name] = 1

    def removeSerializer(self, name):
        if not self._part_names.has_key(name):
            raise KeyError, "Serializer name %s not in use" % repr(name)
        for lst in (self._parts, self._final_parts):
            for i in range(len(lst)):
                if lst[i][0] == name:
                    del lst[i]
                    break
        del self._part_names[name]

    def hasSerializer(self, name):
        return self._part_names.has_key(name)

    def getSerializers(self):
        return self._parts + self._final_parts

    def getSchema(self):
        res = {}
        for name, serializer in self.getSerializers():
            s = serializer.getSchema()
            if s is not None:
                res[name] = s
        return res

    def canSerialize(self, object):
        if not hasattr(object, '__class__'):
            return 0
        c = object.__class__
        return (c.__module__ == self._module and c.__name__ == self._name)

    def serialize(self, object, event):
        full_state = {}
        for name, serializer in self.getSerializers():
            event.setSerializerName(name)
            state = serializer.serialize(object, event)
            if state is not None:
                full_state[name] = state
        return full_state

    def deserialize(self, object, event, full_state):
        for name, serializer in self.getSerializers():
            state = full_state.get(name)
            event.setSerializerName(name)
            serializer.deserialize(object, event, state)

    def createEmptyInstance(self, classification=None, class_factory=None):
        if class_factory is not None:
            c = class_factory(self._module, self._name)
        else:
            m = __import__(self._module, {}, {}, ('__doc__',))
            c = getattr(m, self._name)
        return c.__basicnew__()


class AnyObjectSerializer (CompositeSerializer):
    """Full serializer that's not tied to a specific class
    """

    __implements__ = IFullObjectSerializer

    def __init__(self, base=None):
        self.init(base)

    def canSerialize(self, object):
        return 1

    def createEmptyInstance(self, classification=None, class_factory=None):
        if classification is None:
            # This serializer can't do anything without the classification.
            return None
        cn = classification['class_name']
        module, name = cn.split(':', 1)
        if class_factory is not None:
            c = class_factory(module, name)
        else:
            m = __import__(module, {}, {}, ('__doc__',))
            c = getattr(m, name)
        return c.__basicnew__()


class FullState:
    """Serializer that reads/writes the entire state of an object."""

    __implements__ = ISerializer

    schema = FieldSchema('data', 'object')

    def getSchema(self):
        return self.schema

    def canSerialize(self, object):
        return 1

    def serialize(self, object, event):
        return object.__getstate__()

    def deserialize(self, object, event, state):
        object.__setstate__(state)



class IgnoredAttribute:
    """Serializer that explicitly ignores an attribute
    """
    __implements__ = ISerializer

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

    def getSchema(self):
        return None  # No storage

    def canSerialize(self, object):
        return 1

    def serialize(self, object, event):
        event.ignoreAttribute(self.attrname)
        return None

    def deserialize(self, object, event, state):
        assert state is None


class OptionalSerializer:
    """Serializer wrapper that serializes only if the object is compatible.
    """

    __implements__ = ISerializer

    def __init__(self, real, default_state=None):
        self._real = real
        self._default_state = default_state

    def getSchema(self):
        return self._real.getSchema()

    def canSerialize(self, object):
        return 1

    def serialize(self, object, event):
        if self._real.canSerialize(object):
            return self._real.serialize(object, event)
        else:
            return self._default_state

    def deserialize(self, object, event, state):
        if self._real.canSerialize(object):
            self._real.deserialize(object, event, state)
        else:
            if state is not None and state != self._default_state:
                raise DeserializationError(
                    "Optional serializer unable to install state %s into %s" %
                    (repr(state), repr(object)))


class StringDataAttribute:
    """Serializer of a simple string data attribute."""

    __implements__ = ISerializer

    schema = FieldSchema('data', 'string')

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

    def getSchema(self):
        return self.schema

    def canSerialize(self, object):
        return 1

    def serialize(self, object, event):
        attrname = self.attrname
        assert attrname
        v = getattr(object, attrname)
        assert isinstance(v, StringType)
        event.notifySerialized(attrname, v, 1)
        return v

    def deserialize(self, object, event, state):
        attrname = self.attrname
        assert attrname
        assert isinstance(state, StringType)
        setattr(object, attrname, state)
        event.notifyDeserialized(attrname, state)