From b8600b9927297f12dcbb8368dfd1bdefe4a22d16 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 31 Jan 2026 13:49:40 +0100 Subject: [PATCH 1/7] Simplify some tests --- tests/messages/frontend/test_cli.py | 6 +---- tests/test_core.py | 11 +++----- tests/test_date_intervals.py | 41 ++++++++++++++++------------- tests/test_languages.py | 6 ++--- tests/test_localedata.py | 15 ++++++----- tests/test_numbers_parsing.py | 9 ++++--- tests/test_plural.py | 11 ++++---- tests/test_support_translations.py | 16 ++++++----- 8 files changed, 57 insertions(+), 58 deletions(-) diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index 480157189..038a103b4 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -59,11 +59,7 @@ def test_usage(cli): with pytest.raises(SystemExit) as ei: cli.run(["pybabel"]) assert ei.value.code == 2 - assert sys.stderr.getvalue().lower() == """\ -usage: pybabel command [options] [args] - -pybabel: error: no valid command or option passed. try the -h/--help option for more information. -""" + assert "error: no valid command or option passed" in sys.stderr.getvalue().lower() def test_list_locales(cli): diff --git a/tests/test_core.py b/tests/test_core.py index 7cb9cd90b..98c8bfb20 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -78,7 +78,6 @@ def test_hash(): class TestLocaleClass: - def test_attributes(self): locale = Locale('en', 'US') assert locale.language == 'en' @@ -158,8 +157,7 @@ def test_territories_property(self): assert Locale('es', 'CO').territories['DE'] == 'Alemania' def test_variants_property(self): - assert (Locale('de', 'DE').variants['1901'] == - 'Alte deutsche Rechtschreibung') + assert Locale('de', 'DE').variants['1901'] == 'Alte deutsche Rechtschreibung' def test_currencies_property(self): assert Locale('en').currencies['COP'] == 'Colombian Peso' @@ -194,10 +192,9 @@ def test_decimal_formats(self): assert Locale('en', 'US').decimal_formats[None].pattern == '#,##0.###' def test_currency_formats_property(self): - assert (Locale('en', 'US').currency_formats['standard'].pattern == - '\xa4#,##0.00') - assert (Locale('en', 'US').currency_formats['accounting'].pattern == - '\xa4#,##0.00;(\xa4#,##0.00)') + en_us_currency_format = Locale('en', 'US').currency_formats + assert en_us_currency_format['standard'].pattern == '\xa4#,##0.00' + assert en_us_currency_format['accounting'].pattern == '\xa4#,##0.00;(\xa4#,##0.00)' def test_percent_formats_property(self): assert Locale('en', 'US').percent_formats[None].pattern == '#,##0%' diff --git a/tests/test_date_intervals.py b/tests/test_date_intervals.py index c0532c9d2..33a85d38a 100644 --- a/tests/test_date_intervals.py +++ b/tests/test_date_intervals.py @@ -1,5 +1,7 @@ import datetime +import pytest + from babel import dates from babel.util import UTC @@ -8,20 +10,17 @@ TEST_DATE = TEST_DT.date() -def test_format_interval_same_instant_1(): - assert dates.format_interval(TEST_DT, TEST_DT, "yMMMd", fuzzy=False, locale="fi") == "8.1.2016" - - -def test_format_interval_same_instant_2(): - assert dates.format_interval(TEST_DT, TEST_DT, "xxx", fuzzy=False, locale="fi") == "8.1.2016 11.46.15" - - -def test_format_interval_same_instant_3(): - assert dates.format_interval(TEST_TIME, TEST_TIME, "xxx", fuzzy=False, locale="fi") == "11.46.15" - - -def test_format_interval_same_instant_4(): - assert dates.format_interval(TEST_DATE, TEST_DATE, "xxx", fuzzy=False, locale="fi") == "8.1.2016" +@pytest.mark.parametrize( + ("start", "end", "skeleton", "expected"), + [ + (TEST_DT, TEST_DT, "yMMMd", "8.1.2016"), + (TEST_DT, TEST_DT, "xxx", "8.1.2016 11.46.15"), + (TEST_TIME, TEST_TIME, "xxx", "11.46.15"), + (TEST_DATE, TEST_DATE, "xxx", "8.1.2016"), + ], +) +def test_format_interval_same_instant(start, end, skeleton, expected): + assert dates.format_interval(start, end, skeleton, fuzzy=False, locale="fi") == expected def test_format_interval_no_difference(): @@ -34,27 +33,31 @@ def test_format_interval_in_tz(timezone_getter): t1 = TEST_DT.replace(tzinfo=UTC) t2 = t1 + datetime.timedelta(minutes=18) hki_tz = timezone_getter("Europe/Helsinki") - assert dates.format_interval(t1, t2, "Hmv", tzinfo=hki_tz, locale="fi") == "13.46\u201314.04 aikavyöhyke: Suomi" + formatted = dates.format_interval(t1, t2, "Hmv", tzinfo=hki_tz, locale="fi") + assert formatted == "13.46\u201314.04 aikavyöhyke: Suomi" def test_format_interval_12_hour(): t2 = TEST_DT t1 = t2 - datetime.timedelta(hours=1) - assert dates.format_interval(t1, t2, "hm", locale="en") == "10:46\u2009\u2013\u200911:46\u202fAM" + formatted = dates.format_interval(t1, t2, "hm", locale="en") + assert formatted == "10:46\u2009\u2013\u200911:46\u202fAM" def test_format_interval_invalid_skeleton(): t1 = TEST_DATE t2 = TEST_DATE + datetime.timedelta(days=1) - assert dates.format_interval(t1, t2, "mumumu", fuzzy=False, locale="fi") == "8.1.2016\u20139.1.2016" + formatted = dates.format_interval(t1, t2, "mumumu", fuzzy=False, locale="fi") + assert formatted == "8.1.2016\u20139.1.2016" assert dates.format_interval(t1, t2, fuzzy=False, locale="fi") == "8.1.2016\u20139.1.2016" def test_issue_825(): - assert dates.format_timedelta( + formatted = dates.format_timedelta( datetime.timedelta(hours=1), granularity='hour', threshold=100, format='short', locale='pt', - ) == '1 h' + ) + assert formatted == '1 h' diff --git a/tests/test_languages.py b/tests/test_languages.py index 41fcc9e83..4a8b2dc60 100644 --- a/tests/test_languages.py +++ b/tests/test_languages.py @@ -10,7 +10,5 @@ def test_official_languages(): def test_get_language_info(): - assert ( - set(get_territory_language_info("HU")) == - {"hu", "fr", "en", "de", "ro", "hr", "sk", "sl"} - ) + expected = {"hu", "fr", "en", "de", "ro", "hr", "sk", "sl"} + assert set(get_territory_language_info("HU")) == expected diff --git a/tests/test_localedata.py b/tests/test_localedata.py index 0e500be4a..1a069067d 100644 --- a/tests/test_localedata.py +++ b/tests/test_localedata.py @@ -14,7 +14,6 @@ import pickle import random import sys -import tempfile import pytest @@ -54,7 +53,10 @@ def test_merge_with_alias_and_resolve(): localedata.merge(d1, d2) assert d1 == {'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 'y': (alias, {'b': 22, 'e': 25})} d = localedata.LocaleDataDict(d1) - assert dict(d.items()) == {'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 'y': {'a': 1, 'b': 22, 'c': 3, 'd': 14, 'e': 25}} + assert dict(d.items()) == { + 'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, + 'y': {'a': 1, 'b': 22, 'c': 3, 'd': 14, 'e': 25}, + } # Resolving the partial alias must not have written the result back into the underlying data (GH-1234) assert d1['y'] == (alias, {'b': 22, 'e': 25}) @@ -229,16 +231,15 @@ def listdir_spy(*args): assert len(listdir_calls) == 2 -def test_locale_name_cleanup(): +def test_locale_name_cleanup(tmp_path): """ Test that locale identifiers are cleaned up to avoid directory traversal. """ - no_exist_name = os.path.join(tempfile.gettempdir(), "babel%d.dat" % random.randint(1, 99999)) - with open(no_exist_name, "wb") as f: - pickle.dump({}, f) + no_exist_path = tmp_path / f"babel{random.randint(1, 99999):d}.dat" + no_exist_path.write_bytes(pickle.dumps({})) try: - name = os.path.splitext(os.path.relpath(no_exist_name, localedata._dirname))[0] + name = os.path.splitext(os.path.relpath(no_exist_path, localedata._dirname))[0] except ValueError: if sys.platform == "win32": pytest.skip("unable to form relpath") diff --git a/tests/test_numbers_parsing.py b/tests/test_numbers_parsing.py index 0b1d03cad..16eae65aa 100644 --- a/tests/test_numbers_parsing.py +++ b/tests/test_numbers_parsing.py @@ -18,10 +18,11 @@ def test_can_parse_decimals(): - assert decimal.Decimal('1099.98') == numbers.parse_decimal('1,099.98', locale='en_US') - assert decimal.Decimal('1099.98') == numbers.parse_decimal('1.099,98', locale='de') - assert decimal.Decimal('1099.98') == numbers.parse_decimal('1,099.98', locale='ar', numbering_system="default") - assert decimal.Decimal('1099.98') == numbers.parse_decimal('1٬099٫98', locale='ar_EG', numbering_system="default") + v = decimal.Decimal('1099.98') + assert numbers.parse_decimal('1,099.98', locale='en_US') == v + assert numbers.parse_decimal('1.099,98', locale='de') == v + assert numbers.parse_decimal('1,099.98', locale='ar', numbering_system="default") == v # fmt: skip + assert numbers.parse_decimal('1٬099٫98', locale='ar_EG', numbering_system="default") == v # fmt: skip with pytest.raises(numbers.NumberFormatError): numbers.parse_decimal('2,109,998', locale='de') with pytest.raises(numbers.UnsupportedNumberingSystemError): diff --git a/tests/test_plural.py b/tests/test_plural.py index bde356bc6..94d917de4 100644 --- a/tests/test_plural.py +++ b/tests/test_plural.py @@ -13,7 +13,7 @@ import pytest -from babel import localedata, plural +from babel import Locale, localedata, plural EPSILON = decimal.Decimal("0.0001") @@ -67,8 +67,8 @@ def test_plural_other_is_ignored(): def test_to_javascript(): - assert (plural.to_javascript({'one': 'n is 1'}) - == "(function(n) { return (n == 1) ? 'one' : 'other'; })") + src = plural.to_javascript({'one': 'n is 1'}) + assert src == "(function(n) { return (n == 1) ? 'one' : 'other'; })" def test_to_python(): @@ -82,8 +82,8 @@ def test_to_python(): def test_to_gettext(): - assert (plural.to_gettext({'one': 'n is 1', 'two': 'n is 2'}) - == 'nplurals=3; plural=((n == 1) ? 0 : (n == 2) ? 1 : 2);') + src = plural.to_gettext({'one': 'n is 1', 'two': 'n is 2'}) + assert src == 'nplurals=3; plural=((n == 1) ? 0 : (n == 2) ? 1 : 2);' def test_in_range_list(): @@ -133,7 +133,6 @@ def test_plural_within_rules(): def test_locales_with_no_plural_rules_have_default(): - from babel import Locale pf = Locale.parse('ii').plural_form assert pf(1) == 'other' assert pf(2) == 'other' diff --git a/tests/test_support_translations.py b/tests/test_support_translations.py index 7e6dc59f9..bf2eb2b98 100644 --- a/tests/test_support_translations.py +++ b/tests/test_support_translations.py @@ -186,15 +186,19 @@ def get_gettext_method_names(obj): def test_null_translations_have_same_methods(empty_translations, null_translations): for name in get_gettext_method_names(empty_translations): - assert hasattr(null_translations, name), f'NullTranslations does not provide method {name!r}' + assert hasattr(null_translations, name), ( + f'NullTranslations does not provide method {name!r}' + ) -def test_null_translations_method_signature_compatibility(empty_translations, null_translations): +def test_null_translations_method_signature_compatibility( + empty_translations, + null_translations, +): for name in get_gettext_method_names(empty_translations): - assert ( - inspect.getfullargspec(getattr(empty_translations, name)) == - inspect.getfullargspec(getattr(null_translations, name)) - ) + spec1 = inspect.getfullargspec(getattr(empty_translations, name)) + spec2 = inspect.getfullargspec(getattr(null_translations, name)) + assert spec1 == spec2 def test_null_translations_same_return_values(empty_translations, null_translations): From 8acd75bcab6a16e6dec1e370bfa408310b8b99d8 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 31 Jan 2026 13:58:42 +0100 Subject: [PATCH 2/7] Add frozen_time fixture --- tests/conftest.py | 13 ++++++++++ tests/messages/frontend/test_cli.py | 31 ++++++++++-------------- tests/messages/frontend/test_extract.py | 17 +++++-------- tests/messages/frontend/test_init.py | 32 ++++++++++--------------- tests/test_dates.py | 6 ++--- 5 files changed, 45 insertions(+), 54 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index dab67a9a3..4466ed0e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,7 @@ +import datetime + import pytest +from freezegun import freeze_time try: import zoneinfo @@ -37,3 +40,13 @@ def timezone_getter(request): pytest.skip("zoneinfo not available") else: raise NotImplementedError + + +#: Frozen datetime used in various tests. +FROZEN_DATETIME = datetime.datetime(1994, 11, 11, 00, 00) + + +@pytest.fixture() +def frozen_time() -> datetime.datetime: + with freeze_time(FROZEN_DATETIME) as frozen: + yield frozen.time_to_freeze diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index 038a103b4..115194dcd 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -21,7 +21,6 @@ from io import StringIO import pytest -from freezegun import freeze_time from babel import __version__ as VERSION from babel.dates import format_datetime @@ -111,8 +110,7 @@ def test_help(cli): assert all(command in content for command in ('init', 'update', 'compile', 'extract')) -@freeze_time("1994-11-11") -def test_extract_with_default_mapping(cli, pot_file): +def test_extract_with_default_mapping(frozen_time, cli, pot_file): cli.run([ 'pybabel', 'extract', @@ -122,7 +120,7 @@ def test_extract_with_default_mapping(cli, pot_file): '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -165,8 +163,7 @@ def test_extract_with_default_mapping(cli, pot_file): assert expected_content == pot_file.read_text() -@freeze_time("1994-11-11") -def test_extract_with_mapping_file(cli, pot_file): +def test_extract_with_mapping_file(frozen_time, cli, pot_file): cli.run([ 'pybabel', 'extract', @@ -177,7 +174,7 @@ def test_extract_with_mapping_file(cli, pot_file): '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -214,8 +211,7 @@ def test_extract_with_mapping_file(cli, pot_file): assert expected_content == pot_file.read_text() -@freeze_time("1994-11-11") -def test_extract_with_exact_file(cli, pot_file): +def test_extract_with_exact_file(frozen_time, cli, pot_file): """Tests that we can call extract with a particular file and only strings from that file get extracted. (Note the absence of strings from file1.py) """ @@ -230,7 +226,7 @@ def test_extract_with_exact_file(cli, pot_file): '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', '-o', pot_file, file_to_extract, ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -261,8 +257,7 @@ def test_extract_with_exact_file(cli, pot_file): assert expected_content == pot_file.read_text() -@freeze_time("1994-11-11") -def test_init_with_output_dir(cli): +def test_init_with_output_dir(frozen_time, cli): po_file = get_po_file_path('en_US') cli.run([ 'pybabel', @@ -271,7 +266,7 @@ def test_init_with_output_dir(cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -311,8 +306,7 @@ def test_init_with_output_dir(cli): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_init_singular_plural_forms(cli): +def test_init_singular_plural_forms(frozen_time, cli): po_file = get_po_file_path('ja_JP') cli.run([ 'pybabel', @@ -321,7 +315,7 @@ def test_init_singular_plural_forms(cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -360,8 +354,7 @@ def test_init_singular_plural_forms(cli): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_init_more_than_2_plural_forms(cli): +def test_init_more_than_2_plural_forms(frozen_time, cli): po_file = get_po_file_path('lv_LV') cli.run([ 'pybabel', @@ -370,7 +363,7 @@ def test_init_more_than_2_plural_forms(cli): '-d', i18n_dir, '-i', os.path.join(i18n_dir, 'messages.pot'), ]) - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject diff --git a/tests/messages/frontend/test_extract.py b/tests/messages/frontend/test_extract.py index 712200fbb..dd98a24df 100644 --- a/tests/messages/frontend/test_extract.py +++ b/tests/messages/frontend/test_extract.py @@ -13,10 +13,8 @@ from __future__ import annotations import time -from datetime import datetime import pytest -from freezegun import freeze_time from babel import __version__ as VERSION from babel.dates import format_datetime @@ -100,8 +98,7 @@ def test_input_dirs_is_mutually_exclusive_with_input_paths(extract_cmd, pot_file extract_cmd.finalize_options() -@freeze_time("1994-11-11") -def test_extraction_with_default_mapping(extract_cmd, pot_file): +def test_extraction_with_default_mapping(frozen_time, extract_cmd, pot_file): extract_cmd.copyright_holder = 'FooBar, Inc.' extract_cmd.msgid_bugs_address = 'bugs.address@email.tld' extract_cmd.output_file = pot_file @@ -110,7 +107,7 @@ def test_extraction_with_default_mapping(extract_cmd, pot_file): extract_cmd.finalize_options() extract_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -153,8 +150,7 @@ def test_extraction_with_default_mapping(extract_cmd, pot_file): assert expected_content == pot_file.read_text() -@freeze_time("1994-11-11") -def test_extraction_with_mapping_file(extract_cmd, pot_file): +def test_extraction_with_mapping_file(frozen_time, extract_cmd, pot_file): extract_cmd.copyright_holder = 'FooBar, Inc.' extract_cmd.msgid_bugs_address = 'bugs.address@email.tld' extract_cmd.mapping_file = 'mapping.cfg' @@ -164,7 +160,7 @@ def test_extraction_with_mapping_file(extract_cmd, pot_file): extract_cmd.finalize_options() extract_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -201,9 +197,8 @@ def test_extraction_with_mapping_file(extract_cmd, pot_file): assert expected_content == pot_file.read_text() -@freeze_time("1994-11-11") @pytest.mark.parametrize("ignore_pattern", ['**/ignored/**.*', 'ignored']) -def test_extraction_with_mapping_dict(extract_cmd, pot_file, ignore_pattern): +def test_extraction_with_mapping_dict(frozen_time, extract_cmd, pot_file, ignore_pattern): extract_cmd.distribution.message_extractors = { 'project': [ (ignore_pattern, 'ignore', None), @@ -218,7 +213,7 @@ def test_extraction_with_mapping_dict(extract_cmd, pot_file, ignore_pattern): extract_cmd.finalize_options() extract_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject diff --git a/tests/messages/frontend/test_init.py b/tests/messages/frontend/test_init.py index f2db64448..da68b2db6 100644 --- a/tests/messages/frontend/test_init.py +++ b/tests/messages/frontend/test_init.py @@ -14,10 +14,8 @@ import os import shutil -from datetime import datetime import pytest -from freezegun import freeze_time from babel import __version__ as VERSION from babel.dates import format_datetime @@ -59,8 +57,7 @@ def test_no_locale(init_cmd): init_cmd.finalize_options() -@freeze_time("1994-11-11") -def test_with_output_dir(init_cmd): +def test_with_output_dir(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/messages.pot' init_cmd.locale = 'en_US' init_cmd.output_dir = 'project/i18n' @@ -68,7 +65,7 @@ def test_with_output_dir(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -108,8 +105,7 @@ def test_with_output_dir(init_cmd): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_keeps_catalog_non_fuzzy(init_cmd): +def test_keeps_catalog_non_fuzzy(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/messages_non_fuzzy.pot' init_cmd.locale = 'en_US' init_cmd.output_dir = 'project/i18n' @@ -117,7 +113,7 @@ def test_keeps_catalog_non_fuzzy(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -157,8 +153,7 @@ def test_keeps_catalog_non_fuzzy(init_cmd): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_correct_init_more_than_2_plurals(init_cmd): +def test_correct_init_more_than_2_plurals(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/messages.pot' init_cmd.locale = 'lv_LV' init_cmd.output_dir = 'project/i18n' @@ -166,7 +161,7 @@ def test_correct_init_more_than_2_plurals(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -208,8 +203,7 @@ def test_correct_init_more_than_2_plurals(init_cmd): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_correct_init_singular_plural_forms(init_cmd): +def test_correct_init_singular_plural_forms(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/messages.pot' init_cmd.locale = 'ja_JP' init_cmd.output_dir = 'project/i18n' @@ -217,7 +211,7 @@ def test_correct_init_singular_plural_forms(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='ja_JP') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='ja_JP') expected_content = fr"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -256,8 +250,7 @@ def test_correct_init_singular_plural_forms(init_cmd): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_supports_no_wrap(init_cmd): +def test_supports_no_wrap(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/long_messages.pot' init_cmd.locale = 'en_US' init_cmd.output_dir = 'project/i18n' @@ -274,7 +267,7 @@ def test_supports_no_wrap(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -314,8 +307,7 @@ def test_supports_no_wrap(init_cmd): assert expected_content == actual_content -@freeze_time("1994-11-11") -def test_supports_width(init_cmd): +def test_supports_width(frozen_time, init_cmd): init_cmd.input_file = 'project/i18n/long_messages.pot' init_cmd.locale = 'en_US' init_cmd.output_dir = 'project/i18n' @@ -331,7 +323,7 @@ def test_supports_width(init_cmd): init_cmd.finalize_options() init_cmd.run() - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') + date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject diff --git a/tests/test_dates.py b/tests/test_dates.py index 7aaf566c2..f17aec9da 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -760,10 +760,8 @@ def test_zh_TW_format(): assert dates.format_time(datetime(2016, 4, 8, 12, 34, 56), locale='zh_TW') == '中午12:34:56' -def test_format_current_moment(): - frozen_instant = datetime.now(UTC) - with freezegun.freeze_time(time_to_freeze=frozen_instant): - assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_instant, locale="en_US") +def test_format_current_moment(frozen_time): + assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_time, locale="en_US") @pytest.mark.all_locales From 7c27207c66b5bc351be8435a33fcbeaf882f75f6 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 31 Jan 2026 13:51:56 +0100 Subject: [PATCH 3/7] Add tasteful fmt:skips --- tests/messages/frontend/test_cli.py | 46 +++++++++++++----------- tests/messages/frontend/test_frontend.py | 12 +++---- tests/test_core.py | 6 ++-- tests/test_smoke.py | 6 ++-- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index 115194dcd..b8ae5433a 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -115,12 +115,14 @@ def test_extract_with_default_mapping(frozen_time, cli, pot_file): 'pybabel', 'extract', '--copyright-holder', 'FooBar, Inc.', - '--project', 'TestProject', '--version', '0.1', + '--project', 'TestProject', + '--version', '0.1', '--msgid-bugs-address', 'bugs.address@email.tld', - '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', + '-c', 'TRANSLATOR', + '-c', 'TRANSLATORS:', '-o', pot_file, 'project', - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -168,13 +170,15 @@ def test_extract_with_mapping_file(frozen_time, cli, pot_file): 'pybabel', 'extract', '--copyright-holder', 'FooBar, Inc.', - '--project', 'TestProject', '--version', '0.1', + '--project', 'TestProject', + '--version', '0.1', '--msgid-bugs-address', 'bugs.address@email.tld', '--mapping', os.path.join(data_dir, 'mapping.cfg'), - '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', + '-c', 'TRANSLATOR', + '-c', 'TRANSLATORS:', '-o', pot_file, 'project', - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -220,13 +224,15 @@ def test_extract_with_exact_file(frozen_time, cli, pot_file): 'pybabel', 'extract', '--copyright-holder', 'FooBar, Inc.', - '--project', 'TestProject', '--version', '0.1', + '--project', 'TestProject', + '--version', '0.1', '--msgid-bugs-address', 'bugs.address@email.tld', '--mapping', os.path.join(data_dir, 'mapping.cfg'), - '-c', 'TRANSLATOR', '-c', 'TRANSLATORS:', + '-c', 'TRANSLATOR', + '-c', 'TRANSLATORS:', '-o', pot_file, file_to_extract, - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -265,8 +271,8 @@ def test_init_with_output_dir(frozen_time, cli): '--locale', 'en_US', '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -314,8 +320,8 @@ def test_init_singular_plural_forms(frozen_time, cli): '--locale', 'ja_JP', '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -362,8 +368,8 @@ def test_init_more_than_2_plural_forms(frozen_time, cli): '--locale', 'lv_LV', '-d', i18n_dir, '-i', os.path.join(i18n_dir, 'messages.pot'), - ]) - date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + ]) # fmt: skip + date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -449,7 +455,7 @@ def test_compile_catalog_multidomain(cli): '--domain', 'foo bar', '--use-fuzzy', '-d', i18n_dir, - ]) + ]) # fmt: skip for mo_file in [mo_foo, mo_bar]: assert os.path.isfile(mo_file) assert sys.stderr.getvalue() == ( @@ -605,7 +611,7 @@ def test_check_pot_creation_date(cli): '-o', po_file, '-i', tmpl_file, '--ignore-pot-creation-date', - ]) + ]) # fmt: skip def test_update_init_missing(cli): diff --git a/tests/messages/frontend/test_frontend.py b/tests/messages/frontend/test_frontend.py index 17a7dbbd8..e4fc49aea 100644 --- a/tests/messages/frontend/test_frontend.py +++ b/tests/messages/frontend/test_frontend.py @@ -249,12 +249,12 @@ def test_compile_catalog_dir(tmp_path): l_dir.mkdir(parents=True) po_file = l_dir / 'messages.po' po_file.write_text('msgid "foo"\nmsgstr "bar"\n') - cmdinst = configure_cli_command([ # fmt: skip + cmdinst = configure_cli_command([ 'compile', '--statistics', '--use-fuzzy', '-d', str(tmp_path), - ]) + ]) # fmt: skip assert not cmdinst.run() for locale in locales: assert (tmp_path / locale / "LC_MESSAGES" / "messages.mo").exists() @@ -267,14 +267,14 @@ def test_compile_catalog_explicit(tmp_path): po_file = tmp_path / 'temp.po' po_file.write_text('msgid "foo"\nmsgstr "bar"\n') mo_file = tmp_path / 'temp.mo' - cmdinst = configure_cli_command([ # fmt: skip + cmdinst = configure_cli_command([ 'compile', '--statistics', '--use-fuzzy', '-i', str(po_file), '-o', str(mo_file), '-l', 'fi_FI', - ]) + ]) # fmt: skip assert not cmdinst.run() assert mo_file.exists() @@ -297,12 +297,12 @@ def test_update_dir(tmp_path, explicit_locale: bool): l_dir.mkdir(parents=True) po_file = l_dir / 'messages.po' po_file.touch() - cmdinst = configure_cli_command([ # fmt: skip + cmdinst = configure_cli_command([ 'update', '-i', str(tmpl_file), '-d', str(tmp_path), *(['-l', explicit_locale] if explicit_locale else []), - ]) + ]) # fmt: skip assert not cmdinst.run() for locale in locales: if explicit_locale and locale != explicit_locale: diff --git a/tests/test_core.py b/tests/test_core.py index 98c8bfb20..2715afbb7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -23,9 +23,11 @@ def test_locale_provides_access_to_cldr_locale_data(): def test_locale_repr(): + # fmt: off assert repr(Locale('en', 'US')) == "Locale('en', territory='US')" - assert (repr(Locale('de', 'DE')) == "Locale('de', territory='DE')") - assert (repr(Locale('zh', 'CN', script='Hans')) == "Locale('zh', territory='CN', script='Hans')") + assert repr(Locale('de', 'DE')) == "Locale('de', territory='DE')" + assert repr(Locale('zh', 'CN', script='Hans')) == "Locale('zh', territory='CN', script='Hans')" + # fmt: on def test_locale_comparison(): diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 316979885..f8774adc7 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -46,9 +46,9 @@ def test_smoke_numbers(locale): assert numbers.format_decimal(number, locale=locale) assert numbers.format_decimal(number, locale=locale, numbering_system="default") assert numbers.format_currency(number, "EUR", locale=locale) - assert numbers.format_currency(number, "EUR", locale=locale, numbering_system="default") + assert numbers.format_currency(number, "EUR", locale=locale, numbering_system="default") # fmt: skip assert numbers.format_compact_currency(number, "EUR", locale=locale) - assert numbers.format_compact_currency(number, "EUR", locale=locale, numbering_system="default") + assert numbers.format_compact_currency(number, "EUR", locale=locale, numbering_system="default") # fmt: skip assert numbers.format_scientific(number, locale=locale) assert numbers.format_scientific(number, locale=locale, numbering_system="default") assert numbers.format_percent(number / 100, locale=locale) @@ -61,4 +61,4 @@ def test_smoke_units(locale): for unit in ('length-meter', 'mass-kilogram', 'energy-calorie', 'volume-liter'): for number in NUMBERS: assert units.format_unit(number, measurement_unit=unit, locale=locale) - assert units.format_unit(number, measurement_unit=unit, locale=locale, numbering_system="default") + assert units.format_unit(number, measurement_unit=unit, locale=locale, numbering_system="default") # fmt: skip From 1ae1c21e94786f98cb9aa0fee32a76e2c4e6eccc Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 17 Aug 2026 10:11:35 +0300 Subject: [PATCH 4/7] test_cli: add formatted_frozen_time --- tests/messages/frontend/test_cli.py | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index b8ae5433a..24017fb37 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -110,7 +110,7 @@ def test_help(cli): assert all(command in content for command in ('init', 'update', 'compile', 'extract')) -def test_extract_with_default_mapping(frozen_time, cli, pot_file): +def test_extract_with_default_mapping(formatted_frozen_time, cli, pot_file): cli.run([ 'pybabel', 'extract', @@ -122,7 +122,6 @@ def test_extract_with_default_mapping(frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -134,7 +133,7 @@ def test_extract_with_default_mapping(frozen_time, cli, pot_file): msgstr "" "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" -"POT-Creation-Date: {date}\n" +"POT-Creation-Date: {formatted_frozen_time}\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -165,7 +164,13 @@ def test_extract_with_default_mapping(frozen_time, cli, pot_file): assert expected_content == pot_file.read_text() -def test_extract_with_mapping_file(frozen_time, cli, pot_file): +@pytest.fixture() +def formatted_frozen_time(frozen_time) -> str: + """Freeze time and return it formatted for PO use""" + return format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') + + +def test_extract_with_mapping_file(formatted_frozen_time, cli, pot_file): cli.run([ 'pybabel', 'extract', @@ -178,7 +183,6 @@ def test_extract_with_mapping_file(frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -190,7 +194,7 @@ def test_extract_with_mapping_file(frozen_time, cli, pot_file): msgstr "" "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" -"POT-Creation-Date: {date}\n" +"POT-Creation-Date: {formatted_frozen_time}\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -215,7 +219,7 @@ def test_extract_with_mapping_file(frozen_time, cli, pot_file): assert expected_content == pot_file.read_text() -def test_extract_with_exact_file(frozen_time, cli, pot_file): +def test_extract_with_exact_file(formatted_frozen_time, cli, pot_file): """Tests that we can call extract with a particular file and only strings from that file get extracted. (Note the absence of strings from file1.py) """ @@ -232,7 +236,6 @@ def test_extract_with_exact_file(frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, file_to_extract, ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -244,7 +247,7 @@ def test_extract_with_exact_file(frozen_time, cli, pot_file): msgstr "" "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" -"POT-Creation-Date: {date}\n" +"POT-Creation-Date: {formatted_frozen_time}\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -263,7 +266,7 @@ def test_extract_with_exact_file(frozen_time, cli, pot_file): assert expected_content == pot_file.read_text() -def test_init_with_output_dir(frozen_time, cli): +def test_init_with_output_dir(formatted_frozen_time, cli): po_file = get_po_file_path('en_US') cli.run([ 'pybabel', @@ -272,7 +275,6 @@ def test_init_with_output_dir(frozen_time, cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -284,7 +286,7 @@ def test_init_with_output_dir(frozen_time, cli): "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" "POT-Creation-Date: 2007-04-01 15:30+0200\n" -"PO-Revision-Date: {date}\n" +"PO-Revision-Date: {formatted_frozen_time}\n" "Last-Translator: FULL NAME \n" "Language: en_US\n" "Language-Team: en_US \n" @@ -312,7 +314,7 @@ def test_init_with_output_dir(frozen_time, cli): assert expected_content == actual_content -def test_init_singular_plural_forms(frozen_time, cli): +def test_init_singular_plural_forms(formatted_frozen_time, cli): po_file = get_po_file_path('ja_JP') cli.run([ 'pybabel', @@ -321,7 +323,6 @@ def test_init_singular_plural_forms(frozen_time, cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -333,7 +334,7 @@ def test_init_singular_plural_forms(frozen_time, cli): "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" "POT-Creation-Date: 2007-04-01 15:30+0200\n" -"PO-Revision-Date: {date}\n" +"PO-Revision-Date: {formatted_frozen_time}\n" "Last-Translator: FULL NAME \n" "Language: ja_JP\n" "Language-Team: ja_JP \n" @@ -360,7 +361,7 @@ def test_init_singular_plural_forms(frozen_time, cli): assert expected_content == actual_content -def test_init_more_than_2_plural_forms(frozen_time, cli): +def test_init_more_than_2_plural_forms(formatted_frozen_time, cli): po_file = get_po_file_path('lv_LV') cli.run([ 'pybabel', @@ -369,7 +370,6 @@ def test_init_more_than_2_plural_forms(frozen_time, cli): '-d', i18n_dir, '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - date = format_datetime(datetime(1994, 11, 11, 00, 00), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') expected_content = fr"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -381,7 +381,7 @@ def test_init_more_than_2_plural_forms(frozen_time, cli): "Project-Id-Version: TestProject 0.1\n" "Report-Msgid-Bugs-To: bugs.address@email.tld\n" "POT-Creation-Date: 2007-04-01 15:30+0200\n" -"PO-Revision-Date: {date}\n" +"PO-Revision-Date: {formatted_frozen_time}\n" "Last-Translator: FULL NAME \n" "Language: lv_LV\n" "Language-Team: lv_LV \n" From 0baad5db07fd52c8d6ff4bdd1ca519a6bf3122b8 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 17 Aug 2026 10:22:03 +0300 Subject: [PATCH 5/7] Canonicalize raw-formatted string prefix --- tests/messages/frontend/test_cli.py | 12 ++++++------ tests/messages/frontend/test_extract.py | 6 +++--- tests/messages/frontend/test_init.py | 12 ++++++------ tests/messages/test_checkers.py | 8 ++++---- tests/messages/test_extract.py | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index 24017fb37..7cb11c19b 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -122,7 +122,7 @@ def test_extract_with_default_mapping(formatted_frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) # fmt: skip - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -183,7 +183,7 @@ def test_extract_with_mapping_file(formatted_frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, 'project', ]) # fmt: skip - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -236,7 +236,7 @@ def test_extract_with_exact_file(formatted_frozen_time, cli, pot_file): '-c', 'TRANSLATORS:', '-o', pot_file, file_to_extract, ]) # fmt: skip - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -275,7 +275,7 @@ def test_init_with_output_dir(formatted_frozen_time, cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - expected_content = fr"""# English (United States) translations for TestProject. + expected_content = rf"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -323,7 +323,7 @@ def test_init_singular_plural_forms(formatted_frozen_time, cli): '-d', os.path.join(i18n_dir), '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - expected_content = fr"""# Japanese (Japan) translations for TestProject. + expected_content = rf"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -370,7 +370,7 @@ def test_init_more_than_2_plural_forms(formatted_frozen_time, cli): '-d', i18n_dir, '-i', os.path.join(i18n_dir, 'messages.pot'), ]) # fmt: skip - expected_content = fr"""# Latvian (Latvia) translations for TestProject. + expected_content = rf"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. diff --git a/tests/messages/frontend/test_extract.py b/tests/messages/frontend/test_extract.py index dd98a24df..e1a1d707d 100644 --- a/tests/messages/frontend/test_extract.py +++ b/tests/messages/frontend/test_extract.py @@ -108,7 +108,7 @@ def test_extraction_with_default_mapping(frozen_time, extract_cmd, pot_file): extract_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -161,7 +161,7 @@ def test_extraction_with_mapping_file(frozen_time, extract_cmd, pot_file): extract_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -214,7 +214,7 @@ def test_extraction_with_mapping_dict(frozen_time, extract_cmd, pot_file, ignore extract_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# Translations template for TestProject. + expected_content = rf"""# Translations template for TestProject. # Copyright (C) {time.strftime('%Y')} FooBar, Inc. # This file is distributed under the same license as the TestProject # project. diff --git a/tests/messages/frontend/test_init.py b/tests/messages/frontend/test_init.py index da68b2db6..2d9c2d069 100644 --- a/tests/messages/frontend/test_init.py +++ b/tests/messages/frontend/test_init.py @@ -66,7 +66,7 @@ def test_with_output_dir(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# English (United States) translations for TestProject. + expected_content = rf"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -114,7 +114,7 @@ def test_keeps_catalog_non_fuzzy(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# English (United States) translations for TestProject. + expected_content = rf"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -162,7 +162,7 @@ def test_correct_init_more_than_2_plurals(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en') - expected_content = fr"""# Latvian (Latvia) translations for TestProject. + expected_content = rf"""# Latvian (Latvia) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -212,7 +212,7 @@ def test_correct_init_singular_plural_forms(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='ja_JP') - expected_content = fr"""# Japanese (Japan) translations for TestProject. + expected_content = rf"""# Japanese (Japan) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -268,7 +268,7 @@ def test_supports_no_wrap(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') - expected_content = fr"""# English (United States) translations for TestProject. + expected_content = rf"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. @@ -324,7 +324,7 @@ def test_supports_width(frozen_time, init_cmd): init_cmd.run() date = format_datetime(frozen_time, 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale='en_US') - expected_content = fr"""# English (United States) translations for TestProject. + expected_content = rf"""# English (United States) translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. diff --git a/tests/messages/test_checkers.py b/tests/messages/test_checkers.py index 8d4b1a77d..4f800e943 100644 --- a/tests/messages/test_checkers.py +++ b/tests/messages/test_checkers.py @@ -145,7 +145,7 @@ def test_3_num_plurals_checkers(): for _locale in [p for p in PLURALS if PLURALS[p][0] == 3]: plural = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale) english_name = Locale.parse(_locale).english_name - po_file = fr"""\ + po_file = rf"""\ # {english_name} translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -192,7 +192,7 @@ def test_4_num_plurals_checkers(): date = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale) english_name = Locale.parse(_locale).english_name plural = PLURALS[_locale][0] - po_file = fr"""\ + po_file = rf"""\ # {english_name} translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -240,7 +240,7 @@ def test_5_num_plurals_checkers(): date = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale) english_name = Locale.parse(_locale).english_name plural = PLURALS[_locale][0] - po_file = fr"""\ + po_file = rf"""\ # {english_name} translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject @@ -289,7 +289,7 @@ def test_6_num_plurals_checkers(): english_name = Locale.parse(_locale).english_name date = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale) plural = PLURALS[_locale][0] - po_file = fr"""\ + po_file = rf"""\ # {english_name} translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject diff --git a/tests/messages/test_extract.py b/tests/messages/test_extract.py index 41eda8903..1283681d9 100644 --- a/tests/messages/test_extract.py +++ b/tests/messages/test_extract.py @@ -100,7 +100,7 @@ def arbitrary_extractor(fileobj, keywords, comment_tags, options): def test_future(): - buf = BytesIO(br""" + buf = BytesIO(rb""" # -*- coding: utf-8 -*- from __future__ import unicode_literals nbsp = _('\xa0') @@ -111,7 +111,7 @@ def test_future(): def test_f_strings(): - buf = BytesIO(br""" + buf = BytesIO(rb""" t1 = _('foobar') t2 = _(f'spameggs' f'feast') # should be extracted; constant parts only t2 = _(f'spameggs' 'kerroshampurilainen') # should be extracted (mixing f with no f) From 6f1a287ff5cf7e7ac9af186c70ff0a2b7ac175dc Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 17 Aug 2026 10:23:02 +0300 Subject: [PATCH 6/7] Apply minor Ruff-compatible reformatting to tests --- tests/conftest.py | 1 + tests/messages/frontend/test_cli.py | 5 +- tests/messages/test_catalog.py | 53 +++++----- tests/messages/test_checkers.py | 108 ++++++++++++--------- tests/messages/test_extract.py | 16 +-- tests/messages/test_extract_python.py | 38 +++----- tests/messages/test_js_extract.py | 4 +- tests/messages/test_mofile.py | 6 +- tests/messages/test_pofile.py | 47 +++++---- tests/messages/test_setuptools_frontend.py | 13 ++- tests/test_core.py | 79 ++++++++------- tests/test_day_periods.py | 27 +++--- tests/test_lists.py | 28 ++++-- tests/test_localedata.py | 5 +- tests/test_localtime.py | 1 + tests/test_numbers.py | 100 ++++++++++--------- tests/test_numbers_format_decimal.py | 4 +- tests/test_plural.py | 13 +-- tests/test_smoke.py | 1 + tests/test_support_lazy_proxy.py | 23 +++-- tests/test_units.py | 95 ++++++++++-------- 21 files changed, 366 insertions(+), 301 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4466ed0e6..8e429217e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,6 +22,7 @@ def pytest_generate_tests(metafunc): for mark in metafunc.function.pytestmark: if mark.name == "all_locales": from babel.localedata import locale_identifiers + metafunc.parametrize("locale", list(locale_identifiers())) break diff --git a/tests/messages/frontend/test_cli.py b/tests/messages/frontend/test_cli.py index 7cb11c19b..c6ec2ab17 100644 --- a/tests/messages/frontend/test_cli.py +++ b/tests/messages/frontend/test_cli.py @@ -83,7 +83,7 @@ def test_no_duplicated_output_for_multiple_runs(cli): _run_init_catalog(cli) first_output = sys.stderr.getvalue() _run_init_catalog(cli) - second_output = sys.stderr.getvalue()[len(first_output):] + second_output = sys.stderr.getvalue()[len(first_output) :] # in case the log message is not duplicated we should get the same # output as before @@ -459,8 +459,7 @@ def test_compile_catalog_multidomain(cli): for mo_file in [mo_foo, mo_bar]: assert os.path.isfile(mo_file) assert sys.stderr.getvalue() == ( - f'compiling catalog {po_foo} to {mo_foo}\n' - f'compiling catalog {po_bar} to {mo_bar}\n' + f'compiling catalog {po_foo} to {mo_foo}\ncompiling catalog {po_bar} to {mo_bar}\n' ) finally: diff --git a/tests/messages/test_catalog.py b/tests/messages/test_catalog.py index 4a60208c8..7c730d325 100644 --- a/tests/messages/test_catalog.py +++ b/tests/messages/test_catalog.py @@ -303,8 +303,7 @@ def test_catalog_update_po_updates_pot_creation_date(): localized_catalog.locale = 'de_DE' assert template.mime_headers != localized_catalog.mime_headers assert template.creation_date == localized_catalog.creation_date - template.creation_date = datetime.datetime.now() - \ - datetime.timedelta(minutes=5) + template.creation_date = datetime.datetime.now() - datetime.timedelta(minutes=5) localized_catalog.update(template) assert template.creation_date == localized_catalog.creation_date @@ -315,8 +314,7 @@ def test_catalog_update_po_ignores_pot_creation_date(): localized_catalog.locale = 'de_DE' assert template.mime_headers != localized_catalog.mime_headers assert template.creation_date == localized_catalog.creation_date - template.creation_date = datetime.datetime.now() - \ - datetime.timedelta(minutes=5) + template.creation_date = datetime.datetime.now() - datetime.timedelta(minutes=5) localized_catalog.update(template, update_creation_date=False) assert template.creation_date != localized_catalog.creation_date @@ -329,8 +327,7 @@ def test_catalog_update_po_keeps_po_revision_date(): localized_catalog.revision_date = fake_rev_date assert template.mime_headers != localized_catalog.mime_headers assert template.creation_date == localized_catalog.creation_date - template.creation_date = datetime.datetime.now() - \ - datetime.timedelta(minutes=5) + template.creation_date = datetime.datetime.now() - datetime.timedelta(minutes=5) localized_catalog.update(template) assert localized_catalog.revision_date == fake_rev_date @@ -395,8 +392,7 @@ def test_message_python_brace_format_2(): def test_catalog(): - cat = catalog.Catalog(project='Foobar', version='1.0', - copyright_holder='Foo Company') + cat = catalog.Catalog(project='Foobar', version='1.0', copyright_holder='Foo Company') assert cat.header_comment == ( '# Translations template for Foobar.\n' '# Copyright (C) %(year)d Foo Company\n' @@ -405,8 +401,7 @@ def test_catalog(): '# FIRST AUTHOR , %(year)d.\n' '#') % {'year': datetime.date.today().year} - cat = catalog.Catalog(project='Foobar', version='1.0', - copyright_holder='Foo Company') + cat = catalog.Catalog(project='Foobar', version='1.0', copyright_holder='Foo Company') cat.header_comment = ( '# The POT for my really cool PROJECT project.\n' '# Copyright (C) 1990-2003 ORGANIZATION\n' @@ -418,13 +413,13 @@ def test_catalog(): '# Copyright (C) 1990-2003 Foo Company\n' '# This file is distributed under the same license as the Foobar\n' '# project.\n' - '#\n') + '#\n' + ) def test_catalog_mime_headers(): created = datetime.datetime(1990, 4, 1, 15, 30, tzinfo=UTC) - cat = catalog.Catalog(project='Foobar', version='1.0', - creation_date=created) + cat = catalog.Catalog(project='Foobar', version='1.0', creation_date=created) assert cat.mime_headers == [ ('Project-Id-Version', 'Foobar 1.0'), ('Report-Msgid-Bugs-To', 'EMAIL@ADDRESS'), @@ -442,10 +437,15 @@ def test_catalog_mime_headers(): def test_catalog_mime_headers_set_locale(): created = datetime.datetime(1990, 4, 1, 15, 30, tzinfo=UTC) revised = datetime.datetime(1990, 8, 3, 12, 0, tzinfo=UTC) - cat = catalog.Catalog(locale='de_DE', project='Foobar', version='1.0', - creation_date=created, revision_date=revised, - last_translator='John Doe ', - language_team='de_DE ') + cat = catalog.Catalog( + locale='de_DE', + project='Foobar', + version='1.0', + creation_date=created, + revision_date=revised, + last_translator='John Doe ', + language_team='de_DE ', + ) assert cat.mime_headers == [ ('Project-Id-Version', 'Foobar 1.0'), ('Report-Msgid-Bugs-To', 'EMAIL@ADDRESS'), @@ -518,8 +518,7 @@ def test_catalog_update(): cat = catalog.Catalog(locale='de_DE') cat.add('blue', 'blau', locations=[('main.py', 98)]) cat.add('head', 'Kopf', locations=[('util.py', 33)]) - cat.add(('salad', 'salads'), ('Salat', 'Salate'), - locations=[('util.py', 38)]) + cat.add(('salad', 'salads'), ('Salat', 'Salate'), locations=[('util.py', 38)]) cat.update(template) assert len(cat) == 3 @@ -560,24 +559,30 @@ def test_datetime_parsing(): def test_update_catalog_comments(): # Based on https://web.archive.org/web/20100710131029/http://babel.edgewall.org/attachment/ticket/163/cat-update-comments.py - catalog = pofile.read_po(StringIO(''' + catalog = pofile.read_po( + StringIO(''' # A user comment #. An auto comment #: main.py:1 #, fuzzy, python-format msgid "foo %(name)s" msgstr "foo %(name)s" - ''')) + '''), + ) - assert all(message.user_comments and message.auto_comments for message in catalog if message.id) + assert all( + message.user_comments and message.auto_comments for message in catalog if message.id + ) # NOTE: in the POT file, there are no comments - template = pofile.read_po(StringIO(''' + template = pofile.read_po( + StringIO(''' #: main.py:1 #, fuzzy, python-format msgid "bar %(name)s" msgstr "" - ''')) + '''), + ) catalog.update(template) diff --git a/tests/messages/test_checkers.py b/tests/messages/test_checkers.py index 4f800e943..9b0b4b779 100644 --- a/tests/messages/test_checkers.py +++ b/tests/messages/test_checkers.py @@ -334,65 +334,85 @@ def test_6_num_plurals_checkers(): checkers.num_plurals(catalog, message) -@pytest.mark.parametrize(('msgid', 'msgstr'), [ - ('foo %s', 'foo'), - (('foo %s', 'bar'), ('foo', 'bar')), - (('foo', 'bar %s'), ('foo', 'bar')), - (('foo %s', 'bar'), ('foo')), - (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz')), - (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz %d', 'qux')), -]) +@pytest.mark.parametrize( + ('msgid', 'msgstr'), + [ + ('foo %s', 'foo'), + (('foo %s', 'bar'), ('foo', 'bar')), + (('foo', 'bar %s'), ('foo', 'bar')), + (('foo %s', 'bar'), ('foo')), + (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz')), + (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz %d', 'qux')), + ], +) def test_python_format_invalid(msgid, msgstr): msg = Message(msgid, msgstr) with pytest.raises(TranslationError): python_format(None, msg) -@pytest.mark.parametrize(('msgid', 'msgstr'), [ - ('foo', 'foo'), - ('foo', 'foo %s'), - ('foo %s', ''), - (('foo %s', 'bar %d'), ('foo %s', 'bar %d')), - (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz %d')), - (('foo', 'bar %s'), ('foo')), - (('foo', 'bar %s'), ('', '')), - (('foo', 'bar %s'), ('foo', '')), - (('foo %s', 'bar %d'), ('foo %s', '')), -]) +@pytest.mark.parametrize( + ('msgid', 'msgstr'), + [ + ('foo', 'foo'), + ('foo', 'foo %s'), + ('foo %s', ''), + (('foo %s', 'bar %d'), ('foo %s', 'bar %d')), + (('foo %s', 'bar %d'), ('foo %s', 'bar %d', 'baz %d')), + (('foo', 'bar %s'), ('foo')), + (('foo', 'bar %s'), ('', '')), + (('foo', 'bar %s'), ('foo', '')), + (('foo %s', 'bar %d'), ('foo %s', '')), + ], +) def test_python_format_valid(msgid, msgstr): msg = Message(msgid, msgstr) python_format(None, msg) -@pytest.mark.parametrize(('msgid', 'msgstr', 'error'), [ - ('%s %(foo)s', '%s %(foo)s', 'format string mixes positional and named placeholders'), - ('foo %s', 'foo', 'placeholders are incompatible'), - ('%s', '%(foo)s', 'the format strings are of different kinds'), - ('%s', '%s %d', 'positional format placeholders are unbalanced'), - ('%s', '%d', "incompatible format for placeholder 1: 's' and 'd' are not compatible"), - ('%s %s %d', '%s %s %s', "incompatible format for placeholder 3: 'd' and 's' are not compatible"), - ('%(foo)s', '%(bar)s', "unknown named placeholder 'bar'"), - ('%(foo)s', '%(bar)d', "unknown named placeholder 'bar'"), - ('%(foo)s', '%(foo)d', "incompatible format for placeholder 'foo': 'd' and 's' are not compatible"), -]) +@pytest.mark.parametrize( + ('msgid', 'msgstr', 'error'), + [ + ('%s %(foo)s', '%s %(foo)s', 'format string mixes positional and named placeholders'), + ('foo %s', 'foo', 'placeholders are incompatible'), + ('%s', '%(foo)s', 'the format strings are of different kinds'), + ('%s', '%s %d', 'positional format placeholders are unbalanced'), + ('%s', '%d', "incompatible format for placeholder 1: 's' and 'd' are not compatible"), + ( + '%s %s %d', + '%s %s %s', + "incompatible format for placeholder 3: 'd' and 's' are not compatible", + ), + ('%(foo)s', '%(bar)s', "unknown named placeholder 'bar'"), + ('%(foo)s', '%(bar)d', "unknown named placeholder 'bar'"), + ( + '%(foo)s', + '%(foo)d', + "incompatible format for placeholder 'foo': 'd' and 's' are not compatible", + ), + ], +) def test__validate_format_invalid(msgid, msgstr, error): with pytest.raises(TranslationError, match=error): _validate_format(msgid, msgstr) -@pytest.mark.parametrize(('msgid', 'msgstr'), [ - ('foo', 'foo'), - ('foo', 'foo %s'), - ('%s foo', 'foo %s'), - ('%i', '%d'), - ('%d', '%u'), - ('%x', '%X'), - ('%f', '%F'), - ('%F', '%g'), - ('%g', '%G'), - ('%(foo)s', 'foo'), - ('%(foo)s', '%(foo)s %(foo)s'), - ('%(bar)s foo %(n)d', '%(n)d foo %(bar)s'), -]) +@pytest.mark.parametrize( + ('msgid', 'msgstr'), + [ + ('foo', 'foo'), + ('foo', 'foo %s'), + ('%s foo', 'foo %s'), + ('%i', '%d'), + ('%d', '%u'), + ('%x', '%X'), + ('%f', '%F'), + ('%F', '%g'), + ('%g', '%G'), + ('%(foo)s', 'foo'), + ('%(foo)s', '%(foo)s %(foo)s'), + ('%(bar)s foo %(n)d', '%(n)d foo %(bar)s'), + ], +) def test__validate_format_valid(msgid, msgstr): _validate_format(msgid, msgstr) diff --git a/tests/messages/test_extract.py b/tests/messages/test_extract.py index 1283681d9..1a834d1b7 100644 --- a/tests/messages/test_extract.py +++ b/tests/messages/test_extract.py @@ -31,9 +31,7 @@ def test_invalid_filter(): msg9 = dgettext('wiki', model.addPage()) msg10 = dngettext(domain, 'Page', 'Pages', 3) """) - messages = \ - list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], - {})) + messages = list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], {})) assert messages == [ (5, ('bunny', 'bunnies'), [], None), (8, 'Rabbit', [], None), @@ -56,9 +54,7 @@ def test_different_signatures(): n = ngettext() n = ngettext('foo') """) - messages = \ - list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], - {})) + messages = list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], {})) assert len(messages) == 2 assert messages[0][1] == 'foo' assert messages[1][1] == ('hello', 'there') @@ -71,9 +67,7 @@ def test_empty_string_msgid(): stderr = sys.stderr sys.stderr = StringIO() try: - messages = \ - list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, - [], {})) + messages = list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], {})) assert messages == [] assert 'warning: Empty msgid.' in sys.stderr.getvalue() finally: @@ -95,6 +89,7 @@ def test_warn_if_empty_string_msgid_found_in_context_aware_extraction_method(): def test_extract_allows_callable(): def arbitrary_extractor(fileobj, keywords, comment_tags, options): return [(1, None, (), ())] + for x in extract.extract(arbitrary_extractor, BytesIO(b"")): assert x[0] == 1 @@ -105,8 +100,7 @@ def test_future(): from __future__ import unicode_literals nbsp = _('\xa0') """) - messages = list(extract.extract('python', buf, - extract.DEFAULT_KEYWORDS, [], {})) + messages = list(extract.extract('python', buf, extract.DEFAULT_KEYWORDS, [], {})) assert messages[0][1] == '\xa0' diff --git a/tests/messages/test_extract_python.py b/tests/messages/test_extract_python.py index 86f15b1c6..be1cf970f 100644 --- a/tests/messages/test_extract_python.py +++ b/tests/messages/test_extract_python.py @@ -36,9 +36,7 @@ def test_nested_calls(): len(bunnies) ) """) - messages = list(extract.extract_python(buf, - extract.DEFAULT_KEYWORDS.keys(), - [], {})) + messages = list(extract.extract_python(buf, extract.DEFAULT_KEYWORDS.keys(), [], {})) assert messages == [ (1, '_', None, []), (2, 'ungettext', (None, None, None), []), @@ -77,8 +75,7 @@ def test_nested_comments(): 'pylons', # TRANSLATORS: seeing this count) """) - messages = list(extract.extract_python(buf, ('ngettext',), - ['TRANSLATORS:'], {})) + messages = list(extract.extract_python(buf, ('ngettext',), ['TRANSLATORS:'], {})) assert messages == [(1, 'ngettext', ('pylon', 'pylons', None), [])] @@ -120,9 +117,7 @@ def ngettext(y='arg 1',z='arg 2',**pageargs): class Meta: verbose_name = _('log entry') """) - messages = list(extract.extract_python(buf, - extract.DEFAULT_KEYWORDS.keys(), - [], {})) + messages = list(extract.extract_python(buf, extract.DEFAULT_KEYWORDS.keys(), [], {})) assert messages == [ (3, '_', 'Page arg 1', []), (3, '_', 'Page arg 2', []), @@ -160,7 +155,7 @@ def test_dpgettext(): msg = dpgettext('dev', 'Strings', 'pylon', # TRANSLATORS: shouldn't be ) # TRANSLATORS: seeing this """) - messages = list(extract.extract_python(buf, ('dpgettext',),['TRANSLATORS:'], {})) + messages = list(extract.extract_python(buf, ('dpgettext',), ['TRANSLATORS:'], {})) assert messages == [ (1, 'dpgettext', ('dev', 'Strings', 'pylon', None), []), ] @@ -184,8 +179,7 @@ def test_npgettext(): 'pylons', # TRANSLATORS: seeing this count) """) - messages = list(extract.extract_python(buf, ('npgettext',), - ['TRANSLATORS:'], {})) + messages = list(extract.extract_python(buf, ('npgettext',), ['TRANSLATORS:'], {})) assert messages == [ (1, 'npgettext', ('Strings', 'pylon', 'pylons', None), []), ] @@ -209,7 +203,7 @@ def test_dnpgettext(): 'pylons', # TRANSLATORS: seeing this count) """) - messages = list(extract.extract_python(buf, ('dnpgettext',),['TRANSLATORS:'], {})) + messages = list(extract.extract_python(buf, ('dnpgettext',), ['TRANSLATORS:'], {})) assert messages == [ (1, 'dnpgettext', ('dev', 'Strings', 'pylon', 'pylons', None), []), ] @@ -221,9 +215,7 @@ def test_triple_quoted_strings(): msg2 = ngettext(r'''elvis''', \"\"\"elvises\"\"\", count) msg2 = ngettext(\"\"\"elvis\"\"\", 'elvises', count) """) - messages = list(extract.extract_python(buf, - extract.DEFAULT_KEYWORDS.keys(), - [], {})) + messages = list(extract.extract_python(buf, extract.DEFAULT_KEYWORDS.keys(), [], {})) assert messages == [ (1, '_', 'pylons', []), (2, 'ngettext', ('elvis', 'elvises', None), []), @@ -237,9 +229,7 @@ def test_multiline_strings(): support for your Python programs by providing an interface to the GNU gettext message catalog library.''') """) - messages = list(extract.extract_python(buf, - extract.DEFAULT_KEYWORDS.keys(), - [], {})) + messages = list(extract.extract_python(buf, extract.DEFAULT_KEYWORDS.keys(), [], {})) assert messages == [ (1, '_', 'This module provides internationalization and localization\n' @@ -252,9 +242,7 @@ def test_concatenated_strings(): buf = BytesIO(b"""\ foobar = _('foo' 'bar') """) - messages = list(extract.extract_python(buf, - extract.DEFAULT_KEYWORDS.keys(), - [], {})) + messages = list(extract.extract_python(buf, extract.DEFAULT_KEYWORDS.keys(), [], {})) assert messages[0][2] == 'foobar' @@ -320,8 +308,7 @@ def test_multiple_comment_tags(): # NOTE2: A translation comment for tag2 msg = _('Foo Bar2') """) - messages = list(extract.extract_python(buf, ('_',), - ['NOTE1:', 'NOTE2:'], {})) + messages = list(extract.extract_python(buf, ('_',), ['NOTE1:', 'NOTE2:'], {})) assert messages[0][2] == 'Foo Bar1' assert messages[0][3] == ['NOTE1: A translation comment for tag1', 'with a second line'] assert messages[1][2] == 'Foo Bar2' @@ -479,8 +466,9 @@ def test_extract_strip_comment_tags(): # NOTE: This is a multiline comment with # a prefix too _('Babatschi')""") - messages = list(extract.extract('python', buf, comment_tags=['NOTE:', ':'], - strip_comment_tags=True)) + messages = list( + extract.extract('python', buf, comment_tags=['NOTE:', ':'], strip_comment_tags=True), + ) assert messages[0][1] == 'Servus' assert messages[0][2] == ['This is a comment with a very simple', 'prefix specified'] assert messages[1][1] == 'Babatschi' diff --git a/tests/messages/test_js_extract.py b/tests/messages/test_js_extract.py index fc643851e..3ddb79af6 100644 --- a/tests/messages/test_js_extract.py +++ b/tests/messages/test_js_extract.py @@ -33,9 +33,7 @@ def test_various_calls(): msg9 = dgettext('wiki', model.addPage()) msg10 = dngettext(domain, 'Page', 'Pages', 3) """) - messages = \ - list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS, [], - {})) + messages = list(extract.extract('javascript', buf, extract.DEFAULT_KEYWORDS, [], {})) assert messages == [ (5, ('bunny', 'bunnies'), [], None), (8, 'Rabbit', [], None), diff --git a/tests/messages/test_mofile.py b/tests/messages/test_mofile.py index a96f4d830..e572931c2 100644 --- a/tests/messages/test_mofile.py +++ b/tests/messages/test_mofile.py @@ -20,8 +20,7 @@ def test_basics(): - mo_path = os.path.join(data_dir, 'project', 'i18n', 'de', - 'LC_MESSAGES', 'messages.mo') + mo_path = os.path.join(data_dir, 'project', 'i18n', 'de', 'LC_MESSAGES', 'messages.mo') with open(mo_path, 'rb') as mo_file: catalog = mofile.read_mo(mo_file) assert len(catalog) == 2 @@ -39,7 +38,8 @@ def test_sorting(): catalog = Catalog(locale='en_US') catalog.add('', '''\ "Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n''') +"Content-Transfer-Encoding: 8bit\n''', + ) catalog.add('foo', 'Voh') catalog.add(('There is', 'There are'), ('Es gibt', 'Es gibt')) catalog.add('Fizz', '') diff --git a/tests/messages/test_pofile.py b/tests/messages/test_pofile.py index cdbb58262..dcb91313c 100644 --- a/tests/messages/test_pofile.py +++ b/tests/messages/test_pofile.py @@ -67,34 +67,43 @@ def test_extract_locations_valid_location_comment(line, locations): assert locations == _extract_locations(line) -@pytest.mark.parametrize(("line",), [ - ("\u2068file 1.po",), - ("file 1.po\u2069",), - ("\u2069file 1.po\u2068",), - ("\u2068file 1.po:1 \u2068file 2.po\u2069:2",), - ("\u2068file 1.po\u2069:1 file 2.po\u2069:2",), -]) +@pytest.mark.parametrize( + ("line",), + [ + ("\u2068file 1.po",), + ("file 1.po\u2069",), + ("\u2069file 1.po\u2068",), + ("\u2068file 1.po:1 \u2068file 2.po\u2069:2",), + ("\u2068file 1.po\u2069:1 file 2.po\u2069:2",), + ], +) def test_extract_locations_invalid_location_comment(line): with pytest.raises(ValueError): _extract_locations(line) -@pytest.mark.parametrize(("filename",), [ - ("file.po",), - ("file_a.po",), - ("file-a.po",), - ("file\n.po",), - ("\u2068file.po\u2069",), - ("\u2068file a.po\u2069",), -]) +@pytest.mark.parametrize( + ("filename",), + [ + ("file.po",), + ("file_a.po",), + ("file-a.po",), + ("file\n.po",), + ("\u2068file.po\u2069",), + ("\u2068file a.po\u2069",), + ], +) def test_enclose_filename_if_necessary_no_change(filename): assert filename == _enclose_filename_if_necessary(filename) -@pytest.mark.parametrize(("filename",), [ - ("file a.po",), - ("file\ta.po",), -]) +@pytest.mark.parametrize( + ("filename",), + [ + ("file a.po",), + ("file\ta.po",), + ], +) def test_enclose_filename_if_necessary_enclosed(filename): assert "\u2068" + filename + "\u2069" == _enclose_filename_if_necessary(filename) diff --git a/tests/messages/test_setuptools_frontend.py b/tests/messages/test_setuptools_frontend.py index 5c3f4433b..415924f0a 100644 --- a/tests/messages/test_setuptools_frontend.py +++ b/tests/messages/test_setuptools_frontend.py @@ -12,11 +12,14 @@ Distribution = pytest.importorskip("setuptools").Distribution -@pytest.mark.parametrize("kwarg,expected", [ - ("LW_", ("LW_",)), - ("LW_ QQ Q", ("LW_", "QQ", "Q")), - ("yiy aia", ("yiy", "aia")), -]) +@pytest.mark.parametrize( + "kwarg,expected", + [ + ("LW_", ("LW_",)), + ("LW_ QQ Q", ("LW_", "QQ", "Q")), + ("yiy aia", ("yiy", "aia")), + ], +) def test_extract_distutils_keyword_arg_388(kwarg, expected): from babel.messages import frontend, setuptools_frontend diff --git a/tests/test_core.py b/tests/test_core.py index 2715afbb7..117f8697c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -222,14 +222,14 @@ def test_eras_property(self): def test_time_zones_property(self): time_zones = Locale('en', 'US').time_zones - assert (time_zones['Europe/London']['long']['daylight'] == - 'British Summer Time') + assert time_zones['Europe/London']['long']['daylight'] == 'British Summer Time' assert time_zones['America/St_Johns']['city'] == 'St. John\u2019s' def test_meta_zones_property(self): meta_zones = Locale('en', 'US').meta_zones - assert (meta_zones['Europe_Central']['long']['daylight'] == - 'Central European Summer Time') + assert ( + meta_zones['Europe_Central']['long']['daylight'] == 'Central European Summer Time' + ) def test_zone_formats_property(self): assert Locale('en', 'US').zone_formats['fallback'] == '%(1)s (%(0)s)' @@ -285,15 +285,29 @@ def test_default_locale(monkeypatch): def test_default_locale_multiple_args(monkeypatch): - for name in ['LANGUAGE', 'LANG', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC']: + for name in [ + 'LANGUAGE', + 'LANG', + 'LC_ALL', + 'LC_CTYPE', + 'LC_MESSAGES', + 'LC_MONETARY', + 'LC_NUMERIC', + ]: monkeypatch.setenv(name, '') assert default_locale(["", 0, None]) is None monkeypatch.setenv('LANG', 'en_US') - assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'en_US' # No LC_MONETARY or LC_NUMERIC set + + # No LC_MONETARY or LC_NUMERIC set + assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'en_US' + + # LC_NUMERIC set monkeypatch.setenv('LC_NUMERIC', 'fr_FR.UTF-8') - assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'fr_FR' # LC_NUMERIC set + assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'fr_FR' + + # LC_MONETARY set, it takes precedence monkeypatch.setenv('LC_MONETARY', 'fi_FI.UTF-8') - assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'fi_FI' # LC_MONETARY set, it takes precedence + assert default_locale(('LC_MONETARY', 'LC_NUMERIC')) == 'fi_FI' def test_default_locale_bad_arg(): @@ -302,15 +316,11 @@ def test_default_locale_bad_arg(): def test_negotiate_locale(): - assert (core.negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) == - 'de_DE') + assert core.negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) == 'de_DE' assert core.negotiate_locale(['de_DE', 'en_US'], ['en', 'de']) == 'de' - assert (core.negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == - 'de_DE') - assert (core.negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == - 'de_DE') - assert (core.negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) == - 'ja_JP') + assert core.negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == 'de_DE' + assert core.negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) == 'de_DE' + assert core.negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) == 'ja_JP' assert core.negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']) == 'nb_NO' @@ -326,28 +336,29 @@ def test_parse_locale(): assert core.parse_locale('it_IT@something') == ('it', 'IT', None, None, 'something') assert core.parse_locale('en_US.UTF-8') == ('en', 'US', None, None) - assert (core.parse_locale('de_DE.iso885915@euro') == - ('de', 'DE', None, None, 'euro')) + assert core.parse_locale('de_DE.iso885915@euro') == ('de', 'DE', None, None, 'euro') with pytest.raises(ValueError, match="empty"): core.parse_locale("") -@pytest.mark.parametrize('filename', [ - 'babel/global.dat', - 'babel/locale-data/root.dat', - 'babel/locale-data/en.dat', - 'babel/locale-data/en_US.dat', - 'babel/locale-data/en_US_POSIX.dat', - 'babel/locale-data/zh_Hans_CN.dat', - 'babel/locale-data/zh_Hant_TW.dat', - 'babel/locale-data/es_419.dat', -]) +@pytest.mark.parametrize( + 'filename', + [ + 'babel/global.dat', + 'babel/locale-data/root.dat', + 'babel/locale-data/en.dat', + 'babel/locale-data/en_US.dat', + 'babel/locale-data/en_US_POSIX.dat', + 'babel/locale-data/zh_Hans_CN.dat', + 'babel/locale-data/zh_Hant_TW.dat', + 'babel/locale-data/es_419.dat', + ], +) def test_compatible_classes_in_global_and_localedata(filename): import pickle class Unpickler(pickle.Unpickler): - def find_class(self, module, name): # *.dat files must have compatible classes between Python 2 and 3 if module.split('.')[0] == 'babel': @@ -380,11 +391,11 @@ def test_issue_1112(): get imported from `de_AT` to replace the parent's non-alternate spelling. """ assert ( - Locale.parse('de').territories['TR'] == - Locale.parse('de_AT').territories['TR'] == - Locale.parse('de_CH').territories['TR'] == - Locale.parse('de_DE').territories['TR'] == - 'Türkei' + Locale.parse('de').territories['TR'] + == Locale.parse('de_AT').territories['TR'] + == Locale.parse('de_CH').territories['TR'] + == Locale.parse('de_DE').territories['TR'] + == 'Türkei' ) diff --git a/tests/test_day_periods.py b/tests/test_day_periods.py index b3494cd0c..566f8c87b 100644 --- a/tests/test_day_periods.py +++ b/tests/test_day_periods.py @@ -5,17 +5,20 @@ import babel.dates as dates -@pytest.mark.parametrize("locale, time, expected_period_id", [ - ("de", time(7, 42), "morning1"), # (from, before) - ("de", time(3, 11), "night1"), # (after, before) - ("fi", time(0), "midnight"), # (at) - ("en_US", time(12), "noon"), # (at) - ("en_US", time(21), "night1"), # (from, before) across 0:00 - ("en_US", time(5), "morning1"), # (from, before) across 0:00 - ("en_US", time(6), "morning1"), # (from, before) - ("agq", time(10), "am"), # no periods defined - ("agq", time(22), "pm"), # no periods defined - ("am", time(14), "afternoon1"), # (before, after) -]) +@pytest.mark.parametrize( + "locale, time, expected_period_id", + [ + ("de", time(7, 42), "morning1"), # (from, before) + ("de", time(3, 11), "night1"), # (after, before) + ("fi", time(0), "midnight"), # (at) + ("en_US", time(12), "noon"), # (at) + ("en_US", time(21), "night1"), # (from, before) across 0:00 + ("en_US", time(5), "morning1"), # (from, before) across 0:00 + ("en_US", time(6), "morning1"), # (from, before) + ("agq", time(10), "am"), # no periods defined + ("agq", time(22), "pm"), # no periods defined + ("am", time(14), "afternoon1"), # (before, after) + ], +) def test_day_period_rules(locale, time, expected_period_id): assert dates.get_period_id(time, locale=locale) == expected_period_id diff --git a/tests/test_lists.py b/tests/test_lists.py index da96a124c..448366076 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -3,14 +3,21 @@ from babel import lists, units -@pytest.mark.parametrize(('list', 'locale', 'expected'), [ - ([], 'en', ''), - (['string'], 'en', 'string'), - (['string1', 'string2'], 'en', 'string1 and string2'), - (['string1', 'string2', 'string3'], 'en', 'string1, string2, and string3'), - (['string1', 'string2', 'string3'], 'zh', 'string1、string2和string3'), - (['string1', 'string2', 'string3', 'string4'], 'ne', 'string1, string2, string3 र string4'), -]) +@pytest.mark.parametrize( + ('list', 'locale', 'expected'), + [ + ([], 'en', ''), + (['string'], 'en', 'string'), + (['string1', 'string2'], 'en', 'string1 and string2'), + (['string1', 'string2', 'string3'], 'en', 'string1, string2, and string3'), + (['string1', 'string2', 'string3'], 'zh', 'string1、string2和string3'), + ( + ['string1', 'string2', 'string3', 'string4'], + 'ne', + 'string1, string2, string3 र string4', + ), + ], +) def test_format_list(list, locale, expected): assert lists.format_list(list, locale=locale) == expected @@ -25,8 +32,9 @@ def test_issue_1098(): five_inches = units.format_unit(5, "length-inch", length="short", locale="zh_CN") # zh-CN does not specify the "unit" style, so we fall back to "unit-short" style. assert ( - lists.format_list([one_foot, five_inches], style="unit", locale="zh_CN") == - lists.format_list([one_foot, five_inches], style="unit-short", locale="zh_CN") == + lists.format_list([one_foot, five_inches], style="unit", locale="zh_CN") + == lists.format_list([one_foot, five_inches], style="unit-short", locale="zh_CN") + == # Translation verified using Google Translate. It would add more spacing, but the glyphs are correct. "1英尺5英寸" ) diff --git a/tests/test_localedata.py b/tests/test_localedata.py index 1a069067d..70d115bff 100644 --- a/tests/test_localedata.py +++ b/tests/test_localedata.py @@ -115,7 +115,9 @@ def test_no_cross_locale_contamination(reverse): assert cold['he']['months'] != cold['de']['months'] # Sanity check localedata.clear_caches() - warm = {name: _calendar_snapshot(name) for name in (reversed(locales) if reverse else locales)} + warm = { + name: _calendar_snapshot(name) for name in (reversed(locales) if reverse else locales) + } assert warm == cold # Repeated reads in the warmed-up state must stay correct too assert {name: _calendar_snapshot(name) for name in locales} == cold @@ -165,7 +167,6 @@ def test_load_inheritance(monkeypatch): # form used by 'nonLikelyScripts'. This is because 'hi_Latn' has an explicit parent locale 'en_IN'. assert set(localedata._cache) == {'root', 'en', 'en_001', 'en_IN', 'hi_Latn'} - localedata.clear_caches() localedata.load('az_Arab') # Must not include 'az' as 'Arab' is not a likely script for 'az'. diff --git a/tests/test_localtime.py b/tests/test_localtime.py index a7eaba8f7..640576451 100644 --- a/tests/test_localtime.py +++ b/tests/test_localtime.py @@ -40,5 +40,6 @@ def test_issue_990(monkeypatch): fake_readlink = Mock(return_value="/usr/share/zoneinfo////UTC") # Double slash, oops! monkeypatch.setattr(os, "readlink", fake_readlink) from babel.localtime._unix import _get_localzone + assert _get_localzone() is not None fake_readlink.assert_called_with("/etc/localtime") diff --git a/tests/test_numbers.py b/tests/test_numbers.py index 4060da2ae..fd407b268 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -107,13 +107,14 @@ def test_get_territory_currencies(): assert sorted(numbers.get_territory_currencies('US', date(2013, 1, 1), non_tender=True)) == ['USD', 'USN', 'USS'] - assert numbers.get_territory_currencies('US', date(2013, 1, 1), - include_details=True) == [{ - 'currency': 'USD', - 'from': date(1792, 1, 1), - 'to': None, - 'tender': True, - }] + assert numbers.get_territory_currencies('US', date(2013, 1, 1), include_details=True) == [ + { + 'currency': 'USD', + 'from': date(1792, 1, 1), + 'to': None, + 'tender': True, + }, + ] assert numbers.get_territory_currencies('LS', date(2013, 1, 1)) == ['ZAR', 'LSL'] @@ -483,7 +484,7 @@ def test_format_scientific(): def test_default_scientific_format(): - """ Check the scientific format method auto-correct the rendering pattern + """Check the scientific format method auto-correct the rendering pattern in case of a missing fractional part. """ assert numbers.format_scientific(12345, locale='en_US') == '1.2345E4' @@ -492,28 +493,31 @@ def test_default_scientific_format(): assert numbers.format_scientific(12345.678, '#E0', locale='en_US') == '1.2345678E4' -@pytest.mark.parametrize('input_value, expected_value', [ - ('10000', '1E4'), - ('1', '1E0'), - ('1.0', '1E0'), - ('1.1', '1.1E0'), - ('1.11', '1.11E0'), - ('1.110', '1.11E0'), - ('1.001', '1.001E0'), - ('1.00100', '1.001E0'), - ('01.00100', '1.001E0'), - ('101.00100', '1.01001E2'), - ('00000', '0E0'), - ('0', '0E0'), - ('0.0', '0E0'), - ('0.1', '1E-1'), - ('0.11', '1.1E-1'), - ('0.110', '1.1E-1'), - ('0.001', '1E-3'), - ('0.00100', '1E-3'), - ('00.00100', '1E-3'), - ('000.00100', '1E-3'), -]) +@pytest.mark.parametrize( + 'input_value, expected_value', + [ + ('10000', '1E4'), + ('1', '1E0'), + ('1.0', '1E0'), + ('1.1', '1.1E0'), + ('1.11', '1.11E0'), + ('1.110', '1.11E0'), + ('1.001', '1.001E0'), + ('1.00100', '1.001E0'), + ('01.00100', '1.001E0'), + ('101.00100', '1.01001E2'), + ('00000', '0E0'), + ('0', '0E0'), + ('0.0', '0E0'), + ('0.1', '1E-1'), + ('0.11', '1.1E-1'), + ('0.110', '1.1E-1'), + ('0.001', '1E-3'), + ('0.00100', '1E-3'), + ('00.00100', '1E-3'), + ('000.00100', '1E-3'), + ], +) def test_format_scientific_precision(input_value, expected_value): # Test precision conservation. assert numbers.format_scientific( @@ -539,29 +543,36 @@ def test_parse_number(): numbers.parse_number('1.099,98', locale='en', numbering_system="unsupported") -@pytest.mark.parametrize('string', [ - '1 099', - '1\xa0099', - '1\u202f099', -]) +@pytest.mark.parametrize( + 'string', + [ + '1 099', + '1\xa0099', + '1\u202f099', + ], +) def test_parse_number_group_separator_can_be_any_space(string): assert numbers.parse_number(string, locale='fr') == 1099 def test_parse_decimal(): - assert (numbers.parse_decimal('1,099.98', locale='en_US') - == decimal.Decimal('1099.98')) + assert numbers.parse_decimal('1,099.98', locale='en_US') == decimal.Decimal('1099.98') assert numbers.parse_decimal('1.099,98', locale='de') == decimal.Decimal('1099.98') - with pytest.raises(numbers.NumberFormatError, match="'2,109,998' is not a valid decimal number"): + with pytest.raises( + numbers.NumberFormatError, match="'2,109,998' is not a valid decimal number", + ): numbers.parse_decimal('2,109,998', locale='de') -@pytest.mark.parametrize('string', [ - '1 099,98', - '1\xa0099,98', - '1\u202f099,98', -]) +@pytest.mark.parametrize( + 'string', + [ + '1 099,98', + '1\xa0099,98', + '1\u202f099,98', + ], +) def test_parse_decimal_group_separator_can_be_any_space(string): assert decimal.Decimal('1099.98') == numbers.parse_decimal(string, locale='fr') @@ -656,7 +667,8 @@ def test_format_currency_with_none_locale_with_default(monkeypatch): def test_format_currency_with_none_locale(monkeypatch): """Test that the API raises the "Empty locale identifier" error when locale is None, and the default is too.""" - monkeypatch.setattr(numbers, "LC_MONETARY", None) # Pretend we couldn't find any locale when importing the module + # Pretend we couldn't find any locale when importing the module + monkeypatch.setattr(numbers, "LC_MONETARY", None) with pytest.raises(TypeError, match="Empty"): numbers.format_currency(0, "USD", locale=None) diff --git a/tests/test_numbers_format_decimal.py b/tests/test_numbers_format_decimal.py index 356181b69..98660a5ff 100644 --- a/tests/test_numbers_format_decimal.py +++ b/tests/test_numbers_format_decimal.py @@ -27,8 +27,8 @@ def test_patterns(): def test_subpatterns(): - assert numbers.format_decimal((- 12345), '#,##0.##;-#', locale='en_US') == '-12,345' - assert numbers.format_decimal((- 12345), '#,##0.##;(#)', locale='en_US') == '(12,345)' + assert numbers.format_decimal(-12345, '#,##0.##;-#', locale='en_US') == '-12,345' + assert numbers.format_decimal(-12345, '#,##0.##;(#)', locale='en_US') == '(12,345)' def test_default_rounding(): diff --git a/tests/test_plural.py b/tests/test_plural.py index 94d917de4..c7201b209 100644 --- a/tests/test_plural.py +++ b/tests/test_plural.py @@ -116,10 +116,12 @@ def test_plural_within_rules(): assert plural.to_javascript(p) == ( "(function(n) { " "return ((n == 2) || (n == 4) || (n >= 7 && n <= 9))" - " ? 'few' : (n == 1) ? 'one' : 'other'; })") + " ? 'few' : (n == 1) ? 'one' : 'other'; })" + ) assert plural.to_gettext(p) == ( 'nplurals=3; plural=(((n == 2) || (n == 4) || (n >= 7 && n <= 9))' - ' ? 1 : (n == 1) ? 0 : 2);') + ' ? 1 : (n == 1) ? 0 : 2);' + ) assert p(0) == 'other' assert p(1) == 'one' assert p(2) == 'few' @@ -185,12 +187,7 @@ def test_tokenize_well_formed(rule_text, tokens): assert plural.tokenize_rule(rule_text) == tokens -MALFORMED_TOKEN_TESTS = ( - 'a = 1', 'n ! 2', -) - - -@pytest.mark.parametrize('rule_text', MALFORMED_TOKEN_TESTS) +@pytest.mark.parametrize('rule_text', ('a = 1', 'n ! 2')) def test_tokenize_malformed(rule_text): with pytest.raises(plural.RuleError): plural.tokenize_rule(rule_text) diff --git a/tests/test_smoke.py b/tests/test_smoke.py index f8774adc7..fe55a89fc 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -4,6 +4,7 @@ operations don't fail due to odd corner cases on any locale that we ship. """ + import datetime import decimal diff --git a/tests/test_support_lazy_proxy.py b/tests/test_support_lazy_proxy.py index 8445f71fd..59cab1f58 100644 --- a/tests/test_support_lazy_proxy.py +++ b/tests/test_support_lazy_proxy.py @@ -31,10 +31,13 @@ def add_one(): assert proxy.value == 2 -@pytest.mark.parametrize(("copier", "expected_copy_value"), [ - (copy.copy, 2), - (copy.deepcopy, 1), -]) +@pytest.mark.parametrize( + ("copier", "expected_copy_value"), + [ + (copy.copy, 2), + (copy.deepcopy, 1), + ], +) def test_can_copy_proxy(copier, expected_copy_value): numbers = [1, 2] @@ -68,11 +71,13 @@ def greeting(name='world'): assert '(%s)' % lazy_greeting == '(Hello, Joe!)' assert f"[{lazy_greeting}]" == "[Hello, Joe!]" - greetings = sorted([ - support.LazyProxy(greeting, 'world'), - support.LazyProxy(greeting, 'Joe'), - support.LazyProxy(greeting, 'universe'), - ]) + greetings = sorted( + [ + support.LazyProxy(greeting, 'world'), + support.LazyProxy(greeting, 'Joe'), + support.LazyProxy(greeting, 'universe'), + ], + ) assert [str(g) for g in greetings] == [ "Hello, Joe!", "Hello, universe!", diff --git a/tests/test_units.py b/tests/test_units.py index b701beffb..ddcecdc73 100644 --- a/tests/test_units.py +++ b/tests/test_units.py @@ -4,29 +4,35 @@ # New units in CLDR 46 -@pytest.mark.parametrize(('unit', 'count', 'expected'), [ - ('speed-light-speed', 1, '1 světlo'), - ('speed-light-speed', 2, '2 světla'), - ('speed-light-speed', 5, '5 světel'), - ('concentr-part-per-1e9', 1, '1 částice na miliardu'), - ('concentr-part-per-1e9', 2, '2 částice na miliardu'), - ('concentr-part-per-1e9', 5, '5 částic na miliardu'), - ('duration-night', 1, '1 noc'), - ('duration-night', 2, '2 noci'), - ('duration-night', 5, '5 nocí'), -]) +@pytest.mark.parametrize( + ('unit', 'count', 'expected'), + [ + ('speed-light-speed', 1, '1 světlo'), + ('speed-light-speed', 2, '2 světla'), + ('speed-light-speed', 5, '5 světel'), + ('concentr-part-per-1e9', 1, '1 částice na miliardu'), + ('concentr-part-per-1e9', 2, '2 částice na miliardu'), + ('concentr-part-per-1e9', 5, '5 částic na miliardu'), + ('duration-night', 1, '1 noc'), + ('duration-night', 2, '2 noci'), + ('duration-night', 5, '5 nocí'), + ], +) def test_new_cldr46_units(unit, count, expected): assert format_unit(count, unit, locale='cs_CZ') == expected -@pytest.mark.parametrize('count, unit, locale, length, expected', [ - (1, 'duration-month', 'et', 'long', '1 kuu'), - (1, 'duration-minute', 'et', 'narrow', '1 min'), - (2, 'duration-minute', 'et', 'narrow', '2 min'), - (2, 'digital-byte', 'et', 'long', '2 baiti'), - (1, 'duration-day', 'it', 'long', '1 giorno'), - (1, 'duration-day', 'it', 'short', '1 giorno'), -]) +@pytest.mark.parametrize( + 'count, unit, locale, length, expected', + [ + (1, 'duration-month', 'et', 'long', '1 kuu'), + (1, 'duration-minute', 'et', 'narrow', '1 min'), + (2, 'duration-minute', 'et', 'narrow', '2 min'), + (2, 'digital-byte', 'et', 'long', '2 baiti'), + (1, 'duration-day', 'it', 'long', '1 giorno'), + (1, 'duration-day', 'it', 'short', '1 giorno'), + ], +) def test_issue_1217(count, unit, locale, length, expected): assert format_unit(count, unit, length, locale=locale) == expected @@ -37,30 +43,33 @@ def test_deprecated_unit_ids(): format_unit(1, id, locale='en') -@pytest.mark.parametrize('count, unit, locale, length, expected', [ - # Root aliases `duration-*-person` to `duration-*`; - # no locale defines the person variants at all. - # These resolve length-preservingly (person long -> base long), - # matching ICU's `-person`-stripping behavior - # (see `getMeasureData` in https://github.com/unicode-org/icu/blob/main/icu4c/source/i18n/number_longnames.cpp - # and https://unicode-org.atlassian.net/browse/ICU-20400). - # This deliberately bends literal TR35 alias resolution. - # See `parse_unit_patterns` in `scripts/import_cldr.py` for the full story. - (2, 'duration-day-person', 'af', 'long', '2 dae'), - (3, 'duration-day-person', 'fi', 'long', '3 päivää'), - (3, 'duration-day-person', 'fi', 'short', '3 pv'), - (3, 'duration-day-person', 'fi', 'narrow', '3pv'), - (3, 'duration-year-person', 'fi', 'long', '3 vuotta'), - # `fi` defines `energy-foodcalorie` at long and narrow but not short; - # the root alias fills short in from `energy-kilocalorie`. - (3, 'energy-foodcalorie', 'fi', 'short', '3 kcal'), - # `fi` defines `graphics-dot` at short and narrow but not long; - # the root alias fills long in from short. - (3, 'graphics-dot', 'fi', 'long', '3 pistettä'), - # `cs` defines `graphics-dot` itself but not its short forms; - # the root aliases short to `graphics-pixel` short rather than the display name. - (3, 'graphics-dot', 'cs', 'short', '3 px'), -]) +@pytest.mark.parametrize( + 'count, unit, locale, length, expected', + [ + # Root aliases `duration-*-person` to `duration-*`; + # no locale defines the person variants at all. + # These resolve length-preservingly (person long -> base long), + # matching ICU's `-person`-stripping behavior + # (see `getMeasureData` in https://github.com/unicode-org/icu/blob/main/icu4c/source/i18n/number_longnames.cpp + # and https://unicode-org.atlassian.net/browse/ICU-20400). + # This deliberately bends literal TR35 alias resolution. + # See `parse_unit_patterns` in `scripts/import_cldr.py` for the full story. + (2, 'duration-day-person', 'af', 'long', '2 dae'), + (3, 'duration-day-person', 'fi', 'long', '3 päivää'), + (3, 'duration-day-person', 'fi', 'short', '3 pv'), + (3, 'duration-day-person', 'fi', 'narrow', '3pv'), + (3, 'duration-year-person', 'fi', 'long', '3 vuotta'), + # `fi` defines `energy-foodcalorie` at long and narrow but not short; + # the root alias fills short in from `energy-kilocalorie`. + (3, 'energy-foodcalorie', 'fi', 'short', '3 kcal'), + # `fi` defines `graphics-dot` at short and narrow but not long; + # the root alias fills long in from short. + (3, 'graphics-dot', 'fi', 'long', '3 pistettä'), + # `cs` defines `graphics-dot` itself but not its short forms; + # the root aliases short to `graphics-pixel` short rather than the display name. + (3, 'graphics-dot', 'cs', 'short', '3 px'), + ], +) def test_issue_1076_unit_aliases(count, unit, locale, length, expected): assert format_unit(count, unit, length, locale=locale) == expected From 2e215104372674be75656f96a741475f290cb921 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 17 Aug 2026 10:58:28 +0300 Subject: [PATCH 7/7] Fix parametrize argument types (Ruff PT006, PT007) --- pyproject.toml | 2 + tests/messages/frontend/test_frontend.py | 10 ++-- tests/messages/test_js_extract.py | 2 +- tests/messages/test_pofile.py | 58 ++++++++++++---------- tests/messages/test_setuptools_frontend.py | 2 +- tests/test_dates.py | 45 +++++++++-------- tests/test_day_periods.py | 2 +- tests/test_localedata.py | 2 +- tests/test_numbers.py | 52 +++++++++---------- tests/test_plural.py | 8 +-- tests/test_units.py | 4 +- tests/test_util.py | 2 +- 12 files changed, 102 insertions(+), 87 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ace935525..5d5d63e66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,8 @@ select = [ "SIM300", # Yoda conditions "UP", # upgrades "RUF022", # unsorted __all__ + "PT006", + "PT007", ] ignore = [ "C901", # Complexity diff --git a/tests/messages/frontend/test_frontend.py b/tests/messages/frontend/test_frontend.py index e4fc49aea..c8b8d17d5 100644 --- a/tests/messages/frontend/test_frontend.py +++ b/tests/messages/frontend/test_frontend.py @@ -186,8 +186,8 @@ def configure_cli_command(cmdline: str | list[str]): return cmdinst -@pytest.mark.parametrize("split", (False, True)) -@pytest.mark.parametrize("arg_name", ("-k", "--keyword", "--keywords")) +@pytest.mark.parametrize("split", [False, True]) +@pytest.mark.parametrize("arg_name", ["-k", "--keyword", "--keywords"]) def test_extract_keyword_args_384(split, arg_name): # This is a regression test for https://github.com/python-babel/babel/issues/384 # and it also tests that the rest of the forgotten aliases/shorthands implied by @@ -279,7 +279,7 @@ def test_compile_catalog_explicit(tmp_path): assert mo_file.exists() -@pytest.mark.parametrize("explicit_locale", (None, 'fi_FI'), ids=("implicit", "explicit")) +@pytest.mark.parametrize("explicit_locale", [None, 'fi_FI'], ids=("implicit", "explicit")) def test_update_dir(tmp_path, explicit_locale: bool): """ Test that `update` can deal with directories too. @@ -351,7 +351,7 @@ def test_extract_error_code(monkeypatch, capsys): assert "unknown named placeholder 'merkki'" in err -@pytest.mark.parametrize("with_underscore_ignore", (False, True)) +@pytest.mark.parametrize("with_underscore_ignore", [False, True]) def test_extract_ignore_dirs(monkeypatch, capsys, tmp_path, with_underscore_ignore): pot_file = tmp_path / 'temp.pot' monkeypatch.chdir(project_dir) @@ -383,7 +383,7 @@ def test_extract_header_comment(monkeypatch, tmp_path): assert 'Boing' in pot_content -@pytest.mark.parametrize("mapping_format", ("toml", "cfg")) +@pytest.mark.parametrize("mapping_format", ["toml", "cfg"]) def test_pr_1121(tmp_path, monkeypatch, caplog, mapping_format): """ Test that extraction uses the first matching method and options, diff --git a/tests/messages/test_js_extract.py b/tests/messages/test_js_extract.py index 3ddb79af6..af6dfb144 100644 --- a/tests/messages/test_js_extract.py +++ b/tests/messages/test_js_extract.py @@ -115,7 +115,7 @@ class Foo { EXPECTED_JSX_MESSAGES = ["hello", "String1", "String 2", "String 3", "String 4", "String 5"] -@pytest.mark.parametrize("jsx_enabled", (False, True)) +@pytest.mark.parametrize("jsx_enabled", [False, True]) def test_jsx_extraction(jsx_enabled): buf = BytesIO(JSX_SOURCE) messages = [m[2] for m in extract.extract_javascript(buf, ('_', 'gettext'), [], {"jsx": jsx_enabled})] diff --git a/tests/messages/test_pofile.py b/tests/messages/test_pofile.py index dcb91313c..0f4b483cf 100644 --- a/tests/messages/test_pofile.py +++ b/tests/messages/test_pofile.py @@ -54,27 +54,35 @@ def test_denormalize_on_msgstr_without_empty_first_line(): assert expected_denormalized == pofile.denormalize(f'""\n{msgstr}') -@pytest.mark.parametrize(("line", "locations"), [ - ("\u2068file1.po\u2069", ["file1.po"]), - ("file1.po \u2068file 2.po\u2069 file3.po", ["file1.po", "file 2.po", "file3.po"]), - ("file1.po:1 \u2068file 2.po\u2069:2 file3.po:3", ["file1.po:1", "file 2.po:2", "file3.po:3"]), - ("\u2068file1.po\u2069:1 \u2068file\t2.po\u2069:2 file3.po:3", - ["file1.po:1", "file\t2.po:2", "file3.po:3"]), - ("file1.po file2.po", ["file1.po", "file2.po"]), - ("file1.po \u2068\u2069 file2.po", ["file1.po", "file2.po"]), -]) +@pytest.mark.parametrize( + ("line", "locations"), + [ + ("\u2068file1.po\u2069", ["file1.po"]), + ("file1.po \u2068file 2.po\u2069 file3.po", ["file1.po", "file 2.po", "file3.po"]), + ( + "file1.po:1 \u2068file 2.po\u2069:2 file3.po:3", + ["file1.po:1", "file 2.po:2", "file3.po:3"], + ), + ( + "\u2068file1.po\u2069:1 \u2068file\t2.po\u2069:2 file3.po:3", + ["file1.po:1", "file\t2.po:2", "file3.po:3"], + ), + ("file1.po file2.po", ["file1.po", "file2.po"]), + ("file1.po \u2068\u2069 file2.po", ["file1.po", "file2.po"]), + ], +) def test_extract_locations_valid_location_comment(line, locations): assert locations == _extract_locations(line) @pytest.mark.parametrize( - ("line",), + "line", [ - ("\u2068file 1.po",), - ("file 1.po\u2069",), - ("\u2069file 1.po\u2068",), - ("\u2068file 1.po:1 \u2068file 2.po\u2069:2",), - ("\u2068file 1.po\u2069:1 file 2.po\u2069:2",), + "\u2068file 1.po", + "file 1.po\u2069", + "\u2069file 1.po\u2068", + "\u2068file 1.po:1 \u2068file 2.po\u2069:2", + "\u2068file 1.po\u2069:1 file 2.po\u2069:2", ], ) def test_extract_locations_invalid_location_comment(line): @@ -83,14 +91,14 @@ def test_extract_locations_invalid_location_comment(line): @pytest.mark.parametrize( - ("filename",), + "filename", [ - ("file.po",), - ("file_a.po",), - ("file-a.po",), - ("file\n.po",), - ("\u2068file.po\u2069",), - ("\u2068file a.po\u2069",), + "file.po", + "file_a.po", + "file-a.po", + "file\n.po", + "\u2068file.po\u2069", + "\u2068file a.po\u2069", ], ) def test_enclose_filename_if_necessary_no_change(filename): @@ -98,10 +106,10 @@ def test_enclose_filename_if_necessary_no_change(filename): @pytest.mark.parametrize( - ("filename",), + "filename", [ - ("file a.po",), - ("file\ta.po",), + "file a.po", + "file\ta.po", ], ) def test_enclose_filename_if_necessary_enclosed(filename): diff --git a/tests/messages/test_setuptools_frontend.py b/tests/messages/test_setuptools_frontend.py index 415924f0a..d74c8e7a7 100644 --- a/tests/messages/test_setuptools_frontend.py +++ b/tests/messages/test_setuptools_frontend.py @@ -13,7 +13,7 @@ @pytest.mark.parametrize( - "kwarg,expected", + ("kwarg", "expected"), [ ("LW_", ("LW_",)), ("LW_ QQ Q", ("LW_", "QQ", "Q")), diff --git a/tests/test_dates.py b/tests/test_dates.py index f17aec9da..2eacdb312 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -483,7 +483,7 @@ def test_get_timezone_location(timezone_getter): @pytest.mark.parametrize( - "tzname, params, expected", + ('tzname', 'params', 'expected'), [ ("America/Los_Angeles", {"locale": "en_US"}, "Pacific Time"), ("America/Los_Angeles", {"width": "short", "locale": "en_US"}, "PT"), @@ -530,7 +530,7 @@ def test_get_timezone_name_tzinfo(timezone_getter, tzname, params, expected): @pytest.mark.parametrize("timezone_getter", ["pytz.timezone"], indirect=True) @pytest.mark.parametrize( - "tzname, params, expected", + ('tzname', 'params', 'expected'), [ ("America/Los_Angeles", {"locale": "en_US"}, "Pacific Standard Time"), ( @@ -670,25 +670,28 @@ def test_parse_date_custom_format(): assert dates.parse_date('2024-04-01', format='yyyy.dd.mm') == date(2024, 1, 4) -@pytest.mark.parametrize('input, expected', [ - # base case, fully qualified time - ('15:30:00', time(15, 30)), - # test digits - ('15:30', time(15, 30)), - ('3:30', time(3, 30)), - ('00:30', time(0, 30)), - # test am parsing - ('03:30 am', time(3, 30)), - ('3:30:21 am', time(3, 30, 21)), - ('3:30 am', time(3, 30)), - # test pm parsing - ('03:30 pm', time(15, 30)), - ('03:30 pM', time(15, 30)), - ('03:30 Pm', time(15, 30)), - ('03:30 PM', time(15, 30)), - # test hour-only parsing - ('4 pm', time(16, 0)), -]) +@pytest.mark.parametrize( + ('input', 'expected'), + [ + # base case, fully qualified time + ('15:30:00', time(15, 30)), + # test digits + ('15:30', time(15, 30)), + ('3:30', time(3, 30)), + ('00:30', time(0, 30)), + # test am parsing + ('03:30 am', time(3, 30)), + ('3:30:21 am', time(3, 30, 21)), + ('3:30 am', time(3, 30)), + # test pm parsing + ('03:30 pm', time(15, 30)), + ('03:30 pM', time(15, 30)), + ('03:30 Pm', time(15, 30)), + ('03:30 PM', time(15, 30)), + # test hour-only parsing + ('4 pm', time(16, 0)), + ], +) def test_parse_time(input, expected): assert dates.parse_time(input, locale='en_US') == expected diff --git a/tests/test_day_periods.py b/tests/test_day_periods.py index 566f8c87b..209e755f1 100644 --- a/tests/test_day_periods.py +++ b/tests/test_day_periods.py @@ -6,7 +6,7 @@ @pytest.mark.parametrize( - "locale, time, expected_period_id", + ("locale", "time", "expected_period_id"), [ ("de", time(7, 42), "morning1"), # (from, before) ("de", time(3, 11), "night1"), # (after, before) diff --git a/tests/test_localedata.py b/tests/test_localedata.py index 70d115bff..c9046da03 100644 --- a/tests/test_localedata.py +++ b/tests/test_localedata.py @@ -95,7 +95,7 @@ def _calendar_snapshot(name): } -@pytest.mark.parametrize('reverse', (False, True)) +@pytest.mark.parametrize('reverse', [False, True]) def test_no_cross_locale_contamination(reverse): """ Alias-heavy calendar data (e.g. what `format_date(..., 'LLLL')` reads) diff --git a/tests/test_numbers.py b/tests/test_numbers.py index fd407b268..d20a18315 100644 --- a/tests/test_numbers.py +++ b/tests/test_numbers.py @@ -213,7 +213,7 @@ def test_format_decimal(): numbers.format_decimal(12345.5, locale='en_US', numbering_system="unknown") -@pytest.mark.parametrize('input_value, expected_value', [ +@pytest.mark.parametrize(('input_value', 'expected_value'), [ ('10000', '10,000'), ('1', '1'), ('1.0', '1'), @@ -329,7 +329,7 @@ def test_format_compact_currency_invalid_format_type(): numbers.format_compact_currency(1099.98, 'USD', locale='en_US', format_type='unknown') -@pytest.mark.parametrize('input_value, expected_value', [ +@pytest.mark.parametrize(('input_value', 'expected_value'), [ ('10000', '$10,000.00'), ('1', '$1.00'), ('1.0', '$1.00'), @@ -434,28 +434,30 @@ def test_format_percent(): assert numbers.format_percent(134.5, locale='ar_EG', numbering_system="default") == '13٬450%' - -@pytest.mark.parametrize('input_value, expected_value', [ - ('100', '10,000%'), - ('0.01', '1%'), - ('0.010', '1%'), - ('0.011', '1.1%'), - ('0.0111', '1.11%'), - ('0.01110', '1.11%'), - ('0.01001', '1.001%'), - ('0.0100100', '1.001%'), - ('0.010100100', '1.01001%'), - ('0.000000', '0%'), - ('0', '0%'), - ('0.00', '0%'), - ('0.01', '1%'), - ('0.011', '1.1%'), - ('0.0110', '1.1%'), - ('0.0001', '0.01%'), - ('0.000100', '0.01%'), - ('0.0000100', '0.001%'), - ('0.00000100', '0.0001%'), -]) +@pytest.mark.parametrize( + ('input_value', 'expected_value'), + [ + ('100', '10,000%'), + ('0.01', '1%'), + ('0.010', '1%'), + ('0.011', '1.1%'), + ('0.0111', '1.11%'), + ('0.01110', '1.11%'), + ('0.01001', '1.001%'), + ('0.0100100', '1.001%'), + ('0.010100100', '1.01001%'), + ('0.000000', '0%'), + ('0', '0%'), + ('0.00', '0%'), + ('0.01', '1%'), + ('0.011', '1.1%'), + ('0.0110', '1.1%'), + ('0.0001', '0.01%'), + ('0.000100', '0.01%'), + ('0.0000100', '0.001%'), + ('0.00000100', '0.0001%'), + ], +) def test_format_percent_precision(input_value, expected_value): # Test precision conservation. assert numbers.format_percent( @@ -494,7 +496,7 @@ def test_default_scientific_format(): @pytest.mark.parametrize( - 'input_value, expected_value', + ('input_value', 'expected_value'), [ ('10000', '1E4'), ('1', '1E0'), diff --git a/tests/test_plural.py b/tests/test_plural.py index c7201b209..4da07429d 100644 --- a/tests/test_plural.py +++ b/tests/test_plural.py @@ -182,12 +182,12 @@ def test_locales_with_no_plural_rules_have_default(): ) -@pytest.mark.parametrize('rule_text,tokens', WELL_FORMED_TOKEN_TESTS) +@pytest.mark.parametrize(("rule_text", "tokens"), WELL_FORMED_TOKEN_TESTS) def test_tokenize_well_formed(rule_text, tokens): assert plural.tokenize_rule(rule_text) == tokens -@pytest.mark.parametrize('rule_text', ('a = 1', 'n ! 2')) +@pytest.mark.parametrize('rule_text', ['a = 1', 'n ! 2']) def test_tokenize_malformed(rule_text): with pytest.raises(plural.RuleError): plural.tokenize_rule(rule_text) @@ -226,7 +226,7 @@ def test_next_token_type_not_ok_and_value_ok(): ) -@pytest.mark.parametrize('source,n,i,v,w,f,t', EXTRACT_OPERANDS_TESTS) +@pytest.mark.parametrize(("source", "n", "i", "v", "w", "f", "t"), EXTRACT_OPERANDS_TESTS) def test_extract_operands(source, n, i, v, w, f, t): e_n, e_i, e_v, e_w, e_f, e_t, e_c, e_e = plural.extract_operands(source) assert abs(e_n - decimal.Decimal(n)) <= EPSILON # float-decimal conversion inaccuracy @@ -239,7 +239,7 @@ def test_extract_operands(source, n, i, v, w, f, t): assert not e_e # Not supported at present -@pytest.mark.parametrize('locale', ('ru', 'pl')) +@pytest.mark.parametrize('locale', ['ru', 'pl']) def test_gettext_compilation(locale): # Test that new plural form elements introduced in recent CLDR versions # are compiled "down" to `n` when emitting Gettext rules. diff --git a/tests/test_units.py b/tests/test_units.py index ddcecdc73..dcb938775 100644 --- a/tests/test_units.py +++ b/tests/test_units.py @@ -23,7 +23,7 @@ def test_new_cldr46_units(unit, count, expected): @pytest.mark.parametrize( - 'count, unit, locale, length, expected', + ('count', 'unit', 'locale', 'length', 'expected'), [ (1, 'duration-month', 'et', 'long', '1 kuu'), (1, 'duration-minute', 'et', 'narrow', '1 min'), @@ -44,7 +44,7 @@ def test_deprecated_unit_ids(): @pytest.mark.parametrize( - 'count, unit, locale, length, expected', + ('count', 'unit', 'locale', 'length', 'expected'), [ # Root aliases `duration-*-person` to `duration-*`; # no locale defines the person variants at all. diff --git a/tests/test_util.py b/tests/test_util.py index a153dd6ff..f51626e25 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -73,7 +73,7 @@ def test_parse_encoding_non_ascii(): assert parse_encoding('K\xf6ln') is None -@pytest.mark.parametrize('source, result', [ +@pytest.mark.parametrize(('source', 'result'), [ (''' from __future__ import print_function, division, with_statement,