[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