[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