[Zope3-checkins] CVS: Zope3/src/zope/configuration - __init__.py:1.1.2.1 action.py:1.1.2.1 exceptions.py:1.1.2.1 hookregistry.py:1.1.2.1 meta.py:1.1.2.1 meta.zcml:1.1.2.1 metaconfigure.py:1.1.2.1 metameta.zcml:1.1.2.1 metametaconfigure.py:1.1.2.1 metametaconfigurefordocgen.py:1.1.2.1 metametafordocgen.zcml:1.1.2.1 name.py:1.1.2.1 xmlconfig.py:1.1.2.1
Jim Fulton
jim@zope.com
Mon, 23 Dec 2002 14:32:44 -0500
Update of /cvs-repository/Zope3/src/zope/configuration
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/configuration
Added Files:
Tag: NameGeddon-branch
__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:
Initial renaming before debugging
=== Added File Zope3/src/zope/configuration/__init__.py ===
##############################################################################
#
# 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"]
=== Added File Zope3/src/zope/configuration/action.py ===
##############################################################################
#
# 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: action.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
def Action(discriminator, callable, args=(), kw={}):
return discriminator, callable, args, kw
=== Added File Zope3/src/zope/configuration/exceptions.py ===
##############################################################################
#
# 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
"""
=== Added File Zope3/src/zope/configuration/hookregistry.py ===
##############################################################################
#
# 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: hookregistry.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
from types import ModuleType
from zope.exceptions import DuplicationError, NotFoundError, ZopeError
import zope.configuration.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]
=== Added File Zope3/src/zope/configuration/meta.py ===
##############################################################################
#
# 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: meta.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
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
=== Added File Zope3/src/zope/configuration/meta.zcml ===
<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>
=== Added File Zope3/src/zope/configuration/metaconfigure.py ===
##############################################################################
#
# 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: metaconfigure.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
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)
)
]
=== Added File Zope3/src/zope/configuration/metameta.zcml ===
<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>
=== Added File Zope3/src/zope/configuration/metametaconfigure.py ===
##############################################################################
#
# 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: metametaconfigure.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
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
=== Added File Zope3/src/zope/configuration/metametaconfigurefordocgen.py ===
##############################################################################
#
# 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: metametaconfigurefordocgen.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
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}
=== Added File Zope3/src/zope/configuration/metametafordocgen.zcml ===
<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>
=== Added File Zope3/src/zope/configuration/name.py ===
##############################################################################
#
# 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: name.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
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
=== Added File Zope3/src/zope/configuration/xmlconfig.py ===
##############################################################################
#
# 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: xmlconfig.py,v 1.1.2.1 2002/12/23 19:32:42 jim Exp $
"""
import os
import zope.configuration.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]