[Zope-CVS] CVS: Products/Ape/lib/apelib/zope2 - apeconf.xml:1.12 classifier.py:1.11

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


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

Modified Files:
	apeconf.xml classifier.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/zope2/apeconf.xml 1.11 => 1.12 ===
--- Products/Ape/lib/apelib/zope2/apeconf.xml:1.11	Wed Mar 24 22:17:09 2004
+++ Products/Ape/lib/apelib/zope2/apeconf.xml	Fri Jul 23 04:37:35 2004
@@ -21,8 +21,7 @@
 
 <!-- Root mapper -->
 
-<mapper name="root" register-class="false"
-   class="Persistence.PersistentMapping">
+<mapper name="root" class="Persistence.PersistentMapping">
  <serializer name="items"
    factory="apelib.zodb3.serializers.PersistentMappingSerializer" />
  <serializer name="roll_call" factory="apelib.zodb3.serializers.RollCall" />
@@ -32,13 +31,13 @@
  <variation name="sql">
   <gateway name="items" factory="apelib.sql.structure.root_mapping" />
  </variation>
- <use-for oid="0" />
 </mapper>
+<load generic="root" using="root" />
 
 <!-- 'common' is an abstract mapper that provides commonly used
 serializers and gateways. -->
 
-<mapper name="common" class="none">
+<mapper name="common">
 
  <serializer name="id" order="a"
    factory="apelib.zope2.ofsserial.IdAttribute" />
@@ -86,7 +85,7 @@
 
 <!-- 'common_p' is an abstract mapper with properties -->
 
-<mapper name="common_p" extends="common" class="none">
+<mapper name="common_p" extends="common">
  <serializer name="properties"
     factory="apelib.zope2.ofsserial.OFSProperties" />
  <variation name="filesystem">
@@ -101,7 +100,7 @@
 
 <!-- 'common_text' is an abstract mapper with properties and a text body -->
 
-<mapper name="common_text" extends="common_p" class="none">
+<mapper name="common_text" extends="common_p">
  <serializer name="text"
    factory="apelib.core.serializers.StringDataAttribute('raw')" />
  <variation name="filesystem">
@@ -116,7 +115,8 @@
 
 <!-- Folder -->
 
-<mapper name="folder" class="OFS.Folder.Folder" extends="common_p">
+<mapper name="folder" extends="common_p"
+  class="OFS.Folder.Folder">
   <serializer name="items"
     factory="apelib.zope2.ofsserial.FolderItems" />
  <variation name="filesystem">
@@ -127,12 +127,14 @@
   <gateway name="items"
     factory="apelib.sql.structure.SQLFolderItems" />
  </variation>
- <use-for generic="directory" />
 </mapper>
+<store exact-class="OFS.Folder.Folder" using="folder" />
+<load generic="directory" using="folder" />
 
 <!-- File -->
 
-<mapper name="file" class="OFS.Image.File" extends="common_p">
+<mapper name="file" extends="common_p"
+  class="OFS.Image.File">
   <serializer name="data"
     factory="apelib.zope2.ofsserial.FilePData" />
  <variation name="filesystem">
@@ -143,69 +145,84 @@
   <gateway name="data"
     factory="apelib.sql.structure.SQLObjectData" />
  </variation>
- <use-for generic="file" />
- <option name="content_type_attr" value="content_type" />
 </mapper>
+<store class="OFS.Image.File" using="file"
+  default-extension-source="content_type" />
+<load generic="file" using="file" />
 
 <!-- Image -->
 
-<mapper name="OFS.Image.Image" extends="file">
- <use-for extensions=".gif .jpg .jpeg .png" />
- <option name="content_type_attr" value="content_type" />
+<mapper name="image" extends="file"
+  class="OFS.Image.Image">
 </mapper>
+<store class="OFS.Image.Image" using="image"
+  default-extension-source="content_type" />
+<load extensions=".gif .jpg .jpeg .png" using="image" />
 
 <!-- Page template -->
 
