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