[Zope-CVS] CVS: Products/Ape/lib/apelib/core - oidgen.py:1.2 classifiers.py:1.2 events.py:1.7 gateways.py:1.8 interfaces.py:1.10 io.py:1.7 mapper.py:1.5 schemas.py:1.5 serializers.py:1.5 exceptions.py:NONE keygen.py:NONE

Shane Hathaway shane at zope.com
Mon Feb 2 10:07:50 EST 2004


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

Modified Files:
	classifiers.py events.py gateways.py interfaces.py io.py 
	mapper.py schemas.py serializers.py 
Added Files:
	oidgen.py 
Removed Files:
	exceptions.py keygen.py 
Log Message:
Moved ape-0_8-branch to the HEAD.

>From CHANGES.txt:

- Major restructuring to reduce the number of concepts in
  Ape. Keychains and keys have been replaced with simple string OIDs.
  There is now a flat namespace of mappers instead of a tree.  Only
  one classifier and one OID generator are used in any object
  database.

- The ZODB root object is now stored on the filesystem.


=== Products/Ape/lib/apelib/core/oidgen.py 1.1 => 1.2 ===
--- /dev/null	Mon Feb  2 10:07:50 2004
+++ Products/Ape/lib/apelib/core/oidgen.py	Mon Feb  2 10:07:18 2004
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# 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 oid generators.
+
+$Id$
+"""
+
+from apelib.core.interfaces import IOIDGenerator, MappingError
+
+
+class PathOIDGenerator:
+    """Path-based OID generator
+    """
+    __implements__ = IOIDGenerator
+
+    root_oid = "/_root"
+
+    def __init__(self, root_oid="/_root"):
+        self.root_oid = root_oid
+
+    def new_oid(self, event, name, stored):
+        if name is None:
+            raise MappingError('Path OIDs require a name')
+        if '/' in name:
+            raise MappingError('%s is not a legal name in a path' % repr(name))
+        p = str(event.oid)  # parent OID
+        if p.endswith('/'):
+            p += name
+        else:
+            p = '%s/%s' % (p, name)
+        return p
+
+
+class SerialOIDGenerator:
+    """Generates OIDs in series.
+    """
+    __implements__ = IOIDGenerator
+
+    root_oid = '0'
+    counter = 1
+
+    def __init__(self, root_oid="0"):
+        self.root_oid = root_oid
+
+    def new_oid(self, event, name, stored):
+        if not stored:
+            raise MappingError('Serial OIDs must be stored')
+        oid = str(self.counter)
+        self.counter += 1
+        return oid
+


=== Products/Ape/lib/apelib/core/classifiers.py 1.1 => 1.2 ===
--- Products/Ape/lib/apelib/core/classifiers.py:1.1	Wed Apr  9 23:09:55 2003
+++ Products/Ape/lib/apelib/core/classifiers.py	Mon Feb  2 10:07:18 2004
@@ -16,56 +16,39 @@
 $Id$
 """
 
-from apelib.core.interfaces import IClassifier
-from apelib.core.exceptions import SerializationError, DeserializationError
+from apelib.core.interfaces import IConfigurableClassifier, IClassifier
+from apelib.core.interfaces import ClassificationError, ConfigurationError
 
 
-
-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])
+class SimpleClassifier:
+    """Classifies objects based purely on the class of the object.
+    """
+
+    __implements__ = IConfigurableClassifier
+    gateway = None
+
+    def __init__(self, gw):
+        self._class_to_mapper = {}  # class name -> mapper_name
+        self.gateway = gw
+
+    def register(self, condition, value, mapper_name):
+        if condition == "class":
+            self._class_to_mapper[value] = mapper_name
+        else:
+            raise ConfigurationError("Unknown condition type: %s" % condition)
+
+    def setOption(self, mapper_name, option, value):
+        raise ConfigurationError("No options available")
+
+    def classifyObject(self, event):
+        c = event.obj.__class__
+        class_name = "%s.%s" % (c.__module__, c.__name__)
+        mapper_name = self._class_to_mapper[class_name]
+        return ({"class_name": class_name}, mapper_name)
 
     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
+        classification, serial = self.gateway.load(event)
+        class_name = classification["class_name"]
+        mapper_name = self._class_to_mapper[class_name]
+        return ({"class_name": class_name}, mapper_name)
 


=== Products/Ape/lib/apelib/core/events.py 1.6 => 1.7 ===
--- Products/Ape/lib/apelib/core/events.py:1.6	Sat Aug  9 11:16:32 2003
+++ Products/Ape/lib/apelib/core/events.py	Mon Feb  2 10:07:18 2004
@@ -19,166 +19,102 @@
 import interfaces
 
 
-SIMPLE_IMMUTABLE_OBJECTS = (None, (), 0, 1, '', u'')
-
-try:
-    True
-except NameError:
-    pass
-else:
-    SIMPLE_IMMUTABLE_OBJECTS += (False, True)
+SIMPLE_IMMUTABLE_OBJECTS = (None, (), 0, 1, '', u'', False, True)
 
 
 class DatabaseInitEvent:
-    """Database initialization event."""
-
+    """Database initialization event.
+    """
     __implements__ = interfaces.IDatabaseInitEvent
+    connections = None
+    clear_all = False
 
-    def __init__(self, connections, clearing):
-        self._connections = connections
-        self._clearing = clearing
-
-    def getConnection(self, name):
-        """Returns the named connection."""
-        return self._connections[name]
-
-    def clearing(self):
-        """Returns true if the database is to be cleared."""
-        return self._clearing
+    def __init__(self, connections, clear_all):
+        self.connections = connections
+        self.clear_all = clear_all
 
 
 class MapperEvent:
-
     __implements__ = interfaces.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)
+    conf = None
+    mapper = None
+    oid = ""
+
+    def __init__(self, conf, mapper, oid):
+        self.conf = conf
+        self.mapper = mapper
+        self.oid = oid
 
 
 class GatewayEvent (MapperEvent):
-
     __implements__ = interfaces.IGatewayEvent
+    connections = None
+    classification = None
 
-    def __init__(self, mapper, keychain, connections, classification):
-        MapperEvent.__init__(self, mapper, keychain)
-        self._connections = connections
-        self._classification = classification
-
-    def getConnection(self, name):
-        """Returns the named connection."""
-        return self._connections[name]
-
-    def getClassification(self):
-        """Returns the innermost classification or None."""
-        return self._classification
+    def __init__(self, conf, mapper, oid, connections, classification):
+        MapperEvent.__init__(self, conf, mapper, oid)
+        self.connections = connections
+        self.classification = classification
 
 
 class LoadEvent (GatewayEvent):
-    """Object loading event."""
-
+    """Object loading event.
+    """
     __implements__ = interfaces.ILoadEvent
 
 
 class StoreEvent (GatewayEvent):
-    """Object storing event."""
-
+    """Object storing event.
+    """
     __implements__ = interfaces.IStoreEvent
+    is_new = False
 
+    def __init__(self, conf, mapper, oid, connections, classification, is_new):
+        GatewayEvent.__init__(
+            self, conf, mapper, oid, connections, classification)
+        self.is_new = is_new
 
-class SDEvent (MapperEvent):
 
+class SDEvent (MapperEvent):
     __implements__ = interfaces.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 = []