-<mapper name="Products.PageTemplates.ZopePageTemplate.ZopePageTemplate"
-  extends="common_text">
+<mapper name="page_template" extends="common_text"
+  class="Products.PageTemplates.ZopePageTemplate.ZopePageTemplate">
  <serializer name="text"
     factory="apelib.core.serializers.StringDataAttribute('_text')" />
  <serializer name="bindings"
     factory="apelib.core.serializers.IgnoredAttribute('_bind_names')" />
- <use-for extensions=".html .htm .zpt .pt" />
 </mapper>
+<store class="Products.PageTemplates.ZopePageTemplate.ZopePageTemplate"
+  using="page_template" default-extension=".html" />
+<load extensions=".html .htm .zpt .pt" using="page_template" />
 
 <!-- DTML Document -->
 
-<mapper name="OFS.DTMLDocument.DTMLDocument"
-  extends="common_text">
+<mapper name="dtml_document" extends="common_text"
+  class="OFS.DTMLDocument.DTMLDocument">
  <serializer name="text"
    factory="apelib.core.serializers.StringDataAttribute('raw')" />
 </mapper>
+<store class="OFS.DTMLDocument.DTMLDocument" using="dtml_document" 
+  default-extension=".dtml" />
 
 <!-- DTML Method -->
 
-<mapper name="OFS.DTMLMethod.DTMLMethod"
-  extends="OFS.DTMLDocument.DTMLDocument">
+<mapper name="dtml_method" extends="dtml_document"
+  class="OFS.DTMLMethod.DTMLMethod">
  <serializer name="properties" enabled="false" />
  <gateway name="properties" enabled="false" />
- <use-for extensions=".dtml" />
 </mapper>
+<store class="OFS.DTMLMethod.DTMLMethod" using="dtml_method"
+  default-extension=".dtml" />
+<load extensions=".dtml" using="dtml_method" />
 
 <!-- ZSQL Method -->
 
-<mapper name="Products.ZSQLMethods.SQL.SQL" extends="common_text">
+<mapper name="zsql" extends="common_text"
+  class="Products.ZSQLMethods.SQL.SQL">
  <serializer name="properties"
    factory="apelib.zope2.scripts.ZSQLMethodPropertiesSerializer" />
  <serializer name="text"
    factory="apelib.zope2.scripts.ZSQLMethodSerializer" />
- <use-for extensions=".sql" />
 </mapper>
+<store class="Products.ZSQLMethods.SQL.SQL" using="zsql"
+  default-extension=".sql" />
+<load extensions=".sql" using="zsql" />
 
 <!-- Python Script -->
 
-<mapper name="Products.PythonScripts.PythonScript.PythonScript"
-  extends="common_text">
+<mapper name="python_script" extends="common_text"
+  class="Products.PythonScripts.PythonScript.PythonScript">
  <serializer name="properties" enabled="false" />
  <gateway name="properties" enabled="false" />
  <serializer name="text"
    factory="apelib.zope2.scripts.PythonScriptSerializer" />
- <use-for extensions=".py" />
 </mapper>
+<store class="Products.PythonScripts.PythonScript.PythonScript"
+  using="python_script" default-extension=".py" />
+<load extensions=".py" using="python_script" />
 
 <!-- User Folder -->
 
-<mapper name="AccessControl.User.UserFolder" extends="common">
+<mapper name="user_folder" extends="common"
+  class="AccessControl.User.UserFolder">
  <serializer name="data"
    factory="apelib.zope2.security.UserFolderSerializer" />
  <variation name="filesystem">
@@ -215,22 +232,23 @@
   <gateway name="data" factory="apelib.sql.security.SQLUserList" />
  </variation>
 </mapper>
+<store exact-class="AccessControl.User.UserFolder"
+  using="user_folder" />
 
-<!-- Mapper of anything that looks like a folder -->
+<!-- Mapper of ObjectManagers -->
 
