Log message for revision 37697: merged yuppie-tal-backports branch: - made sure custom strings like MessageID are not converted by ustr - synced TALInterpreter code and tests with zope.tal where possible without changing behavior - backported MessageID support from zope.tal
Changed: UU Zope/trunk/lib/python/DocumentTemplate/tests/testustr.py UU Zope/trunk/lib/python/DocumentTemplate/ustr.py UU Zope/trunk/lib/python/TAL/TALInterpreter.py UU Zope/trunk/lib/python/TAL/tests/test_talinterpreter.py -=- Modified: Zope/trunk/lib/python/DocumentTemplate/tests/testustr.py =================================================================== --- Zope/trunk/lib/python/DocumentTemplate/tests/testustr.py 2005-08-04 14:22:37 UTC (rev 37696) +++ Zope/trunk/lib/python/DocumentTemplate/tests/testustr.py 2005-08-04 14:24:29 UTC (rev 37697) @@ -7,21 +7,19 @@ # 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 +# FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Document Template Tests +"""ustr unit tests. + +$Id$ """ -__rcs_id__='$Id$' -__version__='$Revision: 1.3 $'[11:-2] - -import sys, os import unittest from DocumentTemplate.ustr import ustr -from ExtensionClass import Base + class force_str: # A class whose string representation is not always a plain string: def __init__(self,s): @@ -29,8 +27,19 @@ def __str__(self): return self.s -class UnicodeTests (unittest.TestCase): +class Foo(str): + + pass + + +class Bar(unicode): + + pass + + +class UnicodeTests(unittest.TestCase): + def testPlain(self): a = ustr('hello') assert a=='hello', `a` @@ -59,13 +68,17 @@ a = ustr(ValueError(unichr(200))) assert a==unichr(200), `a` + def testCustomStrings(self): + a = ustr(Foo('foo')) + self.failUnlessEqual(type(a), Foo) + a = ustr(Bar('bar')) + self.failUnlessEqual(type(a), Bar) + + def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.makeSuite( UnicodeTests ) ) return suite -def main(): - unittest.TextTestRunner().run(test_suite()) - if __name__ == '__main__': - main() + unittest.main(defaultTest='test_suite') Property changes on: Zope/trunk/lib/python/DocumentTemplate/tests/testustr.py ___________________________________________________________________ Name: cvs2svn:cvs-rev - 1.3 Modified: Zope/trunk/lib/python/DocumentTemplate/ustr.py =================================================================== --- Zope/trunk/lib/python/DocumentTemplate/ustr.py 2005-08-04 14:22:37 UTC (rev 37696) +++ Zope/trunk/lib/python/DocumentTemplate/ustr.py 2005-08-04 14:24:29 UTC (rev 37697) @@ -7,11 +7,13 @@ # 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 +# FOR A PARTICULAR PURPOSE. # ############################################################################## +"""ustr function. -from types import StringType, UnicodeType, InstanceType +$Id$ +""" nasty_exception_str = Exception.__str__.im_func @@ -20,8 +22,7 @@ minimising the chance of raising a UnicodeError. This even works with uncooperative objects like Exceptions """ - string_types = (StringType,UnicodeType) - if type(v) in string_types: + if isinstance(v, basestring): return v else: fn = getattr(v,'__str__',None) @@ -41,7 +42,7 @@ else: # Trust the object to do this right v = fn() - if type(v) in string_types: + if isinstance(v, basestring): return v else: raise ValueError('__str__ returned wrong type') @@ -59,4 +60,3 @@ else: return str(exc.args) return str(exc) - Property changes on: Zope/trunk/lib/python/DocumentTemplate/ustr.py ___________________________________________________________________ Name: cvs2svn:cvs-rev - 1.4 Name: svn:keywords + Id Modified: Zope/trunk/lib/python/TAL/TALInterpreter.py =================================================================== --- Zope/trunk/lib/python/TAL/TALInterpreter.py 2005-08-04 14:22:37 UTC (rev 37696) +++ Zope/trunk/lib/python/TAL/TALInterpreter.py 2005-08-04 14:24:29 UTC (rev 37697) @@ -4,29 +4,32 @@ # 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. +# 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. # ############################################################################## +"""Interpreter for a pre-compiled TAL program. + +$Id$ """ -Interpreter for a pre-compiled TAL program. -""" import cgi import sys import getopt import re -from types import ListType from cgi import escape + # Do not use cStringIO here! It's not unicode aware. :( from StringIO import StringIO from DocumentTemplate.DT_Util import ustr from ZODB.POSException import ConflictError -from TALDefs import TAL_VERSION, TALError, METALError, attrEscape -from TALDefs import isCurrentVersion, getProgramVersion, getProgramMode +from zope.i18nmessageid import MessageID +from TALDefs import attrEscape, TAL_VERSION, METALError +from TALDefs import isCurrentVersion +from TALDefs import getProgramVersion, getProgramMode from TALGenerator import TALGenerator from TranslationContext import TranslationContext @@ -41,11 +44,22 @@ "defer" ] +def _init(): + d = {} + for s in BOOLEAN_HTML_ATTRS: + d[s] = 1 + return d + +BOOLEAN_HTML_ATTRS = _init() + +_nulljoin = ''.join +_spacejoin = ' '.join + def normalize(text): # Now we need to normalize the whitespace in implicit message ids and # implicit $name substitution values by stripping leading and trailing # whitespace, and folding all internal whitespace to a single space. - return ' '.join(text.split()) + return _spacejoin(text.split()) NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*" @@ -159,7 +173,8 @@ self.scopeLevel, self.level, self.i18nContext) def restoreState(self, state): - (self.position, self.col, self.stream, scopeLevel, level, i18n) = state + (self.position, self.col, self.stream, + scopeLevel, level, i18n) = state self._stream_write = self.stream.write assert self.level == level while self.scopeLevel > scopeLevel: @@ -169,7 +184,8 @@ self.i18nContext = i18n def restoreOutputState(self, state): - (dummy, self.col, self.stream, scopeLevel, level, i18n) = state + (dummy, self.col, self.stream, + scopeLevel, level, i18n) = state self._stream_write = self.stream.write assert self.level == level assert self.scopeLevel == scopeLevel @@ -195,6 +211,16 @@ self._stream_write("\n") self.col = 0 + def interpretWithStream(self, program, stream): + oldstream = self.stream + self.stream = stream + self._stream_write = stream.write + try: + self.interpret(program) + finally: + self.stream = oldstream + self._stream_write = oldstream.write + def stream_write(self, s, len=len): self._stream_write(s) @@ -206,16 +232,6 @@ bytecode_handlers = {} - def interpretWithStream(self, program, stream): - oldstream = self.stream - self.stream = stream - self._stream_write = stream.write - try: - self.interpret(program) - finally: - self.stream = oldstream - self._stream_write = oldstream.write - def interpret(self, program): oldlevel = self.level self.level = oldlevel + 1 @@ -269,6 +285,7 @@ # for start tags with no attributes; those are optimized down # to rawtext events. Hence, there is no special "fast path" # for that case. + self._currentTag = name L = ["<", name] append = L.append col = self.col + _len(name) + 1 @@ -303,9 +320,9 @@ col = col + 1 + slen append(s) append(end) - self._stream_write("".join(L)) col = col + endlen finally: + self._stream_write(_nulljoin(L)) self.col = col bytecode_handlers["startTag"] = do_startTag @@ -487,6 +504,9 @@ if text is self.Default: self.interpret(stuff[1]) return + if isinstance(text, MessageID): + # Translate this now. + text = self.engine.translate(text.domain, text, text.mapping) s = escape(text) self._stream_write(s) i = s.rfind('\n') @@ -505,7 +525,10 @@ try: tmpstream = self.StringIO() self.interpretWithStream(program, tmpstream) - value = normalize(tmpstream.getvalue()) + if self.html and self._currentTag == "pre": + value = tmpstream.getvalue() + else: + value = normalize(tmpstream.getvalue()) finally: self.restoreState(state) else: @@ -516,8 +539,14 @@ else: value = self.engine.evaluate(expression) + # evaluate() does not do any I18n, so we do it here. + if isinstance(value, MessageID): + # Translate this now. + value = self.engine.translate(value.domain, value, + value.mapping) + if not structure: - value = cgi.escape(str(value)) + value = cgi.escape(ustr(value)) # Either the i18n:name tag is nested inside an i18n:translate in which # case the last item on the stack has the i18n dictionary and string @@ -545,20 +574,29 @@ # # Use a temporary stream to capture the interpretation of the # subnodes, which should /not/ go to the output stream. + currentTag = self._currentTag tmpstream = self.StringIO() self.interpretWithStream(stuff[1], tmpstream) - default = tmpstream.getvalue() # We only care about the evaluated contents if we need an implicit # message id. All other useful information will be in the i18ndict on # the top of the i18nStack. - if msgid == '': - msgid = normalize(default) + default = tmpstream.getvalue() + if not msgid: + if self.html and currentTag == "pre": + msgid = default + else: + msgid = normalize(default) self.i18nStack.pop() # See if there is was an i18n:data for msgid if len(stuff) > 2: obj = self.engine.evaluate(stuff[2]) xlated_msgid = self.translate(msgid, default, i18ndict, obj) - assert xlated_msgid is not None, self.position + # XXX I can't decide whether we want to cgi escape the translated + # string or not. OT1H not doing this could introduce a cross-site + # scripting vector by allowing translators to sneak JavaScript into + # translations. OTOH, for implicit interpolation values, we don't + # want to escape stuff like ${name} <= "<b>Timmy</b>". + assert xlated_msgid is not None self._stream_write(xlated_msgid) bytecode_handlers['insertTranslation'] = do_insertTranslation Property changes on: Zope/trunk/lib/python/TAL/TALInterpreter.py ___________________________________________________________________ Name: cvs2svn:cvs-rev - 1.83 Name: svn:keywords + Id Modified: Zope/trunk/lib/python/TAL/tests/test_talinterpreter.py =================================================================== --- Zope/trunk/lib/python/TAL/tests/test_talinterpreter.py 2005-08-04 14:22:37 UTC (rev 37696) +++ Zope/trunk/lib/python/TAL/tests/test_talinterpreter.py 2005-08-04 14:24:29 UTC (rev 37697) @@ -1,32 +1,35 @@ # -*- coding: ISO-8859-1 -*- ############################################################################## # -# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# 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. +# 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 +# FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Tests for TALInterpreter.""" +"""Tests for TALInterpreter. +$Id$ +""" import sys - -from TAL.tests import utils import unittest from StringIO import StringIO from TAL.TALDefs import METALError, I18NError from TAL.HTMLTALParser import HTMLTALParser +from TAL.TALParser import TALParser from TAL.TALInterpreter import TALInterpreter from TAL.DummyEngine import DummyEngine, DummyTranslationService from TAL.TALInterpreter import interpolate +from TAL.tests import utils +from zope.i18nmessageid import MessageID - class TestCaseBase(unittest.TestCase): def _compile(self, source): @@ -53,10 +56,10 @@ else: self.fail("Expected METALError") - def check_mode_error(self): + def test_mode_error(self): self.macro[1] = ("mode", "duh") - def check_version_error(self): + def test_version_error(self): self.macro[0] = ("version", "duh") @@ -64,7 +67,9 @@ def setUp(self): self.engine = DummyEngine() + self.engine.setLocal('foo', MessageID('FoOvAlUe', 'default')) self.engine.setLocal('bar', 'BaRvAlUe') + self.engine.setLocal('raw', ' \tRaW\n ') def _check(self, program, expected): result = StringIO() @@ -73,11 +78,57 @@ self.interpreter() self.assertEqual(expected, result.getvalue()) + def test_simple_messageid_translate(self): + # This test is mainly here to make sure our DummyEngine works + # correctly. + program, macros = self._compile('<span tal:content="foo"/>') + self._check(program, '<span>FOOVALUE</span>\n') + + program, macros = self._compile('<span tal:replace="foo"/>') + self._check(program, 'FOOVALUE\n') + + def test_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '<div i18n:translate="" >' + '<span tal:replace="foo" i18n:name="foo_name"/>' + '</div>') + self._check(program, '<div>FOOVALUE</div>\n') + + def test_pythonexpr_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '<div i18n:translate="" >' + '<span tal:replace="python: foo" i18n:name="foo_name"/>' + '</div>') + self._check(program, '<div>FOOVALUE</div>\n') + + def test_structure_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '<div i18n:translate="" >' + '<span tal:replace="structure foo" i18n:name="foo_name"/>' + '</div>') + self._check(program, '<div>FOOVALUE</div>\n') + + def test_complex_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '<div i18n:translate="" >' + '<em i18n:name="foo_name">' + '<span tal:replace="foo"/>' + '</em>' + '</div>') + self._check(program, '<div>FOOVALUE</div>\n') + + def test_content_with_messageid_and_i18nname(self): + program, macros = self._compile( + '<div i18n:translate="" >' + '<span tal:content="foo" i18n:name="foo_name"/>' + '</div>') + self._check(program, '<div><span>FOOVALUE</span></div>\n') + def test_content_with_messageid_and_i18nname_and_i18ntranslate(self): # Let's tell the user this is incredibly silly! self.assertRaises( I18NError, self._compile, - '<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>') + '<span i18n:translate="" tal:content="foo" i18n:name="foo_name"/>') def test_content_with_plaintext_and_i18nname_and_i18ntranslate(self): # Let's tell the user this is incredibly silly! @@ -126,20 +177,23 @@ self._check(program, '<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n') - def test_for_correct_msgids(self): - + def _getCollectingTranslationDomain(self): class CollectingTranslationService(DummyTranslationService): data = [] def translate(self, domain, msgid, mapping=None, context=None, target_language=None, default=None): - self.data.append(msgid) + self.data.append((msgid, mapping)) return DummyTranslationService.translate( self, domain, msgid, mapping, context, target_language, default) xlatsvc = CollectingTranslationService() self.engine.translationService = xlatsvc + return xlatsvc + + def test_for_correct_msgids(self): + xlatdmn = self._getCollectingTranslationDomain() result = StringIO() program, macros = self._compile( '<div i18n:translate="">This is text for ' @@ -148,14 +202,91 @@ self.interpreter = TALInterpreter(program, {}, self.engine, stream=result) self.interpreter() - self.assert_('BaRvAlUe' in xlatsvc.data) - self.assert_('This is text for ${bar_name}.' in - xlatsvc.data) + msgids = list(xlatdmn.data) + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual('BaRvAlUe', msgids[0][0]) + self.assertEqual('This is text for ${bar_name}.', msgids[1][0]) + self.assertEqual({'bar_name': '<span>BARVALUE</span>'}, msgids[1][1]) self.assertEqual( '<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n', result.getvalue()) + def test_for_raw_msgids(self): + # Test for Issue 314: i18n:translate removes line breaks from + # <pre>...</pre> contents + # HTML mode + xlatdmn = self._getCollectingTranslationDomain() + result = StringIO() + program, macros = self._compile( + '<div i18n:translate=""> This is text\n' + ' \tfor\n div. </div>' + '<pre i18n:translate=""> This is text\n' + ' <b>\tfor</b>\n pre. </pre>') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = list(xlatdmn.data) + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual(' This is text\n <b>\tfor</b>\n pre. ', msgids[0][0]) + self.assertEqual('This is text for div.', msgids[1][0]) + self.assertEqual( + '<div>THIS IS TEXT FOR DIV.</div>' + '<pre> THIS IS TEXT\n <B>\tFOR</B>\n PRE. </pre>\n', + result.getvalue()) + # XML mode + xlatdmn = self._getCollectingTranslationDomain() + result = StringIO() + parser = TALParser() + parser.parseString( + '<?xml version="1.0"?>\n' + '<pre xmlns:i18n="http://xml.zope.org/namespaces/i18n"' + ' i18n:translate=""> This is text\n' + ' <b>\tfor</b>\n barvalue. </pre>') + program, macros = parser.getCode() + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = list(xlatdmn.data) + msgids.sort() + self.assertEqual(1, len(msgids)) + self.assertEqual('This is text <b> for</b> barvalue.', msgids[0][0]) + self.assertEqual( + '<?xml version="1.0"?>\n' + '<pre>THIS IS TEXT <B> FOR</B> BARVALUE.</pre>\n', + result.getvalue()) + + def test_raw_msgids_and_i18ntranslate_i18nname(self): + xlatdmn = self._getCollectingTranslationDomain() + result = StringIO() + program, macros = self._compile( + '<div i18n:translate=""> This is text\n \tfor\n' + '<pre tal:content="raw" i18n:name="raw"' + ' i18n:translate=""></pre>.</div>') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = list(xlatdmn.data) + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual(' \tRaW\n ', msgids[0][0]) + self.assertEqual('This is text for ${raw}.', msgids[1][0]) + self.assertEqual({'raw': '<pre> \tRAW\n </pre>'}, msgids[1][1]) + self.assertEqual( + u'<div>THIS IS TEXT FOR <pre> \tRAW\n </pre>.</div>\n', + result.getvalue()) + + def test_for_handling_unicode_vars(self): + # Make sure that non-ASCII Unicode is substituted correctly. + # http://collector.zope.org/Zope3-dev/264 + program, macros = self._compile( + "<div i18n:translate='' tal:define='bar python:unichr(0xC0)'>" + "Foo <span tal:replace='bar' i18n:name='bar' /></div>") + self._check(program, u"<div>FOO \u00C0</div>\n") + + class I18NErrorsTestCase(TestCaseBase): def _check(self, src, msg): @@ -187,7 +318,7 @@ class OutputPresentationTestCase(TestCaseBase): - def check_attribute_wrapping(self): + def test_attribute_wrapping(self): # To make sure the attribute-wrapping code is invoked, we have to # include at least one TAL/METAL attribute to avoid having the start # tag optimized into a rawtext instruction. @@ -202,17 +333,17 @@ </html>''' "\n" self.compare(INPUT, EXPECTED) - def check_unicode_content(self): + def test_unicode_content(self): INPUT = """<p tal:content="python:u'déjà-vu'">para</p>""" EXPECTED = u"""<p>déjà-vu</p>""" "\n" self.compare(INPUT, EXPECTED) - def check_unicode_structure(self): + def test_unicode_structure(self): INPUT = """<p tal:replace="structure python:u'déjà-vu'">para</p>""" EXPECTED = u"""déjà-vu""" "\n" self.compare(INPUT, EXPECTED) - def check_i18n_replace_number(self): + def test_i18n_replace_number(self): INPUT = """ <p i18n:translate="foo ${bar}"> <span tal:replace="python:123" i18n:name="bar">para</span> @@ -221,13 +352,13 @@ <p>FOO 123</p>""" "\n" self.compare(INPUT, EXPECTED) - def check_entities(self): - INPUT = ('<img tal:attributes="alt default" ' + def test_entities(self): + INPUT = ('<img tal:define="foo nothing" ' 'alt="&a;  
 &a - &; �a; <>" />') EXPECTED = ('<img alt="&a;  
 ' '&a &#45 &; &#0a; <>" />\n') self.compare(INPUT, EXPECTED) - + def compare(self, INPUT, EXPECTED): program, macros = self._compile(INPUT) sio = StringIO() @@ -236,43 +367,44 @@ self.assertEqual(sio.getvalue(), EXPECTED) class InterpolateTestCase(TestCaseBase): - def check_syntax_ok(self): + + def test_syntax_ok(self): text = "foo ${bar_0MAN} $baz_zz bee" mapping = {'bar_0MAN': 'fish', 'baz_zz': 'moo'} expected = "foo fish moo bee" self.assertEqual(interpolate(text, mapping), expected) - def check_syntax_bad(self): + def test_syntax_bad(self): text = "foo $_bar_man} $ ${baz bee" mapping = {'_bar_man': 'fish', 'baz': 'moo'} expected = text self.assertEqual(interpolate(text, mapping), expected) - def check_missing(self): + def test_missing(self): text = "foo ${bar} ${baz}" mapping = {'bar': 'fish'} expected = "foo fish ${baz}" self.assertEqual(interpolate(text, mapping), expected) - def check_redundant(self): + def test_redundant(self): text = "foo ${bar}" mapping = {'bar': 'fish', 'baz': 'moo'} expected = "foo fish" self.assertEqual(interpolate(text, mapping), expected) - def check_numeric(self): + def test_numeric(self): text = "foo ${bar}" mapping = {'bar': 123} expected = "foo 123" self.assertEqual(interpolate(text, mapping), expected) - def check_unicode(self): + def test_unicode(self): text = u"foo ${bar}" mapping = {u'bar': u'baz'} expected = u"foo baz" self.assertEqual(interpolate(text, mapping), expected) - def check_unicode_mixed_unknown_encoding(self): + def test_unicode_mixed_unknown_encoding(self): # This test assumes that sys.getdefaultencoding is ascii... text = u"foo ${bar}" mapping = {u'bar': 'd\xe9j\xe0'} @@ -280,14 +412,14 @@ self.assertEqual(interpolate(text, mapping), expected) def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(MacroErrorsTestCase, "check_")) - suite.addTest(unittest.makeSuite(OutputPresentationTestCase, "check_")) - suite.addTest(unittest.makeSuite(InterpolateTestCase, "check_")) + suite = unittest.makeSuite(I18NErrorsTestCase) + suite.addTest(unittest.makeSuite(MacroErrorsTestCase)) + suite.addTest(unittest.makeSuite(OutputPresentationTestCase)) suite.addTest(unittest.makeSuite(I18NCornerTestCase)) + suite.addTest(unittest.makeSuite(InterpolateTestCase)) + return suite - if __name__ == "__main__": errs = utils.run_suite(test_suite()) sys.exit(errs and 1 or 0) Property changes on: Zope/trunk/lib/python/TAL/tests/test_talinterpreter.py ___________________________________________________________________ Name: cvs2svn:cvs-rev - 1.9 Name: svn:keywords + Id _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins