[Checkins] SVN: zope.generic/trunk/src/zope/generic/ simple support
for nested configuration (prototype)
Dominik Huber
dominik.huber at perse.ch
Thu Jun 8 13:21:58 EDT 2006
Log message for revision 68530:
simple support for nested configuration (prototype)
Changed:
U zope.generic/trunk/src/zope/generic/configuration/README.txt
U zope.generic/trunk/src/zope/generic/configuration/api.py
U zope.generic/trunk/src/zope/generic/configuration/base.py
A zope.generic/trunk/src/zope/generic/configuration/field.py
U zope.generic/trunk/src/zope/generic/configuration/helper.py
U zope.generic/trunk/src/zope/generic/configuration/interfaces.py
U zope.generic/trunk/src/zope/generic/configuration/metaconfigure.py
U zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py
-=-
Modified: zope.generic/trunk/src/zope/generic/configuration/README.txt
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/README.txt 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/README.txt 2006-06-08 17:21:57 UTC (rev 68530)
@@ -237,88 +237,185 @@
The configuration directive does evaluate if an key interface is nested:
- >>> from zope.schema import Object
+ >>> class IMySubConfiguration(interface.Interface):
+ ... foo = api.SubConfiguration(schema=IFooConfiguration)
- >>> class ISubConfigurationConfiguration(interface.Interface):
- ... foo = Object(schema=IFooConfiguration)
-
>>> registerDirective('''
... <generic:configuration
- ... keyface="example.ISubConfigurationConfiguration"
+ ... keyface="example.IMySubConfiguration"
... />
... ''')
- >>> api.INestedConfigurationType.providedBy(ISubConfigurationConfiguration)
+ >>> api.INestedConfigurationType.providedBy(IMySubConfiguration)
True
- >>> from zope.schema import Tuple
+ >>> from zope.schema import Text
- >>> class ISubTupleConfiguration(interface.Interface):
- ... foo = Tuple(value_type=TextLine())
+ >>> class IMySubConfigurationList(interface.Interface):
+ ... foo = api.SubConfigurationList(value_type=Text())
+ ... bar = api.SubConfigurationList(required=False,
+ ... value_type=api.SubConfiguration(schema=IFooConfiguration))
>>> registerDirective('''
... <generic:configuration
- ... keyface="example.ISubTupleConfiguration"
+ ... keyface="example.IMySubConfigurationList"
... />
... ''')
- >>> api.INestedConfigurationType.providedBy(ISubTupleConfiguration)
+ >>> api.INestedConfigurationType.providedBy(IMySubConfigurationList)
True
- >>> from zope.schema import Tuple
+ >>> from zope.schema import BytesLine
+ >>> class IMySubConfigurationDict(interface.Interface):
+ ... foo = api.SubConfigurationDict(value_type=Text())
+ ... bar = api.SubConfigurationDict(required=False,
+ ... value_type=api.SubConfiguration(schema=IFooConfiguration))
+
+ >>> registerDirective('''
+ ... <generic:configuration
+ ... keyface="example.IMySubConfigurationDict"
+ ... />
+ ... ''')
+
+ >>> api.INestedConfigurationType.providedBy(IMySubConfigurationDict)
+ True
+
You can suppress the evaluation by an explicite declaration using the nested
attribute:
- >>> class ISuppressedContainerConfiguration(interface.Interface):
- ... foo = Tuple(value_type=TextLine())
+ >>> class ISuppressedNestedConfiguration(interface.Interface):
+ ... foo = api.SubConfigurationDict(value_type=Text())
>>> registerDirective('''
... <generic:configuration
- ... keyface="example.ISuppressedContainerConfiguration"
+ ... keyface="example.ISuppressedNestedConfiguration"
... nested="False"
... />
... ''')
- >>> api.INestedConfigurationType.providedBy(ISuppressedContainerConfiguration)
+ >>> api.INestedConfigurationType.providedBy(ISuppressedNestedConfiguration)
False
We can create nested configurations by a dictionary where the hierarchy is
reflected by dotted names.
-First test nested configuration:
+Nested sub-configuration:
-# >>> data = {'foo.foo': u'bla', 'foo.optional': u'Blu'}
-# >>> config = api.createConfiguration(ISubConfigurationConfiguration, data)
-# >>> IFooConfiguration.providedBy(config.foo)
-# True
-# >>> config.foo.foo
-# u'bla'
-# >>> config.foo.optional
-# u'Blu'
-#
-# >>> data = {'foo.foo': u'xxx'}
-# >>> config = api.createConfiguration(ISubConfigurationConfiguration, data)
-# >>> IFooConfiguration.providedBy(config.foo)
-# True
-# >>> config.foo.foo
-# u'xxx'
-# >>> config.foo.optional
-# u'Bla'
-#
-# >>> data = {'foo.optional': u'Blu'}
-# >>> config = api.createConfiguration(ISubConfigurationConfiguration, data)
-# Traceback (most recent call last):
-# ...
-# TypeError: __init__ requires 'foo.foo' of 'ISubConfigurationConfiguration'.
-#
-# >>> subdata = {'foo': u'bla', 'optional': u'Blu'}
-# >>> subconfig = api.createConfiguration(IFooConfiguration, subdata)
-# >>> data = {'foo': subconfig}
-# >>> config = api.createConfiguration(ISubConfigurationConfiguration, data)
-# >>> IFooConfiguration.providedBy(config.foo)
-# True
-# >>> config.foo.foo
-# u'bla'
-# >>> config.foo.optional
-# u'Blu'
+ >>> data = {'foo.foo': u'bla', 'foo.optional': u'Blu'}
+ >>> config = api.createConfiguration(IMySubConfiguration, data)
+ >>> IFooConfiguration.providedBy(config.foo)
+ True
+ >>> config.foo.foo
+ u'bla'
+ >>> config.foo.optional
+ u'Blu'
+
+ >>> data = {'foo.foo': u'xxx'}
+ >>> config = api.createConfiguration(IMySubConfiguration, data)
+ >>> IFooConfiguration.providedBy(config.foo)
+ True
+ >>> config.foo.foo
+ u'xxx'
+ >>> config.foo.optional
+ u'Bla'
+
+ >>> data = {'foo.optional': u'Blu'}
+ >>> config = api.createConfiguration(IMySubConfiguration, data)
+ Traceback (most recent call last):
+ ...
+ TypeError: __init__ requires 'foo' of 'IFooConfiguration'.
+
+ >>> subdata = {'foo': u'bla', 'optional': u'Blu'}
+ >>> subconfig = api.createConfiguration(IFooConfiguration, subdata)
+ >>> data = {'foo': subconfig}
+ >>> config = api.createConfiguration(IMySubConfiguration, data)
+ >>> IFooConfiguration.providedBy(config.foo)
+ True
+ >>> config.foo.foo
+ u'bla'
+ >>> config.foo.optional
+ u'Blu'
+
+Simple sub-configuration lists:
+
+ >>> data = {'foo.0': u'bla', 'foo.1': u'Blu'}
+ >>> config = api.createConfiguration(IMySubConfigurationList, data)
+ >>> from persistent.list import PersistentList
+ >>> isinstance(config.foo, PersistentList)
+ True
+ >>> config.foo[0]
+ u'bla'
+ >>> config.foo[1]
+ u'Blu'
+
+ >>> data = {'foo.1': u'bla', 'foo.2': u'Blu'}
+ >>> config = api.createConfiguration(IMySubConfigurationList, data)
+ Traceback (most recent call last):
+ ...
+ IndexError: list index out of range
+
+Nested sub-configuration lists:
+
+ >>> data = {'foo.0': u'bla', 'foo.1': u'Blu',
+ ... 'bar.0.foo': u'bla 0', 'bar.0.optional': u'Blu 0',
+ ... 'bar.1.foo': u'bla 1'}
+ >>> config = api.createConfiguration(IMySubConfigurationList, data)
+ >>> from persistent.list import PersistentList
+ >>> isinstance(config.bar, PersistentList)
+ True
+ >>> config.foo[0]
+ u'bla'
+ >>> config.foo[1]
+ u'Blu'
+ >>> IFooConfiguration.providedBy(config.bar[0])
+ True
+ >>> config.bar[0].foo
+ u'bla 0'
+ >>> config.bar[0].optional
+ u'Blu 0'
+ >>> config.bar[1].foo
+ u'bla 1'
+
+ >>> data = {'foo.0': u'bla', 'foo.1': u'Blu',
+ ... 'bar.0.foo': u'bla 0', 'bar.0.optional': u'Blu 0',
+ ... 'bar.2.foo': u'bla 1'}
+ >>> config = api.createConfiguration(IMySubConfigurationList, data)
+ Traceback (most recent call last):
+ ...
+ IndexError: list index out of range
+
+Simple sub-configuration dicts:
+
+ >>> data = {'foo.a': u'bla', 'foo.b': u'Blu'}
+ >>> config = api.createConfiguration(IMySubConfigurationDict, data)
+ >>> from persistent.dict import PersistentDict
+ >>> isinstance(config.foo, PersistentDict)
+ True
+ >>> config.foo['a']
+ u'bla'
+ >>> config.foo['b']
+ u'Blu'
+
+Nested sub-configuration dicts:
+
+ >>> data = {'foo.a': u'bla', 'foo.b': u'Blu',
+ ... 'bar.c.foo': u'bla C', 'bar.c.optional': u'Blu C',
+ ... 'bar.d.foo': u'bla D'}
+ >>> config = api.createConfiguration(IMySubConfigurationDict, data)
+ >>> from persistent.dict import PersistentDict
+ >>> isinstance(config.bar, PersistentDict)
+ True
+ >>> config.foo['a']
+ u'bla'
+ >>> config.foo['b']
+ u'Blu'
+ >>> IFooConfiguration.providedBy(config.bar['c'])
+ True
+ >>> config.bar['c'].foo
+ u'bla C'
+ >>> config.bar['c'].optional
+ u'Blu C'
+ >>> config.bar['d'].foo
+ u'bla D'
+
Modified: zope.generic/trunk/src/zope/generic/configuration/api.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/api.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/api.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -23,6 +23,12 @@
from zope.generic.configuration import *
from zope.generic.configuration.adapter import AttributeConfigurations
from zope.generic.configuration.base import createConfiguration
+from zope.generic.configuration.field import ISubConfiguration
+from zope.generic.configuration.field import ISubConfigurationDict
+from zope.generic.configuration.field import ISubConfigurationList
+from zope.generic.configuration.field import SubConfiguration
+from zope.generic.configuration.field import SubConfigurationDict
+from zope.generic.configuration.field import SubConfigurationList
from zope.generic.configuration.helper import configurationToDict
from zope.generic.configuration.helper import provideConfigurationType
from zope.generic.configuration.helper import namesInOrder
Modified: zope.generic/trunk/src/zope/generic/configuration/base.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/base.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/base.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -18,14 +18,15 @@
__docformat__ = 'restructuredtext'
+from persistent import IPersistent
from persistent import Persistent
-from persistent import IPersistent
+from persistent.dict import PersistentDict
+from persistent.list import PersistentList
from zope.interface import directlyProvides
from zope.interface import implements
from zope.schema import ValidationError
from zope.schema.interfaces import IField
-from zope.schema.interfaces import IObject
from zope.generic.face import IAttributeFaced
from zope.generic.face import IFace
@@ -36,14 +37,64 @@
from zope.generic.configuration import IConfigurationData
from zope.generic.configuration import IConfigurations
from zope.generic.configuration import IConfigurationType
+from zope.generic.configuration import INestedConfiguration
+from zope.generic.configuration.field import ISubConfiguration
+from zope.generic.configuration.field import ISubConfigurationDict
+from zope.generic.configuration.field import ISubConfigurationList
def createConfiguration(keyface, data):
+ """Factory function for configuration data."""
+
return ConfigurationData(keyface, data)
+def createConfigurationList(field, data):
+
+ if ISubConfiguration.providedBy(field.value_type):
+ subkeyface = field.value_type.schema
+ counter = 0
+ subconfigurations = []
+ while data:
+ subdata = subData(str(counter), data)
+ if subdata:
+ subconfigurations.append(createConfiguration(subkeyface, subdata))
+ else:
+ raise IndexError('list index out of range')
+
+ counter += 1
+
+ return ConfigurationList(subconfigurations)
+
+ # other objects
+ else:
+ indices = data.keys()
+ indices.sort()
+ # precondition: assume that everythings is ok if the start and end point is ok
+ if int(indices[0]) != 0 or int(indices[-1]) != len(indices) - 1:
+ raise IndexError('list index out of range')
+ return ConfigurationList([data[index] for index in indices])
+
+
+
+def createConfigurationDict(field, data):
+ if ISubConfiguration.providedBy(field.value_type):
+ subkeyface = field.value_type.schema
+ subconfigurations = {}
+ while data:
+ prefix = iter(data).next().split('.')[0] # evaluate the next entry
+ subconfigurations[prefix] = createConfiguration(subkeyface, subData(prefix, data))
+
+ return ConfigurationDict(subconfigurations)
+
+ # other objects
+ else:
+ return ConfigurationDict(data)
+
+
+
def subData(name, data):
"""Return a subdata dict and remove the subdata from the given data dict.
@@ -78,28 +129,52 @@
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:
- relevant_data[name] = createConfiguration(field.schema, subData(name, data))
- continue
+ if INestedConfiguration.providedBy(field):
+ subdata = subData(name, data)
- except:
- pass
+ if subdata or field.required is True:
+ if ISubConfiguration.providedBy(field):
+ relevant_data[name] = createConfiguration(field.schema, subdata)
+ elif ISubConfigurationList.providedBy(field):
+ relevant_data[name] = createConfigurationList(field, subdata)
+ elif ISubConfigurationDict.providedBy(field):
+ relevant_data[name] = createConfigurationDict(field, subdata)
+ else:
+ raise NotImplementedError()
+ continue
+
if field.required is True:
missedArguments.append(name)
+
else:
- relevant_data[name] = data[name]
+ value = data[name]
+ if isinstance(value, list):
+ relevant_data[name] = ConfigurationList(value)
+
+ elif isinstance(value, dict):
+ relevant_data[name] = ConfigurationDict(value)
+
+ else:
+ relevant_data[name] = data[name]
if missedArguments:
raise TypeError("__init__ requires '%s' of '%s'." % (', '.join(missedArguments), __keyface__.__name__))
return relevant_data
-
+
+class ConfigurationList(PersistentList):
+ """List-like configurations."""
+
+
+
+class ConfigurationDict(PersistentDict):
+ """Dict-like configurations."""
+
+
+
_marker = object()
class ConfigurationData(Persistent):
Added: zope.generic/trunk/src/zope/generic/configuration/field.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/field.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/field.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -0,0 +1,82 @@
+##############################################################################
+#
+# Copyright (c) 2005, 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""
+$Id$
+"""
+
+__docformat__ = 'restructuredtext'
+
+from zope.app.i18n import ZopeMessageFactory as _
+from zope.interface import Attribute
+from zope.interface import implements
+from persistent.dict import PersistentDict
+from persistent.list import PersistentList
+from zope.schema import BytesLine
+from zope.schema import Dict
+from zope.schema import List
+from zope.schema import Object
+from zope.schema.interfaces import IDict
+from zope.schema.interfaces import IField
+from zope.schema.interfaces import IList
+from zope.schema.interfaces import IObject
+
+from zope.generic.configuration import INestedConfiguration
+
+
+
+class ISubConfiguration(INestedConfiguration, IObject):
+ """Mark a single sub configuration field."""
+
+ schema = Attribute('schema',
+ _('The interface that defines the fields comprising the sub ' +
+ 'configuration. The interface must be an IConfigurationType.'))
+
+
+
+class SubConfiguration(Object):
+ __doc__ = ISubConfiguration.__doc__
+
+ implements(ISubConfiguration)
+
+
+
+class ISubConfigurationList(INestedConfiguration, IList):
+ """Mark a list of sub configuration objects."""
+
+
+
+class SubConfigurationList(List):
+ __doc__ = ISubConfigurationList.__doc__
+
+ implements(ISubConfigurationList)
+
+ _type = PersistentList
+
+
+
+class ISubConfigurationDict(INestedConfiguration, IDict):
+ """Mark a dictionary of sub-configuration bojects."""
+
+
+
+class SubConfigurationDict(Dict):
+ __doc__ = ISubConfigurationDict.__doc__
+
+ implements(ISubConfigurationDict)
+
+ _type = PersistentDict
+
+ def __init__(self, value_type=None, **kw):
+ super(SubConfigurationDict, self).__init__(BytesLine(), value_type, **kw)
Property changes on: zope.generic/trunk/src/zope/generic/configuration/field.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: zope.generic/trunk/src/zope/generic/configuration/helper.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/helper.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/helper.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -23,6 +23,10 @@
from zope.generic.face import IFace
from zope.generic.configuration import IConfigurationType
+from zope.generic.configuration import INestedConfiguration
+from zope.generic.configuration.field import ISubConfiguration
+from zope.generic.configuration.field import ISubConfigurationDict
+from zope.generic.configuration.field import ISubConfigurationList
def provideConfigurationType(interface):
@@ -66,25 +70,47 @@
for name in keyface:
value = getattr(configuration, name, _marker)
field = keyface[name]
+
+ if INestedConfiguration.providedBy(field):
- if field.required is False:
- if value is not _marker and value != field.default:
- data[name] = value
+ if ISubConfiguration.providedBy(field):
+ data[name] = configurationToDict(value, all)
- elif value == field.default:
- if all:
- data[name] = value
+ elif ISubConfigurationList.providedBy(field):
+ if ISubConfiguration.providedBy(field.value_type):
+ data[name] = [configurationToDict(v, all) for v in value]
+ # regular objects
+ else:
+ data[name] = [v for v in value]
- else:
- if all:
- data[name] = field.default
+ elif ISubConfigurationDict.providedBy(field):
+ if ISubConfiguration.providedBy(field.value_type):
+ data[name] = dict([(k, configurationToDict(v, all)) for k, v in value.items()])
+ # regular objects
+ else:
+ data[name] = dict([item for item in value.items()])
- elif value is not _marker:
- data[name] = value
-
+ # no sub-configuraiton
else:
- raise RuntimeError('Data is missing', name)
+ if field.required is False:
+ if value is not _marker and value != field.default:
+ data[name] = value
+
+ elif value == field.default:
+ if all:
+ data[name] = value
+
+ else:
+ if all:
+ data[name] = field.default
+
+ elif value is not _marker:
+ data[name] = value
+
+ else:
+ raise RuntimeError('Data is missing', name)
+
return data
Modified: zope.generic/trunk/src/zope/generic/configuration/interfaces.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/interfaces.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/interfaces.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -18,12 +18,14 @@
__docformat__ = 'restructuredtext'
+from zope.app.i18n import ZopeMessageFactory as _
from zope.location import ILocation
from zope.component.interfaces import IObjectEvent
from zope.interface import Attribute
from zope.interface import alsoProvides
from zope.interface import Interface
from zope.lifecycleevent.interfaces import IModificationDescription
+from zope.schema.interfaces import IField
from zope.generic.face import IFaced
from zope.generic.face import IKeyfaceType
@@ -45,6 +47,11 @@
+class INestedConfiguration(IField):
+ """Mark nested configuration."""
+
+
+
class IConfigurationData(IFaced):
"""Marker for configuration data implementations."""
Modified: zope.generic/trunk/src/zope/generic/configuration/metaconfigure.py
===================================================================
--- zope.generic/trunk/src/zope/generic/configuration/metaconfigure.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/configuration/metaconfigure.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -24,6 +24,7 @@
from zope.schema.interfaces import IObject
from zope.generic.configuration import IConfigurationType
+from zope.generic.configuration import INestedConfiguration
from zope.generic.configuration import INestedConfigurationType
@@ -43,12 +44,8 @@
elif nested is None:
for name in keyface:
field = keyface[name]
- if IObject.providedBy(field) and IConfigurationType.providedBy(field.schema):
+ if INestedConfiguration.providedBy(field):
type = INestedConfigurationType
break
-
- elif ISequence.providedBy(field) or IDict.providedBy(field):
- type = INestedConfigurationType
- break
provideInterface(None, keyface, type)
Modified: zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py
===================================================================
--- zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py 2006-06-08 17:21:28 UTC (rev 68529)
+++ zope.generic/trunk/src/zope/generic/informationprovider/metaconfigure.py 2006-06-08 17:21:57 UTC (rev 68530)
@@ -178,31 +178,5 @@
# handle wrong usage
else:
raise ConfigurationError('Information subdirective must provide ' +
- 'key and annotation or keyface and configuration.')
-
-
-
-def multiInformationProvidersDirective(_context, iniFiles=()):
- """Ini-file based configurations for multi information provider."""
-
- 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),
- )
-
+ 'key and annotation or keyface and configuration.')
\ No newline at end of file
More information about the Checkins
mailing list