summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-08-02 07:58:48 -0500
committerJason Madden <jamadden@gmail.com>2017-08-02 07:58:48 -0500
commitfbfc90eceaf5c1cbca7ca9d7e41f438348af9fd6 (patch)
tree054c3b0bebf165a429887084caf8eff4964ca578
parentad22eb27f9c7756adbf957caa626ab475d27b157 (diff)
downloadzope-tal-fbfc90eceaf5c1cbca7ca9d7e41f438348af9fd6.tar.gz
Add Python 3.6, drop Python 3.3
- Fix tests under PyPy3 - Whitespace cleanup - Remove _u compat function. - Enable coveralls and a coverage environment - ~82% covered in one run.
-rw-r--r--.coveragerc11
-rw-r--r--.travis.yml26
-rw-r--r--CHANGES.rst39
-rw-r--r--README.rst16
-rw-r--r--setup.cfg2
-rw-r--r--setup.py45
-rw-r--r--src/zope/__init__.py2
-rw-r--r--src/zope/tal/tests/__init__.py19
-rw-r--r--src/zope/tal/tests/test_talgettext.py19
-rw-r--r--src/zope/tal/tests/test_talinterpreter.py305
-rw-r--r--src/zope/tal/tests/test_xmlparser.py53
-rw-r--r--tox.ini12
12 files changed, 272 insertions, 277 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..cb69a0c
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,11 @@
+[run]
+source = zope.tal
+
+[report]
+precision = 2
+exclude_lines =
+ pragma: no cover
+ if __name__ == '__main__':
+ raise NotImplementedError
+ self.fail
+ raise AssertionError
diff --git a/.travis.yml b/.travis.yml
index 5dd4fef..f44d718 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,20 @@
language: python
sudo: false
-matrix:
- include:
- - python: 3.5
- env: TOXENV=py35
-env:
- - TOXENV=py27
- - TOXENV=py33
- - TOXENV=py34
- - TOXENV=pypy
- - TOXENV=pypy3
+python:
+ - 2.7
+ - 3.4
+ - 3.5
+ - 3.6
+ - pypy
+ - pypy3.5-5.8.0
install:
- - pip install tox
+ - pip install -U pip setuptools
+ - pip install -U coverage coveralls
+ - pip install -U -e .[test]
script:
- - tox --develop
+ - coverage run -m zope.testrunner --test-path=src
+after_success:
+ - coveralls
notifications:
email: false
+cache: pip
diff --git a/CHANGES.rst b/CHANGES.rst
index 4eea076..5ad172e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,13 +1,16 @@
-Changes
-=======
+=========
+ Changes
+=========
+
+4.3.0 (unreleased)
+==================
-4.2.1 (unreleased)
-------------------
+- Drop support for Python 3.3.
-- TBD
+- Add support for Python 3.6.
4.2.0 (2016-04-12)
-------------------
+==================
- Drop support for Python 2.6 and 3.2.
@@ -17,7 +20,7 @@ Changes
- Add support for Python 3.5.
4.1.1 (2015-06-05)
-------------------
+==================
- Suppress deprecation under Python 3.4 for default ``convert_charrefs``
argument (passed to ``HTMLParser``). Also ensures that upcoming change
@@ -26,7 +29,7 @@ Changes
- Add support for Python 3.2 and PyPy3.
4.1.0 (2014-12-19)
-------------------
+==================
.. note::
@@ -39,13 +42,13 @@ Changes
4.0.0 (2014-01-13)
-------------------
+==================
- Fix possible UnicodeDecodeError in warning when msgid already exists.
4.0.0a1 (2013-02-15)
---------------------
+====================
- Replace deprecated ``zope.interface.implements`` usage with equivalent
``zope.interface.implementer`` decorator.
@@ -59,7 +62,7 @@ Changes
3.6.1 (2012-03-09)
-------------------
+==================
- Avoid handling end tags within <script> tags in the HTML parser. This works
around http://bugs.python.org/issue670664
@@ -67,7 +70,7 @@ Changes
- Fix documentation link in README.txt.
3.6.0 (2011-08-20)
-------------------
+==================
- Update `talinterpreter.FasterStringIO` to faster list-based implementation.
@@ -78,21 +81,21 @@ Changes
3.5.2 (2009-10-31)
-------------------
+==================
- In ``talgettext.POEngine.translate``, print a warning if a msgid already
exists in the domain with a different default.
3.5.1 (2009-03-08)
-------------------
+==================
- Update tests of "bad" entities for compatibility with the stricter
HTMLParser module shipped with Python 2.6.x.
3.5.0 (2008-06-06)
-------------------
+==================
- Remove artificial addition of a trailing newline if the output doesn't end
in one; this allows the template source to be the full specification of what
@@ -101,7 +104,7 @@ Changes
3.4.1 (2007-11-16)
-------------------
+==================
- Remove unnecessary ``dummyengine`` dependency on zope.i18n to
simplify distribution. The ``dummyengine.DummyTranslationDomain``
@@ -117,13 +120,13 @@ Changes
3.4.0 (2007-10-03)
-------------------
+==================
- Update package meta-data.
3.4.0b1
--------
+=======
- Update dependency on ``zope.i18n`` to a verions requiring the correct
version of ``zope.security``, avoiding a hidden dependency issue in
diff --git a/README.rst b/README.rst
index e40e341..1def1e8 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,21 @@
-``zope.tal``
-============
+==============
+ ``zope.tal``
+==============
.. image:: https://img.shields.io/pypi/v/zope.tal.svg
- :target: https://pypi.python.org/pypi/zope.tal/
- :alt: Latest Version
+ :target: https://pypi.python.org/pypi/zope.tal/
+ :alt: Latest release
+
+.. image:: https://img.shields.io/pypi/pyversions/zope.tal.svg
+ :target: https://pypi.org/project/zope.tal/
+ :alt: Supported Python versions
.. image:: https://travis-ci.org/zopefoundation/zope.tal.png?branch=master
:target: https://travis-ci.org/zopefoundation/zope.tal
+.. image:: https://coveralls.io/repos/github/zopefoundation/zope.tal/badge.svg?branch=master
+ :target: https://coveralls.io/github/zopefoundation/zope.tal?branch=master
+
The Zope3 Template Attribute Languate (TAL) specifies the custom namespace
and attributes which are used by the Zope Page Templates renderer to inject
dynamic markup into a page. It also includes the Macro Expansion for TAL
diff --git a/setup.cfg b/setup.cfg
index c2de98e..46f86e7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -7,3 +7,5 @@ cover-erase=1
with-doctest=0
where=src
+[bdist_wheel]
+universal = 1
diff --git a/setup.py b/setup.py
index 63d19b2..cdc94ef 100644
--- a/setup.py
+++ b/setup.py
@@ -42,8 +42,13 @@ def alltests():
suites = list(find_suites(options))
return TestSuite(suites)
+TESTS_REQUIRE = [
+ 'zope.testing',
+ 'zope.testrunner',
+]
+
setup(name='zope.tal',
- version='4.2.1.dev0',
+ version='4.3.0.dev0',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description='Zope Template Application Language (TAL)',
@@ -51,9 +56,9 @@ setup(name='zope.tal',
read('README.rst')
+ '\n\n' +
read('CHANGES.rst')
- ),
- keywords = "zope template xml tal",
- classifiers = [
+ ),
+ keywords="zope template xml tal",
+ classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
@@ -62,29 +67,31 @@ setup(name='zope.tal',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Natural Language :: English',
'Operating System :: OS Independent',
'Topic :: Internet :: WWW/HTTP',
- 'Framework :: Zope3'],
- url='http://pypi.python.org/pypi/zope.tal',
+ 'Framework :: Zope3',
+ ],
+ url='http://github.com/zopefoundation/zope.tal',
license='ZPL 2.1',
packages=find_packages('src'),
- package_dir = {'': 'src'},
+ package_dir={'': 'src'},
namespace_packages=['zope'],
- extras_require = dict(
- test=['zope.testing',
- ]),
+ extras_require={
+ 'test': TESTS_REQUIRE,
+ },
test_suite="__main__.alltests", # to support "setup.py test"
- tests_require=['zope.testrunner'],
- install_requires=['setuptools',
- 'zope.i18nmessageid',
- 'zope.interface',
- ],
- include_package_data = True,
- zip_safe = False,
- )
+ tests_require=TESTS_REQUIRE,
+ install_requires=[
+ 'setuptools',
+ 'zope.i18nmessageid',
+ 'zope.interface',
+ ],
+ include_package_data=True,
+ zip_safe=False,
+)
diff --git a/src/zope/__init__.py b/src/zope/__init__.py
index de40ea7..2cdb0e4 100644
--- a/src/zope/__init__.py
+++ b/src/zope/__init__.py
@@ -1 +1 @@
-__import__('pkg_resources').declare_namespace(__name__)
+__import__('pkg_resources').declare_namespace(__name__) # pragma: no cover
diff --git a/src/zope/tal/tests/__init__.py b/src/zope/tal/tests/__init__.py
index 09b3f74..05f0007 100644
--- a/src/zope/tal/tests/__init__.py
+++ b/src/zope/tal/tests/__init__.py
@@ -11,22 +11,3 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
-import sys
-
-if sys.version_info[0] < 3: #pragma NO COVER Python2
-
- PY2 = True
- PY3 = False
-
- def _u(s, encoding='unicode_escape'):
- return unicode(s, encoding)
-
-else: #pragma NO COVER Python3
-
- PY2 = False
- PY3 = True
-
- def _u(s, encoding=None):
- if encoding is None:
- return s
- return str(s, encoding)
diff --git a/src/zope/tal/tests/test_talgettext.py b/src/zope/tal/tests/test_talgettext.py
index 5475b90..d3b57d0 100644
--- a/src/zope/tal/tests/test_talgettext.py
+++ b/src/zope/tal/tests/test_talgettext.py
@@ -16,7 +16,6 @@
from __future__ import print_function
-import sys
import tempfile
import unittest
import warnings
@@ -31,7 +30,6 @@ except ImportError:
from zope.tal.htmltalparser import HTMLTALParser
from zope.tal.talgettext import POTALInterpreter
from zope.tal.talgettext import POEngine
-from . import _u
class test_POEngine(unittest.TestCase):
"""Test the PO engine functionality, which simply adds items to a catalog
@@ -47,9 +45,10 @@ class test_POEngine(unittest.TestCase):
engine.translate(key, 'domain')
for key in test_keys:
- self.assertTrue(key in engine.catalog['domain'],
- "POEngine catalog does not properly store message ids"
- )
+ self.assertIn(
+ key, engine.catalog['domain'],
+ "POEngine catalog does not properly store message ids"
+ )
def test_translate_existing(self):
engine = POEngine()
@@ -75,10 +74,10 @@ class test_POEngine(unittest.TestCase):
engine.file = 'psc_release_listing.pt'
# position is position in file.
engine.translate('foo', 'domain',
- default=_u('Read more\u2026'), position=7)
+ default=u'Read more\u2026', position=7)
# Adding the same key with the same default is fine.
engine.translate('foo', 'domain',
- default=_u('Read more\u2026'), position=13)
+ default=u'Read more\u2026', position=13)
# Adding the same key with a different default is bad and
# triggers a warning.
with warnings.catch_warnings(record=True) as log:
@@ -116,9 +115,9 @@ class test_POEngine(unittest.TestCase):
msgids += list(domain)
msgids.sort()
self.assertEqual(msgids,
- ['A <a href="${DYNAMIC_CONTENT}">link</a>.',
- 'Some ${DYNAMIC_CONTENT} text.'])
+ ['A <a href="${DYNAMIC_CONTENT}">link</a>.',
+ 'Some ${DYNAMIC_CONTENT} text.'])
def test_suite():
- return unittest.makeSuite(test_POEngine)
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
diff --git a/src/zope/tal/tests/test_talinterpreter.py b/src/zope/tal/tests/test_talinterpreter.py
index 0e54f24..dd6b4df 100644
--- a/src/zope/tal/tests/test_talinterpreter.py
+++ b/src/zope/tal/tests/test_talinterpreter.py
@@ -15,7 +15,7 @@
"""Tests for TALInterpreter.
"""
import os
-import platform
+
import sys
import unittest
@@ -26,7 +26,6 @@ except ImportError:
# Python 3.x
from io import StringIO
-IS_PYPY = platform.python_implementation() == 'PyPy'
from zope.tal.taldefs import METALError, I18NError, TAL_VERSION
from zope.tal.taldefs import TALExpressionError
@@ -36,8 +35,6 @@ from zope.tal.talinterpreter import TALInterpreter
from zope.tal.talgenerator import TALGenerator
from zope.tal.dummyengine import DummyEngine
from zope.tal.dummyengine import MultipleDomainsDummyEngine
-from zope.tal.tests import utils
-from . import _u
from zope.i18nmessageid import Message
@@ -61,12 +58,8 @@ class MacroErrorsTestCase(TestCaseBase):
self.interpreter = TALInterpreter(program, {}, self.engine)
def tearDown(self):
- try:
+ with self.assertRaises(METALError):
self.interpreter()
- except METALError:
- pass
- else:
- self.fail("Expected METALError")
def test_mode_error(self):
self.macro[1] = ("mode", "duh")
@@ -75,9 +68,13 @@ class MacroErrorsTestCase(TestCaseBase):
self.macro[0] = ("version", "duh")
-class _MacroFunkyErrorTest(TestCaseBase):
+class TestMacroFunkyError(TestCaseBase):
+ @unittest.expectedFailure
def test_div_in_p_using_macro(self):
+ # We have not found a solution for this
+ # and it is a deep and undocumented HTML parser issue.
+ # Fred is looking into this.
dummy, macros = self._compile('<p metal:define-macro="M">Booh</p>')
engine = DummyEngine(macros)
program, dummy = self._compile(
@@ -96,19 +93,18 @@ class MacroExtendTestCase(TestCaseBase):
s = self._read(('input', 'acme_template.pt'))
self.acme_program, acme_macros = self._compile(s)
s = self._read(('input', 'document_list.pt'))
- self.doclist_program, doclist_macros = self._compile(s)
+ self.doclist_program, _doclist_macros = self._compile(s)
macros = {
'pnome_macros_page': pnome_macros['page'],
'acme_macros_page': acme_macros['page'],
- }
+ }
self.engine = DummyEngine(macros)
def _read(self, path):
dir = os.path.dirname(__file__)
fn = os.path.join(dir, *path)
- f = open(fn)
- data = f.read()
- f.close()
+ with open(fn) as f:
+ data = f.read()
return data
def test_preview_acme_template(self):
@@ -134,16 +130,18 @@ class MacroExtendTestCase(TestCaseBase):
self.assertEqual(actual, expected)
-class _I18NCornerTestCaseBase(TestCaseBase):
+class I18NCornerTestCaseMessage(TestCaseBase):
+
+ interpreter = None
- def factory(self, msgid, default, mapping={}):
- raise NotImplementedError("abstract method")
+ def factory(self, msgid, default=None, mapping=None, domain=None):
+ return Message(msgid, domain=domain, default=default, mapping=mapping or {})
def setUp(self):
self.engine = DummyEngine()
# Make sure we'll translate the msgid not its unicode representation
self.engine.setLocal('foo',
- self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
+ self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
self.engine.setLocal('bar', 'BaRvAlUe')
def _check(self, program, expected):
@@ -156,114 +154,114 @@ class _I18NCornerTestCaseBase(TestCaseBase):
def test_simple_messageid_translate(self):
# This test is mainly here to make sure our DummyEngine works
# correctly.
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:content="foo"/>')
self._check(program, '<span>FOOVALUE</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:replace="foo"/>')
self._check(program, 'FOOVALUE')
# i18n messages defined in Python are translated automatically
# (no i18n:translate necessary)
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="foo" />')
self._check(program, '<span>FOOVALUE</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:replace="foo" />')
self._check(program, 'FOOVALUE')
def test_attributes_translation(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:attributes="test bar"/>')
self._check(program, '<span test="BaRvAlUe" />')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span test="bar" i18n:attributes="test"/>')
self._check(program, '<span test="BAR" />')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:attributes="test bar" i18n:attributes="test"/>')
self._check(program, '<span test="BARVALUE" />')
# i18n messages defined in Python are translated automatically
# (no i18n:attributes necessary)
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:attributes="test foo"/>')
self._check(program, '<span test="FOOVALUE" />')
def test_text_variable_translate(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="bar"/>')
self._check(program, '<span>BaRvAlUe</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:content="bar"/>')
self._check(program, '<span>BARVALUE</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:replace="bar"/>')
self._check(program, 'BARVALUE')
def test_text_translate(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="string:BaR"/>')
self._check(program, '<span>BaR</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:content="string:BaR"/>')
self._check(program, '<span>BAR</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:replace="string:BaR"/>')
self._check(program, 'BAR')
def test_structure_text_variable_translate(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="structure bar"/>')
self._check(program, '<span>BaRvAlUe</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:content="structure bar"/>')
self._check(program, '<span>BARVALUE</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:replace="structure bar"/>')
self._check(program, 'BARVALUE')
# i18n messages defined in Python are translated automatically
# (no i18n:translate necessary)
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="structure foo"/>')
self._check(program, '<span>FOOVALUE</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:replace="structure foo"/>')
self._check(program, 'FOOVALUE')
def test_structure_text_translate(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span tal:content="structure string:BaR"/>')
self._check(program, '<span>BaR</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:content="structure string:BaR"/>')
self._check(program, '<span>BAR</span>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<span i18n:translate="" tal:replace="structure string:BaR"/>')
self._check(program, 'BAR')
def test_replace_with_messageid_and_i18nname(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="" >'
'<span i18n:translate="" tal:replace="foo" i18n:name="foo_name"/>'
'</div>')
self._check(program, '<div>FOOVALUE</div>')
def test_pythonexpr_replace_with_messageid_and_i18nname(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="" >'
'<span i18n:translate="" tal:replace="python: foo"'
' i18n:name="foo_name"/>'
@@ -271,7 +269,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
self._check(program, '<div>FOOVALUE</div>')
def test_structure_replace_with_messageid_and_i18nname(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="" >'
'<span i18n:translate="" tal:replace="structure foo"'
' i18n:name="foo_name"/>'
@@ -279,7 +277,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
self._check(program, '<div>FOOVALUE</div>')
def test_complex_replace_with_messageid_and_i18nname(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="" >'
'<em tal:omit-tag="" i18n:name="foo_name">'
'<span i18n:translate="" tal:replace="foo"/>'
@@ -288,7 +286,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
self._check(program, '<div>FOOVALUE</div>')
def test_content_with_messageid_and_i18nname(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="" >'
'<span i18n:translate="" tal:content="foo" i18n:name="foo_name"/>'
'</div>')
@@ -313,13 +311,13 @@ class _I18NCornerTestCaseBase(TestCaseBase):
'<span i18n:translate="" i18n:name="color_name">green</span>')
def test_translate_static_text_as_dynamic(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="">This is text for '
'<span tal:content="bar" i18n:name="bar_name"/>.'
'</div>')
self._check(program,
'<div>THIS IS TEXT FOR <span>BaRvAlUe</span>.</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="">This is text for '
'<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>.'
'</div>')
@@ -327,35 +325,36 @@ class _I18NCornerTestCaseBase(TestCaseBase):
'<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>')
def test_translate_static_text_as_dynamic_from_bytecode(self):
- program = [('version', TAL_VERSION),
- ('mode', 'html'),
-('setPosition', (1, 0)),
-('beginScope', {'i18n:translate': ''}),
-('startTag', ('div', [('i18n:translate', '', 'i18n')])),
-('insertTranslation',
- ('',
- [('rawtextOffset', ('This is text for ', 17)),
- ('setPosition', (1, 40)),
- ('beginScope',
- {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
- ('i18nVariable',
- ('bar_name',
- [('startTag',
- ('span',
- [('i18n:translate', '', 'i18n'),
- ('tal:content', 'bar', 'tal'),
- ('i18n:name', 'bar_name', 'i18n')])),
- ('insertTranslation',
- ('',
- [('insertText', ('$bar$', []))])),
- ('rawtextOffset', ('</span>', 7))],
- None,
- 0)),
- ('endScope', ()),
- ('rawtextOffset', ('.', 1))])),
-('endScope', ()),
-('rawtextOffset', ('</div>', 6))
-]
+ program = [
+ ('version', TAL_VERSION),
+ ('mode', 'html'),
+ ('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('div', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('rawtextOffset', ('This is text for ', 17)),
+ ('setPosition', (1, 40)),
+ ('beginScope',
+ {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
+ ('i18nVariable',
+ ('bar_name',
+ [('startTag',
+ ('span',
+ [('i18n:translate', '', 'i18n'),
+ ('tal:content', 'bar', 'tal'),
+ ('i18n:name', 'bar_name', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('insertText', ('$bar$', []))])),
+ ('rawtextOffset', ('</span>', 7))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextOffset', ('.', 1))])),
+ ('endScope', ()),
+ ('rawtextOffset', ('</div>', 6))
+ ]
self._check(program,
'<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>')
@@ -369,7 +368,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
# '<div i18n:translate="">This is text for '
# '<span i18n:translate="" tal:content="bar" '
# 'i18n:name="bar_name"/>.</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="">This is text for '
'<span tal:content="bar" '
'i18n:name="bar_name"/>.</div>')
@@ -388,7 +387,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
def test_for_correct_msgids_translate_name(self):
self.engine.translationDomain.clearMsgids()
result = StringIO()
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate="">This is text for '
'<span i18n:translate="" tal:content="bar" '
'i18n:name="bar_name"/>.</div>')
@@ -409,7 +408,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
# on the same element
self.engine.translationDomain.clearMsgids()
result = StringIO()
- program, macros = self._compile(
+ program, _macros = self._compile(
'<p i18n:translate="">'
'Some static text and a <a tal:attributes="href string:url"'
' i18n:name="link" i18n:translate="">link text</a>.</p>')
@@ -432,7 +431,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
# HTML mode
self.engine.translationDomain.clearMsgids()
result = StringIO()
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""> This is text\n'
' \tfor\n div. </div>'
'<pre i18n:translate=""> This is text\n'
@@ -459,7 +458,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
'<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()
+ program, _macros = parser.getCode()
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
@@ -475,7 +474,7 @@ class _I18NCornerTestCaseBase(TestCaseBase):
def test_raw_msgids_and_i18ntranslate_i18nname(self):
self.engine.translationDomain.clearMsgids()
result = StringIO()
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""> This is text\n \tfor\n'
'<pre i18n:name="bar" i18n:translate=""> \tbar\n </pre>.</div>')
self.interpreter = TALInterpreter(program, {}, self.engine,
@@ -488,22 +487,17 @@ class _I18NCornerTestCaseBase(TestCaseBase):
self.assertEqual('This is text for ${bar}.', msgids[1][0])
self.assertEqual({'bar': '<pre> \tBAR\n </pre>'}, msgids[1][1])
self.assertEqual(
- _u('<div>THIS IS TEXT FOR <pre> \tBAR\n </pre>.</div>'),
+ (u'<div>THIS IS TEXT FOR <pre> \tBAR\n </pre>.</div>'),
result.getvalue())
- if sys.version_info[:2] != (3,2) or IS_PYPY:
- 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(
- r'''<div i18n:translate='' tal:define='bar python:u"\u00C0"'>'''
- r'''Foo <span tal:replace='bar' i18n:name='bar' /></div>''')
- self._check(program, _u("<div>FOO \u00C0</div>"))
+ 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(
+ r'''<div i18n:translate='' tal:define='bar python:u"\u00C0"'>'''
+ r'''Foo <span tal:replace='bar' i18n:name='bar' /></div>''')
+ self._check(program, (u"<div>FOO \u00C0</div>"))
-class I18NCornerTestCaseMessage(_I18NCornerTestCaseBase):
-
- def factory(self, msgid, default=None, mapping={}, domain=None):
- return Message(msgid, domain=domain, default=default, mapping=mapping)
class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
@@ -512,40 +506,40 @@ class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
# where default domain transforms to uppercase
self.engine = MultipleDomainsDummyEngine()
self.engine.setLocal('foo',
- self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
+ self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
self.engine.setLocal('bar', 'BaRvAlUe')
self.engine.setLocal('baz',
- self.factory('BaZvAlUe', 'default', {}))
+ self.factory('BaZvAlUe', 'default', {}))
# Message ids with different domains
self.engine.setLocal('toupper',
- self.factory('ToUpper', 'default', {}))
+ self.factory('ToUpper', 'default', {}))
self.engine.setLocal('tolower',
- self.factory('ToLower', 'default', {}, domain='lower'))
+ self.factory('ToLower', 'default', {}, domain='lower'))
def test_multiple_domains(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' tal:content="toupper" />')
self._check(program, '<div>TOUPPER</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' tal:content="tolower" />')
self._check(program, '<div>tolower</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' tal:content="string:ToUpper" />')
self._check(program, '<div>TOUPPER</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' i18n:domain="lower"'
' tal:content="string:ToLower" />')
self._check(program, '<div>tolower</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' tal:define="msgid string:ToUpper"'
' tal:content="msgid" />')
self._check(program, '<div>TOUPPER</div>')
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' i18n:domain="lower"'
' tal:define="msgid string:ToLower"'
@@ -555,20 +549,24 @@ class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
def test_unused_explicit_domain(self):
#a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine
#is a domain that transforms to lowercase
- self.engine.setLocal('othertolower',
- self.factory('OtherToLower', 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine', {}, domain='lower'))
- program, macros = self._compile(
+ self.engine.setLocal(
+ 'othertolower',
+ self.factory('OtherToLower',
+ 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine',
+ {},
+ domain='lower'))
+ program, _macros = self._compile(
'<div i18n:translate=""'
' tal:content="othertolower" />')
self._check(program, '<div>othertolower</div>')
#takes domain into account for strings
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' i18n:domain="a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine"'
' tal:content="string:ToLower" />')
self._check(program, '<div>tolower</div>')
#but not for messageids
- program, macros = self._compile(
+ program, _macros = self._compile(
'<div i18n:translate=""'
' i18n:domain="a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine"'
' tal:content="baz" />')
@@ -576,6 +574,8 @@ class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
class ScriptTestCase(TestCaseBase):
+ interpreter = None
+
def setUp(self):
self.engine = DummyEngine()
@@ -587,12 +587,12 @@ class ScriptTestCase(TestCaseBase):
self.assertEqual(expected, result.getvalue())
def test_simple(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<p tal:script="text/server-python">print("hello")</p>')
self._check(program, '<p>hello\n</p>')
def test_script_and_tal_block(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<tal:block script="text/server-python">\n'
' global x\n'
' x = 1\n'
@@ -602,42 +602,42 @@ class ScriptTestCase(TestCaseBase):
self.assertEqual(self.engine.codeGlobals['x'], 1)
def test_script_and_tal_block_having_inside_print(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<tal:block script="text/server-python">\n'
' print("hello")'
'</tal:block>')
self._check(program, 'hello\n')
def test_script_and_omittag(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<p tal:omit-tag="" tal:script="text/server-python">\n'
' print("hello")'
'</p>')
self._check(program, 'hello\n')
def test_script_and_inside_tags(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<p tal:omit-tag="" tal:script="text/server-python">\n'
' print("<b>hello</b>")'
'</p>')
self._check(program, '<b>hello</b>\n')
def test_script_and_inside_tags_with_tal(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<p tal:omit-tag="" tal:script="text/server-python"> <!--\n'
' print("""<b tal:replace="string:foo">hello</b>""")\n'
'--></p>')
self._check(program, '<b tal:replace="string:foo">hello</b>\n')
def test_html_script(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<script type="text/server-python">\n'
' print("Hello world!")\n'
'</script>')
self._check(program, 'Hello world!\n')
def test_html_script_and_javascript(self):
- program, macros = self._compile(
+ program, _macros = self._compile(
'<script type="text/javascript" src="somefile.js" />\n'
'<script type="text/server-python">\n'
' print("Hello world!")\n'
@@ -670,9 +670,10 @@ class I18NErrorsTestCase(TestCaseBase):
"missing i18n:id value not caught")
def test_id_with_attributes(self):
- self._check('''<input name="Delete"
- tal:attributes="name string:delete_button"
- i18n:attributes="name message-id">''',
+ self._check(
+ '''<input name="Delete"
+ tal:attributes="name string:delete_button"
+ i18n:attributes="name message-id">''',
"expected attribute being both part of tal:attributes" +
" and having a msgid in i18n:attributes to be denied")
@@ -693,50 +694,49 @@ class OutputPresentationTestCase(TestCaseBase):
</html>'''
self.compare(INPUT, EXPECTED)
- if sys.version_info[:2] != (3,2) or IS_PYPY:
- def test_unicode_content(self):
- INPUT = """<p tal:content="python:u'déjà-vu'">para</p>"""
- EXPECTED = _u("""<p>déjà-vu</p>""")
- self.compare(INPUT, EXPECTED)
+ def test_unicode_content(self):
+ INPUT = u"""<p tal:content="python:u'déjà-vu'">para</p>"""
+ EXPECTED = u'<p>d\xe9j\xe0-vu</p>'
+ self.compare(INPUT, EXPECTED)
- def test_unicode_structure(self):
- INPUT = """<p tal:replace="structure python:u'déjà-vu'">para</p>"""
- EXPECTED = _u("""déjà-vu""")
- self.compare(INPUT, EXPECTED)
+ def test_unicode_structure(self):
+ INPUT = u"""<p tal:replace="structure python:u'déjà-vu'">para</p>"""
+ EXPECTED = u'd\xe9j\xe0-vu'
+ self.compare(INPUT, EXPECTED)
def test_i18n_replace_number(self):
INPUT = """
<p i18n:translate="foo ${bar}">
<span tal:replace="python:123" i18n:name="bar">para</span>
</p>"""
- EXPECTED = _u("""
+ EXPECTED = (u"""
<p>FOO 123</p>""")
self.compare(INPUT, EXPECTED)
def test_entities(self):
- if IS_PYPY or sys.version_info[:2] <= (3, 2):
+ if sys.version_info[:2] <= (3, 2):
# HTMLParser.HTMLParser in Python 2.x--3.2 parses "&#45" as "&#45"
INPUT = ('<img tal:define="foo nothing" '
- 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
+ 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
EXPECTED = ('<img alt="&a; \x01 \n '
'&amp;a &amp;#45 &amp;; &lt;&gt;" />')
elif sys.version_info < (3, 4):
# html.parser.HTMLParser in Python 3.3 parses "&#45" as "-"
INPUT = ('<img tal:define="foo nothing" '
- 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
+ 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
EXPECTED = ('<img alt="&a; \x01 \n '
'&amp;a - &amp;; &lt;&gt;" />')
else:
# html.parser.HTMLParser in Python 3.4 parses "&#1" as ""
# because '1' is an "invalid codepoint".
INPUT = ('<img tal:define="foo nothing" '
- 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
+ 'alt="&a; &#1; &#x0a; &a &#45 &; <>" />')
EXPECTED = ('<img alt="&a; \n '
'&amp;a - &amp;; &lt;&gt;" />')
self.compare(INPUT, EXPECTED)
def compare(self, INPUT, EXPECTED):
- program, macros = self._compile(INPUT)
+ program, _macros = self._compile(INPUT)
sio = StringIO()
interp = TALInterpreter(program, {}, DummyEngine(), sio, wrap=60)
interp()
@@ -760,7 +760,8 @@ class TestSourceAnnotations(unittest.TestCase):
interpreter = self.interpreter
interpreter.sourceFile = '/path/to/source.pt'
interpreter.position = (123, 42)
- self.assertEqual(interpreter.formatSourceAnnotation(),
+ self.assertEqual(
+ interpreter.formatSourceAnnotation(),
"<!--\n" +
"=" * 78 + "\n" +
"/path/to/source.pt (line 123)\n" +
@@ -771,7 +772,8 @@ class TestSourceAnnotations(unittest.TestCase):
interpreter = self.interpreter
interpreter.sourceFile = '/path/to/source.pt'
interpreter.position = (None, None)
- self.assertEqual(interpreter.formatSourceAnnotation(),
+ self.assertEqual(
+ interpreter.formatSourceAnnotation(),
"<!--\n" +
"=" * 78 + "\n" +
"/path/to/source.pt\n" +
@@ -808,13 +810,13 @@ class TestErrorTracebacks(TestCaseBase):
# Regression test for http://www.zope.org/Collectors/Zope3-dev/697
def test_define_slot_does_not_clobber_source_file_on_exception(self):
- m_program, m_macros = self._compile("""
+ _m_program, m_macros = self._compile("""
<div metal:define-macro="amacro">
<div metal:define-slot="aslot">
</div>
</div>
""", source_file='macros.pt')
- p_program, p_macros = self._compile("""
+ p_program, _p_macros = self._compile("""
<div metal:use-macro="amacro">
<div metal:fill-slot="aslot">
<tal:x replace="no_such_thing" />
@@ -830,14 +832,14 @@ class TestErrorTracebacks(TestCaseBase):
self.assertEqual(engine.position, (4, 16))
def test_define_slot_restores_source_file_if_no_exception(self):
- m_program, m_macros = self._compile("""
+ _m_program, m_macros = self._compile("""
<div metal:define-macro="amacro">
<div metal:define-slot="aslot">
</div>
<tal:x replace="no_such_thing" />
</div>
""", source_file='macros.pt')
- p_program, p_macros = self._compile("""
+ p_program, _p_macros = self._compile("""
<div metal:use-macro="amacro">
<div metal:fill-slot="aslot">
</div>
@@ -854,19 +856,4 @@ class TestErrorTracebacks(TestCaseBase):
def test_suite():
- return unittest.TestSuite((
- unittest.makeSuite(I18NErrorsTestCase),
- unittest.makeSuite(MacroErrorsTestCase),
- unittest.makeSuite(MacroExtendTestCase),
- unittest.makeSuite(OutputPresentationTestCase),
- unittest.makeSuite(ScriptTestCase),
- unittest.makeSuite(I18NCornerTestCaseMessage),
- unittest.makeSuite(UnusedExplicitDomainTestCase),
- unittest.makeSuite(TestSourceAnnotations),
- unittest.makeSuite(TestErrorTracebacks),
-
- # TODO: Deactivated test, since we have not found a solution for this
- # and it is a deep and undocumented HTML parser issue.
- # Fred is looking into this.
- #unittest.makeSuite(_MacroFunkyErrorTest),
- ))
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
diff --git a/src/zope/tal/tests/test_xmlparser.py b/src/zope/tal/tests/test_xmlparser.py
index b070705..5d66ff9 100644
--- a/src/zope/tal/tests/test_xmlparser.py
+++ b/src/zope/tal/tests/test_xmlparser.py
@@ -13,12 +13,10 @@
##############################################################################
"""Tests for XMLParser.py.
"""
-import sys
import unittest
from zope.tal import xmlparser
from zope.tal.tests import utils
-from . import _u
class EventCollector(xmlparser.XMLParser):
@@ -98,7 +96,7 @@ class SegmentedFile(object):
s = b''
return s
-
+@unittest.skipIf(utils.skipxml, "Skip XML defined")
class XMLParserTestCase(unittest.TestCase):
def _run_check(self, source, events, collector=EventCollector):
@@ -141,7 +139,7 @@ text
("decl", "1.0", "iso-8859-1", -1),
("doctype", "html", "foo", "bar", 0),
("starttag", "html", []),
-# ("entityref", "entity"),
+ # ("entityref", "entity"),
("data", " \n"),
("comment", "comment1a\n-></foo><bar>&lt;<?pi?></foo<bar\ncomment1b"),
("data", "\n"),
@@ -154,37 +152,37 @@ text
])
def test_bad_nesting(self):
- try:
+ with self.assertRaises(Exception) as e:
self._run_check("<a><b></a></b>", [
("starttag", "a", []),
("starttag", "b", []),
("endtag", "a"),
("endtag", "b"),
])
- except:
- e = sys.exc_info()[1]
- self.assertEqual(e.lineno, 1,
- "did not receive correct position information")
- else:
- self.fail("expected parse error: bad nesting")
+
+ e = e.exception
+ self.assertEqual(e.lineno, 1,
+ "did not receive correct position information")
def test_attr_syntax(self):
output = [
- ("starttag", "a", ["b", "v", "c", "v"]),
- ("endtag", "a"),
- ]
+ ("starttag", "a", ["b", "v", "c", "v"]),
+ ("endtag", "a"),
+ ]
self._run_check("""<a b='v' c="v"/>""", output)
self._run_check("""<a b = 'v' c = "v"/>""", output)
self._run_check("""<a\nb\n=\n'v'\nc\n=\n"v"\n/>""", output)
self._run_check("""<a\tb\t=\t'v'\tc\t=\t"v"\t/>""", output)
def test_attr_values(self):
- self._run_check("""<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'/>""",
- [("starttag", "a", ["b", "xxx xxx",
- "c", "yyy yyy",
- "d", " xyz "]),
- ("endtag", "a"),
- ])
+ self._run_check(
+ """<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'/>""",
+ [("starttag", "a", ["b", "xxx xxx",
+ "c", "yyy yyy",
+ "d", " xyz "]),
+ ("endtag", "a"),
+ ]
+ )
self._run_check("""<a b='' c="" d=''/>""", [
("starttag", "a", ["b", "", "c", "", "d", ""]),
("endtag", "a"),
@@ -253,15 +251,12 @@ text
self._parse_error("<!DOCTYPE foo $ >")
def test_unicode_string(self):
- output = [('starttag', _u('p'), []),
- ('data', _u('\xe4\xf6\xfc\xdf')),
- ('endtag', _u('p'))]
- self._run_check(_u('<p>\xe4\xf6\xfc\xdf</p>'), output)
+ output = [('starttag', u'p', []),
+ ('data', u'\xe4\xf6\xfc\xdf'),
+ ('endtag', u'p')]
+ self._run_check(u'<p>\xe4\xf6\xfc\xdf</p>', output)
# Support for the Zope regression test framework:
-def test_suite(skipxml=utils.skipxml):
- if skipxml:
- return unittest.TestSuite()
- else:
- return unittest.makeSuite(XMLParserTestCase)
+def test_suite():
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
diff --git a/tox.ini b/tox.ini
index 1c2eb66..bdd3774 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,20 +1,20 @@
[tox]
envlist =
- py27,py33,py34,py35,pypy,pypy3,coverage
+ py27,py34,py35,py36,pypy,pypy3,coverage
[testenv]
commands =
- python setup.py -q test -q
+ zope-testrunner --test-path=src []
deps =
- zope.testrunner
+ .[test]
[testenv:coverage]
usedevelop = true
basepython =
python2.7
commands =
- nosetests --with-xunit --with-xcoverage
+ coverage run -m zope.testrunner --test-path=src
+ coverage report --fail-under=82
deps =
- nose
+ {[testenv]deps}
coverage
- nosexcover