[Zope-CVS] CVS: Products/Ape/lib/apelib/config - apeconf.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/config
In directory cvs.zope.org:/tmp/cvs-serv26116/lib/apelib/config

Modified Files:
	apeconf.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/config/apeconf.py 1.7 => 1.8 ===
--- Products/Ape/lib/apelib/config/apeconf.py:1.7	Sat Mar 20 01:34:21 2004
+++ Products/Ape/lib/apelib/config/apeconf.py	Fri Jul 23 04:37:34 2004
@@ -19,7 +19,7 @@
 from types import TupleType
 
 from apelib.core.mapper import Mapper, MapperConfiguration
-from apelib.core.serializers import CompositeSerializer, AnyObjectSerializer
+from apelib.core.serializers import CompositeSerializer
 from apelib.core.gateways import CompositeGateway
 from apelib.core.interfaces import IDatabaseInitializer, ConfigurationError
 
@@ -27,22 +27,16 @@
 from common import Directive, DirectiveReader, ComponentSystem
 
 
-class MapperDeclaration(Directive):
+class MapperDefinition(Directive):
     schema = TableSchema()
     schema.add('mapper_name', primary=1)
-
-
-class MapperAttribute(Directive):
-    schema = TableSchema()
-    schema.add('mapper_name', primary=1)
-    # Attribute names: 'class', 'extends', 'register-class'
-    schema.add('name', primary=1, indexed=1)
-    schema.add('value')
+    schema.add('extends')
+    schema.add('class_name')
 
 
 class ComponentDefinition(Directive):
     schema = TableSchema()
-    # comptypes: 'serializer', 'gateway', 'classifier', 'oid_generator'
+    # comptypes: 'classifier', 'oid_generator'
     schema.add('comptype', primary=1)
     schema.add('name', primary=1)
     schema.add('producer')
@@ -51,24 +45,28 @@
 class MapperComponent(Directive):
     schema = TableSchema()
     schema.add('mapper_name', primary=1)
+    # comptypes: 'serializer', 'gateway'
     schema.add('comptype', primary=1)
     schema.add('name', primary=1)
     schema.add('producer')
     schema.add('order')
 
 
-class MapperUseFor(Directive):
+class StoreUsing(Directive):
     schema = TableSchema()
-    schema.add('mapper_name', primary=1)
-    schema.add('attr', primary=1)
-    schema.add('value', primary=1)  # Multiple values allowed
+    schema.add('class_name', primary=1)
+    schema.add('use_mapper')
+    schema.add('exact')  # boolean
+    schema.add('default_extension')
+    schema.add('default_extension_source')
 
 
-class ClassifierOption(Directive):
+class LoadUsing(Directive):
     schema = TableSchema()
-    schema.add('mapper_name', primary=1)
-    schema.add('option', primary=1)
-    schema.add('value')
+    # Criterion is 'extension', 'mapper-name', or 'generic'
+    schema.add('criterion', primary=1)
+    schema.add('value', primary=1)
+    schema.add('use_mapper')
 
 
 class DisabledProducer:
@@ -79,16 +77,6 @@
         return None
 
 
-class UseProducer:
-    def __init__(self, source, comptype, name):
-        self.source = source
-        self.comptype = comptype
-        self.name = name
-
-    def __call__(self, compsys):
-        return compsys.get(self.comptype, self.name)
-
-
 class FactoryProducer:
 
     def __init__(self, source, factory):
@@ -129,14 +117,10 @@
 def make_producer(source, comptype, attrs, raise_exc=1):
     if attrs.get('enabled', '').lower() == 'false':
         return DisabledProducer(source)
-    elif attrs.has_key('use'):
-        if attrs.has_key('factory'):
-            raise ValueError, "Both 'use' and 'factory' not allowed"
-        return UseProducer(source, comptype, attrs['use'])
     elif attrs.has_key('factory'):
         return FactoryProducer(source, attrs['factory'])
     elif raise_exc:
-        raise ValueError, "Need a 'use', 'factory', or 'enabled' attribute"
+        raise ValueError, "Need a 'factory' or 'enabled' attribute"
     else:
         return None
 
@@ -161,29 +145,17 @@
     def handle_mapper(source, vars, attrs):
         d = vars['directives']
         mapper_name = str(attrs['name'])
+        extends = str(attrs.get('extends', ''))
+        class_name = str(attrs.get('class', ''))
         vars['mapper_name'] = mapper_name
-        d.append(MapperDeclaration(source, mapper_name))
-        for key in ('class', 'extends', 'register-class'):
-            if attrs.has_key(key):
-                value = attrs[key]
-                if (key == 'register-class'):
-                    if value.lower() == 'true':
-                        value = True
-                    elif value.lower() == 'false':
-                        value = False
-                    else:
-                        raise ValueError("Value must be true or false")
-                d.append(MapperAttribute(source, mapper_name, key, value))
+        d.append(MapperDefinition(source, mapper_name, extends, class_name))
 
     def handle_mapper_component(source, vars, attrs, comptype):
         d = vars['directives']
         producer = make_producer(source, comptype, attrs)
         mapper_name = vars.get('mapper_name')
         if mapper_name is None:
