[Zope3-checkins] CVS: Zope3/src/zope/configuration - __init__.py:1.2 action.py:1.2 exceptions.py:1.2 hookregistry.py:1.2 meta.py:1.2 meta.zcml:1.2 metaconfigure.py:1.2 metameta.zcml:1.2 metametaconfigure.py:1.2 metametaconfigurefordocgen.py:1.2 metametafordocgen.zcml:1.2 name.py:1.2 xmlconfig.py:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:14:06 -0500


Update of /cvs-repository/Zope3/src/zope/configuration
In directory cvs.zope.org:/tmp/cvs-serv15352/src/zope/configuration

Added Files:
	__init__.py action.py exceptions.py hookregistry.py meta.py 
	meta.zcml metaconfigure.py metameta.zcml metametaconfigure.py 
	metametaconfigurefordocgen.py metametafordocgen.zcml name.py 
	xmlconfig.py 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/zope/configuration/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/__init__.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,37 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Zope configuration support
+
+Software that wants to provide new config directives calls
+zope.configuration.meta.register.
+"""
+
+def namespace(suffix):
+    return 'http://namespaces.zope.org/'+suffix
+
+import sys, os
+from zope.configuration.xmlconfig import XMLConfig
+
+def config(dir):
+    try:
+        XMLConfig(os.path.join(dir, 'site.zcml'))()
+    except:
+        # Use the ExceptionFormatter to provide XMLconfig debug info
+        from zope.exceptions.exceptionformatter import format_exception
+        exc_info = ['='*72, '\nZope Configuration Error\n', '='*72, '\n'] \
+                   + apply(format_exception, sys.exc_info())
+        sys.stderr.write(''.join(exc_info))
+        sys.exit(0) # Fatal config error
+
+__all__ = ["namespace", "config"]


=== Zope3/src/zope/configuration/action.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/action.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,20 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+def Action(discriminator, callable, args=(), kw={}):
+    return discriminator, callable, args, kw


=== Zope3/src/zope/configuration/exceptions.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/exceptions.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Standard configuration errors
+"""
+
+class ConfigurationError(Exception):
+    """There was an error in a configuration
+    """


=== Zope3/src/zope/configuration/hookregistry.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/hookregistry.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,143 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+from types import ModuleType
+from zope.exceptions import DuplicationError, NotFoundError, ZopeError
+from zope.configuration import name
+
+class MissingHookableError(NotFoundError):
+    """the stated hook has not been registered"""
+
+class DuplicateHookError(DuplicationError):
+    """an implementation for the given hook has already been registered"""
+
+class BadHookableError(ZopeError):
+    """hookable cannot be found or is not usable"""
+
+class BadHookError(ZopeError):
+    """hook cannot be set"""
+
+class HookRegistry:
+    def __init__(self):
+        self._reg = {}
+
+    def addHookable(self, hname):
+        if hname in self._reg:
+            raise DuplicationError(hname)
+        try:
+            defaultimpl = name.resolve(hname)
+        except ImportError:
+            raise BadHookableError("hookable %s cannot be found" % hname)
+
+        parent, last=self._getParentAndLast(hname)
+        implfunc="%s_hook" % last
+
+        if getattr(parent, implfunc, self) is self:
+            raise BadHookableError(
+                """default hookable implementation (%s) cannot be found;
+                note it must be in the same module as the hookable""" %
+                implfunc)
+
+        self._reg[hname] = 0
+
+    def addHook(self, hookablename, hookname):
+
+        if not (hookablename in self._reg):
+            raise MissingHookableError(hookablename)
+        if self._reg[hookablename]:
+            raise DuplicateHookError(hookablename, hookname)
+        try:
+            implementation = name.resolve(hookname)
+        except ImportError:
+            raise BadHookError('cannot find implementation', hookname)
+        try:
+            hookableDefault=name.resolve(hookablename)
+        except:
+            raise BadHookableError(
+                'hookable cannot be found, but was found earlier: '
+                'some code has probably masked the hookable',
+                hookablename)
+
+        # This won't work as is: I'd have to create a NumberTypes and do
+        # various annoying checks
+        #if type(implementation) is not type (hookableDefault):
+        #    raise BadHookError(
+        #        'hook and hookable must be same type')
+
+        # if they are functions, could check to see if signature is same
+        # (somewhat tricky because functions and methods could be
+        # interchangable but would have a different signature because
+        # of 'self')
+
+        # for now I'll leave both of the above to the sanity of the site
+        # configuration manager...
+
+        # find and import immediate parent
+
+        parent,last = self._getParentAndLast(hookablename)
+
+        # set parent.last to implementation
+        setattr(parent, "%s_hook" % last, implementation)
+
+        self._reg[hookablename] = hookname
+
+    def _getParentAndLast(self, hookablename):
+        if hookablename.endswith('.') or hookablename.endswith('+'):
+            hookablename = hookablename[:-1]
+            repeat = 1
+        else:
+            repeat = 0
+        names = hookablename.split(".")
+        last = names.pop()
+        importname = ".".join(names)
+        if not importname:
+            if not repeat:
+                raise BadHookableError(
+                    'hookable cannot be on top level of Python namespace',
+                    hookablename)
+            importname = last
+        parent = __import__(importname, {}, {}, ('__doc__',))
+        child = getattr(parent, last, self)
+        if child is self:
+            raise BadHookableError(
+                'hookable cannot be on top level of Python namespace',
+                hookablename)
+        while repeat:
+            grand = getattr(child, last, self)
+            if grand is self:
+                break
+            parent = child
+            child = grand
+
+        if type(parent) is not ModuleType:
+            raise BadHookableError("parent of hookable must be a module")
+
+        return parent, last
+
+    def getHooked(self):
+        return [(key, self._reg[key])
+                for key in self._reg
+                if self._reg[key]]
+
+    def getUnhooked(self):
+        return [(key, self._reg[key])
+                for key in self._reg
+                if not self._reg[key]]
+
+    def getHookables(self):
+        return [(key, self._reg[key])
+                for key in self._reg]


