[Checkins] SVN: zope.generic/trunk/src/zope/generic/ new multiInformationProvider directive.

Dominik Huber dominik.huber at perse.ch
Thu May 4 14:56:15 EDT 2006


Log message for revision 67971:
  new multiInformationProvider directive.
  remove informations subdirective of the informationProvider directive

Changed:
  U   zope.generic/trunk/src/zope/generic/configuration/api.py
  U   zope.generic/trunk/src/zope/generic/configuration/base.py
  U   zope.generic/trunk/src/zope/generic/configuration/helper.py
  U   zope.generic/trunk/src/zope/generic/configuration/tests.py
  U   zope.generic/trunk/src/zope/generic/face/api.py
  U   zope.generic/trunk/src/zope/generic/face/helper.py
  U   zope.generic/trunk/src/zope/generic/face/tests.py
  U   zope.generic/trunk/src/zope/generic/informationprovider/README.txt
  U   zope.generic/trunk/src/zope/generic/informationprovider/meta.zcml
  U   zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py
  U   zope.generic/trunk/src/zope/generic/informationprovider/metadirectives.py

-=-
Modified: zope.generic/trunk/src/zope/generic/configuration/api.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/api.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/configuration/api.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -24,6 +24,7 @@
 from zope.generic.configuration.helper import configuratonToDict
 from zope.generic.configuration.helper import getValue
 from zope.generic.configuration.helper import requiredInOrder
+from zope.generic.configuration.helper import toConfigFaceTriple
 
 
 

Modified: zope.generic/trunk/src/zope/generic/configuration/base.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/base.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/configuration/base.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -24,6 +24,8 @@
 
 from zope.interface import directlyProvides
 from zope.interface import implements
+from zope.schema.interfaces import IObject
+from zope.schema.interfaces import IContainer
 
 from zope.generic.face import IAttributeFaced
 from zope.generic.face import IFace
@@ -32,10 +34,64 @@
 
 from zope.generic.configuration import IConfigurationData
 from zope.generic.configuration import IConfigurations
+from zope.generic.configuration import IConfigurationType
 from zope.generic.configuration.helper import getValue
 
 
 
+def subData(name, data):
+    """Return a subdata dict and remove the subdata from the given data dict.
+
+    Example 1:
+
+        >>> data = {'x.y': 2, 'x.a': 3, 'x.': 4, 'xa': 5}
+        >>> subData('x', data)
+        {'y': 2, 'a': 3}
+    """
+
+    subdata = {}
+
+    prefix = name + '.'
+    prefix_len = len(name + '.')
+
+    # add subdata to dictionary
+    for key, value in data.items():
+        if len(key) > prefix_len and key.startswith(prefix):
+            subdata[key[prefix_len:]] = value
+            del data[key]
+
+    return subdata
+
+
+
+def prepareData(__keyface__, data):
+    """Nested configuration support."""
+    missedArguments = []
+    for name in __keyface__:
+        # forget missing but none-required
+        if name not in data:
+            field = __keyface__[name]
+            # handle nested configuration data
+            if IObject.providedBy(field) and IConfigurationType.providedBy(field.schema):
+                try:
+                    subdata = subData(name, data)
+                    if subdata or field.required is True:
+                        data[name] = ConfigurationData(field.schema, subData(name, data))
+                        break
+
+                except:
+                    pass
+
+            if field.required is True:
+                missedArguments.append(name)
+    
+    if missedArguments:
+        raise TypeError("__init__ requires '%s' of '%s'." % (', '.join(missedArguments), __keyface__.__name__))
+    
+    return data
+    
+
+
 _marker = object()
 
 class ConfigurationData(Persistent):
@@ -117,19 +173,8 @@
     implements(IAttributeFaced, IConfigurationData)
 
     def __init__(self, __keyface__, data):
-        # preconditions
-        missedArguments = []
-        for name in __keyface__:
-            if name not in data:
-                field = __keyface__[name]
-                if field.required is True:
-                    missedArguments.append(name)
-        
-        if missedArguments:
-            raise TypeError("__init__ requires '%s' of '%s'." % (', '.join(missedArguments), __keyface__.__name__))
-    
         # essentials