-        # _refs is the list of externally referenced objects.
-        # It has the form [(keychain, value)]
-        self._refs = []
-
-    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
-
-    def getExternalRefs(self):
-        """Returns the list of external references"""
-        return self._refs
+    obj_db = None
+    obj = None
+    serializer_name = ""
+    upos = None
+    external = None
+
+    def __init__(self, conf, mapper, oid, obj_db, obj):
+        MapperEvent.__init__(self, conf, mapper, oid)
+        self.obj_db = obj_db
+        self.obj = obj
+        self.upos = []
+        # self.external has the form [(oid, subobject)]
+        self.external = []
 
 
 class DeserializationEvent (SDEvent):
-
     __implements__ = interfaces.IFullDeserializationEvent
 
-    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
-        SDEvent.__init__(
-            self, keyed_ob_sys, object_mapper, keychain, object)
+    def __init__(self, conf, mapper, oid, obj_db, obj):
+        SDEvent.__init__(self, conf, mapper, oid, obj_db, obj)
         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 deserialized(self, name, value):
+        self._loaded_refs['%s:%s' % (self.serializer_name, name)] = value
 
-    def dereference(self, name, keychain, hints=None):
+    def resolve(self, name, oid, hints=None):
         """Retrieves a referenced subobject (usually ghosted initially).
         """
-        kos = self.getKeyedObjectSystem()
-        ob = kos.getObject(keychain, hints)
-        self._refs.append((keychain, ob))
-        self.notifyDeserialized(name, ob)
+        ob = self.obj_db.getObject(oid, hints)
+        self.external.append((oid, ob))
+        self.deserialized(name, ob)
         return ob
 
     # IFullDeserializationEvent interface methods:
 
-    def loadInternalRef(self, ref):
+    def resolveInternal(self, ref):
         """Returns an object already deserialized by another serializer.
 
         'ref' is a tuple containing (serializer_name, name).
@@ -187,12 +123,10 @@
 
 
 class SerializationEvent (SDEvent):
-
     __implements__ = interfaces.IFullSerializationEvent
 
-    def __init__(self, keyed_ob_sys, object_mapper, keychain, object):
-        SDEvent.__init__(
-            self, keyed_ob_sys, object_mapper, keychain, object)
+    def __init__(self, conf, mapper, oid, obj_db, obj):
+        SDEvent.__init__(self, conf, mapper, oid, obj_db, obj)
         self._attrs = {}
         # _internal_refs:
         # id(ob) -> (serializer_name, name)
@@ -203,9 +137,8 @@
 
     # ISerializationEvent interface methods:
 
-    def notifySerialized(self, name, value, is_attribute):
+    def serialized(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
@@ -219,29 +152,23 @@
                 self._internal_ref_list.append(value)
                 if name is not None:
                     self._internal_refs[idx] = (
-                        '%s:%s' % (self._serializer_name, name))
+                        '%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 referenced(self, name, value, is_attribute, oid):
+        assert oid is not None
+        self.external.append((oid, value))
+        self.serialized(name, value, is_attribute)
+
+    def ignore(self, name_or_names):
+        if isinstance(name_or_names, (str, unicode)):
+            self._attrs[name_or_names] = 1
+        else:
+            for name in name_or_names:
+                self._attrs[name] = 1
 
 
     # IFullSerializationEvent interface methods:
@@ -250,8 +177,6 @@
         """Returns the name of all attributes serialized."""
         return self._attrs.keys()
 
-    def getInternalRef(self, ob):
+    def identifyInternal(self, ob):
         """Returns (serializer_name, name) or None."""
         return self._internal_refs.get(id(ob))
-
-


=== Products/Ape/lib/apelib/core/gateways.py 1.7 => 1.8 ===
--- Products/Ape/lib/apelib/core/gateways.py:1.7	Wed Jul 30 17:32:59 2003
+++ Products/Ape/lib/apelib/core/gateways.py	Mon Feb  2 10:07:18 2004
@@ -18,43 +18,41 @@
 
 import time
 
-from interfaces import IGateway
-import exceptions
+from interfaces import IGateway, OIDConflictError
 
 
 class CompositeGateway:
     """Gateway that delegates to multiple smaller gateways."""
 
     __implements__ = IGateway
+    schema = None
 
     def __init__(self, base=None):
         self._gws = {}
         if base is not None:
             self._gws.update(base._gws)
+        self._updateSchema()
 
-    def addGateway(self, name, gw, force=0):
+    def _updateSchema(self):
+        self.schema = {}
+        for name, gw in self._gws.items():
+            s = gw.schema
+            if s is not None:
+                self.schema[name] = s
+
+    def add(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
+        self._updateSchema()
 
-    def removeGateway(self, name):
+    def remove(self, name):
         del self._gws[name]  # raise KeyError if not in use
+        self._updateSchema()
 
-    def hasGateway(self, name):
+    def has(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.
 
@@ -83,7 +81,7 @@
         serials = {}
         for name, gw in self._gws.items():
             state = full_state.get(name)
-            # print 'gateway storing', keychain, name, state
+            # print 'gateway storing', event.oid, name, state
             serial = gw.store(event, state)
             if serial is not None:
                 serials[name] = serial
@@ -91,43 +89,38 @@
         serials.sort()
         return tuple(serials)
 
-    def getSources(self, event):
+    def getPollSources(self, event):
         """Returns data source information.  See IGateway.
         """
         res = {}
         for gw in self._gws.values():
-            sources = gw.getSources(event)
+            sources = gw.getPollSources(event)
             if sources is not None:
                 res.update(sources)
         return res
 
 
-class MappingGateway:
+class RAMGateway:
     """Gateway to a simple dictionary (primarily for testing).
     """
-
     __implements__ = IGateway
+    schema = None
 
     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 exceptions.NoStateFoundError(keychain)
+        return self.data[event.oid]
 
     def store(self, event, data):
+        if event.is_new and self.data.has_key(event.oid):
+            raise OIDConflictError(event.oid)
         h = time.time()
-        self.data[event.getKeychain()] = (data, h)
+        self.data[event.oid] = (data, h)
         return h
 
-    def getSources(self, event):
+    def getPollSources(self, event):
         return None
 


=== Products/Ape/lib/apelib/core/interfaces.py 1.9 => 1.10 ===
--- Products/Ape/lib/apelib/core/interfaces.py:1.9	Sat Aug  9 11:16:32 2003
+++ Products/Ape/lib/apelib/core/interfaces.py	Mon Feb  2 10:07:18 2004
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Public interfaces
+"""Public interfaces and exception classes.
 
 $Id$
 """
@@ -19,26 +19,29 @@
 from Interface import Interface, Attribute
 
 
-class ISchema(Interface):
-    """Data schema"""
+class MappingError(Exception):
+    """Object mapping exception"""
 
-    def __eq__(other):
-        """Checks equality with another schema.
+class SerializationError(MappingError):
+    """Error during serialization"""
 
-        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 DeserializationError(MappingError):
+    """Error during deserialization"""
 
+class StoreError(MappingError):
+    """Error while storing"""
 
-class IRelationalSchema(ISchema):
-    """Schema oriented for relational storage.
+class LoadError(MappingError):
+    """Error while loading"""
 
-    Designed to assist integration with relational databases."""
+class ClassificationError(MappingError):
+    """Error during classification"""
 
-    def getColumnDefs():
-        """Returns a sequence of (column_name, column_type, unique_flag)"""
+class ConfigurationError(Exception):
+    """Invalid mapper configuration"""
 
+class OIDConflictError(Exception):
+    """Attempt to write an object with an OID already in use"""
 
 
 class IClassFactory(Interface):
