[Checkins] SVN: z3c.configurator/ initial import of the z3c.configurator package

Bernd Dorn bernd.dorn at fhv.at
Wed Aug 16 02:51:10 EDT 2006


Log message for revision 69557:
  initial import of the z3c.configurator package
  The configurator is designed to extend a component after its
  creation.
  

Changed:
  A   z3c.configurator/
  A   z3c.configurator/trunk/
  A   z3c.configurator/trunk/setup.cfg
  A   z3c.configurator/trunk/setup.py
  A   z3c.configurator/trunk/src/
  A   z3c.configurator/trunk/src/z3c/
  A   z3c.configurator/trunk/src/z3c/__init__.py
  A   z3c.configurator/trunk/src/z3c/configurator/
  A   z3c.configurator/trunk/src/z3c/configurator/README.txt
  A   z3c.configurator/trunk/src/z3c/configurator/__init__.py
  A   z3c.configurator/trunk/src/z3c/configurator/configurator.py
  A   z3c.configurator/trunk/src/z3c/configurator/interfaces.py
  A   z3c.configurator/trunk/src/z3c/configurator/tests.py

-=-
Added: z3c.configurator/trunk/setup.cfg
===================================================================
--- z3c.configurator/trunk/setup.cfg	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/setup.cfg	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = .dev
+tag_svn_revision = 1


Property changes on: z3c.configurator/trunk/setup.cfg
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/setup.py
===================================================================
--- z3c.configurator/trunk/setup.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/setup.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,18 @@
+#!python
+from setuptools import setup, find_packages
+
+setup(name='z3c.configurator',
+      version='0.1',
+      author = "??",
+      author_email = "office at lovelysystems.com",
+      description = "Dynamic configuration",
+      license = "ZPL 2.1",
+      keywords = "zope zope3",
+      url='http://svn.zope.org/z3c.configurator',
+      packages=find_packages('src'),
+      include_package_data=True,
+      package_dir = {'':'src'},
+      namespace_packages=['refline',],
+      install_requires = ['setuptools', ],
+     )
+


Property changes on: z3c.configurator/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/__init__.py
===================================================================
--- z3c.configurator/trunk/src/z3c/__init__.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/__init__.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,6 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace('z3c')
+except ImportError:
+    pass


Property changes on: z3c.configurator/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/configurator/README.txt
===================================================================
--- z3c.configurator/trunk/src/z3c/configurator/README.txt	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/configurator/README.txt	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,127 @@
+================
+The Configurator
+================
+
+The configurator is designed to extend a component after its
+creation. Traditionally this is done by listening to ``ObjectCreatedEvent``
+events. However, this low-level method does not suffice, since configuration
+often depends on other configuration steps and additional data is often needed
+to complete the configuration. And this is where the configurator comes
+in. It uses a separate plugin mechanism to implement the mentioned high-level
+functionality.
+
+Before we can demonstrate the configuration mechanism, we'll have to create an
+interface and a component on which the configuration can act upon:
+
+  >>> import zope.interface
+
+  >>> class ISomething(zope.interface.Interface):
+  ...     """Some interesting interface."""
+
+  >>> class Something(object):
+  ...     """Implementation of something."""
+  ...     zope.interface.implements(ISomething)
+
+  >>> something = Something()
+
+Now we can have the configuration act on the component:
+
+  >>> from z3c import configurator
+  >>> configurator.configure(something, {})
+
+The second argument is the data dictionary, which can be used to pass in
+additional information that might be needed during the configuration. It is up
+to each plugin to interpret the data.
+
+Of course nothing happens, since no configuration plugins are
+registered. Let's now create a new configuration plugin, which sets a new
+attribute on the component:
+
+  >>> import zope.component
+  >>> from z3c.configurator import interfaces
+
+  >>> class AddFooAttribute(configurator.ConfigurationPluginBase):
+  ...     zope.component.adapts(ISomething)
+  ...
+  ...     def __call__(self, data):
+  ...         setattr(self.context, 'foo', data.get('foo'))
+
+  >>> zope.component.provideAdapter(AddFooAttribute, name='add foo')
+
+If we execute the configuration again, the attribute will be added:
+
+  >>> configurator.configure(something, {'foo': u'my value'})
+  >>> something.foo
+  u'my value'
+
+
+Dependencies
+------------
+
+Now that we have simple configuration plugins, we can also develop plugins
+that depend on another one. Let's create a configuration plugin that adds some
+additional data to the foo attribute. Clearly, the foo attribute has to exist
+before this step can be taken. The ``dependencies`` attribute can be used to
+specify all plugin dependencies by name:
+
+  >>> class ExtendFooAttribute(configurator.ConfigurationPluginBase):
+  ...     zope.component.adapts(ISomething)
+  ...     dependencies = ('add foo',)
+  ...
+  ...     def __call__(self, data):
+  ...         self.context.foo = u'Text: ' + self.context.foo
+
+  >>> zope.component.provideAdapter(ExtendFooAttribute, name='extend foo')
+
+If we now execute the configuration again, the extended result should be seen:
+
+  >>> something = Something()
+  >>> configurator.configure(something, {'foo': u'my value'})
+  >>> something.foo
+  u'Text: my value'
+
+
+Data Schemas
+------------
+
+For purely informational purposes, a ``schema`` attribute is used on the
+plugin to describe the fields that the plugin expects from the data
+dictionary. For adding another simple attribute, this could look as follows:
+
+  >>> import zope.schema
+  >>> class IAddBar(zope.interface.Interface):
+  ...     bar = zope.schema.Text(title=u'Bar')
+
+  >>> class AddBarAttribute(configurator.SchemaConfigurationPluginBase):
+  ...     zope.component.adapts(ISomething)
+  ...     schema = IAddBar
+  ...
+  ...     def __call__(self, data):
+  ...         self.verify(data)
+  ...         setattr(self.context, 'bar', data.get('bar'))
+
+  >>> zope.component.provideAdapter(AddBarAttribute, name='add bar')
+
+The advantage of using the base class for this case is that it provides a
+``verify()`` method that allows you to verify the data against the shema. We
+can now run the configuration again:
+
+  >>> something = Something()
+  >>> configurator.configure(something, {'foo': u'my value', 'bar': u'value'})
+  >>> something.bar
+  u'value'
+
+The value must exist and be valid:
+
+  >>> something = Something()
+  >>> configurator.configure(something, {'foo': u'my value'})
+  Traceback (most recent call last):
+  ...
+  KeyError: 'bar'
+
+  >>> something = Something()
+  >>> configurator.configure(something, {'foo': u'my value', 'bar': 1})
+  Traceback (most recent call last):
+  ...
+  WrongType: (1, <type 'unicode'>)
+