-        self.__dict__['_ConfigurationData__data'] = PersistentDict(data)
+        self.__dict__['_ConfigurationData__data'] = PersistentDict(prepareData(__keyface__, data))
         self.__dict__['__keyface__'] = __keyface__
         self.__dict__['__conface__'] = IUndefinedContext
         directlyProvides(self, __keyface__)

Modified: zope.generic/trunk/src/zope/generic/configuration/helper.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/helper.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/configuration/helper.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -22,6 +22,10 @@
 from zope.schema.interfaces import IField
 
 from zope.generic.face import IFace
+from zope.generic.face import IUndefinedContext
+from zope.generic.face import IUndefinedKeyface
+from zope.generic.face.api import toFaceTuple
+from zope.generic.face.api import toInterface
 
 
 
@@ -69,11 +73,11 @@
 
         >>> from zope.generic.configuration.base import ConfigurationData
         >>> configuration = ConfigurationData(IAnyConfiguration, {'a': 'a bla'})
-        >>> configuratonToDict(configuration)
+        >>> api.configuratonToDict(configuration)
         {'a': 'a bla'}
 
     Including defaults:
-        >>> configuratonToDict(configuration, all=True)
+        >>> api.configuratonToDict(configuration, all=True)
         {'a': 'a bla', 'c': u'c default', 'b': None}
 
     """
@@ -121,9 +125,36 @@
         ...    c = TextLine(required=False, readonly=True, default=u'c bla')
         ...    d = TextLine()
 
-        >>> requiredInOrder(IAnyConfiguration)
+        >>> api.requiredInOrder(IAnyConfiguration)
         ['a', 'd']
     
     """
     
     return [name for name in configuration if configuration[name].required is True]
+
+
+
+def toConfigFaceTriple(identifier):
+    """Split configface:keyface at conface to (configface, keyface, conface).
+
+        >>> from zope.interface import Interface
+        
+        >>> class IA(Interface):
+        ...     pass
+
+        >>> from zope.generic.face.api import toDottedName
+
+        >>> api.toConfigFaceTriple(toDottedName(IA) + ':')
+        (<InterfaceClass example.IA>, <....IUndefinedKeyface>, <....IUndefinedContext>)
+    """
+
+    parts = identifier.split(':')
+
+    if len(parts) == 1:
+        return (toInterface(parts[0]), IUndefinedKeyface, IUndefinedContext)
+
+    else:
+        keyface, conface = toFaceTuple(parts[1])
+        return (toInterface(parts[0]), keyface, conface)
+            
+        

Modified: zope.generic/trunk/src/zope/generic/configuration/tests.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/tests.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/configuration/tests.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -65,7 +65,6 @@
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(ConfigurationDataTest),
-        doctest.DocTestSuite('zope.generic.configuration.helper'),
         doctest.DocTestSuite('zope.generic.configuration.base'),
         doctest.DocTestSuite('zope.generic.configuration.event'),
         doctest.DocTestSuite('zope.generic.configuration.adapter',
@@ -76,6 +75,14 @@
                              'testing': testing, 'api': api},
                              optionflags=doctest.NORMALIZE_WHITESPACE+
                                             doctest.ELLIPSIS),