@@ -52,14 +55,14 @@
         """
 
 
-class IKeyedObjectSystem (IClassFactory):
-    """A collection of objects identifiable by keychain.
+class IObjectDatabase (IClassFactory):
+    """A collection of objects identifiable by OID.
 
     In apelib.zodb3, the ZODB Connection object (the _p_jar) is
-    an IKeyedObjectSystem.
+    an IObjectDatabase.
     """
 
-    def getObject(keychain, hints=None):
+    def getObject(oid, hints=None):
         """Returns a class instance, possibly ghosted.
 
         Used during deserialization (loading/import).
@@ -70,28 +73,24 @@
         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)
+        mapper_name
         """
 
-    def identifyObject(obj):
-        """Returns the keychain of an object.
+    def identify(obj):
+        """Returns the OID of an object.
 
         Used during serialization (storing/export).
-        Returns None if the object is not in the keyed object system.
+        Returns None if the object is not in the object database.
+        Raises TypeError if the object can not be stored directly
+        in the database.
         """
 
-    def newKey():
-        """Returns a new, unique key (which might be used in a keychain).
+    def new_oid():
+        """Returns a new OID.
 
         Used during serialization (storing/export).
         """
 
-    # Deprecated
-    def loadStub(keychain, hints=None):
-        """Deprecated alias for getObject()."""
-
-
 
 
 class IDatabaseInitializer (Interface):
@@ -107,50 +106,30 @@
 class IDatabaseInitEvent (Interface):
     """Interface for events involved in initializing databases."""
 
-    def getConnection(name):
-        """Returns the named connection."""
+    connections = Attribute(__doc__="A mapping of database connections")
 
-    def clearing():
-        """Returns true if the database is to be cleared.
+    clear_all = Attribute(__doc__="""True if the database is to be cleared.
 
-        This method is designed for testing purposes.
-        """
+    This attribute is designed for testing purposes.
+    """)
 
 
 class IMapperEvent (Interface):
     """The base interface for events occurring in context of a mapper."""
 
-    def getMapper():
-        """Returns the mapper for the object being (de)serialized."""
+    conf = Attribute(__doc__="The IMapperConfiguration")
 
-    def getKeychain():
-        """Returns the keychain of the object being (de)serialized."""
+    mapper = Attribute(__doc__="The IMapper")
 
-    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.
-        """
+    oid = Attribute(__doc__="The OID of the object being mapped")
 
 
 class IGatewayEvent (IMapperEvent):
     """Interface for events used by gateways."""
 
-    def getConnection(name):
-        """Returns the named connection."""
+    connections = Attribute(__doc__="A mapping of database connections")
 
-    def getClassification():
-        """Returns the innermost classification (for reading only) or None.
-
-        The innermost classification is the classification of the last
-        key in the keychain.  During traversal of the first element of
-        a keychain, no classification has been determined yet, so this
-        method returns None.
-        """
+    classification = Attribute(__doc__="The classification of the object.")
 
 
 class ILoadEvent (IGatewayEvent):
@@ -160,42 +139,36 @@
 class IStoreEvent (IGatewayEvent):
     """Interface for events involved in storing objects."""
 
+    is_new = Attribute(__doc__="""True if the object is new.
 
-class ISDEvent (IMapperEvent):
-    """Base for serialization and deserialization events."""
-
-    def getKeyedObjectSystem():
-        """Returns the IKeyedObjectSystem involved in the event."""
+    When this attribute is true, gateways should not overwrite
+    existing data but instead raise an OIDConflictError if something
+    is in the way.  When it is false, gateways should overwrite
+    existing data.
+    """)
 
-    def getObject():
-        """Returns the object being (de)serialized."""
 
-    def setSerializerName(name):
-        """Sets the name of the next serializer to be used."""
+class ISDEvent (IMapperEvent):
+    """Base for serialization and deserialization events."""
 
-    def getSerializerName():
-        """Returns the name of the serializer in use."""
+    obj_db = Attribute(__doc__="The relevant object database")
 
-    def addUnmanagedPersistentObjects(obs):
-        """Notifies that there are unmanaged persistent objects in the object.
+    obj = Attribute(__doc__="The object being (de)serialized.")
 
-        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.
-        """
+    serializer_name = Attribute(__doc__="The serializer in use.")
 
-    def getUnmanagedPersistentObjects():
-        """Returns the list of unmanaged persistent objects."""
+    upos = Attribute(__doc__="""The list of unmanaged persistent objects.
 
