Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-Chameleon for openSUSE:Factory checked in at 2024-04-21 20:27:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Chameleon (Old) and /work/SRC/openSUSE:Factory/.python-Chameleon.new.26366 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Chameleon" Sun Apr 21 20:27:04 2024 rev:19 rq:1169357 version:4.5.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Chameleon/python-Chameleon.changes 2024-03-18 16:46:17.214783135 +0100 +++ /work/SRC/openSUSE:Factory/.python-Chameleon.new.26366/python-Chameleon.changes 2024-04-21 20:28:38.368912058 +0200 @@ -1,0 +2,12 @@ +Sat Apr 20 13:30:44 UTC 2024 - Dirk Müller <dmuel...@suse.com> + +- update to 4.5.4: + * Fix an issue where $-sign interpolation escaping would not + work correctly when more than two such symbols appeared next + to each other. + * Minor optimization when rendering translations with a static + message id (don't need to test if it's non-empty). + * Fix a bug where a macro could not be used correctly to render + a translation name. + +------------------------------------------------------------------- Old: ---- Chameleon-4.5.2.tar.gz New: ---- Chameleon-4.5.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Chameleon.spec ++++++ --- /var/tmp/diff_new_pack.dNvNn5/_old 2024-04-21 20:28:39.012935700 +0200 +++ /var/tmp/diff_new_pack.dNvNn5/_new 2024-04-21 20:28:39.016935847 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-Chameleon -Version: 4.5.2 +Version: 4.5.4 Release: 0 Summary: Fast HTML/XML Template Compiler License: BSD-3-Clause AND BSD-4-Clause AND Python-2.0 AND ZPL-2.1 ++++++ Chameleon-4.5.2.tar.gz -> Chameleon-4.5.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/.github/workflows/main.yml new/chameleon-4.5.4/.github/workflows/main.yml --- old/chameleon-4.5.2/.github/workflows/main.yml 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/.github/workflows/main.yml 2024-04-08 09:10:10.000000000 +0200 @@ -41,8 +41,9 @@ - ["pypy-3.9", "pypy3"] - ["3.9", "docs"] - ["3.9", "coverage"] - - ["3.12", "mypy"] - + - ["3.12", "mypy"] + - ["3.11", "z3c.macro"] + - ["3.11", "z3c.pt"] 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] }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/CHANGES.rst new/chameleon-4.5.4/CHANGES.rst --- old/chameleon-4.5.2/CHANGES.rst 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/CHANGES.rst 2024-04-08 09:10:10.000000000 +0200 @@ -1,6 +1,24 @@ Changes ======= +4.5.4 (2024-04-08) +------------------ + +- Fix an issue where $-sign interpolation escaping would not work + correctly when more than two such symbols appeared next to each + other. + (`#422 <https://github.com/malthe/chameleon/issues/422>`_) + +4.5.3 (2024-04-05) +------------------ + +- Minor optimization when rendering translations with a static + message id (don't need to test if it's non-empty). + +- Fix a bug where a macro could not be used correctly to render a + translation name. + (`#419 <https://github.com/malthe/chameleon/issues/419>`_) + 4.5.2 (2024-01-29) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/README.rst new/chameleon-4.5.4/README.rst --- old/chameleon-4.5.2/README.rst 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/README.rst 2024-04-08 09:10:10.000000000 +0200 @@ -1,5 +1,10 @@ -Overview -======== +Chameleon +========= + +.. image:: https://img.shields.io/pypi/pyversions/Chameleon + :alt: PyPI - Python Version +.. image:: https://img.shields.io/github/actions/workflow/status/malthe/chameleon/main.yml + :alt: GitHub Actions Workflow Status Chameleon is an HTML/XML template engine for `Python <http://www.python.org>`_. It uses the *page templates* language. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/pyproject.toml new/chameleon-4.5.4/pyproject.toml --- old/chameleon-4.5.2/pyproject.toml 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/pyproject.toml 2024-04-08 09:10:10.000000000 +0200 @@ -7,6 +7,10 @@ # we may want to include tests eventually exclude = "/tests/" follow_imports = "silent" +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true +warn_return_any = true [[tool.mypy.overrides]] # strict config for fully typed modules and public API @@ -23,7 +27,9 @@ disallow_untyped_defs = true disallow_incomplete_defs = true disallow_untyped_decorators = true -warn_unused_ignores = true +no_implicit_reexport = true +strict_equality = true +extra_checks = true [[tool.mypy.overrides]] module = ["zope.*"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/setup.cfg new/chameleon-4.5.4/setup.cfg --- old/chameleon-4.5.2/setup.cfg 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/setup.cfg 2024-04-08 09:10:10.000000000 +0200 @@ -6,9 +6,6 @@ [flake8] doctests = 1 extend-select = TC1 -# F401 imported but unused -per-file-ignores = - src/chameleon/__init__.py: F401 [check-manifest] ignore = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/setup.py new/chameleon-4.5.4/setup.py --- old/chameleon-4.5.2/setup.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/setup.py 2024-04-08 09:10:10.000000000 +0200 @@ -1,4 +1,4 @@ -__version__ = '4.5.2' +__version__ = '4.5.4' import os diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/__init__.py new/chameleon-4.5.4/src/chameleon/__init__.py --- old/chameleon-4.5.2/src/chameleon/__init__.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/__init__.py 2024-04-08 09:10:10.000000000 +0200 @@ -4,3 +4,13 @@ from chameleon.zpt.template import PageTemplateFile from chameleon.zpt.template import PageTextTemplate from chameleon.zpt.template import PageTextTemplateFile + + +__all__ = ( + 'TemplateError', + 'PageTemplateLoader', + 'PageTemplate', + 'PageTemplateFile', + 'PageTextTemplate', + 'PageTextTemplateFile', +) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/astutil.py new/chameleon-4.5.4/src/chameleon/astutil.py --- old/chameleon-4.5.2/src/chameleon/astutil.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/astutil.py 2024-04-08 09:10:10.000000000 +0200 @@ -5,35 +5,44 @@ import ast from copy import deepcopy from typing import TYPE_CHECKING +from typing import Any from typing import ClassVar if TYPE_CHECKING: from collections.abc import Callable - from typing import Union - _NodeTransform = Callable[[ast.AST], Union[ast.AST, None]] + from collections.abc import Hashable + from typing import Optional + + from chameleon.tokenize import Token + + _NodeTransform = Callable[[ast.AST], Optional[ast.AST]] __docformat__ = 'restructuredtext en' -def parse(source, mode='eval'): +def parse(source, mode: str = 'eval') -> ast.AST: return compile(source, '', mode, ast.PyCF_ONLY_AST) -def load(name): +def load(name: str) -> ast.Name: return ast.Name(id=name, ctx=ast.Load()) -def store(name): +def store(name: str) -> ast.Name: return ast.Name(id=name, ctx=ast.Store()) -def param(name): +def param(name: str) -> ast.Name: return ast.Name(id=name, ctx=ast.Param()) -def subscript(name, value, ctx): +def subscript( + name: str, + value: ast.expr, + ctx: ast.expr_context +) -> ast.Subscript: return ast.Subscript( value=value, slice=ast.Index(value=ast.Str(s=name)), @@ -47,7 +56,7 @@ _fields: ClassVar[tuple[str, ...]] = () - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: assert isinstance(self._fields, tuple) self.__dict__.update(kwargs) for name, value in zip(self._fields, args): @@ -77,6 +86,7 @@ _fields = "id", "ctx" + id: str ctx = ast.Load() @@ -85,24 +95,32 @@ _fields = "value", + # Apart from a few builtins this should be type[Any] + value: type[Any] | Hashable + class Static(Node): """Represents a static value.""" _fields = "value", "name" - name = None + value: ast.expr + name: str | None = None class Comment(Node): _fields = "text", + text: str + class TokenRef(Node): """Represents a source-code token reference.""" _fields = "token", + token: Token + class NodeTransformerBase(ast.NodeTransformer): def __init__(self, transform: _NodeTransform): @@ -120,34 +138,34 @@ self.scopes: list[set[str]] = [set()] super().__init__(transform) - def __call__(self, node) -> ast.AST: + def __call__(self, node: ast.AST) -> ast.AST: clone = deepcopy(node) - return self.visit(clone) + return self.visit(clone) # type: ignore[no-any-return] - def visit_arg(self, node) -> ast.AST: + def visit_arg(self, node: ast.arg) -> ast.AST: scope = self.scopes[-1] scope.add(node.arg) return node - def visit_Name(self, node) -> ast.AST: + def visit_Name(self, node: ast.Name) -> ast.AST: scope = self.scopes[-1] if isinstance(node.ctx, ast.Param): scope.add(node.id) return node if node.id not in scope: - node = self.apply_transform(node) + return self.apply_transform(node) return node - def visit_FunctionDef(self, node) -> ast.AST: + def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.AST: self.scopes[-1].add(node.name) return super().generic_visit(node) - def visit_alias(self, node) -> ast.AST: + def visit_alias(self, node: ast.alias) -> ast.AST: name = node.asname if node.asname is not None else node.name self.scopes[-1].add(name) return super().generic_visit(node) - def visit_Lambda(self, node) -> ast.AST: + def visit_Lambda(self, node: ast.Lambda) -> ast.AST: self.scopes.append(set()) try: return super().generic_visit(node) @@ -156,6 +174,6 @@ class ItemLookupOnAttributeErrorVisitor(NodeTransformerBase): - def visit_Attribute(self, node) -> ast.AST: - node = self.apply_transform(node) - return self.generic_visit(node) + def visit_Attribute(self, node: ast.Attribute) -> ast.AST: + transformed = self.apply_transform(node) + return self.generic_visit(transformed) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/codegen.py new/chameleon-4.5.4/src/chameleon/codegen.py --- old/chameleon-4.5.2/src/chameleon/codegen.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/codegen.py 2024-04-08 09:10:10.000000000 +0200 @@ -15,6 +15,8 @@ from ast import NodeTransformer from ast import alias from ast import unparse +from typing import TYPE_CHECKING +from typing import Any from chameleon.astutil import Builtin from chameleon.astutil import Symbol @@ -24,7 +26,15 @@ from chameleon.exc import CompilationError -reverse_builtin_map = {} +if TYPE_CHECKING: + import ast + from collections.abc import Hashable + + from chameleon.astutil import Comment + from chameleon.astutil import Static + + +reverse_builtin_map: dict[type[Any] | Hashable, str] = {} for name, value in builtins.__dict__.items(): try: hash(value) @@ -60,7 +70,7 @@ lineno=None, ) - def visit_Name(self, node) -> AST: + def visit_Name(self, node: ast.Name) -> AST: value = symbols.get(node.id, self) if value is self: if node.id == 'None' or \ @@ -77,7 +87,7 @@ if isinstance(value, str): value = load(value) - return value + return value # type: ignore[no-any-return] expr = parse(textwrap.dedent(source), mode=mode) @@ -103,6 +113,8 @@ names = () + imports: dict[type[Any] | Hashable, ast.Name] + def __init__(self, tree): self.comments = [] self.defines = {} @@ -142,7 +154,7 @@ return load(name) - def require(self, value): + def require(self, value: type[Any] | Hashable) -> ast.Name: node = self.imports.get(value) if node is None: # we come up with a unique symbol based on the class name @@ -155,9 +167,9 @@ return node - def visit_Module(self, module) -> AST: + def visit_Module(self, module: Module) -> AST: assert isinstance(module, Module) - module = super().generic_visit(module) + module = super().generic_visit(module) # type: ignore[assignment] preamble: list[AST] = [] for name, node in self.defines.items(): @@ -188,21 +200,21 @@ return Module(imports + preamble + module.body, ()) - def visit_Comment(self, node) -> AST: + def visit_Comment(self, node: Comment) -> AST: self.comments.append(node.text) return Expr(Constant(...)) - def visit_Builtin(self, node) -> AST: + def visit_Builtin(self, node: Builtin) -> AST: name = load(node.id) - return self.visit(name) + return self.visit(name) # type: ignore[no-any-return] - def visit_Symbol(self, node) -> AST: + def visit_Symbol(self, node: Symbol) -> AST: return self.require(node.value) - def visit_Static(self, node) -> AST: + def visit_Static(self, node: Static) -> AST: if node.name is None: name = "_static_%s" % str(id(node.value)).replace('-', '_') else: name = node.name node = self.define(name, node.value) - return self.visit(node) + return self.visit(node) # type: ignore[no-any-return] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/compiler.py new/chameleon-4.5.4/src/chameleon/compiler.py --- old/chameleon-4.5.2/src/chameleon/compiler.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/compiler.py 2024-04-08 09:10:10.000000000 +0200 @@ -62,11 +62,11 @@ RE_NAME = re.compile('^%s$' % NAME) -def identifier(prefix, suffix=None) -> str: +def identifier(prefix: str, suffix: str | None = None) -> str: return "__{}_{}".format(prefix, mangle(suffix or id(prefix))) -def mangle(string): +def mangle(string: int | str) -> str: return RE_MANGLE.sub('_', str(string)).replace('\n', '').replace('-', '_') @@ -74,12 +74,12 @@ return template("getname(KEY)", KEY=ast.Str(s=name), mode="eval") -def store_econtext(name): +def store_econtext(name: object) -> ast.Subscript: name = str(name) return subscript(name, load("econtext"), ast.Store()) -def store_rcontext(name): +def store_rcontext(name: object) -> ast.Subscript: name = str(name) return subscript(name, load("rcontext"), ast.Store()) @@ -101,7 +101,7 @@ ) -def indent(s): +def indent(s: str | None) -> str: return textwrap.indent(s, " ") if s else "" @@ -277,11 +277,11 @@ class Interpolator: braces_required_regex = re.compile( - r'(\$)?\$({(?P<expression>.*)})', re.DOTALL + r'\$({(?P<expression>.*)})', re.DOTALL ) braces_optional_regex = re.compile( - r'(\$)?\$({(?P<expression>.*)}|(?P<variable>[A-Za-z][A-Za-z0-9_]*))', + r'\$({(?P<expression>.*)}|(?P<variable>[A-Za-z][A-Za-z0-9_]*))', re.DOTALL, ) @@ -335,6 +335,7 @@ while text: matched = text + m = self.regex.search(matched) if m is None: text = text.replace('$$', '$') @@ -344,18 +345,18 @@ part = text[:m.start()] text = text[m.start():] - skip = text.startswith('$$') - if skip: - part = part + '$' - if part: + i = 0 + length = len(part) + while i < length and part[-i - 1] == '$': + i += 1 + skip = i & 1 part = part.replace('$$', '$') node = ast.Str(s=part) nodes.append(node) - - if skip: - text = text[2:] - continue + if skip: + text = text[1:] + continue if not body: target = name @@ -1024,16 +1025,16 @@ def visit_EmitText(self, node) -> ast.AST: append = load(self.scopes[-1].append or "__append") - node = ast.Expr(ast.Call( + expr = ast.Expr(ast.Call( func=append, args=[ast.Str(s=node.s)], keywords=[], starargs=None, kwargs=None )) - return self.visit(node) + return self.visit(expr) # type: ignore[no-any-return] - def visit_Name(self, node) -> ast.AST: + def visit_Name(self, node: ast.Name) -> ast.AST: if isinstance(node.ctx, ast.Load): scope = self.scopes[-1] for name in ("append", "stream"): @@ -1049,7 +1050,7 @@ self.scopes.pop() return stmts - def visit_TokenRef(self, node) -> ast.AST: + def visit_TokenRef(self, node: TokenRef) -> ast.AST: self.tokens.append((node.token.pos, len(node.token))) return ast.Assign( [store("__token")], @@ -1492,13 +1493,20 @@ msgid = ast.Str(s=node.msgid) # emit the translation expression - body += template( - "if msgid: __append(translate(" + translation = template( + "__append(translate(" "msgid, mapping=mapping, default=default, domain=__i18n_domain, context=__i18n_context, target_language=target_language))", # noqa: E501 line too long msgid=msgid, default=default, mapping=mapping) + if not node.msgid: + translation = [ast.If( + test=load(msgid), body=translation, orelse=[] + )] + + body += translation + # pop away translation block reference self._translations.pop() @@ -1703,7 +1711,7 @@ # generate code code = self.visit(node.node) - body.append(TranslationContext(code, append)) + body.append(TranslationContext(code, append, stream)) # output msgid text = Text('${%s}' % node.name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/i18n.py new/chameleon-4.5.4/src/chameleon/i18n.py --- old/chameleon-4.5.2/src/chameleon/i18n.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/i18n.py 2024-04-08 09:10:10.000000000 +0200 @@ -69,11 +69,11 @@ return None if target_language is not None or context is not None: - result = translate( + result: str = translate( msgid, domain=domain, mapping=mapping, context=context, target_language=target_language, default=default) if result != msgid: - return result # type: ignore[no-any-return] + return result if isinstance(msgid, Message): default = msgid.default diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/template.py new/chameleon-4.5.4/src/chameleon/template.py --- old/chameleon-4.5.2/src/chameleon/template.py 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/template.py 2024-04-08 09:10:10.000000000 +0200 @@ -342,7 +342,7 @@ builtins=builtins, strict=self.strict ) - return compiler.code + return compiler.code # type: ignore[no-any-return] class BaseTemplateFile(BaseTemplate): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/tests/inputs/007-content-interpolation.pt new/chameleon-4.5.4/src/chameleon/tests/inputs/007-content-interpolation.pt --- old/chameleon-4.5.2/src/chameleon/tests/inputs/007-content-interpolation.pt 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/tests/inputs/007-content-interpolation.pt 2024-04-08 09:10:10.000000000 +0200 @@ -14,6 +14,10 @@ ${None or 'Hello world'} $leftalone + $${'fred'} + $$${'fred'} + $$$${'fred'} + $$$$${'fred'} <div>${None}</div> <div>${1 < 2 and 'Hello world' or None}</div> <div>${} is ignored.</div> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/tests/inputs/128-translation-macro-name.pt new/chameleon-4.5.4/src/chameleon/tests/inputs/128-translation-macro-name.pt --- old/chameleon-4.5.2/src/chameleon/tests/inputs/128-translation-macro-name.pt 1970-01-01 01:00:00.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/tests/inputs/128-translation-macro-name.pt 2024-04-08 09:10:10.000000000 +0200 @@ -0,0 +1,5 @@ +<a metal:define-macro="fancy-link" href="#">Fancy link</a> + +<tal:something i18n:translate="text_with_link"> + Now follows a fancy link: <metal:fancy-link i18n:name="obj_link" use-macro="macros['fancy-link']" /> +</tal:something> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/tests/outputs/007.pt new/chameleon-4.5.4/src/chameleon/tests/outputs/007.pt --- old/chameleon-4.5.2/src/chameleon/tests/outputs/007.pt 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/tests/outputs/007.pt 2024-04-08 09:10:10.000000000 +0200 @@ -13,6 +13,10 @@ Hello world $leftalone + ${'fred'} + $fred + $${'fred'} + $$fred <div></div> <div>Hello world</div> <div>${} is ignored.</div> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/tests/outputs/128-en.pt new/chameleon-4.5.4/src/chameleon/tests/outputs/128-en.pt --- old/chameleon-4.5.2/src/chameleon/tests/outputs/128-en.pt 1970-01-01 01:00:00.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/tests/outputs/128-en.pt 2024-04-08 09:10:10.000000000 +0200 @@ -0,0 +1,3 @@ +<a href="#">Fancy link</a> + +Now follows a fancy link: <a href="#">Fancy link</a> ('text_with_link' translation into 'en') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/src/chameleon/tests/outputs/128.pt new/chameleon-4.5.4/src/chameleon/tests/outputs/128.pt --- old/chameleon-4.5.2/src/chameleon/tests/outputs/128.pt 1970-01-01 01:00:00.000000000 +0100 +++ new/chameleon-4.5.4/src/chameleon/tests/outputs/128.pt 2024-04-08 09:10:10.000000000 +0200 @@ -0,0 +1,3 @@ +<a href="#">Fancy link</a> + +Now follows a fancy link: <a href="#">Fancy link</a> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/chameleon-4.5.2/tox.ini new/chameleon-4.5.4/tox.ini --- old/chameleon-4.5.2/tox.ini 2024-01-29 21:55:50.000000000 +0100 +++ new/chameleon-4.5.4/tox.ini 2024-04-08 09:10:10.000000000 +0200 @@ -12,6 +12,9 @@ docs coverage mypy + z3c.macro + z3c.pt + [testenv] usedevelop = true deps = @@ -110,3 +113,25 @@ mypy -p chameleon --python-version 3.10 mypy -p chameleon --python-version 3.11 mypy -p chameleon --python-version 3.12 + +[testenv:z3c.macro] +basepython = python3 +skip_install = true +commands_pre = + pip install -e. +usedevelop = true +deps = + z3c.macro[test] +commands = + zope-testrunner --path {env_site_packages_dir} --path . -s z3c.macro {posargs:-vc} + +[testenv:z3c.pt] +basepython = python3 +skip_install = true +commands_pre = + pip install -e. +usedevelop = true +deps = + z3c.pt[test] +commands = + zope-testrunner --path {env_site_packages_dir} --path . -s z3c.pt {posargs:-vc}