-<mapper name="anyfolder" class="none" extends="folder">
- <serializer
-   factory="apelib.core.serializers.AnyObjectSerializer" />
+<mapper name="anyfolder" extends="folder"
+  class="OFS.ObjectManager.ObjectManager">
  <serializer name="properties"
    factory="apelib.zope2.ofsserial.OptionalOFSProperties" />
- <use-for generic="folder_object" />
 </mapper>
+<store class="OFS.ObjectManager.ObjectManager"
+  using="anyfolder" />
 
-<!-- Mapper of anything that looks like a file -->
+<!-- Mapper of SimpleItems -->
 
-<mapper name="anyfile" class="none" extends="common_p">
- <serializer
-   factory="apelib.core.serializers.AnyObjectSerializer" />
+<mapper name="anyfile" extends="common_p"
+  class="OFS.SimpleItem.Item">
  <serializer name="properties"
    factory="apelib.zope2.ofsserial.OptionalOFSProperties" />
  <variation name="filesystem">
@@ -241,27 +259,49 @@
   <gateway name="remainder"
     factory="apelib.sql.structure.SQLObjectData" />
  </variation>
- <use-for generic="file_object" />
 </mapper>
+<store class="OFS.SimpleItem.SimpleItem" using="anyfile" />
 
 <!-- Application -->
 
-<mapper name="OFS.Application.Application" extends="folder">
+<mapper name="application" extends="folder"
+  class="OFS.Application.Application">
  <serializer name="id" enabled="false" />
  <gateway name="id" enabled="false" />
- <use-for generic="basepath" />
 </mapper>
+<store class="OFS.Application.Application" using="application" />
+<load generic="basepath" using="application" />
+
+<!-- Compatibility with former mapper names. -->
+
+<load mapper-name="OFS.Folder.Folder" using="folder" />
+<load mapper-name="OFS.Image.File" using="file" />
+<load mapper-name="OFS.Image.Image" using="image" />
+<load mapper-name="Products.PageTemplates.ZopePageTemplate.ZopePageTemplate"
+   using="page_template" />
+<load mapper-name="OFS.DTMLDocument.DTMLDocument" using="dtml_document" />
+<load mapper-name="OFS.DTMLMethod.DTMLMethod" using="dtml_method" />
+<load mapper-name="Products.ZSQLMethods.SQL.SQL" using="zsql" />
+<load mapper-name="Products.PythonScripts.PythonScript.PythonScript"
+  using="python_script" />
+<load mapper-name="AccessControl.User.UserFolder" using="user_folder" />
+<load mapper-name="OFS.Application.Application" using="application" />
 
 <!-- Workarounds for objects that don't work with the anyfolder
 mapper, but do fine with anyfile. -->
 
-<mapper name="anyfile">
- <use-for class="Products.CMFCore.SkinsTool.SkinsTool" />
- <use-for class="App.ApplicationManager.ApplicationManager" />
-</mapper>
+<store class="Products.CMFCore.SkinsTool.SkinsTool" using="anyfile" />
+<store class="App.ApplicationManager.ApplicationManager" using="anyfile" />
+
+<!-- Here is a special mapper that launches the Python debugger, pdb,
+  just before serialization or deserialization. -->
 
-<!-- Compatibility with former mapper names -->
-<mapper name="OFS.Folder.Folder" extends="folder" register-class="false" />
-<mapper name="OFS.Image.File" extends="file" register-class="false" />
+<!--
+<mapper name="pdbfile" extends="anyfile">
+ <serializer factory="apelib.core.serializers.PDBSerializer" />
+</mapper>
+<store class="Products.GroupUserFolder.GRUFFolder.GRUFUsers"
+  using="pdbfile" />
+-->
 
 </configuration>


=== Products/Ape/lib/apelib/zope2/classifier.py 1.10 => 1.11 ===
--- Products/Ape/lib/apelib/zope2/classifier.py:1.10	Thu Apr 15 19:10:57 2004
+++ Products/Ape/lib/apelib/zope2/classifier.py	Fri Jul 23 04:37:35 2004
@@ -19,7 +19,6 @@
 from mimetypes import guess_extension
 
 from Acquisition import aq_base