-    def getExternalRefs():
-        """Returns the list of external references.
+    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.  Add UPOs to this list
+    so that ZODB will see changes made to them and save the
+    corresponding managed persistent object.""")
+    
+    external = Attribute(__doc__="""The list of external oids.
 
-        The list is built up during (de)serialization.
-        The returned list is of the form [(keychain, subobject)].
-        """
+    The list is built up during (de)serialization.  It contains
+    [(oid, subobject)].""")
 
 
 class IDeserializationEvent(ISDEvent):
@@ -206,12 +179,22 @@
     references.
     """
 
-    def notifyDeserialized(name, value):
-        """Indicates that a named internal subobject was deserialized.
+    def deserialized(name, value):
+        """Indicates that a named subobject was deserialized.
+
+        The event records an intra-record reference.  Be careful to
+        unwrap non-persistent wrappers around the value before calling
+        this method.
         """
 
-    def dereference(name, keychain, mapper_names=None):
-        """Retrieves a referenced subobject (usually ghosted initially).
+    def resolve(name, oid, hints=None):
+        """Returns the object identified by an inter-record reference.
+
+        The object should have been stored earlier through a call to
+        ISerializationEvent.reference().  The return value is usually
+        ghosted initially.
+
+        The event also records an intra-record reference.
         """
 
 
@@ -219,8 +202,8 @@
     """Deserialization event with features for deserializing remainder data.
     """
 
-    def loadInternalRef(ref):
-        """Returns an object already deserialized by another serializer.
+    def resolveInternal(ref):
+        """Returns the object identified by an intra-record reference.
 
         'ref' is a tuple containing (serializer_name, name).
         """
@@ -234,31 +217,29 @@
     references.
     """
 
-    def notifySerialized(name, value, is_attribute):
-        """Indicates that a named internal subobject was serialized.
+    def serialized(name, value, is_attribute):
+        """Indicates that a named 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.
+        The event records an intra-record reference.  Be careful to
+        unwrap non-persistent wrappers around the value before calling
+        this method.
         """
 
-    def identifyObject(value):
-        """Returns the keychain of an existing object.
+    def referenced(name, value, is_attribute, oid):
+        """Notifies the system of an inter-record reference.
 
-        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.
+        Be careful to unwrap non-persistent wrappers around the value
+        before calling this method.  Once the referenced object gets
+        stored, the deserialize() method of the serializer will be
+        able to find the referenced object by calling
+        IDeserializationEvent.resolve().
 
-        TODO: write more here. ;-)
+        The event also records an intra-record reference.
         """
 
-    def ignoreAttribute(name):
-        """Indicates that an attribute should be ignored when storing."""
-
-    def ignoreAttributes(names):
-        """Indicates that several attributes should be ignored."""
+    def ignore(name_or_names):
+        """Indicates attribute name(s) to be ignored when serializing.
+        """
 
 
 class IFullSerializationEvent(ISerializationEvent):
@@ -268,35 +249,37 @@
     """
 
     def getSerializedAttributeNames():
-        """Returns the name of all attributes serialized."""
+        """Returns the names of all attributes serialized.
+        """
+
+    def identifyInternal(ob):
+        """Returns the intra-record reference for a subobject, if there is one.
 
-    def getInternalRef(ob):
-        """Returns (serializer_name, name) or None."""
+        Returns (serializer_name, name) or None.
+        """
 
 
 class ISerializer(Interface):
     """Object serializer / deserializer"""
 
-    def getSchema():
-        """Returns the schema of records (de)serialized by this component.
-        """
+    schema = Attribute(__doc__="The schema used by this component.")
 
     def canSerialize(obj):
         """Returns true if this serializer can serialize the given object.
         """
 
-    def serialize(obj, event):
+    def serialize(event):
         """Returns the state of this part of the object.
 
         Use the ISerializationEvent to set up internal and external
         references.
         """
 
-    def deserialize(obj, event, state):
+    def deserialize(event, state):
         """Fills in the state of this part of the object.
 
         Use the IDeserializationEvent to resolve external references.
-        Returns nothing.
+        No return value.
         """
 
 
@@ -328,7 +311,7 @@
 
 
 class IGateway (Interface):
-    """Loads and stores data by keychain.
+    """Loads and stores data by OID.
 
     Implementations can store in entire tables, pieces of tables, translate
     for storage in joined tables, or store in some entirely different way.
@@ -337,11 +320,7 @@
     by Martin Fowler.
     """
 
-    def getSchema():
-        """Returns the ISchema of data stored by this gateway.
-
-        See serial.interfaces.ISchema.
-        """
+    schema = Attribute(__doc__="The schema used by this component.")
 
     def load(event):
         """Loads data.
@@ -352,6 +331,9 @@
         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 no data is available for the requested OID, load() should
+        raise a KeyError.
         """
 
     def store(event, data):
@@ -362,18 +344,19 @@
         Returns a new hash value.
         """
 
-    def getSources(event):
-        """Returns source information for a keychain.
+    def getPollSources(event):
+        """Returns source information for an OID.  event is an IGatewayEvent.
 
         The source information allows the system to poll for changes
         to keep caches in sync with the data.  Where polling is not
         necessary, gateways are free to return None.
 
         The source information is a dictionary in the format:
-        {(repository, source): state}.  The repository must be an
+        {(source_repository, path): state}.  The repository must be an
         ISourceRepository.  The source and state must be in a form
-        recognized by the repository.  Both the repository and source
-        must be hashable.
+        recognized by the repository.  Since they are used as
+        dictionary keys, both the repositories and paths must be
+        hashable.
         """
 
 
@@ -391,22 +374,28 @@
     The keys in classifications are implementation-dependent.
     """
 
-    def classifyObject(value, keychain):
+    gateway = Attribute(__doc__="""The classification IGateway.
+
+    Classifiers load and store classifications using a gateway.  This
+    attribute allows the system to store the classification of an
+    object by calling gateway.store().
+    """)
+
+    def classifyObject(event):
         """Returns a classification and mapper_name.
+
+        event is an ISerializationEvent without a mapper (since this
+        method chooses the mapper).
         """
 
     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 ILoadEvent without a mapper (since this method
+        chooses the mapper).
 
-        event is an IStoreEvent.
+        May load the classification from storage by calling
+        self.gateway.load().
         """
 
 
@@ -422,112 +411,92 @@
 
         'class'     - matches a fully-qualified class name
         'extension' - matches a filename extension
-        'key'       - matched the end of the keychain
-        'fallback'  - matches when no other condition is met.  The
-                      fallback values depend on the classifier.
+        'generic'  - matches when no other condition is met.  The
+                      generic types depend on the classifier, but
+                      usually include 'file', 'directory', 'file_object',
+                      and 'folder_object'.
+        """
+
+    def setOption(mapper_name, option, value):
+        """Sets a classification option pertaining to a particular mapper.
+
+        As an example, the zope2 classifier allows at least two options:
+
+        'default_extension' - specifies the filename extension to
+        generate if the object is being stored on the filesystem and
+        has no extension.
+
+        'content_type_attr' - The name of the object attribute that
+        specifies the MIME type of the object, for the purpose of
+        determining an appropriate default filename extension.
         """
 
 
-class IKeychainGenerator (Interface):
-    """A utility for generating keychains.
+class IOIDGenerator (Interface):
+    """A utility for generating OIDs.
 
     Many serializers encode references to other objects, but it is
-    desirable to not hard code the format of keychains into
+    desirable to not hard code the format of OIDs into
     ISerializer implementations.  This utility, normally accessed
-    through an IMapperEvent, allows keychain knowledge to be kept in
+    through an IMapperEvent, allows OID knowledge to be kept in
     one place.
     """
 
-    def makeKeychain(event, name, stored):
-        """Returns a new keychain.
+    root_oid = Attribute(__doc__="The OID to use for the root")
+
+    def new_oid(event, name, stored):
+        """Returns a new oid.
 
         event is an IMapperEvent.  Additionally, it is always either
         an ISDEvent or an IGatewayEvent.
 
         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.
+        stored is a boolean indicating whether the returned oid will be
+        stored.  If it will not be stored, the generated oid can not
+        be arbitrary.
         """
 
 
 class IMapper (Interface):
     """A hub for mapping a certain kind of object.
     """
+    serializer = Attribute(__doc__="The IObjectSerializer for this mapper")
 
-    def getSerializer():
-        """Returns the IObjectSerializer for this mapper."""
-
-    def getGateway():
-        """Returns the IGateway for this mapper."""
+    gateway = Attribute(__doc__="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 listSubMapperNames():
-        """Returns the name of all sub-IMappers."""
-
-    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.
-        """
-
-    def getInitializers():
-        """Returns the list of configured IDatabaseInitializers."""
+    initializers = Attribute(__doc__="A list of IDatabaseInitializers")
 
 
 class IConfigurableMapper (IMapper):
     """Adds operations to IMapper for configuration.
     """
 
-    def setSerializer(s):
-        """Sets the IFullObjectSerializer for this mapper."""
+    def check(my_name):
+        """Verifies the mapper configuration is sane.
 
-    def setGateway(g):
-        """Returns the IGateway for this mapper."""
+        Raises a ConfigurationError if inconsistencies are detected.
 
-    def setClassifier(c):
-        """Sets the IClassifier for this mapper."""
+        'my_name' gives the name of the mapper for debugging purposes.
+        """
 
-    def setKeychainGenerator(k):
-        """Sets the IKeychainGenerator for subobjects."""
 
-    def addSubMapper(name, m=None, replace=0):
-        """Adds a named sub-IMapper, returning the mapper added.
+class IMapperConfiguration (Interface):
+    """A configuration of mappers.
+    """
 
-        If m is None, a default IConfigurableMapper
-        implementation is created.  If replace is not set,
-        the implemenation prevents overriding an existing mapper.
-        """
+    mappers = Attribute(__doc__="Maps mapper name to IMapper")
 
-    def addInitializer(obj):
-        """Adds a database initializer.
+    classifier = Attribute(__doc__="The IClassifier")
 
-        Database initializers get notified of new connections,
-        allowing them to prepare the database.
-        """
+    oid_gen = Attribute(__doc__="The IOIDGenerator")
 
-    def checkConfiguration(path='root', recursive=1):
-        """Verifies the mapper configuration is sane.
+    initializers = Attribute(__doc__="A list of IDatabaseInitializers")
 
-        Raises an exception if there are errors.
+    def check():
+        """Verifies the configuration is sane.
 
-        'path' gives the path to the mapper, for debugging purposes.
-        'recursive' can be turned off to not descend into sub-mappers.
+        Raises a ConfigurationError if inconsistencies are detected.
         """
 
 
@@ -564,7 +533,7 @@
     Designed to helps keep a cache in sync with its sources.
     """
 
-    def freshen(sources):
+    def poll(sources):
         """Returns changed source information.
 
         The source information is a mapping that maps
@@ -574,4 +543,3 @@
         mapping containing only the items of the input dictionary
         whose state has changed.
         """
-


=== Products/Ape/lib/apelib/core/io.py 1.6 => 1.7 ===
--- Products/Ape/lib/apelib/core/io.py:1.6	Sat Aug  9 11:16:32 2003
+++ Products/Ape/lib/apelib/core/io.py	Mon Feb  2 10:07:18 2004
@@ -23,24 +23,25 @@
 from events \
      import DatabaseInitEvent, GatewayEvent, LoadEvent, StoreEvent, \
      SerializationEvent, DeserializationEvent
-from interfaces import ITPCConnection, IKeyedObjectSystem
+from interfaces import IMapperConfiguration, ITPCConnection, IObjectDatabase
 
 
 class ClassifiedState:
     """Object state with classification information."""
 
-    def __init__(self, state, classification, mapper_names):
+    def __init__(self, state, classification, mapper_name):
         self.state = state
         self.classification = classification
-        self.mapper_names = mapper_names
+        self.mapper_name = mapper_name
 
 
 class GatewayIO:
     """Gateway operations facade."""
 
-    def __init__(self, root_mapper, connections):
-        self._root_mapper = root_mapper
-        self._conn_map = connections
+    def __init__(self, conf, connections):
+        assert IMapperConfiguration.isImplementedBy(conf), conf
+        self.conf = conf
+        self.conn_map = connections
         # Sort the connections by sort key.  Use an extra index to avoid
         # using connections as sort keys.
         items = []  # [(sort_key, index, conn)]
@@ -54,12 +55,12 @@
         conn_list = []
         for sort_key, index, c in items:
             conn_list.append(c)
-        self._conn_list = conn_list
+        self.conn_list = conn_list
 
     def openConnections(self):
         try:
             opened = []
-            for c in self._conn_list:
+            for c in self.conn_list:
                 c.connect()
                 opened.append(c)
         except:
@@ -68,242 +69,192 @@
             raise
 
     def closeConnections(self):
-        for conn in self._conn_list:
+        for conn in self.conn_list:
             conn.close()
 
     def getConnectionList(self):
-        return self._conn_list
+        return self.conn_list
 
     def getConnectionMap(self):
-        return self._conn_map
-
+        return self.conn_map
 
     def initDatabases(self, clear_all=0):
         """Creates tables, etc.
         """
         # Find all initializers, eliminating duplicates.
         initializers = {}  # obj -> 1
-        todo = [self._root_mapper]
-        while todo:
-            mapper = todo.pop()
-            for obj in mapper.getInitializers():
+        for mapper in self.conf.mappers.values():
+            for obj in mapper.initializers:
                 initializers[obj] = 1
-            sub = mapper.listSubMapperNames()
-            if sub:
-                for name in sub:
-                    m = mapper.getSubMapper(name)
-                    todo.append(m)
+        for obj in self.conf.initializers:
+            initializers[obj] = 1
+            
         # Now call them.
-        event = DatabaseInitEvent(self._conn_map, clear_all)
+        event = DatabaseInitEvent(self.conn_map, clear_all)
         for initializer in initializers.keys():
             initializer.init(event)
 
-
-    def classifyState(self, keychain):
-        mapper = self._root_mapper
-        mapper_names = []
-        # Follow the keychain to find the right mapper.
-        classification = None
-        for i in range(len(keychain)):
-            k = keychain[:i + 1]
-            cfr = mapper.getClassifier()
-            assert cfr is not None, keychain
-            event = LoadEvent(mapper, k, self._conn_map, classification)
-            classification, sub_mapper_name = cfr.classifyState(event)
-            mapper_names.append(sub_mapper_name)
-            mapper = mapper.getSubMapper(sub_mapper_name)
-        return classification, mapper_names, mapper
-
-
-    def load(self, keychain):
-        classification, mapper_names, mapper = self.classifyState(keychain)
-        event = LoadEvent(mapper, keychain, self._conn_map, classification)
-        state, hash_value = mapper.getGateway().load(event)
-        cs = ClassifiedState(state, classification, mapper_names)
+    def classifyState(self, oid):
+        event = LoadEvent(self.conf, None, oid, self.conn_map, None)
+        # Return (classification, mapper_name)
+        return self.conf.classifier.classifyState(event)
+
+    def load(self, oid):
+        classification, mapper_name = self.classifyState(oid)
+        mapper = self.conf.mappers[mapper_name]
+        event = LoadEvent(self.conf, mapper, oid, self.conn_map, classification)
+        state, hash_value = mapper.gateway.load(event)
+        cs = ClassifiedState(state, classification, mapper_name)
         return event, cs, hash_value
 
-
-    def store(self, keychain, classified_state):
-        classification = classified_state.classification
-        mapper_names = classified_state.mapper_names
-        assert len(keychain) == len(mapper_names)
-        mapper = self._root_mapper
-        prev_mapper = mapper
-        for mapper_name in mapper_names:
-            prev_mapper = mapper
-            mapper = mapper.getSubMapper(mapper_name)
-        cfr = prev_mapper.getClassifier()
-        event = StoreEvent(mapper, keychain, self._conn_map, classification)
-        new_hash = mapper.getGateway().store(event, classified_state.state)
-        if cfr is not None:
-            cfr.store(event, classification)
+    def store(self, oid, classified_state, is_new):
+        mapper = self.conf.mappers[classified_state.mapper_name]
+        event = StoreEvent(self.conf, mapper, oid, self.conn_map,
+                           classified_state.classification, is_new)
+        # Store the classification first
+        self.conf.classifier.gateway.store(
+            event, classified_state.classification)
+        # Store the state second
+        new_hash = mapper.gateway.store(event, classified_state.state)
         return event, new_hash
 
+    def getPollSources(self, oid):
+        classification, mapper_name = self.classifyState(oid)
+        mapper = self.conf.mappers[mapper_name]
+        event = LoadEvent(self.conf, mapper, oid, self.conn_map, classification)
+        return mapper.gateway.getPollSources(event)
+
+    def new_oid(self):
+        event = GatewayEvent(self.conf, None, None, self.conn_map, None)
+        return self.conf.oid_gen.new_oid(event, None, 1)
 
-    def getSources(self, keychain):
-        classification, mapper_names, mapper = self.classifyState(keychain)
-        event = LoadEvent(mapper, keychain, self._conn_map, classification)
-        return mapper.getGateway().getSources(event)
-
-
-    def newKeychain(self):
-        # Try to use the root keychain generator to make a keychain.
-        kgen = self._root_mapper.getKeychainGenerator()
-        event = GatewayEvent(self._root_mapper, (), self._conn_map, None)
-        return kgen.makeKeychain(event, None, 1)
 
 
 class ObjectSystemIO:
     """Object system (de)serialization facade."""
 
-    def __init__(self, root_mapper, kos):
-        self._root_mapper = root_mapper
-        self._kos = kos
-
-
-    def classifyObject(self, obj, keychain):
-        mapper = self._root_mapper
-        mapper_names = []
-        classification = None
-        if keychain:
-            # Classify the parents first to discover what mapper to
-            # use for storage.
-            for i in range(1, len(keychain)):
-                k = keychain[:i]
-                o = self._kos.getObject(k)
-                cfr = mapper.getClassifier()
-                classification, sub_mapper_name = cfr.classifyObject(o, k)
-                mapper_names.append(sub_mapper_name)
-                mapper = mapper.getSubMapper(sub_mapper_name)
-            # Now classify the object being stored.
-            cfr = mapper.getClassifier()
-            classification, sub_mapper_name = cfr.classifyObject(obj, keychain)
-            mapper_names.append(sub_mapper_name)
-            mapper = mapper.getSubMapper(sub_mapper_name)
-        return classification, mapper_names, mapper
-
-
-    def serialize(self, keychain, obj):
-        classification, mapper_names, mapper = self.classifyObject(
-            obj, keychain)
-        # Now serialize.
-        ser = mapper.getSerializer()
-        event = SerializationEvent(self._kos, mapper, keychain, obj)
-        state = ser.serialize(obj, event)
-        cs = ClassifiedState(state, classification, mapper_names)
+    def __init__(self, conf, obj_db):
+        assert IMapperConfiguration.isImplementedBy(conf), conf
+        assert IObjectDatabase.isImplementedBy(obj_db), obj_db
+        self.conf = conf
+        self.obj_db = obj_db
+
+    def classifyObject(self, obj, oid):
+        event = SerializationEvent(self.conf, None, oid, self.obj_db, obj)
+        # Returns (classification, mapper_name)
+        return self.conf.classifier.classifyObject(event)
+
+    def serialize(self, oid, obj):
+        classification, mapper_name = self.classifyObject(obj, oid)
+        mapper = self.conf.mappers[mapper_name]
+        event = SerializationEvent(self.conf, mapper, oid, self.obj_db, obj)
+        state = mapper.serializer.serialize(event)
+        cs = ClassifiedState(state, classification, mapper_name)
         return event, cs
 
-
-    def deserialize(self, keychain, obj, classified_state):
-        mapper = self._root_mapper
-        assert len(keychain) == len(classified_state.mapper_names)
-        for mapper_name in classified_state.mapper_names:
-            mapper = mapper.getSubMapper(mapper_name)
-        ser = mapper.getSerializer()
-        event = DeserializationEvent(self._kos, mapper, keychain, obj)
-        ser.deserialize(obj, event, classified_state.state)
+    def deserialize(self, oid, obj, classified_state):
+        mapper = self.conf.mappers[classified_state.mapper_name]
+        event = DeserializationEvent(self.conf, mapper, oid, self.obj_db, obj)
+        mapper.serializer.deserialize(event, classified_state.state)
         return event
 
-
     def newObject(self, classified_state):
-        mapper = self._root_mapper
-        for mapper_name in classified_state.mapper_names:
-            mapper = mapper.getSubMapper(mapper_name)
-        ser = mapper.getSerializer()
-        return ser.createEmptyInstance(
-            self._kos, classification=classified_state.classification)
+        mapper = self.conf.mappers[classified_state.mapper_name]
+        return mapper.serializer.createEmptyInstance(
+            self.obj_db, classification=classified_state.classification)
 
 
 
 class ExportImport:
     """Simple import/export facade.
     """
+    __implements__ = IObjectDatabase
 
-    __implements__ = IKeyedObjectSystem
-
-    def __init__(self, root_mapper, connections, class_factory=None):
-        self._objects = {}     # { keychain -> obj }
-        self._keychains = {}   # { id(obj) -> keychain }
-        # _incomplete contains the keychains of objects not yet
+    def __init__(self, conf, connections, class_factory=None):
+        self._objects = {}     # { oid -> obj }
+        self._oids = {}   # { id(obj) -> oid }
+        # _incomplete contains the oids of objects not yet
         # imported fully.
-        self._incomplete = {}   # { keychain -> 1 }
+        self._incomplete = {}   # { oid -> 1 }
         self._class_factory = class_factory
         # Avoid a circular reference by making a weakref proxy
-        self.obj_io = ObjectSystemIO(root_mapper, proxy(self))
-        self.gw_io = GatewayIO(root_mapper, connections)
+        self.obj_io = ObjectSystemIO(conf, proxy(self))
+        self.gw_io = GatewayIO(conf, connections)
 
 
-    def _register(self, keychain, obj):
+    def _register(self, oid, obj):
         """Registers obj in the temporary object index.
 
         Returns true if the object was added to the index for the first
         time.  If the registration conflicts, raises an exception.
         """
         is_new = 0
-        if self._objects.has_key(keychain):
-            if self._objects[keychain] is not obj:
+        if self._objects.has_key(oid):
+            if self._objects[oid] is not obj:
                 raise ValueError, (
-                    "Multiple objects for keychain %s" % repr(keychain))
+                    "Multiple objects for oid %s" % repr(oid))
         else:
-            self._objects[keychain] = obj
+            self._objects[oid] = obj
             is_new = 1
         obj_id = id(obj)
-        if self._keychains.has_key(obj_id):
-            if self._keychains[obj_id] != keychain:
+        if self._oids.has_key(obj_id):
+            if self._oids[obj_id] != oid:
                 raise ValueError, (
-                    "Multiple keychains for object %s" % repr(obj))
+                    "Multiple oids for object %s" % repr(obj))
         else:
-            self._keychains[obj_id] = keychain
+            self._oids[obj_id] = oid
             is_new = 1
         return is_new
 
 
-    def exportObject(self, src_obj, dest_keychain=None, deactivate_func=None):
+    def exportObject(self, src_obj, dest_oid=None, deactivate_func=None):
         count = 0
-        if dest_keychain is None:
-            dest_keychain = (self.newKey(),)
-        self._register(dest_keychain, src_obj)
-        todo = [(dest_keychain, src_obj)]
+        if dest_oid is None:
+            dest_oid = self.new_oid()
+        self._register(dest_oid, src_obj)
+        # Export subobjects.
+        todo = [(dest_oid, src_obj)]
         while todo:
-            keychain, obj = todo.pop()
-            event, classified_state = self.obj_io.serialize(keychain, obj)
+            oid, obj = todo.pop()
+            event, classified_state = self.obj_io.serialize(oid, obj)
             count += 1
             if deactivate_func is not None:
                 deactivate_func(obj, count)
