diff --git a/src/chameleon/tests/test_templates.py b/src/chameleon/tests/test_templates.py index 30cb253c..d94030f9 100644 --- a/src/chameleon/tests/test_templates.py +++ b/src/chameleon/tests/test_templates.py @@ -1,22 +1,58 @@ +import glob import os import re import shutil import sys -import tempfile from functools import partial from functools import wraps -from unittest import TestCase from chameleon.exc import RenderError +from chameleon.exc import TemplateError from chameleon.tales import DEFAULT_MARKER +import pytest + +ROOT = os.path.dirname(__file__) + + +def find_files(ext): + inputs = os.path.join(ROOT, "inputs") + outputs = os.path.join(ROOT, "outputs") + found = [] + for filename in sorted(os.listdir(inputs)): + name, extension = os.path.splitext(filename) + if extension != ext: + continue + path = os.path.join(inputs, filename) + + # if there's no output file, treat document as static and + # expect input equal to output + globbed = tuple(glob.iglob(os.path.join( + outputs, "{}*{}".format(name.split('-', 1)[0], ext)))) + + if not globbed: + raise RuntimeError("Missing output for: %s." % name) + + for output in globbed: + name, ext = os.path.splitext(output) + basename = os.path.basename(name) + if '-' in basename: + language = basename.split('-')[1] + else: + language = None + + base = len(ROOT) + 1 + found.append((path[base:], output[base:], language)) + + return pytest.mark.parametrize("input_path,output_path,language", found) + class Message: def __str__(self): return "message" -class ImportTestCase(TestCase): +class TestImports: def test_pagetemplates(self): from chameleon import PageTemplate # noqa: F401 unused from chameleon import PageTemplateFile # noqa: F401 unused @@ -27,7 +63,7 @@ def test_pagetexttemplates(self): from chameleon import PageTextTemplateFile # noqa: F401 unused -class TemplateFileTestCase(TestCase): +class TestTemplateFile: @property def _class(self): from chameleon.template import BaseTemplateFile @@ -41,28 +77,22 @@ def cook(self, body): return TestTemplateFile - def setUp(self): - self.tempdir = tempfile.mkdtemp(prefix='chameleon-tests') - - def tearDown(self): - shutil.rmtree(self.tempdir) - - def _get_temporary_file(self): - filename = os.path.join(self.tempdir, 'template.py') + def _get_temporary_file(self, tmp_path): + filename = os.path.join(tmp_path, 'template.py') assert not os.path.exists(filename) f = open(filename, 'w') f.flush() f.close() return filename - def test_cook_check(self): - fn = self._get_temporary_file() + def test_cook_check(self, tmp_path): + fn = self._get_temporary_file(tmp_path) template = self._class(fn) template.cook_check() - self.assertEqual(template.cook_count, 1) + assert template.cook_count == 1 - def test_auto_reload(self): - fn = self._get_temporary_file() + def test_auto_reload(self, tmp_path): + fn = self._get_temporary_file(tmp_path) # set time in past os.utime(fn, (0, 0)) @@ -72,62 +102,26 @@ def test_auto_reload(self): # a second cook check makes no difference template.cook_check() - self.assertEqual(template.cook_count, 1) + assert template.cook_count == 1 # set current time on file os.utime(fn, None) # file is reloaded template.cook_check() - self.assertEqual(template.cook_count, 2) + assert template.cook_count == 2 def test_relative_is_expanded_to_cwd(self): template = self._class("___does_not_exist___") try: template.cook_check() except OSError as exc: - self.assertEqual( - os.getcwd(), - os.path.dirname(exc.filename) - ) + assert os.getcwd() == os.path.dirname(exc.filename) else: - self.fail("Expected OSError.") - - -class RenderTestCase(TestCase): - root = os.path.dirname(__file__) - maxDiff = 4096 - - def find_files(self, ext): - inputs = os.path.join(self.root, "inputs") - outputs = os.path.join(self.root, "outputs") - for filename in sorted(os.listdir(inputs)): - name, extension = os.path.splitext(filename) - if extension != ext: - continue - path = os.path.join(inputs, filename) - - # if there's no output file, treat document as static and - # expect input equal to output - import glob - globbed = tuple(glob.iglob(os.path.join( - outputs, "{}*{}".format(name.split('-', 1)[0], ext)))) - - if not globbed: - self.fail("Missing output for: %s." % name) + pytest.fail("Expected OSError.") - for output in globbed: - name, ext = os.path.splitext(output) - basename = os.path.basename(name) - if '-' in basename: - language = basename.split('-')[1] - else: - language = None - yield path, output, language - - -class ZopePageTemplatesTest(RenderTestCase): +class TestZopePageTemplates: @property def from_string(self): from chameleon.zpt.template import PageTemplate @@ -148,30 +142,14 @@ def wrapper(self): return wrapper return decorator - def error(body): - def decorator(func): - @wraps(func) - def wrapper(self): - from chameleon.exc import TemplateError - try: - self.from_string(body) - except TemplateError as exc: - return func(self, body, exc) - else: - self.fail("Expected exception.") - - return wrapper - return decorator - def test_syntax_error_in_strict_mode(self): from chameleon.exc import ExpressionError - self.assertRaises( - ExpressionError, - self.from_string, - """""", - strict=True - ) + with pytest.raises(ExpressionError): + self.from_string( + """""", + strict=True + ) def test_syntax_error_in_non_strict_mode(self): from chameleon.exc import ExpressionError @@ -182,9 +160,9 @@ def test_syntax_error_in_non_strict_mode(self): try: template() except ExpressionError as exc: - self.assertTrue(body[exc.offset:].startswith('bad ///')) + assert body[exc.offset:].startswith('bad ///') else: - self.fail("Expected exception") + pytest.fail("Expected exception") def test_exists_error_leak(self): body = '''\ @@ -197,9 +175,9 @@ def test_exists_error_leak(self): try: template() except RenderError as exc: - self.assertNotIn('var_does_not_exists', str(exc)) + assert 'var_does_not_exists' not in str(exc) else: - self.fail("Expected exception") + pytest.fail("Expected exception") def test_sys_exc_info_is_clear_after_pipe(self): body = ( @@ -208,7 +186,7 @@ def test_sys_exc_info_is_clear_after_pipe(self): template = self.from_string(body, strict=False) got = template.render(error=sys.exc_info) - self.assertTrue('(None, None, None)' in got) + assert '(None, None, None)' in got def test_render_macro_include_subtemplate_containing_error(self): macro_inner = self.from_string( @@ -226,8 +204,8 @@ def test_render_macro_include_subtemplate_containing_error(self): try: template(macro=macro_wrap, macro_inner=macro_inner) except RenderError as exc: - self.assertTrue(isinstance(exc, KeyError)) - self.assertIn(''''key-does-not-exist' + assert isinstance(exc, KeyError) + assert ''''key-does-not-exist' - Expression: "dict()['key-does-not-exist']" - Filename: @@ -238,9 +216,9 @@ def test_render_macro_include_subtemplate_containing_error(self): - Expression: "macro" - Filename: - Location: (line 4: col 38) -''', str(exc)) +''' in str(exc) else: - self.fail("Expected exception") + pytest.fail("Expected exception") def test_render_error_macro_include(self): body = """""" @@ -249,46 +227,42 @@ def test_render_error_macro_include(self): try: template() except RenderError as exc: - self.assertTrue(isinstance(exc, AttributeError)) - self.assertIn('bad', str(exc)) + assert isinstance(exc, AttributeError) + assert 'bad' in str(exc) else: - self.fail("Expected exception") - - @error("""""") - def test_attributes_on_tal_tag_fails(self, body, exc): - self.assertTrue(body[exc.offset:].startswith('dummy')) - - @error("""""") - def test_i18n_attributes_with_non_identifiers(self, body, exc): - self.assertTrue(body[exc.offset:].startswith('foo,')) - - @error("""""") - def test_repeat_syntax_error_message(self, body, exc): - self.assertTrue(body[exc.offset:].startswith('key,value')) - - @error('''

