Commit f97d3008 authored by Florent Guillaume's avatar Florent Guillaume

Merge of the Zope-2_6-i18n-branch into HEAD.

Impacted code:
- TAL: merge of the 2.7 i18n stuff, unicode fixes, tests.
- PageTemplates: addition of a global translation service and of its use
  by the TALES engine, unicode fixes, tests.
- StructuredText: unicode fixes, tests.
parent 66478f12
......@@ -17,7 +17,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths.
"""
__version__='$Revision: 1.37 $'[11:-2]
__version__='$Revision: 1.38 $'[11:-2]
import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
......@@ -246,7 +246,10 @@ class NotExpr:
self._c = compiler.compile(expr)
def __call__(self, econtext):
return not econtext.evaluateBoolean(self._c)
# We use the (not x) and 1 or 0 formulation to avoid changing
# the representation of the result in Python 2.3, where the
# result of "not" becomes an instance of bool.
return (not econtext.evaluateBoolean(self._c)) and 1 or 0
def __repr__(self):
return 'not:%s' % `self._s`
......
##############################################################################
#
# 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.
#
##############################################################################
"""Global Translation Service for providing I18n to Page Templates.
$Id: GlobalTranslationService.py,v 1.2 2002/09/18 15:12:46 efge Exp $
"""
class DummyTranslationService:
"""Translation service that does nothing and returns the message id."""
def translate(self, domain, msgid, mapping=None,
context=None, target_language=None):
return msgid
# XXX Not all of Zope.I18n.ITranslationService is implemented.
translationService = DummyTranslationService()
def setGlobalTranslationService(service):
"""Sets the global translation service, and returns the previous one."""
global translationService
old_service = translationService
translationService = service
return old_service
def getGlobalTranslationService():
"""Returns the global translation service."""
return translationService
......@@ -15,7 +15,7 @@
HTML- and XML-based template objects using TAL, TALES, and METAL.
"""
__version__='$Revision: 1.27 $'[11:-2]
__version__='$Revision: 1.28 $'[11:-2]
import sys
......@@ -24,7 +24,8 @@ from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALGenerator import TALGenerator
from TAL.TALInterpreter import TALInterpreter
from Expressions import getEngine
from cStringIO import StringIO
# Do not use cStringIO here! It's not unicode aware. :(
from StringIO import StringIO
from ExtensionClass import Base
from ComputedAttribute import ComputedAttribute
......@@ -208,3 +209,4 @@ class PageTemplateTracebackSupplement:
if e:
w = list(w) + list(e)
self.warnings = w
......@@ -15,7 +15,7 @@
Zope object encapsulating a Page Template from the filesystem.
"""
__version__='$Revision: 1.20 $'[11:-2]
__version__='$Revision: 1.21 $'[11:-2]
import os, AccessControl, Acquisition, sys
from Globals import package_home, DevelopmentMode
......@@ -85,7 +85,8 @@ class PageTemplateFile(Script, PageTemplate, Traversable):
response = self.REQUEST.RESPONSE
if not response.headers.has_key('content-type'):
response.setHeader('content-type', self.content_type)
except AttributeError: pass
except AttributeError:
pass
# Execute the template in a new security context.
security=getSecurityManager()
......
......@@ -14,7 +14,7 @@
"""Generic Python Expression Handler
"""
__version__='$Revision: 1.8 $'[11:-2]
__version__='$Revision: 1.9 $'[11:-2]
from TALES import CompilerError
from sys import exc_info
......@@ -78,3 +78,4 @@ class ExprTypeProxy:
def __call__(self, text):
return self._handler(self._name, text,
self._econtext._engine)(self._econtext)
......@@ -15,10 +15,12 @@
An implementation of a generic TALES engine
"""
__version__='$Revision: 1.31 $'[11:-2]
__version__='$Revision: 1.32 $'[11:-2]
import re, sys, ZTUtils
from MultiMapping import MultiMapping
from DocumentTemplate.DT_Util import ustr
from GlobalTranslationService import getGlobalTranslationService
StringType = type('')
......@@ -222,11 +224,11 @@ class Context:
def evaluateBoolean(self, expr):
return not not self.evaluate(expr)
def evaluateText(self, expr, None=None):
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is Default or text is None:
return text
return str(text)
return ustr(text)
def evaluateStructure(self, expr):
return self.evaluate(expr)
......@@ -249,6 +251,11 @@ class Context:
def setPosition(self, position):
self.position = position
def translate(self, domain, msgid, mapping=None,
context=None, target_language=None):
return getGlobalTranslationService().translate(
domain, msgid, mapping=mapping,
context=context, target_language=target_language)
class TALESTracebackSupplement:
......@@ -282,3 +289,4 @@ class SimpleExpr:
return self._name, self._expr
def __repr__(self):
return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
......@@ -15,7 +15,7 @@
Zope object encapsulating a Page Template.
"""
__version__='$Revision: 1.43 $'[11:-2]
__version__='$Revision: 1.44 $'[11:-2]
import os, AccessControl, Acquisition, sys
from types import StringType
......@@ -369,3 +369,4 @@ def initialize(context):
)
context.registerHelp()
context.registerHelpTitle('Zope Help')
......@@ -34,3 +34,4 @@ class harness2(harness1):
assert aargs == args, "Harness method arguments"
assert akwargs == kwargs, "Harness method keyword args"
return result
<html>
<body>
<head>
<p i18n:translate="foobar">baz</p>
</head>
</body>
</html>
<html>
<body>
<head>
<p i18n:domain="foo" i18n:translate="bar">baz</p>
</head>
</body>
</html>
<html>
<body>
<head>
<p tal:content="python:u'e acute=\xe9'">e acute here</p>
</head>
</body>
</html>
<html>
<body>
<head>
<p>foobar</p>
</head>
</body>
</html>
<html>
<body>
<head>
<p>[foo](bar)</p>
</head>
</body>
</html>
<html>
<body>
<head>
<p>e acute=é</p>
</head>
</body>
</html>
......@@ -140,3 +140,4 @@ def test_suite():
if __name__=='__main__':
main()
......@@ -15,8 +15,9 @@ import os, sys, unittest
from Products.PageTemplates.tests import util
from Products.PageTemplates.PageTemplate import PageTemplate
import ZODB
from AccessControl import User, SecurityManager
from Products.PageTemplates.GlobalTranslationService import \
setGlobalTranslationService
from AccessControl import SecurityManager
from AccessControl.SecurityManagement import noSecurityManager
from Acquisition import Implicit
......@@ -26,6 +27,10 @@ class AqPageTemplate(Implicit, PageTemplate):
class Folder(util.Base):
pass
class TestTranslationService:
def translate(self, domain, msgid, *args, **kw):
return "[%s](%s)" % (domain, msgid)
class UnitTestSecurityPolicy:
"""
......@@ -69,6 +74,14 @@ class HTMLTests(unittest.TestCase):
out = apply(t, args, kwargs)
util.check_html(expect, out)
def assert_expected_unicode(self, t, fname, *args, **kwargs):
t.write(util.read_input(fname))
assert not t._v_errors, 'Template errors: %s' % t._v_errors
expect = util.read_output(fname)
expect = unicode(expect, 'utf8')
out = apply(t, args, kwargs)
util.check_html(expect, out)
def getProducts(self):
return [
{'description': 'This is the tee for those who LOVE Zope. '
......@@ -126,8 +139,20 @@ class HTMLTests(unittest.TestCase):
def checkBatchIteration(self):
self.assert_expected(self.folder.t, 'CheckBatchIteration.html')
def checkUnicodeInserts(self):
self.assert_expected_unicode(self.folder.t, 'CheckUnicodeInserts.html')
def checkI18nTranslate(self):
self.assert_expected(self.folder.t, 'CheckI18nTranslate.html')
def checkI18nTranslateHooked(self):
old_ts = setGlobalTranslationService(TestTranslationService())
self.assert_expected(self.folder.t, 'CheckI18nTranslateHooked.html')
setGlobalTranslationService(old_ts)
def test_suite():
return unittest.makeSuite(HTMLTests, 'check')
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
main()
......@@ -4,6 +4,16 @@ from Products.PageTemplates import TALES
from Products.PageTemplates.tests import harness1
import string
class DummyUnicodeExpr:
'''Dummy expression type handler returning unicode'''
def __init__(self, name, expr, engine):
self._name = name
self._expr = expr
def __call__(self, econtext):
return unicode(self._expr, 'latin1')
def __repr__(self):
return '<SimpleExpr %s %s>' % (self._name, `self._expr`)
class TALESTests(unittest.TestCase):
def testIterator0(self):
......@@ -77,6 +87,7 @@ class TALESTests(unittest.TestCase):
def getContext(self, **kws):
e = TALES.Engine()
e.registerType('simple', TALES.SimpleExpr)
e.registerType('unicode', DummyUnicodeExpr)
return apply(e.getContext, (), kws)
def testContext0(self):
......@@ -85,6 +96,11 @@ class TALESTests(unittest.TestCase):
assert se == ('simple', 'x'), (
'Improperly evaluated expression %s.' % `se`)
def testContextUnicode(self):
'''Test evaluateText on unicode-returning expressions'''
se = self.getContext().evaluateText('unicode:\xe9')
self.assertEqual(se, u'\xe9')
def testVariables(self):
'''Test variables'''
ctxt = self.getContext()
......@@ -114,4 +130,4 @@ def test_suite():
return unittest.makeSuite(TALESTests)
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
main()
......@@ -14,8 +14,8 @@
import re, ST, STDOM
from STletters import letters
StringType=type('')
ListType=type([])
from types import StringType, UnicodeType, ListType
StringTypes = (StringType, UnicodeType)
class StructuredTextExample(ST.StructuredTextParagraph):
"""Represents a section of document with literal text, as for examples"""
......@@ -235,7 +235,7 @@ class DocumentClass:
]
def __call__(self, doc):
if type(doc) is type(''):
if type(doc) in StringTypes:
doc=ST.StructuredText(doc)
doc.setSubparagraphs(self.color_paragraphs(
doc.getSubparagraphs()))
......@@ -245,7 +245,7 @@ class DocumentClass:
return doc
def parse(self, raw_string, text_type,
type=type, st=type(''), lt=type([])):
type=type, sts=StringTypes, lt=type([])):
"""
Parse accepts a raw_string, an expr to test the raw_string,
......@@ -261,7 +261,7 @@ class DocumentClass:
tmp = [] # the list to be returned if raw_string is split
append=tmp.append
if type(text_type) is st: text_type=getattr(self, text_type)
if type(text_type) in sts: text_type=getattr(self, text_type)
while 1:
t = text_type(raw_string)
......@@ -272,7 +272,7 @@ class DocumentClass:
if start: append(raw_string[0:start])
tt=type(t)
if tt is st:
if tt in sts:
# if we get a string back, add it to text to be parsed
raw_string = t+raw_string[end:len(raw_string)]
else:
......@@ -299,12 +299,12 @@ class DocumentClass:
for text_type in types:
if type(str) is StringType:
if type(str) in StringTypes:
str = self.parse(str, text_type)
elif type(str) is ListType:
r=[]; a=r.append
for s in str:
if type(s) is StringType:
if type(s) in StringTypes:
s=self.parse(s, text_type)
if type(s) is ListType: r[len(r):]=s
else: a(s)
......@@ -327,7 +327,7 @@ class DocumentClass:
def color_paragraphs(self, raw_paragraphs,
type=type, sequence_types=(type([]), type(())),
st=type('')):
sts=StringTypes):
result=[]
for paragraph in raw_paragraphs:
......@@ -336,7 +336,7 @@ class DocumentClass:
continue
for pt in self.paragraph_types:
if type(pt) is st:
if type(pt) in sts:
# grab the corresponding function
pt=getattr(self, pt)
# evaluate the paragraph
......
......@@ -15,8 +15,8 @@ import re, ST, STDOM
from STletters import letters, digits, literal_punc, under_punc,\
strongem_punc, phrase_delimiters,dbl_quoted_punc
StringType=type('')
ListType=type([])
from types import StringType, UnicodeType, ListType
StringTypes = (StringType, UnicodeType)
def flatten(obj, append):
if obj.getNodeType()==STDOM.TEXT_NODE:
......@@ -308,7 +308,7 @@ class DocumentClass:
]
def __call__(self, doc):
if type(doc) is type(''):
if type(doc) in StringTypes:
doc=ST.StructuredText(doc)
doc.setSubparagraphs(self.color_paragraphs(
doc.getSubparagraphs()))
......@@ -318,7 +318,7 @@ class DocumentClass:
return doc
def parse(self, raw_string, text_type,
type=type, st=type(''), lt=type([])):
type=type, sts=StringTypes, lt=type([])):
"""
Parse accepts a raw_string, an expr to test the raw_string,
......@@ -334,7 +334,7 @@ class DocumentClass:
tmp = [] # the list to be returned if raw_string is split
append=tmp.append
if type(text_type) is st: text_type=getattr(self, text_type)
if type(text_type) in sts: text_type=getattr(self, text_type)
while 1:
t = text_type(raw_string)
......@@ -345,7 +345,7 @@ class DocumentClass:
if start: append(raw_string[0:start])
tt=type(t)
if tt is st:
if tt in sts:
# if we get a string back, add it to text to be parsed
raw_string = t+raw_string[end:len(raw_string)]
else:
......@@ -372,12 +372,12 @@ class DocumentClass:
for text_type in types:
if type(str) is StringType:
if type(str) in StringTypes:
str = self.parse(str, text_type)
elif type(str) is ListType:
r=[]; a=r.append
for s in str:
if type(s) is StringType:
if type(s) in StringTypes:
s=self.parse(s, text_type)
if type(s) is ListType: r[len(r):]=s
else: a(s)
......@@ -400,7 +400,7 @@ class DocumentClass:
def color_paragraphs(self, raw_paragraphs,
type=type, sequence_types=(type([]), type(())),
st=type('')):
sts=StringTypes):
result=[]
for paragraph in raw_paragraphs:
if paragraph.getNodeName() != 'StructuredTextParagraph':
......@@ -408,7 +408,7 @@ class DocumentClass:
continue
for pt in self.paragraph_types:
if type(pt) is st:
if type(pt) in sts:
# grab the corresponding function
pt=getattr(self, pt)
# evaluate the paragraph
......
......@@ -16,6 +16,9 @@ DOM implementation in StructuredText : Read-Only methods
All standard Zope objects support DOM to a limited extent.
"""
from types import StringType, UnicodeType
StringTypes = (StringType, UnicodeType)
# Node type codes
# ---------------
......@@ -81,7 +84,7 @@ class ParentNode:
the child access methods of the DOM.
"""
def getChildNodes(self, type=type, st=type('')):
def getChildNodes(self, type=type, sts=StringTypes):
"""
Returns a NodeList that contains all children of this node.
If there are no children, this is a empty NodeList
......@@ -89,12 +92,12 @@ class ParentNode:
r=[]
for n in self.getChildren():
if type(n) is st: n=TextNode(n)
if type(n) in sts: n=TextNode(n)
r.append(n.__of__(self))
return NodeList(r)
def getFirstChild(self, type=type, st=type('')):
def getFirstChild(self, type=type, sts=StringTypes):
"""
The first child of this node. If there is no such node
this returns None
......@@ -106,12 +109,12 @@ class ParentNode:
n=children[0]
if type(n) is st:
if type(n) in sts:
n=TextNode(n)
return n.__of__(self)
def getLastChild(self, type=type, st=type('')):
def getLastChild(self, type=type, sts=StringTypes):
"""
The last child of this node. If there is no such node
this returns None.
......@@ -119,21 +122,21 @@ class ParentNode:
children = self.getChildren()
if not children: return None
n=chidren[-1]
if type(n) is st: n=TextNode(n)
if type(n) in sts: n=TextNode(n)
return n.__of__(self)
"""
create aliases for all above functions in the pythony way.
"""
def _get_ChildNodes(self, type=type, st=type('')):
return self.getChildNodes(type,st)
def _get_ChildNodes(self, type=type, sts=StringTypes):
return self.getChildNodes(type,sts)
def _get_FirstChild(self, type=type, st=type('')):
return self.getFirstChild(type,st)
def _get_FirstChild(self, type=type, sts=StringTypes):
return self.getFirstChild(type,sts)
def _get_LastChild(self, type=type, st=type('')):
return self.getLastChild(type,st)
def _get_LastChild(self, type=type, sts=StringTypes):
return self.getLastChild(type,sts)
class NodeWrapper(ParentNode):
"""
......@@ -167,7 +170,7 @@ class NodeWrapper(ParentNode):
def getPreviousSibling(self,
type=type,
st=type(''),
sts=StringTypes,
getattr=getattr,
None=None):
......@@ -190,13 +193,13 @@ class NodeWrapper(ParentNode):
try: n=children[index]
except IndexError: return None
else:
if type(n) is st:
if type(n) in sts:
n=TextNode(n)
n._DOMIndex=index
return n.__of__(self)
def getNextSibling(self, type=type, st=type('')):
def getNextSibling(self, type=type, sts=StringTypes):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
......@@ -216,7 +219,7 @@ class NodeWrapper(ParentNode):
except IndexError:
return None
else:
if type(n) is st:
if type(n) in sts:
n=TextNode(n)
n._DOMIndex=index
return n.__of__(self)
......@@ -239,14 +242,14 @@ class NodeWrapper(ParentNode):
def _get_PreviousSibling(self,
type=type,
st=type(''),
sts=StringTypes,
getattr=getattr,
None=None):
return self.getPreviousSibling(type,st,getattr,None)
return self.getPreviousSibling(type,sts,getattr,None)
def _get_NextSibling(self, type=type, st=type('')):
return self.getNextSibling(type,st)
def _get_NextSibling(self, type=type, sts=StringTypes):
return self.getNextSibling(type,sts)
def _get_OwnerDocument(self):
return self.getOwnerDocument()
......@@ -288,7 +291,7 @@ class Node(ParentNode):
def getPreviousSibling(self,
type=type,
st=type(''),
sts=StringTypes,
getattr=getattr,
None=None):
"""
......@@ -296,7 +299,7 @@ class Node(ParentNode):
there is no such node, this returns None.
"""
def getNextSibling(self, type=type, st=type('')):
def getNextSibling(self, type=type, sts=StringTypes):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
......@@ -342,13 +345,13 @@ class Node(ParentNode):
def _get_PreviousSibling(self,
type=type,
st=type(''),
sts=StringTypes,
getattr=getattr,
None=None):
return self.getPreviousSibling(type,st,getattr,None)
return self.getPreviousSibling(type,sts,getattr,None)
def _get_NextSibling(self, type=type, st=type('')):
def _get_NextSibling(self, type=type, sts=StringTypes):
return self.getNextSibling()
def _get_Attributes(self):
......@@ -407,10 +410,10 @@ class Element(Node):
"""A code representing the type of the node."""
return ELEMENT_NODE
def getNodeValue(self, type=type, st=type('')):
def getNodeValue(self, type=type, sts=StringTypes):
r=[]
for c in self.getChildren():
if type(c) is not st:
if type(c) not in sts:
c=c.getNodeValue()
r.append(c)
return ''.join(r)
......@@ -480,8 +483,8 @@ class Element(Node):
def _get_NodeType(self):
return self.getNodeType()
def _get_NodeValue(self, type=type, st=type('')):
return self.getNodeValue(type,st)
def _get_NodeValue(self, type=type, sts=StringTypes):
return self.getNodeValue(type,sts)
def _get_ParentNode(self):
return self.getParentNode()
......@@ -517,7 +520,7 @@ class NodeList:
def __init__(self,list=None):
self._data = list or []
def __getitem__(self, index, type=type, st=type('')):
def __getitem__(self, index, type=type, sts=StringTypes):
return self._data[index]
def __getslice__(self, i, j):
......
......@@ -17,14 +17,14 @@ from StructuredText import html_with_references, HTML, html_quote
from ST import Basic
import DocBookClass
import HTMLWithImages
from types import StringType
from types import StringType, UnicodeType
import DocumentWithImages
ClassicHTML=HTML
HTMLNG=HTMLClass.HTMLClass()
def HTML(src, level=1):
if isinstance(src, StringType):
if isinstance(src, StringType) or isinstance(src, UnicodeType):
return ClassicHTML(src, level)
return HTMLNG(src, level)
......
......@@ -18,6 +18,7 @@ from StructuredText import StructuredText
from StructuredText import HTMLClass
from StructuredText.StructuredText import HTML
import sys, os, unittest, cStringIO
from types import UnicodeType
from OFS import ndiff
"""
......@@ -52,6 +53,8 @@ class StructuredTextTests(unittest.TestCase):
raw_text = readFile(regressions,f)
assert StructuredText.StructuredText(raw_text),\
'StructuredText failed on %s' % f
assert StructuredText.StructuredText(unicode(raw_text)),\
'StructuredText failed on Unicode %s' % f
def testStructuredTextNG(self):
""" testing StructuredTextNG """
......@@ -60,6 +63,8 @@ class StructuredTextTests(unittest.TestCase):
raw_text = readFile(regressions,f)
assert ST.StructuredText(raw_text),\
'StructuredText failed on %s' % f
assert ST.StructuredText(unicode(raw_text)),\
'StructuredText failed on Unicode %s' % f
def testDocumentClass(self):
......@@ -131,12 +136,25 @@ class BasicTests(unittest.TestCase):
def _test(self,stxtxt , expected):
res = HTML(stxtxt,level=1,header=0)
if not isinstance(stxtxt, UnicodeType):
res = HTML(stxtxt,level=1,header=0)
if res.find(expected)==-1:
print "Text: ",stxtxt
print "Converted:",res
print "Expected: ",expected
raise AssertionError,"basic test failed for '%s'" % stxtxt
if isinstance(stxtxt, UnicodeType):
ustxtxt = stxtxt
else:
ustxtxt = unicode(stxtxt)
res = HTML(ustxtxt,level=1,header=0)
if res.find(expected)==-1:
print "Text: ",stxtxt
print "Converted:",res
print "Expected: ",expected
raise AssertionError,"basic test failed for '%s'" % stxtxt
print "Text: ",stxtxt.encode('latin-1')
print "Converted:",res.encode('latin-1')
print "Expected: ",expected.encode('latin-1')
raise AssertionError, ("basic test failed for Unicode '%s'"
% stxtxt)
def testUnderline(self):
......@@ -192,6 +210,14 @@ class BasicTests(unittest.TestCase):
'<code>"literal":http://www.zope.org/.</code>')
def XXXtestUnicodeContent(self):
# This fails because ST uses the default locale to get "letters"
# whereas it should use \w+ and re.U if the string is Unicode.
#self._test(u"h\xe9 **y\xe9** xx",
# u"h\xe9 <strong>y\xe9</strong> xx")
pass
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( StructuredTextTests ) )
......
......@@ -8,7 +8,7 @@
# 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.
#
##############################################################################
"""
......@@ -18,13 +18,20 @@ Dummy TALES engine so that I can test out the TAL implementation.
import re
import sys
import driver
from TALDefs import NAME_RE, TALESError, ErrorInfo
class Default:
from ITALES import ITALESCompiler, ITALESEngine
from DocumentTemplate.DT_Util import ustr
try:
from Zope.I18n.ITranslationService import ITranslationService
from Zope.I18n.IDomain import IDomain
except ImportError:
# Before 2.7
class ITranslationService: pass
class IDomain: pass
class _Default:
pass
Default = Default()
Default = _Default()
name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match
......@@ -36,6 +43,8 @@ class DummyEngine:
position = None
source_file = None
__implements__ = ITALESCompiler, ITALESEngine
def __init__(self, macros=None):
if macros is None:
macros = {}
......@@ -43,6 +52,7 @@ class DummyEngine:
dict = {'nothing': None, 'default': Default}
self.locals = self.globals = dict
self.stack = [dict]
self.translationService = DummyTranslationService()
def getCompilerError(self):
return CompilerError
......@@ -90,13 +100,7 @@ class DummyEngine:
if type in ("string", "str"):
return expr
if type in ("path", "var", "global", "local"):
expr = expr.strip()
if self.locals.has_key(expr):
return self.locals[expr]
elif self.globals.has_key(expr):
return self.globals[expr]
else:
raise TALESError("unknown variable: %s" % `expr`)
return self.evaluatePathOrVar(expr)
if type == "not":
return not self.evaluate(expr)
if type == "exists":
......@@ -116,6 +120,15 @@ class DummyEngine:
return '%s (%s,%s)' % (self.source_file, lineno, offset)
raise TALESError("unrecognized expression: " + `expression`)
def evaluatePathOrVar(self, expr):
expr = expr.strip()
if self.locals.has_key(expr):
return self.locals[expr]
elif self.globals.has_key(expr):
return self.globals[expr]
else:
raise TALESError("unknown variable: %s" % `expr`)
def evaluateValue(self, expr):
return self.evaluate(expr)
......@@ -125,7 +138,7 @@ class DummyEngine:
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is not None and text is not Default:
text = str(text)
text = ustr(text)
return text
def evaluateStructure(self, expr):
......@@ -146,6 +159,7 @@ class DummyEngine:
macro = self.macros[localName]
else:
# External macro
import driver
program, macros = driver.compilefile(file)
macro = macros.get(localName)
if not macro:
......@@ -157,6 +171,7 @@ class DummyEngine:
file, localName = self.findMacroFile(macroName)
if not file:
return file, localName
import driver
doc = driver.parsefile(file)
return doc, localName
......@@ -183,6 +198,10 @@ class DummyEngine:
def getDefault(self):
return Default
def translate(self, domain, msgid, mapping):
return self.translationService.translate(domain, msgid, mapping)
class Iterator:
def __init__(self, name, seq, engine):
......@@ -200,3 +219,31 @@ class Iterator:
self.nextIndex = i+1
self.engine.setLocal(self.name, item)
return 1
class DummyDomain:
__implements__ = IDomain
def translate(self, msgid, mapping=None, context=None,
target_language=None):
# This is a fake translation service which simply uppercases non
# ${name} placeholder text in the message id.
#
# First, transform a string with ${name} placeholders into a list of
# substrings. Then upcase everything but the placeholders, then glue
# things back together.
def repl(m, mapping=mapping):
return mapping[m.group(m.lastindex).lower()]
cre = re.compile(r'\$(?:([_A-Z]\w*)|\{([_A-Z]\w*)\})')
return cre.sub(repl, msgid.upper())
class DummyTranslationService:
__implements__ = ITranslationService
def translate(self, domain, msgid, mapping=None, context=None,
target_language=None):
# Ignore domain
return self.getDomain(domain).translate(msgid, mapping, context,
target_language)
def getDomain(self, domain):
return DummyDomain()
......@@ -8,7 +8,7 @@
# 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.
#
##############################################################################
"""
......@@ -18,8 +18,9 @@ Parse HTML and compile to TALInterpreter intermediate code.
import sys
from TALGenerator import TALGenerator
from TALDefs import ZOPE_METAL_NS, ZOPE_TAL_NS, METALError, TALError
from HTMLParser import HTMLParser, HTMLParseError
from TALDefs import \
ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS, METALError, TALError, I18NError
BOOLEAN_HTML_ATTRS = [
# List of Boolean attributes in HTML that may be given in
......@@ -106,13 +107,20 @@ class HTMLTALParser(HTMLParser):
self.gen = gen
self.tagstack = []
self.nsstack = []
self.nsdict = {'tal': ZOPE_TAL_NS, 'metal': ZOPE_METAL_NS}
self.nsdict = {'tal': ZOPE_TAL_NS,
'metal': ZOPE_METAL_NS,
'i18n': ZOPE_I18N_NS,
}
def parseFile(self, file):
f = open(file)
data = f.read()
f.close()
self.parseString(data)
try:
self.parseString(data)
except TALError, e:
e.setFile(file)
raise
def parseString(self, data):
self.feed(data)
......@@ -132,9 +140,10 @@ class HTMLTALParser(HTMLParser):
def handle_starttag(self, tag, attrs):
self.close_para_tags(tag)
self.scan_xmlns(attrs)
tag, attrlist, taldict, metaldict = self.process_ns(tag, attrs)
tag, attrlist, taldict, metaldict, i18ndict \
= self.process_ns(tag, attrs)
self.tagstack.append(tag)
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.gen.emitStartElement(tag, attrlist, taldict, metaldict, i18ndict,
self.getpos())
if tag in EMPTY_HTML_TAGS:
self.implied_endtag(tag, -1)
......@@ -142,14 +151,15 @@ class HTMLTALParser(HTMLParser):
def handle_startendtag(self, tag, attrs):
self.close_para_tags(tag)
self.scan_xmlns(attrs)
tag, attrlist, taldict, metaldict = self.process_ns(tag, attrs)
tag, attrlist, taldict, metaldict, i18ndict \
= self.process_ns(tag, attrs)
if taldict.get("content"):
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos())
i18ndict, self.getpos())
self.gen.emitEndElement(tag, implied=-1)
else:
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos(), isend=1)
i18ndict, self.getpos(), isend=1)
self.pop_xmlns()
def handle_endtag(self, tag):
......@@ -252,7 +262,7 @@ class HTMLTALParser(HTMLParser):
prefix, suffix = name.split(':', 1)
if prefix == 'xmlns':
nsuri = self.nsdict.get(suffix)
if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS):
if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS, ZOPE_I18N_NS):
return name, name, prefix
else:
nsuri = self.nsdict.get(prefix)
......@@ -260,20 +270,20 @@ class HTMLTALParser(HTMLParser):
return name, suffix, 'tal'
elif nsuri == ZOPE_METAL_NS:
return name, suffix, 'metal'
elif nsuri == ZOPE_I18N_NS:
return name, suffix, 'i18n'
return name, name, 0
def process_ns(self, name, attrs):
attrlist = []
taldict = {}
metaldict = {}
i18ndict = {}
name, namebase, namens = self.fixname(name)
for item in attrs:
key, value = item
key, keybase, keyns = self.fixname(key)
if ':' in key and not keyns:
ns = 0
else:
ns = keyns or namens # default to tag namespace
ns = keyns or namens # default to tag namespace
if ns and ns != 'unknown':
item = (key, value, ns)
if ns == 'tal':
......@@ -286,7 +296,12 @@ class HTMLTALParser(HTMLParser):
raise METALError("duplicate METAL attribute " +
`keybase`, self.getpos())
metaldict[keybase] = value
elif ns == 'i18n':
if i18ndict.has_key(keybase):
raise I18NError("duplicate i18n attribute " +
`keybase`, self.getpos())
i18ndict[keybase] = value
attrlist.append(item)
if namens in ('metal', 'tal'):
taldict['tal tag'] = namens
return name, attrlist, taldict, metaldict
return name, attrlist, taldict, metaldict, i18ndict
"""Interface that a TALES engine provides to the METAL/TAL implementation."""
try:
from Interface import Interface
from Interface.Attribute import Attribute
except:
# Before 2.7
class Interface: pass
def Attribute(*args): pass
class ITALESCompiler(Interface):
"""Compile-time interface provided by a TALES implementation.
The TAL compiler needs an instance of this interface to support
compilation of TALES expressions embedded in documents containing
TAL and METAL constructs.
"""
def getCompilerError():
"""Return the exception class raised for compilation errors.
"""
def compile(expression):
"""Return a compiled form of 'expression' for later evaluation.
'expression' is the source text of the expression.
The return value may be passed to the various evaluate*()
methods of the ITALESEngine interface. No compatibility is
required for the values of the compiled expression between
different ITALESEngine implementations.
"""
class ITALESEngine(Interface):
"""Render-time interface provided by a TALES implementation.
The TAL interpreter uses this interface to TALES to support
evaluation of the compiled expressions returned by
ITALESCompiler.compile().
"""
def getDefault():
"""Return the value of the 'default' TALES expression.
Checking a value for a match with 'default' should be done
using the 'is' operator in Python.
"""
def setPosition((lineno, offset)):
"""Inform the engine of the current position in the source file.
This is used to allow the evaluation engine to report
execution errors so that site developers can more easily
locate the offending expression.
"""
def setSourceFile(filename):
"""Inform the engine of the name of the current source file.
This is used to allow the evaluation engine to report
execution errors so that site developers can more easily
locate the offending expression.
"""
def beginScope():
"""Push a new scope onto the stack of open scopes.
"""
def endScope():
"""Pop one scope from the stack of open scopes.
"""
def evaluate(compiled_expression):
"""Evaluate an arbitrary expression.
No constraints are imposed on the return value.
"""
def evaluateBoolean(compiled_expression):
"""Evaluate an expression that must return a Boolean value.
"""
def evaluateMacro(compiled_expression):
"""Evaluate an expression that must return a macro program.
"""
def evaluateStructure(compiled_expression):
"""Evaluate an expression that must return a structured
document fragment.
The result of evaluating 'compiled_expression' must be a
string containing a parsable HTML or XML fragment. Any TAL
markup cnotained in the result string will be interpreted.
"""
def evaluateText(compiled_expression):
"""Evaluate an expression that must return text.
The returned text should be suitable for direct inclusion in
the output: any HTML or XML escaping or quoting is the
responsibility of the expression itself.
"""
def evaluateValue(compiled_expression):
"""Evaluate an arbitrary expression.
No constraints are imposed on the return value.
"""
def createErrorInfo(exception, (lineno, offset)):
"""Returns an ITALESErrorInfo object.
The returned object is used to provide information about the
error condition for the on-error handler.
"""
def setGlobal(name, value):
"""Set a global variable.
The variable will be named 'name' and have the value 'value'.
"""
def setLocal(name, value):
"""Set a local variable in the current scope.
The variable will be named 'name' and have the value 'value'.
"""
def setRepeat(name, compiled_expression):
"""
"""
def translate(domain, msgid, mapping):
"""
See ITranslationService.translate()
"""
class ITALESErrorInfo(Interface):
type = Attribute("type",
"The exception class.")
value = Attribute("value",
"The exception instance.")
lineno = Attribute("lineno",
"The line number the error occurred on in the source.")
offset = Attribute("offset",
"The character offset at which the error occurred.")
......@@ -8,7 +8,7 @@
# 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.
#
##############################################################################
"""
......@@ -17,13 +17,16 @@ Common definitions used by TAL and METAL compilation an transformation.
from types import ListType, TupleType
TAL_VERSION = "1.3.2"
from ITALES import ITALESErrorInfo
TAL_VERSION = "1.4"
XML_NS = "http://www.w3.org/XML/1998/namespace" # URI for XML namespace
XMLNS_NS = "http://www.w3.org/2000/xmlns/" # URI for XML NS declarations
ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal"
ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal"
ZOPE_I18N_NS = "http://xml.zope.org/namespaces/i18n"
NAME_RE = "[a-zA-Z_][a-zA-Z0-9_]*"
......@@ -32,7 +35,6 @@ KNOWN_METAL_ATTRIBUTES = [
"use-macro",
"define-slot",
"fill-slot",
"slot",
]
KNOWN_TAL_ATTRIBUTES = [
......@@ -47,6 +49,16 @@ KNOWN_TAL_ATTRIBUTES = [
"tal tag",
]
KNOWN_I18N_ATTRIBUTES = [
"translate",
"domain",
"target",
"source",
"attributes",
"data",
"name",
]
class TALError(Exception):
def __init__(self, msg, position=(None, None)):
......@@ -54,6 +66,10 @@ class TALError(Exception):
self.msg = msg
self.lineno = position[0]
self.offset = position[1]
self.filename = None
def setFile(self, filename):
self.filename = filename
def __str__(self):
result = self.msg
......@@ -61,6 +77,8 @@ class TALError(Exception):
result = result + ", at line %d" % self.lineno
if self.offset is not None:
result = result + ", column %d" % (self.offset + 1)
if self.filename is not None:
result = result + ', in file %s' % self.filename
return result
class METALError(TALError):
......@@ -69,9 +87,14 @@ class METALError(TALError):
class TALESError(TALError):
pass
class I18NError(TALError):
pass
class ErrorInfo:
__implements__ = ITALESErrorInfo
def __init__(self, err, position=(None, None)):
if isinstance(err, Exception):
self.type = err.__class__
......
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,7 @@
# 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.
#
##############################################################################
"""
......@@ -16,7 +16,7 @@ Parse XML and compile to TALInterpreter intermediate code.
"""
from XMLParser import XMLParser
from TALDefs import *
from TALDefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
from TALGenerator import TALGenerator
class TALParser(XMLParser):
......@@ -58,13 +58,15 @@ class TALParser(XMLParser):
# attrs is a dict of {name: value}
attrlist = attrs.items()
attrlist.sort() # For definiteness
name, attrlist, taldict, metaldict = self.process_ns(name, attrlist)
name, attrlist, taldict, metaldict, i18ndict \
= self.process_ns(name, attrlist)
attrlist = self.xmlnsattrs() + attrlist
self.gen.emitStartElement(name, attrlist, taldict, metaldict)
self.gen.emitStartElement(name, attrlist, taldict, metaldict, i18ndict)
def process_ns(self, name, attrlist):
taldict = {}
metaldict = {}
i18ndict = {}
fixedattrlist = []
name, namebase, namens = self.fixname(name)
for key, value in attrlist:
......@@ -77,10 +79,14 @@ class TALParser(XMLParser):
elif ns == 'tal':
taldict[keybase] = value
item = item + ("tal",)
elif ns == 'i18n':
assert 0, "dealing with i18n: " + `(keybase, value)`
i18ndict[keybase] = value
item = item + ('i18n',)
fixedattrlist.append(item)
if namens in ('metal', 'tal'):
if namens in ('metal', 'tal', 'i18n'):
taldict['tal tag'] = namens
return name, fixedattrlist, taldict, metaldict
return name, fixedattrlist, taldict, metaldict, i18ndict
def xmlnsattrs(self):
newlist = []
......@@ -89,7 +95,7 @@ class TALParser(XMLParser):
key = "xmlns:" + prefix
else:
key = "xmlns"
if uri in (ZOPE_METAL_NS, ZOPE_TAL_NS):
if uri in (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS):
item = (key, uri, "xmlns")
else:
item = (key, uri)
......@@ -109,6 +115,8 @@ class TALParser(XMLParser):
ns = 'tal'
elif uri == ZOPE_METAL_NS:
ns = 'metal'
elif uri == ZOPE_I18N_NS:
ns = 'i18n'
return (prefixed, name, ns)
return (name, name, None)
......
##############################################################################
#
# 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.
#
##############################################################################
"""Translation context object for the TALInterpreter's I18N support.
The translation context provides a container for the information
needed to perform translation of a marked string from a page template.
$Id: TranslationContext.py,v 1.2 2002/09/18 15:12:48 efge Exp $
"""
DEFAULT_DOMAIN = "default"
class TranslationContext:
"""Information about the I18N settings of a TAL processor."""
def __init__(self, parent=None, domain=None, target=None, source=None):
if parent:
if not domain:
domain = parent.domain
if not target:
target = parent.target
if not source:
source = parent.source
elif domain is None:
domain = DEFAULT_DOMAIN
self.parent = parent
self.domain = domain
self.target = target
self.source = source
......@@ -50,7 +50,7 @@ class XMLParser:
try:
self.parser.ordered_attributes = self.ordered_attributes
except AttributeError:
zLOG.LOG("TAL.XMLParser", zLOG.INFO,
zLOG.LOG("TAL.XMLParser", zLOG.INFO,
"Can't set ordered_attributes")
self.ordered_attributes = 0
for name in self.handler_names:
......
......@@ -9,11 +9,31 @@
# 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.
#
##############################################################################
"""
Driver program to test METAL and TAL implementation.
Usage: driver.py [options] [file]
Options:
-h / --help
Print this message and exit.
-H / --html
-x / --xml
Explicitly choose HTML or XML input. The default is to automatically
select based on the file extension. These options are mutually
exclusive.
-l
Lenient structure insertion.
-m
Macro expansion only
-s
Print intermediate opcodes only
-t
Leave TAL/METAL attributes in output
-i
Leave I18N substitution strings un-interpolated.
"""
import os
......@@ -26,68 +46,131 @@ if __name__ == "__main__":
# Import local classes
import TALDefs
import DummyEngine
from DummyEngine import DummyEngine
from DummyEngine import DummyTranslationService
FILE = "tests/input/test01.xml"
class TestTranslations(DummyTranslationService):
def translate(self, domain, msgid, mapping=None, context=None,
target_language=None):
if msgid == 'timefmt':
return '%(minutes)s minutes after %(hours)s %(ampm)s' % mapping
elif msgid == 'jobnum':
return '%(jobnum)s is the JOB NUMBER' % mapping
elif msgid == 'verify':
s = 'Your contact email address is recorded as %(email)s'
return s % mapping
elif msgid == 'mailto:${request/submitter}':
return 'mailto:bperson@dom.ain'
elif msgid == 'origin':
return '%(name)s was born in %(country)s' % mapping
return DummyTranslationService.translate(self, domain, msgid,
mapping, context,
target_language)
class TestEngine(DummyEngine):
def __init__(self, macros=None):
DummyEngine.__init__(self, macros)
self.translationService = TestTranslations()
def evaluatePathOrVar(self, expr):
if expr == 'here/currentTime':
return {'hours' : 6,
'minutes': 59,
'ampm' : 'PM',
}
elif expr == 'context/@@object_name':
return '7'
elif expr == 'request/submitter':
return 'aperson@dom.ain'
return DummyEngine.evaluatePathOrVar(self, expr)
# This is a disgusting hack so that we can use engines that actually know
# something about certain object paths. TimeEngine knows about
# here/currentTime.
ENGINES = {'test23.html': TestEngine,
'test24.html': TestEngine,
'test26.html': TestEngine,
'test27.html': TestEngine,
'test28.html': TestEngine,
'test29.html': TestEngine,
'test30.html': TestEngine,
'test31.html': TestEngine,
'test32.html': TestEngine,
}
def usage(code, msg=''):
# Python 2.1 required
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
def main():
versionTest = 1
macros = 0
mode = None
showcode = 0
showtal = -1
strictinsert = 1
i18nInterpolate = 1
try:
opts, args = getopt.getopt(sys.argv[1:], "hxlmnst")
opts, args = getopt.getopt(sys.argv[1:], "hHxlmsti",
['help', 'html', 'xml'])
except getopt.error, msg:
sys.stderr.write("\n%s\n" % str(msg))
sys.stderr.write(
"usage: driver.py [-h|-x] [-l] [-m] [-n] [-s] [-t] [file]\n")
sys.stderr.write("-h/-x -- HTML/XML input (default auto)\n")
sys.stderr.write("-l -- lenient structure insertion\n")
sys.stderr.write("-m -- macro expansion only\n")
sys.stderr.write("-n -- turn off the Python 1.5.2 test\n")
sys.stderr.write("-s -- print intermediate code\n")
sys.stderr.write("-t -- leave tal/metal attributes in output\n")
sys.exit(2)
for o, a in opts:
if o == '-h':
usage(2, msg)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
if opt in ('-H', '--html'):
if mode == 'xml':
usage(1, '--html and --xml are mutually exclusive')
mode = "html"
if o == '-l':
if opt == '-l':
strictinsert = 0
if o == '-m':
if opt == '-m':
macros = 1
if o == '-n':
if opt == '-n':
versionTest = 0
if o == '-x':
if opt in ('-x', '--xml'):
if mode == 'html':
usage(1, '--html and --xml are mutually exclusive')
mode = "xml"
if o == '-s':
if opt == '-s':
showcode = 1
if o == '-t':
if opt == '-t':
showtal = 1
if not versionTest:
if sys.version[:5] != "1.5.2":
sys.stderr.write(
"Use Python 1.5.2 only; use -n to disable this test\n")
sys.exit(2)
if opt == '-i':
i18nInterpolate = 0
if args:
file = args[0]
else:
file = FILE
it = compilefile(file, mode)
if showcode: showit(it)
else: interpretit(it, tal=(not macros), showtal=showtal,
strictinsert=strictinsert)
if showcode:
showit(it)
else:
# See if we need a special engine for this test
engine = None
engineClass = ENGINES.get(os.path.basename(file))
if engineClass is not None:
engine = engineClass(macros)
interpretit(it, engine=engine,
tal=(not macros), showtal=showtal,
strictinsert=strictinsert,
i18nInterpolate=i18nInterpolate)
def interpretit(it, engine=None, stream=None, tal=1, showtal=-1,
strictinsert=1):
strictinsert=1, i18nInterpolate=1):
from TALInterpreter import TALInterpreter
program, macros = it
assert TALDefs.isCurrentVersion(program)
if engine is None:
engine = DummyEngine.DummyEngine(macros)
engine = DummyEngine(macros)
TALInterpreter(program, macros, engine, stream, wrap=0,
tal=tal, showtal=showtal, strictinsert=strictinsert)()
tal=tal, showtal=showtal, strictinsert=strictinsert,
i18nInterpolate=i18nInterpolate)()
def compilefile(file, mode=None):
assert mode in ("html", "xml", None)
......
......@@ -71,8 +71,8 @@ def main():
htmlargs.sort()
args = xmlargs + htmlargs
if not args:
sys.stderr.write("No tests found -- please supply filenames\n")
sys.exit(1)
sys.stderr.write("No tests found -- please supply filenames\n")
sys.exit(1)
errors = 0
for arg in args:
locopts = []
......
......@@ -17,6 +17,10 @@ class FileTestCase(unittest.TestCase):
self.__dir = dir
unittest.TestCase.__init__(self)
def shortDescription(self):
return os.path.join("...", "TAL", "tests", "input",
os.path.basename(self.__file))
def runTest(self):
basename = os.path.basename(self.__file)
#sys.stdout.write(basename + " ")
......
This diff is collapsed.
......@@ -68,6 +68,24 @@ class OutputPresentationTestCase(TestCaseBase):
interp()
self.assertEqual(sio.getvalue(), EXPECTED)
def check_unicode_content(self):
INPUT = """<p tal:content="python:u'dj-vu'">para</p>"""
EXPECTED = u"""<p>dj-vu</p>""" "\n"
program, macros = self._compile(INPUT)
sio = StringIO()
interp = TALInterpreter(program, {}, DummyEngine(), sio, wrap=60)
interp()
self.assertEqual(sio.getvalue(), EXPECTED)
def check_unicode_structure(self):
INPUT = """<p tal:replace="structure python:u'dj-vu'">para</p>"""
EXPECTED = u"""dj-vu""" "\n"
program, macros = self._compile(INPUT)
sio = StringIO()
interp = TALInterpreter(program, {}, DummyEngine(), sio, wrap=60)
interp()
self.assertEqual(sio.getvalue(), EXPECTED)
def test_suite():
suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment