[Checkins] SVN: zope.configuration/trunk/ Merge tseaver-test_cleanup branch.

Tres Seaver cvs-admin at zope.org
Fri May 11 00:04:16 UTC 2012


Log message for revision 125836:
  Merge tseaver-test_cleanup branch.

Changed:
  _U  zope.configuration/trunk/
  U   zope.configuration/trunk/.bzrignore
  U   zope.configuration/trunk/CHANGES.txt
  A   zope.configuration/trunk/docs/
  U   zope.configuration/trunk/docs/Makefile
  U   zope.configuration/trunk/docs/api/config.rst
  U   zope.configuration/trunk/docs/api/docutils.rst
  U   zope.configuration/trunk/docs/api/exceptions.rst
  U   zope.configuration/trunk/docs/api/fields.rst
  U   zope.configuration/trunk/docs/api/interfaces.rst
  U   zope.configuration/trunk/docs/api/name.rst
  U   zope.configuration/trunk/docs/api/stxdocs.rst
  U   zope.configuration/trunk/docs/api/xmlconfig.rst
  U   zope.configuration/trunk/docs/api/zopeconfigure.rst
  U   zope.configuration/trunk/docs/conf.py
  U   zope.configuration/trunk/docs/index.rst
  U   zope.configuration/trunk/docs/make.bat
  U   zope.configuration/trunk/docs/narr.rst
  A   zope.configuration/trunk/setup.cfg
  U   zope.configuration/trunk/setup.py
  D   zope.configuration/trunk/src/zope/configuration/README.txt
  A   zope.configuration/trunk/src/zope/configuration/_compat.py
  U   zope.configuration/trunk/src/zope/configuration/config.py
  U   zope.configuration/trunk/src/zope/configuration/docutils.py
  D   zope.configuration/trunk/src/zope/configuration/exclude.txt
  U   zope.configuration/trunk/src/zope/configuration/fields.py
  U   zope.configuration/trunk/src/zope/configuration/interfaces.py
  U   zope.configuration/trunk/src/zope/configuration/name.py
  U   zope.configuration/trunk/src/zope/configuration/stxdocs.py
  A   zope.configuration/trunk/src/zope/configuration/tests/conditions.py
  U   zope.configuration/trunk/src/zope/configuration/tests/conditions.zcml
  U   zope.configuration/trunk/src/zope/configuration/tests/directives.py
  A   zope.configuration/trunk/src/zope/configuration/tests/nested.py
  A   zope.configuration/trunk/src/zope/configuration/tests/notyet.py
  A   zope.configuration/trunk/src/zope/configuration/tests/samplepackage/NamedForClass.py
  U   zope.configuration/trunk/src/zope/configuration/tests/samplepackage/foo.py
  U   zope.configuration/trunk/src/zope/configuration/tests/schema.zcml
  A   zope.configuration/trunk/src/zope/configuration/tests/simple.py
  U   zope.configuration/trunk/src/zope/configuration/tests/simple.zcml
  A   zope.configuration/trunk/src/zope/configuration/tests/test___init__.py
  D   zope.configuration/trunk/src/zope/configuration/tests/test_conditions.py
  U   zope.configuration/trunk/src/zope/configuration/tests/test_config.py
  U   zope.configuration/trunk/src/zope/configuration/tests/test_docutils.py
  A   zope.configuration/trunk/src/zope/configuration/tests/test_fields.py
  A   zope.configuration/trunk/src/zope/configuration/tests/test_name.py
  D   zope.configuration/trunk/src/zope/configuration/tests/test_nested.py
  D   zope.configuration/trunk/src/zope/configuration/tests/test_simple.py
  U   zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py
  A   zope.configuration/trunk/src/zope/configuration/tests/test_zopeconfigure.py
  U   zope.configuration/trunk/src/zope/configuration/xmlconfig.py
  U   zope.configuration/trunk/src/zope/configuration/zopeconfigure.py
  A   zope.configuration/trunk/tox.ini

-=-

Property changes on: zope.configuration/trunk
___________________________________________________________________
Added: svn:mergeinfo
   + /zope.configuration/branches/tseaver-test_cleanup:125657,125662-125671,125696-125724,125749-125791,125795-125805,125824-125835

Added: svk:merge
   + 62d5b8a3-27da-0310-9561-8e5933582275:/zope.configuration/branches/tseaver-test_cleanup:125835


Modified: zope.configuration/trunk/.bzrignore
===================================================================
--- zope.configuration/trunk/.bzrignore	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/.bzrignore	2012-05-11 00:04:13 UTC (rev 125836)
@@ -4,3 +4,9 @@
 ./eggs
 ./parts
 *.egg-info
+docs/_build
+.tox
+.coverage
+nosetests.xml
+src/coverage.xml
+__pycache__

Modified: zope.configuration/trunk/CHANGES.txt
===================================================================
--- zope.configuration/trunk/CHANGES.txt	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/CHANGES.txt	2012-05-11 00:04:13 UTC (rev 125836)
@@ -2,6 +2,30 @@
 Changes
 =======
 
+4.0.0 (unreleased)
+------------------
+
+- 100% unit test coverage.
+
+- Automated build of Sphinx HTML docs and running doctest snippets via tox.
+
+- Dropped hard testing dependency on ``zope.testing``.
+
+- Added explicit support for PyPy.
+
+- Added explicit support for Python 3.2.
+
+- Dropped explicit support for Python 2.4 / 2.5.
+
+- Added support for continuous integration using ``tox`` and ``jenkins``.
+
+- Added ``Sphinx`` documentation.
+
+- Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies).
+
+- Added ``setup.py dev`` alias (runs ``setup.py develop`` plus installs
+  ``nose`` and ``coverage``).
+
 3.8.1 (2012-05-05)
 ------------------
 

Copied: zope.configuration/trunk/setup.cfg (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/setup.cfg)
===================================================================
--- zope.configuration/trunk/setup.cfg	                        (rev 0)
+++ zope.configuration/trunk/setup.cfg	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,11 @@
+[nosetests]
+nocapture=1
+cover-package=zope.configuration
+cover-erase=1
+with-doctest=0
+where=src
+ignore-files=(bad|victim|notyet).py
+
+[aliases]
+dev = develop easy_install zope.configuration[testing]
+docs = easy_install zope.configuration[docs]

Modified: zope.configuration/trunk/setup.py
===================================================================
--- zope.configuration/trunk/setup.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/setup.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -56,19 +56,16 @@
                     suite.addTest(mod.test_suite())
     return suite
 
+TESTS_REQUIRE = []
+
 setup(name='zope.configuration',
-      version = '3.8.1',
+      version = '4.0.0dev',
       author='Zope Foundation and Contributors',
       author_email='zope-dev at zope.org',
       description='Zope Configuration Markup Language (ZCML)',
       long_description=(
           read('README.txt')
           + '\n\n' +
-          'Detailed Documentation\n' +
-          '----------------------\n'
-          + '\n\n' +
-          read('src', 'zope', 'configuration', 'README.txt')
-          + '\n\n' +
           read('CHANGES.txt')
           ),
       keywords = "zope configuration zcml",
@@ -78,6 +75,12 @@
           'Intended Audience :: Developers',
           'License :: OSI Approved :: Zope Public License',
           'Programming Language :: Python',
+          'Programming Language :: Python :: 2.6',
+          'Programming Language :: Python :: 2.7',
+          'Programming Language :: Python3',
+          'Programming Language :: Python :: 3.2',
+          'Programming Language :: Python :: Implementation :: CPython',
+          'Programming Language :: Python :: Implementation :: PyPy',
           'Natural Language :: English',
           'Operating System :: OS Independent',
           'Topic :: Internet :: WWW/HTTP',
@@ -87,8 +90,11 @@
       packages=find_packages('src'),
       package_dir={'': 'src'},
       namespace_packages=['zope'],
-      extras_require=dict(
-          test=['zope.testing']),
+      extras_require={
+        'docs': ['Sphinx', 'repoze.sphinx.autointerface'],
+        'test': ['zope.testing'],
+        'testing': TESTS_REQUIRE + ['nose', 'coverage'],
+      },
       install_requires=['zope.i18nmessageid',
                         'zope.interface',
                         'zope.schema',
@@ -96,6 +102,6 @@
                        ],
       include_package_data=True,
       zip_safe=False,
-      tests_require = 'zope.testing',
+      tests_require = TESTS_REQUIRE,
       test_suite='__main__.alltests',
       )

Deleted: zope.configuration/trunk/src/zope/configuration/README.txt
===================================================================
--- zope.configuration/trunk/src/zope/configuration/README.txt	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/README.txt	2012-05-11 00:04:13 UTC (rev 125836)
@@ -1,75 +0,0 @@
-==========================
-Zope configuration system
-==========================
-
-The zope configuration system provides an extensible system for
-supporting variouse kinds of configurations.
-
-It is based on the idea of configuration directives. Users of the
-configuration system provide configuration directives in some
-language that express configuration choices. The intent is that the
-language be pluggable.  An XML language is provided by default.
-
-Configuration is performed in three stages. In the first stage,
-directives are processed to compute configuration actions.
-Configuration actions consist of:
-
-- A discriminator
-
-- A callable
-
-- Positional arguments
-
-- Keyword arguments
-
-The actions are essentially delayed function calls.  Two or more
-actions conflict if they have the same discriminator.  The
-configuration system has rules for resolving conflicts. If conflicts
-cannot be resolved, an error will result.  Conflict resolution
-typically discards all but one of the conflicting actions, so that
-the remaining action of the originally-conflicting actions no longer
-conflicts.  Non-conflicting actions are executed in the order that
-they were created by passing the positional and non-positional
-arguments to the action callable.
-
-The system is extensible. There is a meta-configuration language for
-defining configuration directives. A directive is defined by
-providing meta data about the directive and handler code to process
-the directive.  There are four kinds of directives:
-
-- Simple directives compute configuration actions.  Their handlers
-  are typically functions that take a context and zero or more
-  keyword arguments and return a sequence of configuration actions.
-
-  To learn how to create simple directives, see `tests/test_simple.py`.
-
-
-- Grouping directives collect information to be used by nested
-  directives. They are called with a context object which they adapt
-  to some interface that extends IConfigurationContext.
-
-  To learn how to create grouping directives, look at the
-  documentation in zopeconfigure.py, which provides the implementation
-  of the zope `configure` directive.
-
-  Other directives can be nested in grouping directives.
-
-  To learn how to implement nested directives, look at the
-  documentation in `tests/test_nested.py`.
-
-- Complex directives are directives that have subdirectives.  
-  Subdirectives have handlers that are simply methods of complex
-  directives. Complex diretives are handled by factories, typically
-  classes, that create objects that have methods for handling
-  subdirectives. These objects also have __call__ methods that are
-  called when processing of subdirectives is finished.
-
-  Complex directives only exist to support old directive
-  handlers. They will probably be deprecated in the future.
-
-- Subdirectives are nested in complex directives. They are like
-  simple directives except that they hane handlers that are complex
-  directive methods.
-
-  Subdirectives, like complex directives only exist to support old
-  directive handlers. They will probably be deprecated in the future.

Copied: zope.configuration/trunk/src/zope/configuration/_compat.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/_compat.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/_compat.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/_compat.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,103 @@
+##############################################################################
+#
+# Copyright (c) 2012 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.
+#
+##############################################################################
+import sys
+
+PY3 = sys.version_info[0] >= 3
+
+if PY3: #pragma NO COVER
+
+    import builtins
+    from io import StringIO
+
+    string_types = str,
+    text_type = str
+    def b(s):
+        return s.encode("latin-1")
+    def u(s):
+        return s
+
+    # borrowed from 'six'
+    print_ = getattr(builtins, "print")
+
+    # borrowed from 'six'
+    def reraise(tp, value, tb=None):
+        if value is None:
+            value = tp
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+else: #pragma NO COVER
+
+    import __builtin__  as builtins
+    from StringIO import StringIO
+
+    text_type = unicode
+    string_types = basestring,
+    def b(s):
+        return s
+    def u(s):
+        return unicode(s, "unicode_escape")
+
+    # borrowed from 'six'
+    def print_(*args, **kwargs):
+        """The new-style print function."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+
+    # borrowed from 'six'
+    exec("""\
+def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")

Modified: zope.configuration/trunk/src/zope/configuration/config.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/config.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/config.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -12,24 +12,30 @@
 #
 ##############################################################################
 """Configuration processor
-
-See README.txt.
 """
-__docformat__ = 'restructuredtext'
-import __builtin__
+from keyword import iskeyword
 import operator
 import os.path
 import sys
 
-import zope.schema
+from zope.interface.adapter import AdapterRegistry
+from zope.interface import Interface
+from zope.interface import implementer
+from zope.interface import providedBy
+from zope.schema import TextLine
+from zope.schema import URI
+from zope.schema import ValidationError
 
-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
+from zope.configuration.fields import GlobalInterface
+from zope.configuration.fields import GlobalObject
+from zope.configuration._compat import builtins
+from zope.configuration._compat import reraise
+from zope.configuration._compat import string_types
+from zope.configuration._compat import text_type
+from zope.configuration._compat import u
 
 
 zopens = 'http://namespaces.zope.org/zope'
@@ -38,6 +44,7 @@
 
 _import_chickens = {}, {}, ("*",) # dead chickens needed by __import__
 
+
 class ConfigurationContext(object):
     """Mix-in that implements IConfigurationContext
 
@@ -75,7 +82,7 @@
 
       - ``args``, positional arguments for the action
 
-      - ``kw`, keyword arguments for the action
+      - ``kw``, keyword arguments for the action
 
       - ``includepath``, a tuple of include file names (defaults to ())
 
@@ -90,42 +97,10 @@
         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'>
+        """Resolve a dotted name to an object.
         """
-
         name = dottedname.strip()
+
         if not name:
             raise ValueError("The given name is blank")
 
@@ -133,6 +108,7 @@
             return self.package
 
         names = name.split('.')
+
         if not names[-1]:
             raise ValueError(
                 "Trailing dots are no longer supported in dotted names")
@@ -140,7 +116,7 @@
         if len(names) == 1:
             # Check for built-in objects
             marker = object()
-            obj = getattr(__builtin__, names[0], marker)
+            obj = getattr(builtins, names[0], marker)
             if obj is not marker:
                 return obj
 
@@ -153,11 +129,8 @@
             pnames = self.package.__name__.split(".")
             pnames.append('')
             while names and not names[0]:
+                names.pop(0)
                 try:
-                    names.pop(0)
-                except IndexError:
-                    raise ConfigurationError("Invalid global name", name)
-                try:
                     pnames.pop()
                 except IndexError:
                     raise ConfigurationError("Invalid global name", name)
@@ -176,7 +149,7 @@
 
         try:
             mod = __import__(mname, *_import_chickens)
-        except ImportError, v:
+        except ImportError as v:
             if sys.exc_info()[2].tb_next is not None:
                 # ImportError was caused deeper
                 raise
@@ -203,37 +176,16 @@
                     "ImportError: Module %s has no global %s" % (mname, oname))
 
     def path(self, filename):
+        """ Compute package-relative paths.
         """
-        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:
@@ -254,61 +206,19 @@
 
         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
+        """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:
@@ -318,111 +228,13 @@
 
     def action(self, discriminator, callable=None, args=(), kw=None, order=0,
                includepath=None, info=None, **extra):
