[Zope-Checkins] CVS: Zope3/lib/python/Zope/Configuration - HookRegistry.py:1.1.2.1 configuration-meta.zcml:1.1.2.1 metaConfigure.py:1.1.2.1 name.py:1.1.2.12 xmlconfig.py:1.1.2.14
Gary Poster
garyposter@earthlink.net
Sun, 14 Apr 2002 17:21:03 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/Configuration
In directory cvs.zope.org:/tmp/cvs-serv2286
Modified Files:
Tag: Zope-3x-branch
name.py xmlconfig.py
Added Files:
Tag: Zope-3x-branch
HookRegistry.py configuration-meta.zcml metaConfigure.py
Log Message:
added support for <hook /> and <hookable /> tags; won't be actually usable until the next commit, in which I point zope.zcml at configuration-meta.zcml. Also added a name normalization routine in names.py.
hookable tag sets up a possible object that can be overwritten:
<hookable module="Zope.ComponentArchitecture" name="getServiceManager" />
module is optional so you can also write
<hookable name=".getServiceManager" />
These are assuming you are setting up a hookable that you loaded in the package's __init__.py. Here's one more example, this time completely hypothetical (the other two will not be hypothetical after the next commit).
<hookable module="Zope.Event.hooks" name="publishEvent" />
You use a hook with the "hook" tag:
<hook module="Zope.ComponentArchitecture" name="getServiceManager"
implementation="Zope.App.OFS.ServiceManager.hooks._getServiceManager" />
or if you are in the right place in the file system, shorten the implementation value to ".hooks._getServiceManager".
Again, it won't be in use until later this evening, after I get back from doing other things. But you can glance at it now, if so desired.
=== Added File Zope3/lib/python/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/04/14 21:21:02 poster Exp $
"""
from types import ModuleType
from Zope.Exceptions import DuplicationError, NotFoundError, ZopeError
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 self._reg.has_key(hname):
raise DuplicationError(hname)
try:
name.resolve(hname)
except ImportError:
raise BadHookableError(
"default hookable implementation cannot be found", hname)
parent, last=self._getParentAndLast(hname)
self._reg[hname]=0
def addHook(self, hookablename, hookname):
if not self._reg.has_key(hookablename):
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, 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/lib/python/Zope/Configuration/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/lib/python/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/04/14 21:21:02 poster Exp $
"""
from Action import Action
from HookRegistry import HookRegistry
hookRegistry=HookRegistry() # one could make this a service and
# theoretically use it TTW, but that doesn't immediately seem like a
# great idea
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/lib/python/Zope/Configuration/name.py 1.1.2.11 => 1.1.2.12 ===
"""Provide configuration object name resolution
+$Id$
"""
import sys
@@ -47,3 +48,23 @@
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
\ No newline at end of file
=== Zope3/lib/python/Zope/Configuration/xmlconfig.py 1.1.2.13 => 1.1.2.14 ===
def resolve(self, dottedname):
return name.resolve(dottedname, self.__package)
+
+ def getNormalizedName(self, dottedname):
+ return name.getNormalizedName(dottedname, self.__package)
def xmlconfig(file, actions=None, context=None, directives=None):