[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