Property changes on: z3c.configurator/trunk/src/z3c/configurator/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/configurator/__init__.py
===================================================================
--- z3c.configurator/trunk/src/z3c/configurator/__init__.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/configurator/__init__.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,5 @@
+# Make a package
+
+from z3c.configurator.configurator import configure
+from z3c.configurator.configurator import ConfigurationPluginBase
+from z3c.configurator.configurator import SchemaConfigurationPluginBase


Property changes on: z3c.configurator/trunk/src/z3c/configurator/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/configurator/configurator.py
===================================================================
--- z3c.configurator/trunk/src/z3c/configurator/configurator.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/configurator/configurator.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Configurator Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import sys
+import zope.component
+import zope.interface
+import zope.schema
+
+from z3c.configurator import interfaces
+
+# Stati values
+NEW = 1
+OPEN = 2
+CLOSED = 3
+
+def configure(component, data):
+
+    plugins = dict(zope.component.getAdapters(
+        (component,), interfaces.IConfigurationPlugin))
+
+    # status is a dict plugin names as keys and stati as values.
+    status = dict([(name, NEW) for name in plugins])
+
+    def visit(name):
+        """The recursive part of the topological sort
+
+        Raises a CyclicDependencyError if cyclic depencencies are found.
+        """
+        if status[name] == NEW:
+            status[name] = OPEN
+            plugin = plugins[name]
+            for dep in getattr(plugin, 'dependencies', ()):
+                visit(dep)
+            plugin(data)
+            status[name] = CLOSED
+
+        elif status[name] == CLOSED:
+            return
+
+        # Stumbling over an OPEN node means there is a cyclic dependency
+        elif status[name] == OPEN:
+            raise interfaces.CyclicDependencyError(
+                "cyclic dependency at '%s'" % name)
+
+
+    for name in plugins:
+        visit(name)
+
+
+class ConfigurationPluginBase(object):
+    zope.interface.implements(interfaces.IConfigurationPlugin)
+
+    def __init__(self, context):
+        self.context = context
+
+    def __call__(self, data):
+        raise NotImplemented
+
+class SchemaConfigurationPluginBase(object):
+    zope.interface.implements(interfaces.IConfigurationPlugin)
+    schema = zope.interface.Interface
+
+    def __init__(self, context):
+        self.context = context
+
+    def verify(self, data):
+        for name, field in zope.schema.getFields(self.schema).items():
+            field.validate(data[name])
+
+    def __call__(self, data):
+        raise NotImplemented


Property changes on: z3c.configurator/trunk/src/z3c/configurator/configurator.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/configurator/interfaces.py
===================================================================
--- z3c.configurator/trunk/src/z3c/configurator/interfaces.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/configurator/interfaces.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Configurator Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+import zope.schema
+
+
+class CyclicDependencyError(ValueError):
+    """Cyclic dependency of configuration plugins"""
+
+
+class DataMissingError(ValueError):
+    """Error raised when required data is missing during configuration
+    execution."""
+
+
+class IConfigurationPlugin(zope.interface.Interface):
+    """An object executing one configuration step."""
+
+    dependencies = zope.interface.Attribute(
+        """A sequence of dependencies to other configuration plugins.""")
+
+    def __call__(self, data):
+        """Execute the configuration.
+
+        The data is a dictionary containing values that might be of interest
+        to the configuration plugin. When some required data field is missing,
+        then raise a ``DataMissingError`` error.
+        """
+
+
+class ISchemaConfigurationPlugin(IConfigurationPlugin):
+    """A configuration plugin that provides a data schema."""
+
+    schema = zope.schema.Object(
+        title=u"Configuration Schema",
+        description=u"The schema describing the data fields needed.",
+        schema=zope.interface.interfaces.IInterface)


Property changes on: z3c.configurator/trunk/src/z3c/configurator/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.configurator/trunk/src/z3c/configurator/tests.py
===================================================================
--- z3c.configurator/trunk/src/z3c/configurator/tests.py	2006-08-16 06:18:13 UTC (rev 69556)
+++ z3c.configurator/trunk/src/z3c/configurator/tests.py	2006-08-16 06:51:09 UTC (rev 69557)
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Configurator Test Setup
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+__docformat__ = 'restructuredtext'
+
+import unittest
+from zope.testing import doctest
+from zope.testing.doctestunit import DocFileSuite
+from zope.app.testing import setup
+
+def setUp(test):
+    setup.placelessSetUp()
+
+def tearDown(test):
+    setup.placelessTearDown()
+
+
+def test_suite():
+    return unittest.TestSuite((
+        DocFileSuite('README.txt',
+                     setUp=setUp, tearDown=tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.configurator/trunk/src/z3c/configurator/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list