-            self.gw_io.store(keychain, classified_state)
-            ext_refs = event.getExternalRefs()
+            self.gw_io.store(oid, classified_state, False)
+            ext_refs = event.external
             if ext_refs:
-                for ext_keychain, ext_obj in ext_refs:
-                    if self._register(ext_keychain, ext_obj):
-                        todo.append((ext_keychain, ext_obj))
+                for ext_oid, ext_obj in ext_refs:
+                    if self._register(ext_oid, ext_obj):
+                        todo.append((ext_oid, ext_obj))
 
 
-    def importObject(self, src_keychain, dest_obj=None, commit_func=None):
+    def importObject(self, src_oid, dest_obj=None, commit_func=None):
         count = 0
         if dest_obj is None:
-            dest_obj = self.getObject(src_keychain)
+            dest_obj = self.getObject(src_oid)
         root_obj = dest_obj
-        self._register(src_keychain, dest_obj)
-        todo = [(src_keychain, dest_obj)]
+        self._register(src_oid, dest_obj)
+        # Import subobjects.
+        todo = [(src_oid, dest_obj)]
         while todo:
-            keychain, obj = todo.pop()
-            e, classified_state, hash_value = self.gw_io.load(keychain)
-            event = self.obj_io.deserialize(keychain, obj, classified_state)
-            if self._incomplete.has_key(keychain):
-                del self._incomplete[keychain]
+            oid, obj = todo.pop()
+            e, classified_state, hash_value = self.gw_io.load(oid)
+            event = self.obj_io.deserialize(oid, obj, classified_state)
+            if self._incomplete.has_key(oid):
+                del self._incomplete[oid]
             count += 1
             if commit_func is not None:
                 commit_func(obj, count)