=== Zope3/src/zope/configuration/meta.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/meta.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,360 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Registration of registration directives
+
+See IEmptyDirective, INonEmptyDirective, and ISubdirectiveHandler.
+
+$Id$
+"""
+
+
+from zope.interfaces.configuration import INonEmptyDirective
+from zope.interfaces.configuration import ISubdirectiveHandler
+
+class InvalidDirective(Exception):
+    """An invalid directive was used"""
+
+class BrokenDirective(Exception):
+    """A directive is implemented incorrectly"""
+
+class InvalidDirectiveDefinition(Exception):
+    """The definition of a directive is incomplete or incorrect"""
+
+
+#
+# Registry data structure and manipulation functions.
+#
+
+# _directives is a registry that holds information on zcml directives
+# and subdirectives.  It is filled by the 'directives', 'directive' and
+# 'subdirective' directives that are provided as the bootstrap
+# directives by the _clear function of this module.
+#
+# The top level of the registry is a dictionary keyed by two element
+# tuples.  Each key tuple consists of a namespace designator (such as
+# http://namespaces.zope.org/zope) and a directive name.  Thus, the
+# key that accesses the 'directive' directive is::
+#
+#     (http://namespaces.zope.org/zope', 'directive')
+#
+# The value of a directive entry is a two element tuple consisting
+# of a callable and a (possibly empty) subdirective registry.  The
+# callable is the object to be called to process the directive and
+# its parameters.  The callable must be either an IEmptyDirective (in
+# which case the subdirective registry should be empty), or an
+# INonEmptyDirective (in which case there should be one or more entries
+# in the subdirective registry).
+#
+# A subdirective registry is also keyed by (ns, name) tuples.  Handler
+# methods for subdirectives are looked up on the ISubdirectiveHandler
+# object that is returned by the INonEmptyDirective that handles
+# the directive to which the subdirective registry belongs.
+# INonEmptyDirective objects are thus most often classes.
+#
+# The value of an entry in the subdirective registry is a tuple of
+# two elements.  The first element is a subdirective registry, and
+# the second is the name to be looked up to find the callable that
+# will handle the processing of the subdirective.  That callable
+# should implement either IEmtpyDirective or INonEmptyDirective.  The
+# accompanying sub-subdirective registry should be empty or not,
+# accordingly.
+
+_directives = {}
+
+def register(name, callable):
+    """Register a top-level directive
+
+    The name argument is a tuple with a namespace URI and an
+    name string.
+
+    The callable must be am IEmptyDirective or an INonEmptyDirective.
+
+    INonEmptyDirective directives may have subdirectives. The
+    subdirectives will be registered in a registry that is stored with
+    the directive. The sub-directive registry is returned so that
+    it can be used for subsequent sub-directive registration.
+
+    If the same name is registered a second time, the existing
+    subdirective registry will be returned.
+
+    """
+
+    subdirs = _directives.get(name,(None,{}))[1]
+    _directives[name] = callable, subdirs
+    return subdirs
+
+def registersub(directives, name, handler_method=None):
+    """Register a subdirective
+
+    directives is the subdirective registry for the containing
+    directive, which may be either a top-level directive or an
+    intermediate sub-directive (if subdirectives are nested more than
+    two deep).
+
+    The name argument is a tuple with a namespace URI and an
+    name string.
+
+    The handler is not passed as it normally is for top-level
+    directives. Rather, the handler will be looked up as an attribute
+    of the ISubdirectiveHandler returned by INonEmptyDirective whose
+    associated registry we have been passed.  The string to be
+    looked up is set to the second element of the name tuple, unless
+    the optional handler attribute is used to provide the lookup
+    string explicitly.
+
+    Subdirectives may have subdirectives. The subdirectives will be
+    registered in a registry that is stored with the containing
+    subdirective. The sub-directive registry is returned so that it
+    can be used for subsequent sub-directive registration.
+
+    If the same name is registered a second time, the existing
+    subdirective registry will be returned.
+
+    """
+    if not handler_method:
+        handler_method = name[1]
+    subdirs = directives.get(name,({},))[0]
+    directives[name] = subdirs, handler_method
+    return subdirs
+
+#
+# Parser handler methods.  These methods are called by the code that
+# parses configuration data to process the directives and subdirectives.
+# 'begin' is called to start processing a directive.  Its return
+# value should be saved.  When the directive is closed (which will
+# be right away for IEmptyDirectives), that saved return value is
+# passed to end, which will return a list of actions to append to
+# the action list.  Nested subdirectives are processed similarly,
+# except that 'sub' is called to start them rather than begin, and
+# the first argument passed to sub should be the tuple returned
+# by begin (or sub) for the enclosing (sub)directive (it will be
+# an ISubdirectiveHandler, subdirectiveregistry pair).  The
+# end result will be a list of actions.  See IEmptyDirective for a
+# description of the actions data structure.
+#
+
+def _exe(callable, subs, context, kw):
+    """Helper function to turn an IxxxDirective into a (callable, subs) tuple
+    """
+
+    # We've either got an IEmptyDirective or an INonEmptyDirective here.
+    # For the former, we're going to get back a list of actions when
+    # we call it.  For the latter, we're going to get back an
+    # ISubdirectiveHandler.  We need to return something that end
+    # can call the first element of to get a list of actions.
+    # ISubdirectiveHandler qualifies, but we'll have to manufacture
+    # one if we got a list of actions.  When we return the
+    # ISubdirectiveHandler, the parsing code calling begin/sub is
+    # going to pass the tuple along to sub in order to process the
+    # subdirectives.
+
+    r = callable(context, **kw)
+
+    if INonEmptyDirective.isImplementedBy(callable):
+        return r, subs
+    else:
+        return (
+            (lambda: r),
+            subs,
+            )
+
+def begin(_custom_directives, _name, _context, **kw):
+    """Begin executing a top-level directive
+
+    A custom registry is provided to provide specialized directive
+    handlers in addition to the globally registered directives. For
+    example, the XML configuration mechanism uses these to provide XML
+    configuration file directives.
+
+    The _name argument is a tuple with a namespace URI and a
+    name string.
+
+    The _context argument is an execution context object that
+    directives use for functions like resolving names. It will be
+    passed as the first argument to the directive handler.
+
+    kw are the directive arguments.
+
+    The return value is a tuple that contains:
+
+    - An object to be called to finish directive processing. This
+      object will return a sequence of actions. The object must be
+      called after sub-directives are processed.
+
+    - A registry for looking up subdirectives.
+
+    """
+
+    if _custom_directives and (_name in _custom_directives):
+        callable, subs = _custom_directives[_name]
+    else:
+        try:
+            callable, subs = _directives[_name]
+        except KeyError:
+            raise InvalidDirective(_name)
+
+    return _exe(callable, subs, _context, kw)
+
+def sub(handlertuple, _name, _context, **kw):
+    """Begin executing a subdirective
+
+    The first argument, handlertuple, is a pair consisting of
+    an ISubdirectiveHandler and a registry of allowable subdirectives
+    for the containing directive or subdirective.
+
+    The _name argument is a tuple with a namespace URI and a
+    name string, naming the subdirective we are executing.
+
+    The _context argument is an execution context object that
+    directives use for functions like resolving names. It will be
+    passed as the first argument to the directive handler.
+
+    kw are the directive arguments.
+
+    The return value is a tuple that contains:
+
+    - An object to be called to finish directive processing. This
+      object will return a sequence of actions. The object must be
+      called after sub-directives are processed.
+
+    - A registry for looking up sub-subdirectives.
+
+    """
+
+    base, subdirs = handlertuple
+    try:
+        subsubs, handler_method = subdirs[_name]
+    except KeyError:
+        raise InvalidDirective(_name)
+
+    callable = getattr(base, handler_method)
+
+    return _exe(callable, subsubs, _context, kw)
+
+defaultkw = ({},)
+def end(base):
+    """Finish processing a directive or subdirective
+
+    The argument is a return value from begin or sub.  Its first
+    element is called to get a sequence of actions.
+
+    The return value is a list of actions that are normalized to a
+    4-element tuple with a descriminator, a callable, positional
+    arguments, and keyword arguments.
+    """
+
+    actions = base[0]()
+    ractions = []
+    for action in actions:
+        if len(action) < 3 or len(action) > 4:
+            raise BrokenDirective(action)
+        if len(action) == 3:
+            action += defaultkw
+        ractions.append(action)
+    return ractions
+
+
+#
+# The code below provides the implementation for the directives,
+# directive, and subdirective handlers.  These will be called
+# via begin and sub when the code parsing a (meta) configuration
+# file encounters these directives.  The association between
+# the directive names and the particular callables is set up
+# in _clear.
+#
+
+class DirectiveNamespace:
+
+    __class_implements__ = INonEmptyDirective
+    __implements__ = ISubdirectiveHandler
+
+    def __init__(self, _context, namespace):
+        self._namespace = namespace
+
+    def _register(self, _context, name, handler, namespace=None,
+                  attributes=''):
+        namespace = namespace or self._namespace
+        subs = register((namespace, name), _context.resolve(handler))
+        return subs, namespace
+
+    def directive(self, *args, **kw):
+        subs, namespace = self._register(*args, **kw)
+        return Subdirective(subs, namespace=namespace)
+    directive.__implements__ = INonEmptyDirective
+
+    def __call__(self):
+        return ()
+
+
+class Subdirective:
+    """This is the meta-meta-directive"""
+    #
+    # Unlike other directives, it doesn't return any actions, but
+    # takes action right away, since its actions are needed to process other
+    # directives.
+    #
+    # For this reason, this isn't a good directive example.
+
+    __implements__ = ISubdirectiveHandler
+
+    def __init__(self, subs, namespace=None):
+        self._subs = subs
+        self._namespace = namespace
+
+    def _register(self, _context, name, namespace=None, handler_method=None,
+                  attributes=''):
+        namespace = namespace or self._namespace
+        if not namespace:
+            raise InvalidDirectiveDefinition(name)
+        #If handler_method is None, registersub will use name.
+        subs = registersub(self._subs, (namespace, name), handler_method)
+        return subs, namespace
+
+    def subdirective(self, *args, **kw):
+        subs, namespace = self._register(*args, **kw)
+        return Subdirective(subs,namespace=namespace)
+    subdirective.__implements__ = INonEmptyDirective
+
+    def __call__(self):
+        return ()
+
+def _clear():
+    """Initialize _directives data structure with bootstrap directives."""
+
+    # We initialize _directives with handlers for three (sub)directives:
+    # directives, directive, and subdirective.  Given these three
+    # (whose implementation is contained in this module) we can use
+    # zcml to define any other directives needed for a given system.
+    #
+    # The data structure created here is recursive.  This allows for
+    # an unlimited number of levels of subdirective definition
+    # nesting.
+    #
+    # This initialziation is done in a function to facilitate support
+    # the unittest CleanUp class.
+
+    _directives.clear()
+    zopens = 'http://namespaces.zope.org/zope'
+    subdirkey = (zopens, 'subdirective')
+    subs = {}
+    subs[subdirkey] = (subs, 'subdirective')
+    directive = {(zopens, 'directive'): (subs, 'directive')}
+    _directives[(zopens, 'directives')] = (DirectiveNamespace, directive)
+
+_clear()
+
+# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
+from zope.testing.cleanup import addCleanUp
+addCleanUp(_clear)
+del addCleanUp


=== Zope3/src/zope/configuration/meta.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/meta.zcml	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,11 @@
+<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
+
+  <!-- zope.configure -->
+  <directives namespace="http://namespaces.zope.org/zope">
+    <directive name="hookable" attributes="name module"
+       handler="zope.configuration.metaconfigure.provideHookable" />
+    <directive name="hook" attributes="name implementation module"
+       handler="zope.configuration.metaconfigure.provideHook" />
+  </directives>
+
+</zopeConfigure>


=== Zope3/src/zope/configuration/metaconfigure.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/metaconfigure.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+from zope.configuration.action import Action
+from zope.configuration.hookregistry import HookRegistry
+
+# one could make hookRegistry a service and
+# theoretically use it TTW, but that doesn't immediately seem like a
+# great idea
+hookRegistry = HookRegistry()
+
+addHookable = hookRegistry.addHookable
+addHook = hookRegistry.addHook
+
+def provideHookable(_context, name, module=None):
+    if module:
+        name = "%s.%s" % (module, name)
+    name = _context.getNormalizedName(name)
+    return [
+        Action(
+            discriminator=('addHookable', name),
+            callable=addHookable,
+            args=(name,)
+            )
+        ]
+
+
+def provideHook(_context, name, implementation, module=None):
+    if module:
+        name = "%s.%s" % (module, name)
+    name = _context.getNormalizedName(name)
+    implementation = _context.getNormalizedName(implementation)
+    return [
+        Action(
+            discriminator=('addHook', name),
+            callable=addHook,
+            args=(name, implementation)
+            )
+        ]


=== Zope3/src/zope/configuration/metameta.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/metameta.zcml	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,54 @@
+<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
+
+<directives namespace="http://namespaces.zope.org/zope">
+
+  <!-- Modify the bootstrap meta configuration directives
+
+  Note that we are modifying the handler we are using to
+  process the text that is triggering the modifications.
+  However, since modifications are non-destructive of
+  any pre-existing subdirective structure, this works.
+  Further, since the bootstrap namespace for subdirective
+  is recursive, we only have to modify subdirective once.
+
+  The handler routines called out by the declarations in this
+  file simply ignore the additional information provided by the
+  modified directives, which at the moment is what we want
+  done during normal running of zope.
+
+  XXX: In the current implementation of the bootstrap code, the
+  'attributes' attribute doesn't actually do anything.  I include
+  it anyway in case the bootstrap code gets modified to actually
+  do something with attributes.  This however seems unlikely,
+  since it would make more sense to modify the attribute subdirective
+  we are introducing here.  So after people are comfortable with
+  this meta-meta configuration, we could simplify the bootstrap
+  by doing away with 'attributes' in the bootstrap code and here.
+  -->
+
+  <directive
+      name="directives"
+      attributes="namespace name handler attributes description"
+      handler="zope.configuration.metametaconfigure.DirectiveNamespace"
+    >
+    <subdirective
+        name="directive"
+        attributes="namespace name handler attributes description"
+    >
+      <subdirective
+          name="attribute"
+          attributes="name description required" />
+      <subdirective
+          name="subdirective"
+          attributes="name attributes namespace handler_method description"
+      >
+        <subdirective
+            name="attribute"
+            attributes="name description required" />
+      </subdirective>
+    </subdirective>
+  </directive>
+     
+</directives>
+
+</zopeConfigure>


=== Zope3/src/zope/configuration/metametaconfigure.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:04 2002
+++ Zope3/src/zope/configuration/metametaconfigure.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,71 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+from zope.configuration.meta import DirectiveNamespace as bootstrapDirectiveNamespace
+from zope.configuration.meta import Subdirective as bootstrapSubdirective
+from zope.interfaces.configuration import INonEmptyDirective
+from zope.interfaces.configuration import IEmptyDirective
+from zope.interfaces.configuration import ISubdirectiveHandler
+
+#
+# Meta-meta configuration.  These routines replace the bootstrap ones
+# defined in meta.py.
+#
+
+class DirectiveNamespace(bootstrapDirectiveNamespace):
+
+    __class_implements_ = INonEmptyDirective
+    __implements__ = ISubdirectiveHandler
+
+    def _Subdirective(self, *args, **kw): return Subdirective(*args, **kw)
+
+    def _useDescription(self, namespace, name, handler, description, subs): pass
+
+    def directive(self, _context, name, handler, attributes='',
+            namespace=None, description=''):
+        subs, namespace = self._register(_context, name, handler, namespace)
+        self._useDescription(namespace, name, handler, description, subs)
+        return self._Subdirective(subs, namespace=namespace, name=name)
+    directive.__implements__ = INonEmptyDirective
+
+
+class Subdirective(bootstrapSubdirective):
+    """An extended Subdirective that handles descriptions and attributes"""
+
+    __implements__ = ISubdirectiveHandler
+
+    def __init__(self, subs, namespace=None, name=None):
+        bootstrapSubdirective.__init__(self,subs,namespace)
+        self._name = name
+
+    def _useDescription(self, namespace, name, subs, description): pass
+
+    def subdirective(self, _context, name, attributes='',
+                     namespace=None, handler_method=None, description=''):
+        subs, namespace = self._register(_context, name, namespace,
+                                         handler_method)
+        self._useDescription(namespace, name, subs, description)
+        return self.__class__(subs, namespace=namespace, name=name)
+    subdirective.__implements__ = INonEmptyDirective
+
+    def _useAttributeDescription(self, name, required, description): pass
+
+    def attribute(self, _context, name, required='', description=''):
+        required = required.lower()
+        if required not in ('', 'yes', 'no'): raise ValueError(required)
+        self._useAttributeDescription(name, required, description)
+        return ()
+    attribute.__implements__ = IEmptyDirective


=== Zope3/src/zope/configuration/metametaconfigurefordocgen.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:05 2002
+++ Zope3/src/zope/configuration/metametaconfigurefordocgen.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+from zope.configuration.metametaconfigure import DirectiveNamespace as baseDirectiveNamespace
+from zope.configuration.metametaconfigure import Subdirective as baseSubdirective
+from zope.interfaces.configuration import INonEmptyDirective
+from zope.interfaces.configuration import ISubdirectiveHandler
+
+#
+# Versions of the meta configuration directive handlers that save the
+# documentation information as structured data.
+#
+
+"""
+To track the meta-data about configuration directives, we use a
+special key that will never appear as an actual subdirective name.
+So the information stored under that key in a (sub)directive's
+subdirective registry is the meta data about the (sub)directive
+itself.
+
+That data consists of a dictionary with the following keys:
+
+description -- a description of the (sub)directive.  It should
+    explain the semantics of the (sub)directive.
+
+attributes -- a dictionary containing entries for each attribute
+    the (sub)command accepts.  The value of the entries in this
+    dictionary are dictionaries with the following keys:
+
+    description -- a description of the attribute.  It should
+        explain the semantics of the attribute.
+
+    required -- 'yes', 'no', or ''.  Applies to attributes
+        and means what it sounds like it means.  Blank is
+        more or less equivalent to except that an attribute
+        with a blank required might be one that is a member of a
+        set *one* of which is required, while if an explicit
+        'no' is given then the attribute is completely optional.
+        This information will be included in the generated doc strings.
+
+This metadata is intended to serve as the most basic level of documentation
+of the directives, and should be updated along with the directive code
+(which is why it is stored in the meta.zcml file).  The metadata should
+be extracted and made human accessible by a zope-independent program
+and/or a zope-based introspection tool.
+"""
+
+_metadataKey = "__zope.configuration.metadataKey__"
+
+def _recordCommandMetadata(subs, description, handler=None):
+    if _metadataKey not in subs: subs[_metadataKey] = {}
+    md = subs[_metadataKey]
+    if 'attributes' not in md: md['attributes'] = {}
+    if description: md['description'] = ' '.join(description.split())
+    if handler: md['handler'] = handler
+
+
+class DirectiveNamespace(baseDirectiveNamespace):
+    """An extended class that handles descriptions and attributes"""
+
+    __class_implements_ = INonEmptyDirective
+    __implements__ = ISubdirectiveHandler
+
+    def _Subdirective(self, *args, **kw): return Subdirective(*args, **kw)
+
+    def _useDescription(self, namespace, name, handler, description, subs):
+        _recordCommandMetadata(subs, description, handler)
+
+
+class Subdirective(baseSubdirective):
+    """An extended class that handles descriptions and attributes"""
+
+    __implements__ = ISubdirectiveHandler
+
+    def _useDescription(self, namespace, name, subs, description):
+        _recordCommandMetadata(subs, description)
+
+    def _useAttributeDescription(self, name, required, description):
+        attribs = self._subs[_metadataKey]['attributes']
+        attribs[name] = {
+            'description': description and ' '.join(description.split()),
+            'required': required}


=== Zope3/src/zope/configuration/metametafordocgen.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:05 2002
+++ Zope3/src/zope/configuration/metametafordocgen.zcml	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,109 @@
+<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
+
+<directives namespace="http://namespaces.zope.org/zope">
+  <!-- Install the docgen version of the meta configuration handler -->
+  <directive
+      name="directives"
+      handler="zope.configuration.metametaconfigurefordocgen.DirectiveNamespace" />
+</directives>
+
+<directives namespace="http://namespaces.zope.org/zope">
+
+  <!-- Add help text for the meta configuration directives -->
+
+  <directive
+      name="directives"
+      handler="zope.configuration.metametaconfigurefordocgen.DirectiveNamespace"
+      description="Define a set of new configuration directives within a
+          defaultnamespace"
+  >
+    <attribute
+        name="namespace"
+        required="yes"
+        description="XML-style namespace identifier for the namespace
+            in which the directives being defined will reside." />
+    <subdirective
+        name="directive"
+        description="Define a new configuration directive."
+    >
+      <attribute
+          name="name"
+          required="yes"
+          description="the name of the directive." />
+      <attribute
+          name="handler"
+          required="yes"
+          description="resolvable name of an IEmptyDirective or an
+              INonEmptyDirective that is to be called to handle
+              the directive." />
+      <attribute
+          name="namespace"
+          description="XML-style namespace identifier for the namespace
+              in which the directive being defined will reside.  If
+              specified it overrides the namespace inherited from
+              the enclosing directives directive." />
+      <attribute
+          name="description"
+          description="structured Text sentences that document the
+              purpose and function of the directive." />
+      <subdirective
+          name="attribute"
+          description="Define an attribute that may be specified on the
+              directive being defined"
+      >
+        <attribute
+            name="name"
+            required="yes"
+            description="the name of the attribute." />
+        <attribute
+            name="required"
+            required="no"
+            description="'yes', 'no', or missing.  'yes' indicates the attribute
+                being defined *must* be specified when the directive is used.
+                'no' means it can always be omitted.  If not specified, the
+                attribute may be part of a set of attributes one of which may
+                be required." />
+        <attribute
+            name="description"
+            description="structured Text sentences describing the purpose,
+              function, and allowed values of the attribute." />
+      </subdirective>
+      <subdirective
+          name="subdirective"
+          description="Define a subdirective that may be used inside
+              the enclosing directive in a configuration."
+      >
+        <attribute
+            name="name"
+            required="yes"
+            description="the name of the subdirective" />
+        <attribute
+            name="handler_method"
+            description="the name of an IEmptyDirective or an
+                INonEmptyDirective.  The name will be looked up on the
+                ISubDirectiveHandler returned by the handler for
+                the enclosing directive.  If not specified, the
+                subdirective name is used." />
+        <attribute
+            name="description"
+            description="structured Text sentences that document the
+                purpose and function of the subdirective." />
+        <attribute
+            name="namespace"
+            description="XML-style namespace identifier for the namespace
+                in which the directives being defined will reside.  If
+                specified it overrides the namespace inherited from
+                the enclosing directive." />
+        <!--
+             We don't need to redefine the attribute subdirective of
+             the subdirective subdirective, because defining it in
+             directive's namespace defined it in the same (recursive)
+             namespace subdirective uses.
+        -->
+      </subdirective>
+    </subdirective>
+  </directive>
+
+</directives>
+
+</zopeConfigure>


=== Zope3/src/zope/configuration/name.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:05 2002
+++ Zope3/src/zope/configuration/name.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""Provide configuration object name resolution
+
+$Id$
+"""
+
+import os
+import sys
+from types import ModuleType
+
+def resolve(name, package='zopeproducts', _silly=('__doc__',), _globals={}):
+    name = name.strip()
+
+    if name.startswith('.'):
+        name=package+name
+
+    if name.endswith('.') or name.endswith('+'):
+        name = name[:-1]
+        repeat = 1
+    else:
+        repeat = 0
+
+    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)
+        try:
+            a=getattr(m, last)
+        except AttributeError:
+            if not repeat:
+                return __import__(name, _globals, _globals, _silly)
+
+        else:
+            if not repeat or (not isinstance(a, ModuleType)):
+                return a
+        mod += '.' + last
+
+
+def getNormalizedName(name, package):
+    name=name.strip()
+    if name.startswith('.'):
+        name=package+name
+
+    if name.endswith('.') or name.endswith('+'):
+        name = name[:-1]
+        repeat = 1
+    else:
+        repeat = 0
+    name=name.split(".")
+    while len(name)>1 and name[-1]==name[-2]:
+        name.pop()
+        repeat=1
+    name=".".join(name)
+    if repeat:
+        name+="+"
+    return name
+
+def path(file='', package = 'zopeproducts', _silly=('__doc__',), _globals={}):
+    try: package = __import__(package, _globals, _globals, _silly)
+    except ImportError:
+        if file and os.path.abspath(file) == file:
+            # The package didn't matter
+            return file
+        raise
+
+    path = os.path.split(package.__file__)[0]
+    if file:
+        path = os.path.join(path, file)
+    return path


