[Checkins] SVN: zope.configuration/branches/chrism-configmachine/s first cut at halving zope.configuration into zcml-directive-related and core parts

Chris McDonough chrism at plope.com
Sun Aug 28 19:38:31 EDT 2011


Log message for revision 122695:
  first cut at halving zope.configuration into zcml-directive-related and core parts

Changed:
  U   zope.configuration/branches/chrism-configmachine/setup.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/__init__.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/exceptions.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/interfaces.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/__init__.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/bad.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/directives.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/test_config.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/victim.py
  D   zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py
  D   zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py
  U   zope.configuration/branches/chrism-configmachine/src/zope/configuration/interfaces.py
  D   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/bad.py
  D   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py
  A   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_fields.py
  U   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_xmlconfig.py
  D   zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/victim.py

-=-
Modified: zope.configuration/branches/chrism-configmachine/setup.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/setup.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/setup.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -43,17 +43,21 @@
     logging.getLogger().addHandler(NullHandler())
 
     suite = unittest.TestSuite()
-    base = pkg_resources.working_set.find(
+    config_base = pkg_resources.working_set.find(
         pkg_resources.Requirement.parse('zope.configuration')).location
-    for dirpath, dirnames, filenames in os.walk(base):
-        if os.path.basename(dirpath) == 'tests':
-            for filename in filenames:
-                if ( filename.endswith('.py') and
-                     filename.startswith('test') ):
-                    mod = __import__(
-                        _modname(dirpath, base, os.path.splitext(filename)[0]),
-                        {}, {}, ['*'])
-                    suite.addTest(mod.test_suite())
+    configmachine_base = pkg_resources.working_set.find(
+        pkg_resources.Requirement.parse('zope.configmachine')).location
+    for base in (config_base, configmachine_base):
+        for dirpath, dirnames, filenames in os.walk(base):
+            if os.path.basename(dirpath) == 'tests':
+                for filename in filenames:
+                    if ( filename.endswith('.py') and
+                         filename.startswith('test') ):
+                        mod = __import__(
+                            _modname(dirpath, base,
+                                     os.path.splitext(filename)[0]),
+                            {}, {}, ['*'])
+                        suite.addTest(mod.test_suite())
     return suite
 
 setup(name='zope.configuration',

Added: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/__init__.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/__init__.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/__init__.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,785 @@
+##############################################################################
+#
+# Copyright (c) 2011 Zope Foundation 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.
+#
+##############################################################################
+"""Configuration processor
+
+"""
+
+__docformat__ = 'restructuredtext'
+
+import __builtin__
+import os.path
+import sys
+
+from zope.interface.adapter import AdapterRegistry
+from zope.interface import Interface
+from zope.interface import implements
+from zope.interface import providedBy
+
+from zope.configmachine.exceptions import ConfigurationError
+from zope.configmachine.exceptions import ConfigurationExecutionError
+from zope.configmachine.exceptions import ConfigurationConflictError
+from zope.configmachine.interfaces import IConfigurationContext
+from zope.configmachine.interfaces import IGroupingContext
+
+_import_chickens = {}, {}, ("*",) # dead chickens needed by __import__
+
+class ConfigurationContext(object):
+    """Mix-in that implements IConfigurationContext
+
+    Subclasses provide a ``package`` attribute and a ``basepath``
+    attribute.  If the base path is not None, relative paths are
+    converted to absolute paths using the the base path. If the
+    package is not none, relative imports are performed relative to
+    the package.
+
+    In general, the basepath and package attributes should be
+    consistent. When a package is provided, the base path should be
+    set to the path of the package directory.
+
+    Subclasses also provide an ``actions`` attribute, which is a list
+    of actions, an ``includepath`` attribute, and an ``info``
+    attribute.
+
+    The include path is appended to each action and is used when
+    resolving conflicts among actions.  Normally, only the a
+    ConfigurationMachine provides the actions attribute. Decorators
+    simply use the actions of the context they decorate. The
+    ``includepath`` attribute is a tuple of names.  Each name is
+    typically the name of an included configuration file.
+
+    The ``info`` attribute contains descriptive information helpful
+    when reporting errors.  If not set, it defaults to an empty string.
+
+    The actions attribute is a sequence of tuples with items:
+
+      - discriminator, a value that identifies the action. Two actions
+        that have the same (non None) discriminator conflict.
+
+      - an object that is called to execute the action,
+
+      - positional arguments for the action
+
+      - keyword arguments for the action
+
+      - a tuple of include file names (defaults to ())
+
+      - an object that has descriptive information about
+        the action (defaults to '')
+
+    For brevity, trailing items after the callable in the tuples are
+    ommitted if they are empty.
+
+    """
+
+    def __init__(self):
+        super(ConfigurationContext, self).__init__()
+        self._seen_files = set()
+        self._features = set()
+
+    def resolve(self, dottedname):
+        """Resolve a dotted name to an object
+
+        Examples:
+
+
+        >>> c = ConfigurationContext()
+        >>> import zope, zope.interface
+        >>> c.resolve('zope') is zope
+        1
+        >>> c.resolve('zope.interface') is zope.interface
+        1
+
+        >>> c.resolve('zope.configmachine.eek') #doctest: +NORMALIZE_WHITESPACE
+        Traceback (most recent call last):
+        ...
+        ConfigurationError:
+        ImportError: Module zope.configmachine has no global eek
+
+        >>> c.resolve('.ConfigurationContext')
+        Traceback (most recent call last):
+        ...
+        AttributeError: 'ConfigurationContext' object has no attribute """ \
+                                                                  """'package'
+        >>> import zope.configmachine
+        >>> c.package = zope.configmachine
+        >>> c.resolve('.') is zope.configmachine
+        1
+        >>> c.resolve('.ConfigurationContext') is ConfigurationContext
+        1
+        >>> c.resolve('..interface') is zope.interface
+        1
+        >>> c.resolve('unicode')
+        <type 'unicode'>
+        """
+
+        name = dottedname.strip()
+        if not name:
+            raise ValueError("The given name is blank")
+
+        if name == '.':
+            return self.package
+
+        names = name.split('.')
+        if not names[-1]:
+            raise ValueError(
+                "Trailing dots are no longer supported in dotted names")
+
+        if len(names) == 1:
+            # Check for built-in objects
+            marker = object()
+            obj = getattr(__builtin__, names[0], marker)
+            if obj is not marker:
+                return obj
+
+        if not names[0]:
+            # Got a relative name. Convert it to abs using package info
+            if self.package is None:
+                raise ConfigurationError(
+                    "Can't use leading dots in dotted names, "
+                    "no package has been set.")
+            pnames = self.package.__name__.split(".")
+            pnames.append('')
+            while names and not names[0]:
+                try:
+                    names.pop(0)
+                except IndexError:
+                    raise ConfigurationError("Invalid global name", name)
+                try:
+                    pnames.pop()
+                except IndexError:
+                    raise ConfigurationError("Invalid global name", name)
+            names[0:0] = pnames
+
+        # Now we should have an absolute dotted name
+
+        # Split off object name:
+        oname, mname = names[-1], '.'.join(names[:-1])
+
+        # Import the module
+        if not mname:
+            # Just got a single name. Must me a module
+            mname = oname
+            oname = ''
+
+        try:
+            mod = __import__(mname, *_import_chickens)
+        except ImportError, v:
+            if sys.exc_info()[2].tb_next is not None:
+                # ImportError was caused deeper
+                raise
+            raise ConfigurationError(
+                "ImportError: Couldn't import %s, %s" % (mname, v))
+
+        if not oname:
+            # see not mname case above
+            return mod
+
+
+        try:
+            obj = getattr(mod, oname)
+            return obj
+        except AttributeError:
+            # No such name, maybe it's a module that we still need to import
+            try:
+                return __import__(mname+'.'+oname, *_import_chickens)
+            except ImportError:
+                if sys.exc_info()[2].tb_next is not None:
+                    # ImportError was caused deeper
+                    raise
+                raise ConfigurationError(
+                    "ImportError: Module %s has no global %s" % (mname, oname))
+
+    def path(self, filename):
+        """
+        Examples:
+
+        >>> c = ConfigurationContext()
+        >>> c.path("/x/y/z") == os.path.normpath("/x/y/z")
+        1
+        >>> c.path("y/z")
+        Traceback (most recent call last):
+        ...
+        AttributeError: 'ConfigurationContext' object has no attribute """ \
+                                                                 """'package'
+        >>> import zope.configmachine
+        >>> c.package = zope.configmachine
+        >>> import os
+        >>> d = os.path.dirname(zope.configmachine.__file__)
+        >>> c.path("y/z") == d + os.path.normpath("/y/z")
+        1
+        >>> c.path("y/./z") == d + os.path.normpath("/y/z")
+        1
+        >>> c.path("y/../z") == d + os.path.normpath("/z")
+        1
+        """
+
+        filename = os.path.normpath(filename)
+        if os.path.isabs(filename):
+            return filename
+
+        # Got a relative path, combine with base path.
+        # If we have no basepath, compute the base path from the package
+        # path.
+
+        basepath = getattr(self, 'basepath', '')
+
+        if not basepath:
+            if self.package is None:
+                basepath = os.getcwd()
+            else:
+                if hasattr(self.package, '__path__'):
+                    basepath = self.package.__path__[0]
+                else:
+                    basepath = os.path.dirname(self.package.__file__)
+                basepath = os.path.abspath(basepath)
+            self.basepath = basepath
+
+        return os.path.join(basepath, filename)
+
+    def checkDuplicate(self, filename):
+        """Check for duplicate imports of the same file.
+
+        Raises an exception if this file had been processed before.  This
+        is better than an unlimited number of conflict errors.
+
+        >>> c = ConfigurationContext()
+        >>> c.checkDuplicate('/foo.zcml')
+        >>> try:
+        ...     c.checkDuplicate('/foo.zcml')
+        ... except ConfigurationError, e:
+        ...     # On Linux the exact msg has /foo, on Windows \foo.
+        ...     str(e).endswith("foo.zcml' included more than once")
+        True
+
+        You may use different ways to refer to the same file:
+
+        >>> import zope.configmachine
+        >>> c.package = zope.configmachine
+        >>> import os
+        >>> d = os.path.dirname(zope.configmachine.__file__)
+        >>> c.checkDuplicate('bar.zcml')
+        >>> try:
+        ...   c.checkDuplicate(d + os.path.normpath('/bar.zcml'))
+        ... except ConfigurationError, e:
+        ...   str(e).endswith("bar.zcml' included more than once")
+        ...
+        True
+
+        """ #' <-- bow to font-lock
+        path = self.path(filename)
+        if path in self._seen_files:
+            raise ConfigurationError('%r included more than once' % path)
+        self._seen_files.add(path)
+
+    def processFile(self, filename):
+        """Check whether a file needs to be processed
+
+        Return True if processing is needed and False otherwise. If
+        the file needs to be processed, it will be marked as
+        processed, assuming that the caller will procces the file if
+        it needs to be procssed.
+
+        >>> c = ConfigurationContext()
+        >>> c.processFile('/foo.zcml')
+        True
+        >>> c.processFile('/foo.zcml')
+        False
+
+        You may use different ways to refer to the same file:
+
+        >>> import zope.configmachine
+        >>> c.package = zope.configmachine
+        >>> import os
+        >>> d = os.path.dirname(zope.configmachine.__file__)
+        >>> c.processFile('bar.zcml')
+        True
+        >>> c.processFile('bar.zcml')
+        False
+
+        """ #' <-- bow to font-lock
+        path = self.path(filename)
+        if path in self._seen_files:
+            return False
+        self._seen_files.add(path)
+        return True
+
+    def action(self, discriminator, callable=None, args=(), kw={}, order=0,
+               includepath=None, info=None):
+        """Add an action with the given discriminator, callable and arguments
+
+        For testing purposes, the callable and arguments may be omitted.
+        In that case, a default noop callable is used.
+
+        The discriminator must be given, but it can be None, to indicate that
+        the action never conflicts.
+
+        Let's look at some examples:
+
+        >>> c = ConfigurationContext()
+
+        Normally, the context gets actions from subclasses. We'll provide
+        an actions attribute ourselves:
+
+        >>> c.actions = []
+
+        We'll use a test callable that has a convenient string representation
+
+        >>> from zope.configmachine.tests.directives import f
+
+        >>> c.action(1, f, (1, ), {'x': 1})
+        >>> c.actions
+        [(1, f, (1,), {'x': 1})]
+
+        >>> c.action(None)
+        >>> c.actions
+        [(1, f, (1,), {'x': 1}), (None, None)]
+
+        Now set the include path and info:
+
+        >>> c.includepath = ('foo.zcml',)
+        >>> c.info = "?"
+        >>> c.action(None)
+        >>> c.actions[-1]
+        (None, None, (), {}, ('foo.zcml',), '?')
+
+        We can add an order argument to crudely control the order
+        of execution:
+
+        >>> c.action(None, order=99999)
+        >>> c.actions[-1]
+        (None, None, (), {}, ('foo.zcml',), '?', 99999)
+
+        We can also pass an includepath argument, which will be used as the the
+        includepath for the action.  (if includepath is None, self.includepath
+        will be used):
+
+        >>> c.action(None, includepath=('abc',))
+        >>> c.actions[-1]
+        (None, None, (), {}, ('abc',), '?')
+
+        We can also pass an info argument, which will be used as the the
+        source line info for the action.  (if info is None, self.info will be
+        used):
+
+        >>> c.action(None, info='abc')
+        >>> c.actions[-1]
+        (None, None, (), {}, ('foo.zcml',), 'abc')
+        
+        """
+        if info is None:
+            info = getattr(self, 'info', '')
+
+        if includepath is None:
+            includepath = getattr(self, 'includepath', ())
+            
+        action = (discriminator, callable, args, kw, includepath, info, order)
+
+        # remove trailing false items
+        while (len(action) > 2) and not action[-1]:
+            action = action[:-1]
+
+        self.actions.append(action)
+
+    def hasFeature(self, feature):
+        """Check whether a named feature has been provided.
+
+        Initially no features are provided
+
+        >>> c = ConfigurationContext()
+        >>> c.hasFeature('onlinehelp')
+        False
+
+        You can declare that a feature is provided
+
+        >>> c.provideFeature('onlinehelp')
+
+        and it becomes available
+
+        >>> c.hasFeature('onlinehelp')
+        True
+
+        """
+        return feature in self._features
+
+    def provideFeature(self, feature):
+        """Declare thata named feature has been provided.
+
+        See `hasFeature` for examples.
+        """
+        self._features.add(feature)
+
+
+class ConfigurationAdapterRegistry(object):
+    """Simple adapter registry that manages directives as adapters
+
+    >>> r = ConfigurationAdapterRegistry()
+    >>> c = ConfigurationMachine()
+    >>> r.factory(c, ('http://www.zope.com','xxx'))
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx')
+    >>> from zope.configmachine.interfaces import IConfigurationContext
+    >>> def f():
+    ...     pass
+
+    >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f)
+    >>> r.factory(c, ('http://www.zope.com','xxx')) is f
+    1
+    >>> r.factory(c, ('http://www.zope.com','yyy')) is f
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy')
+    >>> r.register(IConfigurationContext, 'yyy', f)
+    >>> r.factory(c, ('http://www.zope.com','yyy')) is f
+    1
+    >>> class IFullInfo(Interface): pass
+
+    Test the documentation feature:
+
+    >>> r._docRegistry
+    []
+    >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None,
+    ...            'inf', None)
+    >>> r._docRegistry[0][0] == ('ns', 'dir')
+    1
+    >>> r._docRegistry[0][1] is IFullInfo
+    1
+    >>> r._docRegistry[0][2] is IConfigurationContext
+    1
+    >>> r._docRegistry[0][3] is None
+    1
+    >>> r._docRegistry[0][4] == 'inf'
+    1
+    >>> r._docRegistry[0][5] is None
+    1
+    >>> r.document('all-dir', None, None, None, None)
+    >>> r._docRegistry[1][0]
+    ('', 'all-dir')
+    """
+
+
+    def __init__(self):
+        super(ConfigurationAdapterRegistry, self).__init__()
+        self._registry = {}
+        # Stores tuples of form:
+        #   (namespace, name), schema, usedIn, info, parent
+        self._docRegistry = []
+
+    def register(self, interface, name, factory):
+        r = self._registry.get(name)
+        if r is None:
+            r = AdapterRegistry()
+            self._registry[name] = r
+
+        r.register([interface], Interface, '', factory)
+
+    def document(self, name, schema, usedIn, handler, info, parent=None):
+        if isinstance(name, (str, unicode)):
+            name = ('', name)
+        self._docRegistry.append((name, schema, usedIn, handler, info, parent))
+
+    def factory(self, context, name):
+        r = self._registry.get(name)
+        if r is None:
+            # Try namespace-independent name
+            ns, n = name
+            r = self._registry.get(n)
+            if r is None:
+                raise ConfigurationError("Unknown directive", ns, n)
+
+        f = r.lookup1(providedBy(context), Interface)
+        if f is None:
+            raise ConfigurationError(
+                "The directive %s cannot be used in this context" % (name, ))
+        return f
+
+class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
+    """Configuration machine
+
+    Example usage can be found in the ``zope.configuration`` unit tests.
+    """
+
+    implements(IConfigurationContext)
+
+    package = None
+    basepath = None
+    includepath = ()
+    info = ''
+
+    def __init__(self):
+        super(ConfigurationMachine, self).__init__()
+        self.actions = []
+        self.stack = [RootStackItem(self)]
+        self.i18n_strings = {}
+
+    def begin(self, __name, __data=None, __info=None, **kw):
+        if __data:
+            if kw:
+                raise TypeError("Can't provide a mapping object and keyword "
+                                "arguments")
+        else:
+            __data = kw
+        self.stack.append(self.stack[-1].contained(__name, __data, __info))
+
+    def end(self):
+        self.stack.pop().finish()
+
+    def __call__(self, __name, __info=None, **__kw):
+        self.begin(__name, __kw, __info)
+        self.end()
+
+    def getInfo(self):
+        return self.stack[-1].context.info
+
+    def setInfo(self, info):
+        self.stack[-1].context.info = info
+
+    def execute_actions(self, clear=True, testing=False):
+        """Execute the configuration actions
+
+        This calls the action callables after resolving conflicts
+
+        For example:
+
+        >>> output = []
+        >>> def f(*a, **k):
+        ...    output.append(('f', a, k))
+        >>> context = ConfigurationMachine()
+        >>> context.actions = [
+        ...   (1, f, (1,)),
+        ...   (1, f, (11,), {}, ('x', )),
+        ...   (2, f, (2,)),
+        ...   ]
+        >>> context.execute_actions()
+        >>> output
+        [('f', (1,), {}), ('f', (2,), {})]
+
+        If the action raises an error, we convert it to a
+        ConfigurationExecutionError.
+
+        >>> output = []
+        >>> def bad():
+        ...    bad.xxx
+        >>> context.actions = [
+        ...   (1, f, (1,)),
+        ...   (1, f, (11,), {}, ('x', )),
+        ...   (2, f, (2,)),
+        ...   (3, bad, (), {}, (), 'oops')
+        ...   ]
+        >>> try:
+        ...    v = context.execute_actions()
+        ... except ConfigurationExecutionError, v:
+        ...    pass
+        >>> print v
+        exceptions.AttributeError: 'function' object has no attribute 'xxx'
+          in:
+          oops
+
+
+        Note that actions executed before the error still have an effect:
+
+        >>> output
+        [('f', (1,), {}), ('f', (2,), {})]
+
+
+        """
+        try:
+            for action in resolveConflicts(self.actions):
+                (discriminator, callable, args, kw, includepath, info, order
+                 ) = expand_action(*action)
+                if callable is None:
+                    continue
+                try:
+                    callable(*args, **kw)
+                except (KeyboardInterrupt, SystemExit):
+                    raise
+                except:
+                    if testing:
+                        raise
+                    t, v, tb = sys.exc_info()
+                    raise ConfigurationExecutionError(t, v, info), None, tb
+        finally:
+            if clear:
+                del self.actions[:]
+
+
+##############################################################################
+# Stack items
+
+class RootStackItem(object):
+
+    def __init__(self, context):
+        self.context = context
+
+    def contained(self, name, data, info):
+        """Handle a contained directive
+
+        We have to compute a new stack item by getting a named adapter
+        for the current context object.
+
+        """
+        factory = self.context.factory(self.context, name)
+        if factory is None:
+            raise ConfigurationError("Invalid directive", name)
+        adapter = factory(self.context, data, info)
+        return adapter
+
+    def finish(self):
+        pass
+
+##############################################################################
+# Helper classes
+
+class GroupingContextDecorator(ConfigurationContext):
+    """Helper mix-in class for building grouping directives
+
+    See the discussion (and test) in GroupingStackItem.
+    """
+
+    implements(IConfigurationContext, IGroupingContext)
+
+    def __init__(self, context, **kw):
+        self.context = context
+        for name, v in kw.items():
+            setattr(self, name, v)
+
+    def __getattr__(self, name,
+                    getattr=getattr, setattr=setattr):
+        v = getattr(self.context, name)
+        # cache result in self
+        setattr(self, name, v)
+        return v
+
+    def before(self):
+        pass
+
+    def after(self):
+        pass
+
+##############################################################################
+# Conflict resolution
+
+def expand_action(discriminator, callable=None, args=(), kw={},
+                   includepath=(), info='', order=0):
+    return (discriminator, callable, args, kw,
+            includepath, info, order)
+
+def resolveConflicts(actions):
+    """Resolve conflicting actions
+
+    Given an actions list, identify and try to resolve conflicting actions.
+    Actions conflict if they have the same non-null discriminator.
+    Conflicting actions can be resolved if the include path of one of
+    the actions is a prefix of the includepaths of the other
+    conflicting actions and is unequal to the include paths in the
+    other conflicting actions.
+
+    Here are some examples to illustrate how this works:
+
+    >>> from zope.configmachine.tests.directives import f
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> pprint(resolveConflicts([
+    ...    (None, f),
+    ...    (1, f, (1,), {}, (), 'first'),
+    ...    (1, f, (2,), {}, ('x',), 'second'),
+    ...    (1, f, (3,), {}, ('y',), 'third'),
+    ...    (4, f, (4,), {}, ('y',), 'should be last', 99999),
+    ...    (3, f, (3,), {}, ('y',)),
+    ...    (None, f, (5,), {}, ('y',)),
+    ... ]))
+    [(None, f),
+     (1, f, (1,), {}, (), 'first'),
+     (3, f, (3,), {}, ('y',)),
+     (None, f, (5,), {}, ('y',)),
+     (4, f, (4,), {}, ('y',), 'should be last')]
+
+    >>> try:
+    ...     v = resolveConflicts([
+    ...        (None, f),
+    ...        (1, f, (2,), {}, ('x',), 'eek'),
+    ...        (1, f, (3,), {}, ('y',), 'ack'),
+    ...        (4, f, (4,), {}, ('y',)),
+    ...        (3, f, (3,), {}, ('y',)),
+    ...        (None, f, (5,), {}, ('y',)),
+    ...     ])
+    ... except ConfigurationConflictError, v:
+    ...    pass
+    >>> print v
+    Conflicting configuration actions
+      For: 1
+        eek
+        ack
+
+    """
+
+    # organize actions by discriminators
+    unique = {}
+    output = []
+    for i in range(len(actions)):
+        (discriminator, callable, args, kw, includepath, info, order
+         ) = expand_action(*(actions[i]))
+
+        order = order or i
+        if discriminator is None:
+            # The discriminator is None, so this directive can
+            # never conflict. We can add it directly to the
+            # configuration actions.
+            output.append(
+                (order, discriminator, callable, args, kw, includepath, info)
+                )
+            continue
+
+
+        a = unique.setdefault(discriminator, [])
+        a.append(
+            (includepath, order, callable, args, kw, info)
+            )
+
+    # Check for conflicts
+    conflicts = {}
+    for discriminator, dups in unique.items():
+
+        # We need to sort the actions by the paths so that the shortest
+        # path with a given prefix comes first:
+        dups.sort()
+        (basepath, i, callable, args, kw, baseinfo) = dups[0]
+        output.append(
+            (i, discriminator, callable, args, kw, basepath, baseinfo)
+            )
+        for includepath, i, callable, args, kw, info in dups[1:]:
+            # Test whether path is a prefix of opath
+            if (includepath[:len(basepath)] != basepath # not a prefix
+                or
+                (includepath == basepath)
+                ):
+                if discriminator not in conflicts:
+                    conflicts[discriminator] = [baseinfo]
+                conflicts[discriminator].append(info)
+
+
+    if conflicts:
+        raise ConfigurationConflictError(conflicts)
+
+    # Now put the output back in the original order, and return it:
+    output.sort()
+    r = []
+    for o in output:
+        action = o[1:]
+        while len(action) > 2 and not action[-1]:
+            action = action[:-1]
+        r.append(action)
+
+    return r
+

Copied: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/exceptions.py (from rev 122694, zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py)
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/exceptions.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/exceptions.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation 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.
+#
+##############################################################################
+"""Standard configuration errors
+"""
+
+class ConfigurationError(Exception):
+    """There was an error in a configuration
+    """
+
+class ConfigurationExecutionError(ConfigurationError):
+    """An error occurred during execution of a configuration action
+    """
+
+    def __init__(self, etype, evalue, info):
+        self.etype, self.evalue, self.info = etype, evalue, info
+
+    def __str__(self):
+        return "%s: %s\n  in:\n  %s" % (self.etype, self.evalue, self.info)
+
+class ConfigurationConflictError(ConfigurationError):
+
+    def __init__(self, conflicts):
+        self._conflicts = conflicts
+
+    def __str__(self):
+        r = ["Conflicting configuration actions"]
+        items = self._conflicts.items()
+        items.sort()
+        for discriminator, infos in items:
+            r.append("  For: %s" % (discriminator, ))
+            for info in infos:
+                for line in unicode(info).rstrip().split(u'\n'):
+                    r.append(u"    "+line)
+
+        return "\n".join(r)
+
+

Added: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/interfaces.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/interfaces.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/interfaces.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,135 @@
+##############################################################################
+#
+# Copyright (c) 2011 Zope Foundation 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.
+#
+##############################################################################
+
+from zope.interface import Interface
+from zope.interface import Attribute
+
+class IConfigurationContext(Interface):
+    """Configuration Context
+
+    The configuration context manages information about the state of
+    the configuration system, such as the package containing the
+    configuration file. More importantly, it provides methods for
+    importing objects and opening files relative to the package.
+    """
+
+    # XXX bw incompat: package = Attribute rather than BytesLine
+
+    package = Attribute(
+          """The name of the package containing the configuration
+          file being executed. If the configuration file was not
+          included by package, then this is None.
+          """
+        )
+
+    def resolve(dottedname):
+        """Resolve a dotted name to an object
+
+        A dotted name is constructed by concatenating a dotted module
+        name with a global name within the module using a dot.  For
+        example, the object named "spam" in the foo.bar module has a
+        dotted name of foo.bar.spam.  If the current package is a
+        prefix of a dotted name, then the package name can be relaced
+        with a leading dot, So, for example, if the configuration file
+        is in the foo package, then the dotted name foo.bar.spam can
+        be shortened to .bar.spam.
+
+        If the current package is multiple levels deep, multiple
+        leading dots can be used to refer to higher-level modules.
+        For example, if the current package is x.y.z, the dotted
+        object name ..foo refers to x.y.foo.
+        """
+
+    def path(filename):
+        """Compute a full file name for the given file
+
+        If the filename is relative to the package, then the returned
+        name will include the package path, otherwise, the original
+        file name is returned.
+        """
+
+    def checkDuplicate(filename):
+        """Check for duplicate imports of the same file.
+
+        Raises an exception if this file had been processed before.  This
+        is better than an unlimited number of conflict errors.
+        """
+
+    def processFile(filename):
+        """Check whether a file needs to be processed.
+
+        Return True if processing is needed and False otherwise.  If
+        the file needs to be processed, it will be marked as
+        processed, assuming that the caller will procces the file if
+        it needs to be procssed.
+        """
+
+    def action(self, discriminator, callable, args=(), kw={}, order=0,
+               includepath=None, info=None):
+        """Record a configuration action
+
+        The job of most directives is to compute actions for later
+        processing.  The action method is used to record those
+        actions.  The discriminator is used to to find actions that
+        conflict. Actions conflict if they have the same
+        discriminator. The exception to this is the special case of
+        the discriminator with the value None. An actions with a
+        discriminator of None never conflicts with other actions. This
+        is possible to add an order argument to crudely control the
+        order of execution.  'info' is optional source line information,
+        'includepath' is None (the default) or a tuple of include paths for
+        this action.
+        """
+
+    def provideFeature(name):
+        """Record that a named feature is available in this context."""
+
+    def hasFeature(name):
+        """Check whether a named feature is available in this context."""
+
+
+class IGroupingContext(Interface):
+
+    def before():
+        """Do something before processing nested directives
+        """
+
+    def after():
+        """Do something after processing nested directives
+        """
+
+class IStackItem(Interface):
+    """Configuration machine stack items
+
+    Stack items are created when a directive is being processed.
+
+    A stack item is created for each directive use.
+    """
+
+    def contained(name, data, info):
+        """Begin processing a contained directive
+
+        The data are a dictionary of attribute names mapped to unicode
+        strings.
+
+        The info argument is an object that can be converted to a
+        string and that contains information about the directive.
+
+        The begin method returns the next item to be placed on the stack.
+        """
+
+    def finish():
+        """Finish processing a directive
+        """
+

Added: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/__init__.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/__init__.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/__init__.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1 @@
+# package

Copied: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/bad.py (from rev 122694, zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/bad.py)
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/bad.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/bad.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,3 @@
+# I'm bad. I want to be bad. Don't try to change me.
+
+import bad_to_the_bone

Added: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/directives.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/directives.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/directives.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,19 @@
+from zope.interface import Interface
+from zope.interface import Attribute
+
+class F(object):
+    def __repr__(self):
+        return 'f'
+    def __call__(self, *a, **k):
+        pass
+
+f = F()
+
+class ISimple(Interface):
+    a = Attribute('a')
+    b = Attribute('b')
+    c = Attribute('c')
+
+def simple(context, a=None, c=None, b=u"xxx"):
+    return [(('simple', a, b, c), f, (a, b, c))]
+

Added: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/test_config.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/test_config.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/test_config.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,147 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation 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.
+#
+##############################################################################
+"""Test configuration machinery.
+"""
+
+import sys
+import unittest
+import re
+from doctest import DocTestSuite
+from zope.testing import renormalizing
+from zope import configmachine
+
+def test_basepath_absolute():
+    """Path must always return an absolute path.
+
+    >>> import os
+    >>> class stub:
+    ...     __file__ = os.path.join('relative', 'path')
+    >>> c = configmachine.ConfigurationContext()
+    >>> c.package = stub()
+
+    >>> os.path.isabs(c.path('y/z'))
+    True
+    """
+
+def test_basepath_uses_dunder_path():
+    """Determine package path using __path__ if __file__ isn't available.
+    (i.e. namespace package installed with --single-version-externally-managed)
+
+    >>> import os
+    >>> class stub:
+    ...     __path__ = [os.path.join('relative', 'path')]
+    >>> c = configmachine.ConfigurationContext()
+    >>> c.package = stub()
+
+    >>> os.path.isabs(c.path('y/z'))
+    True
+    """
+
+def test_trailing_dot_in_resolve():
+    """Dotted names are no longer allowed to end in dots
+
+    >>> c = configmachine.ConfigurationContext()
+
+    >>> c.resolve('zope.')
+    Traceback (most recent call last):
+    ...
+    ValueError: Trailing dots are no longer supported in dotted names
+
+    >>> c.resolve('  ')
+    Traceback (most recent call last):
+    ...
+    ValueError: The given name is blank
+    """
+
+def test_bad_dotted_last_import():
+    """
+    >>> c = configmachine.ConfigurationContext()
+
+    Import error caused by a bad last component in the dotted name.
+
+    >>> c.resolve('zope.configmachine.tests.nosuch')
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ImportError: Module zope.configmachine.tests""" \
+                                               """ has no global nosuch
+    """
+
+def test_bad_dotted_import():
+    """
+    >>> c = configmachine.ConfigurationContext()
+
+    Import error caused by a totally wrong dotted name.
+
+    >>> c.resolve('zope.configmachine.nosuch.noreally')
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ImportError: Couldn't import""" \
+                   """ zope.configmachine.nosuch, No module named nosuch
+    """
+
+def test_bad_sub_last_import():
+    """
+    >>> c = configmachine.ConfigurationContext()
+
+    Import error caused by a bad sub import inside the referenced
+    dotted name. Here we keep the standard traceback.
+
+    >>> c.resolve('zope.configmachine.tests.victim')
+    Traceback (most recent call last):
+    ...
+      File "...bad.py", line 3 in ?
+       import bad_to_the_bone
+    ImportError: No module named bad_to_the_bone
+
+    Cleanup:
+
+    >>> for name in ('zope.configmachine.tests.victim',
+    ...              'zope.configmachine.tests.bad'):
+    ...    if name in sys.modules:
+    ...        del sys.modules[name]
+    """
+
+def test_bad_sub_import():
+    """
+    >>> c = configmachine.ConfigurationContext()
+
+    Import error caused by a bad sub import inside part of the referenced
+    dotted name. Here we keep the standard traceback.
+
+    >>> c.resolve('zope.configmachine.tests.victim.nosuch')
+    Traceback (most recent call last):
+    ...
+      File "...bad.py", line 3 in ?
+       import bad_to_the_bone
+    ImportError: No module named bad_to_the_bone
+
+    Cleanup:
+
+    >>> for name in ('zope.configmachine.tests.victim',
+    ...              'zope.configmachine.tests.bad'):
+    ...    if name in sys.modules:
+    ...        del sys.modules[name]
+    """
+
+def test_suite():
+    checker = renormalizing.RENormalizing([
+        (re.compile(r"<type 'exceptions.(\w+)Error'>:"),
+                    r'exceptions.\1Error:'),
+        ])
+    return unittest.TestSuite((
+        DocTestSuite('zope.configmachine',checker=checker),
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()

Copied: zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/victim.py (from rev 122694, zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/victim.py)
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/victim.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configmachine/tests/victim.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1 @@
+import bad

Deleted: zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -1,1655 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation 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.
-#
-##############################################################################
-"""Configuration processor
-
-See README.txt.
-"""
-__docformat__ = 'restructuredtext'
-import __builtin__
-import os.path
-import sys
-
-import zope.schema
-
-from keyword import iskeyword
-from zope.configuration.exceptions import ConfigurationError
-from zope.configuration.interfaces import IConfigurationContext
-from zope.configuration.interfaces import IGroupingContext
-from zope.interface.adapter import AdapterRegistry
-from zope.interface import Interface, implements, providedBy
-from zope.configuration import fields
-
-
-zopens = 'http://namespaces.zope.org/zope'
-metans = 'http://namespaces.zope.org/meta'
-testns = 'http://namespaces.zope.org/test'
-
-_import_chickens = {}, {}, ("*",) # dead chickens needed by __import__
-
-class ConfigurationContext(object):
-    """Mix-in that implements IConfigurationContext
-
-    Subclasses provide a ``package`` attribute and a ``basepath``
-    attribute.  If the base path is not None, relative paths are
-    converted to absolute paths using the the base path. If the
-    package is not none, relative imports are performed relative to
-    the package.
-
-    In general, the basepath and package attributes should be
-    consistent. When a package is provided, the base path should be
-    set to the path of the package directory.
-
-    Subclasses also provide an ``actions`` attribute, which is a list
-    of actions, an ``includepath`` attribute, and an ``info``
-    attribute.
-
-    The include path is appended to each action and is used when
-    resolving conflicts among actions.  Normally, only the a
-    ConfigurationMachine provides the actions attribute. Decorators
-    simply use the actions of the context they decorate. The
-    ``includepath`` attribute is a tuple of names.  Each name is
-    typically the name of an included configuration file.
-
-    The ``info`` attribute contains descriptive information helpful
-    when reporting errors.  If not set, it defaults to an empty string.
-
-    The actions attribute is a sequence of tuples with items:
-
-      - discriminator, a value that identifies the action. Two actions
-        that have the same (non None) discriminator conflict.
-
-      - an object that is called to execute the action,
-
-      - positional arguments for the action
-
-      - keyword arguments for the action
-
-      - a tuple of include file names (defaults to ())
-
-      - an object that has descriptive information about
-        the action (defaults to '')
-
-    For brevity, trailing items after the callable in the tuples are
-    ommitted if they are empty.
-
-    """
-
-    def __init__(self):
-        super(ConfigurationContext, self).__init__()
-        self._seen_files = set()
-        self._features = set()
-
-    def resolve(self, dottedname):
-        """Resolve a dotted name to an object
-
-        Examples:
-
-
-        >>> c = ConfigurationContext()
-        >>> import zope, zope.interface
-        >>> c.resolve('zope') is zope
-        1
-        >>> c.resolve('zope.interface') is zope.interface
-        1
-
-        >>> c.resolve('zope.configuration.eek') #doctest: +NORMALIZE_WHITESPACE
-        Traceback (most recent call last):
-        ...
-        ConfigurationError:
-        ImportError: Module zope.configuration has no global eek
-
-        >>> c.resolve('.config.ConfigurationContext')
-        Traceback (most recent call last):
-        ...
-        AttributeError: 'ConfigurationContext' object has no attribute """ \
-                                                                  """'package'
-        >>> import zope.configuration
-        >>> c.package = zope.configuration
-        >>> c.resolve('.') is zope.configuration
-        1
-        >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext
-        1
-        >>> c.resolve('..interface') is zope.interface
-        1
-        >>> c.resolve('unicode')
-        <type 'unicode'>
-        """
-
-        name = dottedname.strip()
-        if not name:
-            raise ValueError("The given name is blank")
-
-        if name == '.':
-            return self.package
-
-        names = name.split('.')
-        if not names[-1]:
-            raise ValueError(
-                "Trailing dots are no longer supported in dotted names")
-
-        if len(names) == 1:
-            # Check for built-in objects
-            marker = object()
-            obj = getattr(__builtin__, names[0], marker)
-            if obj is not marker:
-                return obj
-
-        if not names[0]:
-            # Got a relative name. Convert it to abs using package info
-            if self.package is None:
-                raise ConfigurationError(
-                    "Can't use leading dots in dotted names, "
-                    "no package has been set.")
-            pnames = self.package.__name__.split(".")
-            pnames.append('')
-            while names and not names[0]:
-                try:
-                    names.pop(0)
-                except IndexError:
-                    raise ConfigurationError("Invalid global name", name)
-                try:
-                    pnames.pop()
-                except IndexError:
-                    raise ConfigurationError("Invalid global name", name)
-            names[0:0] = pnames
-
-        # Now we should have an absolute dotted name
-
-        # Split off object name:
-        oname, mname = names[-1], '.'.join(names[:-1])
-
-        # Import the module
-        if not mname:
-            # Just got a single name. Must me a module
-            mname = oname
-            oname = ''
-
-        try:
-            mod = __import__(mname, *_import_chickens)
-        except ImportError, v:
-            if sys.exc_info()[2].tb_next is not None:
-                # ImportError was caused deeper
-                raise
-            raise ConfigurationError(
-                "ImportError: Couldn't import %s, %s" % (mname, v))
-
-        if not oname:
-            # see not mname case above
-            return mod
-
-
-        try:
-            obj = getattr(mod, oname)
-            return obj
-        except AttributeError:
-            # No such name, maybe it's a module that we still need to import
-            try:
-                return __import__(mname+'.'+oname, *_import_chickens)
-            except ImportError:
-                if sys.exc_info()[2].tb_next is not None:
-                    # ImportError was caused deeper
-                    raise
-                raise ConfigurationError(
-                    "ImportError: Module %s has no global %s" % (mname, oname))
-
-    def path(self, filename):
-        """
-        Examples:
-
-        >>> c = ConfigurationContext()
-        >>> c.path("/x/y/z") == os.path.normpath("/x/y/z")
-        1
-        >>> c.path("y/z")
-        Traceback (most recent call last):
-        ...
-        AttributeError: 'ConfigurationContext' object has no attribute """ \
-                                                                 """'package'
-        >>> import zope.configuration
-        >>> c.package = zope.configuration
-        >>> import os
-        >>> d = os.path.dirname(zope.configuration.__file__)
-        >>> c.path("y/z") == d + os.path.normpath("/y/z")
-        1
-        >>> c.path("y/./z") == d + os.path.normpath("/y/z")
-        1
-        >>> c.path("y/../z") == d + os.path.normpath("/z")
-        1
-        """
-
-        filename = os.path.normpath(filename)
-        if os.path.isabs(filename):
-            return filename
-
-        # Got a relative path, combine with base path.
-        # If we have no basepath, compute the base path from the package
-        # path.
-
-        basepath = getattr(self, 'basepath', '')
-
-        if not basepath:
-            if self.package is None:
-                basepath = os.getcwd()
-            else:
-                if hasattr(self.package, '__path__'):
-                    basepath = self.package.__path__[0]
-                else:
-                    basepath = os.path.dirname(self.package.__file__)
-                basepath = os.path.abspath(basepath)
-            self.basepath = basepath
-
-        return os.path.join(basepath, filename)
-
-    def checkDuplicate(self, filename):
-        """Check for duplicate imports of the same file.
-
-        Raises an exception if this file had been processed before.  This
-        is better than an unlimited number of conflict errors.
-
-        >>> c = ConfigurationContext()
-        >>> c.checkDuplicate('/foo.zcml')
-        >>> try:
-        ...     c.checkDuplicate('/foo.zcml')
-        ... except ConfigurationError, e:
-        ...     # On Linux the exact msg has /foo, on Windows \foo.
-        ...     str(e).endswith("foo.zcml' included more than once")
-        True
-
-        You may use different ways to refer to the same file:
-
-        >>> import zope.configuration
-        >>> c.package = zope.configuration
-        >>> import os
-        >>> d = os.path.dirname(zope.configuration.__file__)
-        >>> c.checkDuplicate('bar.zcml')
-        >>> try:
-        ...   c.checkDuplicate(d + os.path.normpath('/bar.zcml'))
-        ... except ConfigurationError, e:
-        ...   str(e).endswith("bar.zcml' included more than once")
-        ...
-        True
-
-        """ #' <-- bow to font-lock
-        path = self.path(filename)
-        if path in self._seen_files:
-            raise ConfigurationError('%r included more than once' % path)
-        self._seen_files.add(path)
-
-    def processFile(self, filename):
-        """Check whether a file needs to be processed
-
-        Return True if processing is needed and False otherwise. If
-        the file needs to be processed, it will be marked as
-        processed, assuming that the caller will procces the file if
-        it needs to be procssed.
-
-        >>> c = ConfigurationContext()
-        >>> c.processFile('/foo.zcml')
-        True
-        >>> c.processFile('/foo.zcml')
-        False
-
-        You may use different ways to refer to the same file:
-
-        >>> import zope.configuration
-        >>> c.package = zope.configuration
-        >>> import os
-        >>> d = os.path.dirname(zope.configuration.__file__)
-        >>> c.processFile('bar.zcml')
-        True
-        >>> c.processFile('bar.zcml')
-        False
-
-        """ #' <-- bow to font-lock
-        path = self.path(filename)
-        if path in self._seen_files:
-            return False
-        self._seen_files.add(path)
-        return True
-
-    def action(self, discriminator, callable=None, args=(), kw={}, order=0,
-               includepath=None, info=None):
-        """Add an action with the given discriminator, callable and arguments
-
-        For testing purposes, the callable and arguments may be omitted.
-        In that case, a default noop callable is used.
-
-        The discriminator must be given, but it can be None, to indicate that
-        the action never conflicts.
-
-        Let's look at some examples:
-
-        >>> c = ConfigurationContext()
-
-        Normally, the context gets actions from subclasses. We'll provide
-        an actions attribute ourselves:
-
-        >>> c.actions = []
-
-        We'll use a test callable that has a convenient string representation
-
-        >>> from zope.configuration.tests.directives import f
-
-        >>> c.action(1, f, (1, ), {'x': 1})
-        >>> c.actions
-        [(1, f, (1,), {'x': 1})]
-
-        >>> c.action(None)
-        >>> c.actions
-        [(1, f, (1,), {'x': 1}), (None, None)]
-
-        Now set the include path and info:
-
-        >>> c.includepath = ('foo.zcml',)
-        >>> c.info = "?"
-        >>> c.action(None)
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), '?')
-
-        We can add an order argument to crudely control the order
-        of execution:
-
-        >>> c.action(None, order=99999)
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), '?', 99999)
-
-        We can also pass an includepath argument, which will be used as the the
-        includepath for the action.  (if includepath is None, self.includepath
-        will be used):
-
-        >>> c.action(None, includepath=('abc',))
-        >>> c.actions[-1]
-        (None, None, (), {}, ('abc',), '?')
-
-        We can also pass an info argument, which will be used as the the
-        source line info for the action.  (if info is None, self.info will be
-        used):
-
-        >>> c.action(None, info='abc')
-        >>> c.actions[-1]
-        (None, None, (), {}, ('foo.zcml',), 'abc')
-        
-        """
-        if info is None:
-            info = getattr(self, 'info', '')
-
-        if includepath is None:
-            includepath = getattr(self, 'includepath', ())
-            
-        action = (discriminator, callable, args, kw, includepath, info, order)
-
-        # remove trailing false items
-        while (len(action) > 2) and not action[-1]:
-            action = action[:-1]
-
-        self.actions.append(action)
-
-    def hasFeature(self, feature):
-        """Check whether a named feature has been provided.
-
-        Initially no features are provided
-
-        >>> c = ConfigurationContext()
-        >>> c.hasFeature('onlinehelp')
-        False
-
-        You can declare that a feature is provided
-
-        >>> c.provideFeature('onlinehelp')
-
-        and it becomes available
-
-        >>> c.hasFeature('onlinehelp')
-        True
-
-        """
-        return feature in self._features
-
-    def provideFeature(self, feature):
-        """Declare thata named feature has been provided.
-
-        See `hasFeature` for examples.
-        """
-        self._features.add(feature)
-
-
-class ConfigurationAdapterRegistry(object):
-    """Simple adapter registry that manages directives as adapters
-
-    >>> r = ConfigurationAdapterRegistry()
-    >>> c = ConfigurationMachine()
-    >>> r.factory(c, ('http://www.zope.com','xxx'))
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx')
-    >>> from zope.configuration.interfaces import IConfigurationContext
-    >>> def f():
-    ...     pass
-
-    >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f)
-    >>> r.factory(c, ('http://www.zope.com','xxx')) is f
-    1
-    >>> r.factory(c, ('http://www.zope.com','yyy')) is f
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy')
-    >>> r.register(IConfigurationContext, 'yyy', f)
-    >>> r.factory(c, ('http://www.zope.com','yyy')) is f
-    1
-
-    Test the documentation feature:
-
-    >>> r._docRegistry
-    []
-    >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None,
-    ...            'inf', None)
-    >>> r._docRegistry[0][0] == ('ns', 'dir')
-    1
-    >>> r._docRegistry[0][1] is IFullInfo
-    1
-    >>> r._docRegistry[0][2] is IConfigurationContext
-    1
-    >>> r._docRegistry[0][3] is None
-    1
-    >>> r._docRegistry[0][4] == 'inf'
-    1
-    >>> r._docRegistry[0][5] is None
-    1
-    >>> r.document('all-dir', None, None, None, None)
-    >>> r._docRegistry[1][0]
-    ('', 'all-dir')
-    """
-
-
-    def __init__(self):
-        super(ConfigurationAdapterRegistry, self).__init__()
-        self._registry = {}
-        # Stores tuples of form:
-        #   (namespace, name), schema, usedIn, info, parent
-        self._docRegistry = []
-
-    def register(self, interface, name, factory):
-        r = self._registry.get(name)
-        if r is None:
-            r = AdapterRegistry()
-            self._registry[name] = r
-
-        r.register([interface], Interface, '', factory)
-
-    def document(self, name, schema, usedIn, handler, info, parent=None):
-        if isinstance(name, (str, unicode)):
-            name = ('', name)
-        self._docRegistry.append((name, schema, usedIn, handler, info, parent))
-
-    def factory(self, context, name):
-        r = self._registry.get(name)
-        if r is None:
-            # Try namespace-independent name
-            ns, n = name
-            r = self._registry.get(n)
-            if r is None:
-                raise ConfigurationError("Unknown directive", ns, n)
-
-        f = r.lookup1(providedBy(context), Interface)
-        if f is None:
-            raise ConfigurationError(
-                "The directive %s cannot be used in this context" % (name, ))
-        return f
-
-class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
-    """Configuration machine
-
-    Example:
-
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
-
-    Register a directive:
-
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="simple",
-    ...         schema="zope.configuration.tests.directives.ISimple",
-    ...         handler="zope.configuration.tests.directives.simple")
-
-    and try it out:
-
-    >>> machine((ns, "simple"), a=u"aa", c=u"cc")
-
-    >>> machine.actions
-    [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'))]
-
-    A more extensive example can be found in the unit tests.
-    """
-
-    implements(IConfigurationContext)
-
-    package = None
-    basepath = None
-    includepath = ()
-    info = ''
-
-    def __init__(self):
-        super(ConfigurationMachine, self).__init__()
-        self.actions = []
-        self.stack = [RootStackItem(self)]
-        self.i18n_strings = {}
-        _bootstrap(self)
-
-    def begin(self, __name, __data=None, __info=None, **kw):
-        if __data:
-            if kw:
-                raise TypeError("Can't provide a mapping object and keyword "
-                                "arguments")
-        else:
-            __data = kw
-        self.stack.append(self.stack[-1].contained(__name, __data, __info))
-
-    def end(self):
-        self.stack.pop().finish()
-
-    def __call__(self, __name, __info=None, **__kw):
-        self.begin(__name, __kw, __info)
-        self.end()
-
-    def getInfo(self):
-        return self.stack[-1].context.info
-
-    def setInfo(self, info):
-        self.stack[-1].context.info = info
-
-    def execute_actions(self, clear=True, testing=False):
-        """Execute the configuration actions
-
-        This calls the action callables after resolving conflicts
-
-        For example:
-
-        >>> output = []
-        >>> def f(*a, **k):
-        ...    output.append(('f', a, k))
-        >>> context = ConfigurationMachine()
-        >>> context.actions = [
-        ...   (1, f, (1,)),
-        ...   (1, f, (11,), {}, ('x', )),
-        ...   (2, f, (2,)),
-        ...   ]
-        >>> context.execute_actions()
-        >>> output
-        [('f', (1,), {}), ('f', (2,), {})]
-
-        If the action raises an error, we convert it to a
-        ConfigurationExecutionError.
-
-        >>> output = []
-        >>> def bad():
-        ...    bad.xxx
-        >>> context.actions = [
-        ...   (1, f, (1,)),
-        ...   (1, f, (11,), {}, ('x', )),
-        ...   (2, f, (2,)),
-        ...   (3, bad, (), {}, (), 'oops')
-        ...   ]
-        >>> try:
-        ...    v = context.execute_actions()
-        ... except ConfigurationExecutionError, v:
-        ...    pass
-        >>> print v
-        exceptions.AttributeError: 'function' object has no attribute 'xxx'
-          in:
-          oops
-
-
-        Note that actions executed before the error still have an effect:
-
-        >>> output
-        [('f', (1,), {}), ('f', (2,), {})]
-
-
-        """
-        try:
-            for action in resolveConflicts(self.actions):
-                (discriminator, callable, args, kw, includepath, info, order
-                 ) = expand_action(*action)
-                if callable is None:
-                    continue
-                try:
-                    callable(*args, **kw)
-                except (KeyboardInterrupt, SystemExit):
-                    raise
-                except:
-                    if testing:
-                        raise
-                    t, v, tb = sys.exc_info()
-                    raise ConfigurationExecutionError(t, v, info), None, tb
-        finally:
-            if clear:
-                del self.actions[:]
-
-
-class ConfigurationExecutionError(ConfigurationError):
-    """An error occurred during execution of a configuration action
-    """
-
-    def __init__(self, etype, evalue, info):
-        self.etype, self.evalue, self.info = etype, evalue, info
-
-    def __str__(self):
-        return "%s: %s\n  in:\n  %s" % (self.etype, self.evalue, self.info)
-
-##############################################################################
-# Stack items
-
-class IStackItem(Interface):
-    """Configuration machine stack items
-
-    Stack items are created when a directive is being processed.
-
-    A stack item is created for each directive use.
-    """
-
-    def contained(name, data, info):
-        """Begin processing a contained directive
-
-        The data are a dictionary of attribute names mapped to unicode
-        strings.
-
-        The info argument is an object that can be converted to a
-        string and that contains information about the directive.
-
-        The begin method returns the next item to be placed on the stack.
-        """
-
-    def finish():
-        """Finish processing a directive
-        """
-
-class SimpleStackItem(object):
-    """Simple stack item
-
-    A simple stack item can't have anything added after it.  It can
-    only be removed.  It is used for simple directives and
-    subdirectives, which can't contain other directives.
-
-    It also defers any computation until the end of the directive
-    has been reached.
-    """
-
-    implements(IStackItem)
-
-    def __init__(self, context, handler, info, *argdata):
-        newcontext = GroupingContextDecorator(context)
-        newcontext.info = info
-        self.context = newcontext
-        self.handler = handler
-        self.argdata = argdata
-
-    def contained(self, name, data, info):
-        raise ConfigurationError("Invalid directive %s" % str(name))
-
-    def finish(self):
-        # We're going to use the context that was passed to us, which wasn't
-        # created for the directive.  We want to set it's info to the one
-        # passed to us while we make the call, so we'll save the old one
-        # and restore it.
-        context = self.context
-        args = toargs(context, *self.argdata)
-        actions = self.handler(context, **args)
-        if actions:
-            # we allow the handler to return nothing
-            for action in actions:
-                context.action(*action)
-
-class RootStackItem(object):
-
-    def __init__(self, context):
-        self.context = context
-
-    def contained(self, name, data, info):
-        """Handle a contained directive
-
-        We have to compute a new stack item by getting a named adapter
-        for the current context object.
-
-        """
-        factory = self.context.factory(self.context, name)
-        if factory is None:
-            raise ConfigurationError("Invalid directive", name)
-        adapter = factory(self.context, data, info)
-        return adapter
-
-    def finish(self):
-        pass
-
-class GroupingStackItem(RootStackItem):
-    """Stack item for a grouping directive
-
-    A grouping stack item is in the stack when a grouping directive is
-    being processed.  Grouping directives group other directives.
-    Often, they just manage common data, but they may also take
-    actions, either before or after contained directives are executed.
-
-    A grouping stack item is created with a grouping directive
-    definition, a configuration context, and directive data.
-
-    To see how this works, let's look at an example:
-
-    We need a context. We'll just use a configuration machine
-
-    >>> context = ConfigurationMachine()
-
-    We need a callable to use in configuration actions.  We'll use a
-    convenient one from the tests:
-
-    >>> from zope.configuration.tests.directives import f
-
-    We need a handler for the grouping directive. This is a class
-    that implements a context decorator.  The decorator must also
-    provide ``before`` and ``after`` methods that are called before
-    and after any contained directives are processed.  We'll typically
-    subclass ``GroupingContextDecorator``, which provides context
-    decoration, and default ``before`` and ``after`` methods.
-
-
-    >>> class SampleGrouping(GroupingContextDecorator):
-    ...    def before(self):
-    ...       self.action(('before', self.x, self.y), f)
-    ...    def after(self):
-    ...       self.action(('after'), f)
-
-    We'll use our decorator to decorate our initial context, providing
-    keyword arguments x and y:
-
-    >>> dec = SampleGrouping(context, x=1, y=2)
-
-    Note that the keyword arguments are made attributes of the
-    decorator.
-
-    Now we'll create the stack item.
-
-    >>> item = GroupingStackItem(dec)
-
-    We still haven't called the before action yet, which we can verify
-    by looking at the context actions:
-
-    >>> context.actions
-    []
-
-    Subdirectives will get looked up as adapters of the context.
-
-    We'll create a simple handler:
-
-    >>> def simple(context, data, info):
-    ...     context.action(("simple", context.x, context.y, data), f)
-    ...     return info
-
-    and register it with the context:
-
-    >>> context.register(IConfigurationContext, (testns, 'simple'), simple)
-
-    This handler isn't really a propert handler, because it doesn't
-    return a new context.  It will do for this example.
-
-    Now we'll call the contained method on the stack item:
-
-    >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo")
-    'someinfo'
-
-    We can verify thet the simple method was called by looking at the
-    context actions. Note that the before method was called before
-    handling the contained directive.
-
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-
-    >>> pprint(context.actions)
-    [(('before', 1, 2), f),
-     (('simple', 1, 2, {'z': 'zope'}), f)]
-
-    Finally, we call finish, which calls the decorator after method:
-
-    >>> item.finish()
-
-    >>> pprint(context.actions)
-    [(('before', 1, 2), f),
-     (('simple', 1, 2, {'z': 'zope'}), f),
-     ('after', f)]
-
-
-    If there were no nested directives:
-
-    >>> context = ConfigurationMachine()
-    >>> dec = SampleGrouping(context, x=1, y=2)
-    >>> item = GroupingStackItem(dec)
-    >>> item.finish()
-
-    Then before will be when we call finish:
-
-    >>> pprint(context.actions)
-    [(('before', 1, 2), f), ('after', f)]
-
-    """
-
-    implements(IStackItem)
-
-    def __init__(self, context):
-        super(GroupingStackItem, self).__init__(context)
-
-    def __callBefore(self):
-        actions = self.context.before()
-        if actions:
-            for action in actions:
-                self.context.action(*action)
-        self.__callBefore = noop
-
-    def contained(self, name, data, info):
-        self.__callBefore()
-        return RootStackItem.contained(self, name, data, info)
-
-    def finish(self):
-        self.__callBefore()
-        actions = self.context.after()
-        if actions:
-            for action in actions:
-                self.context.action(*action)
-
-def noop():
-    pass
-
-class ComplexStackItem(object):
-    """Complex stack item
-
-    A complex stack item is in the stack when a complex directive is
-    being processed.  It only allows subdirectives to be used.
-
-    A complex stack item is created with a complex directive
-    definition (IComplexDirectiveContext), a configuration context,
-    and directive data.
-
-    To see how this works, let's look at an example:
-
-    We need a context. We'll just use a configuration machine
-
-    >>> context = ConfigurationMachine()
-
-    We need a callable to use in configuration actions.  We'll use a
-    convenient one from the tests:
-
-    >>> from zope.configuration.tests.directives import f
-
-    We need a handler for the complex directive. This is a class
-    with a method for each subdirective:
-
-    >>> class Handler(object):
-    ...   def __init__(self, context, x, y):
-    ...      self.context, self.x, self.y = context, x, y
-    ...      context.action('init', f)
-    ...   def sub(self, context, a, b):
-    ...      context.action(('sub', a, b), f)
-    ...   def __call__(self):
-    ...      self.context.action(('call', self.x, self.y), f)
-
-    We need a complex directive definition:
-
-    >>> class Ixy(Interface):
-    ...    x = zope.schema.TextLine()
-    ...    y = zope.schema.TextLine()
-    >>> definition = ComplexDirectiveDefinition(
-    ...        context, name="test", schema=Ixy,
-    ...        handler=Handler)
-    >>> class Iab(Interface):
-    ...    a = zope.schema.TextLine()
-    ...    b = zope.schema.TextLine()
-    >>> definition['sub'] = Iab, ''
-
-    OK, now that we have the context, handler and definition, we're
-    ready to use a stack item.
-
-    >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'},
-    ...                         'foo')
-
-    When we created the definition, the handler (factory) was called.
-
-    >>> context.actions
-    [('init', f, (), {}, (), 'foo')]
-
-    If a subdirective is provided, the ``contained`` method of the stack item
-    is called. It will lookup the subdirective schema and call the
-    corresponding method on the handler instance:
-
-    >>> simple = item.contained(('somenamespace', 'sub'),
-    ...                         {'a': u'av', 'b': u'bv'}, 'baz')
-    >>> simple.finish()
-
-    Note that the name passed to ``contained`` is a 2-part name, consisting of
-    a namespace and a name within the namespace.
-
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-
-    >>> pprint(context.actions)
-    [('init', f, (), {}, (), 'foo'),
-     (('sub', u'av', u'bv'), f, (), {}, (), 'baz')]
-
-    The new stack item returned by contained is one that doesn't allow
-    any more subdirectives,
-
-    When all of the subdirectives have been provided, the ``finish``
-    method is called:
-
-    >>> item.finish()
-
-    The stack item will call the handler if it is callable.
-
-    >>> pprint(context.actions)
-    [('init', f, (), {}, (), 'foo'),
-     (('sub', u'av', u'bv'), f, (), {}, (), 'baz'),
-     (('call', u'xv', u'yv'), f, (), {}, (), 'foo')]
-
-
-    """
-
-    implements(IStackItem)
-
-    def __init__(self, meta, context, data, info):
-        newcontext = GroupingContextDecorator(context)
-        newcontext.info = info
-        self.context = newcontext
-        self.meta = meta
-
-        # Call the handler contructor
-        args = toargs(newcontext, meta.schema, data)
-        self.handler = self.meta.handler(newcontext, **args)
-
-    def contained(self, name, data, info):
-        """Handle a subdirective
-        """
-
-        # Look up the subdirective meta data on our meta object
-        ns, name = name
-        schema = self.meta.get(name)
-        if schema is None:
-            raise ConfigurationError("Invalid directive", name)
-        schema = schema[0] # strip off info
-        handler = getattr(self.handler, name)
-        return SimpleStackItem(self.context, handler, info, schema, data)
-
-    def finish(self):
-
-        # when we're done, we call the handler, which might return more actions
-
-        # Need to save and restore old info
-
-        try:
-            actions = self.handler()
-        except AttributeError, v:
-            if v[0] == '__call__':
-                return # noncallable
-            raise
-        except TypeError:
-            return # non callable
-
-        if actions:
-            # we allow the handler to return nothing
-            for action in actions:
-                self.context.action(*action)
-
-##############################################################################
-# Helper classes
-
-class GroupingContextDecorator(ConfigurationContext):
-    """Helper mix-in class for building grouping directives
-
-    See the discussion (and test) in GroupingStackItem.
-    """
-
-    implements(IConfigurationContext, IGroupingContext)
-
-    def __init__(self, context, **kw):
-        self.context = context
-        for name, v in kw.items():
-            setattr(self, name, v)
-
-    def __getattr__(self, name,
-                    getattr=getattr, setattr=setattr):
-        v = getattr(self.context, name)
-        # cache result in self
-        setattr(self, name, v)
-        return v
-
-    def before(self):
-        pass
-
-    def after(self):
-        pass
-
-##############################################################################
-# Directive-definition
-
-class DirectiveSchema(fields.GlobalInterface):
-    """A field that contains a global variable value that must be a schema
-    """
-
-class IDirectivesInfo(Interface):
-    """Schema for the ``directives`` directive
-    """
-
-    namespace = zope.schema.URI(
-        title=u"Namespace",
-        description=u"The namespace in which directives' names will be defined",
-        )
-
-class IDirectivesContext(IDirectivesInfo, IConfigurationContext):
-    pass
-
-class DirectivesHandler(GroupingContextDecorator):
-    """Handler for the directives directive
-
-    This is just a grouping directive that adds a namespace attribute
-    to the normal directive context.
-
-    """
-    implements(IDirectivesContext)
-
-
-class IDirectiveInfo(Interface):
-    """Information common to all directive definitions have
-    """
-
-    name = zope.schema.TextLine(
-        title = u"Directive name",
-        description = u"The name of the directive being defined",
-        )
-
-    schema = DirectiveSchema(
-        title = u"Directive handler",
-        description = u"The dotted name of the directive handler",
-        )
-
-class IFullInfo(IDirectiveInfo):
-    """Information that all top-level directives (not subdirectives) have
-    """
-
-    handler = fields.GlobalObject(
-        title = u"Directive handler",
-        description = u"The dotted name of the directive handler",
-        )
-
-    usedIn = fields.GlobalInterface(
-        title = u"The directive types the directive can be used in",
-        description = (u"The interface of the directives that can contain "
-                       u"the directive"
-                       ),
-        default = IConfigurationContext,
-        )
-
-class IStandaloneDirectiveInfo(IDirectivesInfo, IFullInfo):
-    """Info for full directives defined outside a directives directives
-    """
-
-def defineSimpleDirective(context, name, schema, handler,
-                          namespace='', usedIn=IConfigurationContext):
-    """Define a simple directive
-
-    Define and register a factory that invokes the simple directive
-    and returns a new stack item, which is always the same simple stack item.
-
-    If the namespace is '*', the directive is registered for all namespaces.
-
-    for example:
-
-    >>> context = ConfigurationMachine()
-    >>> from zope.configuration.tests.directives import f
-    >>> class Ixy(Interface):
-    ...    x = zope.schema.TextLine()
-    ...    y = zope.schema.TextLine()
-    >>> def s(context, x, y):
-    ...    context.action(('s', x, y), f)
-
-    >>> defineSimpleDirective(context, 's', Ixy, s, testns)
-
-    >>> context((testns, "s"), x=u"vx", y=u"vy")
-    >>> context.actions
-    [(('s', u'vx', u'vy'), f)]
-
-    >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's')
-
-    >>> context = ConfigurationMachine()
-    >>> defineSimpleDirective(context, 's', Ixy, s, "*")
-
-    >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
-    >>> context.actions
-    [(('s', u'vx', u'vy'), f)]
-
-    """
-
-    namespace = namespace or context.namespace
-    if namespace != '*':
-        name = namespace, name
-
-    def factory(context, data, info):
-        return SimpleStackItem(context, handler, info, schema, data)
-    factory.schema = schema
-
-    context.register(usedIn, name, factory)
-    context.document(name, schema, usedIn, handler, context.info)
-
-def defineGroupingDirective(context, name, schema, handler,
-                            namespace='', usedIn=IConfigurationContext):
-    """Define a grouping directive
-
-    Define and register a factory that sets up a grouping directive.
-
-    If the namespace is '*', the directive is registered for all namespaces.
-
-    for example:
-
-    >>> context = ConfigurationMachine()
-    >>> from zope.configuration.tests.directives import f
-    >>> class Ixy(Interface):
-    ...    x = zope.schema.TextLine()
-    ...    y = zope.schema.TextLine()
-
-    We won't bother creating a special grouping directive class. We'll
-    just use GroupingContextDecorator, which simply sets up a grouping
-    context that has extra attributes defined by a schema:
-
-    >>> defineGroupingDirective(context, 'g', Ixy,
-    ...                         GroupingContextDecorator, testns)
-
-    >>> context.begin((testns, "g"), x=u"vx", y=u"vy")
-    >>> context.stack[-1].context.x
-    u'vx'
-    >>> context.stack[-1].context.y
-    u'vy'
-
-    >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g')
-
-    >>> context = ConfigurationMachine()
-    >>> defineGroupingDirective(context, 'g', Ixy,
-    ...                         GroupingContextDecorator, "*")
-
-    >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
-    >>> context.stack[-1].context.x
-    u'vx'
-    >>> context.stack[-1].context.y
-    u'vy'
-
-    """
-
-    namespace = namespace or context.namespace
-    if namespace != '*':
-        name = namespace, name
-
-    def factory(context, data, info):
-        args = toargs(context, schema, data)
-        newcontext = handler(context, **args)
-        newcontext.info = info
-        return GroupingStackItem(newcontext)
-    factory.schema = schema
-
-    context.register(usedIn, name, factory)
-    context.document(name, schema, usedIn, handler, context.info)
-
-
-class IComplexDirectiveContext(IFullInfo, IConfigurationContext):
-    pass
-
-class ComplexDirectiveDefinition(GroupingContextDecorator, dict):
-    """Handler for defining complex directives
-
-    See the description and tests for ComplexStackItem.
-    """
-
-    implements(IComplexDirectiveContext)
-
-    def before(self):
-
-        def factory(context, data, info):
-            return ComplexStackItem(self, context, data, info)
-        factory.schema = self.schema
-
-        self.register(self.usedIn, (self.namespace, self.name), factory)
-        self.document((self.namespace, self.name), self.schema, self.usedIn,
-                      self.handler, self.info)
-
-def subdirective(context, name, schema):
-    context.document((context.namespace, name), schema, context.usedIn,
-                     getattr(context.handler, name, context.handler),
-                     context.info, context.context)
-    context.context[name] = schema, context.info
-
-##############################################################################
-# Features
-
-class IProvidesDirectiveInfo(Interface):
-    """Information for a <meta:provides> directive"""
-
-    feature = zope.schema.TextLine(
-        title = u"Feature name",
-        description = u"""The name of the feature being provided
-
-        You can test available features with zcml:condition="have featurename".
-        """,
-        )
-
-def provides(context, feature):
-    """Declare that a feature is provided in context.
-
-    >>> c = ConfigurationContext()
-    >>> provides(c, 'apidoc')
-    >>> c.hasFeature('apidoc')
-    True
-
-    Spaces are not allowed in feature names (this is reserved for providing
-    many features with a single directive in the futute).
-
-    >>> provides(c, 'apidoc onlinehelp')
-    Traceback (most recent call last):
-      ...
-    ValueError: Only one feature name allowed
-
-    >>> c.hasFeature('apidoc onlinehelp')
-    False
-
-    """
-    if len(feature.split()) > 1:
-        raise ValueError("Only one feature name allowed")
-    context.provideFeature(feature)
-
-
-##############################################################################
-# Argument conversion
-
-def toargs(context, schema, data):
-    """Marshal data to an argument dictionary using a schema
-
-    Names that are python keywords have an underscore added as a
-    suffix in the schema and in the argument list, but are used
-    without the underscore in the data.
-
-    The fields in the schema must all implement IFromUnicode.
-
-    All of the items in the data must have corresponding fields in the
-    schema unless the schema has a true tagged value named
-    'keyword_arguments'.
-
-    Here's an example:
-
-    >>> from zope import schema
-
-    >>> class schema(Interface):
-    ...     in_ = zope.schema.Int(constraint=lambda v: v > 0)
-    ...     f = zope.schema.Float()
-    ...     n = zope.schema.TextLine(min_length=1, default=u"rob")
-    ...     x = zope.schema.BytesLine(required=False)
-    ...     u = zope.schema.URI()
-
-    >>> context = ConfigurationMachine()
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
-    ...          'u': u'http://www.zope.org' }))
-    {'f': 1.2,
-     'in_': 1,
-     'n': u'bob',
-     'u': 'http://www.zope.org',
-     'x': 'x.y.z'}
-
-    If we have extra data, we'll get an error:
-
-    >>> toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
-    ...          'u': u'http://www.zope.org', 'a': u'1'})
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Unrecognized parameters:', 'a')
-
-    Unless we set a tagged value to say that extra arguments are ok:
-
-    >>> schema.setTaggedValue('keyword_arguments', True)
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
-    ...          'u': u'http://www.zope.org', 'a': u'1'}))
-    {'a': u'1',
-     'f': 1.2,
-     'in_': 1,
-     'n': u'bob',
-     'u': 'http://www.zope.org',
-     'x': 'x.y.z'}
-
-
-    If we ommit required data we get an error telling us what was omitted:
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'}))
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Missing parameter:', 'u')
-
-    Although we can omit not-required data:
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob',
-    ...          'u': u'http://www.zope.org', 'a': u'1'}))
-    {'a': u'1',
-     'f': 1.2,
-     'in_': 1,
-     'n': u'bob',
-     'u': 'http://www.zope.org'}
-
-    And we can ommit required fields if they have valid defaults
-    (defaults that are valid values):
-
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'1', 'f': u'1.2',
-    ...          'u': u'http://www.zope.org', 'a': u'1'}))
-    {'a': u'1',
-     'f': 1.2,
-     'in_': 1,
-     'n': u'rob',
-     'u': 'http://www.zope.org'}
-
-    We also get an error if any data was invalid:
-
-    >>> pprint(toargs(context, schema,
-    ...        {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
-    ...          'u': u'http://www.zope.org', 'a': u'1'}))
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid value for', 'in', '0')
-
-    """
-
-    data = dict(data)
-    args = {}
-    for name, field in schema.namesAndDescriptions(True):
-        field = field.bind(context)
-        n = name
-        if n.endswith('_') and iskeyword(n[:-1]):
-            n = n[:-1]
-
-        s = data.get(n, data)
-        if s is not data:
-            s = unicode(s)
-            del data[n]
-
-            try:
-                args[str(name)] = field.fromUnicode(s)
-            except zope.schema.ValidationError, v:
-                raise ConfigurationError(
-                    "Invalid value for", n, str(v)), None, sys.exc_info()[2]
-        elif field.required:
-            # if the default is valid, we can use that:
-            default = field.default
-            try:
-                field.validate(default)
-            except zope.schema.ValidationError:
-                raise ConfigurationError("Missing parameter:", n)
-            args[str(name)] = default
-
-    if data:
-        # we had data left over
-        try:
-            keyword_arguments = schema.getTaggedValue('keyword_arguments')
-        except KeyError:
-            keyword_arguments = False
-        if not keyword_arguments:
-            raise ConfigurationError("Unrecognized parameters:", *data)
-
-        for name in data:
-            args[str(name)] = data[name]
-
-    return args
-
-##############################################################################
-# Conflict resolution
-
-def expand_action(discriminator, callable=None, args=(), kw={},
-                   includepath=(), info='', order=0):
-    return (discriminator, callable, args, kw,
-            includepath, info, order)
-
-def resolveConflicts(actions):
-    """Resolve conflicting actions
-
-    Given an actions list, identify and try to resolve conflicting actions.
-    Actions conflict if they have the same non-null discriminator.
-    Conflicting actions can be resolved if the include path of one of
-    the actions is a prefix of the includepaths of the other
-    conflicting actions and is unequal to the include paths in the
-    other conflicting actions.
-
-    Here are some examples to illustrate how this works:
-
-    >>> from zope.configuration.tests.directives import f
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(resolveConflicts([
-    ...    (None, f),
-    ...    (1, f, (1,), {}, (), 'first'),
-    ...    (1, f, (2,), {}, ('x',), 'second'),
-    ...    (1, f, (3,), {}, ('y',), 'third'),
-    ...    (4, f, (4,), {}, ('y',), 'should be last', 99999),
-    ...    (3, f, (3,), {}, ('y',)),
-    ...    (None, f, (5,), {}, ('y',)),
-    ... ]))
-    [(None, f),
-     (1, f, (1,), {}, (), 'first'),
-     (3, f, (3,), {}, ('y',)),
-     (None, f, (5,), {}, ('y',)),
-     (4, f, (4,), {}, ('y',), 'should be last')]
-
-    >>> try:
-    ...     v = resolveConflicts([
-    ...        (None, f),
-    ...        (1, f, (2,), {}, ('x',), 'eek'),
-    ...        (1, f, (3,), {}, ('y',), 'ack'),
-    ...        (4, f, (4,), {}, ('y',)),
-    ...        (3, f, (3,), {}, ('y',)),
-    ...        (None, f, (5,), {}, ('y',)),
-    ...     ])
-    ... except ConfigurationConflictError, v:
-    ...    pass
-    >>> print v
-    Conflicting configuration actions
-      For: 1
-        eek
-        ack
-
-    """
-
-    # organize actions by discriminators
-    unique = {}
-    output = []
-    for i in range(len(actions)):
-        (discriminator, callable, args, kw, includepath, info, order
-         ) = expand_action(*(actions[i]))
-
-        order = order or i
-        if discriminator is None:
-            # The discriminator is None, so this directive can
-            # never conflict. We can add it directly to the
-            # configuration actions.
-            output.append(
-                (order, discriminator, callable, args, kw, includepath, info)
-                )
-            continue
-
-
-        a = unique.setdefault(discriminator, [])
-        a.append(
-            (includepath, order, callable, args, kw, info)
-            )
-
-    # Check for conflicts
-    conflicts = {}
-    for discriminator, dups in unique.items():
-
-        # We need to sort the actions by the paths so that the shortest
-        # path with a given prefix comes first:
-        dups.sort()
-        (basepath, i, callable, args, kw, baseinfo) = dups[0]
-        output.append(
-            (i, discriminator, callable, args, kw, basepath, baseinfo)
-            )
-        for includepath, i, callable, args, kw, info in dups[1:]:
-            # Test whether path is a prefix of opath
-            if (includepath[:len(basepath)] != basepath # not a prefix
-                or
-                (includepath == basepath)
-                ):
-                if discriminator not in conflicts:
-                    conflicts[discriminator] = [baseinfo]
-                conflicts[discriminator].append(info)
-
-
-    if conflicts:
-        raise ConfigurationConflictError(conflicts)
-
-    # Now put the output back in the original order, and return it:
-    output.sort()
-    r = []
-    for o in output:
-        action = o[1:]
-        while len(action) > 2 and not action[-1]:
-            action = action[:-1]
-        r.append(action)
-
-    return r
-
-class ConfigurationConflictError(ConfigurationError):
-
-    def __init__(self, conflicts):
-        self._conflicts = conflicts
-
-    def __str__(self):
-        r = ["Conflicting configuration actions"]
-        items = self._conflicts.items()
-        items.sort()
-        for discriminator, infos in items:
-            r.append("  For: %s" % (discriminator, ))
-            for info in infos:
-                for line in unicode(info).rstrip().split(u'\n'):
-                    r.append(u"    "+line)
-
-        return "\n".join(r)
-
-
-##############################################################################
-# Bootstap code
-
-
-def _bootstrap(context):
-
-    # Set enough machinery to register other directives
-
-    # Define the directive (simple directive) directive by calling it's
-    # handler directly
-
-    info = 'Manually registered in zope/configuration/config.py'
-
-    context.info = info
-    defineSimpleDirective(
-        context,
-        namespace=metans, name='directive',
-        schema=IStandaloneDirectiveInfo,
-        handler=defineSimpleDirective)
-    context.info = ''
-
-    # OK, now that we have that, we can use the machine to define the
-    # other directives. This isn't the easiest way to proceed, but it lets
-    # us eat our own dogfood. :)
-
-    # Standalone groupingDirective
-    context((metans, 'directive'),
-            info,
-            name='groupingDirective',
-            namespace=metans,
-            handler="zope.configuration.config.defineGroupingDirective",
-            schema="zope.configuration.config.IStandaloneDirectiveInfo"
-            )
-
-    # Now we can use the grouping directive to define the directives directive
-    context((metans, 'groupingDirective'),
-            info,
-            name='directives',
-            namespace=metans,
-            handler="zope.configuration.config.DirectivesHandler",
-            schema="zope.configuration.config.IDirectivesInfo"
-            )
-
-    # directive and groupingDirective inside directives
-    context((metans, 'directive'),
-            info,
-            name='directive',
-            namespace=metans,
-            usedIn="zope.configuration.config.IDirectivesContext",
-            handler="zope.configuration.config.defineSimpleDirective",
-            schema="zope.configuration.config.IFullInfo"
-            )
-    context((metans, 'directive'),
-            info,
-            name='groupingDirective',
-            namespace=metans,
-            usedIn="zope.configuration.config.IDirectivesContext",
-            handler="zope.configuration.config.defineGroupingDirective",
-            schema="zope.configuration.config.IFullInfo"
-            )
-
-    # Setup complex directive directive, both standalone, and in
-    # directives directive
-    context((metans, 'groupingDirective'),
-            info,
-            name='complexDirective',
-            namespace=metans,
-            handler="zope.configuration.config.ComplexDirectiveDefinition",
-            schema="zope.configuration.config.IStandaloneDirectiveInfo"
-            )
-    context((metans, 'groupingDirective'),
-            info,
-            name='complexDirective',
-            namespace=metans,
-            usedIn="zope.configuration.config.IDirectivesContext",
-            handler="zope.configuration.config.ComplexDirectiveDefinition",
-            schema="zope.configuration.config.IFullInfo"
-            )
-
-    # Finally, setup subdirective directive
-    context((metans, 'directive'),
-            info,
-            name='subdirective',
-            namespace=metans,
-            usedIn="zope.configuration.config.IComplexDirectiveContext",
-            handler="zope.configuration.config.subdirective",
-            schema="zope.configuration.config.IDirectiveInfo"
-            )
-
-    # meta:provides
-    context((metans, 'directive'),
-            info,
-            name='provides',
-            namespace=metans,
-            handler="zope.configuration.config.provides",
-            schema="zope.configuration.config.IProvidesDirectiveInfo"
-            )
-

Added: zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/config.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,885 @@
+##############################################################################
+#
+# Copyright (c) 2011 Zope Foundation 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.
+#
+##############################################################################
+"""Configuration processor
+
+See README.txt
+"""
+
+__docformat__ = 'restructuredtext'
+
+from keyword import iskeyword
+import sys
+
+import zope.schema
+
+from zope.interface import Interface
+from zope.interface import implements
+from zope.configuration import fields
+
+from zope.configmachine import GroupingContextDecorator
+from zope.configmachine import RootStackItem
+from zope.configmachine.exceptions import ConfigurationError
+from zope.configmachine.interfaces import IConfigurationContext
+from zope.configmachine.interfaces import IStackItem
+
+zopens = 'http://namespaces.zope.org/zope'
+metans = 'http://namespaces.zope.org/meta'
+testns = 'http://namespaces.zope.org/test'
+
+##############################################################################
+# Bw compat imports
+
+from zope.configmachine.exceptions import ConfigurationExecutionError
+from zope.configmachine.exceptions import ConfigurationConflictError
+from zope.configmachine import ConfigurationContext
+from zope.configmachine import ConfigurationAdapterRegistry
+from zope.configmachine import RootStackItem
+from zope.configmachine import expand_action
+from zope.configmachine import resolveConflicts
+
+##############################################################################
+# Redefinitions
+
+from zope.configmachine import ConfigurationMachine as _ConfigurationMachine
+
+class ConfigurationMachine(_ConfigurationMachine):
+    """Configuration machine
+
+    Example:
+
+    >>> machine = ConfigurationMachine()
+    >>> ns = "http://www.zope.org/testing"
+
+    Register a directive:
+
+    >>> machine((metans, "directive"),
+    ...         namespace=ns, name="simple",
+    ...         schema="zope.configuration.tests.directives.ISimple",
+    ...         handler="zope.configuration.tests.directives.simple")
+
+    and try it out:
+
+    >>> machine((ns, "simple"), a=u"aa", c=u"cc")
+
+    >>> machine.actions
+    [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'))]
+
+    A more extensive example can be found in the unit tests.
+    """
+    def __init__(self):
+        # the base configuration machine does not bootstrap any directives
+        super(ConfigurationMachine, self).__init__()
+        _bootstrap(self)
+
+##############################################################################
+# Stack items
+
+class SimpleStackItem(object):
+    """Simple stack item
+
+    A simple stack item can't have anything added after it.  It can
+    only be removed.  It is used for simple directives and
+    subdirectives, which can't contain other directives.
+
+    It also defers any computation until the end of the directive
+    has been reached.
+    """
+
+    implements(IStackItem)
+
+    def __init__(self, context, handler, info, *argdata):
+        newcontext = GroupingContextDecorator(context)
+        newcontext.info = info
+        self.context = newcontext
+        self.handler = handler
+        self.argdata = argdata
+
+    def contained(self, name, data, info):
+        raise ConfigurationError("Invalid directive %s" % str(name))
+
+    def finish(self):
+        # We're going to use the context that was passed to us, which wasn't
+        # created for the directive.  We want to set it's info to the one
+        # passed to us while we make the call, so we'll save the old one
+        # and restore it.
+        context = self.context
+        args = toargs(context, *self.argdata)
+        actions = self.handler(context, **args)
+        if actions:
+            # we allow the handler to return nothing
+            for action in actions:
+                context.action(*action)
+
+
+class GroupingStackItem(RootStackItem):
+    """Stack item for a grouping directive
+
+    A grouping stack item is in the stack when a grouping directive is
+    being processed.  Grouping directives group other directives.
+    Often, they just manage common data, but they may also take
+    actions, either before or after contained directives are executed.
+
+    A grouping stack item is created with a grouping directive
+    definition, a configuration context, and directive data.
+
+    To see how this works, let's look at an example:
+
+    We need a context. We'll just use a configuration machine
+
+    >>> context = ConfigurationMachine()
+
+    We need a callable to use in configuration actions.  We'll use a
+    convenient one from the tests:
+
+    >>> from zope.configuration.tests.directives import f
+
+    We need a handler for the grouping directive. This is a class
+    that implements a context decorator.  The decorator must also
+    provide ``before`` and ``after`` methods that are called before
+    and after any contained directives are processed.  We'll typically
+    subclass ``GroupingContextDecorator``, which provides context
+    decoration, and default ``before`` and ``after`` methods.
+
+
+    >>> class SampleGrouping(GroupingContextDecorator):
+    ...    def before(self):
+    ...       self.action(('before', self.x, self.y), f)
+    ...    def after(self):
+    ...       self.action(('after'), f)
+
+    We'll use our decorator to decorate our initial context, providing
+    keyword arguments x and y:
+
+    >>> dec = SampleGrouping(context, x=1, y=2)
+
+    Note that the keyword arguments are made attributes of the
+    decorator.
+
+    Now we'll create the stack item.
+
+    >>> item = GroupingStackItem(dec)
+
+    We still haven't called the before action yet, which we can verify
+    by looking at the context actions:
+
+    >>> context.actions
+    []
+
+    Subdirectives will get looked up as adapters of the context.
+
+    We'll create a simple handler:
+
+    >>> def simple(context, data, info):
+    ...     context.action(("simple", context.x, context.y, data), f)
+    ...     return info
+
+    and register it with the context:
+
+    >>> context.register(IConfigurationContext, (testns, 'simple'), simple)
+
+    This handler isn't really a propert handler, because it doesn't
+    return a new context.  It will do for this example.
+
+    Now we'll call the contained method on the stack item:
+
+    >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo")
+    'someinfo'
+
+    We can verify thet the simple method was called by looking at the
+    context actions. Note that the before method was called before
+    handling the contained directive.
+
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+
+    >>> pprint(context.actions)
+    [(('before', 1, 2), f),
+     (('simple', 1, 2, {'z': 'zope'}), f)]
+
+    Finally, we call finish, which calls the decorator after method:
+
+    >>> item.finish()
+
+    >>> pprint(context.actions)
+    [(('before', 1, 2), f),
+     (('simple', 1, 2, {'z': 'zope'}), f),
+     ('after', f)]
+
+
+    If there were no nested directives:
+
+    >>> context = ConfigurationMachine()
+    >>> dec = SampleGrouping(context, x=1, y=2)
+    >>> item = GroupingStackItem(dec)
+    >>> item.finish()
+
+    Then before will be when we call finish:
+
+    >>> pprint(context.actions)
+    [(('before', 1, 2), f), ('after', f)]
+
+    """
+
+    implements(IStackItem)
+
+    def __init__(self, context):
+        super(GroupingStackItem, self).__init__(context)
+
+    def __callBefore(self):
+        actions = self.context.before()
+        if actions:
+            for action in actions:
+                self.context.action(*action)
+        self.__callBefore = noop
+
+    def contained(self, name, data, info):
+        self.__callBefore()
+        return RootStackItem.contained(self, name, data, info)
+
+    def finish(self):
+        self.__callBefore()
+        actions = self.context.after()
+        if actions:
+            for action in actions:
+                self.context.action(*action)
+
+def noop():
+    pass
+
+
+class ComplexStackItem(object):
+    """Complex stack item
+
+    A complex stack item is in the stack when a complex directive is
+    being processed.  It only allows subdirectives to be used.
+
+    A complex stack item is created with a complex directive
+    definition (IComplexDirectiveContext), a configuration context,
+    and directive data.
+
+    To see how this works, let's look at an example:
+
+    We need a context. We'll just use a configuration machine
+
+    >>> context = ConfigurationMachine()
+
+    We need a callable to use in configuration actions.  We'll use a
+    convenient one from the tests:
+
+    >>> from zope.configuration.tests.directives import f
+
+    We need a handler for the complex directive. This is a class
+    with a method for each subdirective:
+
+    >>> class Handler(object):
+    ...   def __init__(self, context, x, y):
+    ...      self.context, self.x, self.y = context, x, y
+    ...      context.action('init', f)
+    ...   def sub(self, context, a, b):
+    ...      context.action(('sub', a, b), f)
+    ...   def __call__(self):
+    ...      self.context.action(('call', self.x, self.y), f)
+
+    We need a complex directive definition:
+
+    >>> class Ixy(Interface):
+    ...    x = zope.schema.TextLine()
+    ...    y = zope.schema.TextLine()
+    >>> definition = ComplexDirectiveDefinition(
+    ...        context, name="test", schema=Ixy,
+    ...        handler=Handler)
+    >>> class Iab(Interface):
+    ...    a = zope.schema.TextLine()
+    ...    b = zope.schema.TextLine()
+    >>> definition['sub'] = Iab, ''
+
+    OK, now that we have the context, handler and definition, we're
+    ready to use a stack item.
+
+    >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'},
+    ...                         'foo')
+
+    When we created the definition, the handler (factory) was called.
+
+    >>> context.actions
+    [('init', f, (), {}, (), 'foo')]
+
+    If a subdirective is provided, the ``contained`` method of the stack item
+    is called. It will lookup the subdirective schema and call the
+    corresponding method on the handler instance:
+
+    >>> simple = item.contained(('somenamespace', 'sub'),
+    ...                         {'a': u'av', 'b': u'bv'}, 'baz')
+    >>> simple.finish()
+
+    Note that the name passed to ``contained`` is a 2-part name, consisting of
+    a namespace and a name within the namespace.
+
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=60).pprint
+
+    >>> pprint(context.actions)
+    [('init', f, (), {}, (), 'foo'),
+     (('sub', u'av', u'bv'), f, (), {}, (), 'baz')]
+
+    The new stack item returned by contained is one that doesn't allow
+    any more subdirectives,
+
+    When all of the subdirectives have been provided, the ``finish``
+    method is called:
+
+    >>> item.finish()
+
+    The stack item will call the handler if it is callable.
+
+    >>> pprint(context.actions)
+    [('init', f, (), {}, (), 'foo'),
+     (('sub', u'av', u'bv'), f, (), {}, (), 'baz'),
+     (('call', u'xv', u'yv'), f, (), {}, (), 'foo')]
+
+
+    """
+
+    implements(IStackItem)
+
+    def __init__(self, meta, context, data, info):
+        newcontext = GroupingContextDecorator(context)
+        newcontext.info = info
+        self.context = newcontext
+        self.meta = meta
+
+        # Call the handler contructor
+        args = toargs(newcontext, meta.schema, data)
+        self.handler = self.meta.handler(newcontext, **args)
+
+    def contained(self, name, data, info):
+        """Handle a subdirective
+        """
+
+        # Look up the subdirective meta data on our meta object
+        ns, name = name
+        schema = self.meta.get(name)
+        if schema is None:
+            raise ConfigurationError("Invalid directive", name)
+        schema = schema[0] # strip off info
+        handler = getattr(self.handler, name)
+        return SimpleStackItem(self.context, handler, info, schema, data)
+
+    def finish(self):
+
+        # when we're done, we call the handler, which might return more actions
+
+        # Need to save and restore old info
+
+        try:
+            actions = self.handler()
+        except AttributeError, v:
+            if v[0] == '__call__':
+                return # noncallable
+            raise
+        except TypeError:
+            return # non callable
+
+        if actions:
+            # we allow the handler to return nothing
+            for action in actions:
+                self.context.action(*action)
+
+def defineGroupingDirective(context, name, schema, handler,
+                            namespace='', usedIn=IConfigurationContext):
+    """Define a grouping directive
+
+    Define and register a factory that sets up a grouping directive.
+
+    If the namespace is '*', the directive is registered for all namespaces.
+
+    for example:
+
+    >>> context = ConfigurationMachine()
+    >>> from zope.configuration.tests.directives import f
+    >>> class Ixy(Interface):
+    ...    x = zope.schema.TextLine()
+    ...    y = zope.schema.TextLine()
+
+    We won't bother creating a special grouping directive class. We'll
+    just use GroupingContextDecorator, which simply sets up a grouping
+    context that has extra attributes defined by a schema:
+
+    >>> defineGroupingDirective(context, 'g', Ixy,
+    ...                         GroupingContextDecorator, testns)
+
+    >>> context.begin((testns, "g"), x=u"vx", y=u"vy")
+    >>> context.stack[-1].context.x
+    u'vx'
+    >>> context.stack[-1].context.y
+    u'vy'
+
+    >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g')
+
+    >>> context = ConfigurationMachine()
+    >>> defineGroupingDirective(context, 'g', Ixy,
+    ...                         GroupingContextDecorator, "*")
+
+    >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
+    >>> context.stack[-1].context.x
+    u'vx'
+    >>> context.stack[-1].context.y
+    u'vy'
+
+    """
+
+    namespace = namespace or context.namespace
+    if namespace != '*':
+        name = namespace, name
+
+    def factory(context, data, info):
+        args = toargs(context, schema, data)
+        newcontext = handler(context, **args)
+        newcontext.info = info
+        return GroupingStackItem(newcontext)
+    factory.schema = schema
+
+    context.register(usedIn, name, factory)
+    context.document(name, schema, usedIn, handler, context.info)
+
+
+##############################################################################
+# Directive-definition
+
+class DirectiveSchema(fields.GlobalInterface):
+    """A field that contains a global variable value that must be a schema
+    """
+
+class IDirectivesInfo(Interface):
+    """Schema for the ``directives`` directive
+    """
+
+    namespace = zope.schema.URI(
+        title=u"Namespace",
+        description=u"The namespace in which directives' names will be defined",
+        )
+
+class IDirectivesContext(IDirectivesInfo, IConfigurationContext):
+    pass
+
+class DirectivesHandler(GroupingContextDecorator):
+    """Handler for the directives directive
+
+    This is just a grouping directive that adds a namespace attribute
+    to the normal directive context.
+
+    """
+    implements(IDirectivesContext)
+
+
+class IDirectiveInfo(Interface):
+    """Information common to all directive definitions have
+    """
+
+    name = zope.schema.TextLine(
+        title = u"Directive name",
+        description = u"The name of the directive being defined",
+        )
+
+    schema = DirectiveSchema(
+        title = u"Directive handler",
+        description = u"The dotted name of the directive handler",
+        )
+
+class IFullInfo(IDirectiveInfo):
+    """Information that all top-level directives (not subdirectives) have
+    """
+
+    handler = fields.GlobalObject(
+        title = u"Directive handler",
+        description = u"The dotted name of the directive handler",
+        )
+
+    usedIn = fields.GlobalInterface(
+        title = u"The directive types the directive can be used in",
+        description = (u"The interface of the directives that can contain "
+                       u"the directive"
+                       ),
+        default = IConfigurationContext,
+        )
+
+class IStandaloneDirectiveInfo(IDirectivesInfo, IFullInfo):
+    """Info for full directives defined outside a directives directives
+    """
+
+def defineSimpleDirective(context, name, schema, handler,
+                          namespace='', usedIn=IConfigurationContext):
+    """Define a simple directive
+
+    Define and register a factory that invokes the simple directive
+    and returns a new stack item, which is always the same simple stack item.
+
+    If the namespace is '*', the directive is registered for all namespaces.
+
+    for example:
+
+    >>> context = ConfigurationMachine()
+    >>> from zope.configuration.tests.directives import f
+    >>> class Ixy(Interface):
+    ...    x = zope.schema.TextLine()
+    ...    y = zope.schema.TextLine()
+    >>> def s(context, x, y):
+    ...    context.action(('s', x, y), f)
+
+    >>> defineSimpleDirective(context, 's', Ixy, s, testns)
+
+    >>> context((testns, "s"), x=u"vx", y=u"vy")
+    >>> context.actions
+    [(('s', u'vx', u'vy'), f)]
+
+    >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's')
+
+    >>> context = ConfigurationMachine()
+    >>> defineSimpleDirective(context, 's', Ixy, s, "*")
+
+    >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
+    >>> context.actions
+    [(('s', u'vx', u'vy'), f)]
+
+    """
+
+    namespace = namespace or context.namespace
+    if namespace != '*':
+        name = namespace, name
+
+    def factory(context, data, info):
+        return SimpleStackItem(context, handler, info, schema, data)
+    factory.schema = schema
+
+    context.register(usedIn, name, factory)
+    context.document(name, schema, usedIn, handler, context.info)
+
+class IComplexDirectiveContext(IFullInfo, IConfigurationContext):
+    pass
+
+class ComplexDirectiveDefinition(GroupingContextDecorator, dict):
+    """Handler for defining complex directives
+
+    See the description and tests for ComplexStackItem.
+    """
+
+    implements(IComplexDirectiveContext)
+
+    def before(self):
+
+        def factory(context, data, info):
+            return ComplexStackItem(self, context, data, info)
+        factory.schema = self.schema
+
+        self.register(self.usedIn, (self.namespace, self.name), factory)
+        self.document((self.namespace, self.name), self.schema, self.usedIn,
+                      self.handler, self.info)
+
+def subdirective(context, name, schema):
+    context.document((context.namespace, name), schema, context.usedIn,
+                     getattr(context.handler, name, context.handler),
+                     context.info, context.context)
+    context.context[name] = schema, context.info
+
+##############################################################################
+# Features
+
+class IProvidesDirectiveInfo(Interface):
+    """Information for a <meta:provides> directive"""
+
+    feature = zope.schema.TextLine(
+        title = u"Feature name",
+        description = u"""The name of the feature being provided
+
+        You can test available features with zcml:condition="have featurename".
+        """,
+        )
+
+def provides(context, feature):
+    """Declare that a feature is provided in context.
+
+    >>> c = ConfigurationContext()
+    >>> provides(c, 'apidoc')
+    >>> c.hasFeature('apidoc')
+    True
+
+    Spaces are not allowed in feature names (this is reserved for providing
+    many features with a single directive in the futute).
+
+    >>> provides(c, 'apidoc onlinehelp')
+    Traceback (most recent call last):
+      ...
+    ValueError: Only one feature name allowed
+
+    >>> c.hasFeature('apidoc onlinehelp')
+    False
+
+    """
+    if len(feature.split()) > 1:
+        raise ValueError("Only one feature name allowed")
+    context.provideFeature(feature)
+
+
+##############################################################################
+# Argument conversion
+
+def toargs(context, schema, data):
+    """Marshal data to an argument dictionary using a schema
+
+    Names that are python keywords have an underscore added as a
+    suffix in the schema and in the argument list, but are used
+    without the underscore in the data.
+
+    The fields in the schema must all implement IFromUnicode.
+
+    All of the items in the data must have corresponding fields in the
+    schema unless the schema has a true tagged value named
+    'keyword_arguments'.
+
+    Here's an example:
+
+    >>> from zope import schema
+
+    >>> class schema(Interface):
+    ...     in_ = zope.schema.Int(constraint=lambda v: v > 0)
+    ...     f = zope.schema.Float()
+    ...     n = zope.schema.TextLine(min_length=1, default=u"rob")
+    ...     x = zope.schema.BytesLine(required=False)
+    ...     u = zope.schema.URI()
+
+    >>> context = ConfigurationMachine()
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=50).pprint
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+    ...          'u': u'http://www.zope.org' }))
+    {'f': 1.2,
+     'in_': 1,
+     'n': u'bob',
+     'u': 'http://www.zope.org',
+     'x': 'x.y.z'}
+
+    If we have extra data, we'll get an error:
+
+    >>> toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+    ...          'u': u'http://www.zope.org', 'a': u'1'})
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Unrecognized parameters:', 'a')
+
+    Unless we set a tagged value to say that extra arguments are ok:
+
+    >>> schema.setTaggedValue('keyword_arguments', True)
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+    ...          'u': u'http://www.zope.org', 'a': u'1'}))
+    {'a': u'1',
+     'f': 1.2,
+     'in_': 1,
+     'n': u'bob',
+     'u': 'http://www.zope.org',
+     'x': 'x.y.z'}
+
+
+    If we ommit required data we get an error telling us what was omitted:
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'}))
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Missing parameter:', 'u')
+
+    Although we can omit not-required data:
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2', 'n': u'bob',
+    ...          'u': u'http://www.zope.org', 'a': u'1'}))
+    {'a': u'1',
+     'f': 1.2,
+     'in_': 1,
+     'n': u'bob',
+     'u': 'http://www.zope.org'}
+
+    And we can ommit required fields if they have valid defaults
+    (defaults that are valid values):
+
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'1', 'f': u'1.2',
+    ...          'u': u'http://www.zope.org', 'a': u'1'}))
+    {'a': u'1',
+     'f': 1.2,
+     'in_': 1,
+     'n': u'rob',
+     'u': 'http://www.zope.org'}
+
+    We also get an error if any data was invalid:
+
+    >>> pprint(toargs(context, schema,
+    ...        {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+    ...          'u': u'http://www.zope.org', 'a': u'1'}))
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Invalid value for', 'in', '0')
+
+    """
+
+    data = dict(data)
+    args = {}
+    for name, field in schema.namesAndDescriptions(True):
+        field = field.bind(context)
+        n = name
+        if n.endswith('_') and iskeyword(n[:-1]):
+            n = n[:-1]
+
+        s = data.get(n, data)
+        if s is not data:
+            s = unicode(s)
+            del data[n]
+
+            try:
+                args[str(name)] = field.fromUnicode(s)
+            except zope.schema.ValidationError, v:
+                raise ConfigurationError(
+                    "Invalid value for", n, str(v)), None, sys.exc_info()[2]
+        elif field.required:
+            # if the default is valid, we can use that:
+            default = field.default
+            try:
+                field.validate(default)
+            except zope.schema.ValidationError:
+                raise ConfigurationError("Missing parameter:", n)
+            args[str(name)] = default
+
+    if data:
+        # we had data left over
+        try:
+            keyword_arguments = schema.getTaggedValue('keyword_arguments')
+        except KeyError:
+            keyword_arguments = False
+        if not keyword_arguments:
+            raise ConfigurationError("Unrecognized parameters:", *data)
+
+        for name in data:
+            args[str(name)] = data[name]
+
+    return args
+
+##############################################################################
+# Bootstrap code
+
+
+def _bootstrap(context):
+
+    # Set enough machinery to register other directives
+
+    # Define the directive (simple directive) directive by calling it's
+    # handler directly
+
+    info = 'Manually registered in zope/configuration/config.py'
+
+    context.info = info
+    defineSimpleDirective(
+        context,
+        namespace=metans, name='directive',
+        schema=IStandaloneDirectiveInfo,
+        handler=defineSimpleDirective)
+    context.info = ''
+
+    # OK, now that we have that, we can use the machine to define the
+    # other directives. This isn't the easiest way to proceed, but it lets
+    # us eat our own dogfood. :)
+
+    # Standalone groupingDirective
+    context((metans, 'directive'),
+            info,
+            name='groupingDirective',
+            namespace=metans,
+            handler="zope.configuration.config.defineGroupingDirective",
+            schema="zope.configuration.config.IStandaloneDirectiveInfo"
+            )
+
+    # Now we can use the grouping directive to define the directives directive
+    context((metans, 'groupingDirective'),
+            info,
+            name='directives',
+            namespace=metans,
+            handler="zope.configuration.config.DirectivesHandler",
+            schema="zope.configuration.config.IDirectivesInfo"
+            )
+
+    # directive and groupingDirective inside directives
+    context((metans, 'directive'),
+            info,
+            name='directive',
+            namespace=metans,
+            usedIn="zope.configuration.config.IDirectivesContext",
+            handler="zope.configuration.config.defineSimpleDirective",
+            schema="zope.configuration.config.IFullInfo"
+            )
+    context((metans, 'directive'),
+            info,
+            name='groupingDirective',
+            namespace=metans,
+            usedIn="zope.configuration.config.IDirectivesContext",
+            handler="zope.configuration.config.defineGroupingDirective",
+            schema="zope.configuration.config.IFullInfo"
+            )
+
+    # Setup complex directive directive, both standalone, and in
+    # directives directive
+    context((metans, 'groupingDirective'),
+            info,
+            name='complexDirective',
+            namespace=metans,
+            handler="zope.configuration.config.ComplexDirectiveDefinition",
+            schema="zope.configuration.config.IStandaloneDirectiveInfo"
+            )
+    context((metans, 'groupingDirective'),
+            info,
+            name='complexDirective',
+            namespace=metans,
+            usedIn="zope.configuration.config.IDirectivesContext",
+            handler="zope.configuration.config.ComplexDirectiveDefinition",
+            schema="zope.configuration.config.IFullInfo"
+            )
+
+    # Finally, setup subdirective directive
+    context((metans, 'directive'),
+            info,
+            name='subdirective',
+            namespace=metans,
+            usedIn="zope.configuration.config.IComplexDirectiveContext",
+            handler="zope.configuration.config.subdirective",
+            schema="zope.configuration.config.IDirectiveInfo"
+            )
+
+    # meta:provides
+    context((metans, 'directive'),
+            info,
+            name='provides',
+            namespace=metans,
+            handler="zope.configuration.config.provides",
+            schema="zope.configuration.config.IProvidesDirectiveInfo"
+            )
+

Deleted: zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -1,19 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation 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.
-#
-##############################################################################
-"""Standard configuration errors
-"""
-
-class ConfigurationError(Exception):
-    """There was an error in a configuration
-    """

Added: zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/exceptions.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,18 @@
+##############################################################################
+#
+# Copyright (c) 2011 Zope Foundation 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.
+#
+##############################################################################
+""" Backwards compatibility
+"""
+from zope.configmachine.exceptions import ConfigurationError # bw compat
+ConfigurationError = ConfigurationError # pyflakes
+

Modified: zope.configuration/branches/chrism-configmachine/src/zope/configuration/interfaces.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/interfaces.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/interfaces.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -13,104 +13,14 @@
 ##############################################################################
 """Zope Configuration (ZCML) interfaces
 """
-from zope.interface import Interface
-from zope.schema import BytesLine
 from zope.schema.interfaces import ValidationError
 
+from zope.configmachine.interfaces import IConfigurationContext # bw compat
+from zope.configmachine.interfaces import IGroupingContext # bw compat
+
+IConfigurationContext = IConfigurationContext # pyflakes
+IGroupingContext = IGroupingContext # pyflakes
+
 class InvalidToken(ValidationError):
     """Invaid token in list."""
 
-class IConfigurationContext(Interface):
-    """Configuration Context
-
-    The configuration context manages information about the state of
-    the configuration system, such as the package containing the
-    configuration file. More importantly, it provides methods for
-    importing objects and opening files relative to the package.
-    """
-
-    package = BytesLine(
-        title=u"The current package name",
-        description=u"""\
-          This is the name of the package containing the configuration
-          file being executed. If the configuration file was not
-          included by package, then this is None.
-          """,
-        required=False,
-        )
-
-    def resolve(dottedname):
-        """Resolve a dotted name to an object
-
-        A dotted name is constructed by concatenating a dotted module
-        name with a global name within the module using a dot.  For
-        example, the object named "spam" in the foo.bar module has a
-        dotted name of foo.bar.spam.  If the current package is a
-        prefix of a dotted name, then the package name can be relaced
-        with a leading dot, So, for example, if the configuration file
-        is in the foo package, then the dotted name foo.bar.spam can
-        be shortened to .bar.spam.
-
-        If the current package is multiple levels deep, multiple
-        leading dots can be used to refer to higher-level modules.
-        For example, if the current package is x.y.z, the dotted
-        object name ..foo refers to x.y.foo.
-        """
-
-    def path(filename):
-        """Compute a full file name for the given file
-
-        If the filename is relative to the package, then the returned
-        name will include the package path, otherwise, the original
-        file name is returned.
-        """
-
-    def checkDuplicate(filename):
-        """Check for duplicate imports of the same file.
-
-        Raises an exception if this file had been processed before.  This
-        is better than an unlimited number of conflict errors.
-        """
-
-    def processFile(filename):
-        """Check whether a file needs to be processed.
-
-        Return True if processing is needed and False otherwise.  If
-        the file needs to be processed, it will be marked as
-        processed, assuming that the caller will procces the file if
-        it needs to be procssed.
-        """
-
-    def action(self, discriminator, callable, args=(), kw={}, order=0,
-               includepath=None, info=None):
-        """Record a configuration action
-
-        The job of most directives is to compute actions for later
-        processing.  The action method is used to record those
-        actions.  The discriminator is used to to find actions that
-        conflict. Actions conflict if they have the same
-        discriminator. The exception to this is the special case of
-        the discriminator with the value None. An actions with a
-        discriminator of None never conflicts with other actions. This
-        is possible to add an order argument to crudely control the
-        order of execution.  'info' is optional source line information,
-        'includepath' is None (the default) or a tuple of include paths for
-        this action.
-        """
-
-    def provideFeature(name):
-        """Record that a named feature is available in this context."""
-
-    def hasFeature(name):
-        """Check whether a named feature is available in this context."""
-
-
-class IGroupingContext(Interface):
-
-    def before():
-        """Do something before processing nested directives
-        """
-
-    def after():
-        """Do something after processing nested directives
-        """

Deleted: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/bad.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/bad.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/bad.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -1,3 +0,0 @@
-# I'm bad. I want to be bad. Don't try to change me.
-
-import bad_to_the_bone

Deleted: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -1,358 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation 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.
-#
-##############################################################################
-"""Test configuration machinery.
-"""
-
-import sys
-import unittest
-import re
-from doctest import DocTestSuite
-from zope.testing import renormalizing
-from zope.configuration.config import metans, ConfigurationMachine
-from zope.configuration import config
-
-def test_config_extended_example():
-    """Configuration machine
-
-    Examples:
-
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
-
-    Register some test directives:
-
-    Start with a grouping directive that sets a package:
-
-    >>> machine((metans, "groupingDirective"),
-    ...         name="package", namespace=ns,
-    ...         schema="zope.configuration.tests.directives.IPackaged",
-    ...         handler="zope.configuration.tests.directives.Packaged",
-    ...         )
-
-    Now we can set the package:
-
-    >>> machine.begin((ns, "package"),
-    ...               package="zope.configuration.tests.directives",
-    ...               )
-
-    Which makes it easier to define the other directives:
-
-    First, define some simple directives:
-
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="simple",
-    ...         schema=".ISimple", handler=".simple")
-
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="newsimple",
-    ...         schema=".ISimple", handler=".newsimple")
-
-
-    and try them out:
-
-    >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc")
-    >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb")
-
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
-
-    >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second')]
-
-
-    Define and try a simple directive that uses a component:
-
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="factory",
-    ...         schema=".IFactory", handler=".factory")
-
-
-    >>> machine((ns, "factory"), factory=u".f")
-    >>> pprint(machine.actions[-1:])
-    [(('factory', 1, 2), f)]
-
-    Define and try a complex directive:
-
-    >>> machine.begin((metans, "complexDirective"),
-    ...               namespace=ns, name="testc",
-    ...               schema=".ISimple", handler=".Complex")
-
-    >>> machine((metans, "subdirective"),
-    ...         name="factory", schema=".IFactory")
-
-    >>> machine.end()
-
-    >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc')
-    >>> machine((ns, "factory"), "fourth", factory=".f")
-
-    Note that we can't call a complex method unless there is a directive for
-    it:
-
-    >>> machine((ns, "factory2"), factory=".f")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid directive', 'factory2')
-
-
-    >>> machine.end()
-    >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second'),
-     (('factory', 1, 2), f),
-     ('Complex.__init__', None, (), {}, (), 'third'),
-     (('Complex.factory', 1, 2),
-      f,
-      (u'ca',),
-      {},
-      (),
-      'fourth'),
-     (('Complex', 1, 2),
-      f,
-      (u'xxx', 'cc'),
-      {},
-      (),
-      'third')]
-
-    Done with the package
-
-    >>> machine.end()
-
-
-    Verify that we can use a simple directive outside of the package:
-
-    >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb")
-
-    But we can't use the factory directive, because it's only valid
-    inside a package directive:
-
-    >>> machine((ns, "factory"), factory=u".F")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid value for', 'factory',""" \
-       """ "Can't use leading dots in dotted names, no package has been set.")
-
-    >>> pprint(machine.actions)
-    [(('simple', u'aa', u'xxx', 'cc'),
-      f,
-      (u'aa', u'xxx', 'cc'),
-      {},
-      (),
-      'first'),
-     (('newsimple', u'naa', u'nbb', 'ncc'),
-      f,
-      (u'naa', u'nbb', 'ncc'),
-      {},
-      (),
-      'second'),
-     (('factory', 1, 2), f),
-     ('Complex.__init__', None, (), {}, (), 'third'),
-     (('Complex.factory', 1, 2),
-      f,
-      (u'ca',),
-      {},
-      (),
-      'fourth'),
-     (('Complex', 1, 2),
-      f,
-      (u'xxx', 'cc'),
-      {},
-      (),
-      'third'),
-     (('simple', u'oaa', u'obb', 'occ'),
-      f,
-      (u'oaa', u'obb', 'occ'))]
-
-    """
-    #'
-
-def test_keyword_handling():
-    """
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
-
-    Register some test directives:
-
-    Start with a grouping directive that sets a package:
-
-    >>> machine((metans, "groupingDirective"),
-    ...         name="package", namespace=ns,
-    ...         schema="zope.configuration.tests.directives.IPackaged",
-    ...         handler="zope.configuration.tests.directives.Packaged",
-    ...         )
-
-    Now we can set the package:
-
-    >>> machine.begin((ns, "package"),
-    ...               package="zope.configuration.tests.directives",
-    ...               )
-
-    Which makes it easier to define the other directives:
-
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="k",
-    ...         schema=".Ik", handler=".k")
-
-
-    >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"})
-
-    >>> machine.actions
-    [(('k', 'f'), f, ('f', 'c', 'x'), {}, (), 'yee ha')]
-    """
-
-def test_basepath_absolute():
-    """Path must always return an absolute path.
-
-    >>> import os
-    >>> class stub:
-    ...     __file__ = os.path.join('relative', 'path')
-    >>> c = config.ConfigurationContext()
-    >>> c.package = stub()
-
-    >>> os.path.isabs(c.path('y/z'))
-    True
-    """
-
-def test_basepath_uses_dunder_path():
-    """Determine package path using __path__ if __file__ isn't available.
-    (i.e. namespace package installed with --single-version-externally-managed)
-
-    >>> import os
-    >>> class stub:
-    ...     __path__ = [os.path.join('relative', 'path')]
-    >>> c = config.ConfigurationContext()
-    >>> c.package = stub()
-
-    >>> os.path.isabs(c.path('y/z'))
-    True
-    """
-
-def test_trailing_dot_in_resolve():
-    """Dotted names are no longer allowed to end in dots
-
-    >>> c = config.ConfigurationContext()
-
-    >>> c.resolve('zope.')
-    Traceback (most recent call last):
-    ...
-    ValueError: Trailing dots are no longer supported in dotted names
-
-    >>> c.resolve('  ')
-    Traceback (most recent call last):
-    ...
-    ValueError: The given name is blank
-    """
-
-def test_bad_dotted_last_import():
-    """
-    >>> c = config.ConfigurationContext()
-
-    Import error caused by a bad last component in the dotted name.
-
-    >>> c.resolve('zope.configuration.tests.nosuch')
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ImportError: Module zope.configuration.tests""" \
-                                               """ has no global nosuch
-    """
-
-def test_bad_dotted_import():
-    """
-    >>> c = config.ConfigurationContext()
-
-    Import error caused by a totally wrong dotted name.
-
-    >>> c.resolve('zope.configuration.nosuch.noreally')
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ImportError: Couldn't import""" \
-                   """ zope.configuration.nosuch, No module named nosuch
-    """
-
-def test_bad_sub_last_import():
-    """
-    >>> c = config.ConfigurationContext()
-
-    Import error caused by a bad sub import inside the referenced
-    dotted name. Here we keep the standard traceback.
-
-    >>> c.resolve('zope.configuration.tests.victim')
-    Traceback (most recent call last):
-    ...
-      File "...bad.py", line 3 in ?
-       import bad_to_the_bone
-    ImportError: No module named bad_to_the_bone
-
-    Cleanup:
-
-    >>> for name in ('zope.configuration.tests.victim',
-    ...              'zope.configuration.tests.bad'):
-    ...    if name in sys.modules:
-    ...        del sys.modules[name]
-    """
-
-def test_bad_sub_import():
-    """
-    >>> c = config.ConfigurationContext()
-
-    Import error caused by a bad sub import inside part of the referenced
-    dotted name. Here we keep the standard traceback.
-
-    >>> c.resolve('zope.configuration.tests.victim.nosuch')
-    Traceback (most recent call last):
-    ...
-      File "...bad.py", line 3 in ?
-       import bad_to_the_bone
-    ImportError: No module named bad_to_the_bone
-
-    Cleanup:
-
-    >>> for name in ('zope.configuration.tests.victim',
-    ...              'zope.configuration.tests.bad'):
-    ...    if name in sys.modules:
-    ...        del sys.modules[name]
-    """
-
-def test_suite():
-    checker = renormalizing.RENormalizing([
-        (re.compile(r"<type 'exceptions.(\w+)Error'>:"),
-                    r'exceptions.\1Error:'),
-        ])
-    return unittest.TestSuite((
-        DocTestSuite('zope.configuration.fields'),
-        DocTestSuite('zope.configuration.config',checker=checker),
-        DocTestSuite(),
-        ))
-
-if __name__ == '__main__': unittest.main()

Added: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,248 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation 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.
+#
+##############################################################################
+"""Test configuration machinery.
+"""
+import re
+import unittest
+
+from doctest import DocTestSuite
+from zope.testing import renormalizing
+
+from zope.configuration.config import metans
+from zope.configuration.config import ConfigurationMachine
+
+def test_config_extended_example():
+    """Configuration machine
+
+    Examples:
+
+    >>> machine = ConfigurationMachine()
+    >>> ns = "http://www.zope.org/testing"
+
+    Register some test directives:
+
+    Start with a grouping directive that sets a package:
+
+    >>> machine((metans, "groupingDirective"),
+    ...         name="package", namespace=ns,
+    ...         schema="zope.configuration.tests.directives.IPackaged",
+    ...         handler="zope.configuration.tests.directives.Packaged",
+    ...         )
+
+    Now we can set the package:
+
+    >>> machine.begin((ns, "package"),
+    ...               package="zope.configuration.tests.directives",
+    ...               )
+
+    Which makes it easier to define the other directives:
+
+    First, define some simple directives:
+
+    >>> machine((metans, "directive"),
+    ...         namespace=ns, name="simple",
+    ...         schema=".ISimple", handler=".simple")
+
+    >>> machine((metans, "directive"),
+    ...         namespace=ns, name="newsimple",
+    ...         schema=".ISimple", handler=".newsimple")
+
+
+    and try them out:
+
+    >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc")
+    >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb")
+
+    >>> from pprint import PrettyPrinter
+    >>> pprint=PrettyPrinter(width=50).pprint
+
+    >>> pprint(machine.actions)
+    [(('simple', u'aa', u'xxx', 'cc'),
+      f,
+      (u'aa', u'xxx', 'cc'),
+      {},
+      (),
+      'first'),
+     (('newsimple', u'naa', u'nbb', 'ncc'),
+      f,
+      (u'naa', u'nbb', 'ncc'),
+      {},
+      (),
+      'second')]
+
+
+    Define and try a simple directive that uses a component:
+
+    >>> machine((metans, "directive"),
+    ...         namespace=ns, name="factory",
+    ...         schema=".IFactory", handler=".factory")
+
+
+    >>> machine((ns, "factory"), factory=u".f")
+    >>> pprint(machine.actions[-1:])
+    [(('factory', 1, 2), f)]
+
+    Define and try a complex directive:
+
+    >>> machine.begin((metans, "complexDirective"),
+    ...               namespace=ns, name="testc",
+    ...               schema=".ISimple", handler=".Complex")
+
+    >>> machine((metans, "subdirective"),
+    ...         name="factory", schema=".IFactory")
+
+    >>> machine.end()
+
+    >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc')
+    >>> machine((ns, "factory"), "fourth", factory=".f")
+
+    Note that we can't call a complex method unless there is a directive for
+    it:
+
+    >>> machine((ns, "factory2"), factory=".f")
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Invalid directive', 'factory2')
+
+
+    >>> machine.end()
+    >>> pprint(machine.actions)
+    [(('simple', u'aa', u'xxx', 'cc'),
+      f,
+      (u'aa', u'xxx', 'cc'),
+      {},
+      (),
+      'first'),
+     (('newsimple', u'naa', u'nbb', 'ncc'),
+      f,
+      (u'naa', u'nbb', 'ncc'),
+      {},
+      (),
+      'second'),
+     (('factory', 1, 2), f),
+     ('Complex.__init__', None, (), {}, (), 'third'),
+     (('Complex.factory', 1, 2),
+      f,
+      (u'ca',),
+      {},
+      (),
+      'fourth'),
+     (('Complex', 1, 2),
+      f,
+      (u'xxx', 'cc'),
+      {},
+      (),
+      'third')]
+
+    Done with the package
+
+    >>> machine.end()
+
+
+    Verify that we can use a simple directive outside of the package:
+
+    >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb")
+
+    But we can't use the factory directive, because it's only valid
+    inside a package directive:
+
+    >>> machine((ns, "factory"), factory=u".F")
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: ('Invalid value for', 'factory',""" \
+       """ "Can't use leading dots in dotted names, no package has been set.")
+
+    >>> pprint(machine.actions)
+    [(('simple', u'aa', u'xxx', 'cc'),
+      f,
+      (u'aa', u'xxx', 'cc'),
+      {},
+      (),
+      'first'),
+     (('newsimple', u'naa', u'nbb', 'ncc'),
+      f,
+      (u'naa', u'nbb', 'ncc'),
+      {},
+      (),
+      'second'),
+     (('factory', 1, 2), f),
+     ('Complex.__init__', None, (), {}, (), 'third'),
+     (('Complex.factory', 1, 2),
+      f,
+      (u'ca',),
+      {},
+      (),
+      'fourth'),
+     (('Complex', 1, 2),
+      f,
+      (u'xxx', 'cc'),
+      {},
+      (),
+      'third'),
+     (('simple', u'oaa', u'obb', 'occ'),
+      f,
+      (u'oaa', u'obb', 'occ'))]
+
+    """
+    #'
+
+def test_keyword_handling():
+    """
+    >>> machine = ConfigurationMachine()
+    >>> ns = "http://www.zope.org/testing"
+
+    Register some test directives:
+
+    Start with a grouping directive that sets a package:
+
+    >>> machine((metans, "groupingDirective"),
+    ...         name="package", namespace=ns,
+    ...         schema="zope.configuration.tests.directives.IPackaged",
+    ...         handler="zope.configuration.tests.directives.Packaged",
+    ...         )
+
+    Now we can set the package:
+
+    >>> machine.begin((ns, "package"),
+    ...               package="zope.configuration.tests.directives",
+    ...               )
+
+    Which makes it easier to define the other directives:
+
+    >>> machine((metans, "directive"),
+    ...         namespace=ns, name="k",
+    ...         schema=".Ik", handler=".k")
+
+
+    >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"})
+
+    >>> machine.actions
+    [(('k', 'f'), f, ('f', 'c', 'x'), {}, (), 'yee ha')]
+    """
+
+
+
+def test_suite():
+    checker = renormalizing.RENormalizing([
+        (re.compile(r"<type 'exceptions.(\w+)Error'>:"),
+                    r'exceptions.\1Error:'),
+        ])
+    return unittest.TestSuite((
+        DocTestSuite('zope.configuration.config',checker=checker),
+        DocTestSuite('zope.configuration.fields'),
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()
+

Copied: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_fields.py (from rev 122694, zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_config.py)
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_fields.py	                        (rev 0)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_fields.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation 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.
+#
+##############################################################################
+"""Test configuration machinery.
+"""
+import unittest
+
+from doctest import DocTestSuite
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('zope.configuration.fields'),
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()

Modified: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_xmlconfig.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_xmlconfig.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/test_xmlconfig.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -20,6 +20,7 @@
 from zope.testing import renormalizing
 from zope.configuration import xmlconfig, config
 from zope.configuration.tests.samplepackage import foo
+from zope.configuration.config import _bootstrap
 from pprint import PrettyPrinter, pprint
 
 

Deleted: zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/victim.py
===================================================================
--- zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/victim.py	2011-08-28 20:50:38 UTC (rev 122694)
+++ zope.configuration/branches/chrism-configmachine/src/zope/configuration/tests/victim.py	2011-08-28 23:38:30 UTC (rev 122695)
@@ -1 +0,0 @@
-import bad



More information about the checkins mailing list