[Zope-CVS] CVS: Products/AdaptableStorage/serial - .cvsignore:1.1 DeserializationEvent.py:1.1 DomainMapper.py:1.1 EventBase.py:1.1 ObjectGateway.py:1.1 ObjectMapper.py:1.1 ObjectSerializer.py:1.1 RecordSchema.py:1.1 SerializationEvent.py:1.1 __init__.py:1.1 exceptions.py:1.1 public.py:1.1

Shane Hathaway shane@zope.com
Wed, 27 Nov 2002 13:37:08 -0500


Update of /cvs-repository/Products/AdaptableStorage/serial
In directory cvs.zope.org:/tmp/cvs-serv12157/serial

Added Files:
	.cvsignore DeserializationEvent.py DomainMapper.py 
	EventBase.py ObjectGateway.py ObjectMapper.py 
	ObjectSerializer.py RecordSchema.py SerializationEvent.py 
	__init__.py exceptions.py public.py 
Log Message:
Moved the latest AdaptableStorage work out of the private repository.
It took a long time, but I moved it as soon as all the unit tests
passed and I felt that all the interface names and conventions were
good enough.

Documentation is still minimal, but now I think the system is finally
straight enough in my head to write down. :-) If you want a sneak
peek, the interfaces have some docstrings, if you're looking for a
"tree" view, while the OpenOffice diagram presents something of a
"forest" view.

Also note that I'm trying a new coding convention.  The "public"
module in each package defines exactly which objects should be
exported from the package.  This solves a few problems with imports
such as doubling of names and shadowing of modules.  Overall, the
"public" module makes it easier to tell which classes are supposed to
be used by other packages, and makes it easier for other packages to
use the public classes.  See what you think.



=== Added File Products/AdaptableStorage/serial/.cvsignore ===
*.pyc


=== Added File Products/AdaptableStorage/serial/DeserializationEvent.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.
#
##############################################################################
"""Standard deserialization event class

$Id: DeserializationEvent.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""


from interfaces.public import IFullDeserializationEvent

from EventBase import EventBase


class DeserializationEvent (EventBase):

    __implements__ = IFullDeserializationEvent

    def __init__(self, object_mapper, key, object, keyed_ob_sys):
        EventBase.__init__(self, object_mapper, key, object, keyed_ob_sys)
        self._loaded_refs = {}  # { (aspect_name, name) -> object }

    # IDeserializationEvent interface methods:

    def notifyDeserialized(self, name, value):
        """See the IDeserializationEvent interface."""
        self._loaded_refs[(self._aspect_name, name)] = value

    def dereference(self, name, mapper_name=None, key=None,
                    classification=None):
        """Retrieves a referenced subobject (usually ghosted initially).
        """
        dm = self.getObjectMapper().getDomainMapper()
        if mapper_name is None:
            classifier = self.getObjectMapper().getClassifier()
            mapper_name = classifier.chooseMapper(classification)
            assert mapper_name is not None
        if key is None:
            sub_mapper = dm.getMapper(mapper_name)
            key = sub_mapper.getGateway().makeKey(self, name, 0)
            assert key is not None
        mapper = dm.getMapper(mapper_name)
        class_info = mapper.getSerializer().getClassInfo()
        kos = self.getKeyedObjectSystem()
        ob = kos.loadStub(mapper_name, key, class_info)
        self.notifyDeserialized(name, ob)
        return ob


    # IFullDeserializationEvent interface methods:

    def loadInternalReference(self, ref):
        """Returns an object for a reference of the form (aspect_name, name).
        """
        return self._loaded_refs[ref]



=== Added File Products/AdaptableStorage/serial/DomainMapper.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.
#
##############################################################################
"""Default domain mapper.

$Id: DomainMapper.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import IDomainMapper


class DomainMapper:
    """DomainMapper that just uses ObjectMappers.
    """

    __implements__ = IDomainMapper

    def __init__(self, classifier):
        self._mappers = {}
        self._classifier = classifier

    def addMapper(self, name, m):
        self._mappers[name] = m

    def getMapper(self, name):
        return self._mappers[name]

    def getClassifier(self):
        return self._classifier