+        doctest.DocTestSuite('zope.generic.configuration.helper',
+                             setUp=testing.placelesssetup.setUp,
+                             tearDown=testing.placelesssetup.tearDown,
+                             globs={'component': component, 'interface': interface,
+                             'registerDirective': registerDirective,
+                             'testing': testing, 'api': api},
+                             optionflags=doctest.NORMALIZE_WHITESPACE+
+                                            doctest.ELLIPSIS),
         doctest.DocFileSuite('README.txt',
                              setUp=testing.placelesssetup.setUp,
                              tearDown=testing.placelesssetup.tearDown,

Modified: zope.generic/trunk/src/zope/generic/face/api.py
===================================================================
--- zope.generic/trunk/src/zope/generic/face/api.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/face/api.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -23,6 +23,7 @@
 from zope.generic.face.base import Face
 from zope.generic.face.helper import toDescription
 from zope.generic.face.helper import toDottedName
+from zope.generic.face.helper import toFaceTuple
 from zope.generic.face.helper import toInterface
 
 

Modified: zope.generic/trunk/src/zope/generic/face/helper.py
===================================================================
--- zope.generic/trunk/src/zope/generic/face/helper.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/face/helper.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -19,6 +19,8 @@
 __docformat__ = 'restructuredtext'
 
 from zope.dottedname.resolve import resolve
+from zope.generic.face import IUndefinedContext
+from zope.generic.face import IUndefinedKeyface
 
 
 
@@ -48,7 +50,7 @@
         >>> class A:
         ...     pass
 
-        >>> label, hint = toDescription(A)
+        >>> label, hint = api.toDescription(A)
         >>> label
         u''
         >>> hint
@@ -60,7 +62,7 @@
         ...    '''Test label.   
         ...    '''
 
-        >>> label, hint = toDescription(B)
+        >>> label, hint = api.toDescription(B)
         >>> label
         u'Test label.'
         >>> hint
@@ -75,7 +77,7 @@
         ...    bla bla bla:  
         ...         - bla, bla, bla.   '''
 
-        >>> label, hint = toDescription(C)
+        >>> label, hint = api.toDescription(C)
         >>> label
         u'Test label.'
         >>> hint
@@ -84,13 +86,13 @@
     You can overwrite the underlying doc string providing your own
     label or hint:
 
-        >>> label, hint = toDescription(C, label=u'My label')
+        >>> label, hint = api.toDescription(C, label=u'My label')
         >>> label
         u'My label'
         >>> hint
         u'Test hint, bla bla .\\\\nbla bla bla:\\\\n- bla, bla, bla.'
 
-        >>> label, hint = toDescription(C, hint=u'My hint')
+        >>> label, hint = api.toDescription(C, hint=u'My hint')
         >>> label
         u'Test label.'
         >>> hint
@@ -129,4 +131,72 @@
                 else:
                     return (unicode(lines[0].strip()), u'')
 
-        return (u'', u'')   
+        return (u'', u'')
+
+
+
+def toFaceTuple(identifier):
+    """Resolve 'keyface at conface' to (keyface, conface).
+
+        >>> from zope.interface import Interface
+        
+        >>> class IK(Interface):
+        ...     pass
+        
+        >>> class IC(Interface):
+        ...     pass
+
+    Example 1:
+        
+        >>> api.toFaceTuple(api.toDottedName(IK))
+        (<InterfaceClass example.IK>, <....IUndefinedContext>)
+
+    Example 2:
+        
+        >>> api.toFaceTuple('wrong')
+        (<....IUndefinedKeyface>, <....IUndefinedContext>)
+
+    Example 3:
+        
+        >>> api.toFaceTuple(api.toDottedName(IK) + '@' + api.toDottedName(IC))
+        (<InterfaceClass example.IK>, <InterfaceClass example.IC>)
+
+    Example 4:
+        
+        >>> api.toFaceTuple('@' + api.toDottedName(IC))
+        (<....IUndefinedKeyface>, <InterfaceClass example.IC>)
+
+    Example 5:
+        
+        >>> api.toFaceTuple(api.toDottedName(IK) + '@')
+        (<InterfaceClass example.IK>, <....IUndefinedContext>)
+
+    Example 6:
+        
+        >>> api.toFaceTuple('@')
+        (<....IUndefinedKeyface>, <....IUndefinedContext>)
+  
+    """
+    
+    parts = identifier.split('@')
+    
+    if len(parts) == 1:
+        try:
+            return (toInterface(parts[0]), IUndefinedContext)
+
+        except:
+            return (IUndefinedKeyface, IUndefinedContext)
+
+    else:
+        try:
+            keyface = toInterface(parts[0])
+        
+        except:
+            keyface = IUndefinedKeyface
+        
+        try:
+            return (keyface, toInterface(parts[1]))
+        
+        except:
+            return (keyface, IUndefinedContext)
+

Modified: zope.generic/trunk/src/zope/generic/face/tests.py
===================================================================
--- zope.generic/trunk/src/zope/generic/face/tests.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/face/tests.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -61,7 +61,14 @@
         unittest.makeSuite(FaceAdapterTest),
         doctest.DocTestSuite('zope.generic.face.adapter'),
         doctest.DocTestSuite('zope.generic.face.base'),
-        doctest.DocTestSuite('zope.generic.face.helper'),
+        doctest.DocTestSuite('zope.generic.face.helper',
+                             setUp=testing.placelesssetup.setUp,
+                             tearDown=testing.placelesssetup.tearDown,
+                             globs={'component': component, 'interface': interface,
+                             'registerDirective': registerDirective,
+                             'testing': testing, 'api': api},
+                             optionflags=doctest.NORMALIZE_WHITESPACE+
+                                            doctest.ELLIPSIS),
         doctest.DocFileSuite('README.txt',
                              setUp=testing.placelesssetup.setUp,
                              tearDown=testing.placelesssetup.tearDown,

Modified: zope.generic/trunk/src/zope/generic/informationprovider/README.txt
===================================================================
--- zope.generic/trunk/src/zope/generic/informationprovider/README.txt	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/informationprovider/README.txt	2006-05-04 18:56:14 UTC (rev 67971)
@@ -424,6 +424,22 @@
     ...     />
     ... ''') 
 