+ pytest.fail("Expected exception") + + @pytest.mark.parametrize( + "body,s", [ + ("", 'dummy'), + ("", 'foo'), + ("", 'key,value'), + ('''

-

''') - def test_repeat_i18n_name_error(self, body, exc): - self.assertTrue(body[exc.offset:].startswith( - 'repeat'), body[exc.offset:]) - - @error(''' +

''', 'repeat'), + (''' - ''') - def test_i18n_name_not_in_translation_error(self, body, exc): - self.assertTrue(body[exc.offset:].startswith('not_in_translation')) +
''', 'not_in_translation') + ] + ) + def test_errors(self, body, s): + try: + self.from_string(body) + except TemplateError as exc: + assert body[exc.offset:].startswith(s) + else: + pytest.fail("Expected exception.") def test_encoded(self): filename = '074-encoded-template.pt' - with open(os.path.join(self.root, 'inputs', filename), 'rb') as f: + with open(os.path.join(ROOT, 'inputs', filename), 'rb') as f: body = f.read() self.from_string(body) def test_utf8_encoded(self): filename = '073-utf8-encoded.pt' - with open(os.path.join(self.root, 'inputs', filename), 'rb') as f: + with open(os.path.join(ROOT, 'inputs', filename), 'rb') as f: body = f.read() self.from_string(body) @@ -298,15 +272,16 @@ def test_recursion_error(self): '
' ) - self.assertRaises(RecursionError, template) + with pytest.raises(RecursionError): + template() try: template() except RecursionError as exc: - self.assertFalse(isinstance(exc, RenderError)) + assert not isinstance(exc, RenderError) def test_unicode_decode_error(self): template = self.from_file( - os.path.join(self.root, 'inputs', 'greeting.pt') + os.path.join(ROOT, 'inputs', 'greeting.pt') ) string = native = "the artist formerly known as ƤŗíƞĆě" @@ -321,7 +296,7 @@ def __html__(): # This raises a decoding exception string.encode('utf-8').decode('ascii') - self.fail("Expected exception raised.") + pytest.fail("Expected exception raised.") try: template(name=name) @@ -330,10 +305,10 @@ def __html__(): # There's a marker under the expression that has the # unicode decode error - self.assertTrue('^^^^^' in formatted) - self.assertTrue(native in formatted) + assert '^^^^^' in formatted + assert native in formatted else: - self.fail("expected error") + pytest.fail("expected error") def test_package_name_cook_check(self): template = self.from_file( @@ -362,10 +337,7 @@ def test_custom_encoding_for_str_or_bytes_in_content(self): rendered = template(text=text.encode('windows-1251')) - self.assertEqual( - rendered, - string.replace('${text}', text) - ) + assert rendered == string.replace('${text}', text) def test_custom_encoding_for_str_or_bytes_in_attributes(self): string = '' @@ -385,15 +357,12 @@ def test_custom_encoding_for_str_or_bytes_in_attributes(self): rendered = template(text=text.encode('windows-1251')) - self.assertEqual( - rendered, - string.replace('${text}', text) - ) + assert rendered == string.replace('${text}', text) def test_null_translate_function(self): template = self.from_string('${test}', translate=None) rendered = template(test=object()) - self.assertTrue('object' in rendered) + assert 'object' in rendered def test_on_error_handler(self): exc = [] @@ -403,33 +372,33 @@ def test_on_error_handler(self): on_error_handler=handler ) template() - self.assertEqual(len(exc), 1) - self.assertEqual(exc[0].__class__.__name__, "NameError") + assert len(exc) == 1 + assert exc[0].__class__.__name__ == "NameError" def test_object_substitution_coerce_to_str(self): template = self.from_string('${test}', translate=None) class dummy: def __repr__(inst): - self.fail("call not expected") + pytest.fail("call not expected") def __str__(inst): return '' rendered = template(test=dummy()) - self.assertEqual(rendered, '<dummy>') + assert rendered == '<dummy>' def test_repr(self): template = self.from_file( - os.path.join(self.root, 'inputs', 'hello_world.pt') + os.path.join(ROOT, 'inputs', 'hello_world.pt') ) - self.assertTrue(template.filename in repr(template)) + assert template.filename in repr(template) def test_underscore_variable(self): template = self.from_string( "
${_dummy}
" ) - self.assertTrue(template(), "
foo
") + assert template(), "
foo
" def test_trim_attribute_space(self): document = '''
" in result1) - self.assertTrue(" />" in result2) + assert result1.count(" ") == 49 + assert result2.count(" ") == 4 + assert " />" in result1 + assert " />" in result2 def test_exception(self): from traceback import format_exception_only @@ -458,22 +427,22 @@ def test_exception(self): try: template() except Exception as exc: - self.assertIn(RenderError, type(exc).__bases__) + assert RenderError in type(exc).__bases__ formatted = str(exc) - self.assertFalse('NameError:' in formatted) - self.assertTrue('foo' in formatted) - self.assertTrue('(line 1: col 23)' in formatted) + assert 'NameError:' not in formatted + assert 'foo' in formatted + assert '(line 1: col 23)' in formatted formatted_exc = "\n".join(format_exception_only(type(exc), exc)) - self.assertTrue('NameError: foo' in formatted_exc) + assert 'NameError: foo' in formatted_exc else: - self.fail("expected error") + pytest.fail("expected error") def test_create_formatted_exception(self): from chameleon.utils import create_formatted_exception exc = create_formatted_exception(NameError('foo'), NameError, str) - self.assertEqual(exc.args, ('foo', )) + assert exc.args == ('foo', ) class MyNameError(NameError): def __init__(self, boo): @@ -481,8 +450,8 @@ def __init__(self, boo): self.bar = boo exc = create_formatted_exception(MyNameError('foo'), MyNameError, str) - self.assertEqual(exc.args, ('foo', )) - self.assertEqual(exc.bar, 'foo') + assert exc.args == ('foo', ) + assert exc.bar == 'foo' def test_create_formatted_exception_no_subclass(self): from chameleon.utils import create_formatted_exception @@ -496,7 +465,7 @@ def __init__(self, class_name, bases, namespace): 'Difficult', (BaseException, ), {'args': ()}) exc = create_formatted_exception(Difficult(), Difficult, str) - self.assertEqual(exc.args, ()) + assert exc.args == () def test_error_handler_makes_safe_copy(self): calls = [] @@ -515,27 +484,27 @@ def _render(stream, econtext, rcontext, **kw): try: template() except TestException as exc: - self.assertEqual(calls, [(('foo', ), {'bar': 'baz'})]) + assert calls == [(('foo', ), {'bar': 'baz'})] formatted = str(exc) - self.assertTrue('TestException' in formatted) - self.assertTrue('"expression"' in formatted) - self.assertTrue('(line 1: col 42)' in formatted) + assert 'TestException' in formatted + assert '"expression"' in formatted + assert '(line 1: col 42)' in formatted else: - self.fail("unexpected error") + pytest.fail("unexpected error") def test_double_underscore_variable(self): from chameleon.exc import TranslationError - self.assertRaises( - TranslationError, self.from_string, - "
${__dummy}
", - ) + with pytest.raises(TranslationError): + self.from_string( + "
${__dummy}
" + ) def test_disable_comment_interpolation(self): template = self.from_string( '', enable_comment_interpolation=False ) - self.assertEqual(template(), '') + assert template() == '' def test_compiler_internals_are_disallowed(self): from chameleon.compiler import COMPILER_INTERNALS_OR_DISALLOWED @@ -543,7 +512,8 @@ def test_compiler_internals_are_disallowed(self): for name in COMPILER_INTERNALS_OR_DISALLOWED: body = "${{{}}}".format(name, name) - self.assertRaises(TranslationError, self.from_string, body) + with pytest.raises(TranslationError): + self.from_string(body) def test_simple_translate_mapping(self): template = self.from_string( @@ -551,7 +521,7 @@ def test_simple_translate_mapping(self): 'foo' '
') - self.assertEqual(template(), '
foo
') + assert template() == '
foo
' def test_translate_is_not_an_internal(self): macro = self.from_string('bar') @@ -564,16 +534,12 @@ def test_translate_is_not_an_internal(self): ''') result = template(macro=macro) - self.assertTrue('foo' in result) - self.assertTrue('foo' in result) + assert 'foo' in result + assert 'foo' in result def test_default_marker(self): template = self.from_string('') - self.assertEqual( - template(), - str(id(DEFAULT_MARKER)), - template.source - ) + assert template() == str(id(DEFAULT_MARKER)), template.source def test_boolean_attributes(self): template = self.from_string( @@ -594,12 +560,12 @@ def test_boolean_attributes(self): )) ) - self.assertEqual( - template( - dynamic_true={"checked": True}, - dynamic_false={"checked": False}, - dynamic_marker=template.default_marker.value, - ), + rendered = template( + dynamic_true={"checked": True}, + dynamic_false={"checked": False}, + dynamic_marker=template.default_marker.value, + ) + assert rendered == \ "\n".join(( '', '', @@ -614,37 +580,36 @@ def test_boolean_attributes(self): '', '', '', - )), + )), \ "Output mismatch\n" + template.source - ) def test_default_debug_flag(self): from chameleon.config import DEBUG_MODE template = self.from_file( - os.path.join(self.root, 'inputs', 'hello_world.pt'), + os.path.join(ROOT, 'inputs', 'hello_world.pt'), ) - self.assertEqual(template.debug, DEBUG_MODE) - self.assertTrue('debug' not in template.__dict__) + assert template.debug == DEBUG_MODE + assert 'debug' not in template.__dict__ def test_debug_flag_on_string(self): from chameleon.loader import ModuleLoader - with open(os.path.join(self.root, 'inputs', 'hello_world.pt')) as f: + with open(os.path.join(ROOT, 'inputs', 'hello_world.pt')) as f: source = f.read() template = self.from_string(source, debug=True) - self.assertTrue(template.debug) - self.assertTrue(isinstance(template.loader, ModuleLoader)) + assert template.debug + assert isinstance(template.loader, ModuleLoader) def test_debug_flag_on_file(self): from chameleon.loader import ModuleLoader template = self.from_file( - os.path.join(self.root, 'inputs', 'hello_world.pt'), + os.path.join(ROOT, 'inputs', 'hello_world.pt'), debug=True, ) - self.assertTrue(template.debug) - self.assertTrue(isinstance(template.loader, ModuleLoader)) + assert template.debug + assert isinstance(template.loader, ModuleLoader) def test_tag_mismatch(self): from chameleon.exc import ParseError @@ -656,24 +621,22 @@ def test_tag_mismatch(self):
""") except ParseError as exc: - self.assertTrue("" in str(exc)) + assert "" in str(exc) else: - self.fail("Expected error.") + pytest.fail("Expected error.") def test_f_strings(self): from math import pi from math import sin template = self.from_string('${f"sin({a}) is {sin(a):.3}"}') rendered = template(sin=sin, a=pi) - self.assertEqual('sin(3.141592653589793) is 1.22e-16', rendered) + assert 'sin(3.141592653589793) is 1.22e-16' == rendered def test_windows_line_endings(self): template = self.from_string('') - self.assertEqual(template(), - 'bar') + assert template() == 'bar' def test_digest(self): # Make sure ``digest`` doesn't error out when ``filename`` is something @@ -681,21 +644,13 @@ def test_digest(self): data = '' template = self.from_string(data) template.filename = None - self.assertTrue(template.digest(data, [])) + assert template.digest(data, []) template.filename = '' - self.assertTrue(template.digest(data, [])) + assert template.digest(data, []) - -class ZopeTemplatesTestSuite(RenderTestCase): - def setUp(self): - self.temp_path = temp_path = tempfile.mkdtemp() - - @self.addCleanup - def cleanup(path=temp_path): - shutil.rmtree(path) - - def test_pt_files(self): + @find_files(".pt") + def test_pt_files(self, input_path, output_path, language): from chameleon.zpt.template import PageTemplateFile class Literal: @@ -710,125 +665,129 @@ def __str__(self): "%r is a literal." % self.s) from chameleon.loader import TemplateLoader - loader = TemplateLoader(os.path.join(self.root, "inputs")) + + # TODO: Should take the path from the input path? + loader = TemplateLoader(os.path.join(ROOT, "inputs")) self.execute( - ".pt", PageTemplateFile, + input_path, + output_path, + language, + PageTemplateFile, literal=Literal("
Hello world!
"), content="
Hello world!
", message=Message(), load=loader.bind(PageTemplateFile), ) - def test_txt_files(self): + @find_files(".txt") + def test_txt_files(self, input_path, output_path, language): from chameleon.zpt.template import PageTextTemplateFile - self.execute(".txt", PageTextTemplateFile) - - def execute(self, ext, factory, **kwargs): - def translate(msgid, domain=None, mapping=None, context=None, - target_language=None, default=None): - if default is None: - default = str(msgid) - - if isinstance(msgid, Message): - default = "Message" - - if mapping: - default = re.sub(r'\${([a-z_]+)}', r'%(\1)s', default) % \ - mapping - - if target_language is None: - return default - - if domain is None: - with_domain = "" - else: - with_domain = " with domain '%s'" % domain - - if context is None: - with_context = "" - else: - with_context = ", context '%s'" % context - - stripped = default.rstrip('\n ') - return "{} ('{}' translation into '{}'{}{}){}".format( - stripped, msgid, target_language, with_domain, with_context, - default[len(stripped):] - ) - - for input_path, output_path, language in self.find_files(ext): - # Make friendly title so we can locate the generated - # source when debugging - self.shortDescription = lambda: input_path - - # When input path contains the string 'implicit-i18n', we - # enable "implicit translation". - implicit_i18n = 'implicit-i18n' in input_path - implicit_i18n_attrs = ("alt", "title") if implicit_i18n else () - - enable_data_attributes = 'data-attributes' in input_path - - template = factory( - input_path, - keep_source=True, - strict=False, - implicit_i18n_translate=implicit_i18n, - implicit_i18n_attributes=implicit_i18n_attrs, - enable_data_attributes=enable_data_attributes, - ) + self.execute(input_path, output_path, language, PageTextTemplateFile) + + def execute(self, input_path, output_path, language, factory, **kwargs): + # Make friendly title so we can locate the generated + # source when debugging + self.shortDescription = lambda: input_path + + # When input path contains the string 'implicit-i18n', we + # enable "implicit translation". + implicit_i18n = 'implicit-i18n' in input_path + implicit_i18n_attrs = ("alt", "title") if implicit_i18n else () + + enable_data_attributes = 'data-attributes' in input_path + + template = factory( + os.path.join(ROOT, input_path), + keep_source=True, + strict=False, + implicit_i18n_translate=implicit_i18n, + implicit_i18n_attributes=implicit_i18n_attrs, + enable_data_attributes=enable_data_attributes, + ) - params = kwargs.copy() - params.update({ - 'translate': translate, - 'target_language': language, - }) + params = kwargs.copy() + params.update({ + 'translate': self.translate, + 'target_language': language, + }) - template.cook_check() + template.cook_check() - try: - got = template.render(**params) - except BaseException: - import traceback - e = traceback.format_exc() - self.fail("{}\n\n Example source:\n\n{}".format( - e, - "\n".join( - ["%#03.d%s" % (lineno + 1, line and " " + line or "") - for (lineno, line) in enumerate(template.source.split( - '\n'))]))) - - if isinstance(got, bytes): - got = got.decode('utf-8') - - from doctest import OutputChecker - checker = OutputChecker() - - if not os.path.exists(output_path): - output = template.body - else: - with open(output_path, 'rb') as f: - output = f.read() + try: + got = template.render(**params) + except BaseException: + import traceback + e = traceback.format_exc() + pytest.fail("{}\n\n Example source:\n\n{}".format( + e, + "\n".join( + ["%#03.d%s" % (lineno + 1, line and " " + line or "") + for (lineno, line) in enumerate(template.source.split( + '\n'))]))) + + if isinstance(got, bytes): + got = got.decode('utf-8') + + from doctest import OutputChecker + checker = OutputChecker() + + output_filename = os.path.join(ROOT, output_path) + with open(output_filename, 'rb') as f: + output = f.read() + + from chameleon.utils import detect_encoding + from chameleon.utils import read_xml_encoding + + if template.content_type == 'text/xml': + encoding = read_xml_encoding(output) or \ + template.default_encoding + else: + content_type, encoding = detect_encoding( + output, template.default_encoding) + + # Newline normalization across platforms + want = '\n'.join(output.decode(encoding).splitlines()) + got = '\n'.join(got.splitlines()) + + if checker.check_output(want, got, 0) is False: + from doctest import Example + example = Example(input_path, want) + diff = checker.output_difference( + example, got, 0) + source = template.source + pytest.fail("({}) - \n{}\n\nCode:\n{}".format( + input_path, diff.rstrip('\n'), + source)) + + @staticmethod + def translate(msgid, domain=None, mapping=None, context=None, + target_language=None, default=None): + if default is None: + default = str(msgid) + + if isinstance(msgid, Message): + default = "Message" + + if mapping: + default = re.sub(r'\${([a-z_]+)}', r'%(\1)s', default) % \ + mapping + + if target_language is None: + return default + + if domain is None: + with_domain = "" + else: + with_domain = " with domain '%s'" % domain - from chameleon.utils import detect_encoding - from chameleon.utils import read_xml_encoding + if context is None: + with_context = "" + else: + with_context = ", context '%s'" % context - if template.content_type == 'text/xml': - encoding = read_xml_encoding(output) or \ - template.default_encoding - else: - content_type, encoding = detect_encoding( - output, template.default_encoding) - - # Newline normalization across platforms - want = '\n'.join(output.decode(encoding).splitlines()) - got = '\n'.join(got.splitlines()) - - if checker.check_output(want, got, 0) is False: - from doctest import Example - example = Example(input_path, want) - diff = checker.output_difference( - example, got, 0) - source = template.source - self.fail("({}) - \n{}\n\nCode:\n{}".format( - input_path, diff.rstrip('\n'), - source)) + stripped = default.rstrip('\n ') + return "{} ('{}' translation into '{}'{}{}){}".format( + stripped, msgid, target_language, with_domain, with_context, + default[len(stripped):] + ) diff --git a/src/chameleon/utils.py b/src/chameleon/utils.py index 6d394e73..efda2d88 100644 --- a/src/chameleon/utils.py +++ b/src/chameleon/utils.py @@ -376,9 +376,7 @@ class DebuggingOutputStream(list[str]): def append(self, value: str) -> None: if not isinstance(value, str): raise TypeError(value) - - str(value) - list.append(self, value) + super().append(value) class Scope(dict[str, Any]):