From d4282a8eaba4ba843b07f088e9443340833cdff6 Mon Sep 17 00:00:00 2001 From: Michael Howitz Date: Fri, 6 Jan 2023 06:54:21 +0100 Subject: Config with pure python template (#19) * Drop support for Python 2.7, 3.5, 3.6. * Add support for Python 3.11. --- .github/workflows/tests.yml | 18 ++- .gitignore | 1 + .meta.toml | 14 +- CHANGES.rst | 11 +- setup.cfg | 17 ++- setup.py | 30 +--- src/zope/tal/driver.py | 3 +- src/zope/tal/dummyengine.py | 35 ++--- src/zope/tal/htmltalparser.py | 61 ++++---- src/zope/tal/interfaces.py | 3 +- src/zope/tal/runtest.py | 19 +-- src/zope/tal/taldefs.py | 7 +- src/zope/tal/talgenerator.py | 38 +++-- src/zope/tal/talgettext.py | 232 ++---------------------------- src/zope/tal/talinterpreter.py | 94 ++++++------ src/zope/tal/talparser.py | 9 +- src/zope/tal/tests/markbench.py | 21 +-- src/zope/tal/tests/run.py | 7 +- src/zope/tal/tests/test_files.py | 10 +- src/zope/tal/tests/test_htmltalparser.py | 11 +- src/zope/tal/tests/test_sourcepos.py | 14 +- src/zope/tal/tests/test_talgettext.py | 15 +- src/zope/tal/tests/test_talinterpreter.py | 68 +++------ src/zope/tal/tests/test_talparser.py | 2 +- src/zope/tal/tests/test_xmlparser.py | 10 +- src/zope/tal/tests/utils.py | 3 +- src/zope/tal/timer.py | 3 +- src/zope/tal/translationcontext.py | 2 +- src/zope/tal/xmlparser.py | 22 +-- tox.ini | 30 ++-- 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 `_) +- 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 - Output the translation .po file to . - -u / --update - Update the existing translation 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 '' % (sep, location, sep) + return ''.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 @@ -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 ('endScope', ()), ('rawtextColumn', ('

\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 = ''' @@ -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 for 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': '
 \tBAR\n 
'}, msgids[1][1]) self.assertEqual( - (u'
THIS IS TEXT FOR
 \tBAR\n 
.
'), + ('
THIS IS TEXT FOR
 \tBAR\n 
.
'), result.getvalue()) def test_for_handling_unicode_vars(self): @@ -499,7 +491,7 @@ class I18NCornerTestCaseMessage(TestCaseBase): program, _macros = self._compile( r'''
''' r'''Foo
''') - self._check(program, (u"
FOO \u00C0
")) + self._check(program, ("
FOO \u00C0
")) class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage): @@ -708,13 +700,13 @@ class OutputPresentationTestCase(TestCaseBase): self.compare(INPUT, EXPECTED) def test_unicode_content(self): - INPUT = u"""

para

""" - EXPECTED = u'

d\xe9j\xe0-vu

' + INPUT = """

para

""" + EXPECTED = '

d\xe9j\xe0-vu

' self.compare(INPUT, EXPECTED) def test_unicode_structure(self): - INPUT = u"""

para

""" - EXPECTED = u'd\xe9j\xe0-vu' + INPUT = """

para

""" + EXPECTED = 'd\xe9j\xe0-vu' self.compare(INPUT, EXPECTED) def test_i18n_replace_number(self): @@ -722,30 +714,16 @@ class OutputPresentationTestCase(TestCaseBase):

para

""" - EXPECTED = (u""" + EXPECTED = ("""

FOO 123

""") self.compare(INPUT, EXPECTED) def test_entities(self): - if sys.version_info[:2] <= (3, 2): - # HTMLParser.HTMLParser in Python 2.x--3.2 parses "-" as "-" - INPUT = ('') - EXPECTED = ('&a; \x01 \n '
-                        '&a &#45 &; <>') - elif sys.version_info < (3, 4): - # html.parser.HTMLParser in Python 3.3 parses "-" as "-" - INPUT = ('') - EXPECTED = ('&a; \x01 \n '
-                        '&a - &; <>') - else: - # html.parser.HTMLParser in Python 3.4 parses "" as "" - # because '1' is an "invalid codepoint". - INPUT = ('') - EXPECTED = ('&a;  \n '
-                        '&a - &; <>') + # Python parses "" as "" because '1' is an "invalid codepoint". + INPUT = ('') + EXPECTED = ('&a;  \n '
+                    '&a - &; <>') 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("") def test_unicode_string(self): - output = [('starttag', u'p', []), - ('data', u'\xe4\xf6\xfc\xdf'), - ('endtag', u'p')] - self._run_check(u'

\xe4\xf6\xfc\xdf

', output) + output = [('starttag', 'p', []), + ('data', '\xe4\xf6\xfc\xdf'), + ('endtag', 'p')] + self._run_check('

\xe4\xf6\xfc\xdf

', 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] -- cgit v1.2.1