-            ext_refs = event.getExternalRefs()
+            ext_refs = event.external
             if ext_refs:
-                for ext_keychain, ext_obj in ext_refs:
-                    if (self._register(ext_keychain, ext_obj)
-                        or self._incomplete.has_key(ext_keychain)):
-                        todo.append((ext_keychain, ext_obj))
+                for ext_oid, ext_obj in ext_refs:
+                    if (self._register(ext_oid, ext_obj)
+                        or self._incomplete.has_key(ext_oid)):
+                        todo.append((ext_oid, ext_obj))
         return root_obj
 
 
-    # IKeyedObjectSystem implementation
+    # IObjectDatabase implementation
 
     def getClass(self, module, name):
         # Normally called only while importing
@@ -313,27 +264,24 @@
             m = __import__(module, {}, {}, ('__doc__',))
             return getattr(m, name)
 
-    def getObject(self, keychain, hints=None):
+    def getObject(self, oid, hints=None):
         # Should be called only while importing
         try:
-            return self._objects[keychain]
+            return self._objects[oid]
         except KeyError:
             # This object has not been loaded yet.  Make a stub.
-            e, classified_state, hash_value = self.gw_io.load(keychain)
+            e, classified_state, hash_value = self.gw_io.load(oid)
             obj = self.obj_io.newObject(classified_state)
             # Don't fill in the state yet, to avoid infinite
             # recursion.  Just register it.
-            self._incomplete[keychain] = 1
-            self._register(keychain, obj)
+            self._incomplete[oid] = 1
+            self._register(oid, obj)
             return obj
 
-    loadStub = getObject
-
-    def identifyObject(self, obj):
+    def identify(self, obj):
         # Normally called only while exporting
-        return self._keychains.get(id(obj))
+        return self._oids.get(id(obj))
 
-    def newKey(self):
+    def new_oid(self):
         # Should be called only while exporting
-        return self.gw_io.newKeychain()[-1]
-
+        return self.gw_io.new_oid()


=== Products/Ape/lib/apelib/core/mapper.py 1.4 => 1.5 ===
--- Products/Ape/lib/apelib/core/mapper.py:1.4	Wed Jul  9 11:39:59 2003
+++ Products/Ape/lib/apelib/core/mapper.py	Mon Feb  2 10:07:18 2004
@@ -19,75 +19,44 @@
 from types import DictType
 
 import interfaces
-from exceptions import ConfigurationError
+from interfaces import ConfigurationError
 
 
 class Mapper:
-    """Standard mapper class."""
-
+    """Standard mapper class.
+    """
     __implements__ = interfaces.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
-        self._initializers = []
+    serializer = None
+    gateway = None
+    initializers = None
+
+    def __init__(self, serializer=None, gateway=None):
+        self.serializer = serializer
+        self.gateway = gateway
+        self.initializers = []
 
     # IConfigurableMapper implementation
 
-    def setParent(self, p):
-        self._parent = p
-
-    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 addInitializer(self, obj):
-        self._initializers.append(obj)
-
-    def checkConfiguration(self, path='root', recursive=1):
-        s = self._serializer
+    def check(self, my_name):
+        s = self.serializer
         if s is None:
             raise ConfigurationError(
-                'No serializer configured for mapper %s' % repr(path))
+                'Mapper %s: No serializer configured' % my_name)
         if not interfaces.IFullObjectSerializer.isImplementedBy(s):
             raise ConfigurationError(
-                'Not an IFullObjectSerializer: %s' % repr(s))
-        g = self._gateway
+                'Mapper %s: Serializer is not an IFullObjectSerializer'
+                % my_name)
+        g = self.gateway
         if g is None:
             raise ConfigurationError(
-                'No gateway configured for mapper %s' % repr(path))
+                'Mapper %s: No gateway configured' % my_name)
         if not interfaces.IGateway.isImplementedBy(g):
             raise ConfigurationError(
-                'Not an IGateway: %s' % repr(g))
-        if s.getSchema() != g.getSchema():
+                'Mapper %s: Gateway is not an IGateway' % my_name)
+        if s.schema != g.schema:
             # Try to show a descriptive error
-            ss = s.getSchema()
-            gs = g.getSchema()
+            ss = s.schema
+            gs = g.schema
             msg = None
             if isinstance(ss, DictType) and isinstance(gs, DictType):
                 for key in ss.keys():
@@ -107,61 +76,24 @@
             if msg is None:
                 msg = '%s != %s' % (ss, gs)
             raise ConfigurationError(
-                'Mismatched schemas in mapper "%s": %s' % (path, msg))
-        if self._parent is None:
-            if self._classifier is None:
-                raise ConfigurationError('No root classifier configured')
-            if self._kgen is None:
-                raise ConfigurationError(
-                    'No root keychain generator configured')
-        else:
-            if not interfaces.IMapper.isImplementedBy(self._parent):
-                raise ConfigurationError(
-                    'Not an IMapper: %s' % repr(self._parent))
-        if (self._classifier is not None
-            and not interfaces.IClassifier.isImplementedBy(self._classifier)):
-            raise ConfigurationError(
-                'Not an IClassifier: %s' % repr(self._classifier))
-        if (self._kgen is not None
-            and not interfaces.IKeychainGenerator.isImplementedBy(self._kgen)):
-            raise ConfigurationError(
-                'Not an IKeychainGenerator: %s' % repr(self._kgen))
-
-        if recursive:
-            for n, m in self._sub_mappers.items():
-                if not interfaces.IMapper.isImplementedBy(m):
-                    raise ConfigurationError(
-                        'Not an IMapper: %s' % repr(m))
-                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 listSubMapperNames(self):
-        return self._sub_mappers.keys()
-
-    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
+                'Mapper %s: Mismatched schemas. %s' % (my_name, msg))
 
-    def getInitializers(self):
-        return self._initializers
 
+class MapperConfiguration:
+    """Collects the mapper configuration with a classifier and OID generator.
+    """
+    __implements__ = interfaces.IMapperConfiguration
+    mappers = None
+    classifier = None
+    oid_gen = None
+    initializers = None
+
+    def __init__(self, mappers, classifier, oid_gen):
+        self.mappers = mappers
+        self.classifier = classifier
+        self.oid_gen = oid_gen
+        self.initializers = []
+
+    def check(self):
+        for name, mapper in self.mappers.items():
+            mapper.check(name)


=== Products/Ape/lib/apelib/core/schemas.py 1.4 => 1.5 ===
--- Products/Ape/lib/apelib/core/schemas.py:1.4	Wed Jul  9 11:39:59 2003
+++ Products/Ape/lib/apelib/core/schemas.py	Mon Feb  2 10:07:18 2004
@@ -18,10 +18,8 @@
 
 from types import StringType
 