-            # Reusable component
-            name = attrs['name']
-            directive = ComponentDefinition(
-                source, comptype, name, producer)
+            raise ValueError('Not inside a mapper tag')
         else:
             # Composite component of a mapper
             name = attrs.get('name', '')
@@ -202,8 +174,7 @@
             # Set a gateway for a classifier.
             if not hasattr(p, 'sub_producer'):
                 raise ValueError(
-                    "Classifier at %s needs a factory in order to "
-                    "use a gateway" % source)
+                    "Classifier at %s needs a factory" % source)
             if p.sub_producer is not None:
                 raise ValueError(
                     "Multiple gateways in classifiers not allowed at %s" %
@@ -225,11 +196,32 @@
         directive = ComponentDefinition(source, 'oid_generator', '', producer)
         d.append(directive)
 
-    def handle_use_for(source, vars, attrs):
+    def handle_store(source, vars, attrs):
+        d = vars['directives']
+        cn = attrs.get('class')
+        ecn = attrs.get('exact-class')
+        if cn and ecn or not cn and not ecn:
+            raise ValueError("One of 'class' or 'exact-class' is required")
+        mapper_name = str(attrs['using'])
+        def_ext = attrs.get('default-extension')
+        def_ext_src = attrs.get('default-extension-source')
+        if def_ext and def_ext_src:
+            raise ValueError(
+                "Only one of 'default-extension' "
+                "or 'default-extension-source' is allowed")
+        directive = StoreUsing(
+            source, cn or ecn, mapper_name, bool(ecn), def_ext, def_ext_src)
+        d.append(directive)
+
+    def handle_load(source, vars, attrs):
         d = vars['directives']
-        mapper_name = vars['mapper_name']
-        for attr in ('class', 'extensions', 'generic', 'oid'):
+        mapper_name = str(attrs['using'])
+        criterion = None
+        for attr in ('mapper-name', 'extensions', 'generic'):
             if attrs.has_key(attr):
+                if criterion is not None:
+                    raise ValueError("Only one criterion allowed")
+                criterion = attr
                 v = attrs[attr]
                 if attr == 'extensions':
                     first = 1
@@ -237,24 +229,11 @@
                         if not ext.startswith('.'):
                             ext = '.' + ext
                         ext = ext.lower()
-                        d.append(MapperUseFor(
-                            source, mapper_name, 'extension', ext))
-                        if first:
-                            # Use a classifier option to set the default
-                            # extension.
-                            first = 0
-                            d.append(ClassifierOption(
-                                source, mapper_name, 'default_extension', ext))
+                        d.append(LoadUsing(
+                            source, 'extension', ext, mapper_name))
                 else:
-                    d.append(MapperUseFor(
-                        source, mapper_name, attr, v))
-
-    def handle_option(source, vars, attrs):
-        d = vars['directives']
-        mapper_name = vars['mapper_name']
-        name = attrs['name']
-        value = attrs['value']
-        d.append(ClassifierOption(source, mapper_name, name, value))
+                    d.append(LoadUsing(
+                        source, attr, v, mapper_name))
 
     handlers = {
         'configuration': handle_configuration,
@@ -264,8 +243,8 @@
         'gateway':       handle_gateway,
         'classifier':    handle_classifier,
         'oid-generator': handle_oid_generator,
-        'use-for':       handle_use_for,
-        'option':        handle_option,
+        'store':        handle_store,
+        'load':       handle_load,
         }
 
     return handlers
@@ -290,7 +269,8 @@
         self.producer = producer
 
     def create(self):
-        return self.producer(self.compsys)
+        self.obj = self.producer(self.compsys)
+        return self.obj
 
     def configure(self):
         pass
@@ -303,15 +283,16 @@
         self.compsys = compsys
         dtables = compsys.dtables
         self.mapper_name = name
-        if not dtables.query(MapperDeclaration, mapper_name=name):
+        recs = dtables.query(MapperDefinition, mapper_name=name)
+        if not recs:
             raise ConfigurationError("No mapper named %s exists" % repr(name))
+        self.directive = recs[0]
         self.subobjs = []  # all subobjects
-        self.attrs = {}
-        for record in dtables.query(MapperAttribute, mapper_name=name):
-            self.attrs[record.name] = record.value
         self._prepare_sub_components()
 
     def _prepare_sub_components(self):
+        """Populates multi_comps with components to be used in this mapper.
+        """
         self.multi_comps = {}  # comptype -> name -> record
         dtables = self.compsys.dtables
         name = self.mapper_name
@@ -324,7 +305,7 @@
                 d = self.multi_comps.setdefault(r.comptype, {})
                 d.setdefault(r.name, r)
             name = dtables.query_field(
-                MapperAttribute, 'value', mapper_name=name, name='extends')
+                MapperDefinition, 'extends', mapper_name=name)
             if name and name in all_names:
                 raise ConfigurationError(
                     "Circular extension in mappers %s" % repr(all_names))
