diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index f74105ca1ffb3e..4385b93ee56e7d 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -6,8 +6,10 @@ import io import operator import os +import re import shutil import stat +import subprocess import sys import textwrap import tempfile @@ -16,7 +18,15 @@ import warnings from enum import StrEnum -from test.support import os_helper, captured_stderr +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 +from test.support import requires_subprocess +from test.support import script_helper +from test.test_tools import skip_if_missing from unittest import mock @@ -6752,6 +6762,56 @@ def test_os_error(self): self.parser.parse_args, ['@no-such-file']) +# ================= +# Translation tests +# ================= + +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'"((?:\\"|[^"])*)"') + + +@requires_subprocess() +class TestTranslations(unittest.TestCase): + + def test_translations(self): + # Test messages extracted from the argparse module against a snapshot + skip_if_missing('i18n') + 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. RFile.seen = {} @@ -6759,4 +6819,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/translationdata/argparse/msgids.txt b/Lib/test/translationdata/argparse/msgids.txt new file mode 100644 index 00000000000000..97416aad520271 --- /dev/null +++ b/Lib/test/translationdata/argparse/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/Makefile.pre.in b/Makefile.pre.in index 1ac07b1f31779c..445fa6381c20a4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2488,6 +2488,8 @@ TESTSUBDIRS= idlelib/idle_test \ test/tkinterdata \ test/tokenizedata \ test/tracedmodules \ + test/translationdata \ + test/translationdata/argparse \ test/typinganndata \ test/wheeldata \ test/xmltestdata \ 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..3c2455cfc8c530 --- /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.