-from interfaces import IRelationalSchema
-
 ok_types = ['unicode', 'string', 'int', 'float', 'bool', 'object',
-            'classification', 'keychain', 'string:list', 'blob']
+            'classification', 'string:list', 'blob']
 
 
 def addFieldType(t):
@@ -33,7 +31,7 @@
 
 class FieldSchema:
     """Defines the schema of one field."""
-    __implements__ = IRelationalSchema
+#    __implements__ = IRelationalSchema
 
     def __init__(self, name, type='string', unique=0):
         assert type in ok_types, type
@@ -62,7 +60,7 @@
 class RowSchema:
     """Defines an ordered set of fields for exactly one row.
     """
-    __implements__ = IRelationalSchema
+#    __implements__ = IRelationalSchema
 
     def __init__(self, fields=()):
         self.fields = []
@@ -101,7 +99,6 @@
 class RowSequenceSchema (RowSchema):
     """Defines a schema for a sequence of rows, including row count limits.
     """
-    __implements__ = IRelationalSchema
 
     def __init__(self, fields=(), min_rows=0, max_rows=0):
         # max_rows == 0 means unlimited.


=== Products/Ape/lib/apelib/core/serializers.py 1.4 => 1.5 ===
--- Products/Ape/lib/apelib/core/serializers.py:1.4	Wed Jul 30 18:11:40 2003
+++ Products/Ape/lib/apelib/core/serializers.py	Mon Feb  2 10:07:18 2004
@@ -19,15 +19,15 @@
 from types import StringType
 
 from interfaces import ISerializer, IFullObjectSerializer
-from exceptions import DeserializationError, SerializationError
+from interfaces import DeserializationError, SerializationError
 from schemas import FieldSchema
 
 
 class CompositeSerializer:
     """Full serializer based on partial serializers.
     """
-
     __implements__ = IFullObjectSerializer
+    schema = None
 
     def __init__(self, module, name, base=None):
         self._module = module
@@ -42,8 +42,16 @@
             self._part_names.update(base._part_names)
             self._parts[:] = base._parts
             self._final_parts[:] = base._final_parts
+        self._updateSchema()
+
+    def _updateSchema(self):
+        self.schema = {}
+        for name, serializer in self.getSerializers():
+            s = serializer.schema
+            if s is not None:
+                self.schema[name] = s
 
-    def addSerializer(self, name, serializer, force=0, final=0):
+    def add(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)
@@ -53,8 +61,9 @@
         else:
             self._parts.append((name, serializer))
         self._part_names[name] = 1
+        self._updateSchema()
 
-    def removeSerializer(self, name):
+    def remove(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):
@@ -63,41 +72,34 @@
                     del lst[i]
                     break
         del self._part_names[name]
+        self._updateSchema()
 
-    def hasSerializer(self, name):
+    def has(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__'):
+    def canSerialize(self, obj):
+        if not hasattr(obj, '__class__'):
             return 0
-        c = object.__class__
+        c = obj.__class__
         return (c.__module__ == self._module and c.__name__ == self._name)
 
-    def serialize(self, object, event):
+    def serialize(self, event):
         full_state = {}
-        for name, serializer in self.getSerializers():
-            event.setSerializerName(name)
-            state = serializer.serialize(object, event)
+        for name, s in self.getSerializers():
+            event.serializer_name = name
+            state = s.serialize(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():
+    def deserialize(self, event, full_state):
+        for name, s in self.getSerializers():
             state = full_state.get(name)
-            event.setSerializerName(name)
-            serializer.deserialize(object, event, state)
+            event.serializer_name = name
+            s.deserialize(event, state)
 
     def createEmptyInstance(self, class_factory, classification=None):
         c = class_factory.getClass(self._module, self._name)
@@ -107,13 +109,12 @@
 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):
+    def canSerialize(self, obj):
         return 1
 
     def createEmptyInstance(self, class_factory, classification=None):
@@ -127,7 +128,10 @@
         module = cn[:pos]
         name = cn[pos + 1:]
         c = class_factory.getClass(module, name)
-        return c.__basicnew__()
+        if hasattr(c, "__basicnew__"):  # ExtensionClass
+            return c.__basicnew__()
+        else:
+            return c.__new__()
 
 
 class FullState:
@@ -137,17 +141,14 @@
 
     schema = FieldSchema('data', 'object')
 
-    def getSchema(self):
-        return self.schema
-
-    def canSerialize(self, object):
+    def canSerialize(self, obj):
         return 1
 
-    def serialize(self, object, event):
-        return object.__getstate__()
+    def serialize(self, event):
+        return event.obj.__getstate__()
 
-    def deserialize(self, object, event, state):
-        object.__setstate__(state)
+    def deserialize(self, event, state):
+        event.obj.__setstate__(state)
 
 
 
@@ -155,22 +156,20 @@
     """Serializer that explicitly ignores an attribute
     """
     __implements__ = ISerializer
+    schema = None  # No storage
 
     def __init__(self, attrname):
         self.attrname = attrname
 
-    def getSchema(self):
-        return None  # No storage
-
-    def canSerialize(self, object):
+    def canSerialize(self, obj):
         return 1
 
-    def serialize(self, object, event):
-        event.ignoreAttribute(self.attrname)
+    def serialize(self, event):
+        event.ignore(self.attrname)
         return None
 
-    def deserialize(self, object, event, state):
-        assert state is None
+    def deserialize(self, event, state):
+        assert state is None, state
 
 
 class OptionalSerializer:
@@ -178,31 +177,30 @@
     """
 
     __implements__ = ISerializer
+    schema = None
 
     def __init__(self, real, default_state=None):
         self._real = real
         self._default_state = default_state
+        self.schema = real.schema
 
-    def getSchema(self):
-        return self._real.getSchema()
-
-    def canSerialize(self, object):
+    def canSerialize(self, obj):
         return 1
 
-    def serialize(self, object, event):
-        if self._real.canSerialize(object):
-            return self._real.serialize(object, event)
+    def serialize(self, event):
+        if self._real.canSerialize(event.obj):
+            return self._real.serialize(event)
         else:
             return self._default_state
 
-    def deserialize(self, object, event, state):
-        if self._real.canSerialize(object):
-            self._real.deserialize(object, event, state)
+    def deserialize(self, event, state):
+        if self._real.canSerialize(event.obj):
+            self._real.deserialize(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)))
+                    (repr(state), repr(event.obj)))
 
 
 class StringDataAttribute:
@@ -215,24 +213,21 @@
     def __init__(self, attrname):
         self.attrname = attrname
 
-    def getSchema(self):
-        return self.schema
-
     def canSerialize(self, object):
         return 1
 
-    def serialize(self, object, event):
+    def serialize(self, event):
         attrname = self.attrname
         assert attrname
-        v = getattr(object, attrname)
+        v = getattr(event.obj, attrname)
         assert isinstance(v, StringType)
-        event.notifySerialized(attrname, v, 1)
+        event.serialized(attrname, v, 1)
         return v
 
-    def deserialize(self, object, event, state):
+    def deserialize(self, event, state):
         attrname = self.attrname
         assert attrname
         assert isinstance(state, StringType)
-        setattr(object, attrname, state)
-        event.notifyDeserialized(attrname, state)
+        setattr(event.obj, attrname, state)
+        event.deserialized(attrname, state)
 

=== Removed File Products/Ape/lib/apelib/core/exceptions.py ===

=== Removed File Products/Ape/lib/apelib/core/keygen.py ===




More information about the Zope-CVS mailing list