+    >>> from zope.schema import Dict, Object, Tuple
+
+    >>> class INestedConfiguration(Interface):
+    ...    one = Object(title=u'One configuration', schema=IOneConfiguration)
+    ...    optionalother = Object(title=u'Other configuration', required=False, schema=IOtherConfiguration)
+    ...    requiredother = Object(title=u'Other configuration', schema=IOtherConfiguration)
+    ...    tuple = Tuple(title=u'Tuple of Int', value_type=Int())
+    ...    dict = Dict(title=u'Dict of TextLine', value_type=TextLine())
+
+    >>> registerDirective('''
+    ... <generic:interface
+    ...     interface="example.INestedConfiguration"
+    ...     type="zope.generic.configuration.IConfigurationType"
+    ...     />
+    ... ''') 
+
     >>> import os, tempfile
     >>> temp_dir = tempfile.mkdtemp()
     >>> iniFile = os.path.join(temp_dir, 'example.ini')
@@ -435,32 +451,114 @@
     ... [example.IOtherConfiguration]
     ... bool = True
     ... int = 77
+    ... 
+    ... [example.INestedConfiguration]
+    ... one.textline = Bingo
+    ... one.text = Lotto.
+    ... requiredother.bool = False
+    ... tuple.0 = 2
+    ... tuple.1 = 3
+    ... tuple.2 = 19
+    ... dict.roger = ineichen
+    ... dict.dominik = huber
+    ... dict.daniel = meier
     ... ''')
 
+Such a file can be used for the configuration initializiation. Therefore you
+have to declare one or more files within the iniFiles attribute of the
+informaiton subdirective of the informationProvider direcitve:
+
     >>> registerDirective('''
     ... <generic:informationProvider
     ...     keyface="example.IFoo"
     ...     conface="example.ISpecialContext"
     ...     >
-    ...   <informations
+    ...   <information
     ...       iniFiles="%s"
     ...       />
