17db96d56Sopenharmony_ci"""Tests to cover the Tools/i18n package""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport os 47db96d56Sopenharmony_ciimport sys 57db96d56Sopenharmony_ciimport unittest 67db96d56Sopenharmony_cifrom textwrap import dedent 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_ok 97db96d56Sopenharmony_cifrom test.test_tools import skip_if_missing, toolsdir 107db96d56Sopenharmony_cifrom test.support.os_helper import temp_cwd, temp_dir 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ciskip_if_missing() 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_ciclass Test_pygettext(unittest.TestCase): 177db96d56Sopenharmony_ci """Tests for the pygettext.py tool""" 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_ci script = os.path.join(toolsdir,'i18n', 'pygettext.py') 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci def get_header(self, data): 227db96d56Sopenharmony_ci """ utility: return the header of a .po file as a dictionary """ 237db96d56Sopenharmony_ci headers = {} 247db96d56Sopenharmony_ci for line in data.split('\n'): 257db96d56Sopenharmony_ci if not line or line.startswith(('#', 'msgid','msgstr')): 267db96d56Sopenharmony_ci continue 277db96d56Sopenharmony_ci line = line.strip('"') 287db96d56Sopenharmony_ci key, val = line.split(':',1) 297db96d56Sopenharmony_ci headers[key] = val.strip() 307db96d56Sopenharmony_ci return headers 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci def get_msgids(self, data): 337db96d56Sopenharmony_ci """ utility: return all msgids in .po file as a list of strings """ 347db96d56Sopenharmony_ci msgids = [] 357db96d56Sopenharmony_ci reading_msgid = False 367db96d56Sopenharmony_ci cur_msgid = [] 377db96d56Sopenharmony_ci for line in data.split('\n'): 387db96d56Sopenharmony_ci if reading_msgid: 397db96d56Sopenharmony_ci if line.startswith('"'): 407db96d56Sopenharmony_ci cur_msgid.append(line.strip('"')) 417db96d56Sopenharmony_ci else: 427db96d56Sopenharmony_ci msgids.append('\n'.join(cur_msgid)) 437db96d56Sopenharmony_ci cur_msgid = [] 447db96d56Sopenharmony_ci reading_msgid = False 457db96d56Sopenharmony_ci continue 467db96d56Sopenharmony_ci if line.startswith('msgid '): 477db96d56Sopenharmony_ci line = line[len('msgid '):] 487db96d56Sopenharmony_ci cur_msgid.append(line.strip('"')) 497db96d56Sopenharmony_ci reading_msgid = True 507db96d56Sopenharmony_ci else: 517db96d56Sopenharmony_ci if reading_msgid: 527db96d56Sopenharmony_ci msgids.append('\n'.join(cur_msgid)) 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ci return msgids 557db96d56Sopenharmony_ci 567db96d56Sopenharmony_ci def extract_docstrings_from_str(self, module_content): 577db96d56Sopenharmony_ci """ utility: return all msgids extracted from module_content """ 587db96d56Sopenharmony_ci filename = 'test_docstrings.py' 597db96d56Sopenharmony_ci with temp_cwd(None) as cwd: 607db96d56Sopenharmony_ci with open(filename, 'w', encoding='utf-8') as fp: 617db96d56Sopenharmony_ci fp.write(module_content) 627db96d56Sopenharmony_ci assert_python_ok(self.script, '-D', filename) 637db96d56Sopenharmony_ci with open('messages.pot', encoding='utf-8') as fp: 647db96d56Sopenharmony_ci data = fp.read() 657db96d56Sopenharmony_ci return self.get_msgids(data) 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci def test_header(self): 687db96d56Sopenharmony_ci """Make sure the required fields are in the header, according to: 697db96d56Sopenharmony_ci http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry 707db96d56Sopenharmony_ci """ 717db96d56Sopenharmony_ci with temp_cwd(None) as cwd: 727db96d56Sopenharmony_ci assert_python_ok(self.script) 737db96d56Sopenharmony_ci with open('messages.pot', encoding='utf-8') as fp: 747db96d56Sopenharmony_ci data = fp.read() 757db96d56Sopenharmony_ci header = self.get_header(data) 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci self.assertIn("Project-Id-Version", header) 787db96d56Sopenharmony_ci self.assertIn("POT-Creation-Date", header) 797db96d56Sopenharmony_ci self.assertIn("PO-Revision-Date", header) 807db96d56Sopenharmony_ci self.assertIn("Last-Translator", header) 817db96d56Sopenharmony_ci self.assertIn("Language-Team", header) 827db96d56Sopenharmony_ci self.assertIn("MIME-Version", header) 837db96d56Sopenharmony_ci self.assertIn("Content-Type", header) 847db96d56Sopenharmony_ci self.assertIn("Content-Transfer-Encoding", header) 857db96d56Sopenharmony_ci self.assertIn("Generated-By", header) 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci # not clear if these should be required in POT (template) files 887db96d56Sopenharmony_ci #self.assertIn("Report-Msgid-Bugs-To", header) 897db96d56Sopenharmony_ci #self.assertIn("Language", header) 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci #"Plural-Forms" is optional 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci @unittest.skipIf(sys.platform.startswith('aix'), 947db96d56Sopenharmony_ci 'bpo-29972: broken test on AIX') 957db96d56Sopenharmony_ci def test_POT_Creation_Date(self): 967db96d56Sopenharmony_ci """ Match the date format from xgettext for POT-Creation-Date """ 977db96d56Sopenharmony_ci from datetime import datetime 987db96d56Sopenharmony_ci with temp_cwd(None) as cwd: 997db96d56Sopenharmony_ci assert_python_ok(self.script) 1007db96d56Sopenharmony_ci with open('messages.pot', encoding='utf-8') as fp: 1017db96d56Sopenharmony_ci data = fp.read() 1027db96d56Sopenharmony_ci header = self.get_header(data) 1037db96d56Sopenharmony_ci creationDate = header['POT-Creation-Date'] 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_ci # peel off the escaped newline at the end of string 1067db96d56Sopenharmony_ci if creationDate.endswith('\\n'): 1077db96d56Sopenharmony_ci creationDate = creationDate[:-len('\\n')] 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci # This will raise if the date format does not exactly match. 1107db96d56Sopenharmony_ci datetime.strptime(creationDate, '%Y-%m-%d %H:%M%z') 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci def test_funcdocstring(self): 1137db96d56Sopenharmony_ci for doc in ('"""doc"""', "r'''doc'''", "R'doc'", 'u"doc"'): 1147db96d56Sopenharmony_ci with self.subTest(doc): 1157db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1167db96d56Sopenharmony_ci def foo(bar): 1177db96d56Sopenharmony_ci %s 1187db96d56Sopenharmony_ci ''' % doc)) 1197db96d56Sopenharmony_ci self.assertIn('doc', msgids) 1207db96d56Sopenharmony_ci 1217db96d56Sopenharmony_ci def test_funcdocstring_bytes(self): 1227db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1237db96d56Sopenharmony_ci def foo(bar): 1247db96d56Sopenharmony_ci b"""doc""" 1257db96d56Sopenharmony_ci ''')) 1267db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci def test_funcdocstring_fstring(self): 1297db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1307db96d56Sopenharmony_ci def foo(bar): 1317db96d56Sopenharmony_ci f"""doc""" 1327db96d56Sopenharmony_ci ''')) 1337db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1347db96d56Sopenharmony_ci 1357db96d56Sopenharmony_ci def test_classdocstring(self): 1367db96d56Sopenharmony_ci for doc in ('"""doc"""', "r'''doc'''", "R'doc'", 'u"doc"'): 1377db96d56Sopenharmony_ci with self.subTest(doc): 1387db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1397db96d56Sopenharmony_ci class C: 1407db96d56Sopenharmony_ci %s 1417db96d56Sopenharmony_ci ''' % doc)) 1427db96d56Sopenharmony_ci self.assertIn('doc', msgids) 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ci def test_classdocstring_bytes(self): 1457db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1467db96d56Sopenharmony_ci class C: 1477db96d56Sopenharmony_ci b"""doc""" 1487db96d56Sopenharmony_ci ''')) 1497db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci def test_classdocstring_fstring(self): 1527db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1537db96d56Sopenharmony_ci class C: 1547db96d56Sopenharmony_ci f"""doc""" 1557db96d56Sopenharmony_ci ''')) 1567db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci def test_moduledocstring(self): 1597db96d56Sopenharmony_ci for doc in ('"""doc"""', "r'''doc'''", "R'doc'", 'u"doc"'): 1607db96d56Sopenharmony_ci with self.subTest(doc): 1617db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1627db96d56Sopenharmony_ci %s 1637db96d56Sopenharmony_ci ''' % doc)) 1647db96d56Sopenharmony_ci self.assertIn('doc', msgids) 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci def test_moduledocstring_bytes(self): 1677db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1687db96d56Sopenharmony_ci b"""doc""" 1697db96d56Sopenharmony_ci ''')) 1707db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1717db96d56Sopenharmony_ci 1727db96d56Sopenharmony_ci def test_moduledocstring_fstring(self): 1737db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1747db96d56Sopenharmony_ci f"""doc""" 1757db96d56Sopenharmony_ci ''')) 1767db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1777db96d56Sopenharmony_ci 1787db96d56Sopenharmony_ci def test_msgid(self): 1797db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str( 1807db96d56Sopenharmony_ci '''_("""doc""" r'str' u"ing")''') 1817db96d56Sopenharmony_ci self.assertIn('docstring', msgids) 1827db96d56Sopenharmony_ci 1837db96d56Sopenharmony_ci def test_msgid_bytes(self): 1847db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str('_(b"""doc""")') 1857db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci def test_msgid_fstring(self): 1887db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str('_(f"""doc""")') 1897db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'doc' in msgid]) 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci def test_funcdocstring_annotated_args(self): 1927db96d56Sopenharmony_ci """ Test docstrings for functions with annotated args """ 1937db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 1947db96d56Sopenharmony_ci def foo(bar: str): 1957db96d56Sopenharmony_ci """doc""" 1967db96d56Sopenharmony_ci ''')) 1977db96d56Sopenharmony_ci self.assertIn('doc', msgids) 1987db96d56Sopenharmony_ci 1997db96d56Sopenharmony_ci def test_funcdocstring_annotated_return(self): 2007db96d56Sopenharmony_ci """ Test docstrings for functions with annotated return type """ 2017db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2027db96d56Sopenharmony_ci def foo(bar) -> str: 2037db96d56Sopenharmony_ci """doc""" 2047db96d56Sopenharmony_ci ''')) 2057db96d56Sopenharmony_ci self.assertIn('doc', msgids) 2067db96d56Sopenharmony_ci 2077db96d56Sopenharmony_ci def test_funcdocstring_defvalue_args(self): 2087db96d56Sopenharmony_ci """ Test docstring for functions with default arg values """ 2097db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2107db96d56Sopenharmony_ci def foo(bar=()): 2117db96d56Sopenharmony_ci """doc""" 2127db96d56Sopenharmony_ci ''')) 2137db96d56Sopenharmony_ci self.assertIn('doc', msgids) 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ci def test_funcdocstring_multiple_funcs(self): 2167db96d56Sopenharmony_ci """ Test docstring extraction for multiple functions combining 2177db96d56Sopenharmony_ci annotated args, annotated return types and default arg values 2187db96d56Sopenharmony_ci """ 2197db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2207db96d56Sopenharmony_ci def foo1(bar: tuple=()) -> str: 2217db96d56Sopenharmony_ci """doc1""" 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci def foo2(bar: List[1:2]) -> (lambda x: x): 2247db96d56Sopenharmony_ci """doc2""" 2257db96d56Sopenharmony_ci 2267db96d56Sopenharmony_ci def foo3(bar: 'func'=lambda x: x) -> {1: 2}: 2277db96d56Sopenharmony_ci """doc3""" 2287db96d56Sopenharmony_ci ''')) 2297db96d56Sopenharmony_ci self.assertIn('doc1', msgids) 2307db96d56Sopenharmony_ci self.assertIn('doc2', msgids) 2317db96d56Sopenharmony_ci self.assertIn('doc3', msgids) 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci def test_classdocstring_early_colon(self): 2347db96d56Sopenharmony_ci """ Test docstring extraction for a class with colons occurring within 2357db96d56Sopenharmony_ci the parentheses. 2367db96d56Sopenharmony_ci """ 2377db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2387db96d56Sopenharmony_ci class D(L[1:2], F({1: 2}), metaclass=M(lambda x: x)): 2397db96d56Sopenharmony_ci """doc""" 2407db96d56Sopenharmony_ci ''')) 2417db96d56Sopenharmony_ci self.assertIn('doc', msgids) 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def test_calls_in_fstrings(self): 2447db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2457db96d56Sopenharmony_ci f"{_('foo bar')}" 2467db96d56Sopenharmony_ci ''')) 2477db96d56Sopenharmony_ci self.assertIn('foo bar', msgids) 2487db96d56Sopenharmony_ci 2497db96d56Sopenharmony_ci def test_calls_in_fstrings_raw(self): 2507db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2517db96d56Sopenharmony_ci rf"{_('foo bar')}" 2527db96d56Sopenharmony_ci ''')) 2537db96d56Sopenharmony_ci self.assertIn('foo bar', msgids) 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci def test_calls_in_fstrings_nested(self): 2567db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2577db96d56Sopenharmony_ci f"""{f'{_("foo bar")}'}""" 2587db96d56Sopenharmony_ci ''')) 2597db96d56Sopenharmony_ci self.assertIn('foo bar', msgids) 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci def test_calls_in_fstrings_attribute(self): 2627db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2637db96d56Sopenharmony_ci f"{obj._('foo bar')}" 2647db96d56Sopenharmony_ci ''')) 2657db96d56Sopenharmony_ci self.assertIn('foo bar', msgids) 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_ci def test_calls_in_fstrings_with_call_on_call(self): 2687db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2697db96d56Sopenharmony_ci f"{type(str)('foo bar')}" 2707db96d56Sopenharmony_ci ''')) 2717db96d56Sopenharmony_ci self.assertNotIn('foo bar', msgids) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci def test_calls_in_fstrings_with_format(self): 2747db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2757db96d56Sopenharmony_ci f"{_('foo {bar}').format(bar='baz')}" 2767db96d56Sopenharmony_ci ''')) 2777db96d56Sopenharmony_ci self.assertIn('foo {bar}', msgids) 2787db96d56Sopenharmony_ci 2797db96d56Sopenharmony_ci def test_calls_in_fstrings_with_wrong_input_1(self): 2807db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2817db96d56Sopenharmony_ci f"{_(f'foo {bar}')}" 2827db96d56Sopenharmony_ci ''')) 2837db96d56Sopenharmony_ci self.assertFalse([msgid for msgid in msgids if 'foo {bar}' in msgid]) 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_ci def test_calls_in_fstrings_with_wrong_input_2(self): 2867db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2877db96d56Sopenharmony_ci f"{_(1)}" 2887db96d56Sopenharmony_ci ''')) 2897db96d56Sopenharmony_ci self.assertNotIn(1, msgids) 2907db96d56Sopenharmony_ci 2917db96d56Sopenharmony_ci def test_calls_in_fstring_with_multiple_args(self): 2927db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 2937db96d56Sopenharmony_ci f"{_('foo', 'bar')}" 2947db96d56Sopenharmony_ci ''')) 2957db96d56Sopenharmony_ci self.assertNotIn('foo', msgids) 2967db96d56Sopenharmony_ci self.assertNotIn('bar', msgids) 2977db96d56Sopenharmony_ci 2987db96d56Sopenharmony_ci def test_calls_in_fstring_with_keyword_args(self): 2997db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 3007db96d56Sopenharmony_ci f"{_('foo', bar='baz')}" 3017db96d56Sopenharmony_ci ''')) 3027db96d56Sopenharmony_ci self.assertNotIn('foo', msgids) 3037db96d56Sopenharmony_ci self.assertNotIn('bar', msgids) 3047db96d56Sopenharmony_ci self.assertNotIn('baz', msgids) 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_ci def test_calls_in_fstring_with_partially_wrong_expression(self): 3077db96d56Sopenharmony_ci msgids = self.extract_docstrings_from_str(dedent('''\ 3087db96d56Sopenharmony_ci f"{_(f'foo') + _('bar')}" 3097db96d56Sopenharmony_ci ''')) 3107db96d56Sopenharmony_ci self.assertNotIn('foo', msgids) 3117db96d56Sopenharmony_ci self.assertIn('bar', msgids) 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci def test_files_list(self): 3147db96d56Sopenharmony_ci """Make sure the directories are inspected for source files 3157db96d56Sopenharmony_ci bpo-31920 3167db96d56Sopenharmony_ci """ 3177db96d56Sopenharmony_ci text1 = 'Text to translate1' 3187db96d56Sopenharmony_ci text2 = 'Text to translate2' 3197db96d56Sopenharmony_ci text3 = 'Text to ignore' 3207db96d56Sopenharmony_ci with temp_cwd(None), temp_dir(None) as sdir: 3217db96d56Sopenharmony_ci os.mkdir(os.path.join(sdir, 'pypkg')) 3227db96d56Sopenharmony_ci with open(os.path.join(sdir, 'pypkg', 'pymod.py'), 'w', 3237db96d56Sopenharmony_ci encoding='utf-8') as sfile: 3247db96d56Sopenharmony_ci sfile.write(f'_({text1!r})') 3257db96d56Sopenharmony_ci os.mkdir(os.path.join(sdir, 'pkg.py')) 3267db96d56Sopenharmony_ci with open(os.path.join(sdir, 'pkg.py', 'pymod2.py'), 'w', 3277db96d56Sopenharmony_ci encoding='utf-8') as sfile: 3287db96d56Sopenharmony_ci sfile.write(f'_({text2!r})') 3297db96d56Sopenharmony_ci os.mkdir(os.path.join(sdir, 'CVS')) 3307db96d56Sopenharmony_ci with open(os.path.join(sdir, 'CVS', 'pymod3.py'), 'w', 3317db96d56Sopenharmony_ci encoding='utf-8') as sfile: 3327db96d56Sopenharmony_ci sfile.write(f'_({text3!r})') 3337db96d56Sopenharmony_ci assert_python_ok(self.script, sdir) 3347db96d56Sopenharmony_ci with open('messages.pot', encoding='utf-8') as fp: 3357db96d56Sopenharmony_ci data = fp.read() 3367db96d56Sopenharmony_ci self.assertIn(f'msgid "{text1}"', data) 3377db96d56Sopenharmony_ci self.assertIn(f'msgid "{text2}"', data) 3387db96d56Sopenharmony_ci self.assertNotIn(text3, data) 339