[Zope-Checkins] CVS: Zope3/lib/python/Zope/Configuration - Exceptions.py:1.1.4.1 Action.py:1.1.2.4 ConfigurationDirectiveInterfaces.py:1.1.2.4 HookRegistry.py:1.1.2.3 configuration-meta.zcml:1.1.2.2 meta.py:1.1.2.10 metaConfigure.py:1.1.2.2 name.py:1.1.2.13 xmlconfig.py:1.1.2.16
Jim Fulton
jim@zope.com
Fri, 7 Jun 2002 10:41:54 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/Configuration
In directory cvs.zope.org:/tmp/cvs-serv12187/lib/python/Zope/Configuration
Modified Files:
Tag: Zope-3x-branch
Action.py ConfigurationDirectiveInterfaces.py HookRegistry.py
configuration-meta.zcml meta.py metaConfigure.py name.py
xmlconfig.py
Added Files:
Tag: Zope-3x-branch
Exceptions.py
Log Message:
Merging in Zope3InWonderland-branch, which implemented the following
proposals (see
http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/OldProposals):
- RenameAllowToRequire
- GroupClassRelatedDirectivesInClassDirective
- ViewInterfaceAndSimplification
- ConsistentUseOfSpacesAsDelimitersInZCMLAttributes
- TwoArgumentViewConstructors
- ImplementsInZCML
- SimpleViewCreationInZCML
- RemoveGetView
- ReplaceProtectWithAllow
- ViewMethodsAsViews
- MergeProtectionAndComponentDefinitions
There were also various security fixes resulting of better integration
of security with components.
=== Added File Zope3/lib/python/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
"""
=== Zope3/lib/python/Zope/Configuration/Action.py 1.1.2.3 => 1.1.2.4 ===
"""
-def Action( discriminator, callable, args=(), kw={}):
+def Action(discriminator, callable, args=(), kw={}):
return discriminator, callable, args, kw
=== Zope3/lib/python/Zope/Configuration/ConfigurationDirectiveInterfaces.py 1.1.2.3 => 1.1.2.4 ===
- A discriminator, value used to identify conflicting
- actions. Actions cnflict of they have the same value values
+ actions. Actions conflict if they have the same values
for their discriminators.
- callable object
=== Zope3/lib/python/Zope/Configuration/HookRegistry.py 1.1.2.2 => 1.1.2.3 ===
raise DuplicationError(hname)
try:
- defaultimpl=name.resolve(hname)
+ defaultimpl = name.resolve(hname)
except ImportError:
- raise BadHookableError(
- "hookable %s cannot be found" % hname)
+ raise BadHookableError("hookable %s cannot be found" % hname)
parent, last=self._getParentAndLast(hname)
implfunc="%s_hook" % last
@@ -53,7 +52,7 @@
note it must be in the same module as the hookable""" %
implfunc)
- self._reg[hname]=0
+ self._reg[hname] = 0
def addHook(self, hookablename, hookname):
@@ -62,14 +61,15 @@
if self._reg[hookablename]:
raise DuplicateHookError(hookablename, hookname)
try:
- implementation=name.resolve(hookname)
+ 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',
+ '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
@@ -88,12 +88,12 @@
# find and import immediate parent
- parent, last=self._getParentAndLast(hookablename)
+ parent,last = self._getParentAndLast(hookablename)
# set parent.last to implementation
setattr(parent, "%s_hook" % last, implementation)
- self._reg[hookablename]=hookname
+ self._reg[hookablename] = hookname
def _getParentAndLast(self, hookablename):
if hookablename.endswith('.') or hookablename.endswith('+'):
@@ -101,25 +101,27 @@
repeat = 1
else:
repeat = 0
- names=hookablename.split(".")
- last=names.pop()
- importname=".".join(names)
+ 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)
+ '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)
+ 'hookable cannot be on top level of Python namespace',
+ hookablename)
while repeat:
- grand=getattr(child, last, self)
+ grand = getattr(child, last, self)
if grand is self:
break
- parent=child
- child=grand
+ parent = child
+ child = grand
if type(parent) is not ModuleType:
raise BadHookableError("parent of hookable must be a module")
@@ -127,10 +129,15 @@
return parent, last
def getHooked(self):
- return [(key, self._reg[key]) for key in self._reg if self._reg[key]]
+ 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]]
+ 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]
\ No newline at end of file
+ return [(key, self._reg[key])
+ for key in self._reg]
=== Zope3/lib/python/Zope/Configuration/configuration-meta.zcml 1.1.2.1 => 1.1.2.2 ===
<!-- Zope.Configure -->
<directives namespace="http://namespaces.zope.org/zope">
- <directive name="hookable" attributes="name, module"
+ <directive name="hookable" attributes="name module"
handler="Zope.Configuration.metaConfigure.provideHookable" />
- <directive name="hook" attributes="name, implementation, module"
+ <directive name="hook" attributes="name implementation module"
handler="Zope.Configuration.metaConfigure.provideHook" />
</directives>
=== Zope3/lib/python/Zope/Configuration/meta.py 1.1.2.9 => 1.1.2.10 ===
"""
+#
+
from ConfigurationDirectiveInterfaces import INonEmptyDirective
from ConfigurationDirectiveInterfaces import ISubdirectiveHandler
@@ -30,13 +32,51 @@
"A directive is implemented incorrectly"
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.
+
+ """
+
subdirs = {}
_directives[name] = callable, subdirs
return subdirs
-def registersub(directives, name):
+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 is looked up as an attribute of
+ the top-level directive object using the name string that is the
+ second element in the name tuple. An optional handler attribute
+ can be used to specify the method to be used.
+
+ 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 not handler_method:
+ handler_method = name[1]
subdirs = {}
- directives[name] = subdirs
+ directives[name] = subdirs, handler_method
return subdirs
def _exe(callable, subs, context, kw):
@@ -45,9 +85,41 @@
if subs or INonEmptyDirective.isImplementedBy(callable):
return r, subs
else:
- return lambda: r, subs
+ return (
+ # We already have our list of actions, but we're expected to
+ # provide a callable that returns one.
+ (lambda: r),
+
+ subs,
+ )
def begin(_custom_directives, _name, _context, **kw):
+ """Begin executing a top-level directive
+
+ A custom registry is provided to provides 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 an
+ name string.
+
+ Thje _context argument is an execution context objects 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 processes.
+
+ - A registry for looking up subdirectives.
+
+ """
+
if _custom_directives and (_name in _custom_directives):
callable, subs = _custom_directives[_name]
else:
@@ -59,20 +131,62 @@
return _exe(callable, subs, _context, kw)
def sub(subs, _name, _context, **kw):
+ """Begin executing a subdirective
+
+ The first argument, subs, is a registry of allowable subdirectives
+ for the containing directive or subdirective.
+
+ The _name argument is a tuple with a namespace URI and an
+ name string.
+
+ Thje _context argument is an execution context objects 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 processes.
+
+ - A registry for looking up subdirectives.
+
+ """
base, subdirs = subs
-
try:
subs = subdirs[_name]
except KeyError:
raise InvalidDirective(_name)
-
- callable = getattr(base, _name[1])
+
+ # this is crufty.
+ # if this is a tuple, it means we created it as such in
+ # registersub, and so we grab item 1 as the handler_method
+ # and rebind subs as item 0
+
+ if isinstance(subs, tuple):
+ handler_method = subs[1]
+ subs = subs[0]
+ else:
+ handler_method = _name[1]
+ callable = getattr(base, handler_method)
return _exe(callable, subs, _context, kw)
defaultkw = ({},)
def end(base):
+ """Finish processing a directive or subdirective
+
+ The argument is a return value from begin or sub. It's first
+ element is called to get a sequence of actions.
+
+ The actions 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:
@@ -92,14 +206,14 @@
namespace=None):
namespace = namespace or self._namespace
subs = register((namespace, name), _context.resolve(handler))
- return Subdirective(subs, namespace)
+ return Subdirective(subs, namespace=namespace)
def __call__(self):
return ()
def Directive(_context, namespace, name, handler, attributes=''):
subs = register((namespace, name), _context.resolve(handler))
- return Subdirective(subs, namespace)
+ return Subdirective(subs, namespace=namespace)
Directive.__implements__ = INonEmptyDirective
@@ -120,12 +234,14 @@
self._subs = subs
self._namespace = namespace
- def subdirective(self, _context, name, attributes='', namespace=None):
+ def subdirective(self, _context, name, attributes='',
+ namespace=None, handler_method=None):
namespace = namespace or self._namespace
if not namespace:
raise InvaliDirectiveDefinition(name)
-
- subs = registersub(self._subs, (namespace, name))
+ #if not handler_method:
+ # handler_method = name
+ subs = registersub(self._subs, (namespace, name), handler_method)
return Subdirective(subs)
def __call__(self):
=== Zope3/lib/python/Zope/Configuration/metaConfigure.py 1.1.2.1 => 1.1.2.2 ===
$Id$
"""
-
from Action import Action
from HookRegistry import HookRegistry
-hookRegistry=HookRegistry() # one could make this a service and
+# 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
+addHookable = hookRegistry.addHookable
+addHook = hookRegistry.addHook
def provideHookable(_context, name, module=None):
if module:
- name="%s.%s" % (module, name)
- name=_context.getNormalizedName(name)
+ name = "%s.%s" % (module, name)
+ name = _context.getNormalizedName(name)
return [
Action(
- discriminator = ('addHookable', name),
- callable = addHookable,
- args = (name,)
+ 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)
+ name = "%s.%s" % (module, name)
+ name = _context.getNormalizedName(name)
+ implementation = _context.getNormalizedName(implementation)
return [
Action(
- discriminator = ('addHook', name),
- callable = addHook,
- args = (name, implementation)
+ discriminator=('addHook', name),
+ callable=addHook,
+ args=(name, implementation)
)
]
=== Zope3/lib/python/Zope/Configuration/name.py 1.1.2.12 => 1.1.2.13 ===
"""
+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
@@ -67,4 +70,17 @@
name=".".join(name)
if repeat:
name+="+"
- return name
\ No newline at end of file
+ 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/lib/python/Zope/Configuration/xmlconfig.py 1.1.2.15 => 1.1.2.16 ===
"""
+import os
import name
from xml.sax import make_parser
from xml.sax.xmlreader import InputSource
@@ -24,77 +25,95 @@
from keyword import iskeyword
import sys, os
from types import StringType
+from Exceptions import ConfigurationError
-class ZopeXMLConfigurationError(Exception):
+class ZopeXMLConfigurationError(ConfigurationError):
"Zope XML Configuration error"
- def __init__(self, locator, mess):
- if not isinstance(mess, StringType):
- try:
- mess = "\n%s: %s" % (mess.__class__.__name__, mess)
- except AttributeError:
- mess=str(mess)
- self.lno=locator.getLineNumber()
- self.cno=locator.getColumnNumber()
- self.sid=locator.getSystemId()
- self.mess=mess
+ def __init__(self, locator, mess, etype=None):
+ if etype is None:
+ if not isinstance(mess, StringType):
+ try:
+ mess = "\n%s: %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 "%s\nat line %s column %s of %s" % (
- self.mess, self.lno, self.cno, self.sid)
+ 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):
- if isinstance(mess, StringType):
- try:
- mess = "%s: %s" % (mess.__class__.__name__, mess)
- except AttributeError:
- mess=str(mess)
+ 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
+ self.mess = mess
class ConfigurationHandler(ContentHandler):
__top_name = 'http://namespaces.zope.org/zope', 'zopeConfigure'
- def __init__(self, actions, context, directives=None):
+ def __init__(self, actions, context, directives=None, testing=0):
self.__stack = []
self.__actions = actions
self.__directives = directives
self.__context = context
+ self.__testing = testing
context.resolve
def setDocumentLocator(self, locator):
- self.__locator=locator
+ self.__locator = locator
def startElementNS(self, name, qname, attrs):
- stack=self.__stack
+ 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={}
+ kw = {}
for (ns, aname), value in attrs.items():
if ns is None:
- aname=str(aname)
+ aname = str(aname)
if iskeyword(aname): aname += '_'
- kw[aname]=value
-
+ kw[aname] = value
+
if len(stack) == 1:
try:
- stack.append(begin(self.__directives, name, self.__context,
- **kw))
- except Exception, v:
- __traceback_supplement__ = (
- ConfigurationTracebackSupplement, self.__locator, v
+ stack.append(
+ begin(self.__directives, name, self.__context, **kw)
)
- raise
+ 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:
@@ -103,11 +122,11 @@
try:
stack.append(sub(subs, name, self.__context, **kw))
except Exception, v:
- __traceback_supplement__ = (
- ConfigurationTracebackSupplement, self.__locator, v
- )
- raise
-
+ 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:
@@ -117,10 +136,10 @@
try:
actions = end(subs)
except Exception, v:
- __traceback_supplement__ = (
- ConfigurationTracebackSupplement, self.__locator, v
- )
- raise
+ if self.__testing:
+ raise
+ raise ZopeXMLConfigurationError, (
+ self.__locator, str(v)), sys.exc_info()[2]
append = self.__actions.append
@@ -139,9 +158,9 @@
"Zope XML Configuration error"
def __init__(self, l1, l2, des):
- self.l1=l1
- self.l2=l2
- self.des=des
+ self.l1 = l1
+ self.l2 = l2
+ self.des = des
def __str__(self):
return """Conflicting configuration action:
@@ -167,8 +186,18 @@
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 xmlconfig(file, actions=None, context=None, directives=None):
+
+def xmlconfig(file, actions=None, context=None, directives=None,
+ testing=0):
if context is None:
context = name
@@ -180,19 +209,28 @@
src = InputSource(getattr(file, 'name', '<string>'))
src.setByteStream(file)
parser = make_parser()
- parser.setContentHandler(ConfigurationHandler(actions, context,
- directives))
+ parser.setContentHandler(
+ ConfigurationHandler(actions, context,directives,
+ testing=testing)
+ )
parser.setFeature(feature_namespaces, 1)
- parser.parse(src)
+ parser.parse(src)
if call:
descriptors = {}
for level, loc, des, callable, args, kw in call:
if des in descriptors:
raise ZopeConflictingConfigurationError(
- descriptors[des], loc, des)
+ 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):
@@ -216,11 +254,11 @@
(self.include, {})}
f = open(file_name)
- self._stack=[file_name]
+ self._stack = [file_name]
xmlconfig(f, self._actions, Context(self._stack), self._directives)
f.close()
- def include(self, _context, file, package=None):
+ def include(self, _context, file='config.zcml', package=None):
if package is not None:
try:
package = _context.resolve(package)
@@ -294,23 +332,5 @@
callable, args, kw = f
callable(*args, **kw)
except Exception, v:
- raise ConfigurationExecutionError(loc, v)
-
-
-class ConfigurationTracebackSupplement:
- """Implementation of Zope.Exceptions.ITracebackSupplement"""
- def __init__(self, locator, message):
- self.message = message
- self.line = locator.getLineNumber()
- self.column = locator.getColumnNumber()
- self.source_url = locator.getSystemId()
-
- def getInfo(self, as_html=0):
-
- if not as_html:
- return ' - Message: %s' % self.message
- else:
- from cgi import escape
- return '<b>Message:</b><pre>%s</pre> in %s' % escape(self.message)
- return None
-
+ raise ConfigurationExecutionError, (
+ loc, v, sys.exc_info()[0]), sys.exc_info()[2]