[Zope3-checkins] SVN: Zope3/trunk/ Add ZCML support for conditional
directives using the zcml:condition
Fred L. Drake, Jr.
fdrake at gmail.com
Fri Jan 7 11:43:09 EST 2005
Log message for revision 28766:
Add ZCML support for conditional directives using the zcml:condition
attribute. The value of the attribute is a TALES expression; if the
expression is true, the element the attribute is attached to will be
used, otherwise the element and its descendents will be ignored.
Changed:
U Zope3/trunk/doc/CHANGES.txt
A Zope3/trunk/src/zope/configuration/tests/conditions.zcml
A Zope3/trunk/src/zope/configuration/tests/test_conditions.py
U Zope3/trunk/src/zope/configuration/xmlconfig.py
-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt 2005-01-07 16:26:36 UTC (rev 28765)
+++ Zope3/trunk/doc/CHANGES.txt 2005-01-07 16:43:09 UTC (rev 28766)
@@ -10,6 +10,12 @@
New features
+ - ZCML supports conditional directives using the zcml:condition
+ attribute. The value of the attribute is a TALES expression;
+ if the expression is true, the element the attribute is
+ attached to will be used, otherwise the element and its
+ descendents will be ignored.
+
- Added lazy properties. (See zope/cachedescriptors/README.txt)
- Added new `getNextUtility()`, `queryNextUtility()`, and
Added: Zope3/trunk/src/zope/configuration/tests/conditions.zcml
===================================================================
--- Zope3/trunk/src/zope/configuration/tests/conditions.zcml 2005-01-07 16:26:36 UTC (rev 28765)
+++ Zope3/trunk/src/zope/configuration/tests/conditions.zcml 2005-01-07 16:43:09 UTC (rev 28766)
@@ -0,0 +1,75 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta"
+ xmlns:test="http://sample.namespaces.zope.org/test"
+ xmlns:zcml="http://namespaces.zope.org/zcml"
+ >
+
+ <meta:directive
+ name="register"
+ namespace="http://sample.namespaces.zope.org/test"
+ schema=".test_conditions.IRegister"
+ handler=".test_conditions.register"
+ >
+ This registers a directive which creates registrations we can test.
+ </meta:directive>
+
+ <test:register id="unqualified.registration" />
+
+ <configure zcml:condition="python:modules['sys'].platform">
+ ZCML directives inside here should be included.
+
+ <configure>
+ <test:register id="nested.true.condition" />
+ </configure>
+
+ <!-- These registrations stand on the basis of their own
+ conditions: -->
+ <test:register
+ zcml:condition="python:42"
+ id="true.condition.nested.in.true"
+ />
+
+ <test:register
+ zcml:condition="python:0"
+ id="false.condition.nested.in.true"
+ />
+
+ </configure>
+
+ <test:register
+ zcml:condition="string:yes!"
+ id="direct.true.condition"
+ >
+ This registration should be included.
+ </test:register>
+
+ <configure zcml:condition="python:False">
+ ZCML directives inside here should be ignored.
+
+ <configure>
+ <test:register id="nested.false.condition" />
+ </configure>
+
+ <!-- These registrations are ignored, since the container is
+ ignored: -->
+ <test:register
+ zcml:condition="python:42"
+ id="true.condition.nested.in.true"
+ />
+
+ <test:register
+ zcml:condition="python:0"
+ id="false.condition.nested.in.true"
+ />
+
+ </configure>
+
+ <test:register
+ zcml:condition="string:"
+ id="direct.false.condition"
+ >
+ This registration should be ignored.
+ </test:register>
+
+</configure>
Property changes on: Zope3/trunk/src/zope/configuration/tests/conditions.zcml
___________________________________________________________________
Name: svn:mime-type
+ text/xml
Name: svn:eol-style
+ native
Added: Zope3/trunk/src/zope/configuration/tests/test_conditions.py
===================================================================
--- Zope3/trunk/src/zope/configuration/tests/test_conditions.py 2005-01-07 16:26:36 UTC (rev 28765)
+++ Zope3/trunk/src/zope/configuration/tests/test_conditions.py 2005-01-07 16:43:09 UTC (rev 28766)
@@ -0,0 +1,106 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+r'''How to conditionalize specific directives
+
+There is a "condition" attribute in the
+"http://namespaces.zope.org/zcml" namespace which is honored on all
+elements in ZCML. The value of the attribute is a TALES expression
+which is used to determine if that element and its descendents are
+used. If the condition is true, processing continues normally,
+otherwise that element and its descendents are ignored.
+
+Our demonstration uses a trivial registry; each registration consists
+of a simple id inserted in the global `registry` in this module. We
+can checked that a registration was made by checking whether the id is
+present in `registry`.
+
+We start by loading the example ZCML file, *conditions.zcml*::
+
+ >>> import zope.configuration.tests
+ >>> import zope.configuration.xmlconfig
+
+ >>> context = zope.configuration.xmlconfig.file("conditions.zcml",
+ ... zope.configuration.tests)
+
+To show that our sample directive works, we see that the unqualified
+registration was successful::
+
+ >>> "unqualified.registration" in registry
+ True
+
+When the expression specified with ``zcml:condition`` evaluates to
+true, the element it is attached to and all contained elements (not
+otherwise conditioned) should be processed normally::
+
+ >>> "direct.true.condition" in registry
+ True
+ >>> "nested.true.condition" in registry
+ True
+
+However, when the expression evaluates to false, the conditioned
+element and all contained elements should be ignored::
+
+ >>> "direct.false.condition" in registry
+ False
+ >>> "nested.false.condition" in registry
+ False
+
+Conditions on container elements affect the conditions in nested
+elements in a reasonable way. If an "outer" condition is true, nested
+conditions are processed normally::
+
+ >>> "true.condition.nested.in.true" in registry
+ True
+ >>> "false.condition.nested.in.true" in registry
+ False
+
+If the outer condition is false, inner conditions are not even
+evaluated, and the nested elements are ignored::
+
+ >>> "true.condition.nested.in.false" in registry
+ False
+ >>> "false.condition.nested.in.false" in registry
+ False
+
+Now we need to clean up after ourselves::
+
+ >>> del registry[:]
+
+'''
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+import zope.testing.doctest
+
+
+class IRegister(zope.interface.Interface):
+ """Trivial sample registry."""
+
+ id = zope.schema.Id(
+ title=u"Identifier",
+ description=u"Some identifier that can be checked.",
+ required=True,
+ )
+
+registry = []
+
+def register(context, id):
+ context.action(discriminator=('Register', id),
+ callable=registry.append,
+ args=(id,)
+ )
+
+def test_suite():
+ return zope.testing.doctest.DocTestSuite()
Property changes on: Zope3/trunk/src/zope/configuration/tests/test_conditions.py
___________________________________________________________________
Name: svn:mime-type
+ text/x-python
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/configuration/xmlconfig.py
===================================================================
--- Zope3/trunk/src/zope/configuration/xmlconfig.py 2005-01-07 16:26:36 UTC (rev 28765)
+++ Zope3/trunk/src/zope/configuration/xmlconfig.py 2005-01-07 16:43:09 UTC (rev 28766)
@@ -38,6 +38,10 @@
logger = logging.getLogger("config")
+ZCML_NAMESPACE = "http://namespaces.zope.org/zcml"
+ZCML_CONDITION = (ZCML_NAMESPACE, u"condition")
+
+
class ZopeXMLConfigurationError(ConfigurationError):
"""Zope XML Configuration error
@@ -181,6 +185,7 @@
def __init__(self, context, testing=0):
self.context = context
self.testing = testing
+ self.ignore_depth = 0
def setDocumentLocator(self, locator):
self.locator = locator
@@ -189,12 +194,22 @@
self.context.getInfo().characters(text)
def startElementNS(self, name, qname, attrs):
+ if self.ignore_depth:
+ self.ignore_depth += 1
+ return
data = {}
for (ns, aname), value in attrs.items():
if ns is None:
aname = str(aname)
data[aname] = value
+ if (ns, aname) == ZCML_CONDITION:
+ # need to process TALES expression to determine if we
+ # use this element and it's descendents
+ use = self.evaluateTalesCondition(value)
+ if not use:
+ self.ignore_depth = 1
+ return
info = ParserInfo(
self.locator.getSystemId(),
@@ -213,8 +228,20 @@
self.context.setInfo(info)
+ def evaluateTalesCondition(self, expression):
+ import zope.tales.engine
+ engine = zope.tales.engine.Engine
+ code = engine.compile(expression)
+ context = engine.getContext(engine.getBaseNames())
+ return context.evaluateBoolean(code)
def endElementNS(self, name, qname):
+ # If ignore_depth is set, this element will be ignored, even
+ # if this this decrements ignore_depth to 0.
+ if self.ignore_depth:
+ self.ignore_depth -= 1
+ return
+
info = self.context.getInfo()
info.end(
self.locator.getLineNumber(),
More information about the Zope3-Checkins
mailing list