-        """Add an action with the given discriminator, callable and arguments
+        """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})
-        >>> from pprint import PrettyPrinter
-        >>> pprint=PrettyPrinter(width=60).pprint
-        >>> pprint(c.actions)
-        [{'args': (1,),
-          'callable': f,
-          'discriminator': 1,
-          'includepath': (),
-          'info': '',
-          'kw': {'x': 1},
-          'order': 0}]
-
-        >>> c.action(None)
-        >>> pprint(c.actions)
-        [{'args': (1,),
-          'callable': f,
-          'discriminator': 1,
-          'includepath': (),
-          'info': '',
-          'kw': {'x': 1},
-          'order': 0},
-         {'args': (),
-          'callable': None,
-          'discriminator': None,
-          'includepath': (),
-          'info': '',
-          'kw': {},
-          'order': 0}]
-
-        Now set the include path and info:
-
-        >>> c.includepath = ('foo.zcml',)
-        >>> c.info = "?"
-        >>> c.action(None)
-        >>> pprint(c.actions[-1])
-        {'args': (),
-         'callable': None,
-         'discriminator': None,
-         'includepath': ('foo.zcml',),
-         'info': '?',
-         'kw': {},
-         'order': 0}
-
-        We can add an order argument to crudely control the order
-        of execution:
-
-        >>> c.action(None, order=99999)
-        >>> pprint(c.actions[-1])
-        {'args': (),
-         'callable': None,
-         'discriminator': None,
-         'includepath': ('foo.zcml',),
-         'info': '?',
-         'kw': {},
-         'order': 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',))
-        >>> pprint(c.actions[-1])
-        {'args': (),
-         'callable': None,
-         'discriminator': None,
-         'includepath': ('abc',),
-         'info': '?',
-         'kw': {},
-         'order': 0}
-
-        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')
-        >>> pprint(c.actions[-1])
-        {'args': (),
-         'callable': None,
-         'discriminator': None,
-         'includepath': ('foo.zcml',),
-         'info': 'abc',
-         'kw': {},
-         'order': 0}
-        
         """
         if kw is None:
             kw = {}
@@ -453,76 +265,19 @@
         """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.
+        See :meth:`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')
     """
 
 
@@ -542,7 +297,7 @@
         r.register([interface], Interface, '', factory)
 
     def document(self, name, schema, usedIn, handler, info, parent=None):
-        if isinstance(name, (str, unicode)):
+        if isinstance(name, string_types):
             name = ('', name)
         self._docRegistry.append((name, schema, usedIn, handler, info, parent))
 
@@ -561,40 +316,10 @@
                 "The directive %s cannot be used in this context" % (name, ))
         return f
 
+ at implementer(IConfigurationContext)
 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")
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple', u'aa', u'xxx', 'cc'),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
-
-    A more extensive example can be found in the unit tests.
     """
-
-    implements(IConfigurationContext)
-
     package = None
     basepath = None
     includepath = ()
@@ -630,55 +355,10 @@
         self.stack[-1].context.info = info
 
     def execute_actions(self, clear=True, testing=False):
-        """Execute the configuration actions
+        """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,), {})]
-
-
+        This calls the action callables after resolving conflicts.
         """
-
         try:
             for action in resolveConflicts(self.actions):
                 callable = action['callable']
@@ -689,14 +369,15 @@
                 info = action['info']
                 try:
                     callable(*args, **kw)
-                except (KeyboardInterrupt, SystemExit): # pragma: no cover
+                except (KeyboardInterrupt, SystemExit): #pragma NO COVER
                     raise
                 except:
                     if testing:
                         raise
                     t, v, tb = sys.exc_info()
                     try:
-                        raise ConfigurationExecutionError(t, v, info), None, tb
+                        reraise(ConfigurationExecutionError(t, v, info),
+                                None, tb)
                     finally:
                        del t, v, tb
                 
@@ -707,11 +388,10 @@
 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):
+    def __str__(self): #pragma NO COVER
         return "%s: %s\n  in:\n  %s" % (self.etype, self.evalue, self.info)
 
 ##############################################################################
@@ -741,6 +421,7 @@
         """Finish processing a directive
         """
 
+ at implementer(IStackItem)
 class SimpleStackItem(object):
     """Simple stack item
 
@@ -751,9 +432,7 @@
     It also defers any computation until the end of the directive
     has been reached.
     """
-
-    implements(IStackItem)
-
+    #XXX why this *argdata hack instead of schema, data?
     def __init__(self, context, handler, info, *argdata):
         newcontext = GroupingContextDecorator(context)
         newcontext.info = info
@@ -779,6 +458,7 @@
                     action = expand_action(*action) # b/c
                 context.action(**action)
 
+ at implementer(IStackItem)
 class RootStackItem(object):
 
     def __init__(self, context):
@@ -789,7 +469,6 @@
 
         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:
@@ -800,6 +479,7 @@
     def finish(self):
         pass
 
+ at implementer(IStackItem)
 class GroupingStackItem(RootStackItem):
     """Stack item for a grouping directive
 
@@ -810,148 +490,8 @@
 
     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)
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('before', 1, 2),
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('simple', 1, 2, {'z': 'zope'}),
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0}]
-
-    Finally, we call finish, which calls the decorator after method:
-
-    >>> item.finish()
-
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('before', 1, 2),
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('simple', 1, 2, {'z': 'zope'}),
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': 'after',
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0}]
-
-    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)
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('before', 1, 2),
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': 'after',
-      'includepath': (),
-      'info': '',
-      'kw': {},
-      'order': 0}]
     """
 
-    implements(IStackItem)
-
     def __init__(self, context):
         super(GroupingStackItem, self).__init__(context)
 
@@ -980,6 +520,8 @@
 def noop():
     pass
 
+
+ at implementer(IStackItem)
 class ComplexStackItem(object):
     """Complex stack item
 
@@ -989,128 +531,7 @@
     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.
-
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': 'init',
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0}]
-
-    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)
-    [{'args': (),
-      'callable': f,
-      'discriminator': 'init',
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('sub', u'av', u'bv'),
-      'includepath': (),
-      'info': 'baz',
-      'kw': {},
-      'order': 0}]
-
-    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)
-    [{'args': (),
-      'callable': f,
-      'discriminator': 'init',
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('sub', u'av', u'bv'),
-      'includepath': (),
-      'info': 'baz',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('call', u'xv', u'yv'),
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0}]
     """
-
-    implements(IStackItem)
-
     def __init__(self, meta, context, data, info):
         newcontext = GroupingContextDecorator(context)
         newcontext.info = info
@@ -1124,7 +545,6 @@
     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)
@@ -1135,15 +555,13 @@
         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
-
+        # XXX why not just use callable()?
         try:
             actions = self.handler()
-        except AttributeError, v:
-            if v[0] == '__call__':
+        except AttributeError as v:
+            if v.args[0] == '__call__':
                 return # noncallable
             raise
         except TypeError:
@@ -1156,17 +574,17 @@
                     action = expand_action(*action)
                 self.context.action(**action)
 
+
 ##############################################################################
 # Helper classes
 
+ at implementer(IConfigurationContext, IGroupingContext)
 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():
@@ -1188,7 +606,7 @@
 ##############################################################################
 # Directive-definition
 
-class DirectiveSchema(fields.GlobalInterface):
+class DirectiveSchema(GlobalInterface):
     """A field that contains a global variable value that must be a schema
     """
 
@@ -1196,14 +614,18 @@
     """Schema for the ``directives`` directive
     """
 
-    namespace = zope.schema.URI(
-        title=u"Namespace",
-        description=u"The namespace in which directives' names will be defined",
+    namespace = URI(
+        title=u("Namespace"),
+        description=u("The namespace in which directives' names "
+                      "will be defined"),
         )
 
+
 class IDirectivesContext(IDirectivesInfo, IConfigurationContext):
     pass
 
+
+ at implementer(IDirectivesContext)
 class DirectivesHandler(GroupingContextDecorator):
     """Handler for the directives directive
 
@@ -1211,36 +633,35 @@
     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",
+    name = 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",
+        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",
+    handler = 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"
+    usedIn = GlobalInterface(
+        title = u("The directive types the directive can be used in"),
+        description = u("The interface of the directives that can contain "
+                        "the directive"
                        ),
         default = IConfigurationContext,
         )
@@ -1257,51 +678,7 @@
     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")
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('s', u'vx', u'vy'),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
-
-    >>> 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")
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('s', u'vx', u'vy'),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
-
     """
-
     namespace = namespace or context.namespace
     if namespace != '*':
         name = namespace, name
@@ -1320,45 +697,7 @@
     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
@@ -1377,14 +716,13 @@
 class IComplexDirectiveContext(IFullInfo, IConfigurationContext):
     pass
 
+
+ at implementer(IComplexDirectiveContext)
 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):
@@ -1407,33 +745,16 @@
 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
+    feature = 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")
@@ -1455,98 +776,7 @@
     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):
@@ -1557,20 +787,20 @@
 
         s = data.get(n, data)
         if s is not data:
-            s = unicode(s)
+            s = text_type(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]
+            except ValidationError as v:
+                reraise(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:
+            except ValidationError:
                 raise ConfigurationError("Missing parameter:", n)
             args[str(name)] = default
 
@@ -1685,25 +915,24 @@
     if conflicts:
         raise ConfigurationConflictError(conflicts)
 
-    # Sort conflict-resolved actions by (order, i) and return them.  Wanted
-    # to use operator.itemgetter(0, 1) instead of the lambda below but that
-    # doesn't work on Py24.
-    return [ x[2] for x in sorted( output, key=lambda ai: (ai[0], ai[1]) ) ]
+    # Sort conflict-resolved actions by (order, i) and return them.
+    return [x[2] for x in sorted(output, key=operator.itemgetter(0, 1))]
 
+
 class ConfigurationConflictError(ConfigurationError):
 
     def __init__(self, conflicts):
         self._conflicts = conflicts
 
-    def __str__(self):
+    def __str__(self): #pragma NO COVER
         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)
+                for line in text_type(info).rstrip().split(u('\n')):
+                    r.append(u("    ") + line)
 
         return "\n".join(r)
 

Modified: zope.configuration/trunk/src/zope/configuration/docutils.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/docutils.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/docutils.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -18,25 +18,10 @@
 import re
 
 para_sep = re.compile('\n{2,}')
-whitespace=re.compile('[ \t\n\r]+')
+whitespace = re.compile('[ \t\n\r]+')
 
 def wrap(text, width=78, indent=0):
     """Makes sure that we keep a line length of a certain width.
-
-    Examples:
-
-    >>> print wrap('foo bar')[:-2]
-    foo bar
-    >>> print wrap('foo bar', indent=2)[:-2]
-      foo bar
-    >>> print wrap('foo bar, more foo bar', 10)[:-2]
-    foo bar,
-    more foo
-    bar
-    >>> print wrap('foo bar, more foo bar', 10, 2)[:-2]
-      foo bar,
-      more foo
-      bar
     """
     paras = para_sep.split(text.strip())
 

Deleted: zope.configuration/trunk/src/zope/configuration/exclude.txt
===================================================================
--- zope.configuration/trunk/src/zope/configuration/exclude.txt	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/exclude.txt	2012-05-11 00:04:13 UTC (rev 125836)
@@ -1,56 +0,0 @@
-Filtering and Inhibiting Configuration
-======================================
-
-The ``exclude`` standard directive is provided for inhibiting unwanted
-configuration. It is used to exclude processing of configuration files.
-It is useful when including a configuration that includes some other
-configuration that you don't want.
-
-It must be used BEFORE including the files to be excluded.
-
-First, let's look at an example.  The zope.configuration.tests.excludedemo
-package has a ZCML configuration that includes some other configuration files.
-
-We'll set a log handler so we can see what's going on:
-
-    >>> import logging, sys
-    >>> logger = logging.getLogger('config')
-    >>> oldlevel = logger.level
-    >>> logger.setLevel(logging.DEBUG)
-    >>> handler = logging.StreamHandler(sys.stdout)
-    >>> logger.addHandler(handler)
- 
-Now, we'll include the zope.configuration.tests.excludedemo config:
-
-    >>> from zope.configuration import xmlconfig
-    >>> _ = xmlconfig.string('<include package="zope.configuration.tests.excludedemo" />')
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml
-
-Each run of the configuration machinery runs with fresh state, so
-rerunning gives the same thing:
-
-    >>> _ = xmlconfig.string('<include package="zope.configuration.tests.excludedemo" />')
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml
-
-Now, we'll use the exclude directive to exclude the two files included
-by the configuration file in zope.configuration.tests.excludedemo:
-
-    >>> _ = xmlconfig.string(
-    ... '''
-    ... <configure  xmlns="http://namespaces.zope.org/zope">
-    ...   <exclude package="zope.configuration.tests.excludedemo.sub" />
-    ...   <exclude package="zope.configuration.tests.excludedemo" file="spam.zcml" />
-    ...   <include package="zope.configuration.tests.excludedemo" />
-    ... </configure>
-    ... ''')
-    include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml
-    
-
-.. cleanup
-
-    >>> logger.setLevel(oldlevel)
-    >>> logger.removeHandler(handler)

Modified: zope.configuration/trunk/src/zope/configuration/fields.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/fields.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/fields.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -13,110 +13,45 @@
 ##############################################################################
 """Configuration-specific schema fields
 """
-__docformat__ = 'restructuredtext'
-import os, re, warnings
-from zope import schema
+import os
+import re
+import warnings
+
+from zope.interface import implementer
+from zope.schema import Bool as schema_Bool
+from zope.schema import Field
+from zope.schema import InterfaceField
+from zope.schema import List
+from zope.schema import Text
+from zope.schema import TextLine
+from zope.schema import ValidationError
 from zope.schema.interfaces import IFromUnicode
-from zope.schema.interfaces import ConstraintNotSatisfied
+
 from zope.configuration.exceptions import ConfigurationError
-from zope.interface import implements
 from zope.configuration.interfaces import InvalidToken
+from zope.configuration._compat import u
 
-PYIDENTIFIER_REGEX = u'\\A[a-zA-Z_]+[a-zA-Z0-9_]*\\Z'
+PYIDENTIFIER_REGEX = u('\\A[a-zA-Z_]+[a-zA-Z0-9_]*\\Z')
 pyidentifierPattern = re.compile(PYIDENTIFIER_REGEX)
 
-class PythonIdentifier(schema.TextLine):
-    r"""This field describes a python identifier, i.e. a variable name.
 
-    Let's look at an example:
-
-    >>> class FauxContext(object):
-    ...     pass
-    >>> context = FauxContext()
-    >>> field = PythonIdentifier().bind(context)
-
-    Let's test the fromUnicode method:
-
-    >>> field.fromUnicode(u'foo')
-    u'foo'
-    >>> field.fromUnicode(u'foo3')
-    u'foo3'
-    >>> field.fromUnicode(u'_foo3')
-    u'_foo3'
-
-    Now let's see whether validation works alright
-
-    >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'):
-    ...     field._validate(value)
-    >>>
-    >>> from zope import schema
-    >>>
-    >>> for value in (u'3foo', u'foo:', u'\\', u''):
-    ...     try:
-    ...         field._validate(value)
-    ...     except schema.ValidationError:
-    ...         print 'Validation Error'
-    Validation Error
-    Validation Error
-    Validation Error
-    Validation Error
-
+ at implementer(IFromUnicode)
+class PythonIdentifier(TextLine):
+    """This field describes a python identifier, i.e. a variable name.
     """
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         return u.strip()
 
     def _validate(self, value):
         super(PythonIdentifier, self)._validate(value)
         if pyidentifierPattern.match(value) is None:
-            raise schema.ValidationError(value)
+            raise ValidationError(value)
 
-class GlobalObject(schema.Field):
-    """An object that can be accessed as a module global.
 
-    Examples:
-
-    First, we need to set up a stub name resolver:
-
-    >>> d = {'x': 1, 'y': 42, 'z': 'zope'}
-    >>> class fakeresolver(dict):
-    ...     def resolve(self, n):
-    ...         return self[n]
-
-    >>> fake = fakeresolver(d)
-
-
-    >>> g = GlobalObject(value_type=schema.Int())
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode("x")
-    1
-    >>> gg.fromUnicode("   x  \\n  ")
-    1
-    >>> gg.fromUnicode("y")
-    42
-    >>> gg.fromUnicode("z")
-    Traceback (most recent call last):
-    ...
-    WrongType: ('zope', (<type 'int'>, <type 'long'>), '')
-
-    >>> g = GlobalObject(constraint=lambda x: x%2 == 0)
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode("x")
-    Traceback (most recent call last):
-    ...
-    ConstraintNotSatisfied: 1
-    >>> gg.fromUnicode("y")
-    42
-    >>> g = GlobalObject()
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode('*')
-    >>>
-
+ at implementer(IFromUnicode)
+class GlobalObject(Field):
+    """An object that can be accessed as a module global.
     """
-
-    implements(IFromUnicode)
-
     def __init__(self, value_type=None, **kw):
         self.value_type = value_type
         super(GlobalObject, self).__init__(**kw)
@@ -135,88 +70,25 @@
 
         try:
             value = self.context.resolve(name)
-        except ConfigurationError, v:
-            raise schema.ValidationError(v)
+        except ConfigurationError as v:
+            raise ValidationError(v)
 
         self.validate(value)
         return value
 
+
+ at implementer(IFromUnicode)
 class GlobalInterface(GlobalObject):
     """An interface that can be accessed from a module.
-
-    First, we need to set up a stub name resolver:
-
-    >>> class Foo(object): pass
-
-    >>> from zope.interface import Interface
-    >>> class IFoo(Interface): pass
-
-    >>> d = {'Foo': Foo, 'IFoo': IFoo}
-    >>> class fakeresolver(dict):
-    ...     def resolve(self, n):
-    ...         return self[n]
-
-    >>> fake = fakeresolver(d)
-
-    Now verify constraints are checked correctly.
-
-    >>> g = GlobalInterface()
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode('IFoo')
-    <InterfaceClass zope.configuration.fields.IFoo>
-    >>> gg.fromUnicode('  IFoo  ')
-    <InterfaceClass zope.configuration.fields.IFoo>
-    >>> gg.fromUnicode('Foo')
-    Traceback (most recent call last):
-    ...
-    WrongType: ('An interface is required', <class 'zope.configuration.fields.Foo'>, '')
     """
-
     def __init__(self, **kw):
-        super(GlobalInterface, self).__init__(schema.InterfaceField(), **kw)
+        super(GlobalInterface, self).__init__(InterfaceField(), **kw)
 
-class Tokens(schema.List):
-    """A list that can be read from a space-separated string
 
-    Consider GlobalObject tokens:
-
-    Examples:
-
-    First, we need to set up a stub name resolver:
-
-    >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'}
-    >>> class fakeresolver(dict):
-    ...     def resolve(self, n):
-    ...         return self[n]
-
-    >>> fake = fakeresolver(d)
-
-
-    >>> g = Tokens(value_type=GlobalObject())
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode("  \\n  x y z  \\n")
-    [1, 42, 'zope']
-
-    >>> g = Tokens(value_type=
-    ...            GlobalObject(value_type=
-    ...                         schema.Int(constraint=lambda x: x%2 == 0)))
-    >>> gg = g.bind(fake)
-    >>> gg.fromUnicode("x y")
-    Traceback (most recent call last):
-    ...
-    InvalidToken: 1 in x y
-
-    >>> gg.fromUnicode("z y")
-    Traceback (most recent call last):
-    ...
-    InvalidToken: ('zope', (<type 'int'>, <type 'long'>), '') in z y
-    >>> gg.fromUnicode("y y")
-    [42, 42]
-    >>>
-
+ at implementer(IFromUnicode)
+class Tokens(List):
+    """A list that can be read from a space-separated string.
     """
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         u = u.strip()
         if u:
@@ -225,7 +97,7 @@
             for s in u.split():
                 try:
                     v = vt.fromUnicode(s)
-                except schema.ValidationError, v:
+                except ValidationError as v:
                     raise InvalidToken("%s in %s" % (v, u))
                 else:
                     values.append(v)
@@ -236,51 +108,13 @@
 
         return values
 
-class Path(schema.Text):
-    r"""A file path name, which may be input as a relative path
 
-    Input paths are converted to absolute paths and normalized.
+ at implementer(IFromUnicode)
+class Path(Text):
+    """A file path name, which may be input as a relative path
 
-    Let's look at an example:
-
-    First, we need a "context" for the field that has a path
-    function for converting relative path to an absolute path.
-
-    We'll be careful to do this in an os-independent fashion.
-
-    >>> class FauxContext(object):
-    ...    def path(self, p):
-    ...       return os.path.join(os.sep, 'faux', 'context', p)
-
-    >>> context = FauxContext()
-    >>> field = Path().bind(context)
-
-    Lets try an absolute path first:
-
-    >>> p = unicode(os.path.join(os.sep, 'a', 'b'))
-    >>> n = field.fromUnicode(p)
-    >>> n.split(os.sep)
-    [u'', u'a', u'b']
-
-    This should also work with extra spaces around the path:
-
-    >>> p = "   \n   %s   \n\n   " % p
-    >>> n = field.fromUnicode(p)
-    >>> n.split(os.sep)
-    [u'', u'a', u'b']
-
-    Now try a relative path:
-
-    >>> p = unicode(os.path.join('a', 'b'))
-    >>> n = field.fromUnicode(p)
-    >>> n.split(os.sep)
-    [u'', u'faux', u'context', u'a', u'b']
-
-
+    Input paths are converted to absolute paths and normalized.
     """
-
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         u = u.strip()
         if os.path.isabs(u):
@@ -289,122 +123,30 @@
         return self.context.path(u)
 
 
-class Bool(schema.Bool):
+ at implementer(IFromUnicode)
+class Bool(schema_Bool):
     """A boolean value
 
     Values may be input (in upper or lower case) as any of:
        yes, no, y, n, true, false, t, or f.
-
-    >>> Bool().fromUnicode(u"yes")
-    1
-    >>> Bool().fromUnicode(u"y")
-    1
-    >>> Bool().fromUnicode(u"true")
-    1
-    >>> Bool().fromUnicode(u"no")
-    0
     """
-
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         u = u.lower()
         if u in ('1', 'true', 'yes', 't', 'y'):
             return True
         if u in ('0', 'false', 'no', 'f', 'n'):
             return False
-        raise schema.ValidationError
+        raise ValidationError
 
-class MessageID(schema.Text):
+
+ at implementer(IFromUnicode)
+class MessageID(Text):
     """Text string that should be translated.
 
     When a string is converted to a message ID, it is also
     recorded in the context.
-
-    >>> class Info(object):
-    ...     file = 'file location'
-    ...     line = 8
-
-    >>> class FauxContext(object):
-    ...     i18n_strings = {}
-    ...     info = Info()
-
-    >>> context = FauxContext()
-    >>> field = MessageID().bind(context)
-
-    There is a fallback domain when no domain has been specified.
-
-    Exchange the warn function so we can make test whether the warning
-    has been issued
-
-    >>> warned = None
-    >>> def fakewarn(*args, **kw):
-    ...     global warned
-    ...     warned = args
-
-    >>> import warnings
-    >>> realwarn = warnings.warn
-    >>> warnings.warn = fakewarn
-
-    >>> i = field.fromUnicode(u"Hello world!")
-    >>> i
-    u'Hello world!'
-    >>> i.domain
-    'untranslated'
-    >>> warned
-    ("You did not specify an i18n translation domain for the '' """ \
-        """field in file location",)
-
-    >>> warnings.warn = realwarn
-
-    With the domain specified:
-
-    >>> context.i18n_strings = {}
-    >>> context.i18n_domain = 'testing'
-
-    We can get a message id:
-
-    >>> i = field.fromUnicode(u"Hello world!")
-    >>> i
-    u'Hello world!'
-    >>> i.domain
-    'testing'
-
-    In addition, the string has been registered with the context:
-
-    >>> context.i18n_strings
-    {'testing': {u'Hello world!': [('file location', 8)]}}
-
-    >>> i = field.fromUnicode(u"Foo Bar")
-    >>> i = field.fromUnicode(u"Hello world!")
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=70).pprint
-    >>> pprint(context.i18n_strings)
-    {'testing': {u'Foo Bar': [('file location', 8)],
-                 u'Hello world!': [('file location', 8),
-                                   ('file location', 8)]}}
-
-    >>> from zope.i18nmessageid import Message
-    >>> isinstance(context.i18n_strings['testing'].keys()[0], Message)
-    1
-
-    Explicit Message IDs
-
-    >>> i = field.fromUnicode(u'[View-Permission] View')
-    >>> i
-    u'View-Permission'
-    >>> i.default
-    u'View'
-
-    >>> i = field.fromUnicode(u'[] [Some] text')
-    >>> i
-    u'[Some] text'
-    >>> i.default is None
-    True
     """
 
-    implements(IFromUnicode)
-
     __factories = {}
 
     def fromUnicode(self, u):

Modified: zope.configuration/trunk/src/zope/configuration/interfaces.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/interfaces.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/interfaces.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -16,6 +16,7 @@
 from zope.interface import Interface
 from zope.schema import BytesLine
 from zope.schema.interfaces import ValidationError
+from zope.configuration._compat import u
 
 class InvalidToken(ValidationError):
     """Invaid token in list."""
@@ -30,12 +31,12 @@
     """
 
     package = BytesLine(
-        title=u"The current package name",
-        description=u"""\
+        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,
         )
 
@@ -81,7 +82,7 @@
         it needs to be procssed.
         """
 
-    def action(self, discriminator, callable, args=(), kw={}, order=0,
+    def action(discriminator, callable, args=(), kw={}, order=0,
                includepath=None, info=None):
         """Record a configuration action
 

Modified: zope.configuration/trunk/src/zope/configuration/name.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/name.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/name.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -21,25 +21,25 @@
     name = name.strip()
 
     if name.startswith('.'):
-        name=package+name
+        name = package + name
 
     if name.endswith('.') or name.endswith('+'):
         name = name[:-1]
-        repeat = 1
+        repeat = True
     else:
-        repeat = 0
+        repeat = False
 
-    names=name.split('.')
-    last=names[-1]
-    mod='.'.join(names[:-1])
+    names = name.split('.')
+    last = names[-1]
+    mod = '.'.join(names[:-1])
 
     if not mod:
         return __import__(name, _globals, _globals, _silly)
 
     while 1:
-        m=__import__(mod, _globals, _globals, _silly)
+        m = __import__(mod, _globals, _globals, _silly)
         try:
-            a=getattr(m, last)
+            a = getattr(m, last)
         except AttributeError:
             if not repeat:
                 return __import__(name, _globals, _globals, _silly)
@@ -51,26 +51,28 @@
 
 
 def getNormalizedName(name, package):
-    name=name.strip()
+    name = name.strip()
     if name.startswith('.'):
-        name=package+name
+        name = package + name
 
     if name.endswith('.') or name.endswith('+'):
         name = name[:-1]
-        repeat = 1
+        repeat = True
     else:
-        repeat = 0
-    name=name.split(".")
-    while len(name)>1 and name[-1]==name[-2]:
+        repeat = False
+    name = name.split(".")
+    while len(name) > 1 and name[-1] == name[-2]:
         name.pop()
-        repeat=1
-    name=".".join(name)
+        repeat = 1
+    name = ".".join(name)
     if repeat:
-        name+="+"
+        name += "+"
     return name
 
-def path(file='', package = 'zopeproducts', _silly=('__doc__',), _globals={}):
-    try: package = __import__(package, _globals, _globals, _silly)
+def path(file='', package='zopeproducts', _silly=('__doc__',), _globals={}):
+    # XXX WTF? why not look for abspath before importing?
+    try:
+        package = __import__(package, _globals, _globals, _silly)
     except ImportError:
         if file and os.path.abspath(file) == file:
             # The package didn't matter
@@ -78,6 +80,8 @@
         raise
 
     path = os.path.dirname(package.__file__)
+
     if file:
         path = os.path.join(path, file)
+
     return path

Modified: zope.configuration/trunk/src/zope/configuration/stxdocs.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/stxdocs.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/stxdocs.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -27,20 +27,21 @@
         documentation is stored. Note that this tool will create
         sub-directories with files in them. 
 """
+# XXX This should be ripped out of the package.
 import sys, os, getopt
 import zope.configuration
 from zope.schema import getFieldsInOrder 
 from zope.configuration import config, xmlconfig
 from zope.configuration.docutils import wrap, makeDocStructures
 
-def usage(code, msg=''):
+def usage(code, msg=''): #pragma NO COVER
     # Python 2.1 required
     print >> sys.stderr, __doc__
     if msg:
         print >> sys.stderr, msg
     sys.exit(code)
 
-def _directiveDocs(name, schema, handler, info, indent_offset=0):
+def _directiveDocs(name, schema, handler, info, indent_offset=0): #pragma NO COVER
     """Generate the documentation for one directive."""
 
     # Write out the name of the directive
@@ -86,7 +87,7 @@
 
     return text
 
-def _subDirectiveDocs(subdirs, namespace, name):
+def _subDirectiveDocs(subdirs, namespace, name): #pragma NO COVER
     """Appends a list of sub-directives and their full specification."""
     if subdirs.has_key((namespace, name)):
         text = '\n  Subdirectives\n\n'
@@ -100,7 +101,7 @@
         return text + '\n\n'.join(sub_dirs)
     return ''
 
-def makedocs(target_dir, zcml_file):
+def makedocs(target_dir, zcml_file): #pragma NO COVER
     """Generate the documentation tree.
 
     All we need for this is a starting ZCML file and a directory in which to
@@ -122,7 +123,7 @@
             text += _subDirectiveDocs(subdirs, namespace, name)
             open(dir_file, 'w').write(text)
 
-def _makeabs(path):
+def _makeabs(path): #pragma NO COVER
     """Make an absolute path from the possibly relative path."""
     if not path == os.path.abspath(path):
         cwd = os.getcwd()
@@ -132,7 +133,7 @@
         path = os.path.normpath(os.path.join(cwd, path))    
     return path
 
-def main(argv=sys.argv):
+def main(argv=sys.argv): #pragma NO COVER
     try:
         opts, args = getopt.getopt(
             sys.argv[1:],
@@ -159,5 +160,5 @@
     # Generate the docs
     makedocs(output_dir, zcml_file)
 
-if __name__ == '__main__':
+if __name__ == '__main__': #pragma NO COVER
     main()

Copied: zope.configuration/trunk/src/zope/configuration/tests/conditions.py (from rev 125658, zope.configuration/trunk/src/zope/configuration/tests/test_conditions.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/conditions.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/conditions.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+""" Enable "Making specific directives condition" section of narrative docs.
+"""
+from zope.interface import Interface
+from zope.schema import Id
+
+
+class IRegister(Interface):
+    """Trivial sample registry."""
+
+    id = Id(
+        title=u"Identifier",
+        description=u"Some identifier that can be checked.",
+        required=True,
+        )
+
+registry = []
+
+def register(context, id):
+    context.action(discriminator=('Register', id),
+                   callable=registry.append,
+                   args=(id,)
+                   )

Modified: zope.configuration/trunk/src/zope/configuration/tests/conditions.zcml
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/conditions.zcml	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/conditions.zcml	2012-05-11 00:04:13 UTC (rev 125836)
@@ -8,8 +8,8 @@
   <meta:directive
       name="register"
       namespace="http://sample.namespaces.zope.org/test"
-      schema=".test_conditions.IRegister"
-      handler=".test_conditions.register"
+      schema=".conditions.IRegister"
+      handler=".conditions.register"
       >
     This registers a directive which creates registrations we can test.
   </meta:directive>

Modified: zope.configuration/trunk/src/zope/configuration/tests/directives.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/directives.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/directives.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -13,11 +13,15 @@
 ##############################################################################
 """Test directives
 """
-from zope.interface import Interface, implements
-from zope.schema import Text, BytesLine
+from zope.interface import Interface
+from zope.interface import implementer
+from zope.schema import BytesLine
+from zope.schema import Text
+
 from zope.configuration.config import GroupingContextDecorator
 from zope.configuration.interfaces import IConfigurationContext
 from zope.configuration.fields import GlobalObject
+from zope.configuration._compat import u
 
 
 class F(object):
@@ -34,10 +38,10 @@
     b = Text(required=False)
     c = BytesLine()
 
-def simple(context, a=None, c=None, b=u"xxx"):
+def simple(context, a=None, c=None, b=u("xxx")):
     return [(('simple', a, b, c), f, (a, b, c))]
 
-def newsimple(context, a, c, b=u"xxx"):
+def newsimple(context, a, c, b=u("xxx")):
     context.action(('newsimple', a, b, c), f, (a, b, c))
 
 
@@ -45,14 +49,16 @@
 
     package = GlobalObject()
 
+
 class IPackagedContext(IPackaged, IConfigurationContext):
     pass
 
+
+ at implementer(IPackagedContext)
 class Packaged(GroupingContextDecorator):
+    pass
 
-    implements(IPackagedContext)
 
-
 class IFactory(Interface):
 
     factory = GlobalObject()
@@ -62,7 +68,7 @@
 
 class Complex(object):
 
-    def __init__(self, context, a, c, b=u"xxx"):
+    def __init__(self, context, a, c, b=u("xxx")):
         self.a, self.b, self.c = a, b, c
         context.action("Complex.__init__")
 

Copied: zope.configuration/trunk/src/zope/configuration/tests/nested.py (from rev 125658, zope.configuration/trunk/src/zope/configuration/tests/test_nested.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/nested.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/nested.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,149 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+""" Utilities for the 'nested directive' section in the narrative docs.
+"""
+
+from zope.interface import Attribute
+from zope.interface import Interface
+from zope.interface import implementer
+from zope.schema import BytesLine
+from zope.schema import Id
+from zope.schema import Int
+from zope.schema import Text
+from zope.schema import TextLine
+from zope.configuration.config import GroupingContextDecorator
+from zope.configuration.config import IConfigurationContext
+from zope.configuration.fields import Bool
+from zope.configuration._compat import u
+
+
+schema_registry = {}
+
+class ISchemaInfo(Interface):
+    """Parameter schema for the schema directive
+    """
+
+    name = TextLine(
+        title=u("The schema name"),
+        description=u("This is a descriptive name for the schema."),
+        )
+
+    id = Id(title=u("The unique id for the schema"))
+
+class ISchema(Interface):
+    """Interface that distinguishes the schema directive
+    """
+
+    fields = Attribute("Dictionary of field definitions")
+    
+
+ at implementer(IConfigurationContext, ISchema)
+class Schema(GroupingContextDecorator):
+    """Handle schema directives
+    """
+
+
+    def __init__(self, context, name, id):
+        self.context, self.name, self.id = context, name, id
+        self.fields = {}
+
+    def after(self):
+        schema = Interface.__class__(
+            self.name,
+            (Interface, ),
+            self.fields
+            )
+        schema.__doc__ = self.info.text.strip()
+        self.action(
+            discriminator=('schema', self.id),
+            callable=schema_registry.__setitem__,
+            args=(self.id, schema),
+            )
+        
+
+class IFieldInfo(Interface):
+
+    name = BytesLine(
+        title=u("The field name"),
+        )
+
+    title = TextLine(
+        title=u("Title"),
+        description=u("A short summary or label"),
+        default=u(""),
+        required=False,
+        )
+
+    required = Bool(
+        title=u("Required"),
+        description=u("Determines whether a value is required."),
+        default=True)
+
+    readonly = Bool(
+        title=u("Read Only"),
+        description=u("Can the value be modified?"),
+        required=False,
+        default=False)
+
+class ITextInfo(IFieldInfo):
+
+    min_length = Int(
+        title=u("Minimum length"),
+        description=u("Value after whitespace processing cannot have less than "
+                      "min_length characters. If min_length is None, there is "
+                      "no minimum."),
+        required=False,
+        min=0, # needs to be a positive number
+        default=0)
+
+    max_length = Int(
+        title=u("Maximum length"),
+        description=u("Value after whitespace processing cannot have greater "
+                      "or equal than max_length characters. If max_length is "
+                      "None, there is no maximum."),
+        required=False,
+        min=0, # needs to be a positive number
+        default=None)
+
+def field(context, constructor, name, **kw):
+
+    # Compute the field
+    field = constructor(description=context.info.text.strip(), **kw)
+
+    # Save it in the schema's field dictionary
+    schema = context.context
+    if name in schema.fields:
+        raise ValueError("Duplicate field", name)
+    schema.fields[name] = field
+
+    
+def textField(context, **kw):
+    field(context, Text, **kw)
+
+class IIntInfo(IFieldInfo):
+
+    min = Int(
+        title=u("Start of the range"),
+        required=False,
+        default=None
+        )
+
+    max = Int(
+        title=u("End of the range (excluding the value itself)"),
+        required=False,
+        default=None
+        )
+    
+def intField(context, **kw):
+    field(context, Int, **kw)

Copied: zope.configuration/trunk/src/zope/configuration/tests/notyet.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/notyet.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/notyet.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/notyet.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1 @@
+# We don't get imported automagically

Copied: zope.configuration/trunk/src/zope/configuration/tests/samplepackage/NamedForClass.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/samplepackage/NamedForClass.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/samplepackage/NamedForClass.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/samplepackage/NamedForClass.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,3 @@
+# Test the "repeat" feature of zope.configuration.name.resolve.
+class NamedForClass(object):
+    pass

Modified: zope.configuration/trunk/src/zope/configuration/tests/samplepackage/foo.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/samplepackage/foo.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/samplepackage/foo.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -28,8 +28,7 @@
          ) = args, info, basepath, package, includepath
 
 def handler(_context, **kw):
-    args = kw.items()
-    args.sort()
+    args = sorted(kw.items())
     args = tuple(args)
     discriminator = args
     args = (stuff(args, _context.info, _context.basepath, _context.package,

Modified: zope.configuration/trunk/src/zope/configuration/tests/schema.zcml
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/schema.zcml	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/schema.zcml	2012-05-11 00:04:13 UTC (rev 125836)
@@ -6,8 +6,8 @@
   <meta:groupingDirective
       name="schema"
       namespace="http://sample.namespaces.zope.org/schema"
-      schema=".test_nested.ISchemaInfo"
-      handler=".test_nested.Schema"
+      schema=".nested.ISchemaInfo"
+      handler=".nested.Schema"
       >
 
       Define a schema
@@ -21,9 +21,9 @@
   <meta:directive
       name="text"
       namespace="http://sample.namespaces.zope.org/schema"
-      usedIn=".test_nested.ISchema"
-      schema=".test_nested.ITextInfo"
-      handler=".test_nested.textField"
+      usedIn=".nested.ISchema"
+      schema=".nested.ITextInfo"
+      handler=".nested.textField"
       >
       
       Define a text field
@@ -32,15 +32,15 @@
   <meta:directive
       name="int"
       namespace="http://sample.namespaces.zope.org/schema"
-      usedIn=".test_nested.ISchema"
-      schema=".test_nested.IIntInfo"
-      handler=".test_nested.intField"
+      usedIn=".nested.ISchema"
+      schema=".nested.IIntInfo"
+      handler=".nested.intField"
       >
       
       Define an integer field
   </meta:directive>
 
-  <schema:schema name="I1" id="zope.configuration.tests.test_nested.I1">
+  <schema:schema name="I1" id="zope.configuration.tests.nested.I1">
       Sample interface I1
   
       <schema:text name="a" min_length="1">
@@ -56,7 +56,7 @@
       </schema:int>
   </schema:schema>
 
-  <schema:schema name="I2" id="zope.configuration.tests.test_nested.I2">
+  <schema:schema name="I2" id="zope.configuration.tests.nested.I2">
       Sample interface I2
   
       <schema:text name="x">X</schema:text>

Copied: zope.configuration/trunk/src/zope/configuration/tests/simple.py (from rev 125658, zope.configuration/trunk/src/zope/configuration/tests/test_simple.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/simple.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/simple.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+""" Utilities for the 'simple directive' section in the narrative docs.
+"""
+
+from zope.interface import Interface
+from zope.schema import Text
+
+from zope.configuration.fields import Path
+from zope.configuration._compat import u
+
+class IRegisterFile(Interface):
+
+    path = Path(
+        title=u("File path"),
+        description=u("This is the path name of the file to be registered."),
+        )
+
+    title = Text(
+        title=u("Short summary of the file"),
+        description=u("This will be used in file listings"),
+        required = False
+        )
+
+class FileInfo(object):
+
+    def __init__(self, path, title, description, info):
+        (self.path, self.title, self.description, self.info
+         ) = path, title, description, info
+
+file_registry = []
+
+def registerFile(context, path, title=u("")):
+    info = context.info
+    description = info.text.strip()
+    context.action(discriminator=('RegisterFile', path),
+                   callable=file_registry.append,
+                   args=(FileInfo(path, title, description, info),)
+                   )

Modified: zope.configuration/trunk/src/zope/configuration/tests/simple.zcml
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/simple.zcml	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/simple.zcml	2012-05-11 00:04:13 UTC (rev 125836)
@@ -6,8 +6,8 @@
   <meta:directive
       name="register"
       namespace="http://sample.namespaces.zope.org/files"
-      schema=".test_simple.IRegisterFile"
-      handler=".test_simple.registerFile"
+      schema=".simple.IRegisterFile"
+      handler=".simple.registerFile"
       >
 
       Register a file with the file registry
@@ -17,7 +17,7 @@
   </meta:directive>
 
   <files:register 
-      path="test_simple.py"
+      path="simple.py"
       title="How to create a simple directive"
       >
     Describes how to implement a simple directive

Copied: zope.configuration/trunk/src/zope/configuration/tests/test___init__.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test___init__.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test___init__.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/test___init__.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,36 @@
+##############################################################################
+#
+# Copyright (c) 20!2 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 zope.configuration.__init__
+"""
+import unittest
+
+
+class Test_namespace(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration import namespace
+        return namespace(*args, **kw)
+
+    def test_empty(self):
+        self.assertEqual(self._callFUT(''),
+                         'http://namespaces.zope.org/')
+
+    def test_non_empty(self):
+        self.assertEqual(self._callFUT('test'),
+                         'http://namespaces.zope.org/test')
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(Test_namespace),
+    ))

Deleted: zope.configuration/trunk/src/zope/configuration/tests/test_conditions.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_conditions.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_conditions.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -1,109 +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.
-#
-##############################################################################
-r'''How to conditionalize specific directives
-
-There is a "condition" attribute in the
-"http://namespaces.zope.org/zcml" namespace which is honored on all
-elements in ZCML.  The value of the attribute is an expression
-which is used to determine if that element and its descendents are
-used.  If the condition is true, processing continues normally,
-otherwise that element and its descendents are ignored.
-
-Currently the expression is always of the form "have featurename", and it
-checks for the presence of a <meta:provides feature="featurename" />.
-
-Our demonstration uses a trivial registry; each registration consists
-of a simple id inserted in the global `registry` in this module.  We
-can checked that a registration was made by checking whether the id is
-present in `registry`.
-
-We start by loading the example ZCML file, *conditions.zcml*::
-
-  >>> import zope.configuration.tests
-  >>> import zope.configuration.xmlconfig
-
-  >>> context = zope.configuration.xmlconfig.file("conditions.zcml",
-  ...                                             zope.configuration.tests)
-
-To show that our sample directive works, we see that the unqualified
-registration was successful::
-
-  >>> "unqualified.registration" in registry
-  True
-
-When the expression specified with ``zcml:condition`` evaluates to
-true, the element it is attached to and all contained elements (not
-otherwise conditioned) should be processed normally::
-
-  >>> "direct.true.condition" in registry
-  True
-  >>> "nested.true.condition" in registry
-  True
-
-However, when the expression evaluates to false, the conditioned
-element and all contained elements should be ignored::
-
-  >>> "direct.false.condition" in registry
-  False
-  >>> "nested.false.condition" in registry
-  False
-
-Conditions on container elements affect the conditions in nested
-elements in a reasonable way.  If an "outer" condition is true, nested
-conditions are processed normally::
-
-  >>> "true.condition.nested.in.true" in registry
-  True
-  >>> "false.condition.nested.in.true" in registry
-  False
-
-If the outer condition is false, inner conditions are not even
-evaluated, and the nested elements are ignored::
-
-  >>> "true.condition.nested.in.false" in registry
-  False
-  >>> "false.condition.nested.in.false" in registry
-  False
-
-Now we need to clean up after ourselves::
-
-  >>> del registry[:]
-
-'''
-__docformat__ = "reStructuredText"
-
-import doctest
-import zope.interface
-import zope.schema
-
-
-class IRegister(zope.interface.Interface):
-    """Trivial sample registry."""
-
-    id = zope.schema.Id(
-        title=u"Identifier",
-        description=u"Some identifier that can be checked.",
-        required=True,
-        )
-
-registry = []
-
-def register(context, id):
-    context.action(discriminator=('Register', id),
-                   callable=registry.append,
-                   args=(id,)
-                   )
-
-def test_suite():
-    return doctest.DocTestSuite()

Modified: zope.configuration/trunk/src/zope/configuration/tests/test_config.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_config.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_config.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -13,418 +13,1868 @@
 ##############################################################################
 """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
+class _Catchable(object):
+    # Mixin for classes which need to make assertions about the exception
+    # instance.
+    def assertRaises(self, excClass, callableObj, *args, **kwargs):
+        # Morph stdlib version to return the raised exception
+        try:
+            callableObj(*args, **kwargs)
+        except excClass as exc:
+            return exc
+        if hasattr(excClass,'__name__'):
+            excName = excClass.__name__
+        else:
+            excName = str(excClass)
+        raise self.failureException("%s not raised" % excName)
 
-    Examples:
 
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
+class ConfigurationContextTests(_Catchable,
+                                unittest.TestCase,
+                               ):
 
-    Register some test directives:
+    def _getTargetClass(self):
+        from zope.configuration.config import ConfigurationContext
+        return ConfigurationContext
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    Start with a grouping directive that sets a package:
+    def test_resolve_blank(self):
+        c = self._makeOne()
+        self.assertRaises(ValueError, c.resolve, '')
+        self.assertRaises(ValueError, c.resolve, '   ')
 
-    >>> machine((metans, "groupingDirective"),
-    ...         name="package", namespace=ns,
-    ...         schema="zope.configuration.tests.directives.IPackaged",
-    ...         handler="zope.configuration.tests.directives.Packaged",
-    ...         )
+    def test_resolve_dot(self):
+        c = self._makeOne()
+        package = c.package = object()
+        self.assertTrue(c.resolve('.') is package)
 
-    Now we can set the package:
+    def test_resolve_trailing_dot_in_resolve(self):
+        #Dotted names are no longer allowed to end in dots
+        c = self._makeOne()
+        self.assertRaises(ValueError, c.resolve, 'zope.')
 
-    >>> machine.begin((ns, "package"),
-    ...               package="zope.configuration.tests.directives",
-    ...               )
+    def test_resolve_builtin(self):
+        c = self._makeOne()
+        self.assertTrue(c.resolve('str') is str)
 
-    Which makes it easier to define the other directives:
+    def test_resolve_single_non_builtin(self):
+        import os
+        c = self._makeOne()
+        self.assertTrue(c.resolve('os') is os)
 
-    First, define some simple directives:
+    def test_resolve_relative_miss_no_package(self):
+        from zope.configuration.exceptions import ConfigurationError
+        c = self._makeOne()
+        c.package = None
+        self.assertRaises(ConfigurationError, c.resolve, '.nonesuch')
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="simple",
-    ...         schema=".ISimple", handler=".simple")
+    def test_resolve_relative_miss_w_package_too_many_dots(self):
+        class FauxPackage(object):
+            pass
+        from zope.configuration.exceptions import ConfigurationError
+        c = self._makeOne()
+        package = c.package = FauxPackage()
+        package.__name__ = 'one.dot'
+        self.assertRaises(ConfigurationError, c.resolve, '....nonesuch')
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="newsimple",
-    ...         schema=".ISimple", handler=".newsimple")
+    def test_resolve_bad_dotted_last_import(self):
+        # Import error caused by a bad last component in the dotted name.
+        from zope.configuration.exceptions import ConfigurationError
+        c = self._makeOne()
+        exc = self.assertRaises(ConfigurationError,
+                          c.resolve, 'zope.configuration.tests.nosuch')
+        self.assertTrue('ImportError' in str(exc))
 
+    def test_resolve_bad_dotted_import(self):
+        # Import error caused by a totally wrong dotted name.
+        from zope.configuration.exceptions import ConfigurationError
+        c = self._makeOne()
+        exc = self.assertRaises(ConfigurationError, 
+                          c.resolve, 'zope.configuration.nosuch.noreally')
+        self.assertTrue('ImportError' in str(exc))
 
-    and try them out:
+    def test_resolve_bad_sub_last_import(self):
+        #Import error caused by a bad sub import inside the referenced
+        #dotted name. Here we keep the standard traceback.
+        import sys
+        c = self._makeOne()
+        self.assertRaises(ImportError,
+                          c.resolve, 'zope.configuration.tests.victim')
+        #Cleanup:
+        for name in ('zope.configuration.tests.victim',
+                     'zope.configuration.tests.bad'):
+           if name in sys.modules:
+               del sys.modules[name]
 
-    >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc")
-    >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb")
+    def test_resolve_bad_sub_import(self):
+        #Import error caused by a bad sub import inside part of the referenced
+        #dotted name. Here we keep the standard traceback.
+        import sys
+        c = self._makeOne()
+        self.assertRaises(ImportError, 
+                          c.resolve, 'zope.configuration.tests.victim.nosuch')
+        #Cleanup:
+        for name in ('zope.configuration.tests.victim',
+                     'zope.configuration.tests.bad'):
+           if name in sys.modules:
+               del sys.modules[name]
 
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
+    def test_path_w_absolute_filename(self):
+        c = self._makeOne()
+        self.assertEqual(c.path('/path/to/somewhere'), '/path/to/somewhere')
 
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0}]
+    def test_path_w_relative_filename_w_basepath(self):
+        c = self._makeOne()
+        c.basepath = '/path/to'
+        self.assertEqual(c.path('somewhere'), '/path/to/somewhere')
 
-    Define and try a simple directive that uses a component:
+    def test_path_w_relative_filename_wo_basepath_wo_package(self):
+        import os
+        c = self._makeOne()
+        c.package = None
+        self.assertEqual(c.path('somewhere'),
+                         os.path.join(os.getcwd(), 'somewhere'))
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="factory",
-    ...         schema=".IFactory", handler=".factory")
+    def test_path_wo_basepath_w_package_having_file(self):
+        #Path must always return an absolute path.
+        import os
+        class stub:
+            __file__ = os.path.join('relative', 'path')
+        c = self._makeOne()
+        c.package = stub()
+        self.assertTrue(os.path.isabs(c.path('y/z')))
 
+    def test_path_wo_basepath_w_package_having_no_file_but_path(self):
+        #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 = self._makeOne()
+        c.package = stub()
+        os.path.isabs(c.path('y/z'))
 
-    >>> machine((ns, "factory"), factory=u".f")
-    >>> pprint(machine.actions[-1:])
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
+    def test_checkDuplicate_miss(self):
+        c = self._makeOne()
+        c.checkDuplicate('/path') # doesn't raise
+        self.assertEqual(list(c._seen_files), ['/path'])
 
-    Define and try a complex directive:
+    def test_checkDuplicate_hit(self):
+        from zope.configuration.exceptions import ConfigurationError
+        c = self._makeOne()
+        c.checkDuplicate('/path')
+        self.assertRaises(ConfigurationError, c.checkDuplicate, '/path')
+        self.assertEqual(list(c._seen_files), ['/path'])
 
-    >>> machine.begin((metans, "complexDirective"),
-    ...               namespace=ns, name="testc",
-    ...               schema=".ISimple", handler=".Complex")
+    def test_processFile_miss(self):
+        c = self._makeOne()
+        self.assertEqual(c.processFile('/path'), True)
+        self.assertEqual(list(c._seen_files), ['/path'])
 
-    >>> machine((metans, "subdirective"),
-    ...         name="factory", schema=".IFactory")
+    def test_processFile_hit(self):
+        c = self._makeOne()
+        c.processFile('/path')
+        self.assertEqual(c.processFile('/path'), False)
+        self.assertEqual(list(c._seen_files), ['/path'])
 
-    >>> machine.end()
+    def test_action_defaults_no_info_no_includepath(self):
+        DISCRIMINATOR = ('a', ('b',), 0)
+        c = self._makeOne()
+        c.actions = [] # normally provided by subclass
+        c.action(DISCRIMINATOR)
+        self.assertEqual(len(c.actions), 1)
+        info = c.actions[0]
+        self.assertEqual(info['discriminator'], DISCRIMINATOR)
+        self.assertEqual(info['callable'], None)
+        self.assertEqual(info['args'], ())
+        self.assertEqual(info['kw'], {})
+        self.assertEqual(info['includepath'], ())
+        self.assertEqual(info['info'], '')
+        self.assertEqual(info['order'], 0)
 
-    >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc')
-    >>> machine((ns, "factory"), "fourth", factory=".f")
+    def test_action_defaults_w_info_w_includepath(self):
+        DISCRIMINATOR = ('a', ('b',), 0)
+        c = self._makeOne()
+        c.actions = [] # normally provided by subclass
+        c.info = 'INFO' # normally provided by subclass
+        c.includepath = ('a', 'b') # normally provided by subclass
+        c.action(DISCRIMINATOR)
+        self.assertEqual(len(c.actions), 1)
+        info = c.actions[0]
+        self.assertEqual(info['discriminator'], DISCRIMINATOR)
+        self.assertEqual(info['callable'], None)
+        self.assertEqual(info['args'], ())
+        self.assertEqual(info['kw'], {})
+        self.assertEqual(info['order'], 0)
+        self.assertEqual(info['includepath'], ('a', 'b'))
+        self.assertEqual(info['info'], 'INFO')
 
-    Note that we can't call a complex method unless there is a directive for
-    it:
+    def test_action_explicit_no_extra(self):
+        DISCRIMINATOR = ('a', ('b',), 0)
+        ARGS = (12, 'z')
+        KW = {'one': 1}
+        INCLUDE_PATH = ('p', 'q/r')
+        INFO = 'INFO'
+        def _callable():
+            pass
+        c = self._makeOne()
+        c.actions = [] # normally provided by subclass
+        c.action(DISCRIMINATOR,
+                 _callable,
+                 ARGS,
+                 KW,
+                 42,
+                 INCLUDE_PATH,
+                 INFO,
+                )
+        self.assertEqual(len(c.actions), 1)
+        info = c.actions[0]
+        self.assertEqual(info['discriminator'], DISCRIMINATOR)
+        self.assertEqual(info['callable'], _callable)
+        self.assertEqual(info['args'], ARGS)
+        self.assertEqual(info['kw'], KW)
+        self.assertEqual(info['order'], 42)
+        self.assertEqual(info['includepath'], INCLUDE_PATH)
+        self.assertEqual(info['info'], INFO)
 
-    >>> machine((ns, "factory2"), factory=".f")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid directive', 'factory2')
+    def test_action_explicit_w_extra(self):
+        DISCRIMINATOR = ('a', ('b',), 0)
+        ARGS = (12, 'z')
+        KW = {'one': 1}
+        INCLUDE_PATH = ('p', 'q/r')
+        INFO = 'INFO'
+        def _callable():
+            pass
+        c = self._makeOne()
+        c.actions = [] # normally provided by subclass
+        c.action(DISCRIMINATOR,
+                 _callable,
+                 ARGS,
+                 KW,
+                 42,
+                 INCLUDE_PATH,
+                 INFO,
+                 foo='bar',
+                 baz=17,
+                )
+        self.assertEqual(len(c.actions), 1)
+        info = c.actions[0]
+        self.assertEqual(info['discriminator'], DISCRIMINATOR)
+        self.assertEqual(info['callable'], _callable)
+        self.assertEqual(info['args'], ARGS)
+        self.assertEqual(info['kw'], KW)
+        self.assertEqual(info['order'], 42)
+        self.assertEqual(info['includepath'], INCLUDE_PATH)
+        self.assertEqual(info['info'], INFO)
+        self.assertEqual(info['foo'], 'bar')
+        self.assertEqual(info['baz'], 17)
 
+    def test_hasFeature_miss(self):
+        c = self._makeOne()
+        self.assertFalse(c.hasFeature('nonesuch'))
 
-    >>> machine.end()
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': None,
-      'discriminator': 'Complex.__init__',
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'ca',),
-      'callable': f,
-      'discriminator': ('Complex.factory', 1, 2),
-      'includepath': (),
-      'info': 'fourth',
-      'kw': {},
-      'order': 0},
-     {'args': (u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('Complex', 1, 2),
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0}]
+    def test_hasFeature_hit(self):
+        c = self._makeOne()
+        c._features.add('a.feature')
+        self.assertTrue(c.hasFeature('a.feature'))
 
-    Done with the package
+    def test_provideFeature(self):
+        c = self._makeOne()
+        c.provideFeature('a.feature')
+        self.assertTrue(c.hasFeature('a.feature'))
 
-    >>> machine.end()
 
+class ConfigurationAdapterRegistryTests(unittest.TestCase):
 
-    Verify that we can use a simple directive outside of the package:
+    def _getTargetClass(self):
+        from zope.configuration.config import ConfigurationAdapterRegistry
+        return ConfigurationAdapterRegistry
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb")
+    def test_ctor(self):
+        reg = self._makeOne()
+        self.assertEqual(len(reg._registry), 0)
+        self.assertEqual(len(reg._docRegistry), 0)
 
-    But we can't use the factory directive, because it's only valid
-    inside a package directive:
+    def test_register(self):
+        from zope.interface import Interface
+        class IFoo(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        def _factory():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, (NS, NAME), _factory)
+        self.assertEqual(len(reg._registry), 1)
+        areg = reg._registry[(NS, NAME)]
+        self.assertTrue(areg.lookup1(IFoo, Interface) is _factory)
+        self.assertEqual(len(reg._docRegistry), 0)
 
-    >>> 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.")
+    def test_register_replacement(self):
+        from zope.interface import Interface
+        class IFoo(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        def _factory():
+            pass
+        def _rival():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, (NS, NAME), _factory)
+        reg.register(IFoo, (NS, NAME), _rival)
+        self.assertEqual(len(reg._registry), 1)
+        areg = reg._registry[(NS, NAME)]
+        self.assertTrue(areg.lookup1(IFoo, Interface) is _rival)
+        self.assertEqual(len(reg._docRegistry), 0)
 
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': None,
-      'discriminator': 'Complex.__init__',
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'ca',),
-      'callable': f,
-      'discriminator': ('Complex.factory', 1, 2),
-      'includepath': (),
-      'info': 'fourth',
-      'kw': {},
-      'order': 0},
-     {'args': (u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('Complex', 1, 2),
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'oaa', u'obb', 'occ'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'oaa',
-                        u'obb',
-                        'occ'),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
+    def test_register_new_name(self):
+        from zope.interface import Interface
+        class IFoo(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        NAME2 = 'other'
+        def _factory():
+            pass
+        def _rival():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, (NS, NAME), _factory)
+        reg.register(IFoo, (NS, NAME2), _rival)
+        self.assertEqual(len(reg._registry), 2)
+        areg = reg._registry[(NS, NAME)]
+        self.assertTrue(areg.lookup1(IFoo, Interface) is _factory)
+        areg = reg._registry[(NS, NAME2)]
+        self.assertTrue(areg.lookup1(IFoo, Interface) is _rival)
+        self.assertEqual(len(reg._docRegistry), 0)
 
-    """
-    #'
+    def test_document_non_string_name(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        HANDLER = object()
+        INFO = 'INFO'
+        PARENT = object()
+        reg = self._makeOne()
+        reg.document((NS, NAME), ISchema, IUsedIn, HANDLER, INFO, PARENT)
+        self.assertEqual(len(reg._registry), 0)
+        self.assertEqual(len(reg._docRegistry), 1)
+        name, schema, used_in, handler, info, parent = reg._docRegistry[0]
+        self.assertEqual(name, (NS, NAME))
+        self.assertEqual(schema, ISchema)
+        self.assertEqual(used_in, IUsedIn)
+        self.assertEqual(info, INFO)
+        self.assertEqual(parent, PARENT)
 
-def test_keyword_handling():
-    """
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
+    def test_document_w_string_name(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        NAME = 'testing'
+        HANDLER = object()
+        INFO = 'INFO'
+        PARENT = object()
+        reg = self._makeOne()
+        reg.document(NAME, ISchema, IUsedIn, HANDLER, INFO, PARENT)
+        self.assertEqual(len(reg._registry), 0)
+        self.assertEqual(len(reg._docRegistry), 1)
+        name, schema, used_in, handler, info, parent = reg._docRegistry[0]
+        self.assertEqual(name, ('', NAME))
+        self.assertEqual(schema, ISchema)
+        self.assertEqual(used_in, IUsedIn)
+        self.assertEqual(info, INFO)
+        self.assertEqual(parent, PARENT)
 
-    Register some test directives:
+    def test_factory_miss(self):
+        from zope.configuration.exceptions import ConfigurationError
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = object()
+        reg = self._makeOne()
+        self.assertRaises(ConfigurationError, reg.factory, context, (NS, NAME))
 
-    Start with a grouping directive that sets a package:
+    def test_factory_hit_on_fqn(self):
+        from zope.interface import Interface
+        from zope.interface import implementer
+        class IFoo(Interface):
+            pass
+        @implementer(IFoo)
+        class Context(object):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = Context()
+        def _factory():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, (NS, NAME), _factory)
+        self.assertEqual(reg.factory(context, (NS, NAME)), _factory)
 
-    >>> machine((metans, "groupingDirective"),
-    ...         name="package", namespace=ns,
-    ...         schema="zope.configuration.tests.directives.IPackaged",
-    ...         handler="zope.configuration.tests.directives.Packaged",
-    ...         )
+    def test_factory_miss_on_fqn_hit_on_bare_name(self):
+        from zope.interface import Interface
+        from zope.interface import implementer
+        class IFoo(Interface):
+            pass
+        @implementer(IFoo)
+        class Context(object):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = Context()
+        def _factory():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, NAME, _factory)
+        self.assertEqual(reg.factory(context, (NS, NAME)), _factory)
 
-    Now we can set the package:
+    def test_factory_hit_on_fqn_lookup_fails(self):
+        from zope.interface import Interface
+        from zope.configuration.exceptions import ConfigurationError
+        class IFoo(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = object() # doesn't provide IFoo
+        def _factory():
+            pass
+        reg = self._makeOne()
+        reg.register(IFoo, (NS, NAME), _factory)
+        self.assertRaises(ConfigurationError, reg.factory, context, (NS, NAME))
 
-    >>> machine.begin((ns, "package"),
-    ...               package="zope.configuration.tests.directives",
-    ...               )
 
-    Which makes it easier to define the other directives:
+class _ConformsToIConfigurationContext(object):
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="k",
-    ...         schema=".Ik", handler=".k")
+    def test_class_conforms_to_IConfigurationContext(self):
+        from zope.interface.verify import verifyClass
+        from zope.configuration.interfaces import IConfigurationContext
+        verifyClass(IConfigurationContext, self._getTargetClass())
 
+    def test_instance_conforms_to_IConfigurationContext(self):
+        from zope.interface.verify import verifyObject
+        from zope.configuration.interfaces import IConfigurationContext
+        verifyObject(IConfigurationContext, self._makeOne())
 
-    >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"})
 
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
-    >>> pprint(machine.actions)
-    [{'args': ('f', 'c', 'x'),
-      'callable': f,
-      'discriminator': ('k', 'f'),
-      'includepath': (),
-      'info': 'yee ha',
-      'kw': {},
-      'order': 0}]
-    """
+class ConfigurationMachineTests(_Catchable,
+                                _ConformsToIConfigurationContext,
+                                unittest.TestCase,
+                               ):
 
-def test_basepath_absolute():
-    """Path must always return an absolute path.
+    def _getTargetClass(self):
+        from zope.configuration.config import ConfigurationMachine
+        return ConfigurationMachine
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    >>> import os
-    >>> class stub:
-    ...     __file__ = os.path.join('relative', 'path')
-    >>> c = config.ConfigurationContext()
-    >>> c.package = stub()
+    def test_ctor(self):
+        from zope.configuration.config import RootStackItem
+        from zope.configuration.config import metans
+        cm = self._makeOne()
+        self.assertEqual(cm.package, None)
+        self.assertEqual(cm.basepath, None)
+        self.assertEqual(cm.includepath, ())
+        self.assertEqual(cm.info, '')
+        self.assertEqual(len(cm.actions), 0)
+        self.assertEqual(len(cm.stack), 1)
+        root = cm.stack[0]
+        self.assertTrue(isinstance(root, RootStackItem))
+        self.assertTrue(root.context is cm)
+        self.assertEqual(len(cm.i18n_strings), 0)
+        # Check bootstrapped meta:*.
+        self.assertTrue((metans, 'directive') in cm._registry)
+        self.assertTrue((metans, 'directives') in cm._registry)
+        self.assertTrue((metans, 'complexDirective') in cm._registry)
+        self.assertTrue((metans, 'groupingDirective') in cm._registry)
+        self.assertTrue((metans, 'provides') in cm._registry)
+        self.assertTrue((metans, 'subdirective') in cm._registry)
 
-    >>> os.path.isabs(c.path('y/z'))
-    True
-    """
+    def test_begin_w___data_and_kw(self):
+        from zope.configuration.config import IConfigurationContext
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        def _factory(context, data, info):
+            pass
+        cm = self._makeOne()
+        cm.register(IConfigurationContext, (NS, NAME), _factory)
+        self.assertRaises(TypeError,
+                          cm.begin, (NS, NAME), {'foo': 'bar'}, baz='bam')
 
-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)
+    def test_begin_w___data_no_kw(self):
+        from zope.interface import Interface
+        from zope.configuration.config import IConfigurationContext
+        from zope.configuration.config import RootStackItem
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        def _handler(*args, **kw):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        _called_with = []
+        item = object()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return item
+        cm = self._makeOne()
+        cm.register(IConfigurationContext, (NS, NAME), _factory)
+        cm.begin((NS, NAME), {'name': 'testing',
+                                  'schema': ISchema,
+                                  'usedIn': IUsedIn,
+                                  'handler': _handler,
+                                 }, 'INFO')
+        self.assertEqual(len(cm.stack), 2)
+        root = cm.stack[0]
+        self.assertTrue(isinstance(root, RootStackItem))
+        nested = cm.stack[1]
+        self.assertTrue(nested is item)
+        self.assertEqual(_called_with,
+                        [(cm, {'name': 'testing',
+                               'schema': ISchema,
+                               'usedIn': IUsedIn,
+                               'handler': _handler,
+                              }, 'INFO')])
 
-    >>> import os
-    >>> class stub:
-    ...     __path__ = [os.path.join('relative', 'path')]
-    >>> c = config.ConfigurationContext()
-    >>> c.package = stub()
+    def test_begin_wo___data_w_kw(self):
+        from zope.interface import Interface
+        from zope.configuration.config import IConfigurationContext
+        from zope.configuration.config import RootStackItem
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        def _handler(*args, **kw):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        _called_with = []
+        item = object()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return item
+        cm = self._makeOne()
+        cm.register(IConfigurationContext, (NS, NAME), _factory)
+        cm.begin((NS, NAME), None, 'INFO',
+                  name='testing',
+                  schema=ISchema,
+                  usedIn=IUsedIn,
+                  handler=_handler,
+                )
+        self.assertEqual(len(cm.stack), 2)
+        root = cm.stack[0]
+        self.assertTrue(isinstance(root, RootStackItem))
+        nested = cm.stack[1]
+        self.assertTrue(nested is item)
+        self.assertEqual(_called_with,
+                        [(cm, {'name': 'testing',
+                               'schema': ISchema,
+                               'usedIn': IUsedIn,
+                               'handler': _handler,
+                              }, 'INFO')])
 
-    >>> os.path.isabs(c.path('y/z'))
-    True
-    """
+    def test_end(self):
+        from zope.configuration.config import RootStackItem
+        class FauxItem(object):
+            _finished = False
+            def finish(self):
+                self._finished = True
+        cm = self._makeOne()
+        item = FauxItem()
+        cm.stack.append(item)
+        cm.end()
+        self.assertEqual(len(cm.stack), 1)
+        root = cm.stack[0]
+        self.assertTrue(isinstance(root, RootStackItem))
+        self.assertTrue(item._finished)
 
-def test_trailing_dot_in_resolve():
-    """Dotted names are no longer allowed to end in dots
+    def test___call__(self):
+        from zope.interface import Interface
+        from zope.configuration.config import IConfigurationContext
+        from zope.configuration.config import RootStackItem
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        class FauxItem(object):
+            _finished = False
+            def finish(self):
+                self._finished = True
+        def _handler(*args, **kw):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        _called_with = []
+        item = FauxItem()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return item
+        cm = self._makeOne()
+        cm.register(IConfigurationContext, (NS, NAME), _factory)
+        cm((NS, NAME), 'INFO',
+            name='testing',
+            schema=ISchema,
+            usedIn=IUsedIn,
+            handler=_handler,
+           )
+        self.assertEqual(len(cm.stack), 1)
+        root = cm.stack[0]
+        self.assertTrue(isinstance(root, RootStackItem))
+        self.assertEqual(_called_with,
+                        [(cm, {'name': 'testing',
+                               'schema': ISchema,
+                               'usedIn': IUsedIn,
+                               'handler': _handler,
+                              }, 'INFO')])
+        self.assertTrue(item._finished)
 
-    >>> c = config.ConfigurationContext()
+    def test_getInfo_only_root_default(self):
+        cm = self._makeOne()
+        self.assertEqual(cm.getInfo(), '')
 
-    >>> c.resolve('zope.')
-    Traceback (most recent call last):
-    ...
-    ValueError: Trailing dots are no longer supported in dotted names
+    def test_getInfo_only_root(self):
+        cm = self._makeOne()
+        cm.info = 'INFO'
+        self.assertEqual(cm.getInfo(), 'INFO')
 
-    >>> c.resolve('  ')
-    Traceback (most recent call last):
-    ...
-    ValueError: The given name is blank
-    """
+    def test_getInfo_w_item(self):
+        class FauxItem(object):
+            info = 'FAUX'
+            def __init__(self):
+                self.context = self
+        cm = self._makeOne()
+        cm.stack.append(FauxItem())
+        self.assertEqual(cm.getInfo(), 'FAUX')
 
-def test_bad_dotted_last_import():
-    """
-    >>> c = config.ConfigurationContext()
+    def test_setInfo_only_root(self):
+        cm = self._makeOne()
+        cm.setInfo('INFO')
+        self.assertEqual(cm.info, 'INFO')
 
-    Import error caused by a bad last component in the dotted name.
+    def test_setInfo_w_item(self):
+        class FauxItem(object):
+            info = 'FAUX'
+            def __init__(self):
+                self.context = self
+        cm = self._makeOne()
+        item = FauxItem()
+        cm.stack.append(item)
+        cm.setInfo('UPDATED')
+        self.assertEqual(item.info, 'UPDATED')
 
-    >>> c.resolve('zope.configuration.tests.nosuch')
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ImportError: Module zope.configuration.tests""" \
-                                               """ has no global nosuch
-    """
+    def test_execute_actions_empty(self):
+        cm = self._makeOne()
+        cm.execute_actions() # noop
 
-def test_bad_dotted_import():
-    """
-    >>> c = config.ConfigurationContext()
+    def test_execute_actions_wo_errors(self):
+        _called_with = {}
+        def _a1(*args, **kw):
+            _called_with.setdefault('_a1', []).append((args, kw))
+        def _a2(*args, **kw):
+            _called_with.setdefault('_a2', []).append((args, kw))
+        cm = self._makeOne()
+        cm.action(None, None) # will be skipped
+        cm.action(None, _a1, ('a', 0), {'foo': 'bar'}, 4)
+        cm.action(None, _a2, ('a', 1), {'foo': 'baz'}, 3)
+        cm.action(None, _a1, ('b', 2), {'foo': 'qux'}, 2)
+        cm.execute_actions()
+        self.assertEqual(_called_with['_a1'],
+                        [(('b', 2), {'foo': 'qux'}),
+                         (('a', 0), {'foo': 'bar'}),
+                        ])
+        self.assertEqual(_called_with['_a2'],
+                        [(('a', 1), {'foo': 'baz'}),
+                        ])
 
-    Import error caused by a totally wrong dotted name.
+    def test_execute_actions_w_errors_w_testing(self):
+        def _err(*args, **kw):
+            raise ValueError('XXX')
+        cm = self._makeOne()
+        cm.action(None, _err)
+        exc = self.assertRaises(ValueError, cm.execute_actions, testing=True)
+        self.assertEqual(str(exc), 'XXX')
 
-    >>> 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_execute_actions_w_errors_wo_testing(self):
+        from zope.configuration.config import ConfigurationExecutionError
+        def _err(*args, **kw):
+            raise ValueError('XXX')
+        cm = self._makeOne()
+        cm.info = 'INFO'
+        cm.action(None, _err)
+        exc = self.assertRaises(ConfigurationExecutionError,
+                                cm.execute_actions)
+        self.assertTrue(exc.etype is ValueError)
+        self.assertEqual(str(exc.evalue), "XXX")
+        self.assertEqual(exc.info, "INFO")
 
-def test_bad_sub_last_import():
-    """
-    >>> c = config.ConfigurationContext()
+    def test_keyword_handling(self):
+        # This is really an integraiton test.
+        from zope.configuration.config import metans
+        from zope.configuration.tests.directives import f
+        from zope.configuration._compat import b
+        from zope.configuration._compat import u
+        machine = self._makeOne()
+        ns = "http://www.zope.org/testing"
 
-    Import error caused by a bad sub import inside the referenced
-    dotted name. Here we keep the standard traceback.
+        #Register some test directives, starting with a grouping directive
+        # that sets a package:
 
-    >>> 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
+        machine((metans, "groupingDirective"),
+                 name="package", namespace=ns,
+                 schema="zope.configuration.tests.directives.IPackaged",
+                 handler="zope.configuration.tests.directives.Packaged",
+                )
 
-    Cleanup:
+        # set the package:
+        machine.begin((ns, "package"),
+                       package="zope.configuration.tests.directives",
+                      )
 
-    >>> for name in ('zope.configuration.tests.victim',
-    ...              'zope.configuration.tests.bad'):
-    ...    if name in sys.modules:
-    ...        del sys.modules[name]
-    """
+        #Which makes it easier to define the other directives:
+        machine((metans, "directive"),
+                namespace=ns, name="k",
+                schema=".Ik", handler=".k")
 
-def test_bad_sub_import():
-    """
-    >>> c = config.ConfigurationContext()
+        machine((ns, "k"), "yee ha",
+                **{"for": u("f"), "class": u("c"), "x": u("x")})
 
-    Import error caused by a bad sub import inside part of the referenced
-    dotted name. Here we keep the standard traceback.
+        self.assertEqual(len(machine.actions), 1)
+        self.assertEqual(machine.actions[0],
+                         {'args': (b('f'), b('c'), b('x')),
+                          'callable': f,
+                          'discriminator': ('k', b('f')),
+                          'includepath': (),
+                          'info': 'yee ha',
+                          'kw': {},
+                          'order': 0,
+                         })
 
-    >>> 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:
+class _ConformsToIStackItem(object):
 
-    >>> for name in ('zope.configuration.tests.victim',
-    ...              'zope.configuration.tests.bad'):
-    ...    if name in sys.modules:
-    ...        del sys.modules[name]
-    """
+    def test_class_conforms_to_IStackItem(self):
+        from zope.interface.verify import verifyClass
+        from zope.configuration.config import IStackItem
+        verifyClass(IStackItem, self._getTargetClass())
 
+    def test_instance_conforms_to_IStackItem(self):
+        from zope.interface.verify import verifyObject
+        from zope.configuration.config import IStackItem
+        verifyObject(IStackItem, self._makeOne())
+
+
+class SimpleStackItemTests(_ConformsToIStackItem,
+                           unittest.TestCase,
+                          ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import SimpleStackItem
+        return SimpleStackItem
+    
+    def _makeOne(self,
+                 context=None, handler=None, info=None,
+                 schema=None, data=None):
+        from zope.interface import Interface
+        if context is None:
+            context = FauxContext()
+        if handler is None:
+            def handler():
+                pass
+        if info is None:
+            info = 'INFO'
+        if schema is None:
+            schema = Interface
+        if data is None:
+            data = {}
+        return self._getTargetClass()(context, handler, info, schema, data)
+
+    def test_ctor(self):
+        from zope.interface import Interface
+        from zope.configuration.config import GroupingContextDecorator
+        class ISchema(Interface):
+            pass
+        context = FauxContext()
+        def _handler():
+            pass
+        _data = {}
+        ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data)
+        self.assertTrue(isinstance(ssi.context, GroupingContextDecorator))
+        self.assertTrue(ssi.context.context is context)
+        self.assertEqual(ssi.context.info, 'INFO')
+        self.assertEqual(ssi.handler, _handler)
+        self.assertEqual(ssi.argdata, (ISchema, _data))
+
+    def test_contained_raises(self):
+        from zope.configuration.exceptions import ConfigurationError
+        ssi = self._makeOne()
+        self.assertRaises(ConfigurationError,
+                          ssi.contained, ('ns', 'name'), {}, '')
+
+    def test_finish_handler_returns_no_actions(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            name = Text(required=True)
+        context = FauxContext()
+        def _handler(context, **kw):
+            return ()
+        _data = {'name': 'NAME'}
+        ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data)
+        ssi.finish() # noraise
+        self.assertEqual(context.actions, [])
+
+    def test_finish_handler_returns_oldstyle_actions(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            name = Text(required=True)
+        context = FauxContext()
+        def _action(context, **kw):
+            pass
+        def _handler(context, **kw):
+            return [(None, _action)]
+        _data = {'name': 'NAME'}
+        ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data)
+        ssi.finish()
+        self.assertEqual(context.actions,
+                         [{'discriminator': None,
+                           'callable': _action,
+                           'args': (),
+                           'kw': {},
+                           'includepath': (),
+                           'info': 'INFO',
+                           'order': 0,
+                          }])
+
+    def test_finish_handler_returns_newstyle_actions(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            name = Text(required=True)
+        context = FauxContext()
+        def _action(context, **kw):
+            pass
+        def _handler(context, **kw):
+            return [{'discriminator': None, 'callable': _action}]
+        _data = {'name': 'NAME'}
+        ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data)
+        ssi.finish()
+        self.assertEqual(context.actions,
+                         [{'discriminator': None,
+                           'callable': _action,
+                           'args': (),
+                           'kw': {},
+                           'includepath': (),
+                           'info': 'INFO',
+                           'order': 0,
+                          }])
+
+
+
+class RootStackItemTests(_ConformsToIStackItem,
+                         unittest.TestCase,
+                        ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import RootStackItem
+        return RootStackItem
+    
+    def _makeOne(self, context=None):
+        if context is None:
+            context = object()
+        return self._getTargetClass()(context)
+
+    def test_contained_context_factory_fails(self):
+        from zope.configuration.exceptions import ConfigurationError
+        class _Context(object):
+            def factory(self, context, name):
+                pass
+        rsi = self._makeOne(_Context())
+        self.assertRaises(ConfigurationError,
+                          rsi.contained, ('ns', 'name'), {}, '')
+
+    def test_contained_context_factory_normal(self):
+        _called_with = []
+        _adapter = object()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return _adapter
+        class _Context(object):
+            def factory(self, context, name):
+                return _factory
+        context = _Context()
+        rsi = self._makeOne(context)
+        adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO')
+        self.assertTrue(adapter is _adapter)
+        self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')])
+
+    def test_finish(self):
+        rsi = self._makeOne()
+        rsi.finish() #noraise
+
+
+class GroupingStackItemTests(_ConformsToIStackItem,
+                             unittest.TestCase,
+                            ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import GroupingStackItem
+        return GroupingStackItem
+    
+    def _makeOne(self, context=None):
+        if context is None:
+            context = object()
+        return self._getTargetClass()(context)
+
+    def test_contained_context_before_returns_oldstyle_actions(self):
+        _called_with = []
+        _adapter = object()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return _adapter
+        def _action(*args, **kw):
+            pass
+        class _Context(FauxContext):
+            def factory(self, context, name):
+                return _factory
+            def before(self):
+                return [(None, _action)]
+            def after(self):
+                return ()
+        context = _Context()
+        rsi = self._makeOne(context)
+        adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO')
+        self.assertTrue(adapter is _adapter)
+        self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')])
+        self.assertEqual(len(context.actions), 1)
+        self.assertEqual(context.actions[0],
+                         {'discriminator': None,
+                          'callable': _action,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': None,
+                          'order': 0,
+                         })
+        rsi.finish() # doesn't re-do the 'before' dance
+        self.assertEqual(len(context.actions), 1)
+
+    def test_contained_context_before_returns_newstyle_actions(self):
+        _called_with = []
+        _adapter = object()
+        def _factory(context, data, info):
+            _called_with.append((context, data, info))
+            return _adapter
+        def _before(*args, **kw):
+            pass
+        def _after(*args, **kw):
+            pass
+        class _Context(FauxContext):
+            def factory(self, context, name):
+                return _factory
+            def before(self):
+                return [{'discriminator': None, 'callable': _before}]
+            def after(self):
+                return [{'discriminator': None, 'callable': _after}]
+        context = _Context()
+        rsi = self._makeOne(context)
+        adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO')
+        self.assertTrue(adapter is _adapter)
+        self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')])
+        self.assertEqual(len(context.actions), 1)
+        self.assertEqual(context.actions[0], # no GSI to add extras
+                         {'discriminator': None,
+                          'callable': _before,
+                         })
+        rsi.finish() # doesn't re-do the 'before' dance
+        self.assertEqual(len(context.actions), 2)
+        self.assertEqual(context.actions[1],
+                         {'discriminator': None,
+                          'callable': _after,
+                         })
+
+    def test_finish_calls_before_if_not_already_called(self):
+        def _before(*args, **kw):
+            pass
+        def _after(*args, **kw):
+            pass
+        class _Context(FauxContext):
+            def before(self):
+                return [(None, _before)]
+            def after(self):
+                return [(None, _after)]
+        context = _Context()
+        rsi = self._makeOne(context)
+        adapter = rsi.finish()
+        self.assertEqual(len(context.actions), 2)
+        self.assertEqual(context.actions[0], # no GSI to add extras
+                         {'discriminator': None,
+                          'callable': _before,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': None,
+                          'order': 0,
+                         })
+        self.assertEqual(context.actions[1],
+                         {'discriminator': None,
+                          'callable': _after,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': None,
+                          'order': 0,
+                         })
+
+
+class ComplexStackItemTests(_ConformsToIStackItem,
+                            unittest.TestCase,
+                           ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import ComplexStackItem
+        return ComplexStackItem
+    
+    def _makeOne(self, meta=None, context=None, data=None, info=None):
+        if meta is None:
+            meta = self._makeMeta()
+        if context is None:
+            context = object()
+        if data is None:
+            data = {'name': 'NAME'}
+        if info is None:
+            info = 'INFO'
+        return self._getTargetClass()(meta, context, data, info)
+
+    def _makeMeta(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            name = Text()
+        class FauxMeta(dict):
+            schema = ISchema
+            _handler_args = None
+            _handler = object()
+            def handler(self, newcontext, **kw):
+                self._handler_kwargs = kw
+                return self._handler
+        return FauxMeta()
+
+    def test_ctor(self):
+        from zope.configuration.config import GroupingContextDecorator
+        meta = self._makeMeta()
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        self.assertTrue(isinstance(csi.context, GroupingContextDecorator))
+        self.assertTrue(csi.context.context is context)
+        self.assertEqual(csi.context.info, 'INFO')
+        self.assertEqual(csi.handler, meta._handler)
+        self.assertEqual(meta._handler_kwargs, _data)
+
+    def test_contained_miss(self):
+        from zope.configuration.exceptions import ConfigurationError
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        csi = self._makeOne()
+        self.assertRaises(ConfigurationError,
+                          csi.contained, (NS, NAME), {}, 'INFO')
+
+    def test_contained_hit(self):
+        from zope.interface import Interface
+        from zope.configuration.config import GroupingContextDecorator
+        from zope.configuration.config import SimpleStackItem
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        class ISubSchema(Interface):
+            pass
+        class WithName(object):
+            def testing(self, *args):
+                pass
+        meta = self._makeMeta()
+        wn = meta._handler = WithName()
+        meta[NAME] = (ISubSchema, 'SUBINFO')
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        ssi = csi.contained((NS, NAME), {}, 'SUBINFO')
+        self.assertTrue(isinstance(ssi, SimpleStackItem))
+        self.assertTrue(isinstance(ssi.context, GroupingContextDecorator))
+        self.assertTrue(ssi.context.context is csi.context)
+        self.assertEqual(ssi.context.info, 'SUBINFO')
+        self.assertEqual(ssi.handler, wn.testing)
+        self.assertEqual(ssi.argdata, (ISubSchema, {}))
+
+    def test_finish_handler_is_noncallable(self):
+        meta = self._makeMeta()
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        csi.finish() #noraise
+        self.assertEqual(len(context.actions), 0)
+
+    def test_finish_handler_raises_AE_for___call__(self):
+        def _handler():
+            raise AttributeError('__call__')
+        meta = self._makeMeta()
+        meta._handler = _handler
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        csi.finish() #noraise
+        self.assertEqual(len(context.actions), 0)
+
+    def test_finish_handler_raises_AE_for_other(self):
+        def _handler():
+            raise AttributeError('other')
+        meta = self._makeMeta()
+        meta._handler = _handler
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        self.assertRaises(AttributeError, csi.finish)
+
+    def test_finish_handler_returns_oldstyle_actions(self):
+        def _action():
+            pass
+        def _handler():
+            return [(None, _action)]
+        meta = self._makeMeta()
+        meta._handler = _handler
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        csi.finish()
+        self.assertEqual(len(context.actions), 1)
+        self.assertEqual(context.actions[0],
+                         {'discriminator': None,
+                          'callable': _action,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': 'INFO',
+                          'order': 0,
+                         })
+
+    def test_finish_handler_returns_newstyle_actions(self):
+        def _action():
+            pass
+        def _handler():
+            return [{'discriminator': None, 'callable': _action}]
+        meta = self._makeMeta()
+        meta._handler = _handler
+        context = FauxContext()
+        _data = {'name': 'NAME'}
+        csi = self._makeOne(meta, context, _data, 'INFO')
+        csi.finish()
+        self.assertEqual(len(context.actions), 1)
+        self.assertEqual(context.actions[0],
+                         {'discriminator': None,
+                          'callable': _action,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': 'INFO',
+                          'order': 0,
+                         })
+
+
+class _ConformsToIGroupingContext(object):
+
+    def test_class_conforms_to_IGroupingContext(self):
+        from zope.interface.verify import verifyClass
+        from zope.configuration.interfaces import IGroupingContext
+        verifyClass(IGroupingContext, self._getTargetClass())
+
+    def test_instance_conforms_to_IGroupingContext(self):
+        from zope.interface.verify import verifyObject
+        from zope.configuration.interfaces import IGroupingContext
+        verifyObject(IGroupingContext, self._makeOne())
+
+
+class GroupingContextDecoratorTests(_ConformsToIConfigurationContext,
+                                    _ConformsToIGroupingContext,
+                                    unittest.TestCase,
+                                   ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import GroupingContextDecorator
+        return GroupingContextDecorator
+    
+    def _makeOne(self, context=None, **kw):
+        if context is None:
+            context = FauxContext()
+            context.package = None #appease IConfigurationContext
+        instance = self._getTargetClass()(context, **kw)
+        return instance
+
+    def test_ctor_no_kwargs(self):
+        context = FauxContext()
+        gcd = self._makeOne(context)
+        self.assertTrue(gcd.context is context)
+
+    def test_ctor_w_kwargs(self):
+        context = FauxContext()
+        gcd = self._makeOne(context, foo='bar', baz=42)
+        self.assertTrue(gcd.context is context)
+        self.assertEqual(gcd.foo, 'bar')
+        self.assertEqual(gcd.baz, 42)
+
+    def test_getattr_fetches_from_context_and_caches(self):
+        context = FauxContext()
+        gcd = self._makeOne(context)
+        context.foo = 'bar'
+        self.assertEqual(gcd.foo, 'bar')
+        self.assertTrue('foo' in gcd.__dict__)
+
+    def test_before(self):
+        gcd = self._makeOne()
+        gcd.before() #noraise
+
+    def test_after(self):
+        gcd = self._makeOne()
+        gcd.after() #noraise
+
+
+class _ConformsToIDirectivesContext(object):
+
+    def test_class_conforms_to_IDirectivesContext(self):
+        from zope.interface.verify import verifyClass
+        from zope.configuration.config import IDirectivesContext
+        verifyClass(IDirectivesContext, self._getTargetClass())
+
+    def test_instance_conforms_to_IDirectivesContext(self):
+        from zope.interface.verify import verifyObject
+        from zope.configuration.config import IDirectivesContext
+        verifyObject(IDirectivesContext, self._makeOne())
+
+
+class DirectivesHandlerTests(_ConformsToIDirectivesContext,
+                             unittest.TestCase,
+                            ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import DirectivesHandler
+        return DirectivesHandler
+    
+    def _makeOne(self, context=None):
+        if context is None:
+            context = FauxContext()
+            context.package = None #appease IConfigurationContext
+            context.namespace = None #appease IDirectivesInfo
+        instance = self._getTargetClass()(context)
+        return instance
+
+
+class Test_defineSimpleDirective(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import defineSimpleDirective
+        return defineSimpleDirective(*args, **kw)
+
+    def _makeContext(self):
+        class _Context(FauxContext):
+            def __init__(self):
+                self._registered = []
+                self._documented = []
+            def register(self, usedIn, name, factory):
+                self._registered.append((usedIn, name, factory))
+            def document(self, name, schema, usedIn, handler, info):
+                self._documented.append((name, schema, usedIn, handler, info))
+        return _Context()
+
+    def test_defaults(self):
+        from zope.interface import Interface
+        from zope.configuration.interfaces import IConfigurationContext as ICC
+        class ISchema(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = self._makeContext()
+        context.namespace = NS
+        context.info = 'INFO'
+        def _handler():
+            pass
+
+        self._callFUT(context, NAME, ISchema, _handler)
+
+        self.assertEqual(len(context._registered), 1)
+        usedIn, name, factory = context._registered[0]
+        self.assertEqual(usedIn, ICC)
+        self.assertEqual(name, (NS, NAME))
+        sub = object()
+        ssi = factory(sub, {'a': 1}, 'SUBINFO')
+        self.assertTrue(ssi.context.context is sub)
+        self.assertEqual(ssi.context.info, 'SUBINFO')
+        self.assertEqual(ssi.handler, _handler)
+
+        self.assertEqual(len(context._documented), 1)
+        self.assertEqual(context._documented[0],
+                         ((NS, NAME), ISchema, ICC, _handler, 'INFO'))
+
+    def test_explicit_w_star_namespace(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = self._makeContext()
+        context.namespace = NS
+        context.info = 'INFO'
+        def _handler():
+            pass
+
+        self._callFUT(context, NAME, ISchema, _handler,
+                      namespace='*', usedIn=IUsedIn)
+
+        self.assertEqual(len(context._registered), 1)
+        usedIn, name, factory = context._registered[0]
+        self.assertEqual(usedIn, IUsedIn)
+        self.assertEqual(name, NAME)
+        sub = object()
+        ssi = factory(sub, {'a': 1}, 'SUBINFO')
+        self.assertTrue(ssi.context.context is sub)
+        self.assertEqual(ssi.context.info, 'SUBINFO')
+        self.assertEqual(ssi.handler, _handler)
+
+        self.assertEqual(len(context._documented), 1)
+        self.assertEqual(context._documented[0],
+                         (NAME, ISchema, IUsedIn, _handler, 'INFO'))
+
+
+class Test_defineGroupingDirective(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import defineGroupingDirective
+        return defineGroupingDirective(*args, **kw)
+
+    def _makeContext(self):
+        class _Context(FauxContext):
+            def __init__(self):
+                self._registered = []
+                self._documented = []
+            def register(self, usedIn, name, factory):
+                self._registered.append((usedIn, name, factory))
+            def document(self, name, schema, usedIn, handler, info):
+                self._documented.append((name, schema, usedIn, handler, info))
+        return _Context()
+
+    def test_defaults(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        from zope.configuration.interfaces import IConfigurationContext as ICC
+        class ISchema(Interface):
+            arg = Text()
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = self._makeContext()
+        context.namespace = NS
+        context.info = 'INFO'
+        newcontext = FauxContext()
+        _called_with = []
+        def _handler(context, **kw):
+            _called_with.append((context, kw))
+            return newcontext
+
+        self._callFUT(context, NAME, ISchema, _handler)
+
+        self.assertEqual(len(context._registered), 1)
+        usedIn, name, factory = context._registered[0]
+        self.assertEqual(usedIn, ICC)
+        self.assertEqual(name, (NS, NAME))
+        sub = object()
+        gsi = factory(sub, {'arg': 'val'}, 'SUBINFO')
+        self.assertTrue(gsi.context is newcontext)
+        self.assertEqual(newcontext.info, 'SUBINFO')
+        self.assertEqual(_called_with, [(sub, {'arg': 'val'})])
+
+        self.assertEqual(len(context._documented), 1)
+        self.assertEqual(context._documented[0],
+                         ((NS, NAME), ISchema, ICC, _handler, 'INFO'))
+
+    def test_explicit_w_star_namespace(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            arg = Text()
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        context = self._makeContext()
+        context.namespace = NS
+        context.info = 'INFO'
+        newcontext = FauxContext()
+        _called_with = []
+        def _handler(context, **kw):
+            _called_with.append((context, kw))
+            return newcontext
+
+        self._callFUT(context, NAME, ISchema, _handler,
+                      namespace='*', usedIn=IUsedIn)
+
+        self.assertEqual(len(context._registered), 1)
+        usedIn, name, factory = context._registered[0]
+        self.assertEqual(usedIn, IUsedIn)
+        self.assertEqual(name, NAME)
+        sub = object()
+        gsi = factory(sub, {'arg': 'val'}, 'SUBINFO')
+        self.assertTrue(gsi.context is newcontext)
+        self.assertEqual(newcontext.info, 'SUBINFO')
+        self.assertEqual(_called_with, [(sub, {'arg': 'val'})])
+
+        self.assertEqual(len(context._documented), 1)
+        self.assertEqual(context._documented[0],
+                         (NAME, ISchema, IUsedIn, _handler, 'INFO'))
+
+
+class _ConformsToIComplexDirectiveContext(object):
+
+    def test_class_conforms_to_IComplexDirectiveContext(self):
+        from zope.interface.verify import verifyClass
+        from zope.configuration.config import IComplexDirectiveContext
+        verifyClass(IComplexDirectiveContext, self._getTargetClass())
+
+    def test_instance_conforms_to_IComplexDirectiveContext(self):
+        from zope.interface.verify import verifyObject
+        from zope.configuration.config import IComplexDirectiveContext
+        verifyObject(IComplexDirectiveContext, self._makeOne())
+
+
+class ComplexDirectiveDefinitionTests(_ConformsToIComplexDirectiveContext,
+                                      unittest.TestCase,
+                                     ):
+
+    def _getTargetClass(self):
+        from zope.configuration.config import ComplexDirectiveDefinition
+        return ComplexDirectiveDefinition
+    
+    def _makeOne(self, context=None):
+        if context is None:
+            context = self._makeContext()
+        instance = self._getTargetClass()(context)
+        return instance
+
+    def _makeContext(self, package=None, namespace=None, name=None,
+                     schema=None, handler=None, usedIn=None):
+        context = FauxContext()
+        context.package = package
+        context.namespace = namespace
+        context.name = name
+        context.schema = schema
+        context.handler = handler
+        context.usedIn = usedIn
+        return context
+
+    def test_before(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            arg = Text()
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        _handled = []
+        _csi_handler = object()
+        def _handler(context, **kw):
+            _handled.append((context, kw))
+            return _csi_handler
+        context = self._makeContext(namespace=NS, name=NAME, schema=ISchema,
+                                    handler=_handler, usedIn=IUsedIn)
+        context.info = 'INFO'
+        _registered = []
+        def _register(*args):
+            _registered.append(args)
+        context.register = _register
+        _documented = []
+        def _document(*args):
+            _documented.append(args)
+        context.document = _document
+        cdd = self._makeOne(context)
+
+        cdd.before()
+
+        self.assertEqual(len(_registered), 1)
+        usedIn, fqn, factory = _registered[0]
+        self.assertEqual(usedIn, IUsedIn)
+        self.assertEqual(fqn, (NS, NAME))
+        sub = FauxContext()
+        csi = factory(sub, {'arg': 'val'}, 'SUBINFO')
+        self.assertEqual(csi.meta, cdd)
+        self.assertEqual(csi.context.context, sub)
+        self.assertEqual(csi.context.info, 'SUBINFO')
+        self.assertEqual(csi.handler, _csi_handler)
+        self.assertEqual(_handled, [(csi.context, {'arg': 'val'})])
+
+        self.assertEqual(_documented,
+                        [((NS, NAME), ISchema, IUsedIn, _handler, 'INFO')])
+
+
+class Test_subdirective(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import subdirective
+        return subdirective(*args, **kw)
+
+    def _makeContext(self, package=None, namespace=None, name=None,
+                     schema=None, handler=None, usedIn=None):
+        class _Context(object):
+            def __init__(self):
+                self.context = {}
+                self._documented = []
+            def document(self, *args):
+                self._documented.append(args)
+        context = _Context()
+        context.package = package
+        context.namespace = namespace
+        context.name = name
+        context.schema = schema
+        context.handler = handler
+        context.usedIn = usedIn
+        return context
+
+    def test_wo_handler_attribute(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISubSchema(Interface):
+            arg = Text()
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        SUBNAME = 'sub'
+        _handler = object()
+        context = self._makeContext(None, NS, NAME, ISchema, _handler, IUsedIn)
+        context.info = 'INFO'
+        self._callFUT(context, SUBNAME, ISubSchema)
+        self.assertEqual(len(context._documented), 1)
+        fqn, schema, usedIn, handler, info, ctx = context._documented[0]
+        self.assertEqual(fqn, (NS, SUBNAME))
+        self.assertEqual(schema, ISubSchema)
+        self.assertEqual(usedIn, IUsedIn)
+        self.assertEqual(handler, _handler)
+        self.assertEqual(info, 'INFO')
+        self.assertEqual(ctx, context.context)
+        self.assertEqual(context.context[SUBNAME], (ISubSchema, 'INFO'))
+
+    def test_w_handler_attribute(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISubSchema(Interface):
+            arg = Text()
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        class Handler(object):
+            sub = object()
+        NS = 'http://namespace.example.com/'
+        NAME = 'testing'
+        SUBNAME = 'sub'
+        handler = Handler()
+        context = self._makeContext(None, NS, NAME, ISchema, handler, IUsedIn)
+        context.info = 'INFO'
+        self._callFUT(context, SUBNAME, ISubSchema)
+        self.assertEqual(len(context._documented), 1)
+        fqn, schema, usedIn, handler, info, ctx = context._documented[0]
+        self.assertEqual(fqn, (NS, SUBNAME))
+        self.assertEqual(schema, ISubSchema)
+        self.assertEqual(usedIn, IUsedIn)
+        self.assertEqual(handler, Handler.sub)
+        self.assertEqual(info, 'INFO')
+        self.assertEqual(ctx, context.context)
+        self.assertEqual(context.context[SUBNAME], (ISubSchema, 'INFO'))
+
+
+class Test_provides(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import provides
+        return provides(*args, **kw)
+
+    def test_w_multiple(self):
+        context = FauxContext()
+        self.assertRaises(ValueError, self._callFUT, context, 'one two')
+
+    def test_w_single(self):
+        _provided = []
+        def _provideFeature(feature):
+            _provided.append(feature)
+        context = FauxContext()
+        context.provideFeature = _provideFeature
+        self._callFUT(context, 'one')
+        self.assertEqual(_provided, ['one'])
+
+
+class Test_toargs(_Catchable, unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import toargs
+        return toargs(*args, **kw)
+
+    def test_w_empty_schema_no_data(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        context = FauxContext()
+        self.assertEqual(self._callFUT(context, ISchema, {}), {})
+
+    def test_w_empty_schema_w_data_no_kwargs_allowed(self):
+        from zope.configuration.exceptions import ConfigurationError
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        context = FauxContext()
+        exc = self.assertRaises(ConfigurationError,
+                                self._callFUT, context, ISchema, {'a': 'b'})
+        self.assertEqual(exc.args, ('Unrecognized parameters:', 'a'))
+
+    def test_w_empty_schema_w_data_w_kwargs_allowed(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        ISchema.setTaggedValue('keyword_arguments', True)
+        context = FauxContext()
+        self.assertEqual(self._callFUT(context, ISchema, {'a': 'b'}),
+                         {'a': 'b'})
+
+    def test_w_keyword_sub(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        class ISchema(Interface):
+            for_ = Text()
+        context = FauxContext()
+        self.assertEqual(self._callFUT(context, ISchema, {'for': 'foo'}),
+                         {'for_': 'foo'})
+
+    def test_w_field_missing_no_default(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        from zope.configuration.exceptions import ConfigurationError
+        class ISchema(Interface):
+            no_default = Text()
+        context = FauxContext()
+        exc = self.assertRaises(ConfigurationError,
+                                self._callFUT, context, ISchema, {})
+        self.assertEqual(exc.args, ('Missing parameter:', 'no_default'))
+
+    def test_w_field_missing_but_default(self):
+        from zope.interface import Interface
+        from zope.schema import Text
+        from zope.configuration._compat import u
+        class ISchema(Interface):
+            w_default = Text(default=u('default'))
+        context = FauxContext()
+        self.assertEqual(self._callFUT(context, ISchema, {}),
+                         {'w_default': 'default'})
+
+    def test_w_invalid_value(self):
+        from zope.interface import Interface
+        from zope.schema import Int
+        from zope.configuration.exceptions import ConfigurationError
+        class ISchema(Interface):
+            count = Int(min=0)
+        context = FauxContext()
+        exc = self.assertRaises(ConfigurationError,
+                               self._callFUT, context, ISchema, {'count': '-1'})
+        self.assertEqual(exc.args, ('Invalid value for', 'count', '(-1, 0)'))
+
+
+class Test_expand_action(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import expand_action
+        return expand_action(*args, **kw)
+
+    def test_defaults(self):
+        self.assertEqual(self._callFUT(('a', 1, None)),
+                         {'discriminator': ('a', 1, None),
+                          'callable': None,
+                          'args': (),
+                          'kw': {},
+                          'includepath': (),
+                          'info': None,
+                          'order': 0,
+                         })
+
+    def test_explicit_no_extra(self):
+        def _callable():
+            pass
+        self.assertEqual(self._callFUT(('a', 1, None),
+                                       _callable, ('b', 2), {'c': None},
+                                       ('p', 'q/r'), 'INFO', 42,
+                                      ),
+                         {'discriminator': ('a', 1, None),
+                          'callable': _callable,
+                          'args': ('b', 2),
+                          'kw': {'c': None},
+                          'includepath': ('p', 'q/r'),
+                          'info': 'INFO',
+                          'order': 42,
+                         })
+
+    def test_explicit_w_extra(self):
+        def _callable():
+            pass
+        self.assertEqual(self._callFUT(('a', 1, None),
+                                       _callable, ('b', 2), {'c': None},
+                                       ('p', 'q/r'), 'INFO', 42,
+                                       foo='bar', baz=None,
+                                      ),
+                         {'discriminator': ('a', 1, None),
+                          'callable': _callable,
+                          'args': ('b', 2),
+                          'kw': {'c': None},
+                          'includepath': ('p', 'q/r'),
+                          'info': 'INFO',
+                          'order': 42,
+                          'foo': 'bar',
+                          'baz': None,
+                         })
+
+
+class Test_resolveConflicts(_Catchable, unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.config import resolveConflicts
+        return resolveConflicts(*args, **kw)
+
+    def test_empty(self):
+        self.assertEqual(self._callFUT(()), [])
+
+    def test_expands_oldstyle_actions(self):
+        def _callable():
+            pass
+        self.assertEqual(self._callFUT([(None, _callable)]),
+                                       [{'discriminator': None,
+                                         'callable': _callable,
+                                         'args': (),
+                                         'kw': {},
+                                         'includepath': (),
+                                         'info': None,
+                                         'order': 0,
+                                        }])
+
+    def test_wo_discriminator_clash(self):
+        from zope.configuration.config import expand_action
+        def _a():
+            pass
+        def _b():
+            pass
+        def _c():
+            pass
+        def _d():
+            pass
+        actions = [expand_action(('a', 1), _a, order=3),
+                   expand_action(('b', 2), _b, order=1),
+                   expand_action(('c', 3), _c, order=2),
+                   expand_action(('d', 4), _d, order=1),
+                  ]
+        self.assertEqual([x['callable'] for x in self._callFUT(actions)],
+                         [_b, _d, _c, _a])
+
+    def test_w_resolvable_discriminator_clash(self):
+        from zope.configuration.config import expand_action
+        def _a():
+            pass
+        def _b():
+            pass
+        actions = [expand_action(('a', 1), _a, includepath=('a',)),
+                   expand_action(('a', 1), _b, includepath=('a', 'b')),
+                  ]
+        self.assertEqual([x['callable'] for x in self._callFUT(actions)],
+                         [_a])
+
+    def test_w_non_resolvable_discriminator_clash_different_paths(self):
+        from zope.configuration.config import ConfigurationConflictError
+        from zope.configuration.config import expand_action
+        def _a():
+            pass
+        def _b():
+            pass
+        actions = [expand_action(('a', 1), _a, includepath=('b','c'), info='X'),
+                   expand_action(('a', 1), _b, includepath=('a',), info='Y'),
+                  ]
+        exc = self.assertRaises(ConfigurationConflictError,
+                                self._callFUT, actions)
+        self.assertEqual(exc._conflicts, {('a', 1): ['Y', 'X']})
+
+    def test_w_non_resolvable_discriminator_clash_same_path(self):
+        from zope.configuration.config import ConfigurationConflictError
+        from zope.configuration.config import expand_action
+        def _a():
+            pass
+        def _b():
+            pass
+        actions = [expand_action(('a', 1), _a, includepath=('a',), info='X'),
+                   expand_action(('a', 1), _b, includepath=('a',), info='Y'),
+                  ]
+        exc = self.assertRaises(ConfigurationConflictError,
+                                self._callFUT, actions)
+        self.assertEqual(exc._conflicts, {('a', 1): ['X', 'Y']})
+
+    def test_wo_discriminators_final_sorting_order(self):
+        from zope.configuration.config import expand_action
+        def _a():
+            pass
+        def _b():
+            pass
+        def _c():
+            pass
+        def _d():
+            pass
+        actions = [expand_action(None, _a, order=3),
+                   expand_action(None, _b, order=1),
+                   expand_action(None, _c, order=2),
+                   expand_action(None, _d, order=1),
+                  ]
+        self.assertEqual([x['callable'] for x in self._callFUT(actions)],
+                         [_b, _d, _c, _a])
+
+
+class FauxContext(object):
+    def __init__(self):
+        self.actions = []
+    def action(self, **kw):
+        self.actions.append(kw)
+
 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()
+        unittest.makeSuite(ConfigurationContextTests),
+        unittest.makeSuite(ConfigurationAdapterRegistryTests),
+        unittest.makeSuite(ConfigurationMachineTests),
+        unittest.makeSuite(SimpleStackItemTests),
+        unittest.makeSuite(RootStackItemTests),
+        unittest.makeSuite(GroupingStackItemTests),
+        unittest.makeSuite(ComplexStackItemTests),
+        unittest.makeSuite(GroupingContextDecoratorTests),
+        unittest.makeSuite(DirectivesHandlerTests),
+        unittest.makeSuite(Test_defineSimpleDirective),
+        unittest.makeSuite(Test_defineGroupingDirective),
+        unittest.makeSuite(ComplexDirectiveDefinitionTests),
+        unittest.makeSuite(Test_subdirective),
+        unittest.makeSuite(Test_provides),
+        unittest.makeSuite(Test_toargs),
+        unittest.makeSuite(Test_expand_action),
+        unittest.makeSuite(Test_resolveConflicts),
+    ))

Modified: zope.configuration/trunk/src/zope/configuration/tests/test_docutils.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_docutils.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_docutils.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -11,14 +11,120 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Doc Tests for for zope.configuration.docutils
+"""Tests for for zope.configuration.docutils
 """
 import unittest
-from doctest import DocTestSuite
 
+
+class Test_wrap(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.docutils import wrap
+        return wrap(*args, **kw)
+
+    def test_empty(self):
+        self.assertEqual(self._callFUT(''), '\n\n')
+
+    def test_only_whitespace(self):
+        self.assertEqual(self._callFUT(' \t\n\r'), '\n\n')
+
+    def test_single_paragraphs(self):
+        self.assertEqual(
+                self._callFUT('abcde fghij klmno pqrst uvwxy', 10, 3),
+                '   abcde\n   fghij\n   klmno\n   pqrst\n   uvwxy\n\n')
+
+    def test_multiple_paragraphs(self):
+        self.assertEqual(
+                self._callFUT('abcde fghij klmno\n\npqrst uvwxy', 10, 3),
+                '   abcde\n   fghij\n   klmno\n\n   pqrst\n   uvwxy\n\n')
+
+
+class Test_makeDocStructures(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.docutils import makeDocStructures
+        return makeDocStructures(*args, **kw)
+
+    def _makeContext(self):
+        class _Context(object):
+            def __init__(self):
+                self._docRegistry = []
+        return _Context()
+
+    def test_empty(self):
+        context = self._makeContext()
+        namespaces, subdirs = self._callFUT(context)
+        self.assertEqual(len(namespaces), 0)
+        self.assertEqual(len(subdirs), 0)
+
+    def test_wo_parents(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        NS = 'http://namespace.example.com/main'
+        NS2 = 'http://namespace.example.com/other'
+        def _one():
+            pass
+        def _two():
+            pass
+        def _three():
+            pass
+        context = self._makeContext()
+        context._docRegistry.append(
+                    ((NS, 'one'), ISchema, IUsedIn, _one, 'ONE', None))
+        context._docRegistry.append(
+                    ((NS2, 'two'), ISchema, IUsedIn, _two, 'TWO', None))
+        context._docRegistry.append(
+                    ((NS, 'three'), ISchema, IUsedIn, _three, 'THREE', None))
+        namespaces, subdirs = self._callFUT(context)
+        self.assertEqual(len(namespaces), 2)
+        self.assertEqual(namespaces[NS], {'one': (ISchema, _one, 'ONE'),
+                                          'three': (ISchema, _three, 'THREE')})
+        self.assertEqual(namespaces[NS2], {'two': (ISchema, _two, 'TWO')})
+        self.assertEqual(len(subdirs), 0)
+
+    def test_w_parents(self):
+        from zope.interface import Interface
+        class ISchema(Interface):
+            pass
+        class IUsedIn(Interface):
+            pass
+        PNS = 'http://namespace.example.com/parent'
+        NS = 'http://namespace.example.com/main'
+        NS2 = 'http://namespace.example.com/other'
+        def _one():
+            pass
+        def _two():
+            pass
+        def _three():
+            pass
+        class Parent(object):
+            namespace = PNS
+            name = 'parent'
+        parent1 = Parent()
+        parent2 = Parent()
+        parent2.name = 'parent2'
+        context = self._makeContext()
+        context._docRegistry.append(
+                    ((NS, 'one'), ISchema, IUsedIn, _one, 'ONE', parent1))
+        context._docRegistry.append(
+                    ((NS2, 'two'), ISchema, IUsedIn, _two, 'TWO', parent2))
+        context._docRegistry.append(
+                    ((NS, 'three'), ISchema, IUsedIn, _three, 'THREE', parent1))
+        namespaces, subdirs = self._callFUT(context)
+        self.assertEqual(len(namespaces), 0)
+        self.assertEqual(len(subdirs), 2)
+        self.assertEqual(subdirs[(PNS, 'parent')],
+                         [(NS, 'one', ISchema, _one, 'ONE'),
+                          (NS, 'three', ISchema, _three, 'THREE')])
+        self.assertEqual(subdirs[(PNS, 'parent2')],
+                         [(NS2, 'two', ISchema, _two, 'TWO')])
+
+
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.configuration.docutils'),
-        ))
-
-if __name__ == '__main__': unittest.main()
+        unittest.makeSuite(Test_wrap),
+        unittest.makeSuite(Test_makeDocStructures),
+    ))

Copied: zope.configuration/trunk/src/zope/configuration/tests/test_fields.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_fields.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_fields.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_fields.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,314 @@
+##############################################################################
+#
+# Copyright (c) 20!2 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 zope.configuration.fields.
+"""
+import unittest
+
+
+class _ConformsToIFromUnicode(object):
+
+    def test_class_conforms_to_IFromUnicode(self):
+        from zope.interface.verify import verifyClass
+        from zope.schema.interfaces import IFromUnicode
+        verifyClass(IFromUnicode, self._getTargetClass())
+
+    def test_instance_conforms_to_IFromUnicode(self):
+        from zope.interface.verify import verifyObject
+        from zope.schema.interfaces import IFromUnicode
+        verifyObject(IFromUnicode, self._makeOne())
+
+
+class PythonIdentifierTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import PythonIdentifier
+        return PythonIdentifier
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_fromUnicode_empty(self):
+        pi = self._makeOne()
+        self.assertEqual(pi.fromUnicode(''), '')
+
+    def test_fromUnicode_normal(self):
+        pi = self._makeOne()
+        self.assertEqual(pi.fromUnicode('normal'), 'normal')
+
+    def test_fromUnicode_strips_ws(self):
+        pi = self._makeOne()
+        self.assertEqual(pi.fromUnicode('   '), '')
+        self.assertEqual(pi.fromUnicode(' normal  '), 'normal')
+
+    def test__validate_miss(self):
+        from zope.schema import ValidationError
+        from zope.configuration._compat import u
+        pi = self._makeOne()
+        self.assertRaises(ValidationError,
+                          pi._validate, u('not-an-identifier'))
+
+    def test__validate_hit(self):
+        from zope.configuration._compat import u
+        pi = self._makeOne()
+        pi._validate(u('is_an_identifier'))
+
+
+class GlobalObjectTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import GlobalObject
+        return GlobalObject
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test__validate_wo_value_type(self):
+        from zope.configuration._compat import u
+        from zope.configuration._compat import b
+        go = self._makeOne(value_type=None)
+        for value in [0, 0.0, (), [], set(), frozenset(), u(''), b('')]:
+            go._validate(value) #noraise
+
+    def test__validate_w_value_type(self):
+        from zope.schema import Text
+        from zope.schema.interfaces import WrongType
+        from zope.configuration._compat import u
+        from zope.configuration._compat import b
+        go = self._makeOne(value_type=Text())
+        go.validate(u(''))
+        for value in [0, 0.0, (), [], set(), frozenset(), b('')]:
+            self.assertRaises(WrongType, go._validate, value)
+
+    def test_fromUnicode_w_star_and_extra_ws(self):
+        go = self._makeOne()
+        self.assertEqual(go.fromUnicode(' * '), None)
+
+    def test_fromUnicode_w_resolve_fails(self):
+        from zope.schema import ValidationError
+        from zope.configuration.config import ConfigurationError
+        class Context(object):
+            def resolve(self, name):
+                self._resolved = name
+                raise ConfigurationError()
+        go = self._makeOne()
+        context = Context()
+        bound = go.bind(context)
+        self.assertRaises(ValidationError, bound.fromUnicode, 'tried')
+        self.assertEqual(context._resolved, 'tried')
+
+    def test_fromUnicode_w_resolve_success(self):
+        _target = object()
+        class Context(object):
+            def resolve(self, name):
+                self._resolved = name
+                return _target
+        go = self._makeOne()
+        context = Context()
+        bound = go.bind(context)
+        found = bound.fromUnicode('tried')
+        self.assertTrue(found is _target)
+        self.assertEqual(context._resolved, 'tried')
+
+    def test_fromUnicode_w_resolve_but_validation_fails(self):
+        from zope.schema import Text
+        from zope.schema import ValidationError
+        _target = object()
+        class Context(object):
+            def resolve(self, name):
+                self._resolved = name
+                return _target
+        go = self._makeOne(value_type=Text())
+        context = Context()
+        bound = go.bind(context)
+        self.assertRaises(ValidationError, bound.fromUnicode, 'tried')
+        self.assertEqual(context._resolved, 'tried')
+
+
+class GlobalInterfaceTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import GlobalInterface
+        return GlobalInterface
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_ctor(self):
+        from zope.schema import InterfaceField
+        gi = self._makeOne()
+        self.assertTrue(isinstance(gi.value_type, InterfaceField))
+
+class TokensTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import Tokens
+        return Tokens
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_fromUnicode_empty(self):
+        tok = self._makeOne()
+        self.assertEqual(tok.fromUnicode(''), [])
+
+    def test_fromUnicode_strips_ws(self):
+        from zope.schema import Text
+        from zope.configuration._compat import u
+        tok = self._makeOne(value_type=Text())
+        context = object()
+        self.assertEqual(tok.fromUnicode(u(' one two three ')),
+                         [u('one'), u('two'), u('three')])
+
+    def test_fromUnicode_invalid(self):
+        from zope.schema import Int
+        from zope.configuration.interfaces import InvalidToken
+        from zope.configuration._compat import u
+        tok = self._makeOne(value_type=Int(min=0))
+        context = object()
+        self.assertRaises(InvalidToken,
+                          tok.fromUnicode, u(' 1 -1 3 '))
+
+
+class PathTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import Path
+        return Path
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_fromUnicode_absolute(self):
+        path = self._makeOne()
+        self.assertEqual(path.fromUnicode('/'), '/')
+
+    def test_fromUnicode_relative(self):
+        class Context(object):
+            def path(self, value):
+                self._pathed = value
+                return '/hard/coded'
+        context = Context()
+        path = self._makeOne()
+        bound = path.bind(context)
+        self.assertEqual(bound.fromUnicode('relative/path'), '/hard/coded')
+        self.assertEqual(context._pathed, 'relative/path')
+
+
+class BoolTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import Bool
+        return Bool
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_fromUnicode_w_true_values(self):
+        values = ['1', 'true', 'yes', 't', 'y']
+        values += [x.upper() for x in values]
+        bo = self._makeOne()
+        for value in values:
+            self.assertEqual(bo.fromUnicode(value), True)
+
+    def test_fromUnicode_w_false_values(self):
+        values = ['0', 'false', 'no', 'f', 'n']
+        values += [x.upper() for x in values]
+        bo = self._makeOne()
+        for value in values:
+            self.assertEqual(bo.fromUnicode(value), False)
+
+    def test_fromUnicode_w_invalid(self):
+        from zope.schema import ValidationError
+        bo = self._makeOne()
+        self.assertRaises(ValidationError, bo.fromUnicode, 'notvalid')
+
+
+class MessageIDTests(unittest.TestCase, _ConformsToIFromUnicode):
+
+    def _getTargetClass(self):
+        from zope.configuration.fields import MessageID
+        return MessageID
+    
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def _makeContext(self, domain='testing_domain'):
+        class Info(object):
+            file = 'test_file'
+            line = 42
+        class Context(object):
+            i18n_domain = domain
+            def __init__(self):
+                self.i18n_strings = {}
+                self.info = Info()
+        return Context()
+
+    def test_wo_domain(self):
+        import warnings
+        from zope.configuration._compat import u
+        mid = self._makeOne()
+        context = self._makeContext(None)
+        bound = mid.bind(context)
+        with warnings.catch_warnings(record=True) as log:
+            msgid = bound.fromUnicode(u('testing'))
+        self.assertEqual(len(log), 1)
+        self.assertTrue(str(log[0].message).startswith(
+                            'You did not specify an i18n translation domain'))
+        self.assertEqual(msgid, 'testing')
+        self.assertEqual(msgid.default, None)
+        self.assertEqual(msgid.domain, 'untranslated')
+        self.assertEqual(context.i18n_strings,
+                         {'untranslated': {'testing': [('test_file', 42)]}})
+
+    def test_w_empty_id(self):
+        import warnings
+        from zope.configuration._compat import u
+        mid = self._makeOne()
+        context = self._makeContext()
+        bound = mid.bind(context)
+        with warnings.catch_warnings(record=True) as log:
+            msgid = bound.fromUnicode(u('[] testing'))
+        self.assertEqual(len(log), 0)
+        self.assertEqual(msgid, 'testing')
+        self.assertEqual(msgid.default, None)
+        self.assertEqual(msgid.domain, 'testing_domain')
+        self.assertEqual(context.i18n_strings,
+                         {'testing_domain': {'testing': [('test_file', 42)]}})
+
+    def test_w_id_and_default(self):
+        import warnings
+        from zope.configuration._compat import u
+        mid = self._makeOne()
+        context = self._makeContext()
+        bound = mid.bind(context)
+        with warnings.catch_warnings(record=True) as log:
+            msgid = bound.fromUnicode(u('[testing] default'))
+        self.assertEqual(len(log), 0)
+        self.assertEqual(msgid, 'testing')
+        self.assertEqual(msgid.default, 'default')
+        self.assertEqual(msgid.domain, 'testing_domain')
+        self.assertEqual(context.i18n_strings,
+                         {'testing_domain': {'testing': [('test_file', 42)]}})
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(PythonIdentifierTests),
+        unittest.makeSuite(GlobalObjectTests),
+        unittest.makeSuite(GlobalInterfaceTests),
+        unittest.makeSuite(TokensTests),
+        unittest.makeSuite(PathTests),
+        unittest.makeSuite(BoolTests),
+        unittest.makeSuite(MessageIDTests),
+        ))

Copied: zope.configuration/trunk/src/zope/configuration/tests/test_name.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_name.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_name.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_name.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,134 @@
+##############################################################################
+#
+# Copyright (c) 20!2 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 zope.configuration.name
+"""
+import unittest
+
+
+class Test_resolve(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.name import resolve
+        return resolve(*args, **kw)
+
+    def test_top_level_module(self):
+        import os
+        self.assertTrue(self._callFUT('os') is os)
+
+    def test_nested_module(self):
+        import os.path
+        self.assertTrue(self._callFUT('os.path') is os.path)
+
+    def test_function_in_module(self):
+        import os.path
+        self.assertTrue(self._callFUT('os.path.join') is os.path.join)
+
+    def test_importable_but_not_attr_of_parent(self):
+        import sys
+        import zope.configuration.tests as zct
+        self.assertFalse('notyet' in zct.__dict__)
+        mod = self._callFUT('zope.configuration.tests.notyet')
+        self.assertTrue(mod is zct.notyet)
+        del zct.notyet
+        del sys.modules['zope.configuration.tests.notyet']
+
+    def test_function_in_module_relative(self):
+        import os.path
+        self.assertTrue(self._callFUT('.join', 'os.path') is os.path.join)
+
+    def test_class_in_module(self):
+        from zope.configuration.tests.directives import Complex
+        self.assertTrue(
+            self._callFUT('zope.configuration.tests.directives.Complex')
+                    is Complex)
+
+    def test_class_w_same_name_as_module(self):
+        from zope.configuration.tests.samplepackage.NamedForClass \
+            import NamedForClass
+        self.assertTrue(
+            self._callFUT(
+                'zope.configuration.tests.samplepackage.NamedForClass+')
+                    is NamedForClass)
+        self.assertTrue(
+            self._callFUT(
+                'zope.configuration.tests.samplepackage.NamedForClass.')
+                    is NamedForClass)
+
+class Test_getNormalizedName(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.name import getNormalizedName
+        return getNormalizedName(*args, **kw)
+
+    def test_no_dots(self):
+        self.assertEqual(self._callFUT('os', None), 'os')
+
+    def test_one_dot(self):
+        self.assertEqual(self._callFUT('os.path', None), 'os.path')
+
+    def test_two_dots(self):
+        self.assertEqual(self._callFUT('os.path.join', None), 'os.path.join')
+
+    def test_relative(self):
+        self.assertEqual(self._callFUT('.join', 'os.path'), 'os.path.join')
+
+    def test_repeat_plus(self):
+        self.assertEqual(
+            self._callFUT('zope.configuration.tests.NamedForClass+', None),
+            'zope.configuration.tests.NamedForClass+')
+
+    def test_repeat_dot(self):
+        self.assertEqual(
+            self._callFUT('zope.configuration.tests.NamedForClass.', None),
+            'zope.configuration.tests.NamedForClass+')
+
+    def test_repeat_inferred(self):
+        self.assertEqual(
+            self._callFUT(
+                'zope.configuration.tests.NamedForClass.NamedForClass', None),
+            'zope.configuration.tests.NamedForClass+')
+
+
+class Test_path(unittest.TestCase):
+
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.name import path
+        return path(*args, **kw)
+
+    def test_absolute(self):
+        self.assertEqual(self._callFUT('/absolute'), '/absolute')
+
+    def test_relative_bogus_package(self):
+        self.assertRaises(ImportError,
+                          self._callFUT, '', 'no.such.package.exists')
+
+    def test_relative_empty(self):
+        import os
+        self.assertEqual(self._callFUT('', 'zope.configuration.tests'),
+                         os.path.dirname(__file__))
+
+    def test_relative_w_file(self):
+        import os
+        self.assertEqual(
+            self._callFUT('configure.zcml', 'zope.configuration.tests'),
+            os.path.join(os.path.dirname(__file__), 'configure.zcml'))
+
+
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(Test_resolve),
+        unittest.makeSuite(Test_resolve),
+        unittest.makeSuite(Test_path),
+    ))

Deleted: zope.configuration/trunk/src/zope/configuration/tests/test_nested.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_nested.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_nested.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -1,319 +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.
-#
-##############################################################################
-r"""Creating nested directives
-
-When using ZCML, you sometimes nest ZCML directives. This is typically
-done either to:
-
-- Avoid repetative input.  Information shared among multiple
-  directives is provided in a surrounding directive.
-
-- Put together information that is too complex or structured to express
-  with a single set of directive parameters.
-
-Grouping directives are used to handle both of these cases.  See the
-documentation in ``../zopeconfigure.py``. This file describes the
-implementation of the zope ``configure`` directive, which groups
-directives that use a common package or internationalization domain.
-The documentation in ``../zopeconfigure.py`` provides background for
-the documentation here.  You should also have read the documentation
-in ``test_simple.py``, which documents how to create simple
-directives.
-
-This file shows you how to handle the second case above. In this case,
-we have grouping directives that are meant to collaborate with
-specific contained directives.  To do this, you have the grouping
-directives declare a more specific (or alternate) interface to
-``IConfigurationContext``. Directives designed to work with those
-grouping directives are registered for the new interface.
-
-Let's look at example. Suppose we wanted to be able to define schema
-using ZCML.  We'd use a grouping directive to specify schemas and
-contained directives to specify fields within the schema.  We'll use a
-schema registry to hold the defined schemas.
-
-A schema has a name, an id, some documentation, and some fields.
-We'll provide the name and the id as parameters. We'll define fields
-as subdirectives and documentation as text contained in the schema
-directive.  The schema directive uses the schema, ``ISchemaInfo`` for
-it's parameters.
-
-We also define the schema, ISchema, that specifies an attribute that
-nested field directives will use to store the fields they define.
-
-The class, ``Schema``, provides the handler for the schema directive. (If
-you haven't read the documentation in ``zopeconfigure.py``, you need
-to do so now.)  The constructor saves its arguments as attributes
-and initializes its ``fields`` attribute.
-
-The ``after`` method of the ``Schema`` class creates a schema and
-computes an action to register the schema in the schema registry.  The
-discriminator prevents two schema directives from registering the same
-schema.
-
-It's important to note that when we call the ``action`` method on
-``self``, rather than on ``self.context``.  This is because, in a
-grouping directive handler, the handler instance is itself a context.
-When we call the ``action`` method, the method stores additional meta
-data associated with the context it was called on. This meta data
-includes an include path, used when resolving conflicting actions,
-and an object that contains information about the XML source used
-to invole the directive. If we called the action method on
-``self.context``, the wrong meta data would be associated with the
-configuration action.
-
-The file ``schema.zcml`` contains the meta-configuration directive
-that defines the schema directive.
-
-To define fields, we'll create directives to define the fields.
-Let's start with a ``text`` field.  ``ITextField`` defines the schema for
-text field parameters. It extends ``IFieldInfo``, which defines data
-common to all fields.  We also define a simple handler method,
-textField, that takes a context and keyword arguments. (For
-information on writing simple directives, see ``test_simple.py``.)
-We've abstracted most of the logic into the function ``field``.
-
-The ``field`` function computes a field instance using the
-constructor, and the keyword arguments passed to it.  It also uses the
-context information object to get the text content of the directive,
-which it uses for the field description.
-
-After computing the field instance, it gets the ``Schema`` instance,
-which is the context of the context passed to the function. The
-function checks to see if there is already a field with that name. If
-there is, it raises an error. Otherwise, it saves the field. 
-
-We also define an ``IIntInfo`` schema and ``intField`` handler
-function to support defining integer fields. 
-
-We register the ``text`` and ``int`` directives in ``schema.zcml``.
-These are like the simple directive definition we saw in
-``test_simple.py`` with an important exception.  We provide a
-``usedIn`` parameter to say that these directives can *only* ne used
-in a ``ISchema`` context. In other words, these can only be used
-inside of ``schema`` directives.
-
-The ``schema.zcml`` file also contains some sample ``schema``
-directives.  We can execute the file:
-
->>> from zope.configuration import tests
->>> context = xmlconfig.file("schema.zcml", tests)
-
-And verify that the schema registery has the schemas we expect:
-
->>> from pprint import PrettyPrinter
->>> pprint=PrettyPrinter(width=70).pprint
->>> pprint(list(schema_registry))
-['zope.configuration.tests.test_nested.I1',
- 'zope.configuration.tests.test_nested.I2']
-
->>> def sorted(x):
-...     r = list(x)
-...     r.sort()
-...     return r
-
->>> i1 = schema_registry['zope.configuration.tests.test_nested.I1']
->>> sorted(i1)
-['a', 'b']
->>> i1['a'].__class__.__name__
-'Text'
->>> i1['a'].description.strip()
-u'A\n\n          Blah blah'
->>> i1['a'].min_length
-1
->>> i1['b'].__class__.__name__
-'Int'
->>> i1['b'].description.strip()
-u'B\n\n          Not feeling very creative'
->>> i1['b'].min
-1
->>> i1['b'].max
-10
-
->>> i2 = schema_registry['zope.configuration.tests.test_nested.I2']
->>> sorted(i2)
-['x', 'y']
-
-
-Now let's look at some error situations. For example, let's see what
-happens if we use a field directive outside of a schema dorective.
-(Note that we used the context we created above, so we don't have to
-redefine our directives:
-
->>> try:
-...    v = xmlconfig.string(
-...      '<text xmlns="http://sample.namespaces.zope.org/schema" name="x" />',
-...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
-File "<string>", line 1.0
-    ConfigurationError: The directive """ \
-        """(u'http://sample.namespaces.zope.org/schema', u'text') """ \
-        """cannot be used in this context
-
-Let's see what happens if we declare duplicate fields:
-
->>> try:
-...    v = xmlconfig.string(
-...      '''
-...      <schema name="I3" id="zope.configuration.tests.test_nested.I3"
-...              xmlns="http://sample.namespaces.zope.org/schema">
-...        <text name="x" />
-...        <text name="x" />
-...      </schema>
-...      ''',
-...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
-File "<string>", line 5.7-5.24
-    ValueError: ('Duplicate field', 'x')
-"""
-
-import unittest
-from doctest import DocTestSuite
-from zope import interface, schema
-from zope.configuration import config, xmlconfig, fields
-
-
-schema_registry = {}
-
-class ISchemaInfo(interface.Interface):
-    """Parameter schema for the schema directive
-    """
-
-    name = schema.TextLine(
-        title=u"The schema name",
-        description=u"This is a descriptive name for the schema."
-        )
-
-    id = schema.Id(
-        title=u"The unique id for the schema"
-        )
-
-class ISchema(interface.Interface):
-    """Interface that distinguishes the schema directive
-    """
-
-    fields = interface.Attribute("Dictionary of field definitions"
-        )
-    
-class Schema(config.GroupingContextDecorator):
-    """Handle schema directives
-    """
-
-    interface.implements(config.IConfigurationContext, ISchema)
-
-    def __init__(self, context, name, id):
-        self.context, self.name, self.id = context, name, id
-        self.fields = {}
-
-    def after(self):
-        schema = interface.Interface.__class__(
-            self.name,
-            (interface.Interface, ),
-            self.fields
-            )
-        schema.__doc__ = self.info.text.strip()
-        self.action(
-            discriminator=('schema', self.id),
-            callable=schema_registry.__setitem__,
-            args=(self.id, schema),
-            )
-        
-
-class IFieldInfo(interface.Interface):
-
-    name = schema.BytesLine(
-        title=u"The field name"
-        )
-
-    title = schema.TextLine(
-        title=u"Title",
-        description=u"A short summary or label",
-        default=u"",
-        required=False,
-        )
-
-    required = fields.Bool(
-        title=u"Required",
-        description=u"Determines whether a value is required.",
-        default=True)
-
-    readonly = fields.Bool(
-        title=u"Read Only",
-        description=u"Can the value be modified?",
-        required=False,
-        default=False)
-
-class ITextInfo(IFieldInfo):
-
-    min_length = schema.Int(
-        title=u"Minimum length",
-        description=u"Value after whitespace processing cannot have less than "
-                    u"min_length characters. If min_length is None, there is "
-                    u"no minimum.",
-        required=False,
-        min=0, # needs to be a positive number
-        default=0)
-
-    max_length = schema.Int(
-        title=u"Maximum length",
-        description=u"Value after whitespace processing cannot have greater "
-                    u"or equal than max_length characters. If max_length is "
-                    u"None, there is no maximum.",
-        required=False,
-        min=0, # needs to be a positive number
-        default=None)
-
-def field(context, constructor, name, **kw):
-
-    # Compute the field
-    field = constructor(description=context.info.text.strip(), **kw)
-
-    # Save it in the schema's field dictionary
-    schema = context.context
-    if name in schema.fields:
-        raise ValueError("Duplicate field", name)
-    schema.fields[name] = field
-
-    
-def textField(context, **kw):
-    field(context, schema.Text, **kw)
-
-class IIntInfo(IFieldInfo):
-
-    min = schema.Int(
-        title=u"Start of the range",
-        required=False,
-        default=None
-        )
-
-    max = schema.Int(
-        title=u"End of the range (excluding the value itself)",
-        required=False,
-        default=None
-        )
-    
-def intField(context, **kw):
-    field(context, schema.Int, **kw)
-    
-
-def test_suite():
-    return unittest.TestSuite((
-        DocTestSuite(),
-        ))
-
-if __name__ == '__main__': unittest.main()

Deleted: zope.configuration/trunk/src/zope/configuration/tests/test_simple.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_simple.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_simple.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -1,208 +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.
-#
-##############################################################################
-r"""How to write a simple directive
-
-This module documents how to write a simple directive.
-
-A simple directive is a directive that doesn't contain other
-directives. It can be implemented via a fairly simple function.
-To implement a simple directive, you need to do 3 things:
-
-- You need to create a schema to describe the directive parameters,
-
-- You need to write a directive handler, and
-
-- You need to register the directive.
-
-In this module, we'll implement a contrived example that records
-information about files in a file registry. The file registry is just
-the list, ``file_registry``. Our registry will contain tuples
-with:
-
-  - file path
-
-  - file title
-
-  - description
-
-  - Information about where the file was defined
-
-Our schema is defined in IRegisterFile (see below). Our schema lists
-the path and title.  We'll get the description and other information
-for free, as we'll see later.  The title is not required, and may be
-ommmitted.
-
-The job of a configuration handler is to compute one or more
-configuration actions.  Configuration actions are defered function
-calls. The handler doesn't perform the actions. It just computes
-actions, which may be performed later if they are not overridden by
-other directives.
-
-Out handler is given in the function, ``registerFile``. It takes a context,
-a path and a title. All directive handlers take the directive context
-as the first argument.  A directive context, at a minimim, implements,
-``zope.configuration.IConfigurationContext``.  (Specialized contexts
-can implement more specific interfaces. We'll say more about that when
-we talk about grouping directives.)  The title argument
-must have a default value, because we indicated that the title was not
-required in the schema. (Alternatively, we could have made the title
-required, but provided a default value in the schema.
-
-In the first line of function `registerFile`, we get the context information
-object. This object contains information about the configuration
-directive, such as the file and location within the file of the
-directive.
-
-The context information object also has a text attribute that contains
-the textual data contained by the configuration directive. (This is
-the concatenation of all of the xml text nodes directly contained by
-the directive.)  We use this for our description in the second line
-of the handler.
-
-The last thing the handler does is to compute an action by calling the
-action method of the context.  It passes the action method 3 keyword
-arguments:
-
-- discriminator
-
-  The discriminator is used to identify the action to be performed so
-  that duplicate actions can be detected.  Two actions are duplicated,
-  and this conflict, if they have the same discriminator values and
-  the values are not ``None``.  Conflicting actions can be resolved if
-  one of the conflicting actions is from a configuration file that
-  directly or indirectly includes the files containing the other
-  conflicting actions.
-
-  In function ``registerFile``, we a tuple with the string
-  ``'RegisterFile'`` and the path to be registered.
-
-- callable
-
-  The callable is the object to be called to perform the action.
-
-- args
-
-  The args argument contains positinal arguments to be passed to the
-  callable. In function ``registerFile``, we pass a tuple containing a
-  ``FileInfo`` object.
-
-  (Note that there's nothing special about the FileInfo class. It has
-   nothing to do with creating simple directives. It's just used in
-   this example to organize the application data.)
-
-
-The final step in implementing the simple directive is to register
-it. We do that with the zcml ``meta:directive`` directive.  This is
-given in the file simple.zcml.  Here we specify the name, namespace,
-schema, and handler for the directive.  We also provide a
-documentation for the directive as text between the start and end
-tags.
-
-The file simple.zcml also includes some directives that use the new
-directive to register some files.
-
-Now let's try it all out:
-
->>> from zope.configuration import tests
->>> context = xmlconfig.file("simple.zcml", tests)
-
-Now we should see some file information in the  registry:
-
->>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths
->>> from zope.configuration.tests.test_xmlconfig import clean_path
->>> for i in file_registry:
-...   print "path:", clean_path(i.path)
-...   print "title:", i.title
-...   print "description:", '\n'.join(
-...               [l.rstrip()
-...                for l in i.description.strip().split('\n')
-...                if l.rstrip()])
-...   print "info:"
-...   print clean_text_w_paths(i.info)
-path: tests/test_simple.py
-title: How to create a simple directive
-description: Describes how to implement a simple directive
-info:
-File "tests/simple.zcml", line 19.2-24.2
-    <files:register
-        path="test_simple.py"
-        title="How to create a simple directive"
-        >
-      Describes how to implement a simple directive
-    </files:register>
-path: tests/simple.zcml
-title: 
-description: Shows the ZCML directives needed to register a simple directive.
-    Also show some usage examples,
-info:
-File "tests/simple.zcml", line 26.2-30.2
-    <files:register path="simple.zcml">
-      Shows the ZCML directives needed to register a simple directive.
-      Also show some usage examples,
-    </files:register>
-path: tests/__init__.py
-title: Make this a package
-description: 
-info:
-File "tests/simple.zcml", line 32.2-32.67
-    <files:register path="__init__.py" title="Make this a package" />
-
-
-We'll clean up after ourselves:
-
->>> del file_registry[:]
-"""
-
-file_registry = []
-
-
-import unittest
-from doctest import DocTestSuite
-from zope import interface
-from zope import schema
-from zope.configuration import fields, xmlconfig
-
-class IRegisterFile(interface.Interface):
-
-    path = fields.Path(
-        title=u"File path",
-        description=u"This is the path name of the file to be registered."
-        )
-
-    title = schema.Text(
-        title=u"Short summary of the file",
-        description=u"This will be used in file listings",
-        required = False
-        )
-
-class FileInfo(object):
-
-    def __init__(self, path, title, description, info):
-        (self.path, self.title, self.description, self.info
-         ) = path, title, description, info
-
-def registerFile(context, path, title=u""):
-    info = context.info
-    description = info.text.strip()
-    context.action(discriminator=('RegisterFile', path),
-                   callable=file_registry.append,
-                   args=(FileInfo(path, title, description, info),)
-                   )
-
-def test_suite():
-    return unittest.TestSuite((
-        DocTestSuite(),
-        ))
-
-if __name__ == '__main__': unittest.main()

Modified: zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_xmlconfig.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -11,626 +11,1165 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Test XML configuration (ZCML) machinery.
+"""Test zope.configuration.xmlconfig.
 """
 import unittest
-import os
-import re
-from doctest import DocTestSuite, DocFileSuite
-from zope.testing import renormalizing
-from zope.configuration import xmlconfig, config
-from zope.configuration.tests.samplepackage import foo
-from pprint import PrettyPrinter, pprint
 
+from zope.configuration._compat import u
 
-class FauxLocator(object):
-  def __init__(self, file, line, column):
-    self.file, self.line, self.column = file, line, column
-  def getSystemId(self):
-    return self.file
-  def getLineNumber(self):
-    return self.line
-  def getColumnNumber(self):
-    return self.column
+NS = u('ns')
+FOO = u('foo')
+XXX = u('xxx')
+SPLAT = u('splat')
+SPLATV = u('splatv')
+A = u('a')
+AVALUE = u('avalue')
+B = u('b')
+BVALUE = u('bvalue')
 
-class FauxContext(object):
 
-  def setInfo(self, info):
-    self.info = info
-  def getInfo(self):
-    return self.info
-  def begin(self, name, data, info):
-    self.begin_args = name, data
-    self.info = info
-  def end(self):
-    self.end_called = 1
+class _Catchable(object):
+    # Mixin for classes which need to make assertions about the exception
+    # instance.
+    def assertRaises(self, excClass, callableObj, *args, **kwargs):
+        # Morph stdlib version to return the raised exception
+        try:
+            callableObj(*args, **kwargs)
+        except excClass as exc:
+            return exc
+        if hasattr(excClass,'__name__'):
+            excName = excClass.__name__
+        else:
+            excName = str(excClass)
+        raise self.failureException("%s not raised" % excName)
 
-def path(*p):
-    return os.path.join(os.path.dirname(__file__), *p)
 
-def test_ConfigurationHandler_normal():
-    """
-    >>> context = FauxContext()
-    >>> locator = FauxLocator('tests//sample.zcml', 1, 1)
-    >>> handler = xmlconfig.ConfigurationHandler(context)
-    >>> handler.setDocumentLocator(locator)
+class ZopeXMLConfigurationErrorTests(unittest.TestCase):
 
-    >>> handler.startElementNS((u"ns", u"foo"), u"foo",
-    ...                        {(u"xxx", u"splat"): u"splatv",
-    ...                         (None, u"a"): u"avalue",
-    ...                         (None, u"b"): u"bvalue",
-    ...                        })
-    >>> context.info
-    File "tests//sample.zcml", line 1.1
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
-    >>> pprint(context.begin_args)
-    ((u'ns', u'foo'),
-     {'a': u'avalue', 'b': u'bvalue'})
-    >>> getattr(context, "end_called", 0)
-    0
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+        return ZopeXMLConfigurationError
 
-    >>> locator.line, locator.column = 7, 16
-    >>> handler.endElementNS((u"ns", u"foo"), u"foo")
-    >>> context.info
-    File "tests//sample.zcml", line 1.1-7.16
-    >>> context.end_called
-    1
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    """
+    def test___str___uses_repr_of_info(self):
+        zxce = self._makeOne('info', Exception, 'value')
+        self.assertEqual(str(zxce), "'info'\n    Exception: value")
 
-def test_ConfigurationHandler_err_start():
-    """
 
-    >>> class FauxContext(FauxContext):
-    ...   def begin(self, *args):
-    ...     raise AttributeError("xxx")
+class ZopeSAXParseExceptionTests(unittest.TestCase):
 
-    >>> context = FauxContext()
-    >>> locator = FauxLocator('tests//sample.zcml', 1, 1)
-    >>> handler = xmlconfig.ConfigurationHandler(context)
-    >>> handler.setDocumentLocator(locator)
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import ZopeSAXParseException
+        return ZopeSAXParseException
 
-    >>> try:
-    ...   v = handler.startElementNS((u"ns", u"foo"), u"foo",
-    ...                              {(u"xxx", u"splat"): u"splatv",
-    ...                               (None, u"a"): u"avalue",
-    ...                               (None, u"b"): u"bvalue",
-    ...                              })
-    ... except xmlconfig.ZopeXMLConfigurationError, v:
-    ...   pass
-    >>> print v
-    File "tests//sample.zcml", line 1.1
-        AttributeError: xxx
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    """
+    def test___str___not_a_sax_error(self):
+        zxce = self._makeOne(Exception('Not a SAX error'))
+        self.assertEqual(str(zxce), "Not a SAX error")
 
-def test_ConfigurationHandler_err_end():
-    """
+    def test___str___w_a_sax_error(self):
+        zxce = self._makeOne(Exception('filename.xml:24:32:WAAA'))
+        self.assertEqual(str(zxce), 'File "filename.xml", line 24.32, WAAA')
 
-    >>> class FauxContext(FauxContext):
-    ...   def end(self):
-    ...     raise AttributeError("xxx")
 
-    >>> context = FauxContext()
-    >>> locator = FauxLocator('tests//sample.zcml', 1, 1)
-    >>> handler = xmlconfig.ConfigurationHandler(context)
-    >>> handler.setDocumentLocator(locator)
+class ParserInfoTests(unittest.TestCase):
 
-    >>> handler.startElementNS((u"ns", u"foo"), u"foo",
-    ...                        {(u"xxx", u"splat"): u"splatv",
-    ...                         (None, u"a"): u"avalue",
-    ...                         (None, u"b"): u"bvalue",
-    ...                        })
+    _tempdir = None
 
-    >>> locator.line, locator.column = 7, 16
-    >>> try:
-    ...   v = handler.endElementNS((u"ns", u"foo"), u"foo")
-    ... except xmlconfig.ZopeXMLConfigurationError, v:
-    ...   pass
-    >>> print v
-    File "tests//sample.zcml", line 1.1-7.16
-        AttributeError: xxx
+    def tearDown(self):
+        if self._tempdir is not None:
+            import shutil
+            shutil.rmtree(self._tempdir)
 
-    """
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import ParserInfo
+        return ParserInfo
 
-def clean_info_path(s):
-    part1 = s[:6]
-    part2 = s[6:s.find('"', 6)]
-    part2 = part2[part2.rfind("tests"):]
-    part2 = part2.replace(os.sep, '/')
-    part3 = s[s.find('"', 6):].rstrip()
-    return part1+part2+part3
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-def clean_path(s):
-    s = s[s.rfind("tests"):]
-    s = s.replace(os.sep, '/')
-    return s
+    def test___repr___w_eline_ecolumn_match_line_column(self):
+        pi = self._makeOne('filename.xml', 24, 32)
+        pi.end(24, 32)
+        self.assertEqual(repr(pi), 'File "filename.xml", line 24.32')
 
-def test_processxmlfile():
-    """
+    def test___repr___w_eline_ecolumn_dont_match_line_column(self):
+        pi = self._makeOne('filename.xml', 24, 32)
+        pi.end(33, 21)
+        self.assertEqual(repr(pi), 'File "filename.xml", line 24.32-33.21')
 
-    >>> file = open(path("samplepackage", "configure.zcml"))
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> xmlconfig.processxmlfile(file, context)
+    def test___str___w_eline_ecolumn_match_line_column(self):
+        pi = self._makeOne('filename.xml', 24, 32)
+        pi.end(24, 32)
+        self.assertEqual(str(pi), 'File "filename.xml", line 24.32')
 
-    >>> foo.data
-    []
+    def test___str___w_eline_ecolumn_dont_match_line_column_bad_file(self):
+        pi = self._makeOne('/path/to/nonesuch.xml', 24, 32)
+        pi.end(33, 21)
+        self.assertEqual(str(pi),
+                        'File "/path/to/nonesuch.xml", line 24.32-33.21\n'
+                        '  Could not read source.')
 
-    >>> context.execute_actions()
+    def test___str___w_good_file(self):
+        pi = self._makeOne('tests//sample.zcml', 3, 2)
+        pi.end(3, 57)
+        self.assertEqual(
+            str(pi),
+            'File "tests//sample.zcml", line 3.2-3.57\n'
+            '    <directives namespace="http://namespaces.zope.org/zope">')
 
-    >>> data = foo.data.pop()
 
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
+class ConfigurationHandlerTests(_Catchable, unittest.TestCase):
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import ConfigurationHandler
+        return ConfigurationHandler
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
-        <test:foo x="blah" y="0" />
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    >>> data.package
-    >>> data.basepath
-    """
+    def test_ctor_defaults(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertTrue(handler.context is context)
+        self.assertFalse(handler.testing)
+        self.assertEqual(handler.ignore_depth, 0)
 
-def test_file():
-    """
+    def test_ctor_explicit(self):
+        context = FauxContext()
+        handler = self._makeOne(context, True)
+        self.assertTrue(handler.context is context)
+        self.assertTrue(handler.testing)
+        self.assertEqual(handler.ignore_depth, 0)
+        self.assertTrue(handler.locator is None)
 
-    >>> file_name = path("samplepackage", "configure.zcml")
-    >>> context = xmlconfig.file(file_name)
+    def test_setDocumentLocator(self):
+        context = FauxContext()
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context, True)
+        handler.setDocumentLocator(locator)
+        self.assertTrue(handler.locator is locator)
 
-    >>> data = foo.data.pop()
+    def test_startElementNS_w_zcml_condition_failing(self):
+        from zope.configuration.xmlconfig import ZCML_CONDITION
+        context = FauxContext()
+        handler = self._makeOne(context, True)
+        # No locator set:  we won't need it, due to a failed condition.
+        handler.startElementNS((NS, FOO), FOO,
+                               {ZCML_CONDITION: 'have nonesuch',
+                                (None, A): AVALUE,
+                                (None, B): BVALUE,
+                               })
+        self.assertEqual(handler.ignore_depth, 1)
 
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
+    def test_startElementNS_w_ignore_depth_already_set(self):
+        context = FauxContext()
+        handler = self._makeOne(context, True)
+        handler.ignore_depth = 1
+        # No locator set:  we won't need it, as an ancestor had a
+        # failed condition.
+        handler.startElementNS((NS, FOO), FOO,
+                               {(XXX, SPLAT): SPLATV,
+                                (None, A): AVALUE,
+                                (None, B): BVALUE,
+                               })
+        self.assertEqual(handler.ignore_depth, 2)
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
+    def test_startElementNS_context_begin_raises_wo_testing(self):
+        from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+        class ErrorContext(FauxContext):
+          def begin(self, *args):
+            raise AttributeError("xxx")
+        context = ErrorContext()
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context)
+        handler.setDocumentLocator(locator)
+        exc = self.assertRaises(ZopeXMLConfigurationError,
+                    handler.startElementNS, (NS, FOO), FOO,
+                                     {(XXX, SPLAT): SPLATV,
+                                      (None, A): AVALUE,
+                                      (None, B): BVALUE,
+                                     })
+        self.assertEqual(exc.info.file, 'tests//sample.zcml')
+        self.assertEqual(exc.info.line, 1)
+        self.assertEqual(exc.info.column, 1)
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
-        <test:foo x="blah" y="0" />
+    def test_startElementNS_context_begin_raises_w_testing(self):
+        class ErrorContext(FauxContext):
+          def begin(self, *args):
+            raise AttributeError("xxx")
+        context = ErrorContext()
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context, True)
+        handler.setDocumentLocator(locator)
+        self.assertRaises(AttributeError,
+                    handler.startElementNS, (NS, FOO), FOO,
+                                     {(XXX, SPLAT): SPLATV,
+                                      (None, A): AVALUE,
+                                      (None, B): BVALUE,
+                                     })
 
-    >>> data.package
-    >>> print clean_path(data.basepath)
-    tests/samplepackage
-    """
+    def test_startElementNS_normal(self):
+        # Integration test of startElementNS / endElementNS pair.
+        context = FauxContext()
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context)
+        handler.setDocumentLocator(locator)
 
-def test_include_by_package():
-    """
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> import zope.configuration.tests.samplepackage as package
-    >>> xmlconfig.include(context, 'configure.zcml', package)
-    >>> context.execute_actions()
+        handler.startElementNS((NS, FOO), FOO,
+                               {(XXX, SPLAT): SPLATV,
+                                (None, A): AVALUE,
+                                (None, B): BVALUE,
+                               })
+        self.assertEqual(context.info.file, 'tests//sample.zcml')
+        self.assertEqual(context.info.line, 1)
+        self.assertEqual(context.info.column, 1)
+        self.assertEqual(context.begin_args,
+                         ((NS, FOO),
+                          {'a': AVALUE, 'b': BVALUE}))
+        self.assertFalse(context._end_called)
 
-    >>> data = foo.data.pop()
+    def test_endElementNS_w_ignore_depth_already_set(self):
+        context = FauxContext()
+        handler = self._makeOne(context, True)
+        handler.ignore_depth = 1
+        # No locator set:  we won't need it, as we had a
+        # failed condition.
+        handler.endElementNS((NS, FOO), FOO)
+        self.assertEqual(handler.ignore_depth, 0)
 
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
+    def test_endElementNS_context_end_raises_wo_testing(self):
+        from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+        class ErrorContext(FauxContext):
+          def end(self):
+            raise AttributeError("xxx")
+        class Info(object):
+            _line = _col = None
+            def end(self, line, col):
+                self._line, self._col = line, col
+        context = ErrorContext()
+        info = Info()
+        context.setInfo(info)
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context)
+        handler.setDocumentLocator(locator)
+        locator.line, locator.column = 7, 16
+        exc = self.assertRaises(ZopeXMLConfigurationError,
+                          handler.endElementNS, (NS, FOO), FOO)
+        self.assertTrue(exc.info is context.info)
+        self.assertEqual(exc.info._line, 7)
+        self.assertEqual(exc.info._col, 16)
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
+    def test_endElementNS_context_end_raises_w_testing(self):
+        class ErrorContext(FauxContext):
+          def end(self):
+            raise AttributeError("xxx")
+        class Info(object):
+            _line = _col = None
+            def end(self, line, col):
+                self._line, self._col = line, col
+        context = ErrorContext()
+        info = Info()
+        context.setInfo(info)
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = self._makeOne(context, True)
+        handler.setDocumentLocator(locator)
+        locator.line, locator.column = 7, 16
+        self.assertRaises(AttributeError,
+                          handler.endElementNS, (NS, FOO), FOO)
+        self.assertEqual(context.info._line, 7)
+        self.assertEqual(context.info._col, 16)
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/configure.zcml", line 12.2-12.29
-        <test:foo x="blah" y="0" />
+    def test_evaluateCondition_w_have_no_args(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        exc = self.assertRaises(ValueError,
+                                handler.evaluateCondition, 'have')
+        self.assertEqual(str(exc.args[0]), "Feature name missing: 'have'")
 
-    >>> data.package is package
-    1
+    def test_evaluateCondition_w_not_have_too_many_args(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        exc = self.assertRaises(ValueError,
+                                handler.evaluateCondition, 'not-have a b')
+        self.assertEqual(str(exc.args[0]),
+                         "Only one feature allowed: 'not-have a b'")
 
-    >>> data.basepath[-13:]
-    'samplepackage'
+    def test_evaluateCondition_w_have_miss(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertFalse(handler.evaluateCondition('have feature'))
 
-    >>> [clean_path(p) for p in data.includepath]
-    ['tests/samplepackage/configure.zcml']
+    def test_evaluateCondition_w_have_hit(self):
+        context = FauxContext()
+        context._features = ('feature',)
+        handler = self._makeOne(context)
+        self.assertTrue(handler.evaluateCondition('have feature'))
 
+    def test_evaluateCondition_w_not_have_miss(self):
+        context = FauxContext()
+        context._features = ('feature',)
+        handler = self._makeOne(context)
+        self.assertFalse(handler.evaluateCondition('not-have feature'))
 
-    """
+    def test_evaluateCondition_w_not_have_hit(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertTrue(handler.evaluateCondition('not-have feature'))
 
-# Not any more
-##     Including the same file more than once produces an error:
+    def test_evaluateCondition_w_installed_no_args(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        exc = self.assertRaises(ValueError,
+                                handler.evaluateCondition, 'installed')
+        self.assertEqual(str(exc.args[0]), "Package name missing: 'installed'")
 
-##     >>> try:
-##     ...   xmlconfig.include(context, 'configure.zcml', package)
-##     ... except xmlconfig.ConfigurationError, e:
-##     ...   'OK'
-##     ...
-##     'OK'
+    def test_evaluateCondition_w_not_installed_too_many_args(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        exc = self.assertRaises(ValueError,
+                                handler.evaluateCondition, 'not-installed a b')
+        self.assertEqual(str(exc.args[0]),
+                         "Only one package allowed: 'not-installed a b'")
 
-def test_include_by_file():
-    """
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> here = os.path.dirname(__file__)
-    >>> path = os.path.join(here, "samplepackage", "foo.zcml")
-    >>> xmlconfig.include(context, path)
-    >>> context.execute_actions()
+    def test_evaluateCondition_w_installed_miss(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertFalse(handler.evaluateCondition('installed nonsuch.package'))
 
-    >>> data = foo.data.pop()
+    def test_evaluateCondition_w_installed_hit(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertTrue(handler.evaluateCondition('installed os'))
 
-    >>> data.args
-    (('x', 'foo'), ('y', 2))
+    def test_evaluateCondition_w_not_installed_miss(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertFalse(handler.evaluateCondition('not-installed os'))
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/foo.zcml.in", line 12.2-12.28
+    def test_evaluateCondition_w_not_installed_hit(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        self.assertTrue(
+                handler.evaluateCondition('not-installed nonsuch.package'))
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/foo.zcml.in", line 12.2-12.28
-        <test:foo x="foo" y="2" />
+    def test_evaluateCondition_w_unknown_verb(self):
+        context = FauxContext()
+        handler = self._makeOne(context)
+        exc = self.assertRaises(ValueError,
+                                handler.evaluateCondition, 'nonesuch')
+        self.assertEqual(str(exc.args[0]),
+                         "Invalid ZCML condition: 'nonesuch'")
 
-    >>> data.package
+    def test_endElementNS_normal(self):
+        class Info(object):
+            _line = _col = None
+            def end(self, line, col):
+                self._line, self._col = line, col
+        context = FauxContext()
+        info = Info()
+        context.setInfo(info)
+        locator = FauxLocator('tests//sample.zcml', 7, 16)
+        handler = self._makeOne(context, True)
+        handler.setDocumentLocator(locator)
+        handler.endElementNS((NS, FOO), FOO)
+        self.assertEqual(context.info._line, 7)
+        self.assertEqual(context.info._col, 16)
+        self.assertTrue(context._end_called)
 
-    >>> data.basepath[-13:]
-    'samplepackage'
 
-    >>> [clean_path(p) for p in data.includepath]
-    ['tests/samplepackage/foo.zcml.in']
-    """
+class Test_processxmlfile(_Catchable, unittest.TestCase):
 
-def test_include_by_file_glob():
-    """
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> here = os.path.dirname(__file__)
-    >>> path = os.path.join(here, "samplepackage/baz*.zcml")
-    >>> xmlconfig.include(context, files=path)
-    >>> context.execute_actions()
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import processxmlfile
+        return processxmlfile(*args, **kw)
 
-    >>> data = foo.data.pop()
-    >>> data.args
-    (('x', 'foo'), ('y', 3))
+    def test_w_empty_xml(self):
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration.xmlconfig import ZopeSAXParseException
+        from zope.configuration._compat import StringIO
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        exc = self.assertRaises(ZopeSAXParseException,
+                                self._callFUT, StringIO(), context)
+        self.assertEqual(str(exc._v), '<string>:1:0: no element found')
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/baz3.zcml", line 5.2-5.28
+    def test_w_valid_xml_fp(self):
+        # Integration test, really
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        file = open(path("samplepackage", "configure.zcml"))
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        self._callFUT(file, context)
+        self.assertEqual(foo.data, [])
+        context.execute_actions()
+        data = foo.data.pop()
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertEqual(clean_info_path(repr(data.info)),
+                'File "tests/samplepackage/configure.zcml", line 12.2-12.29')
+        self.assertEqual(clean_info_path(str(data.info)),
+                'File "tests/samplepackage/configure.zcml", line 12.2-12.29\n'
+                + '    <test:foo x="blah" y="0" />')
+        self.assertEqual(data.package, None)
+        self.assertEqual(data.basepath, None)
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/baz3.zcml", line 5.2-5.28
-        <test:foo x="foo" y="3" />
 
-    >>> data.package
+class Test_openInOrPlain(_Catchable, unittest.TestCase):
 
-    >>> data.basepath[-13:]
-    'samplepackage'
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import openInOrPlain
+        return openInOrPlain(*args, **kw)
 
-    >>> [clean_path(p) for p in data.includepath]
-    ['tests/samplepackage/baz3.zcml']
+    def _makeFilename(self, fn):
+        import os
+        from zope.configuration.tests.samplepackage import __file__
+        return os.path.join(os.path.dirname(__file__), fn)
 
-    >>> data = foo.data.pop()
-    >>> data.args
-    (('x', 'foo'), ('y', 2))
+    def test_file_present(self):
+        import os
+        fp = self._callFUT(self._makeFilename('configure.zcml'))
+        self.assertEqual(os.path.basename(fp.name), 'configure.zcml')
 
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/baz2.zcml", line 5.2-5.28
+    def test_file_missing_but_dot_in_present(self):
+        import os
+        fp = self._callFUT(self._makeFilename('foo.zcml'))
+        self.assertEqual(os.path.basename(fp.name), 'foo.zcml.in')
 
-    >>> print clean_info_path(str(data.info))
-    File "tests/samplepackage/baz2.zcml", line 5.2-5.28
-        <test:foo x="foo" y="2" />
+    def test_file_missing_and_dot_in_not_present(self):
+        import errno
+        exc = self.assertRaises(    
+                IOError,
+                self._callFUT, self._makeFilename('nonesuch.zcml'))
+        self.assertEqual(exc.errno, errno.ENOENT)
 
-    >>> data.package
 
-    >>> data.basepath[-13:]
-    'samplepackage'
+class Test_include(_Catchable, unittest.TestCase):
 
-    >>> [clean_path(p) for p in data.includepath]
-    ['tests/samplepackage/baz2.zcml']
-    """
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import include
+        return include(*args, **kw)
 
-def clean_actions(actions):
-    return [
-      {'discriminator': action['discriminator'],
-       'info': clean_info_path(`action['info']`),
-       'includepath': [clean_path(p) for p in action['includepath']],
-       }
-      for action in actions
-      ]
+    def test_both_file_and_files_passed(self):
+        context = FauxContext()
+        exc = self.assertRaises(ValueError,
+                                self._callFUT, context, 'tests//sample.zcml',
+                                files=['tests/*.zcml'])
+        self.assertEqual(str(exc), "Must specify only one of file or files")
 
-def clean_text_w_paths(error):
-    r = []
-    for line in unicode(error).split("\n"):
-      line = line.rstrip()
-      if not line:
-        continue
-      l = line.find('File "')
-      if l >= 0:
-        line = line[:l] + clean_info_path(line[l:])
-      r.append(line)
-    return '\n'.join(r)
+    def test_neither_file_nor_files_passed_already_seen(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration.tests import samplepackage
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        context.package = samplepackage
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        context._seen_files.add(fqn)
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            self._callFUT(context) #skips
+        self.assertEqual(len(logger.debugs), 0)
+        self.assertEqual(len(context.actions), 0)
 
-def test_includeOverrides():
-    """
-    When we have conflicting directives, we can resolve them if one of
-    the conflicting directives was from a file that included all of
-    the others.  The problem with this is that this requires that all
-    of the overriding directives be in one file, typically the
-    top-most including file. This isn't very convenient.  Fortunately,
-    we can overcome this with the includeOverrides directive. Let's
-    look at an example to see how this works.
+    def test_neither_file_nor_files_passed(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        before_stack = context.stack[:]
+        context.package = samplepackage
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            self._callFUT(context)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['callable'], foo.data.append)
+        self.assertEqual(action['includepath'], (fqn,))
+        self.assertEqual(context.stack, before_stack)
+        self.assertEqual(len(context._seen_files), 1)
+        self.assertTrue(fqn in context._seen_files)
 
-    Look at the file bar.zcml. It includes bar1.zcml and bar2.zcml.
-    bar2.zcml includes configure.zcml and has a foo
-    directive. bar2.zcml includes bar21.zcml.  bar2.zcml has a foo
-    directive that conflicts with one in bar1.zcml.  bar2.zcml also
-    overrides a foo directive in bar21.zcml.  bar21.zcml has a foo
-    directive that conflicts with one in in configure.zcml. Whew!
+    def test_w_file_passed(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration import tests
+        from zope.configuration.tests import simple
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        before_stack = context.stack[:]
+        context.package = tests
+        fqn = _packageFile(tests, 'simple.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            self._callFUT(context, 'simple.zcml')
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(context.actions), 3)
+        action = context.actions[0]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqn,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, 'simple.py'))
+        action = context.actions[1]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqn,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, 'simple.zcml'))
+        action = context.actions[2]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqn,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, '__init__.py'))
+        self.assertEqual(context.stack, before_stack)
+        self.assertEqual(len(context._seen_files), 1)
+        self.assertTrue(fqn in context._seen_files)
 
-    Let's see what happens when we try to process bar.zcml.
+    def test_w_files_passed_and_package(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        before_stack = context.stack[:]
+        fqn1 = _packageFile(samplepackage, 'baz1.zcml')
+        fqn2 = _packageFile(samplepackage, 'baz2.zcml')
+        fqn3 = _packageFile(samplepackage, 'baz3.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            self._callFUT(context, package=samplepackage, files='baz*.zcml')
+        self.assertEqual(len(logger.debugs), 3)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn1, (), {}))
+        self.assertEqual(logger.debugs[1], ('include %s' % fqn2, (), {}))
+        self.assertEqual(logger.debugs[2], ('include %s' % fqn3, (), {}))
+        self.assertEqual(len(context.actions), 2)
+        action = context.actions[0]
+        self.assertEqual(action['callable'], foo.data.append)
+        self.assertEqual(action['includepath'], (fqn2,))
+        self.assertTrue(isinstance(action['args'][0], foo.stuff))
+        self.assertEqual(action['args'][0].args, (('x', b('foo')), ('y', 2)))
+        action = context.actions[1]
+        self.assertEqual(action['callable'], foo.data.append)
+        self.assertEqual(action['includepath'], (fqn3,))
+        self.assertTrue(isinstance(action['args'][0], foo.stuff))
+        self.assertEqual(action['args'][0].args, (('x', b('foo')), ('y', 3)))
+        self.assertEqual(context.stack, before_stack)
+        self.assertEqual(len(context._seen_files), 3)
+        self.assertTrue(fqn1 in context._seen_files)
+        self.assertTrue(fqn2 in context._seen_files)
+        self.assertTrue(fqn3 in context._seen_files)
 
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
 
-    >>> here = os.path.dirname(__file__)
-    >>> path = os.path.join(here, "samplepackage", "bar.zcml")
-    >>> xmlconfig.include(context, path)
+class Test_exclude(_Catchable, unittest.TestCase):
 
-    So far so good, let's look at the configuration actions:
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import exclude
+        return exclude(*args, **kw)
 
-    >>> pprint=PrettyPrinter(width=70).pprint
-    >>> pprint(clean_actions(context.actions))
-    [{'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar1.zcml',
-                      'tests/samplepackage/configure.zcml'],
-      'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar1.zcml'],
-      'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar2.zcml',
-                      'tests/samplepackage/bar21.zcml'],
-      'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'},
-     {'discriminator': (('x', 'blah'), ('y', 2)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar2.zcml',
-                      'tests/samplepackage/bar21.zcml'],
-      'info': 'File "tests/samplepackage/bar21.zcml", line 4.2-4.24'},
-     {'discriminator': (('x', 'blah'), ('y', 2)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar2.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/bar.zcml',
-                      'tests/samplepackage/bar2.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}]
+    def test_both_file_and_files_passed(self):
+        context = FauxContext()
+        exc = self.assertRaises(ValueError,
+                                self._callFUT, context, 'tests//sample.zcml',
+                                files=['tests/*.zcml'])
+        self.assertEqual(str(exc), "Must specify only one of file or files")
 
-    As you can see, there are a number of conflicts (actions with the same
-    discriminator).  Some of these can be resolved, but many can't, as
-    we'll find if we try to execuse the actions:
+    def test_neither_file_nor_files_passed(self):
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.tests import samplepackage
+        context = ConfigurationMachine()
+        context.package = samplepackage
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        self._callFUT(context)
+        self.assertEqual(len(context.actions), 0)
+        self.assertEqual(len(context._seen_files), 1)
+        self.assertTrue(fqn in context._seen_files)
 
-    >>> try:
-    ...    v = context.execute_actions()
-    ... except config.ConfigurationConflictError, v:
-    ...    pass
-    >>> print clean_text_w_paths(str(v))
-    Conflicting configuration actions
-      For: (('x', 'blah'), ('y', 0))
-        File "tests/samplepackage/configure.zcml", line 12.2-12.29
-            <test:foo x="blah" y="0" />
-        File "tests/samplepackage/bar21.zcml", line 3.2-3.24
-            <foo x="blah" y="0" />
-      For: (('x', 'blah'), ('y', 1))
-        File "tests/samplepackage/bar1.zcml", line 5.2-5.24
-            <foo x="blah" y="1" />
-        File "tests/samplepackage/bar2.zcml", line 6.2-6.24
-            <foo x="blah" y="1" />
+    def test_w_file_passed(self):
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration import tests
+        context = ConfigurationMachine()
+        context.package = tests
+        fqn = _packageFile(tests, 'simple.zcml')
+        self._callFUT(context, 'simple.zcml')
+        self.assertEqual(len(context.actions), 0)
+        self.assertEqual(len(context._seen_files), 1)
+        self.assertTrue(fqn in context._seen_files)
 
-    Note that the conflicts for (('x', 'blah'), ('y', 2)) aren't
-    included in the error because they could be resolved.
+    def test_w_files_passed_and_package(self):
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.tests import samplepackage
+        context = ConfigurationMachine()
+        fqn1 = _packageFile(samplepackage, 'baz1.zcml')
+        fqn2 = _packageFile(samplepackage, 'baz2.zcml')
+        fqn3 = _packageFile(samplepackage, 'baz3.zcml')
+        self._callFUT(context, package=samplepackage, files='baz*.zcml')
+        self.assertEqual(len(context.actions), 0)
+        self.assertEqual(len(context._seen_files), 3)
+        self.assertTrue(fqn1 in context._seen_files)
+        self.assertTrue(fqn2 in context._seen_files)
+        self.assertTrue(fqn3 in context._seen_files)
 
-    Let's try this again using includeOverrides.  We'll include
-    baro.zcml which includes bar2.zcml as overrides.
 
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> path = os.path.join(here, "samplepackage", "baro.zcml")
-    >>> xmlconfig.include(context, path)
+class Test_includeOverrides(unittest.TestCase):
 
-    Now, if we look at the actions:
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import includeOverrides
+        return includeOverrides(*args, **kw)
 
-    >>> pprint(clean_actions(context.actions))
-    [{'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/baro.zcml',
-                      'tests/samplepackage/bar1.zcml',
-                      'tests/samplepackage/configure.zcml'],
-      'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/baro.zcml',
-                      'tests/samplepackage/bar1.zcml'],
-      'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/baro.zcml'],
-      'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'},
-     {'discriminator': (('x', 'blah'), ('y', 2)),
-      'includepath': ['tests/samplepackage/baro.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/baro.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}]
+    def test_actions_have_parents_includepath(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration import tests
+        from zope.configuration.tests import simple
+        context = ConfigurationMachine()
+        fqp = _packageFile(tests, 'configure.zcml')
+        registerCommonDirectives(context)
+        before_stack = context.stack[:]
+        context.package = tests
+        # dummy action, path from "previous" include
+        context.includepath = (fqp,)
+        def _callable():
+            pass
+        context.actions.append({'discriminator': None,
+                                'callable': _callable,
+                               })
+        fqn = _packageFile(tests, 'simple.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            self._callFUT(context, 'simple.zcml')
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(context.actions), 4)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], None)
+        self.assertEqual(action['callable'], _callable)
+        action = context.actions[1]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqp,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, 'simple.py'))
+        action = context.actions[2]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqp,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, 'simple.zcml'))
+        action = context.actions[3]
+        self.assertEqual(action['callable'], simple.file_registry.append)
+        self.assertEqual(action['includepath'], (fqp,))
+        self.assertEqual(action['args'][0].path,
+                         _packageFile(tests, '__init__.py'))
+        self.assertEqual(context.stack, before_stack)
+        self.assertEqual(len(context._seen_files), 1)
+        self.assertTrue(fqn in context._seen_files)
 
-    We see that:
 
-    - The conflicting actions between bar2.zcml and bar21.zcml have
-      been resolved, and
+class Test_file(unittest.TestCase):
 
-    - The remaining (after conflict resolution) actions from bar2.zcml
-      and bar21.zcml have the includepath that they would have if they
-      were defined in baro.zcml and this override the actions from
-      bar1.zcml and configure.zcml.
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import file
+        return file(*args, **kw)
 
-    We can now execute the actions without problem, since the
-    remaining conflicts are resolvable:
+    def test_wo_execute_wo_context_wo_package(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        file_name = path("samplepackage", "configure.zcml")
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            context = self._callFUT(file_name, execute=False)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % file_name, (), {}))
+        self.assertEqual(len(foo.data), 0)
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    >>> context.execute_actions()
+    def test_wo_execute_wo_context_w_package(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        file_name = path("samplepackage", "configure.zcml")
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            context = self._callFUT('configure.zcml', package=samplepackage,
+                                    execute=False)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % file_name, (), {}))
+        self.assertEqual(len(foo.data), 0)
+        self.assertTrue(context.package is samplepackage)
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    We should now have three entries in foo.data:
+    def test_wo_execute_w_context(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        context = ConfigurationMachine()
+        context.package = samplepackage
+        registerCommonDirectives(context)
+        file_name = path("samplepackage", "configure.zcml")
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            ret = self._callFUT('configure.zcml', context=context,
+                                execute=False)
+        self.assertTrue(ret is context)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % file_name, (), {}))
+        self.assertEqual(len(foo.data), 0)
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    >>> len(foo.data)
-    3
+    def test_w_execute(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        file_name = path("samplepackage", "configure.zcml")
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            context = self._callFUT(file_name)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % file_name, (), {}))
+        data = foo.data.pop()
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file.endswith(
+                            'tests/samplepackage/configure.zcml'))
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
+        self.assertEqual(data.package, None)
+        self.assertTrue(data.basepath.endswith('tests/samplepackage'))
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar21.zcml", line 3.2-3.24
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 5.2-5.24
+class Test_string(unittest.TestCase):
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 6.2-6.24
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import string
+        return string(*args, **kw)
 
+    def test_wo_execute_wo_context(self):
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        file_name = path("samplepackage", "configure.zcml")
+        xml = open(file_name).read()
+        context = self._callFUT(xml, execute=False)
+        self.assertEqual(len(foo.data), 0)
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    We expect the exact same results when using includeOverrides with
-    the ``files`` argument instead of the ``file`` argument.  The
-    baro2.zcml file uses the former:
+    def test_wo_execute_w_context(self):
+        from zope.configuration.config import ConfigurationMachine
+        from zope.configuration.xmlconfig import registerCommonDirectives
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        context = ConfigurationMachine()
+        registerCommonDirectives(context)
+        file_name = path("samplepackage", "configure.zcml")
+        xml = open(file_name).read()
+        ret = self._callFUT(xml, context=context, execute=False)
+        self.assertTrue(ret is context)
+        self.assertEqual(len(foo.data), 0)
+        self.assertEqual(len(context.actions), 1)
+        action = context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    >>> context = config.ConfigurationMachine()
-    >>> xmlconfig.registerCommonDirectives(context)
-    >>> path = os.path.join(here, "samplepackage", "baro2.zcml")
-    >>> xmlconfig.include(context, path)
+    def test_w_execute(self):
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        file_name = path("samplepackage", "configure.zcml")
+        xml = open(file_name).read()
+        context = self._callFUT(xml)
+        data = foo.data.pop()
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file, '<string>')
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
+        self.assertEqual(data.package, None)
+        self.assertEqual(data.basepath, None)
 
-    Actions look like above:
 
-    >>> pprint(clean_actions(context.actions))
-    [{'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/baro2.zcml',
-                      'tests/samplepackage/bar1.zcml',
-                      'tests/samplepackage/configure.zcml'],
-      'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/baro2.zcml',
-                      'tests/samplepackage/bar1.zcml'],
-      'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 0)),
-      'includepath': ['tests/samplepackage/baro2.zcml'],
-      'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'},
-     {'discriminator': (('x', 'blah'), ('y', 2)),
-      'includepath': ['tests/samplepackage/baro2.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'},
-     {'discriminator': (('x', 'blah'), ('y', 1)),
-      'includepath': ['tests/samplepackage/baro2.zcml'],
-      'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}]
+class XMLConfigTests(unittest.TestCase):
 
-    >>> context.execute_actions()
-    >>> len(foo.data)
-    3
-    >>> del foo.data[:]
+    def setUp(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-    """
+    def tearDown(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-def test_XMLConfig():
-    """Test processing a configuration file.
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import XMLConfig
+        return XMLConfig
 
-    We'll use the same example from test_includeOverrides:
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
 
-    >>> here = os.path.dirname(__file__)
-    >>> path = os.path.join(here, "samplepackage", "baro.zcml")
+    def test_ctor_w_global_context_missing(self):
+        import os
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        here = os.path.dirname(__file__)
+        path = os.path.join(here, "samplepackage", "configure.zcml")
+        logger = LoggerStub()
+        xmlconfig._context = None
+        with _Monkey(xmlconfig, logger=logger):
+            xc = self._makeOne(path)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % path, (), {}))
+        self.assertEqual(len(foo.data), 0) # no execut_actions
+        self.assertEqual(len(xc.context.actions), 1)
+        action = xc.context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    First, process the configuration file:
+    def test_ctor(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            xc = self._makeOne(fqn)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(foo.data), 0) # no execut_actions
+        self.assertEqual(len(xc.context.actions), 1)
+        action = xc.context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    >>> x = xmlconfig.XMLConfig(path)
+    def test_ctor_w_module(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests.samplepackage import foo
+        from zope.configuration.tests import samplepackage
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            xc = self._makeOne("configure.zcml", samplepackage)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(foo.data), 0) # no execut_actions
+        self.assertEqual(len(xc.context.actions), 1)
+        action = xc.context.actions[0]
+        self.assertEqual(action['discriminator'], (('x', b('blah')), ('y', 0)))
+        self.assertEqual(action['callable'], foo.data.append)
 
-    Second, call the resulting object to process the actions:
+    def test___call__(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        logger = LoggerStub()
+        with _Monkey(xmlconfig, logger=logger):
+            xc = self._makeOne(fqn)
+        self.assertEqual(len(logger.debugs), 1)
+        self.assertEqual(logger.debugs[0], ('include %s' % fqn, (), {}))
+        self.assertEqual(len(foo.data), 0)
+        xc() # call to process the actions
+        self.assertEqual(len(foo.data), 1)
+        data = foo.data.pop(0)
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file.endswith(
+                'tests/samplepackage/configure.zcml'))
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
 
-    >>> x()
 
-    And verify the data as above:
 
-    >>> len(foo.data)
-    3
+class Test_xmlconfig(unittest.TestCase):
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar21.zcml", line 3.2-3.24
+    def setUp(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 5.2-5.24
+    def tearDown(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 6.2-6.24
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import xmlconfig
+        return xmlconfig(*args, **kw)
 
-    Finally, clean up.
+    def test_wo_testing_passed(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        def _assertTestingFalse(func):
+            def _wrapper(*args, **kw):
+                assert(not kw['testing'])
+                return func(*args, **kw)
+            return _wrapper
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        context = xmlconfig._getContext()
+        context.execute_actions = _assertTestingFalse(context.execute_actions)
+        with _Monkey(xmlconfig,
+                        processxmlfile=_assertTestingFalse(
+                                                xmlconfig.processxmlfile)):
+            self._callFUT(open(fqn), False)
+        self.assertEqual(len(foo.data), 1)
+        data = foo.data.pop(0)
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file.endswith(
+                'tests/samplepackage/configure.zcml'))
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
 
-    >>> from zope.testing.cleanup import CleanUp
-    >>> CleanUp().cleanUp()
-    """
+    def test_w_testing_passed(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        def _assertTestingTrue(func):
+            def _wrapper(*args, **kw):
+                assert(kw['testing'])
+                return func(*args, **kw)
+            return _wrapper
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        context = xmlconfig._getContext()
+        context.execute_actions = _assertTestingTrue(context.execute_actions)
+        with _Monkey(xmlconfig,
+                        processxmlfile=_assertTestingTrue(
+                                                xmlconfig.processxmlfile)):
+            self._callFUT(open(fqn), True)
+        self.assertEqual(len(foo.data), 1)
+        data = foo.data.pop(0)
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file.endswith(
+                'tests/samplepackage/configure.zcml'))
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
 
-def test_XMLConfig_w_module():
-    """Test processing a configuration file for a module.
 
-    We'll use the same example from test_includeOverrides:
+class Test_testxmlconfig(unittest.TestCase):
 
-    >>> import zope.configuration.tests.samplepackage as module
+    def setUp(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-    First, process the configuration file:
+    def tearDown(self):
+        from zope.configuration.xmlconfig import _clearContext
+        from zope.configuration.tests.samplepackage.foo import data
+        _clearContext()
+        del data[:]
 
-    >>> x = xmlconfig.XMLConfig("baro.zcml", module)
+    def _callFUT(self, *args, **kw):
+        from zope.configuration.xmlconfig import testxmlconfig
+        return testxmlconfig(*args, **kw)
 
-    Second, call the resulting object to process the actions:
+    def test_w_testing_passed(self):
+        from zope.configuration import xmlconfig
+        from zope.configuration._compat import b
+        from zope.configuration.tests import samplepackage
+        from zope.configuration.tests.samplepackage import foo
+        def _assertTestingTrue(func):
+            def _wrapper(*args, **kw):
+                assert(kw['testing'])
+                return func(*args, **kw)
+            return _wrapper
+        fqn = _packageFile(samplepackage, 'configure.zcml')
+        context = xmlconfig._getContext()
+        context.execute_actions = _assertTestingTrue(context.execute_actions)
+        with _Monkey(xmlconfig,
+                        processxmlfile=_assertTestingTrue(
+                                                xmlconfig.processxmlfile)):
+            self._callFUT(open(fqn))
+        self.assertEqual(len(foo.data), 1)
+        data = foo.data.pop(0)
+        self.assertEqual(data.args, (('x', b('blah')), ('y', 0)))
+        self.assertTrue(data.info.file.endswith(
+                'tests/samplepackage/configure.zcml'))
+        self.assertEqual(data.info.line, 12)
+        self.assertEqual(data.info.column, 2)
+        self.assertEqual(data.info.eline, 12)
+        self.assertEqual(data.info.ecolumn, 29)
 
-    >>> x()
 
-    And verify the data as above:
 
-    >>> len(foo.data)
-    3
+class FauxLocator(object):
+    def __init__(self, file, line, column):
+        self.file, self.line, self.column = file, line, column
+    def getSystemId(self):
+        return self.file
+    def getLineNumber(self):
+        return self.line
+    def getColumnNumber(self):
+        return self.column
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar21.zcml", line 3.2-3.24
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 5.2-5.24
+class FauxContext(object):
+    includepath = ()
+    _features = ()
+    _end_called = False
+    def setInfo(self, info):
+        self.info = info
+    def getInfo(self):
+        return self.info
+    def begin(self, name, data, info):
+        self.begin_args = name, data
+        self.info = info
+    def end(self):
+        self._end_called = 1
+    def hasFeature(self, feature):
+        return feature in self._features
 
-    >>> data = foo.data.pop(0)
-    >>> data.args
-    (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
-    File "tests/samplepackage/bar2.zcml", line 6.2-6.24
 
-    Finally, clean up.
+def path(*p):
+    import os
+    return os.path.join(os.path.dirname(__file__), *p)
 
-    >>> from zope.testing.cleanup import CleanUp
-    >>> CleanUp().cleanUp()
-    """
+def clean_info_path(s):
+    import os
+    part1 = s[:6]
+    part2 = s[6:s.find('"', 6)]
+    part2 = part2[part2.rfind("tests"):]
+    part2 = part2.replace(os.sep, '/')
+    part3 = s[s.find('"', 6):].rstrip()
+    return part1+part2+part3
 
+def clean_path(s):
+    import os
+    s = s[s.rfind("tests"):]
+    s = s.replace(os.sep, '/')
+    return s
 
+def clean_actions(actions):
+    return [
+      {'discriminator': action['discriminator'],
+       'info': clean_info_path(repr(action['info'])),
+       'includepath': [clean_path(p) for p in action['includepath']],
+       }
+      for action in actions
+      ]
 
+def clean_text_w_paths(error):
+    r = []
+    for line in unicode(error).split("\n"):
+      line = line.rstrip()
+      if not line:
+        continue
+      l = line.find('File "')
+      if l >= 0:
+        line = line[:l] + clean_info_path(line[l:])
+      r.append(line)
+    return '\n'.join(r)
+
+
+def _packageFile(package, filename):
+    import os
+    return os.path.join(os.path.dirname(package.__file__), filename)
+
+class _Monkey(object):
+
+    def __init__(self, module, **replacements):
+        self.module = module
+        self.orig = {}
+        self.replacements = replacements
+        
+    def __enter__(self):
+        for k, v in self.replacements.items():
+            orig = getattr(self.module, k, self)
+            if orig is not self:
+                self.orig[k] = orig
+            setattr(self.module, k, v)
+
+    def __exit__(self, *exc_info):
+        for k, v in self.replacements.items():
+            if k in self.orig:
+                setattr(self.module, k, self.orig[k])
+            else: #pragma NO COVERSGE
+                delattr(self.module, k)
+
+
+class LoggerStub(object):
+
+    def __init__(self):
+        self.errors = []
+        self.warnings = []
+        self.infos = []
+        self.debugs = []
+
+    def error(self, msg, *args, **kwargs):
+        self.errors.append((msg, args, kwargs))
+
+    def warning(self, msg, *args, **kwargs):
+        self.warnings.append((msg, args, kwargs))
+
+    def info(self, msg, *args, **kwargs):
+        self.infos.append((msg, args, kwargs))
+
+    def debug(self, msg, *args, **kwargs):
+        self.debugs.append((msg, args, kwargs))
+
+
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.configuration.xmlconfig'),
-        DocTestSuite(),
-        DocFileSuite('../exclude.txt',
-            checker=renormalizing.RENormalizing([
-                (re.compile('include [^\n]+zope.configuration[\S+]'),
-                 'include /zope.configuration\2'),
-                (re.compile(r'\\'), '/'),
-                ]))
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')
+        unittest.makeSuite(ZopeXMLConfigurationErrorTests),
+        unittest.makeSuite(ZopeSAXParseExceptionTests),
+        unittest.makeSuite(ParserInfoTests),
+        unittest.makeSuite(ConfigurationHandlerTests),
+        unittest.makeSuite(Test_processxmlfile),
+        unittest.makeSuite(Test_openInOrPlain),
+        unittest.makeSuite(Test_include),
+        unittest.makeSuite(Test_exclude),
+        unittest.makeSuite(Test_includeOverrides),
+        unittest.makeSuite(Test_file),
+        unittest.makeSuite(Test_string),
+        unittest.makeSuite(XMLConfigTests),
+        unittest.makeSuite(Test_xmlconfig),
+        unittest.makeSuite(Test_testxmlconfig),
+    ))

Copied: zope.configuration/trunk/src/zope/configuration/tests/test_zopeconfigure.py (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_zopeconfigure.py)
===================================================================
--- zope.configuration/trunk/src/zope/configuration/tests/test_zopeconfigure.py	                        (rev 0)
+++ zope.configuration/trunk/src/zope/configuration/tests/test_zopeconfigure.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,45 @@
+##############################################################################
+#
+# 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 zope.configuration.xmlconfig.
+"""
+import unittest
+
+
+class ZopeConfigureTests(unittest.TestCase):
+
+    def _getTargetClass(self):
+        from zope.configuration.xmlconfig import ZopeConfigure
+        return ZopeConfigure
+
+    def _makeOne(self, *args, **kw):
+        return self._getTargetClass()(*args, **kw)
+
+    def test_ctor_wo_package(self):
+        zc = self._makeOne(Context())
+        self.assertEqual(zc.basepath, None)
+
+    def test_ctor_w_package(self):
+        import os
+        import zope.configuration.tests as zct
+        zc = self._makeOne(Context(), package=zct)
+        self.assertEqual(zc.basepath, os.path.dirname(zct.__file__))
+
+
+class Context(object):
+    basepath = None
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(ZopeConfigureTests),
+    ))

Modified: zope.configuration/trunk/src/zope/configuration/xmlconfig.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/xmlconfig.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/xmlconfig.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -20,40 +20,44 @@
 __docformat__ = 'restructuredtext'
 
 import errno
+from glob import glob
+import logging
 import os
 import sys
-import logging
-import zope.configuration.config as config
-
-from glob import glob
 from xml.sax import make_parser
 from xml.sax.xmlreader import InputSource
 from xml.sax.handler import ContentHandler, feature_namespaces
 from xml.sax import SAXParseException
-from zope import schema
-from zope.configuration.exceptions import ConfigurationError
-from zope.configuration.zopeconfigure import IZopeConfigure, ZopeConfigure
+
 from zope.interface import Interface
+from zope.schema import NativeStringLine
 
+from zope.configuration.config import ConfigurationMachine
+from zope.configuration.config import defineGroupingDirective
+from zope.configuration.config import defineSimpleDirective
+from zope.configuration.config import GroupingContextDecorator
+from zope.configuration.config import GroupingStackItem
+from zope.configuration.config import resolveConflicts
+from zope.configuration.exceptions import ConfigurationError
+from zope.configuration.fields import GlobalObject
+from zope.configuration.zopeconfigure import IZopeConfigure
+from zope.configuration.zopeconfigure import ZopeConfigure
+from zope.configuration._compat import StringIO
+from zope.configuration._compat import reraise
+from zope.configuration._compat import u
+
 logger = logging.getLogger("config")
 
 ZCML_NAMESPACE = "http://namespaces.zope.org/zcml"
-ZCML_CONDITION = (ZCML_NAMESPACE, u"condition")
+ZCML_CONDITION = (ZCML_NAMESPACE, u("condition"))
 
 
 class ZopeXMLConfigurationError(ConfigurationError):
     """Zope XML Configuration error
 
-    These errors are wrappers for other errors. The include configuration
-    info and the wrapped error type and value:
-
-    >>> v = ZopeXMLConfigurationError("blah", AttributeError, "xxx")
-    >>> print v
-    'blah'
-        AttributeError: xxx
-
+    These errors are wrappers for other errors. They include configuration
+    info and the wrapped error type and value.
     """
-
     def __init__(self, info, etype, evalue):
         self.info, self.etype, self.evalue = info, etype, evalue
 
@@ -61,17 +65,11 @@
         # Only use the repr of the info. This is because we expect to
         # get a parse info and we only want the location information.
         return "%s\n    %s: %s" % (
-            `self.info`, self.etype.__name__, self.evalue)
+            repr(self.info), self.etype.__name__, self.evalue)
 
 class ZopeSAXParseException(ConfigurationError):
     """Sax Parser errors, reformatted in an emacs friendly way
-
-    >>> v = ZopeSAXParseException("foo.xml:12:3:Not well formed")
-    >>> print v
-    File "foo.xml", line 12.3, Not well formed
-
     """
-
     def __init__(self, v):
         self._v = v
 
@@ -88,38 +86,9 @@
 
     This includes the directive location, as well as text data
     contained in the directive.
-
-    >>> info = ParserInfo('tests//sample.zcml', 1, 0)
-    >>> info
-    File "tests//sample.zcml", line 1.0
-
-    >>> print info
-    File "tests//sample.zcml", line 1.0
-
-    >>> info.characters("blah\\n")
-    >>> info.characters("blah")
-    >>> info.text
-    u'blah\\nblah'
-
-    >>> info.end(7, 0)
-    >>> info
-    File "tests//sample.zcml", line 1.0-7.0
-
-    >>> print info
-    File "tests//sample.zcml", line 1.0-7.0
-      <configure xmlns='http://namespaces.zope.org/zope'>
-        <!-- zope.configure -->
-        <directives namespace="http://namespaces.zope.org/zope">
-          <directive name="hook" attributes="name implementation module"
-             handler="zope.configuration.metaconfigure.hook" />
-        </directives>
-      </configure>
-
-
     """
+    text = u('')
 
-    text = u''
-
     def __init__(self, file, line, column):
         self.file, self.line, self.column = file, line, column
         self.eline, self.ecolumn = line, column
@@ -153,31 +122,33 @@
         else:
             lines = f.readlines()[self.line-1:self.eline]
             ecolumn = self.ecolumn
-            if lines[-1][ecolumn:ecolumn+2] == '</':
+            if lines[-1][ecolumn:ecolumn+2] == '</': #pragma NO COVER
                 # We're pointing to the start of an end tag. Try to find
                 # the end
                 l = lines[-1].find('>', ecolumn)
                 if l >= 0:
                     lines[-1] = lines[-1][:l+1]
-            else:
+            else: #pragma NO COVER
                 lines[-1] = lines[-1][:ecolumn+1]
 
             column = self.column
-            if lines[0][:column].strip():
+            if lines[0][:column].strip(): #pragma NO COVER
                 # Remove text before start if it's noy whitespace
                 lines[0] = lines[0][self.column:]
 
+            pad = u('  ')
+            blank = u('')
             try:
-                src = u''.join([u"  "+l for l in lines])
-            except UnicodeDecodeError:
+                src = blank.join([pad + l for l in lines])
+            except UnicodeDecodeError: #pragma NO COVER
                 # XXX:
                 # I hope so most internation zcml will use UTF-8 as encoding
                 # otherwise this code must be made more clever
-                src = u''.join([u"  "+l.decode('utf-8') for l in lines])
+                src = blank.join([pad + l.decode('utf-8') for l in lines])
                 # unicode won't be printable, at least on my console
                 src = src.encode('ascii','replace')
 
-        return "%s\n%s" % (`self`, src)
+        return "%s\n%s" % (repr(self), src)
 
     def characters(self, characters):
         self.text += characters
@@ -188,8 +159,9 @@
 
     Translate parser events into calls into the configuration system.
     """
+    locator = None
 
-    def __init__(self, context, testing=0):
+    def __init__(self, context, testing=False):
         self.context = context
         self.testing = testing
         self.ignore_depth = 0
@@ -230,84 +202,25 @@
 
         try:
             self.context.begin(name, data, info)
-        except (KeyboardInterrupt, SystemExit):
+        except (KeyboardInterrupt, SystemExit): #pragma NO COVER
             raise
         except:
             if self.testing:
                 raise
-            raise ZopeXMLConfigurationError(info, sys.exc_info()[0],
-                sys.exc_info()[1]), None, sys.exc_info()[2]
+            reraise(ZopeXMLConfigurationError(info,
+                                              sys.exc_info()[0],
+                                              sys.exc_info()[1]),
+                    None, sys.exc_info()[2])
 
         self.context.setInfo(info)
 
     def evaluateCondition(self, expression):
         """Evaluate a ZCML condition.
 
-        `expression` is a string of the form "verb arguments".
+        ``expression`` is a string of the form "verb arguments".
 
-        Currently the supported verbs are 'have', 'not-have',
-        'installed' and 'not-installed'.
-
-        The 'have' verb takes one argument: the name of a feature.
-
-        >>> from zope.configuration.config import ConfigurationContext
-        >>> context = ConfigurationContext()
-        >>> context.provideFeature('apidoc')
-        >>> c = ConfigurationHandler(context, testing=True)
-        >>> c.evaluateCondition("have apidoc")
-        True
-        >>> c.evaluateCondition("not-have apidoc")
-        False
-        >>> c.evaluateCondition("have onlinehelp")
-        False
-        >>> c.evaluateCondition("not-have onlinehelp")
-        True
-
-        Ill-formed expressions raise an error
-
-        >>> c.evaluateCondition("want apidoc")
-        Traceback (most recent call last):
-          ...
-        ValueError: Invalid ZCML condition: 'want apidoc'
-
-        >>> c.evaluateCondition("have x y")
-        Traceback (most recent call last):
-          ...
-        ValueError: Only one feature allowed: 'have x y'
-
-        >>> c.evaluateCondition("have")
-        Traceback (most recent call last):
-          ...
-        ValueError: Feature name missing: 'have'
-
-
-        The 'installed' verb takes one argument: the dotted name of a
-        pacakge. If the pacakge is found, in other words, can be imported,
-        then the condition will return true.
-
-        >>> from zope.configuration.config import ConfigurationContext
-        >>> context = ConfigurationContext()
-        >>> c = ConfigurationHandler(context, testing=True)
-        >>> c.evaluateCondition('installed zope.interface')
-        True
-        >>> c.evaluateCondition('not-installed zope.interface')
-        False
-        >>> c.evaluateCondition('installed zope.foo')
-        False
-        >>> c.evaluateCondition('not-installed zope.foo')
-        True
-
-        Ill-formed expressions raise an error
-
-        >>> c.evaluateCondition("installed foo bar")
-        Traceback (most recent call last):
-          ...
-        ValueError: Only one package allowed: 'installed foo bar'
-
-        >>> c.evaluateCondition("installed")
-        Traceback (most recent call last):
-          ...
-        ValueError: Package name missing: 'installed'
+        Currently the supported verbs are ``have``, ``not-have``,
+        ``installed`` and ``not-installed``.
         """
         arguments = expression.split(None)
         verb = arguments.pop(0)
@@ -357,13 +270,15 @@
 
         try:
             self.context.end()
-        except (KeyboardInterrupt, SystemExit):
+        except (KeyboardInterrupt, SystemExit): #pragma NO COVER
             raise
         except:
             if self.testing:
                 raise
-            raise ZopeXMLConfigurationError(info, sys.exc_info()[0],
-                sys.exc_info()[1]), None, sys.exc_info()[2]
+            reraise(ZopeXMLConfigurationError(info,
+                                              sys.exc_info()[0],
+                                              sys.exc_info()[1]),
+                    None, sys.exc_info()[2])
 
 
 def processxmlfile(file, context, testing=False):
@@ -379,7 +294,8 @@
     try:
         parser.parse(src)
     except SAXParseException:
-        raise ZopeSAXParseException(sys.exc_info()[1]), None, sys.exc_info()[2]
+        reraise(ZopeSAXParseException(sys.exc_info()[1]),
+                None, sys.exc_info()[2])
 
 
 def openInOrPlain(filename):
@@ -388,56 +304,18 @@
     If the requested file does not exist and filename.in does, fall
     back to filename.in.  If opening the original filename fails for
     any other reason, allow the failure to propogate.
-
-    For example, the tests/samplepackage dirextory has files:
-
-       configure.zcml
-       configure.zcml.in
-       foo.zcml.in
-
-    If we open configure.zcml, we'll get that file:
-
-    >>> here = os.path.dirname(__file__)
-    >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml')
-    >>> f = openInOrPlain(path)
-    >>> f.name[-14:]
-    'configure.zcml'
-
-    But if we open foo.zcml, we'll get foo.zcml.in, since there isn't a
-    foo.zcml:
-
-    >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml')
-    >>> f = openInOrPlain(path)
-    >>> f.name[-11:]
-    'foo.zcml.in'
-
-    Make sure other IOErrors are re-raised.  We need to do this in a
-    try-except block because different errors are raised on Windows and
-    on Linux.
-
-    >>> try:
-    ...     f = openInOrPlain('.')
-    ... except IOError:
-    ...     print "passed"
-    ... else:
-    ...     print "failed"
-    ...
-    passed
-
     """
     try:
-        fp = open(filename)
-    except IOError, (code, msg):
+        return open(filename)
+    except IOError as e:
+        code, msg = e.args
         if code == errno.ENOENT:
             fn = filename + ".in"
             if os.path.exists(fn):
-                fp = open(fn)
-            else:
-                raise
-        else:
-            raise
-    return fp
+                return open(fn)
+        raise
 
+
 class IInclude(Interface):
     """The ``include``, ``includeOverrides`` and ``exclude`` directives
 
@@ -446,21 +324,21 @@
     files in each package and then link them together.
     """
 
-    file = schema.BytesLine(
-        title=u"Configuration file name",
-        description=u"The name of a configuration file to be included/excluded, "
-                    u"relative to the directive containing the "
-                    u"including configuration file.",
+    file = NativeStringLine(
+        title=u("Configuration file name"),
+        description=u("The name of a configuration file to be included/"
+                      "excluded, relative to the directive containing the "
+                      "including configuration file."),
         required=False,
         )
 
-    files = schema.BytesLine(
-        title=u"Configuration file name pattern",
-        description=u"""
+    files = NativeStringLine(
+        title=u("Configuration file name pattern"),
+        description=u("""
         The names of multiple configuration files to be included/excluded,
         expressed as a file-name pattern, relative to the directive
-        containing the including or excluding configuration file.  The pattern
-        can include:
+        containing the including or excluding configuration file.
+        The pattern can include:
 
         - ``*`` matches 0 or more characters
 
@@ -472,16 +350,16 @@
 
         The file names are included in sorted order, where sorting is
         without regard to case.
-        """,
+        """),
         required=False,
         )
 
-    package = config.fields.GlobalObject(
-        title=u"Include or exclude package",
-        description=u"""
-        Include or exclude the named file (or configure.zcml) from the directory
-        of this package.
-        """,
+    package = GlobalObject(
+        title=u("Include or exclude package"),
+        description=u("""
+        Include or exclude the named file (or configure.zcml) from the
+        directory of this package.
+        """),
         required=False,
         )
 
@@ -498,40 +376,16 @@
     elif not file:
         file = 'configure.zcml'
 
-    # BBB 2006/12/19 -- to be removed after 12 months
-    # This is a backward-compatibility support for old site.conf
-
-    if package and (package.__name__ == 'zope.app'):
-        try:
-            import zope.app.zcmlfiles
-        except ImportError:
-            pass # maybe this is an old zope without zope.app.zcmlfiles
-        else:
-            dirpath, filename = os.path.split(file)
-            # be careful, because zope.app is a namespace package
-            # we can't assume that zcmlfiles is a subdirectory of the 
-            # zope.app package
-            dirpath = os.path.dirname(zope.app.zcmlfiles.__file__)
-            file = os.path.join(dirpath, filename)
-            import warnings
-            warnings.warn('In configuration file: %s '
-                          'replace: <include package="zope.app" /> '
-                          'with: <include package="zope.app.zcmlfiles" /> '
-                          'This will go away in Zope 3.6.' % os.path.abspath(file),
-                          DeprecationWarning,
-                          2)
-
     # This is a tad tricky. We want to behave as a grouping directive.
 
-    context = config.GroupingContextDecorator(_context)
+    context = GroupingContextDecorator(_context)
     if package is not None:
         context.package = package
         context.basepath = None
 
     if files:
         paths = glob(context.path(files))
-        paths = zip([path.lower() for path in paths], paths)
-        paths.sort()
+        paths = sorted(zip([path.lower() for path in paths], paths))
         paths = [path for (l, path) in paths]
     else:
         paths = [context.path(file)]
@@ -543,7 +397,7 @@
 
             context.basepath = os.path.dirname(path)
             context.includepath = _context.includepath + (f.name, )
-            _context.stack.append(config.GroupingStackItem(context))
+            _context.stack.append(GroupingStackItem(context))
 
             processxmlfile(f, context)
             f.close()
@@ -564,15 +418,14 @@
         file = 'configure.zcml'
 
 
-    context = config.GroupingContextDecorator(_context)
+    context = GroupingContextDecorator(_context)
     if package is not None:
         context.package = package
         context.basepath = None
 
     if files:
         paths = glob(context.path(files))
-        paths = zip([path.lower() for path in paths], paths)
-        paths.sort()
+        paths = sorted(zip([path.lower() for path in paths], paths))
         paths = [path for (l, path) in paths]
     else:
         paths = [context.path(file)]
@@ -607,7 +460,7 @@
     # and munge the includepath:
     newactions = []
 
-    for action in config.resolveConflicts(_context.actions[nactions:]):
+    for action in resolveConflicts(_context.actions[nactions:]):
         action['includepath'] = includepath
         newactions.append(action)
 
@@ -617,16 +470,16 @@
     # We have to use the direct definition functions to define
     # a directive for all namespaces.
 
-    config.defineSimpleDirective(
+    defineSimpleDirective(
         context, "include", IInclude, include, namespace="*")
 
-    config.defineSimpleDirective(
+    defineSimpleDirective(
         context, "exclude", IInclude, exclude, namespace="*")
 
-    config.defineSimpleDirective(
+    defineSimpleDirective(
         context, "includeOverrides", IInclude, includeOverrides, namespace="*")
 
-    config.defineGroupingDirective(
+    defineGroupingDirective(
         context,
         name="configure",
         namespace="*",
@@ -639,7 +492,7 @@
     """
 
     if context is None:
-        context = config.ConfigurationMachine()
+        context = ConfigurationMachine()
         registerCommonDirectives(context)
         context.package = package
 
@@ -652,10 +505,8 @@
 def string(s, context=None, name="<string>", execute=True):
     """Execute a zcml string
     """
-    from StringIO import StringIO
-
     if context is None:
-        context = config.ConfigurationMachine()
+        context = ConfigurationMachine()
         registerCommonDirectives(context)
 
     f = StringIO(s)
@@ -675,7 +526,7 @@
 _context = None
 def _clearContext():
     global _context
-    _context = config.ConfigurationMachine()
+    _context = ConfigurationMachine()
     registerCommonDirectives(_context)
 
 def _getContext():
@@ -684,9 +535,9 @@
         _clearContext()
         try:
             from zope.testing.cleanup import addCleanUp
-        except ImportError:
+        except ImportError: #pragma NO COVER
             pass
-        else:
+        else: #pragma NO COVER
             addCleanUp(_clearContext)
             del addCleanUp
     return _context
@@ -711,7 +562,7 @@
     context.execute_actions(testing=testing)
 
 
-def testxmlconfig(file, context=None):
+def testxmlconfig(file):
     """xmlconfig that doesn't raise configuration errors
 
     This is useful for testing, as it doesn't mask exception types.

Modified: zope.configuration/trunk/src/zope/configuration/zopeconfigure.py
===================================================================
--- zope.configuration/trunk/src/zope/configuration/zopeconfigure.py	2012-05-10 23:26:50 UTC (rev 125835)
+++ zope.configuration/trunk/src/zope/configuration/zopeconfigure.py	2012-05-11 00:04:13 UTC (rev 125836)
@@ -99,10 +99,14 @@
 """
 __docformat__ = 'restructuredtext'
 import os
-import zope.configuration.config as config
-from zope import schema
+
 from zope.interface import Interface
+from zope.schema import BytesLine
 
+from zope.configuration.config import GroupingContextDecorator
+from zope.configuration.fields import GlobalObject
+from zope.configuration._compat import u
+
 class IZopeConfigure(Interface):
     """The ``zope:configure`` Directive
 
@@ -116,24 +120,24 @@
     be applied whereever it is convenient. 
     """
 
-    package = config.fields.GlobalObject(
-        title=u"Package",
-        description=u"The package to be used for evaluating relative imports "
-                    u"and file names.",
+    package = GlobalObject(
+        title=u("Package"),
+        description=u("The package to be used for evaluating relative imports "
+                      "and file names."),
         required=False)
 
-    i18n_domain = schema.BytesLine(
-        title=u"Internationalization domain",
-        description=u"This is a name for the software project. It must be a "
-                    u"legal file-system name as it will be used to contruct "
-                    u"names for directories containing translation data. "
-                    u"\n"
-                    u"The domain defines a namespace for the message ids "
-                    u"used by a project.",
+    i18n_domain = BytesLine(
+        title=u("Internationalization domain"),
+        description=u("This is a name for the software project. It must be a "
+                      "legal file-system name as it will be used to contruct "
+                      "names for directories containing translation data. "
+                      "\n"
+                      "The domain defines a namespace for the message ids "
+                      "used by a project."),
         required=False)
 
 
-class ZopeConfigure(config.GroupingContextDecorator):
+class ZopeConfigure(GroupingContextDecorator):
     __doc__ = __doc__
 
     def __init__(self, context, **kw):

Copied: zope.configuration/trunk/tox.ini (from rev 125835, zope.configuration/branches/tseaver-test_cleanup/tox.ini)
===================================================================
--- zope.configuration/trunk/tox.ini	                        (rev 0)
+++ zope.configuration/trunk/tox.ini	2012-05-11 00:04:13 UTC (rev 125836)
@@ -0,0 +1,56 @@
+[tox]
+envlist = 
+# Jython support pending 2.7 support, due 2012-07-15 or so.  See:
+# http://fwierzbicki.blogspot.com/2012/03/adconion-to-fund-jython-27.html
+#   py26,py27,py32,jython,pypy,coverage
+    py26,py27,py32,pypy,coverage,docs
+
+[testenv]
+deps =
+    zope.configuration[test]
+commands = 
+# Temporarily install the zope.schema trunk
+    pip uninstall -y zope.schema
+    pip install svn+ssh://svn.zope.org/repos/main/zope.schema/trunk
+    python setup.py test -q
+
+[testenv:jython]
+commands = 
+   jython setup.py test -q
+
+[testenv:coverage]
+basepython =
+    python2.6
+commands = 
+# Temporarily install the zope.schema trunk
+    pip uninstall -y zope.schema
+    pip install svn+ssh://svn.zope.org/repos/main/zope.schema/trunk
+#   The installed version messes up nose's test discovery / coverage reporting
+#   So, we uninstall that from the environment, and then install the editable
+#   version, before running nosetests.
+    pip uninstall -y zope.configuration
+    pip install -e .
+    nosetests --with-xunit --with-xcoverage
+deps =
+    zope.configuration[test]
+    zope.interface>=3.6.0
+    zope.event
+    zope.testing
+    nose
+    coverage
+    nosexcover
+
+[testenv:docs]
+basepython =
+    python2.6
+commands = 
+# Temporarily install the zope.schema trunk
+    pip uninstall -y zope.schema
+    pip install svn+ssh://svn.zope.org/repos/main/zope.schema/trunk
+    sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
+    sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
+deps =
+    zope.interface>=3.6.0
+    zope.event
+    Sphinx
+    repoze.sphinx.autointerface



More information about the checkins mailing list