=== Added File Products/AdaptableStorage/serial/EventBase.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.
#
##############################################################################
"""(de)serialization event base.

$Id: EventBase.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import IEventBase

class EventBase:

    __implements__ = IEventBase

    _aspect_name = ''

    def __init__(self, object_mapper, key, object, keyed_ob_sys):
        self._object_mapper = object_mapper
        self._key = key
        self._object = object
        self._keyed_ob_sys = keyed_ob_sys

    def setAspectName(self, name):
        """Sets the name of the aspect being (de)serialized."""
        self._aspect_name = name

    def getAspectName(self):
        """Returns the name of the aspect being (de)serialized."""
        return self._aspect_name

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

    def getKey(self):
        """Returns the key of the object being serialized/deserialized."""
        return self._key

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

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



=== Added File Products/AdaptableStorage/serial/ObjectGateway.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.
#
##############################################################################
"""Basic object gateway based on smaller gateways.

$Id: ObjectGateway.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""


from interfaces.public import IObjectGateway


class ObjectGateway:

    __implements__ = IObjectGateway

    def __init__(self):
        self._gws = {}

    def addGateway(self, name, gw):
        self._gws[name] = gw


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

        See serial.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, object_mapper, key):
        """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(object_mapper, key)
            if state is not None:
                assert serial is not None
                full_state[name] = state
                serials[name] = serial
        serials = serials.items()
        serials.sort()
        return full_state, serials

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

        Returns a new serial.
        """
        serials = {}
        for name, gw in self._gws.items():
            state = full_state.get(name)
            serial = gw.store(object_mapper, key, state)
            assert serial is not None
            serials[name] = serial
        serials = serials.items()
        serials.sort()
        return serials

    def makeKey(self, event, name, stored):
        # By default, use simple path-based keys
        return event.getKey() + '/' + name



=== Added File Products/AdaptableStorage/serial/ObjectMapper.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.
#
##############################################################################
"""Default object mapper.

$Id: ObjectMapper.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import IObjectMapper


class ObjectMapper:

    __implements__ = IObjectMapper

    def __init__(self, serializer, gateway, domain_mapper, volatile=1):
        assert serializer.getSchema() == gateway.getSchema(), (
            serializer.getSchema(), gateway.getSchema())
        self._serializer = serializer
        self._gateway = gateway
        self._dm = domain_mapper
        self._volatile = volatile

    def getSerializer(self):
        return self._serializer

    def getGateway(self):
        return self._gateway

    def getDomainMapper(self):
        return self._dm

    def getClassifier(self):
        return self._dm.getClassifier()

    def isVolatile(self):
        return self._volatile



=== Added File Products/AdaptableStorage/serial/ObjectSerializer.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.
#
##############################################################################
"""Basic object serializer based on aspects.

$Id: ObjectSerializer.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import IObjectSerializer
from SerializationEvent import SerializationEvent
from DeserializationEvent import DeserializationEvent


class ObjectSerializer:

    __implements__ = IObjectSerializer

    def __init__(self, class_info):
        self._class_info = class_info
        self._aspects = []  # [(name, aspect)] -- Order matters.

    def addAspect(self, name, aspect):
        self._aspects.append((name, aspect))

    def getSchema(self):
        res = {}
        for name, aspect in self._aspects:
            s = aspect.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__
        info = ((c.__module__, c.__name__), None)
        return (info == self._class_info)

    def serialize(self, object_mapper, key, object, keyed_ob_sys):
        event = SerializationEvent(object_mapper, key, object, keyed_ob_sys)
        full_state = {}
        for name, aspect in self._aspects:
            event.setAspectName(name)
            state = aspect.serialize(object, event)
            if state is not None:
                full_state[name] = state
        return full_state, event.getExternalRefs()

    def deserialize(self, object_mapper, key, object, keyed_ob_sys,
                    full_state):
        event = DeserializationEvent(object_mapper, key, object, keyed_ob_sys)
        for name, aspect in self._aspects:
            state = full_state.get(name)
            event.setAspectName(name)
            aspect.deserialize(object, event, state)

    def getClassInfo(self, state=None):
        return self._class_info