@@ -334,23 +315,13 @@
         return self.obj
 
     def configure(self):
+        self.obj.name = self.mapper_name
+        self.obj.class_name = self.directive.class_name or ''
         self.add_serializers()
         self.add_gateways()
         self.add_initializers()
 
     def add_serializers(self):
-        cname = self.attrs.get('class')
-        if cname == 'none':
-            # This mapper is abstract (usable for no classes)
-            module, name = None, None
-        else:
-            # This mapper is concrete (usable for one class only)
-            if cname is None:
-                cname = self.mapper_name
-            pos = cname.rfind('.')
-            if pos < 0:
-                raise ConfigurationError("Class name must include a module name")
-            module, name = cname[:pos], cname[pos + 1:]
         d = self.multi_comps.get('serializer', {})
 
         # Create the main serializer
@@ -358,7 +329,7 @@
         if r:
             s = r.producer(self.compsys)
         else:
-            s = CompositeSerializer(module, name)
+            s = CompositeSerializer()
 
         # Create the contained serializers
         ordered = [
@@ -409,64 +380,15 @@
         assert name == '', name
         BasicComponentAssembler.__init__(self, compsys, comptype, name)
 
-    def create(self):
-        self.obj = BasicComponentAssembler.create(self)
-        return self.obj
-
     def configure(self):
         dtables = self.compsys.dtables
-        all_regs = {}     # { (attr, value) -> mapper_name }
-        all_options = {}  # { (mapper_name, option) -> value }
-        mapper_names = [r.mapper_name for r in
-                        dtables.query(MapperDeclaration)]
-        # Gather classification options from each mapper configuration.
-        for name in mapper_names:
-            # use-for directives
-            records = dtables.query(
-                MapperUseFor, mapper_name=name)
-            for r in records:
-                key = ((r.attr, r.value))
-                if all_regs.has_key(key) and all_regs[key] != name:
-                    raise ConfigurationError(
-                        "Mappers %s and %s are contending over %s == %s" % (
-                        name, all_regs[key],
-                        repr(r.attr), repr(r.value)))
-                all_regs[key] = name
-
-            register_class = dtables.query_field(
-                MapperAttribute, 'value', mapper_name=name,
-                name='register-class')
-            if register_class or register_class is None:
-                # Create an implicit 'use-for class' directive
-                class_name = dtables.query_field(
-                    MapperAttribute, 'value', mapper_name=name, name='class')
-                if class_name is None:
-                    class_name = name
-                elif class_name == 'none':
-                    class_name = None
-                if class_name is not None:
-                    key = ('class', class_name)
-                    if all_regs.has_key(key) and all_regs[key] != name:
-                        raise ConfigurationError(
-                            "Mappers %s and %s are contending over %s == %s" % (
-                            name, all_regs[key],
-                            "'class'", repr(class_name)))
-                    all_regs[key] = name
-
-            # options
-            records = dtables.query(
-                ClassifierOption, mapper_name=name)
-            for r in records:
-                all_options[(name, r.option)] = r.value
+        for r in dtables.query(StoreUsing):
+            self.obj.add_store_rule(
+                r.class_name, r.use_mapper, r.exact,
+                r.default_extension, r.default_extension_source)
+        for r in dtables.query(LoadUsing):
+            self.obj.add_load_rule(r.criterion, r.value, r.use_mapper)
 
-        # Perform the registrations.
-        if all_regs or all_options:
-            for (attr, value), name in all_regs.items():
-                self.obj.register(attr, value, name)
-            for (name, option), value in all_options.items():
-                self.obj.set_option(name, option, value)
-            
-        
 
 def configure(filenames, vname=''):
     """Returns a MapperConfiguration built from configuration files.
@@ -479,10 +401,9 @@
     cs = ComponentSystem(directives)
     cs.add_component_type('mapper', MapperAssembler)
     cs.add_component_type('classifier', ClassifierAssembler)
-    for comptype in ('serializer', 'gateway', 'oid_generator'):
-        cs.add_component_type(comptype, BasicComponentAssembler)
+    cs.add_component_type('oid_generator', BasicComponentAssembler)
     mappers = {}
-    for record in cs.dtables.query(MapperDeclaration):
+    for record in cs.dtables.query(MapperDefinition):
         name = record.mapper_name
         mappers[name] = cs.get('mapper', name)
     classifier = cs.get('classifier', '')



More information about the Zope-CVS mailing list