-from OFS.ObjectManager import ObjectManager
 
 from apelib.core.interfaces import IConfigurableClassifier
 from apelib.core.interfaces import ClassificationError
@@ -38,8 +37,7 @@
     'application/octet-stream': '',  # No extension--too overloaded.
     }
 
-generic_classifications = (
-    'directory', 'file', 'folder_object', 'file_object', 'basepath')
+generic_classifications = ('directory', 'file', 'basepath')
 
 
 class Classifier:
@@ -50,74 +48,91 @@
 
     def __init__(self, gw=None):
         self.gateway = gw
-        self.oid_to_mapper = {}
-        self.class_to_mapper = {}
-        self.ext_to_mapper = {}
-        self.generic_to_mapper = {}
-        self.options = {}  # { (mapper_name, option) -> value }
+        self.store_map = {}  # { class_name -> { mapper name, other options } }
+        self.load_alias_map = {}  # { old mapper name -> new mapper name }
+        self.load_ext_map = {}  # { '.ext' -> mapper name }
+        self.load_generic_map = {}  # { keyword -> mapper name }
+
+    def add_store_rule(self, class_name, mapper_name, exact=False,
+                       default_extension=None, default_extension_source=None):
+        self.store_map[class_name] = {
+            'mapper_name': mapper_name,
+            'exact': exact,
+            'default_extension': default_extension,
+            'default_extension_source': default_extension_source,
+            }
 
-    def register(self, condition, value, mapper_name):
+    def add_load_rule(self, criterion, value, mapper_name):
         value = str(value)  # Avoid unicode
-        if condition == 'extension':
-            self.ext_to_mapper[value] = mapper_name
-        elif condition == 'class':
-            self.class_to_mapper[value] = mapper_name
-        elif condition == 'generic':
-            assert value in generic_classifications, value
-            self.generic_to_mapper[value] = mapper_name
-        elif condition == 'oid':
-            self.oid_to_mapper[value] = mapper_name
+        if criterion == 'mapper-name':
+            self.load_alias_map[value] = mapper_name
+        elif criterion == 'extension':
+            self.load_ext_map[value] = mapper_name
+        elif criterion == 'generic':
+            self.load_generic_map[value] = mapper_name
         else:
-            raise ValueError('Unknown classification condition: %s'
-                             % repr(condition))
+            raise ValueError('Unknown classification criterion: %s'
+                             % repr(criterion))
 
-    def set_option(self, mapper_name, option, value):
-        assert option in ('default_extension', 'content_type_attr'), option
-        self.options[(mapper_name, option)] = value
+
+    def find_class_mapper(self, event, klass, is_base=False):
+        """Searches for a mapper of a given class, including base classes.
+
+        Returns a value in store_map or None.
+        """
+        try:
+            class_name = '%s.%s' % (klass.__module__, klass.__name__)
+        except AttributeError:
+            return None
+        d = self.store_map.get(class_name)
+        if d is not None:
+            if is_base and d.get('exact'):
+                # this rule doesn't want subclasses.
+                d = None
+        if d is None:
+            for base in klass.__bases__:
+                d = self.find_class_mapper(event, base, is_base=True)
+                if d is not None:
+                    break
+        return d
 
 
     def classify_object(self, event):
