[Zope-CVS] CVS: Products/Ape/lib/apelib/core - classifiers.py:1.5 interfaces.py:1.17 io.py:1.13 mapper.py:1.7 serializers.py:1.8

Shane Hathaway shane at zope.com
Fri Jul 23 04:38:04 EDT 2004


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

Modified Files:
	classifiers.py interfaces.py io.py mapper.py serializers.py 
Log Message:
Ape now supports mapping subclasses.  Also revised the config format.

>From CHANGES.txt:

  - Ape now supports mapping subclasses.  Until now, mappers were
    registered for classes but not for all subclasses.  Now, a mapper
    applies to subclasses as well, unless the configuration file
    specifies 'exact-class'.

  - Revised the configuration file format for simplification and to
    allow mapping subclasses.

Also added PDBSerializer, a wrapper around CompositeSerializer that 
launches pdb.  It is really handy when you know which 
mapper is serializing incorrectly but you don't know why.



=== Products/Ape/lib/apelib/core/classifiers.py 1.4 => 1.5 ===
--- Products/Ape/lib/apelib/core/classifiers.py:1.4	Sat Mar 20 01:34:22 2004
+++ Products/Ape/lib/apelib/core/classifiers.py	Fri Jul 23 04:37:34 2004
@@ -31,11 +31,11 @@
         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 add_store_rule(self, class_name, mapper_name, *args, **kw):
+        self._class_to_mapper[class_name] = mapper_name
+
+    def add_load_rule(self, criterion, value, mapper_name):
+        pass
 
     def set_option(self, mapper_name, option, value):
         raise ConfigurationError("No options available")


=== Products/Ape/lib/apelib/core/interfaces.py 1.16 => 1.17 ===
--- Products/Ape/lib/apelib/core/interfaces.py:1.16	Wed Jul 21 02:38:05 2004
+++ Products/Ape/lib/apelib/core/interfaces.py	Fri Jul 23 04:37:34 2004
@@ -296,18 +296,19 @@
     and schema of its constituent ISerializers.
     """
 
-    def new_instance(class_factory, classification=None):
+    def new_instance(event):
         """Returns a new instance.
 
+        event is as IDeserializationEvent.
+
         If this serializer works with instances of only one class,
-        new_instance() should not require the use of the
-        classification argument.  Implementations that need the
+        new_instance() should not require the use of a
+        classification.  Implementations that need the
         classification argument can return None when classification is
-        None, but doing so may incur a performance penalty.
+        None, but it may take more work to fetch the classification.
 
-        class_factory is an IClassFactory.  The serializer may use it
-        instead of the standard Python import mechanism to find a
-        class.
+        Implementations should use the IClassFactory implementation
+        in the obj_db attribute of the event to load classes.
         """
 
 
@@ -404,32 +405,31 @@
     """Classifier that accepts registrations.
     """
 
-    def register(condition, value, mapper_name):
-        """Registers a sub-mapper to be used when a condition is met.
-
-        The kinds of conditions available depend on the classifier,
-        but the following conditions are expected to be common:
-
-        'class'     - matches a fully-qualified class name
-        'extension' - matches a filename extension
-        '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 add_store_rule(class_name, mapper_name, exact=False,
+                        default_extension=None, default_extension_source=None):
+        """Adds a rule that says which mapper to use for storing an instance.
+
+        If 'exact' is true, the mapper will not be used for
+        subclasses.  'default_extension' provides the default filename
+        extension to use when storing to the filesystem.
+        'default_extension_source' selects a method of determining the
+        extension.  One method is 'content_type', which reads the
+        content_type attribute of the object being stored and
+        translates the mime type to an extension.  Don't provide both
+        'default_extension' and 'default_extension_source'.
         """
 
-    def set_option(mapper_name, option, value):
-        """Sets a classification option pertaining to a particular mapper.
+    def add_load_rule(criterion, value, mapper_name):
+        """Adds a rule that says which mapper to use for loading some data.
 
-        As an example, the zope2 classifier allows at least two options:
+        The following values for 'criterion' are common:
 
