[Zope-CVS] CVS: Products/AdaptableStorage/mapper - AspectEvent.py:1.1 DeserializationEvent.py:1.1 FieldSchema.py:1.1 MapperEvent.py:1.1 ObjectGateway.py:1.1 ObjectMapper.py:1.1 ObjectSerializer.py:1.1 SerializationEvent.py:1.1 __init__.py:1.1 exceptions.py:1.1 public.py:1.1

Shane Hathaway shane@zope.com
Tue, 31 Dec 2002 16:47:47 -0500


Update of /cvs-repository/Products/AdaptableStorage/mapper
In directory cvs.zope.org:/tmp/cvs-serv18282/mapper

Added Files:
	AspectEvent.py DeserializationEvent.py FieldSchema.py 
	MapperEvent.py ObjectGateway.py ObjectMapper.py 
	ObjectSerializer.py SerializationEvent.py __init__.py 
	exceptions.py public.py 
Log Message:
Changed the name of the "serial" package to "mapper".  It's a more
appropriate name, since mappers are the focus of this software.

Sorry about the flood of checkins.


=== Added File Products/AdaptableStorage/mapper/AspectEvent.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.
#
##############################################################################
"""Aspect-related event base.

$Id: AspectEvent.py,v 1.1 2002/12/31 21:47:46 shane Exp $
"""

from interfaces.public import IAspectEvent
from MapperEvent import MapperEvent


class AspectEvent (MapperEvent):

    __implements__ = IAspectEvent

    _aspect_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 setAspectName(self, name):
        """Sets the name of the aspect being (de)serialized."""
        assert ':' not in name
        self._aspect_name = name

    def getAspectName(self):
        """Returns the name of the aspect being (de)serialized."""
        return self._aspect_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



=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""


from interfaces.public import IFullDeserializationEvent

from AspectEvent import AspectEvent


class DeserializationEvent (AspectEvent):

    __implements__ = IFullDeserializationEvent

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

    # IDeserializationEvent interface methods:

    def notifyDeserialized(self, name, value):
        """See the IDeserializationEvent interface."""
        assert self._aspect_name is not None
        self._loaded_refs['%s:%s' % (self._aspect_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 for a reference of the form (aspect_name, name).
        """
        return self._loaded_refs[ref]



=== Added File Products/AdaptableStorage/mapper/FieldSchema.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.
#
##############################################################################
"""Row-oriented schema definitions

$Id: FieldSchema.py,v 1.1 2002/12/31 21:47:46 shane Exp $
"""

from interfaces.public import ISchema

# 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', 'keychain')


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/AdaptableStorage/mapper/MapperEvent.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.
#
##############################################################################
"""Mapper event base.

$Id: MapperEvent.py,v 1.1 2002/12/31 21:47:46 shane Exp $
"""

from interfaces.public import IMapperEvent

class MapperEvent:

    __implements__ = IMapperEvent

    _aspect_name = ''

    def __init__(self, object_mapper, keychain):
        self._object_mapper = object_mapper
        self._keychain = keychain

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

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

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



=== Added File Products/AdaptableStorage/mapper/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 data gateway based on smaller gateways.

$Id: ObjectGateway.py,v 1.1 2002/12/31 21:47:46 shane Exp $
"""


from interfaces.public import IGateway


class ObjectGateway:

    __implements__ = IGateway

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



=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""

from interfaces.public \
     import IConfigurableObjectMapper, IObjectMapper, IObjectSerializer, \
     IGateway, IClassifier, IKeychainGenerator


class ObjectMapper:

    __implements__ = IConfigurableObjectMapper

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

    # IConfigurableObjectMapper 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 setVolatile(self, v):
        self._volatile = v

    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 = ObjectMapper(self)
        self._sub_mappers[name] = m
        return m

    def checkConfiguration(self, names=(), recursive=1):
        s = self._serializer
        if s is None:
            raise RuntimeError(
                'No serializer configured for mapper %s' % repr(names))
        if not IObjectSerializer.isImplementedBy(s):
            raise RuntimeError(
                'Not an IObjectSerializer: %s' % repr(s))
        g = self._gateway
        if g is None:
            raise RuntimeError(
                'No gateway configured for mapper %s' % repr(names))
        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(names), 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 IObjectMapper.isImplementedBy(self._parent):
                raise RuntimeError(
                    'Not an IObjectMapper: %s' % repr(self._parent))

        if recursive:
            for name, m in self._sub_mappers.items():
                m.checkConfiguration((names + (name,)), recursive)

    # IObjectMapper 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

    def isVolatile(self):
        if self._volatile is not None:
            return self._volatile
        if self._parent is not None:
            return self._parent.isVolatile()
        return None



=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""

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


class ObjectSerializer:

    __implements__ = IObjectSerializer

    def __init__(self, module, name):
        self._module = module
        self._name = name
        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__
        return (c.__module__ == self._module and c.__name__ == self._name)

    def serialize(self, object, event):
        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

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

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



=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""

from interfaces.public import IFullSerializationEvent

from AspectEvent import AspectEvent


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

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


class SerializationEvent (AspectEvent):

    __implements__ = IFullSerializationEvent

    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
        AspectEvent.__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) -> (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, is_attribute):
        """See the ISerializationEvent interface."""
        assert self._aspect_name is not None
        if value not in SIMPLE_IMMUTABLE_OBJECTS:
            # 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._aspect_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 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 (aspect_name, name) or None."""
        return self._internal_refs.get(id(ob))


=== Added File Products/AdaptableStorage/mapper/__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 object mapping framework.

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


=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""

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

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



=== Added File Products/AdaptableStorage/mapper/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/12/31 21:47:46 shane Exp $
"""

from interfaces.public import *
from exceptions import *

from DeserializationEvent import DeserializationEvent
from MapperEvent import MapperEvent
from ObjectGateway import ObjectGateway
from ObjectMapper import ObjectMapper
from ObjectSerializer import ObjectSerializer
from FieldSchema import FieldSchema, RowSchema, RowSequenceSchema
from SerializationEvent import SerializationEvent