From 58e5e0bf5a0efe5508372da9c5c6d1837db2031e Mon Sep 17 00:00:00 2001 From: Akanksha Trehun Date: Sat, 18 Jul 2026 21:01:38 +0530 Subject: [PATCH 1/2] gh-130655: Add tests for dgettext, dngettext, dpgettext, dnpgettext Add focused test coverage for the domain-specific gettext functions that were previously only tested indirectly. This extends the work started in gh-134594 to cover all four domain-specific functions. Each function is tested with a for loop and subTests covering: - Existing translations - Missing translations (fallback to original message) - Nonexistent domains (fallback to original message) - Context-based translations (dpgettext, dnpgettext) - Plural forms (dngettext, dnpgettext) - Wrong/missing contexts Updated the TODO comment to reference the issue and additional functions that now have coverage. --- Lib/test/test_gettext.py | 107 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index 9ad37909a8ec4e0..7eb504addaf3b2e 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -11,7 +11,8 @@ # TODO: -# - Add new tests, for example for "dgettext" +# - Add new tests, for example for "dgettext", "dngettext", "dpgettext", +# "dnpgettext" (gh-130655) # - Tests should have only one assert. GNU_MO_DATA = b'''\ @@ -944,6 +945,110 @@ def test_translation_fallback(self): self.assertIsInstance(t, gettext.NullTranslations) +class DGettextTests(GettextBaseTest): + + def setUp(self): + super().setUp() + gettext.bindtextdomain('gettext', os.curdir) + gettext.textdomain('gettext') + + def test_dgettext_translation(self): + cases = [ + ('gettext', 'nudge nudge', 'wink wink'), + ('gettext', 'mullusk', 'bacon'), + ('gettext', 'Raymond Luxury Yach-t', 'Throatwobbler Mangrove'), + ('gettext', 'albatross', 'albatross'), + ('gettext', 'missing message', 'missing message'), + ('nonexistent_domain', 'mullusk', 'mullusk'), + ('', 'mullusk', 'mullusk'), + ] + for domain, msgid, expected in cases: + with self.subTest(domain=domain, msgid=msgid): + self.assertEqual(gettext.dgettext(domain, msgid), expected) + + +class DnGettextTests(GettextBaseTest): + + def setUp(self): + super().setUp() + gettext.bindtextdomain('gettext', os.curdir) + gettext.textdomain('gettext') + + def test_dngettext_translation(self): + cases = [ + ('gettext', 'There is %s file', 'There are %s files', 1, + 'Hay %s fichero'), + ('gettext', 'There is %s file', 'There are %s files', 5, + 'Hay %s ficheros'), + ('gettext', '%d file deleted', '%d files deleted', 1, + '%d file deleted'), + ('gettext', '%d file deleted', '%d files deleted', 5, + '%d files deleted'), + ('nonexistent_domain', 'There is %s file', 'There are %s files', 1, + 'There is %s file'), + ('nonexistent_domain', 'There is %s file', 'There are %s files', 5, + 'There are %s files'), + ] + for domain, msgid1, msgid2, n, expected in cases: + with self.subTest(domain=domain, n=n): + self.assertEqual(gettext.dngettext(domain, msgid1, msgid2, n), + expected) + + +class DpGettextTests(GettextBaseTest): + + def setUp(self): + super().setUp() + gettext.bindtextdomain('gettext', os.curdir) + gettext.textdomain('gettext') + + def test_dpgettext_translation(self): + cases = [ + ('gettext', 'my context', 'nudge nudge', + 'wink wink (in "my context")'), + ('gettext', 'my other context', 'nudge nudge', + 'wink wink (in "my other context")'), + ('gettext', 'my context', 'albatross', 'albatross'), + ('gettext', 'wrong context', 'nudge nudge', 'nudge nudge'), + ('nonexistent_domain', 'my context', 'nudge nudge', 'nudge nudge'), + ] + for domain, context, msgid, expected in cases: + with self.subTest(domain=domain, context=context): + self.assertEqual(gettext.dpgettext(domain, context, msgid), + expected) + + +class DnpGettextTests(GettextBaseTest): + + def setUp(self): + super().setUp() + gettext.bindtextdomain('gettext', os.curdir) + gettext.textdomain('gettext') + + def test_dnpgettext_translation(self): + cases = [ + ('gettext', 'With context', 'There is %s file', + 'There are %s files', 1, 'Hay %s fichero (context)'), + ('gettext', 'With context', 'There is %s file', + 'There are %s files', 5, 'Hay %s ficheros (context)'), + ('gettext', 'With context', '%d file deleted', + '%d files deleted', 1, '%d file deleted'), + ('gettext', 'With context', '%d file deleted', + '%d files deleted', 5, '%d files deleted'), + ('gettext', 'Wrong context', 'There is %s file', + 'There are %s files', 1, 'There is %s file'), + ('nonexistent_domain', 'With context', 'There is %s file', + 'There are %s files', 1, 'There is %s file'), + ('nonexistent_domain', 'With context', 'There is %s file', + 'There are %s files', 5, 'There are %s files'), + ] + for domain, context, msgid1, msgid2, n, expected in cases: + with self.subTest(domain=domain, context=context, n=n): + self.assertEqual( + gettext.dnpgettext(domain, context, msgid1, msgid2, n), + expected) + + if __name__ == '__main__': unittest.main() From 49a23f1fe7b2291597655293550c01063edb15f2 Mon Sep 17 00:00:00 2001 From: Akanksha Trehun Date: Sat, 18 Jul 2026 21:37:42 +0530 Subject: [PATCH 2/2] Add tests for TextIOWrapper.reconfigure() with invalid encoding/errors Replace four explicit TODO comments in test_reconfigure_errors with actual test assertions: - encoding='utf-8\0' raises LookupError (null byte in encoding name) - encoding='nonexisting' raises LookupError (unknown encoding) - errors='ignore\0' raises LookupError (null byte in error handler) - errors='nonexisting' raises LookupError (unknown error handler) These follow the same pattern already used in the test for encoding='locale\0'. --- Lib/test/test_io/test_textio.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_io/test_textio.py b/Lib/test/test_io/test_textio.py index 82096ab09873955..73857d2db487431 100644 --- a/Lib/test/test_io/test_textio.py +++ b/Lib/test/test_io/test_textio.py @@ -1288,15 +1288,19 @@ def test_reconfigure_errors(self): txt.reconfigure(encoding='\udcfe') with self.assertRaises(LookupError): txt.reconfigure(encoding='locale\0') - # TODO: txt.reconfigure(encoding='utf-8\0') - # TODO: txt.reconfigure(encoding='nonexisting') + with self.assertRaises(LookupError): + txt.reconfigure(encoding='utf-8\0') + with self.assertRaises(LookupError): + txt.reconfigure(encoding='nonexisting') with self.assertRaises(TypeError): txt.reconfigure(errors=42) if self.is_C: with self.assertRaises(UnicodeEncodeError): txt.reconfigure(errors='\udcfe') - # TODO: txt.reconfigure(errors='ignore\0') - # TODO: txt.reconfigure(errors='nonexisting') + with self.assertRaises(LookupError): + txt.reconfigure(errors='ignore\0') + with self.assertRaises(LookupError): + txt.reconfigure(errors='nonexisting') with self.assertRaises(TypeError): txt.reconfigure(newline=42) with self.assertRaises(ValueError):