-        '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.
+        'mapper_name' - matches a previously stored mapper name
+                      (useful for mapper name changes)
+        'extension' - matches a filename extension
+        'generic'  - matches certain kinds of data.  The
+                      generic values depend on the classifier, but
+                      include 'file', 'directory', 'basepath', and 'root'.
         """
 
 
@@ -449,7 +449,13 @@
 class IMapper (Interface):
     """A hub for mapping a certain kind of object.
     """
-    serializer = Attribute("serializer", "The IObjectSerializer for this mapper")
+    name = Attribute("name", "The name of this mapper")
+
+    class_name = Attribute(
+        "class_name", "The class expected by this mapper (may be empty)")
+
+    serializer = Attribute(
+        "serializer", "The IObjectSerializer for this mapper")
 
     gateway = Attribute("gateway", "The IGateway for this mapper")
 


=== Products/Ape/lib/apelib/core/io.py 1.12 => 1.13 ===
--- Products/Ape/lib/apelib/core/io.py:1.12	Fri Mar 26 11:07:59 2004
+++ Products/Ape/lib/apelib/core/io.py	Fri Jul 23 04:37:34 2004
@@ -160,11 +160,12 @@
         mapper.serializer.deserialize(event, state)
         return event
 
-    def new_instance(self, classification):
+    def new_instance(self, oid, classification):
         mapper_name = classification['mapper_name']
         mapper = self.conf.mappers[mapper_name]
-        return mapper.serializer.new_instance(
-            self.obj_db, classification=classification)
+        event = DeserializationEvent(
+            self.conf, mapper, oid, classification, self.obj_db, None)
+        return mapper.serializer.new_instance(event)
 
 
 
@@ -274,7 +275,7 @@
         except KeyError:
             # This object has not been loaded yet.  Make a stub.
             e, classification, state, hash_value = self.gw_io.load(oid)
-            obj = self.obj_io.new_instance(classification)
+            obj = self.obj_io.new_instance(oid, classification)
             # Don't fill in the state yet, to avoid infinite
             # recursion.  Just register it.
             self._incomplete[oid] = 1


=== Products/Ape/lib/apelib/core/mapper.py 1.6 => 1.7 ===
--- Products/Ape/lib/apelib/core/mapper.py:1.6	Thu Mar 11 00:46:16 2004
+++ Products/Ape/lib/apelib/core/mapper.py	Fri Jul 23 04:37:34 2004
@@ -26,11 +26,16 @@
     """Standard mapper class.
     """
     __implements__ = interfaces.IConfigurableMapper
+    name = None
+    class_name = None
     serializer = None
     gateway = None
     initializers = None
 
-    def __init__(self, serializer=None, gateway=None):
+    def __init__(self, name=None, class_name=None,
+                 serializer=None, gateway=None):
+        self.name = name
+        self.class_name = class_name
         self.serializer = serializer
         self.gateway = gateway
         self.initializers = []


=== Products/Ape/lib/apelib/core/serializers.py 1.7 => 1.8 ===
--- Products/Ape/lib/apelib/core/serializers.py:1.7	Wed Jul 21 02:38:05 2004
+++ Products/Ape/lib/apelib/core/serializers.py	Fri Jul 23 04:37:34 2004
@@ -29,12 +29,7 @@
     __implements__ = IFullObjectSerializer
     schema = None
 
-    def __init__(self, module, name, base=None):
-        self._module = module
-        self._name = name
-        self.init(base)
-
-    def init(self, base=None):
+    def __init__(self, base=None):
         self._part_names = {}  # { name -> 1 }
         self._parts = []       # [(name, serializer)] -- Order matters.
         self._final_parts = [] # [(name, serializer)]
@@ -81,12 +76,32 @@
         return self._parts + self._final_parts
 
     def can_serialize(self, obj):
-        if not hasattr(obj, '__class__'):
-            return 0
-        c = obj.__class__
-        return (c.__module__ == self._module and c.__name__ == self._name)
+        # XXX Need access to the mapper to make this determination.
+        return 1
+##        if not hasattr(obj, '__class__'):
+##            return 0
+##        c = obj.__class__
+##        return (c.__module__ == self._module and c.__name__ == self._name)
+
+    def has_base(self, klass, base_name):
+        try:
+            n = '%s.%s' % (klass.__module__, klass.__name__)
+        except AttributeError:
+            return False
+        if n == base_name:
+            return True
+        for b in klass.__bases__:
+            if self.has_base(b, base_name):
+                return True
+        return False
 
     def serialize(self, event):
+        if event.mapper.class_name:
+            assert self.has_base(
+                event.obj.__class__, event.mapper.class_name), (
+                event.obj, event.mapper.class_name)
+        else:
+            raise RuntimeError("Mapper '%s' is abstract" % event.mapper.name)
         full_state = {}
         for name, s in self.get_serializers():
             event.serializer_name = name
@@ -96,42 +111,47 @@
         return full_state
 
     def deserialize(self, event, full_state):
+        if event.mapper.class_name:
+            assert self.has_base(
+                event.obj.__class__, event.mapper.class_name), (
+                event.obj, event.mapper.class_name)
         for name, s in self.get_serializers():
             state = full_state.get(name)
             event.serializer_name = name
             s.deserialize(event, state)
 
-    def new_instance(self, class_factory, classification=None):
-        c = class_factory.get_class(self._module, 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 can_serialize(self, obj):
-        return 1
-
-    def new_instance(self, class_factory, classification=None):
-        if classification is None:
-            # This serializer can't do anything without the classification.
+    def new_instance(self, event):
+        if event.classification is None:
+            # Can't do anything without the classification.
             return None
-        cn = classification['class_name']
+        cn = event.classification.get('class_name')
+        if cn is None:
+            # Fall back to the default
+            cn = event.mapper.class_name
         pos = cn.rfind('.')
         if pos < 0:
             raise ValueError, "class_name must include the module"
         module = cn[:pos]
         name = cn[pos + 1:]
-        c = class_factory.get_class(module, name)
+        c = event.obj_db.get_class(module, name)
         if hasattr(c, "__basicnew__"):  # ExtensionClass
             return c.__basicnew__()
         else:
             return c.__new__()
+
+
+class PDBSerializer (CompositeSerializer):
+    """Invokes PDB before serialization / deserialization."""
+
+    def serialize(self, event):
+        import pdb
+        pdb.set_trace()
+        return AnyObjectSerializer.serialize(self, event)
+
+    def deserialize(self, event, full_state):
+        import pdb
+        pdb.set_trace()
+        AnyObjectSerializer.deserialize(self, event, full_state)
 
 
 class FullState:



More information about the Zope-CVS mailing list