=== Zope3/src/zope/configuration/xmlconfig.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:14:05 2002
+++ Zope3/src/zope/configuration/xmlconfig.py	Wed Dec 25 09:13:33 2002
@@ -0,0 +1,391 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import os
+from zope.configuration import name
+from os.path import abspath
+from xml.sax import make_parser
+from xml.sax.xmlreader import InputSource
+from xml.sax.handler import ContentHandler, feature_namespaces
+from zope.configuration.meta import begin, sub, end
+from keyword import iskeyword
+import sys, os
+from types import StringType
+from zope.configuration.exceptions import ConfigurationError
+
+# marker used in Context class and XMLConfig class to indicate
+# that a particular zcml file was given no "package" attribute
+# when included, and the same went for all of its parents.
+_NO_MODULE_GIVEN = object()
+
+class ZopeXMLConfigurationError(ConfigurationError):
+    "Zope XML Configuration error"
+
+    def __init__(self, locator, mess, etype=None):
+        if etype is None:
+            if not isinstance(mess, StringType):
+                try:
+                    mess = "\n%s:\n  %s" % (mess.__class__.__name__, mess)
+                except AttributeError:
+                    mess = str(mess)
+        else:
+            mess = "\n%s: %s" % (etype.__name__, mess)
+
+        self.lno = locator.getLineNumber()
+        self.cno = locator.getColumnNumber()
+        self.sid = locator.getSystemId()
+        self.mess = mess
+
+    def __str__(self):
+        return 'File "%s", line %s, column %s\n\t%s' % (
+            self.sid, self.lno, self.cno, self.mess)
+
+class ConfigurationExecutionError(ZopeXMLConfigurationError):
+    """An error occurred during execution of a configuration action
+    """
+
+    def __init__(self, locator, mess, etype=None):
+        if etype is None:
+            if isinstance(mess, StringType):
+                try:
+                    mess = "%s: %s" % (mess.__class__.__name__, mess)
+                except AttributeError:
+                    mess = str(mess)
+        else:
+            mess = "\n%s: %s" % (etype.__name__, mess)
+
+        self.lno, self.cno, self.sid = locator
+        self.mess = mess
+
+class ConfigurationHandler(ContentHandler):
+
+    __top_name = 'http://namespaces.zope.org/zope', 'zopeConfigure'
+
+    def __init__(self, actions, context, directives=None, testing=0):
+        self.__stack = []
+        self.__actions = actions
+        self.__directives = directives
+        self.__context = context
+        self.__testing = testing
+
+    def setDocumentLocator(self, locator):
+        self.__locator = locator
+
+    def startElementNS(self, name, qname, attrs):
+        stack = self.__stack
+        if not stack:
+            if name != self.__top_name:
+                raise ZopeXMLConfigurationError(
+                    self.__locator, "Invalid top element: %s %s" % name)
+
+            for (ns, aname), value in attrs.items():
+                if ns is None:
+                    self.__context.file_attr(aname, value)
+
+
+            stack.append(None)
+            return
+
+        kw = {}
+        for (ns, aname), value in attrs.items():
+            if ns is None:
+                aname = str(aname)
+                if iskeyword(aname): aname += '_'
+                kw[aname] = value
+
+        if len(stack) == 1:
+            try:
+                stack.append(
+                    begin(self.__directives, name, self.__context, **kw)
+                    )
+            except Exception, v:
+                if self.__testing:
+                    raise
+                raise ZopeXMLConfigurationError, (
+                    self.__locator, v), sys.exc_info()[2]
+
+        else:
+            subs = self.__stack[-1]
+            if subs is None:
+                raise ZopeXMLConfigurationError(self.__locator,
+                                                'Invalid sub-directive')
+            try:
+                stack.append(sub(subs, name, self.__context, **kw))
+            except Exception, v:
+                if self.__testing:
+                    raise
+                raise ZopeXMLConfigurationError, (
+                    self.__locator, v), sys.exc_info()[2]
+
+    def endElementNS(self, name, qname):
+        subs = self.__stack.pop()
+        # to fool compiler that thinks actions is used before assignment:
+        actions = ()
+
+        if subs is not None:
+            try:
+                actions = end(subs)
+            except Exception, v:
+                if self.__testing:
+                    raise
+                raise ZopeXMLConfigurationError, (
+                    self.__locator, str(v)), sys.exc_info()[2]
+
+        append = self.__actions.append
+
+        try:
+            for des, callable, args, kw in actions:
+                append((self.__context,
+                        (self.__locator.getLineNumber(),
+                         self.__locator.getColumnNumber(),
+                         self.__locator.getSystemId(),
+                         ), des, callable, args, kw))
+        except:
+            print 'endElementNS', actions
+            raise
+
+class ZopeConflictingConfigurationError(ZopeXMLConfigurationError):
+    "Zope XML Configuration error"
+
+    def __init__(self, l1, l2, des):
+        self.l1 = l1
+        self.l2 = l2
+        self.des = des
+
+    def __str__(self):
+        return """Conflicting configuration action:
+        %s
+        File "%s", line %s column %s
+        File "%s", line %s column %s
+        """ % (self.des,
+               self.l1[2], self.l1[0], self.l1[1],
+               self.l2[2], self.l2[0], self.l2[1],
+               )
+
+class Context:
+    def __init__(self, stack, module):
+        self.__stackcopy = tuple(stack)
+        if module is _NO_MODULE_GIVEN:
+            self.__package = None
+        elif module is None:
+            self.__package = 'zopeproducts'
+        else:
+            self.__package = module.__name__
+
+    def _stackcopy(self):
+        return self.__stackcopy
+
+    def resolve(self, dottedname):
+        return name.resolve(dottedname, self.__package)
+
+    def getNormalizedName(self, dottedname):
+        return name.getNormalizedName(dottedname, self.__package)
+
+    def path(self, file=None):
+        return name.path(file, self.__package)
+
+    def file_attr(self, name, value):
+        if name == 'package':
+            self.__package = value
+        else:
+            raise TypeError, "Unrecognized config file attribute: %s" % name
+
+    def packageWasSet(self):
+        return self.__package is not None
+
+    def package(self):
+        return self.__package
+
+def xmlconfig(file, actions=None, context=None, directives=None,
+              testing=0):
+    if context is None:
+        context = name
+
+    if actions is None:
+        call = actions = []
+    else:
+        call = 0
+
+    src = InputSource(getattr(file, 'name', '<string>'))
+    src.setByteStream(file)
+    parser = make_parser()
+    parser.setContentHandler(
+        ConfigurationHandler(actions, context,directives,
+                             testing=testing)
+        )
+    parser.setFeature(feature_namespaces, 1)
+    parser.parse(src)
+
+    if call:
+        descriptors = {}
+        for level, loc, des, callable, args, kw in call:
+            if des is not None:
+                if des in descriptors:
+                    raise ZopeConflictingConfigurationError(
+                        descriptors[des], loc, des)
+                descriptors[des] = loc
+
+            callable(*args, **kw)
+
+def testxmlconfig(file, actions=None, context=None, directives=None):
+    """xmlconfig that doesn't raise configuration errors
+
+    This is useful for testing, as it doesn't mask exception types.
+    """
+    return xmlconfig(file, actions, context, directives, testing=1)
+
+class ZopeConfigurationConflictError(ZopeXMLConfigurationError):
+
+    def __init__(self, conflicts):
+        self._conflicts = conflicts
+
+    def __str__(self):
+        r = ["Conflicting configuration actions"]
+        for dis, locs in self._conflicts.items():
+            r.append('for: %s' % (dis,))
+            for loc in locs:
+                r.append('  File "%s", line %s column %s' %
+                         (loc[2], loc[0], loc[1]))
+
+        return "\n".join(r)
+
+
+class XMLConfig:
+
+    def __init__(self, file_name, module=_NO_MODULE_GIVEN):
+        if module is not None and module is not _NO_MODULE_GIVEN:
+            module_dir = abspath(os.path.split(module.__file__)[0])
+            file_name = os.path.join(module_dir, file_name)
+
+
+        self._actions = []
+        self._directives = {('http://namespaces.zope.org/zope', 'include'):
+                            (self.include, {})}
+
+        f = open(file_name)
+        self._stack = [file_name]
+        xmlconfig(f, self._actions,
+                  Context(self._stack, module=module),
+                  self._directives)
+        f.close()
+
+    def include(self, _context, file='configure.zcml', package=None):
+        if package is None and _context.packageWasSet():
+            package = _context.package()
+        subpackages = False
+        if package is not None:
+            if package.endswith('.*'):
+                # <include package="package.*" /> includes all subpackages
+                subpackages = True
+                parent = package = package[:-2]
+                if package == "":
+                    package = "."
+            try:
+                package = _context.resolve(package)
+                if len(package.__path__) != 1:
+                    print ("Module Path: '%s' has wrong number of elements"
+                            % str(package.__path__))
+                # XXX: This should work for 99% of cases
+                # We may want to revisit this with a more robust
+                # mechanism later. Specifically, sometimes __path__
+                # will have more than one element. Also, we could
+                # use package.__file__, and lop the tail off that.
+                prefix = package.__path__[0]
+            except (ImportError, AttributeError, ValueError), v:
+                raise # XXX the raise below hides the real error
+                raise ValueError("Invalid package attribute: %s\n(%s)"
+                                 % (package, `v`))
+        else:
+            prefix = os.path.dirname(self._stack[-1])
+
+        if subpackages:
+            for subdir in os.listdir(prefix):
+                file_name = os.path.join(prefix, subdir, file)
+                if not os.access(file_name, os.F_OK):
+                    continue
+                subpackage = "%s.%s" % (parent, subdir)
+                subpackage = _context.resolve(subpackage)
+                self._include(file_name, subpackage)
+        else:
+            file_name = os.path.join(prefix, file)
+            self._include(file_name, package)
+        return ()
+
+    def _include(self, file_name, package):
+
+        f = open(file_name)
+        self._stack.append(file_name)
+        xmlconfig(f, self._actions, Context(self._stack, package),
+                  self._directives)
+        self._stack.pop()
+        f.close()
+
+    def __call__(self):
+        self.organize()
+
+    def __iter__(self): return iter(self._actions)
+
+    def organize(self):
+        actions = self._actions
+
+        # organize actions by discriminators
+        unique = {}
+        cactions = []
+        for i in range(len(actions)):
+            context, loc, des, callable, args, kw = actions[i]
+            if des is None:
+                # The descriminator is None, so this directive can
+                # never conflict. We can add it directly to the
+                # configuration actions.
+                cactions.append((i, loc, (callable, args, kw)))
+                continue
+
+            a = unique.setdefault(des, [])
+            a.append((context._stackcopy(), i, loc, (callable, args, kw)))
+
+        # Check for conflicts
+        conflicts = {}
+        for des, actions in unique.items():
+            path, i, loc, f = actions[0]
+            for opath, i, oloc, f in actions[1:]:
+                # Test whether path is a prefix of opath
+                if opath[:len(path)] != path or (opath == path):
+                    if des not in conflicts:
+                        conflicts[des] = [loc]
+                    conflicts[des].append(oloc)
+
+        if conflicts:
+            raise ZopeConfigurationConflictError(conflicts)
+
+        # Now order the configuration directives
+        for des, actions in unique.items():
+            path, i, loc, f = actions.pop(0)
+            cactions.append((i, loc, f))
+
+        unique = None
+
+        cactions.sort()
+
+        # Call actions
+        for i, loc, f in cactions:
+            try:
+                callable, args, kw = f
+                callable(*args, **kw)
+            except Exception, v:
+                raise ConfigurationExecutionError, (
+                    loc, v, sys.exc_info()[0]), sys.exc_info()[2]