-        """Chooses a mapper and classification for storing an object.
+        """Chooses a classification, including a mapper, for storing an object.
         """
-        mapper_name = self.oid_to_mapper.get(event.oid)
-        if mapper_name is not None:
-            return {'mapper_name': mapper_name}
+        if event.oid == event.conf.oid_gen.root_oid:
+            # Use the root mapper if one is configured.
+            mapper_name = self.load_generic_map.get('root')
+            if mapper_name is not None:
+                return {'mapper_name': mapper_name}
         klass = event.obj.__class__
         class_name = '%s.%s' % (klass.__module__, klass.__name__)
         classification = {'class_name': class_name}
-        mapper_name = self.class_to_mapper.get(class_name)
-        if mapper_name is None:
-            folderish = isinstance(event.obj, ObjectManager)
-            # Store in a generic format
-            if folderish:
-                generic = 'folder_object'
-            else:
-                generic = 'file_object'
-            mapper_name = self.generic_to_mapper.get(generic)
-        if mapper_name is None:
+        opts = self.find_class_mapper(event, klass)
+        if opts is None:
             raise ClassificationError(
                 'No mapper known for class %s' % repr(class_name))
-        cta = self.options.get((mapper_name, 'content_type_attr'))
-        if cta is not None:
-            ct = str(getattr(aq_base(event.obj), cta, None))
+        classification['mapper_name'] = opts['mapper_name']
+        if opts.get('default_extension_source') == 'content_type':
+            ct = str(getattr(aq_base(event.obj), 'content_type', None))
             ext = fixed_extensions.get(ct)
             if ext is None:
                 ext = guess_extension(ct)
         else:
-            ext = self.options.get((mapper_name, 'default_extension'))
+            ext = opts.get('default_extension')
         if ext:
             classification['extension'] = ext
-
-        classification['mapper_name'] = mapper_name
         return classification
 
 
     def classify_state(self, event):
-        """Chooses a mapper and classification for loading an object.
+        """Chooses a classification, including a mapper, for loading an object.
         """
-        mapper_name = self.oid_to_mapper.get(event.oid)
-        if mapper_name is not None:
-            return {'mapper_name': mapper_name}
+        if event.oid == event.conf.oid_gen.root_oid:
+            # Use the root mapper if one is configured.
+            mapper_name = self.load_generic_map.get('root')
+            if mapper_name is not None:
+                return {'mapper_name': mapper_name}
         classification, serial = self.gateway.load(event)
         class_name = classification.get('class_name')
         if class_name and ':' in class_name:
@@ -125,38 +140,39 @@
             class_name = class_name.replace(':', '.')
             classification['class_name'] = class_name
         mapper_name = classification.get('mapper_name')
+        if mapper_name is not None:
+            # Possibly update to a new mapper name
+            mapper_name = self.load_alias_map.get(
+                mapper_name, mapper_name)
         if mapper_name is None:
             # The choice of mapper is not stored explicitly.  Choose
             # one based on several criteria.
-            if 1:
+            if False:
                 # bw compat: look for certain meta_types.
                 mt = classification.get('meta_type')
                 if mt == '(folderish object)':
-                    mapper_name = self.generic_to_mapper.get('folder_object')
+                    mapper_name = 'anyfolder'
                 elif mt == '(fileish object)':
-                    mapper_name = self.generic_to_mapper.get('file_object')
-            if mapper_name is None:
-                if class_name is not None:
-                    mapper_name = self.class_to_mapper.get(class_name)
+                    mapper_name = 'anyfile'
             if mapper_name is None:
                 subpath = classification.get('subpath')
                 if subpath is not None and not subpath:
                     # Application base
-                    mapper_name = self.generic_to_mapper.get('basepath')
+                    mapper_name = self.load_generic_map.get('basepath')
             if mapper_name is None:
                 t = classification.get('node_type')
                 if t == 'd':
                     # Directory
-                    mapper_name = self.generic_to_mapper.get('directory')
+                    mapper_name = self.load_generic_map.get('directory')
                 elif t == 'f':
                     # File
                     ext = classification.get('extension')
                     if ext:
                         if not ext.startswith('.'):
                             ext = '.' + ext
-                        mapper_name = self.ext_to_mapper.get(ext.lower())
+                        mapper_name = self.load_ext_map.get(ext.lower())
                     if not mapper_name:
-                        mapper_name = self.generic_to_mapper.get('file')
+                        mapper_name = self.load_generic_map.get('file')
         if mapper_name is None:
             raise ClassificationError(
                 'No mapper known for oid %s' % repr(event.oid))



More information about the Zope-CVS mailing list