=== Added File Products/AdaptableStorage/serial/RecordSchema.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.
#
##############################################################################
"""Record sequence schema definitions

$Id: RecordSchema.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import ISchema

# Notes:
#
# If there are no unique columns in a schema, the database store operation
# has to use a strategy where it deletes all the rows then re-creates them.


# ok_types just constrains the possible types until we figure out
# what we really want to do here.
ok_types = ('unicode', 'string', 'int', 'float', 'bool', 'object',
            'classification')


class Column:

    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, Column):
            if (other.name == self.name) and (other.type == self.type) and (
                other.unique == self.unique):
                return 1  # Same
        return 0  # Different


class SingleRecordSchema:
    """Defines an ordered set of columns for records.
    """
    __implements__ = ISchema

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

    def _add(self, c):
        if self.column_names.has_key(c.name):
            raise KeyError, 'Duplicate column name: %s' % c.name
        self.column_names[c.name] = 1
        self.columns.append(c)

    def addColumn(self, name, type='string', unique=0):
        self._add(Column(name, type, unique))

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


class RecordSchema (SingleRecordSchema):
    """In addition to columns, defines limits on the number of rows.
    """
    __implements__ = ISchema

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

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



=== Added File Products/AdaptableStorage/serial/SerializationEvent.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.
#
##############################################################################
"""Standard serialization event class

$Id: SerializationEvent.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from types import IntType

from interfaces.public import IFullSerializationEvent

from EventBase import EventBase


class SerializationEvent (EventBase):

    __implements__ = IFullSerializationEvent

    def __init__(self, object_mapper, key, object, keyed_ob_sys):
        EventBase.__init__(self, object_mapper, key, object, keyed_ob_sys)
        self._attrs = {}

        # _refs is the list of externally referenced objects.
        # It has the form [(mapper_name, key, value)]
        self._refs = []
        # _internal_refs:
        # id(ob) -> (aspect_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, attribute=0):
        """See the ISerializationEvent interface."""
        assert self._aspect_name is not None
        if (value is not None and value != () and not isinstance(
            value, IntType)):
            # Make internal references for complex objects only.
            idx = id(value)
            if not self._internal_refs.has_key(idx):
                self._internal_ref_list.append(value)
                self._internal_refs[idx] = (self._aspect_name, name)
        if attribute:
            self._attrs[name] = 1

    def classifyObject(self, value):
        """See the ISerializationEvent interface."""
        c = self.getObjectMapper().getClassifier()
        return c.classifyObject(value)

    def notifySerializedRef(self, name, value, attribute=0,
                            mapper_name=None, key=None, stored_key=0):
        assert mapper_name is not None
        if key is None:
            dm = self.getObjectMapper().getDomainMapper()
            sub_mapper = dm.getMapper(mapper_name)
            key = sub_mapper.getGateway().makeKey(self, name, stored_key)
            assert key is not None
        self._refs.append((mapper_name, key, value))
        self.notifySerialized(name, value, attribute)

    def ignoreAttribute(self, name):
        """See the ISerializationEvent interface."""
        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 getInternalReference(self, ob):
        """Returns (aspect_name, name) or None."""
        return self._internal_refs.get(id(ob))



=== Added File Products/AdaptableStorage/serial/__init__.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.
#
##############################################################################
"""General serialization and mapping framework.

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


=== Added File Products/AdaptableStorage/serial/exceptions.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.
#
##############################################################################
"""Serializer exception types.

$Id: exceptions.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

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

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



=== Added File Products/AdaptableStorage/serial/public.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.
#
##############################################################################
"""Public serialization interfaces and default implementations

$Id: public.py,v 1.1 2002/11/27 18:37:06 shane Exp $
"""

from interfaces.public import *
from exceptions import *
from DeserializationEvent import DeserializationEvent
from DomainMapper import DomainMapper
from EventBase import EventBase
from ObjectGateway import ObjectGateway
from ObjectMapper import ObjectMapper
from ObjectSerializer import ObjectSerializer
from RecordSchema import RecordSchema
from SerializationEvent import SerializationEvent