From b0f80b50a467821915e53671eed818384ae74342 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Mon, 30 Sep 2024 22:41:26 +0200 Subject: [PATCH 01/10] Move test_argparse.py into a folder --- Lib/test/test_argparse/__init__.py | 7 +++++++ Lib/test/{ => test_argparse}/test_argparse.py | 0 2 files changed, 7 insertions(+) create mode 100644 Lib/test/test_argparse/__init__.py rename Lib/test/{ => test_argparse}/test_argparse.py (100%) diff --git a/Lib/test/test_argparse/__init__.py b/Lib/test/test_argparse/__init__.py new file mode 100644 index 00000000000000..9a89d27ba9f979 --- /dev/null +++ b/Lib/test/test_argparse/__init__.py @@ -0,0 +1,7 @@ +import os + +from test import support + + +def load_tests(*args): + return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse/test_argparse.py similarity index 100% rename from Lib/test/test_argparse.py rename to Lib/test/test_argparse/test_argparse.py From 97537cad521de0e0393bba6b93c160199f69d81a Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Mon, 30 Sep 2024 22:42:49 +0200 Subject: [PATCH 02/10] Add translation tests --- Lib/test/test_argparse/data/msgids.txt | 40 ++++++++++++++ Lib/test/test_argparse/test_translations.py | 60 +++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 Lib/test/test_argparse/data/msgids.txt create mode 100644 Lib/test/test_argparse/test_translations.py diff --git a/Lib/test/test_argparse/data/msgids.txt b/Lib/test/test_argparse/data/msgids.txt new file mode 100644 index 00000000000000..97416aad520271 --- /dev/null +++ b/Lib/test/test_argparse/data/msgids.txt @@ -0,0 +1,40 @@ + (default: %(default)s) +%(heading)s: +%(prog)s: error: %(message)s\n +%(prog)s: warning: %(message)s\n +%r is not callable +'required' is an invalid argument for positionals +.__call__() not defined +ambiguous option: %(option)s could match %(matches)s +argument "-" with mode %r +argument %(argument_name)s: %(message)s +argument '%(argument_name)s' is deprecated +can't open '%(filename)s': %(error)s +cannot have multiple subparser arguments +cannot merge actions - two groups are named %r +command '%(parser_name)s' is deprecated +conflicting subparser alias: %s +conflicting subparser: %s +dest= is required for options like %r +expected at least one argument +expected at most one argument +expected one argument +ignored explicit argument %r +invalid %(type)s value: %(value)r +invalid choice: %(value)r (choose from %(choices)s) +invalid conflict_resolution value: %r +invalid option string %(option)r: must start with a character %(prefix_chars)r +mutually exclusive arguments must be optional +not allowed with argument %s +one of the arguments %s is required +option '%(option)s' is deprecated +options +positional arguments +show program's version number and exit +show this help message and exit +subcommands +the following arguments are required: %s +unexpected option string: %s +unknown parser %(parser_name)r (choices: %(choices)s) +unrecognized arguments: %s +usage: \ No newline at end of file diff --git a/Lib/test/test_argparse/test_translations.py b/Lib/test/test_argparse/test_translations.py new file mode 100644 index 00000000000000..2124d289390f88 --- /dev/null +++ b/Lib/test/test_argparse/test_translations.py @@ -0,0 +1,60 @@ +import argparse +import re +import subprocess +import sys +import unittest + +from pathlib import Path + + +i18n_tools = Path(__file__).parents[3] / 'Tools' / 'i18n' +pygettext = i18n_tools / 'pygettext.py' +snapshot_path = Path(__file__).parents[0] / 'data' / 'msgids.txt' + +msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', re.DOTALL) +msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') + + +class TestTranslations(unittest.TestCase): + + def test_translations(self): + # Test messages extracted from the argparse module against a snapshot + res = generate_po_file(stdout_only=False) + self.assertEqual(res.returncode, 0) + self.assertEqual(res.stderr, '') + msgids = extract_msgids(res.stdout) + snapshot = snapshot_path.read_text().splitlines() + self.assertListEqual(msgids, snapshot) + + +def generate_po_file(*, stdout_only=True): + res = subprocess.run([sys.executable, pygettext, + '--no-location', '-o', '-', argparse.__file__], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if stdout_only: + return res.stdout + return res + + +def extract_msgids(po): + msgids = [] + for msgid in msgid_pattern.findall(po): + msgid_string = ''.join(msgid_string_pattern.findall(msgid)) + msgid_string = msgid_string.replace(r'\"', '"') + if msgid_string: + msgids.append(msgid_string) + return sorted(msgids) + + +def update_translation_snapshots(): + contents = generate_po_file() + msgids = extract_msgids(contents) + snapshot_path.write_text('\n'.join(msgids)) + + +if __name__ == '__main__': + # To regenerate translation snapshots + if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': + update_translation_snapshots() + sys.exit(0) + unittest.main() From 7885932b11cdff51e457d277e61bc9c513838e9e Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Mon, 30 Sep 2024 22:52:50 +0200 Subject: [PATCH 03/10] Add news entry --- .../next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst diff --git a/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst b/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst new file mode 100644 index 00000000000000..8dac814cd7e671 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst @@ -0,0 +1 @@ +Add translation tests to the mod:`argparse` module. From 136af62bc00f163a421815201065de99a8e77b6e Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Tue, 15 Oct 2024 23:29:34 +0200 Subject: [PATCH 04/10] Fix news entry typo --- .../next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst b/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst index 8dac814cd7e671..3c2455cfc8c530 100644 --- a/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst +++ b/Misc/NEWS.d/next/Tests/2024-09-30-22-52-44.gh-issue-124295.VZy5kx.rst @@ -1 +1 @@ -Add translation tests to the mod:`argparse` module. +Add translation tests to the :mod:`argparse` module. From f5decd842a9fd5dd05af0336ed6d11f0de1285ef Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Oct 2024 17:48:45 +0200 Subject: [PATCH 05/10] Add test folders to Makefile --- Makefile.pre.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index d6f75a931a3db2..8d3898a7b818a6 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2418,6 +2418,8 @@ LIBSUBDIRS= asyncio \ __phello__ TESTSUBDIRS= idlelib/idle_test \ test \ + test/test_argparse \ + test/test_argparse/data \ test/test_ast \ test/test_ast/data \ test/archivetestdata \ From 80a4bada32bb295a8833d2f816be5af4d04f4704 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Oct 2024 17:52:04 +0200 Subject: [PATCH 06/10] Update snapshots --- Lib/test/test_argparse/data/msgids.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_argparse/data/msgids.txt b/Lib/test/test_argparse/data/msgids.txt index 97416aad520271..f4d0a05635cfc1 100644 --- a/Lib/test/test_argparse/data/msgids.txt +++ b/Lib/test/test_argparse/data/msgids.txt @@ -22,6 +22,7 @@ expected one argument ignored explicit argument %r invalid %(type)s value: %(value)r invalid choice: %(value)r (choose from %(choices)s) +invalid choice: %(value)r, maybe you meant %(closest)r? (choose from %(choices)s) invalid conflict_resolution value: %r invalid option string %(option)r: must start with a character %(prefix_chars)r mutually exclusive arguments must be optional From da3986cbdbbaa466bbd1f76f9a12b64692e312cb Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sat, 26 Oct 2024 20:13:27 +0200 Subject: [PATCH 07/10] Skip on platforms without subprocess support --- Lib/test/test_argparse/test_translations.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_argparse/test_translations.py b/Lib/test/test_argparse/test_translations.py index 2124d289390f88..d3838f4b6dd8e5 100644 --- a/Lib/test/test_argparse/test_translations.py +++ b/Lib/test/test_argparse/test_translations.py @@ -5,8 +5,12 @@ import unittest from pathlib import Path +from test import support +if not support.has_subprocess_support: + raise unittest.SkipTest("test module requires subprocess") + i18n_tools = Path(__file__).parents[3] / 'Tools' / 'i18n' pygettext = i18n_tools / 'pygettext.py' snapshot_path = Path(__file__).parents[0] / 'data' / 'msgids.txt' From a9349a5c2a552a62014ea656565614cf8c08747d Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 27 Oct 2024 13:56:39 +0100 Subject: [PATCH 08/10] Create a new global folder for all translation data. This avoids having to turn tests into packages. --- Lib/test/{test_argparse => }/test_argparse.py | 57 +++++++++++++++++ Lib/test/test_argparse/__init__.py | 7 -- Lib/test/test_argparse/test_translations.py | 64 ------------------- .../argparse}/msgids.txt | 0 Makefile.pre.in | 4 +- 5 files changed, 59 insertions(+), 73 deletions(-) rename Lib/test/{test_argparse => }/test_argparse.py (99%) delete mode 100644 Lib/test/test_argparse/__init__.py delete mode 100644 Lib/test/test_argparse/test_translations.py rename Lib/test/{test_argparse/data => translationdata/argparse}/msgids.txt (100%) diff --git a/Lib/test/test_argparse/test_argparse.py b/Lib/test/test_argparse.py similarity index 99% rename from Lib/test/test_argparse/test_argparse.py rename to Lib/test/test_argparse.py index ed1c5c34e526aa..e3070216aefd7b 100644 --- a/Lib/test/test_argparse/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -7,8 +7,10 @@ import operator import os import py_compile +import re import shutil import stat +import subprocess import sys import textwrap import tempfile @@ -17,7 +19,9 @@ import warnings from enum import StrEnum +from pathlib import Path from test.support import captured_stderr +from test.support import has_subprocess_support from test.support import import_helper from test.support import os_helper from test.support import script_helper @@ -7014,6 +7018,55 @@ def test_directory_in_zipfile(self, compiled=False): def test_directory_in_zipfile_compiled(self): self.test_directory_in_zipfile(compiled=True) +# ================= +# Translation tests +# ================= + +i18n_tools = Path(__file__).parents[2] / 'Tools' / 'i18n' +pygettext = i18n_tools / 'pygettext.py' +snapshot_path = Path(__file__).parent / 'translationdata' / 'argparse' / 'msgids.txt' + +msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', re.DOTALL) +msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') + + +@unittest.skipIf(not has_subprocess_support, "test requires subprocess") +class TestTranslations(unittest.TestCase): + + def test_translations(self): + # Test messages extracted from the argparse module against a snapshot + res = generate_po_file(stdout_only=False) + self.assertEqual(res.returncode, 0) + self.assertEqual(res.stderr, '') + msgids = extract_msgids(res.stdout) + snapshot = snapshot_path.read_text().splitlines() + self.assertListEqual(msgids, snapshot) + + +def generate_po_file(*, stdout_only=True): + res = subprocess.run([sys.executable, pygettext, + '--no-location', '-o', '-', argparse.__file__], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if stdout_only: + return res.stdout + return res + + +def extract_msgids(po): + msgids = [] + for msgid in msgid_pattern.findall(po): + msgid_string = ''.join(msgid_string_pattern.findall(msgid)) + msgid_string = msgid_string.replace(r'\"', '"') + if msgid_string: + msgids.append(msgid_string) + return sorted(msgids) + + +def update_translation_snapshots(): + contents = generate_po_file() + msgids = extract_msgids(contents) + snapshot_path.write_text('\n'.join(msgids)) + def tearDownModule(): # Remove global references to avoid looking like we have refleaks. @@ -7022,4 +7075,8 @@ def tearDownModule(): if __name__ == '__main__': + # To regenerate translation snapshots + if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': + update_translation_snapshots() + sys.exit(0) unittest.main() diff --git a/Lib/test/test_argparse/__init__.py b/Lib/test/test_argparse/__init__.py deleted file mode 100644 index 9a89d27ba9f979..00000000000000 --- a/Lib/test/test_argparse/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -import os - -from test import support - - -def load_tests(*args): - return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_argparse/test_translations.py b/Lib/test/test_argparse/test_translations.py deleted file mode 100644 index d3838f4b6dd8e5..00000000000000 --- a/Lib/test/test_argparse/test_translations.py +++ /dev/null @@ -1,64 +0,0 @@ -import argparse -import re -import subprocess -import sys -import unittest - -from pathlib import Path -from test import support - - -if not support.has_subprocess_support: - raise unittest.SkipTest("test module requires subprocess") - -i18n_tools = Path(__file__).parents[3] / 'Tools' / 'i18n' -pygettext = i18n_tools / 'pygettext.py' -snapshot_path = Path(__file__).parents[0] / 'data' / 'msgids.txt' - -msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', re.DOTALL) -msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') - - -class TestTranslations(unittest.TestCase): - - def test_translations(self): - # Test messages extracted from the argparse module against a snapshot - res = generate_po_file(stdout_only=False) - self.assertEqual(res.returncode, 0) - self.assertEqual(res.stderr, '') - msgids = extract_msgids(res.stdout) - snapshot = snapshot_path.read_text().splitlines() - self.assertListEqual(msgids, snapshot) - - -def generate_po_file(*, stdout_only=True): - res = subprocess.run([sys.executable, pygettext, - '--no-location', '-o', '-', argparse.__file__], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if stdout_only: - return res.stdout - return res - - -def extract_msgids(po): - msgids = [] - for msgid in msgid_pattern.findall(po): - msgid_string = ''.join(msgid_string_pattern.findall(msgid)) - msgid_string = msgid_string.replace(r'\"', '"') - if msgid_string: - msgids.append(msgid_string) - return sorted(msgids) - - -def update_translation_snapshots(): - contents = generate_po_file() - msgids = extract_msgids(contents) - snapshot_path.write_text('\n'.join(msgids)) - - -if __name__ == '__main__': - # To regenerate translation snapshots - if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': - update_translation_snapshots() - sys.exit(0) - unittest.main() diff --git a/Lib/test/test_argparse/data/msgids.txt b/Lib/test/translationdata/argparse/msgids.txt similarity index 100% rename from Lib/test/test_argparse/data/msgids.txt rename to Lib/test/translationdata/argparse/msgids.txt diff --git a/Makefile.pre.in b/Makefile.pre.in index 8d3898a7b818a6..751e7ab8b427f5 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2418,8 +2418,6 @@ LIBSUBDIRS= asyncio \ __phello__ TESTSUBDIRS= idlelib/idle_test \ test \ - test/test_argparse \ - test/test_argparse/data \ test/test_ast \ test/test_ast/data \ test/archivetestdata \ @@ -2550,6 +2548,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/tkinterdata \ test/tokenizedata \ test/tracedmodules \ + test/translationdata \ + test/translationdata/argparse \ test/typinganndata \ test/wheeldata \ test/xmltestdata \ From 28ae5f2fbfd92442dedc94544c32d3414834d858 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 27 Oct 2024 15:28:10 +0100 Subject: [PATCH 09/10] Use `requires_subprocess` --- Lib/test/test_argparse.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index e3070216aefd7b..662673091a6ae0 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -21,9 +21,9 @@ from enum import StrEnum from pathlib import Path from test.support import captured_stderr -from test.support import has_subprocess_support from test.support import import_helper from test.support import os_helper +from test.support import requires_subprocess from test.support import script_helper from unittest import mock @@ -7030,7 +7030,7 @@ def test_directory_in_zipfile_compiled(self): msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"') -@unittest.skipIf(not has_subprocess_support, "test requires subprocess") +@requires_subprocess() class TestTranslations(unittest.TestCase): def test_translations(self): From 21c9aaf214cf92e857fad72f3efc60e972328fd4 Mon Sep 17 00:00:00 2001 From: Tomas Roun Date: Sun, 27 Oct 2024 15:31:40 +0100 Subject: [PATCH 10/10] Use `REPO_ROOT` and `TEST_HOME_DIR` --- Lib/test/test_argparse.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 662673091a6ae0..06b616069c68ff 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -20,6 +20,8 @@ from enum import StrEnum from pathlib import Path +from test.support import REPO_ROOT +from test.support import TEST_HOME_DIR from test.support import captured_stderr from test.support import import_helper from test.support import os_helper @@ -7022,9 +7024,8 @@ def test_directory_in_zipfile_compiled(self): # Translation tests # ================= -i18n_tools = Path(__file__).parents[2] / 'Tools' / 'i18n' -pygettext = i18n_tools / 'pygettext.py' -snapshot_path = Path(__file__).parent / 'translationdata' / 'argparse' / 'msgids.txt' +pygettext = Path(REPO_ROOT) / 'Tools' / 'i18n' / 'pygettext.py' +snapshot_path = Path(TEST_HOME_DIR) / 'translationdata' / 'argparse' / 'msgids.txt' msgid_pattern = re.compile(r'msgid(.*?)(?:msgid_plural|msgctxt|msgstr)', re.DOTALL) msgid_string_pattern = re.compile(r'"((?:\\"|[^"])*)"')