summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Howitz <mh@gocept.com>2023-01-06 06:54:21 +0100
committerGitHub <noreply@github.com>2023-01-06 06:54:21 +0100
commitd4282a8eaba4ba843b07f088e9443340833cdff6 (patch)
tree9446f9756ff1424be9f8ec6a4429d2f65f68a2c2
parent1a7405a1b621b7080450f55f77119bd346cab404 (diff)
downloadzope-tal-d4282a8eaba4ba843b07f088e9443340833cdff6.tar.gz
Config with pure python template (#19)
* Drop support for Python 2.7, 3.5, 3.6. * Add support for Python 3.11.
-rw-r--r--.github/workflows/tests.yml18
-rw-r--r--.gitignore1
-rw-r--r--.meta.toml14
-rw-r--r--CHANGES.rst11
-rw-r--r--setup.cfg17
-rw-r--r--setup.py30
-rw-r--r--src/zope/tal/driver.py3
-rw-r--r--src/zope/tal/dummyengine.py35
-rw-r--r--src/zope/tal/htmltalparser.py61
-rw-r--r--src/zope/tal/interfaces.py3
-rw-r--r--src/zope/tal/runtest.py19
-rw-r--r--src/zope/tal/taldefs.py7
-rw-r--r--src/zope/tal/talgenerator.py38
-rw-r--r--src/zope/tal/talgettext.py232
-rw-r--r--src/zope/tal/talinterpreter.py94
-rw-r--r--src/zope/tal/talparser.py9
-rw-r--r--src/zope/tal/tests/markbench.py21
-rw-r--r--src/zope/tal/tests/run.py7
-rw-r--r--src/zope/tal/tests/test_files.py10
-rw-r--r--src/zope/tal/tests/test_htmltalparser.py11
-rw-r--r--src/zope/tal/tests/test_sourcepos.py14
-rw-r--r--src/zope/tal/tests/test_talgettext.py15
-rw-r--r--src/zope/tal/tests/test_talinterpreter.py68
-rw-r--r--src/zope/tal/tests/test_talparser.py2
-rw-r--r--src/zope/tal/tests/test_xmlparser.py10
-rw-r--r--src/zope/tal/tests/utils.py3
-rw-r--r--src/zope/tal/timer.py3
-rw-r--r--src/zope/tal/translationcontext.py2
-rw-r--r--src/zope/tal/xmlparser.py22
-rw-r--r--tox.ini30
30 files changed, 267 insertions, 543 deletions
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 927d9f3..2ca497a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -17,32 +17,30 @@ jobs:
fail-fast: false
matrix:
os:
- - ubuntu
+ - ["ubuntu", "ubuntu-20.04"]
config:
# [Python version, tox env]
- ["3.9", "lint"]
- - ["2.7", "py27"]
- - ["3.5", "py35"]
- - ["3.6", "py36"]
- ["3.7", "py37"]
- ["3.8", "py38"]
- ["3.9", "py39"]
- ["3.10", "py310"]
- - ["pypy-2.7", "pypy"]
+ - ["3.11", "py311"]
- ["pypy-3.7", "pypy3"]
- ["3.9", "docs"]
- ["3.9", "coverage"]
- runs-on: ${{ matrix.os }}-latest
+ runs-on: ${{ matrix.os[1] }}
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: ${{ matrix.config[1] }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.config[0] }}
- name: Pip cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }}
@@ -58,7 +56,7 @@ jobs:
- name: Coverage
if: matrix.config[1] == 'coverage'
run: |
- pip install coveralls coverage-python-version
+ pip install coveralls
coveralls --service=github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index c724a76..1f321f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,5 @@ lib64
log/
parts/
pyvenv.cfg
+testing.log
var/
diff --git a/.meta.toml b/.meta.toml
index 0a00784..9c7cd6c 100644
--- a/.meta.toml
+++ b/.meta.toml
@@ -2,29 +2,21 @@
# https://github.com/zopefoundation/meta/tree/master/config/pure-python
[meta]
template = "pure-python"
-commit-id = "3b712f305ca8207e971c5bf81f2bdb5872489f2f"
+commit-id = "d03ad585"
[python]
with-pypy = true
-with-legacy-python = true
with-docs = true
with-sphinx-doctests = false
with-windows = false
with-future-python = false
+with-macos = false
[tox]
use-flake8 = true
[coverage]
-fail-under = 81
-
-[flake8]
-additional-config = [
- "# E741 ambiguous variable name",
- "# F821 undefined name",
- "per-file-ignores =",
- " src/zope/tal/talgettext.py: E741 F821",
- ]
+fail-under = 84
[manifest]
additional-rules = [
diff --git a/CHANGES.rst b/CHANGES.rst
index c5f118d..2b44681 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -2,14 +2,23 @@
Changes
=========
-4.6 (unreleased)
+5.0 (unreleased)
================
+- Add support for Python 3.11.
+
+- Drop support for Python 2.7, 3.5, 3.6.
+
- Add support for Python 3.10.
- Add ``nav`` to the list of HTML block level elements.
(`#18 <https://github.com/zopefoundation/zope.tal/pull/18>`_)
+- Remove ``.talgettext.UpdatePOEngine`` and the ability to call
+ ``zope/tal/talgettext.py`` (main function). The code was broken and unused.
+
+- Remove support to run the tests using deprecated ``python setup.py test``.
+
4.5 (2021-05-28)
================
diff --git a/setup.cfg b/setup.cfg
index f32b31d..ebb8516 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,17 +1,24 @@
# Generated from:
# https://github.com/zopefoundation/meta/tree/master/config/pure-python
[bdist_wheel]
-universal = 1
+universal = 0
[flake8]
doctests = 1
-# E741 ambiguous variable name
-# F821 undefined name
-per-file-ignores =
- src/zope/tal/talgettext.py: E741 F821
[check-manifest]
ignore =
.editorconfig
.meta.toml
docs/_build/html/_sources/*
+
+[isort]
+force_single_line = True
+combine_as_imports = True
+sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER
+known_third_party = six, docutils, pkg_resources
+known_zope =
+known_first_party =
+default_section = ZOPE
+line_length = 79
+lines_after_imports = 2
diff --git a/setup.py b/setup.py
index c4cf96f..7441d23 100644
--- a/setup.py
+++ b/setup.py
@@ -19,8 +19,10 @@
"""Setup for zope.tal package
"""
import os
-import sys
-from setuptools import setup, find_packages
+
+from setuptools import find_packages
+from setuptools import setup
+
here = os.path.dirname(__file__)
@@ -30,28 +32,13 @@ def read(*rnames):
return f.read()
-def alltests():
- # use the zope.testrunner machinery to find all the
- # test suites we've put under ourselves
- from zope.testrunner.options import get_options
- from zope.testrunner.find import find_suites
- from unittest import TestSuite
- here = os.path.abspath(os.path.dirname(sys.argv[0]))
- args = sys.argv[:]
- src = os.path.join(here, 'src')
- defaults = ['--test-path', src]
- options = get_options(args, defaults)
- suites = list(find_suites(options))
- return TestSuite(suites)
-
-
TESTS_REQUIRE = [
'zope.testing',
'zope.testrunner',
]
setup(name='zope.tal',
- version='4.6.dev0',
+ version='5.0.dev0',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description='Zope Template Application Language (TAL)',
@@ -67,15 +54,12 @@ setup(name='zope.tal',
'Intended Audience :: Developers',
'License :: OSI Approved :: Zope Public License',
'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
+ 'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Natural Language :: English',
@@ -95,8 +79,6 @@ setup(name='zope.tal',
'repoze.sphinx.autointerface',
],
},
- test_suite="__main__.alltests", # to support "setup.py test"
- tests_require=TESTS_REQUIRE,
install_requires=[
'setuptools',
'zope.i18nmessageid',
diff --git a/src/zope/tal/driver.py b/src/zope/tal/driver.py
index c64c602..2abadea 100644
--- a/src/zope/tal/driver.py
+++ b/src/zope/tal/driver.py
@@ -16,10 +16,9 @@
interprets a file, prints results to stdout.
"""
-from __future__ import print_function
-import os
import optparse
+import os
import sys
# Import local classes
diff --git a/src/zope/tal/dummyengine.py b/src/zope/tal/dummyengine.py
index cfc71ea..237ea84 100644
--- a/src/zope/tal/dummyengine.py
+++ b/src/zope/tal/dummyengine.py
@@ -14,24 +14,16 @@
"""Dummy TAL expression engine so that I can test out the TAL implementation.
"""
import re
+from io import StringIO
-try:
- # Python 2.x
- from StringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
-
-from zope.interface import implementer
-from zope.tal.taldefs import NAME_RE, TALExpressionError, ErrorInfo
-from zope.tal.interfaces import ITALExpressionCompiler, ITALExpressionEngine
from zope.i18nmessageid import Message
+from zope.interface import implementer
-
-try:
- unicode
-except NameError:
- unicode = str # Python 3.x
+from zope.tal.interfaces import ITALExpressionCompiler
+from zope.tal.interfaces import ITALExpressionEngine
+from zope.tal.taldefs import NAME_RE
+from zope.tal.taldefs import ErrorInfo
+from zope.tal.taldefs import TALExpressionError
Default = object()
@@ -44,7 +36,7 @@ class CompilerError(Exception):
@implementer(ITALExpressionCompiler, ITALExpressionEngine)
-class DummyEngine(object):
+class DummyEngine:
position = None
source_file = None
@@ -128,7 +120,7 @@ class DummyEngine(object):
lineno, offset = self.position
else:
lineno, offset = None, None
- return '%s (%s,%s)' % (self.source_file, lineno, offset)
+ return '{} ({},{})'.format(self.source_file, lineno, offset)
raise TALExpressionError(
"unrecognized expression: " +
repr(expression))
@@ -151,7 +143,7 @@ class DummyEngine(object):
def evaluateText(self, expr):
text = self.evaluate(expr)
- if isinstance(text, (str, unicode, Message)):
+ if isinstance(text, (str, Message)):
return text
if text is not None and text is not Default:
text = str(text)
@@ -256,7 +248,7 @@ class DummyEngine(object):
return result.getvalue()
-class Iterator(object):
+class Iterator:
def __init__(self, name, seq, engine):
self.name = name
@@ -273,10 +265,9 @@ class Iterator(object):
self.nextIndex = i + 1
self.engine.setLocal(self.name, item)
return 1
- next = __next__ # Python 2 compatibility
-class DummyTranslationDomain(object):
+class DummyTranslationDomain:
domain = ''
@@ -327,7 +318,7 @@ class DummyTranslationDomain(object):
self.appendMsgid(domain, (msgid, mapping))
def repl(m):
- return unicode(mapping[m.group(m.lastindex).lower()])
+ return str(mapping[m.group(m.lastindex).lower()])
cre = re.compile(r'\$(?:([_A-Za-z][-\w]*)|\{([_A-Za-z][-\w]*)\})')
return cre.sub(repl, text)
diff --git a/src/zope/tal/htmltalparser.py b/src/zope/tal/htmltalparser.py
index 2c65acd..0920072 100644
--- a/src/zope/tal/htmltalparser.py
+++ b/src/zope/tal/htmltalparser.py
@@ -15,40 +15,37 @@
Parse HTML and compile to :class:`~.TALInterpreter` intermediate code, using
a :class:`~.TALGenerator`.
"""
-
-# When Python 3 becomes mainstream please swap the try and except parts.
-try:
- # Python 2.x
- from HTMLParser import HTMLParser, HTMLParseError
-except ImportError:
- # Python 3.x
- from html.parser import HTMLParser
- try:
- from html.parser import HTMLParseError
- except ImportError:
- # Python 3.5 removed it, but we need it as a base class
- # so here's a copy taken from Python 3.4:
- class HTMLParseError(Exception):
- def __init__(self, msg, position=(None, None)):
- Exception.__init__(self)
- assert msg
- self.msg = msg
- self.lineno = position[0]
- self.offset = position[1]
-
- def __str__(self):
- result = self.msg
- if self.lineno is not None:
- result = result + ", at line %d" % self.lineno
- if self.offset is not None:
- result = result + ", column %d" % (self.offset + 1)
- return result
-
-from zope.tal.taldefs import (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS,
- METALError, TALError, I18NError)
+from html.parser import HTMLParser
+
+from zope.tal.taldefs import ZOPE_I18N_NS
+from zope.tal.taldefs import ZOPE_METAL_NS
+from zope.tal.taldefs import ZOPE_TAL_NS
+from zope.tal.taldefs import I18NError
+from zope.tal.taldefs import METALError
+from zope.tal.taldefs import TALError
from zope.tal.talgenerator import TALGenerator
+class HTMLParseError(Exception):
+ # Python 3.5 removed this class, but we need it as a base class
+ # so here's a copy taken from Python 3.4
+
+ def __init__(self, msg, position=(None, None)):
+ Exception.__init__(self)
+ assert msg
+ self.msg = msg
+ self.lineno = position[0]
+ self.offset = position[1]
+
+ def __str__(self):
+ result = self.msg
+ if self.lineno is not None:
+ result = result + ", at line %d" % self.lineno
+ if self.offset is not None:
+ result = result + ", column %d" % (self.offset + 1)
+ return result
+
+
_html_parser_extras = {}
if 'convert_charrefs' in HTMLParser.__init__.__code__.co_names:
_html_parser_extras['convert_charrefs'] = False # pragma: NO COVER py34
@@ -129,7 +126,7 @@ class OpenTagError(NestingError):
def __init__(self, tagstack, tag, position=(None, None)):
self.tag = tag
- msg = 'Tag <%s> is not allowed in <%s>' % (tag, tagstack[-1])
+ msg = 'Tag <{}> is not allowed in <{}>'.format(tag, tagstack[-1])
HTMLParseError.__init__(self, msg, position)
diff --git a/src/zope/tal/interfaces.py b/src/zope/tal/interfaces.py
index bd6286a..311bc76 100644
--- a/src/zope/tal/interfaces.py
+++ b/src/zope/tal/interfaces.py
@@ -22,7 +22,8 @@ most commonly used are :class:`zope.tales.tales.ExpressionEngine`,
:class:`zope.tales.tales.Context`, and
:class:`zope.tales.tales.Iterator`, respectively.
"""
-from zope.interface import Attribute, Interface
+from zope.interface import Attribute
+from zope.interface import Interface
class ITALExpressionCompiler(Interface):
diff --git a/src/zope/tal/runtest.py b/src/zope/tal/runtest.py
index df9d1c7..57feef9 100644
--- a/src/zope/tal/runtest.py
+++ b/src/zope/tal/runtest.py
@@ -17,22 +17,15 @@ compares interpeted test files with expected output files in a sibling
directory.
"""
-from __future__ import print_function
+import copy
+import difflib
import glob
+import optparse
import os
import sys
import traceback
-import difflib
-import copy
-import optparse
-
-try:
- # Python 2.x
- from cStringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
+from io import StringIO
import zope.tal.driver
import zope.tal.tests.utils
@@ -121,7 +114,7 @@ def main(argv=None, out=sys.stdout):
tail)
try:
f = open(outfile)
- except IOError:
+ except OSError:
expected = None
print("(missing file %s)" % outfile, end=' ', file=out)
else:
@@ -135,7 +128,7 @@ def main(argv=None, out=sys.stdout):
if opts.normalize_newlines or "_sa" in arg or arg.endswith('.xml'):
# EOL normalization makes the tests pass:
# - XML files, on Windows, have \r\n line endings. Because
- # expat insists on byte streams on Python 3, we end up with
+ # expat insists on byte streams, we end up with
# those \r\n's going through the entire TAL engine and
# showing up in the actual output. Expected output, on the
# other hand, has just \n's, since we read the file as text.
diff --git a/src/zope/tal/taldefs.py b/src/zope/tal/taldefs.py
index 61d20d1..1a7331d 100644
--- a/src/zope/tal/taldefs.py
+++ b/src/zope/tal/taldefs.py
@@ -14,9 +14,12 @@
"""Common definitions used by TAL and METAL compilation and transformation.
"""
import re
-from zope.tal.interfaces import ITALExpressionErrorInfo
+
from zope.interface import implementer
+from zope.tal.interfaces import ITALExpressionErrorInfo
+
+
#: Version of the specification we implement.
TAL_VERSION = "1.6"
@@ -114,7 +117,7 @@ class I18NError(TALError):
@implementer(ITALExpressionErrorInfo)
-class ErrorInfo(object):
+class ErrorInfo:
"""
Default implementation of
:class:`zope.tal.interfaces.ITALExpressionErrorInfo`.
diff --git a/src/zope/tal/talgenerator.py b/src/zope/tal/talgenerator.py
index a7adf3d..03e0a77 100644
--- a/src/zope/tal/talgenerator.py
+++ b/src/zope/tal/talgenerator.py
@@ -15,30 +15,23 @@
Code generator for :class:`~.TALInterpreter` intermediate code.
"""
import re
-
-try:
- # Python 3.x
- from html import escape
-except ImportError:
- # Python 2.x
- from cgi import escape
+from html import escape
from zope.tal import taldefs
-from zope.tal.taldefs import NAME_RE, TAL_VERSION
-from zope.tal.taldefs import I18NError, METALError, TALError
+from zope.tal.taldefs import NAME_RE
+from zope.tal.taldefs import TAL_VERSION
+from zope.tal.taldefs import I18NError
+from zope.tal.taldefs import METALError
+from zope.tal.taldefs import TALError
from zope.tal.taldefs import parseSubstitution
-from zope.tal.translationcontext import TranslationContext, DEFAULT_DOMAIN
-
-try:
- xrange
-except NameError:
- xrange = range # Python 3.x
+from zope.tal.translationcontext import DEFAULT_DOMAIN
+from zope.tal.translationcontext import TranslationContext
_name_rx = re.compile(NAME_RE)
-class TALGenerator(object):
+class TALGenerator:
"""
Generate intermediate code.
"""
@@ -90,7 +83,7 @@ class TALGenerator(object):
output = []
collect = []
cursor = 0
- for cursor in xrange(len(program) + 1):
+ for cursor in range(len(program) + 1):
try:
item = program[cursor]
except IndexError:
@@ -163,7 +156,7 @@ class TALGenerator(object):
def optimizeStartTag(self, collect, name, attrlist, end):
# return true if the tag can be converted to plain text
if not attrlist:
- collect.append("<%s%s" % (name, end))
+ collect.append("<{}{}".format(name, end))
return 1
opt = 1
new = ["<" + name]
@@ -177,7 +170,7 @@ class TALGenerator(object):
if item[1] is None:
s = item[0]
else:
- s = '%s="%s"' % (item[0], taldefs.attrEscape(item[1]))
+ s = '{}="{}"'.format(item[0], taldefs.attrEscape(item[1]))
attrlist[i] = item[0], s
new.append(" " + s)
# if no non-optimizable attributes were found, convert to plain text
@@ -219,8 +212,11 @@ class TALGenerator(object):
try:
return self.expressionCompiler.compile(expr)
except self.CompilerError as err:
- raise TALError('%s in expression %s' % (err.args[0], repr(expr)),
- self.position)
+ raise TALError(
+ '{} in expression {}'.format(
+ err.args[0],
+ repr(expr)),
+ self.position)
def pushProgram(self):
self.stack.append(self.program)
diff --git a/src/zope/tal/talgettext.py b/src/zope/tal/talgettext.py
index bc90439..08f621b 100644
--- a/src/zope/tal/talgettext.py
+++ b/src/zope/tal/talgettext.py
@@ -12,44 +12,22 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Program to extract internationalization markup from Page Templates.
+"""Extract internationalization markup from Page Templates.
Once you have marked up a Page Template file with i18n: namespace tags, use
-this program to extract GNU gettext .po file entries.
-
-Usage: talgettext.py [options] files
-Options:
- -h / --help
- Print this message and exit.
- -o / --output <file>
- Output the translation .po file to <file>.
- -u / --update <file>
- Update the existing translation <file> with any new translation strings
- found.
+this code to extract GNU gettext .po file entries.
"""
-# XXX this module seems to be unused, it has NameErrors and is not Python 3
-# compatible.
-
-from __future__ import print_function
-
-import sys
-import time
-import getopt
-import traceback
import warnings
+from zope.i18nmessageid import Message
from zope.interface import implementer
-from zope.tal.htmltalparser import HTMLTALParser
-from zope.tal.talinterpreter import TALInterpreter, normalize
+
from zope.tal.dummyengine import DummyEngine
from zope.tal.interfaces import ITALExpressionEngine
-from zope.tal.taldefs import TALExpressionError
-from zope.i18nmessageid import Message
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.talinterpreter import normalize
-PY3 = sys.version_info > (3,)
-if PY3:
- unicode = str
pot_header = '''\
# SOME DESCRIPTIVE TITLE.
@@ -72,18 +50,10 @@ msgstr ""
NLSTR = '"\n"'
-def usage(code, msg=''):
- # Python 2.1 required
- print(__doc__, file=sys.stderr)
- if msg:
- print(msg, file=sys.stderr)
- sys.exit(code)
-
-
class POTALInterpreter(TALInterpreter):
def translate(self, msgid, default=None, i18ndict=None, obj=None):
if default is None:
- default = getattr(msgid, 'default', unicode(msgid))
+ default = getattr(msgid, 'default', str(msgid))
# If no i18n dict exists yet, create one.
if i18ndict is None:
i18ndict = {}
@@ -145,200 +115,16 @@ class POEngine(DummyEngine):
references = '\n'.join([location[0] + ':' + str(location[1])
for location in domain[msgid]])
# Note: a lot of encode calls here are needed so
- # Python 3 does not break.
+ # it does not break.
warnings.warn(
"Warning: msgid '%s' in %s already exists "
"with a different default (bad: %s, should be: %s)\n"
"The references for the existent value are:\n%s\n" %
(msgid.encode('utf-8'),
- self.file.encode('utf-8') + ':'.encode('utf-8')
+ self.file.encode('utf-8') + b':'
+ str(position).encode('utf-8'),
msgid.default.encode('utf-8'),
existing_msgid.default.encode('utf-8'),
references.encode('utf-8')))
domain[msgid].append((self.file, position))
return 'x'
-
-
-class UpdatePOEngine(POEngine):
- """A slightly-less braindead POEngine which supports loading an existing
- .po file first."""
-
- def __init__(self, macros=None, filename=None):
- POEngine.__init__(self, macros)
-
- self._filename = filename
- self._loadFile()
- self.base = self.catalog
- self.catalog = {}
-
- def __add(self, id, s, fuzzy):
- "Add a non-fuzzy translation to the dictionary."
- if not fuzzy and str:
- # check for multi-line values and munge them appropriately
- if '\n' in s:
- lines = s.rstrip().split('\n')
- s = NLSTR.join(lines)
- self.catalog[id] = s
-
- def _loadFile(self):
- # shamelessly cribbed from Python's Tools/i18n/msgfmt.py
- # 25-Mar-2003 Nathan R. Yergler (nathan@zope.org)
- # 14-Apr-2003 Hacked by Barry Warsaw (barry@zope.com)
-
- ID = 1
- STR = 2
-
- try:
- lines = open(self._filename).readlines()
- except IOError as msg:
- print(msg, file=sys.stderr)
- sys.exit(1)
-
- section = None
- fuzzy = False
-
- # Parse the catalog
- lno = 0
- for l in lines:
- lno += True
- # If we get a comment line after a msgstr, this is a new entry
- if l[0] == '#' and section == STR:
- self.__add(msgid, msgstr, fuzzy)
- section = None
- fuzzy = False
- # Record a fuzzy mark
- if l[:2] == '#,' and l.find('fuzzy'):
- fuzzy = True
- # Skip comments
- if l[0] == '#':
- continue
- # Now we are in a msgid section, output previous section
- if l.startswith('msgid'):
- if section == STR:
- self.__add(msgid, msgstr, fuzzy)
- section = ID
- l = l[5:]
- msgid = msgstr = ''
- # Now we are in a msgstr section
- elif l.startswith('msgstr'):
- section = STR
- l = l[6:]
- # Skip empty lines
- if not l.strip():
- continue
- # TODO: Does this always follow Python escape semantics?
- l = eval(l)
- if section == ID:
- msgid += l
- elif section == STR:
- msgstr += '%s\n' % l
- else:
- print('Syntax error on %s:%d' % (infile, lno),
- 'before:', file=sys.stderr)
- print(l, file=sys.stderr)
- sys.exit(1)
- # Add last entry
- if section == STR:
- self.__add(msgid, msgstr, fuzzy)
-
- def evaluate(self, expression):
- try:
- return POEngine.evaluate(self, expression)
- except TALExpressionError:
- pass
-
- def evaluatePathOrVar(self, expr):
- return 'who cares'
-
- def translate(self, msgid, domain=None, mapping=None, default=None,
- position=None):
- if msgid not in self.base:
- POEngine.translate(self, msgid, domain, mapping, default, position)
- return 'x'
-
-
-def main():
- try:
- opts, args = getopt.getopt(
- sys.argv[1:],
- 'ho:u:',
- ['help', 'output=', 'update='])
- except getopt.error as msg:
- usage(1, msg)
-
- outfile = None
- engine = None
- update_mode = False
- for opt, arg in opts:
- if opt in ('-h', '--help'):
- usage(0)
- elif opt in ('-o', '--output'):
- outfile = arg
- elif opt in ('-u', '--update'):
- update_mode = True
- if outfile is None:
- outfile = arg
- engine = UpdatePOEngine(filename=arg)
-
- if not args:
- print('nothing to do')
- return
-
- # We don't care about the rendered output of the .pt file
- class Devnull(object):
- def write(self, s):
- pass
-
- # check if we've already instantiated an engine;
- # if not, use the stupidest one available
- if not engine:
- engine = POEngine()
-
- # process each file specified
- for filename in args:
- try:
- engine.file = filename
- p = HTMLTALParser()
- p.parseFile(filename)
- program, macros = p.getCode()
- POTALInterpreter(program, macros, engine, stream=Devnull(),
- metal=False)()
- except BaseException: # Hee hee, I love bare excepts!
- print('There was an error processing', filename)
- traceback.print_exc()
-
- # Now output the keys in the engine. Write them to a file if --output or
- # --update was specified; otherwise use standard out.
- if (outfile is None):
- outfile = sys.stdout
- else:
- outfile = file(outfile, update_mode and "a" or "w")
-
- catalog = {}
- for domain in engine.catalog:
- catalog.update(engine.catalog[domain])
-
- messages = catalog.copy()
- try:
- messages.update(engine.base)
- except AttributeError:
- pass
- if '' not in messages:
- print(pot_header % {'time': time.ctime(), 'version': __version__},
- file=outfile)
-
- # TODO: You should not sort by msgid, but by filename and position. (SR)
- msgids = sorted(catalog)
- for msgid in msgids:
- positions = engine.catalog[msgid]
- for filename, position in positions:
- outfile.write('#: %s:%s\n' % (filename, position[0]))
-
- outfile.write('msgid "%s"\n' % msgid)
- outfile.write('msgstr ""\n')
- outfile.write('\n')
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/zope/tal/talinterpreter.py b/src/zope/tal/talinterpreter.py
index 7e442d3..1070b94 100644
--- a/src/zope/tal/talinterpreter.py
+++ b/src/zope/tal/talinterpreter.py
@@ -13,28 +13,24 @@
##############################################################################
"""Interpreter for a pre-compiled TAL program.
"""
-import cgi
-import operator
+import html
import sys
from zope.i18nmessageid import Message
-from zope.tal.taldefs import quote, TAL_VERSION, METALError
+
+from zope.tal.taldefs import TAL_VERSION
+from zope.tal.taldefs import METALError
+from zope.tal.taldefs import getProgramMode
+from zope.tal.taldefs import getProgramVersion
from zope.tal.taldefs import isCurrentVersion
-from zope.tal.taldefs import getProgramVersion, getProgramMode
+from zope.tal.taldefs import quote
from zope.tal.talgenerator import TALGenerator
from zope.tal.translationcontext import TranslationContext
-try:
- unicode
-except NameError:
- unicode = str # Python 3.x
-_BLANK = u''
-
# Avoid constructing this tuple over and over
I18nMessageTypes = (Message,)
-
-TypesToTranslate = I18nMessageTypes + (str, unicode)
+TypesToTranslate = I18nMessageTypes + (str, )
BOOLEAN_HTML_ATTRS = frozenset([
# List of Boolean attributes in HTML that should be rendered in
@@ -92,7 +88,7 @@ class AltTALGenerator(TALGenerator):
class MacroStackItem(list):
- # This is a `list` subclass for backward compability.
+ # This is a `list` subclass for backward compatibility.
"""Stack entry for the TALInterpreter.macroStack.
This offers convenience attributes for more readable access.
@@ -100,19 +96,36 @@ class MacroStackItem(list):
"""
__slots__ = ()
- # These would be nicer using @syntax, but that would require
- # Python 2.4.x; this will do for now.
+ @property
+ def macroName(self):
+ return self[0]
+
+ @property
+ def slots(self):
+ return self[1]
+
+ @property
+ def definingName(self):
+ return self[2]
+
+ @property
+ def extending(self):
+ return self[3]
+
+ @property
+ def entering(self):
+ return self[4]
+
+ @entering.setter
+ def entering(self, value):
+ self[4] = value
- macroName = property(lambda self: self[0])
- slots = property(lambda self: self[1])
- definingName = property(lambda self: self[2])
- extending = property(lambda self: self[3])
- entering = property(lambda self: self[4],
- lambda self, value: operator.setitem(self, 4, value))
- i18nContext = property(lambda self: self[5])
+ @property
+ def i18nContext(self):
+ return self[5]
-class TALInterpreter(object):
+class TALInterpreter:
"""TAL interpreter.
Some notes on source annotations. They are HTML/XML comments added to the
@@ -219,7 +232,7 @@ class TALInterpreter(object):
self.sourceAnnotations = sourceAnnotations
def StringIO(self):
- # Third-party products wishing to provide a full Unicode-aware
+ # Third-party products wishing to provide a full text-aware
# StringIO can do so by monkey-patching this method.
return FasterStringIO()
@@ -313,9 +326,9 @@ class TALInterpreter(object):
if lineno is None:
location = self.sourceFile
else:
- location = '%s (line %s)' % (self.sourceFile, lineno)
+ location = '{} (line {})'.format(self.sourceFile, lineno)
sep = '=' * 78
- return '<!--\n%s\n%s\n%s\n-->' % (sep, location, sep)
+ return '<!--\n{}\n{}\n{}\n-->'.format(sep, location, sep)
def stream_write(self, s,
len=len):
@@ -335,8 +348,8 @@ class TALInterpreter(object):
try:
if self.debug:
for (opcode, args) in program:
- s = "%sdo_%s(%s)\n" % (" " * self.level, opcode,
- repr(args))
+ s = "{}do_{}({})\n".format(" " * self.level, opcode,
+ repr(args))
if len(s) > 80:
s = s[:76] + "...\n"
sys.stderr.write(s)
@@ -451,9 +464,10 @@ class TALInterpreter(object):
defName = macs[0].definingName
res = []
if defName:
- res.append('%sdefine-macro=%s' % (prefix, quote(defName)))
+ res.append(
+ '{}define-macro={}'.format(prefix, quote(defName)))
if useName:
- res.append('%suse-macro=%s' % (prefix, quote(useName)))
+ res.append('{}use-macro={}'.format(prefix, quote(useName)))
return res
elif suffix == "define-slot":
name = prefix + "fill-slot"
@@ -465,7 +479,7 @@ class TALInterpreter(object):
if value is None:
value = name
else:
- value = "%s=%s" % (name, quote(value))
+ value = "{}={}".format(name, quote(value))
return [value]
def attrAction_tal(self, item):
@@ -502,7 +516,7 @@ class TALInterpreter(object):
value = translated
if value is None:
value = name
- return ["%s=%s" % (name, quote(value))]
+ return ["{}={}".format(name, quote(value))]
else:
return ()
bytecode_handlers["<attrAction>"] = attrAction
@@ -682,7 +696,7 @@ class TALInterpreter(object):
value = self.translate(value)
if not structure:
- value = cgi.escape(unicode(value))
+ value = html.escape(str(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
@@ -731,7 +745,7 @@ class TALInterpreter(object):
if len(stuff) > 2:
obj = self.engine.evaluate(stuff[2])
xlated_msgid = self.translate(msgid, default, i18ndict, obj)
- # TODO: I can't decide whether we want to cgi escape the translated
+ # TODO: I can't decide whether we want to html.escape the translated
# string or not. OTOH 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
@@ -756,7 +770,7 @@ class TALInterpreter(object):
if isinstance(structure, I18nMessageTypes):
text = self.translate(structure)
else:
- text = unicode(structure)
+ text = str(structure)
if not (repldict or self.strictinsert):
# Take a shortcut, no error checking
self.stream_write(text)
@@ -775,7 +789,7 @@ class TALInterpreter(object):
self.interpret(block)
else:
if not isinstance(structure, TypesToTranslate):
- structure = unicode(structure)
+ structure = str(structure)
text = self.translate(structure)
if not (repldict or self.strictinsert):
# Take a shortcut, no error checking
@@ -835,7 +849,7 @@ class TALInterpreter(object):
def translate(self, msgid, default=None, i18ndict=None,
obj=None, domain=None):
if default is None:
- default = getattr(msgid, 'default', unicode(msgid))
+ default = getattr(msgid, 'default', str(msgid))
if i18ndict is None:
i18ndict = {}
if domain is None:
@@ -997,7 +1011,7 @@ class TALInterpreter(object):
error = engine.createErrorInfo(exc, self.position)
finally:
# Avoid traceback reference cycle due to the __traceback__
- # attribute on Python 3.
+ # attribute.
del exc
engine.setLocal('error', error)
try:
@@ -1025,7 +1039,7 @@ class TALInterpreter(object):
class FasterStringIO(list):
- # Unicode-aware append-only version of StringIO.
+ # text-aware append-only version of StringIO.
write = list.append
def __init__(self, value=None):
@@ -1034,7 +1048,7 @@ class FasterStringIO(list):
self.append(value)
def getvalue(self):
- return _BLANK.join(self)
+ return ''.join(self)
def _write_ValueError(s):
diff --git a/src/zope/tal/talparser.py b/src/zope/tal/talparser.py
index d7d1964..d11fb86 100644
--- a/src/zope/tal/talparser.py
+++ b/src/zope/tal/talparser.py
@@ -15,7 +15,10 @@
Parse XML and compile to :class:`~.TALInterpreter` intermediate code,
using a :class:`~.TALGenerator`.
"""
-from zope.tal.taldefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS
+from zope.tal.taldefs import XML_NS
+from zope.tal.taldefs import ZOPE_I18N_NS
+from zope.tal.taldefs import ZOPE_METAL_NS
+from zope.tal.taldefs import ZOPE_TAL_NS
from zope.tal.talgenerator import TALGenerator
from zope.tal.xmlparser import XMLParser
@@ -125,7 +128,7 @@ class TALParser(XMLParser):
prefix = self.nsDict[uri]
prefixed = name
if prefix:
- prefixed = "%s:%s" % (prefix, name)
+ prefixed = "{}:{}".format(prefix, name)
ns = self._namespaces.get(uri, "x")
return (prefixed, name, ns)
return (name, name, None)
@@ -146,8 +149,8 @@ def test():
file = sys.argv[1]
p.parseFile(file)
program, macros = p.getCode()
- from zope.tal.talinterpreter import TALInterpreter
from zope.tal.dummyengine import DummyEngine
+ from zope.tal.talinterpreter import TALInterpreter
engine = DummyEngine(macros)
TALInterpreter(program, macros, engine, sys.stdout, wrap=0)()
diff --git a/src/zope/tal/tests/markbench.py b/src/zope/tal/tests/markbench.py
index 6dafa74..f3ae0ad 100644
--- a/src/zope/tal/tests/markbench.py
+++ b/src/zope/tal/tests/markbench.py
@@ -15,18 +15,21 @@
"""Run benchmarks of TAL vs. DTML
"""
-from __future__ import print_function
-from zope.tal.dummyengine import DummyEngine
-from zope.tal.talinterpreter import TALInterpreter
-from zope.tal.htmltalparser import HTMLTALParser
-from cStringIO import StringIO
+
import errno
-import time
-import sys
import getopt
import os
-
+import sys
+import time
import warnings
+
+from cStringIO import StringIO
+
+from zope.tal.dummyengine import DummyEngine
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talinterpreter import TALInterpreter
+
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
os.environ['NO_SECURITY'] = 'true'
@@ -191,6 +194,6 @@ if __name__ == "__main__":
p.sort_stats('time', 'calls')
try:
p.print_stats(20)
- except IOError as e:
+ except OSError as e:
if e.errno != errno.EPIPE:
raise
diff --git a/src/zope/tal/tests/run.py b/src/zope/tal/tests/run.py
index 01a52c2..6e64e49 100644
--- a/src/zope/tal/tests/run.py
+++ b/src/zope/tal/tests/run.py
@@ -17,11 +17,12 @@
import sys
import unittest
-from zope.tal.tests import utils
-from zope.tal.tests import test_htmltalparser
-from zope.tal.tests import test_talinterpreter
from zope.tal.tests import test_files
+from zope.tal.tests import test_htmltalparser
from zope.tal.tests import test_sourcepos
+from zope.tal.tests import test_talinterpreter
+from zope.tal.tests import utils
+
# TODO this code isn't picked up by the Zope 3 test framework..
diff --git a/src/zope/tal/tests/test_files.py b/src/zope/tal/tests/test_files.py
index 9ecefd1..4393eb1 100644
--- a/src/zope/tal/tests/test_files.py
+++ b/src/zope/tal/tests/test_files.py
@@ -18,18 +18,12 @@ import glob
import os
import sys
import unittest
-
-try:
- # Python 2.x
- from cStringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
+from io import StringIO
import zope.tal.runtest
-
from zope.tal.tests import utils
+
HERE = os.path.abspath(os.path.dirname(__file__))
PARENTDIR = os.path.dirname(HERE)
PREFIX = os.path.join(HERE, "input", "test*.")
diff --git a/src/zope/tal/tests/test_htmltalparser.py b/src/zope/tal/tests/test_htmltalparser.py
index ba2770b..db5bd0c 100644
--- a/src/zope/tal/tests/test_htmltalparser.py
+++ b/src/zope/tal/tests/test_htmltalparser.py
@@ -16,7 +16,8 @@
import pprint
import unittest
-from zope.tal import htmltalparser, taldefs
+from zope.tal import htmltalparser
+from zope.tal import taldefs
class TestCaseBase(unittest.TestCase):
@@ -1057,11 +1058,3 @@ translated string</span>
('endScope', ()),
('rawtextColumn', ('</p>\n', 0))
])
-
-
-def test_suite():
- return unittest.TestSuite((
- unittest.makeSuite(HTMLTALParserTestCases),
- unittest.makeSuite(METALGeneratorTestCases),
- unittest.makeSuite(TALGeneratorTestCases),
- ))
diff --git a/src/zope/tal/tests/test_sourcepos.py b/src/zope/tal/tests/test_sourcepos.py
index bcbd909..f9a84d5 100644
--- a/src/zope/tal/tests/test_sourcepos.py
+++ b/src/zope/tal/tests/test_sourcepos.py
@@ -14,18 +14,12 @@
"""Tests for TALInterpreter.
"""
import unittest
+from io import StringIO
-try:
- # Python 2.x
- from StringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
-
+from zope.tal.dummyengine import DummyEngine
from zope.tal.htmltalparser import HTMLTALParser
-from zope.tal.talinterpreter import TALInterpreter
from zope.tal.talgenerator import TALGenerator
-from zope.tal.dummyengine import DummyEngine
+from zope.tal.talinterpreter import TALInterpreter
page1 = '''<html metal:use-macro="main"><body>
@@ -89,4 +83,4 @@ class SourcePosTestCase(unittest.TestCase):
def test_suite():
- return unittest.makeSuite(SourcePosTestCase)
+ return unittest.defaultTestLoader.loadTestsFromTestCase(SourcePosTestCase)
diff --git a/src/zope/tal/tests/test_talgettext.py b/src/zope/tal/tests/test_talgettext.py
index 1559e83..fedead8 100644
--- a/src/zope/tal/tests/test_talgettext.py
+++ b/src/zope/tal/tests/test_talgettext.py
@@ -14,22 +14,15 @@
"""Tests for the talgettext utility.
"""
-from __future__ import print_function
import tempfile
import unittest
import warnings
-
-try:
- # Python 2.x
- from StringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
+from io import StringIO
from zope.tal.htmltalparser import HTMLTALParser
-from zope.tal.talgettext import POTALInterpreter
from zope.tal.talgettext import POEngine
+from zope.tal.talgettext import POTALInterpreter
class test_POEngine(unittest.TestCase):
@@ -75,10 +68,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='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='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:
diff --git a/src/zope/tal/tests/test_talinterpreter.py b/src/zope/tal/tests/test_talinterpreter.py
index 26c35cf..6252713 100644
--- a/src/zope/tal/tests/test_talinterpreter.py
+++ b/src/zope/tal/tests/test_talinterpreter.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
@@ -15,27 +14,21 @@
"""Tests for TALInterpreter.
"""
import os
-
-import sys
import unittest
+from io import StringIO
-try:
- # Python 2.x
- from StringIO import StringIO
-except ImportError:
- # Python 3.x
- from io import StringIO
-
+from zope.i18nmessageid import Message
-from zope.tal.taldefs import METALError, I18NError, TAL_VERSION
-from zope.tal.taldefs import TALExpressionError
-from zope.tal.htmltalparser import HTMLTALParser
-from zope.tal.talparser import TALParser
-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.i18nmessageid import Message
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.taldefs import TAL_VERSION
+from zope.tal.taldefs import I18NError
+from zope.tal.taldefs import METALError
+from zope.tal.taldefs import TALExpressionError
+from zope.tal.talgenerator import TALGenerator
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.talparser import TALParser
class TestCaseBase(unittest.TestCase):
@@ -466,8 +459,7 @@ class I18NCornerTestCaseMessage(TestCaseBase):
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
- msgids = self.engine.translationDomain.getMsgids('default')
- msgids.sort()
+ msgids = sorted(self.engine.translationDomain.getMsgids('default'))
self.assertEqual(1, len(msgids))
self.assertEqual('This is text <b> for</b> barvalue.', msgids[0][0])
self.assertEqual(
@@ -490,7 +482,7 @@ class I18NCornerTestCaseMessage(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>'),
+ ('<div>THIS IS TEXT FOR <pre> \tBAR\n </pre>.</div>'),
result.getvalue())
def test_for_handling_unicode_vars(self):
@@ -499,7 +491,7 @@ class I18NCornerTestCaseMessage(TestCaseBase):
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>"))
+ self._check(program, ("<div>FOO \u00C0</div>"))
class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
@@ -708,13 +700,13 @@ class OutputPresentationTestCase(TestCaseBase):
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>'
+ INPUT = """<p tal:content="python:u'déjà-vu'">para</p>"""
+ EXPECTED = '<p>d\xe9j\xe0-vu</p>'
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'
+ INPUT = """<p tal:replace="structure python:u'déjà-vu'">para</p>"""
+ EXPECTED = 'd\xe9j\xe0-vu'
self.compare(INPUT, EXPECTED)
def test_i18n_replace_number(self):
@@ -722,30 +714,16 @@ class OutputPresentationTestCase(TestCaseBase):
<p i18n:translate="foo ${bar}">
<span tal:replace="python:123" i18n:name="bar">para</span>
</p>"""
- EXPECTED = (u"""
+ EXPECTED = ("""
<p>FOO 123</p>""")
self.compare(INPUT, EXPECTED)
def test_entities(self):
- 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 &; <>" />')
- 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 &; <>" />')
- 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 &; <>" />')
- EXPECTED = ('<img alt="&a; \n '
- '&amp;a - &amp;; &lt;&gt;" />')
+ # Python parses "&#1" as "" because '1' is an "invalid codepoint".
+ INPUT = ('<img tal:define="foo nothing" '
+ '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):
diff --git a/src/zope/tal/tests/test_talparser.py b/src/zope/tal/tests/test_talparser.py
index 58c8609..35c611b 100644
--- a/src/zope/tal/tests/test_talparser.py
+++ b/src/zope/tal/tests/test_talparser.py
@@ -34,4 +34,4 @@ class TALParserTestCase(unittest.TestCase):
def test_suite():
- return unittest.makeSuite(TALParserTestCase)
+ return unittest.defaultTestLoader.loadTestsFromTestCase(TALParserTestCase)
diff --git a/src/zope/tal/tests/test_xmlparser.py b/src/zope/tal/tests/test_xmlparser.py
index 437d68f..c02d652 100644
--- a/src/zope/tal/tests/test_xmlparser.py
+++ b/src/zope/tal/tests/test_xmlparser.py
@@ -81,7 +81,7 @@ class EventCollectorExtra(EventCollector):
self.append(("starttag_text", self.get_starttag_text()))
-class SegmentedFile(object):
+class SegmentedFile:
def __init__(self, parts):
self.parts = list(parts)
@@ -253,10 +253,10 @@ 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', 'p', []),
+ ('data', '\xe4\xf6\xfc\xdf'),
+ ('endtag', 'p')]
+ self._run_check('<p>\xe4\xf6\xfc\xdf</p>', output)
# Support for the Zope regression test framework:
diff --git a/src/zope/tal/tests/utils.py b/src/zope/tal/tests/utils.py
index b8d384d..36cf2a8 100644
--- a/src/zope/tal/tests/utils.py
+++ b/src/zope/tal/tests/utils.py
@@ -13,9 +13,10 @@
##############################################################################
"""Helper functions for the test suite.
"""
-import unittest
import os
import sys
+import unittest
+
mydir = os.path.abspath(os.path.dirname(__file__))
codedir = os.path.dirname(os.path.dirname(os.path.dirname(mydir)))
diff --git a/src/zope/tal/timer.py b/src/zope/tal/timer.py
index f64654f..1395515 100644
--- a/src/zope/tal/timer.py
+++ b/src/zope/tal/timer.py
@@ -20,7 +20,8 @@ import time
from cStringIO import StringIO
-from zope.tal.driver import compilefile, interpretit
+from zope.tal.driver import compilefile
+from zope.tal.driver import interpretit
def main():
diff --git a/src/zope/tal/translationcontext.py b/src/zope/tal/translationcontext.py
index e515628..7d12b8f 100644
--- a/src/zope/tal/translationcontext.py
+++ b/src/zope/tal/translationcontext.py
@@ -19,7 +19,7 @@ needed to perform translation of a marked string from a page template.
DEFAULT_DOMAIN = "default"
-class TranslationContext(object):
+class TranslationContext:
"""Information about the I18N settings of a TAL processor."""
def __init__(self, parent=None, domain=None, target=None, source=None):
diff --git a/src/zope/tal/xmlparser.py b/src/zope/tal/xmlparser.py
index 7ebee73..f4891f9 100644
--- a/src/zope/tal/xmlparser.py
+++ b/src/zope/tal/xmlparser.py
@@ -16,22 +16,10 @@
This creates a parser with namespace processing enabled.
"""
import logging
+from urllib.request import urlopen
-try:
- # Python 2.x
- from urllib import urlopen
-except ImportError:
- # Python 3.x
- from urllib.request import urlopen
-
-try:
- unicode
-except NameError:
- unicode = str # Python 3.x
-
-
-class XMLParser(object):
+class XMLParser:
"""
Parse XML using :mod:`xml.parsers.expat`.
"""
@@ -91,9 +79,9 @@ class XMLParser(object):
def parseString(self, s):
"""Parse the given string."""
- if isinstance(s, unicode):
- # Expat cannot deal with unicode strings, only with
- # encoded ones. Also, its range of encodings is rather
+ if isinstance(s, str):
+ # Expat cannot deal with str, only with
+ # bytes. Also, its range of encodings is rather
# limited, UTF-8 is the safest bet here.
s = s.encode('utf-8')
self.parser.Parse(s, 1)
diff --git a/tox.ini b/tox.ini
index 4ef11c7..442ee82 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,14 +4,11 @@
minversion = 3.18
envlist =
lint
- py27
- py35
- py36
py37
py38
py39
py310
- pypy
+ py311
pypy3
docs
coverage
@@ -27,15 +24,26 @@ extras =
[testenv:lint]
basepython = python3
skip_install = true
+commands =
+ isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py
+ flake8 src setup.py
+ check-manifest
+ check-python-versions
deps =
- flake8
check-manifest
check-python-versions >= 0.19.1
wheel
+ flake8
+ isort
+
+[testenv:isort-apply]
+basepython = python3
+skip_install = true
+commands_pre =
+deps =
+ isort
commands =
- flake8 src setup.py
- check-manifest
- check-python-versions
+ isort {toxinidir}/src {toxinidir}/setup.py []
[testenv:docs]
basepython = python3
@@ -52,16 +60,14 @@ allowlist_externals =
mkdir
deps =
coverage
- coverage-python-version
commands =
mkdir -p {toxinidir}/parts/htmlcov
coverage run -m zope.testrunner --test-path=src {posargs:-vc}
- coverage html
- coverage report -m --fail-under=81
+ coverage html --ignore-errors
+ coverage report --ignore-errors --show-missing --fail-under=84
[coverage:run]
branch = True
-plugins = coverage_python_version
source = zope.tal
[coverage:report]