-    ...     </generic:informationProvider>
+    ... </generic:informationProvider>
     ... ''' % iniFile)
 
-    >>> foo_config = api.getInformation(IOneConfiguration, IFoo, ISpecialContext)
-    >>> foo_config.textLine
+    >>> one_config = api.getInformation(IOneConfiguration, IFoo, ISpecialContext)
+    >>> one_config.textLine
     u'Foo'
 
-    >>> foo_config.text
+    >>> one_config.text
     u'Bla bla bla bla.'
 
-    >>> bar_config =  api.getInformation(IOtherConfiguration, IFoo, ISpecialContext)
-    >>> bar_config.bool
+    >>> other_config =  api.getInformation(IOtherConfiguration, IFoo, ISpecialContext)
+    >>> other_config.bool
     True
 
-    >>> bar_config.int
+    >>> other_config.int
     77
 
+    >>> nested_config = api.getInformation(INestedConfiguration, IFoo, ISpecialContext)
+    >>> nested_config.one.textLine
+    u'Bingo'
+
+    >>> nested_config.one.text
+    u'Lotto.'
+
+    >>> nested_config.optionalother is None
+    True
+
+    >>> nested_config.requiredother.bool
+    False
+
+    >>> nested_config.requiredother.int
+    42
+
+    >>> nested_config.tuple
+    (2, 3, 19)
+
+    >>> nested_config.dict
+    {'dominik': u'huber', 'daniel': u'meier', 'roger': u'ineichen'}
+
+Ini-file based configurations for an multi information provider
+---------------------------------------------------------------
+
+    >>> temp_dir = tempfile.mkdtemp()
+    >>> iniFile = os.path.join(temp_dir, 'example2.ini')
+    >>> open(iniFile, 'w').write('''
+    ... [example.IOneConfiguration:example.IBar at example.IMyContext]
+    ... textline = Gaga
+    ... text : Bla bla bla bla.
+    ... 
+    ... [example.IOneConfiguration:example.IBarFoo at example.IMyContext]
+    ... textline = Gugu
+    ... text : Bla bla bla bla.
+    ... 
+    ... [example.INestedConfiguration:example.IBar at example.IMyContext]
+    ... one.textline = Gogo
+    ... one.text = Lotto.
+    ... requiredother.bool = False
+    ... tuple.0 = 2
+    ... tuple.1 = 3
+    ... tuple.2 = 19
+    ... dict.roger = ineichen
+    ... dict.dominik = huber
+    ... dict.daniel = meier
+    ... ''')
+
+Such a file can be used for the configuration initializiation. Therefore you
+have to declare one or more files within the iniFiles attribute of the
+informaiton directive:
+
+    >>> registerDirective('''
+    ... <generic:multiInformationProviders
+    ...     iniFiles="%s"
+    ...     />
+    ... ''' % iniFile)
+
+    >>> api.getInformation(IOneConfiguration, IBar, IMyContext).textLine
+    u'Gaga'
+
+    >>> api.getInformation(IOneConfiguration, IBarFoo, IMyContext).textLine
+    u'Gugu'
+
+    >>> api.getInformation(INestedConfiguration, IBar, IMyContext).one.textLine
+    u'Gogo'
+
     >>> import shutil
     >>> shutil.rmtree(temp_dir)

Modified: zope.generic/trunk/src/zope/generic/informationprovider/meta.zcml
===================================================================
--- zope.generic/trunk/src/zope/generic/informationprovider/meta.zcml	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/informationprovider/meta.zcml	2006-05-04 18:56:14 UTC (rev 67971)
@@ -15,13 +15,14 @@
           schema=".metadirectives.IInformationSubdirective"
           />
 
-      <meta:subdirective
-          name="informations"
-          schema=".metadirectives.IInformationsSubdirective"
-          />
-
     </meta:complexDirective>
 
+    <meta:directive
+        name="multiInformationProviders"
+        schema=".metadirectives.IMultiInformationProviderDirectives"
+        handler=".metaconfigure.multiInformationProviderDirectives"
+        />
+
   </meta:directives>
 
 </configure>

Modified: zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py
===================================================================
--- zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -21,16 +21,23 @@
 from ConfigParser import SafeConfigParser
 
 from zope.annotation import IAnnotations
+from zope.component import getUtility
 from zope.component import provideUtility
-from zope.component import getUtility
 from zope.component import queryUtility
 from zope.component.interface import provideInterface
 from zope.configuration.exceptions import ConfigurationError
 from zope.interface import alsoProvides
+from zope.schema.interfaces import IDict
 from zope.schema.interfaces import IFromUnicode
+from zope.schema.interfaces import IList
+from zope.schema.interfaces import IObject
+from zope.schema.interfaces import ISequence
+from zope.schema.interfaces import ITuple
 
 from zope.generic.configuration import IConfigurations
+from zope.generic.configuration import IConfigurationType
 from zope.generic.configuration.api import ConfigurationData
+from zope.generic.configuration.api import toConfigFaceTriple
 from zope.generic.face import IConfaceType
 from zope.generic.face import IKeyfaceType
 from zope.generic.face import IUndefinedContext
@@ -116,41 +123,125 @@
     annotations[annotation_key] = annotation
 
 
+def nestedConfigurationData(configparser, section, keyface, prefix=''):
+    """Nested configuration support."""
 
+    missedArguments = []
+    data = {}
+
+    for name in keyface:
+        field = keyface[name]
+        lookup_name = prefix + name.lower()
+        # evalutate name: config parser options are always lower case
+        try:
+            value = configparser.get(section, lookup_name)
+            try:
+                data[name] = field.fromUnicode(unicode(value))
+
+            except:
+                data[name] = IFromUnicode(field).fromUnicode(unicode(value))
+
+        except:
+            if IObject.providedBy(field) and IConfigurationType.providedBy(field.schema):
+                subkeyface = field.schema
+                try:
+                    subdata = nestedConfigurationData(configparser, section, subkeyface, lookup_name + '.')
+                except:
+                    subdata = {}
+
+                if subdata or field.required is True:
+                    try:
+                        data[name] = ConfigurationData(subkeyface, subdata)
+                        continue
+                    except:
+                        if field.required is False:
+                            continue
+
+            elif ISequence.providedBy(field):
+                counter = 0
+                subfield = field.value_type
+                sequence = []
+                while True:
+                    try:
+                        value = configparser.get(section, lookup_name + '.' + str(counter))
+
+                        try:
+                            sequence.append(subfield.fromUnicode(unicode(value)))
+            
+                        except:
+                            sequence.append(IFromUnicode(subfield).fromUnicode(unicode(value)))
+            
+                    except:
+                        break
+                    
+                    counter += 1
+                    
+                if sequence or field.required is True:
+                    if ITuple.providedBy(field):
+                        data[name] = tuple(sequence)
+    
+                    else:
+                        data[name] = sequence
+                    
+                    continue
+            
+            elif IDict.providedBy(field):
+                sublookup_name = lookup_name + '.'
+                sublookup_len = len(sublookup_name)
+                subfield = field.value_type
+                subdict = {}
+                for key, value in configparser.items(section):
+                    if len(key) > sublookup_len and key.startswith(sublookup_name):
+                        subkey = key[sublookup_len:]
+                        if subkey.count('.'):
+                            raise NotImplementedError('Not supported yet!')
+
+                        try:
+                            value = configparser.get(section, key)
+
+                            try:
+                                subdict[subkey] = subfield.fromUnicode(unicode(value))
+                
+                            except:
+                                subdict[subkey] = IFromUnicode(subfield).fromUnicode(unicode(value))
+                
+                        except:
+                            break
+
+                if subdict or field.required is True:
+                    data[name] = subdict
+                    continue
+
+            if field.required is True:
+                missedArguments.append(lookup_name)
+
+    if missedArguments:
+        raise TypeError("__init__ requires '%s' of '%s'." % (', '.join(missedArguments), keyface.__name__))
+
+    return data
+
+
+
 _marker = object()
 
-def iniFileToConfiguration(path):
+def iniFileToConfiguration(path, strict=True):
     """Parse ini file to an iterator over keyface, configuration pairs."""
 
     configparser = SafeConfigParser()
     configparser.read(path)
 
     for section in configparser.sections():
-        # convert section to configuration interface
-        keyface = toInterface(section)
 
-        data = {}
-    
-        for name in keyface:
-            field = keyface[name]
-            # evalutate name: config parser options are always lower case
-            try:
-                value = configparser.get(section, name.lower())
+        if strict:
+            configuration, keyface, conface = toConfigFaceTriple(section)
+            yield (configuration, keyface, conface, nestedConfigurationData(configparser, section, configuration))
 
-            except:
-                value = _marker
+        else:
+            configuration = toInterface(section)
+            yield (configuration, nestedConfigurationData(configparser, section, configuration))
 
-            if value is not _marker:
-                try:
-                    data[name] = field.fromUnicode(unicode(value))
-    
-                except:
-                    data[name] = IFromUnicode(field).fromUnicode(unicode(value))
 
-        yield (keyface, data)
 
-
-
 class InformationProviderDirective(object):
     """Provide a new information provider."""
 
@@ -185,10 +276,25 @@
         "Handle empty/simple declaration."
         return ()
 
-    def information(self, _context, keyface=None, configuration=None, key=None, annotation=None):
+    def information(self, _context, keyface=None, configuration=None, key=None, annotation=None, iniFiles=()):
         """Add a configuration to the information provider."""
+
+        # handle ini files
+        if iniFiles:
+            if keyface or configuration or key or annotation:
+                raise ConfigurationError('Attribute iniFiles does not allow other attributes.')
+
+            for path in iniFiles:
+                for configuration, data in iniFileToConfiguration(path, False):
+                     _context.action(
+                        discriminator = (
+                        'informationprovider.configuration', self._keyface, self._conface, configuration),
+                        callable = provideConfiguration,
+                        args = (self._keyface, self._conface, configuration, data),
+                        )
+ 
         # handle configuration
-        if keyface and configuration is not None:
+        elif keyface and configuration is not None:
             # preconditions
             if not (keyface.providedBy(configuration) or type(configuration) is dict):
                 raise ConfigurationError('Data attribute must provide %s.' % keyface.__name__)
@@ -215,12 +321,29 @@
             raise ConfigurationError('Information subdirective must provide ' +
                 'key and annotation or keyface and configuration.')
 
-    def informations(self, _context, iniFiles=()):
-        """Ini-file based configurations for an information provider."""
+
+
+def multiInformationProviderDirectives(_context, iniFiles=()):
+    """Ini-file based configurations for multi information provider."""
         
-        for path in iniFiles:
-            for keyface, configuration in iniFileToConfiguration(path):
-                # register corresponding configuration information
-                self.information(_context, keyface, configuration)
+    for path in iniFiles:
+        for configuration, keyface, conface, data in iniFileToConfiguration(path):
+            # register corresponding configuration information
+            # provide type as soon as possilbe
+            if not IKeyfaceType.providedBy(keyface):
+                provideInterface(None, keyface, IKeyfaceType)
+    
+            if not IConfaceType.providedBy(conface):
+                provideInterface(None, conface, IConfaceType)
+    
+            # ensure the corresponding information provider
+            ensureInformationProvider(keyface, conface)
+
+            _context.action(
+                discriminator = (
+                'informationprovider.configuration', keyface, conface, configuration),
+                callable = provideConfiguration,
+                args = (keyface, conface, configuration, data),
+                )
                 
                 
\ No newline at end of file

Modified: zope.generic/trunk/src/zope/generic/informationprovider/metadirectives.py
===================================================================
--- zope.generic/trunk/src/zope/generic/informationprovider/metadirectives.py	2006-05-04 18:30:20 UTC (rev 67970)
+++ zope.generic/trunk/src/zope/generic/informationprovider/metadirectives.py	2006-05-04 18:56:14 UTC (rev 67971)
@@ -55,6 +55,23 @@
 
 
 
+class IIniFileDirective(Interface):
+    """Ini-file based configurations for multi information provider."""
+
+    iniFiles = Tokens(
+        title=_('*.ini-like File'),
+        description=_('Parse ((ConfigParser) configuration and face interfaces '
+                      'from sections resolving the pattern '
+                      'configuration:keyface at conface. The configuration data '
+                      'are retrieved from the corresponding options using '
+                      'IFromUnicode(field).fromUnicode() from the configuration '
+                      'fields.'),
+        required=False,
+        value_type=Path(constraint=lambda v: v.endswith('.ini'))
+        )
+
+
+
 class IDescriptionDirective(IKeyfaceDirective):
     """Base information provider attributes."""
 
@@ -77,7 +94,7 @@
 
 
 
-class IInformationSubdirective(Interface):
+class IInformationSubdirective(IIniFileDirective):
     """Declare a certain information of an information provider."""
 
     keyface = GlobalInterface(
@@ -106,14 +123,6 @@
         )
 
 
-class IInformationsSubdirective(Interface):
-    """Ini-file based configurations for an information provider."""
+class IMultiInformationProviderDirectives(IIniFileDirective):
+    """Ini-file based configurations for multi information providers."""
 
-    iniFiles = Tokens(
-        title=_('*.ini-like File'),
-        description=_('Parse configuration key interfaces from sections. '
-                      'and the configuration data from theirs options using '
-                      'the config parser and IFromUnicode(field).fromUnicode().'),
-        required=False,
-        value_type=Path(constraint=lambda v: v.endswith('.ini'))
-        )



More information about the Checkins mailing list