From 8cdba6712148ab1dc4f3ef03d9ea825163e4aa0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar=20Rubio?= Date: Sat, 16 Nov 2024 23:45:13 +0100 Subject: [PATCH 1/2] Improve `--no-empty-msgstr` implementation --- src/mdpo/md2po/__main__.py | 24 ++++++----- src/mdpo/md2po2md/__init__.py | 25 +++++------- src/mdpo/md2po2md/__main__.py | 20 ++++++---- src/mdpo/mdpo2html/__main__.py | 27 ++++++++----- src/mdpo/po.py | 42 ++++++++++++++++++++ src/mdpo/po2md/__main__.py | 28 ++++++++----- tests/test_unit/test_md2po/test_md2po_cli.py | 14 +++---- 7 files changed, 120 insertions(+), 60 deletions(-) diff --git a/src/mdpo/md2po/__main__.py b/src/mdpo/md2po/__main__.py index 5929d61..5f390e0 100755 --- a/src/mdpo/md2po/__main__.py +++ b/src/mdpo/md2po/__main__.py @@ -33,6 +33,7 @@ from mdpo.md2po import Md2Po from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS from mdpo.po import ( + check_empty_msgstrs_in_filepaths, check_fuzzy_entries_in_filepaths, check_obsolete_entries_in_filepaths, ) @@ -274,17 +275,22 @@ def run(args=frozenset()): exitcode = 4 if opts.no_empty_msgstr: - for entry in pofile: - if not entry.msgstr: - if not opts.quiet: + locations = list(check_empty_msgstrs_in_filepaths( + (opts.po_filepath,), + )) + if locations: + if len(locations) > 2: # noqa PLR2004 + sys.stderr.write( + f'Found {len(locations)} empty msgstrs:\n', + ) + for location in locations: + sys.stderr.write(f'{location}\n') + else: + for location in locations: sys.stderr.write( - ( - f"Empty msgstr found at {opts.po_filepath}" - " and passed '--no-empty-msgstr'\n" - ), + f'Found empty msgstr at {location}\n', ) - exitcode = 5 - break + exitcode = 5 return (pofile, exitcode) diff --git a/src/mdpo/md2po2md/__init__.py b/src/mdpo/md2po2md/__init__.py index 99bf6f1..39a4554 100644 --- a/src/mdpo/md2po2md/__init__.py +++ b/src/mdpo/md2po2md/__init__.py @@ -6,6 +6,7 @@ from mdpo.md2po import Md2Po from mdpo.md4c import DEFAULT_MD4C_GENERIC_PARSER_EXTENSIONS from mdpo.po import ( + check_empty_msgstrs_in_filepaths, check_fuzzy_entries_in_filepaths, check_obsolete_entries_in_filepaths, ) @@ -30,6 +31,7 @@ def markdown_to_pofile_to_markdown( _check_saved_files_changed=False, no_obsolete=False, no_fuzzy=False, + no_empty_msgstr=False, ): """Translate a set of Markdown files using PO files. @@ -78,6 +80,7 @@ def markdown_to_pofile_to_markdown( ``pofile_to_markdown`` function. no_obsolete (bool): If ``True``, check for obsolete entries in PO files. no_fuzzy (bool): If ``True``, check for fuzzy entries in PO files. + no_empty_msgstr (bool): If ``True``, check for empty ``msgstr`` entries. """ if '{lang}' not in output_paths_schema: raise ValueError( @@ -107,7 +110,7 @@ def markdown_to_pofile_to_markdown( _saved_files_changed = None if not _check_saved_files_changed else False obsoletes = [] fuzzies = [] - empty = False + empties = [] for filepath in input_paths_glob_: for lang in langs: @@ -165,12 +168,6 @@ def markdown_to_pofile_to_markdown( if _check_saved_files_changed and _saved_files_changed is False: _saved_files_changed = md2po._saved_files_changed - if not empty: - for entry in md2po.pofile: - if not entry.msgstr: - empty = True - break - # po2md po2md = Po2Md( [po_filepath], @@ -199,13 +196,9 @@ def markdown_to_pofile_to_markdown( check_fuzzy_entries_in_filepaths([po_filepath]), ) - if not empty: - for pofile in po2md.pofiles: - for entry in pofile: - if not entry.msgstr: - empty = True - break - if empty: - break + if no_empty_msgstr: + empties.extend( + check_empty_msgstrs_in_filepaths([po_filepath]), + ) - return (_saved_files_changed, obsoletes, fuzzies, empty) + return (_saved_files_changed, obsoletes, fuzzies, empties) diff --git a/src/mdpo/md2po2md/__main__.py b/src/mdpo/md2po2md/__main__.py index e8ef104..f668e77 100755 --- a/src/mdpo/md2po2md/__main__.py +++ b/src/mdpo/md2po2md/__main__.py @@ -144,13 +144,14 @@ def run(args=frozenset()): '_check_saved_files_changed': opts.check_saved_files_changed, 'no_obsolete': opts.no_obsolete, 'no_fuzzy': opts.no_fuzzy, + 'no_empty_msgstr': opts.no_empty_msgstr, } ( _saved_files_changed, obsoletes, fuzzies, - empty, + empties, ) = markdown_to_pofile_to_markdown( opts.langs, opts.input_paths_glob, @@ -189,14 +190,19 @@ def run(args=frozenset()): exitcode = 4 - if opts.no_empty_msgstr and empty: - exitcode = 5 - - if not opts.quiet: + if opts.no_empty_msgstr: + if len(empties) > 2: # noqa PLR2004 sys.stderr.write( - "Empty msgstr found at PO files and" - " passed '--no-empty-msgstr'\n", + f'Found {len(empties)} empty msgstrs:\n', ) + for location in empties: + sys.stderr.write(f'{location}\n') + else: + for location in empties: + sys.stderr.write( + f'Found empty msgstr at {location}\n', + ) + exitcode = 5 return exitcode diff --git a/src/mdpo/mdpo2html/__main__.py b/src/mdpo/mdpo2html/__main__.py index 6cfcc2d..a5d42e6 100755 --- a/src/mdpo/mdpo2html/__main__.py +++ b/src/mdpo/mdpo2html/__main__.py @@ -23,6 +23,7 @@ from mdpo.io import environ from mdpo.mdpo2html import MdPo2HTML from mdpo.po import ( + check_empty_msgstrs_in_filepaths, check_fuzzy_entries_in_filepaths, check_obsolete_entries_in_filepaths, paths_or_globs_to_unique_pofiles, @@ -168,16 +169,22 @@ def run(args=frozenset()): exitcode = 4 if opts.no_empty_msgstr: - for pofile in mdpo2html.pofiles: - for entry in pofile: - if not entry.msgstr: - if not opts.quiet: - sys.stderr.write( - f"Empty msgstr for msgid '{entry.msgid}'" - " found at PO files and" - " passed '--no-empty-msgstr'\n", - ) - exitcode = 5 + locations = list(check_empty_msgstrs_in_filepaths( + (opts.po_filepath,), + )) + if locations: + if len(locations) > 2: # noqa PLR2004 + sys.stderr.write( + f'Found {len(locations)} empty msgstrs:\n', + ) + for location in locations: + sys.stderr.write(f'{location}\n') + else: + for location in locations: + sys.stderr.write( + f'Found empty msgstr at {location}\n', + ) + exitcode = 5 return (output, exitcode) diff --git a/src/mdpo/po.py b/src/mdpo/po.py index 746e396..8377833 100644 --- a/src/mdpo/po.py +++ b/src/mdpo/po.py @@ -208,6 +208,25 @@ def check_fuzzy_entries_in_filepaths(filenames): ) +def check_empty_msgstrs_in_filepaths(filenames): + """Warns about all empty msgstr found in a set of PO files. + + Args: + filenames (list): Set of file names to check. + + Returns: + list(str): error messages produced. + """ + for filename in filenames: + with open(filename, 'rb') as f: + content_lines = f.readlines() + + yield from parse_empty_msgstr_from_content_lines( + content_lines, + location_prefix=f'{filename}:', + ) + + def parse_obsoletes_from_content_lines( content_lines, location_prefix='line ', @@ -247,3 +266,26 @@ def parse_fuzzy_from_content_lines( for i, line in enumerate(content_lines): if line.startswith(b'#,') and b'fuzzy' in line: yield f'{location_prefix}{i + 1}' + + +def parse_empty_msgstr_from_content_lines( + content_lines, + location_prefix='line ', +): + """Warns about all empty msgstr found in a set of PO files. + + Args: + content_lines (list): Set of content lines to check. + location_prefix (str): Prefix to use in the location message. + + Returns: + list(str): error locations found. + """ + n_lines = len(content_lines) + for i, line in enumerate(content_lines): + if ( + line.startswith(b'msgstr ""') or line.startswith(b'#~ msgstr ""') + ) and not content_lines[i - 1].startswith(b'msgid ""'): + next_i = i + 1 + if next_i == n_lines or not content_lines[next_i].strip(): + yield f'{location_prefix}{i + 1}' diff --git a/src/mdpo/po2md/__main__.py b/src/mdpo/po2md/__main__.py index e182c80..4b271ea 100755 --- a/src/mdpo/po2md/__main__.py +++ b/src/mdpo/po2md/__main__.py @@ -26,6 +26,7 @@ ) from mdpo.io import environ from mdpo.po import ( + check_empty_msgstrs_in_filepaths, check_fuzzy_entries_in_filepaths, check_obsolete_entries_in_filepaths, paths_or_globs_to_unique_pofiles, @@ -192,17 +193,22 @@ def run(args=frozenset()): exitcode = 4 if opts.no_empty_msgstr: - for pofile in po2md.pofiles: - for entry in pofile: - if not entry.msgstr: - if not opts.quiet: - sys.stderr.write( - ( - f"Empty msgstr found at {opts.po_filepath}" - " and passed '--no-empty-msgstr'\n" - ), - ) - exitcode = 5 + locations = list(check_empty_msgstrs_in_filepaths( + (opts.po_filepath,), + )) + if locations: + if len(locations) > 2: # noqa PLR2004 + sys.stderr.write( + f'Found {len(locations)} empty msgstrs:\n', + ) + for location in locations: + sys.stderr.write(f'{location}\n') + else: + for location in locations: + sys.stderr.write( + f'Found empty msgstr at {location}\n', + ) + exitcode = 5 return (output, exitcode) diff --git a/tests/test_unit/test_md2po/test_md2po_cli.py b/tests/test_unit/test_md2po/test_md2po_cli.py index 24ce696..0711454 100644 --- a/tests/test_unit/test_md2po/test_md2po_cli.py +++ b/tests/test_unit/test_md2po/test_md2po_cli.py @@ -893,14 +893,14 @@ def test_no_empty_mgstr(capsys, arg, tmp_file): with tmp_file(po_input, '.po') as filename: pofile, exitcode = run([arg, '-p', filename, '--no-location', 'Hello']) - stdout, stderr = capsys.readouterr() + stdout, stderr = capsys.readouterr() - assert exitcode == 5 - assert f'{pofile}\n' == po_input - assert stdout == po_input - assert stderr == ( - f"Empty msgstr found at {filename} and passed '--no-empty-msgstr'\n" - ) + assert exitcode == 5 + assert f'{pofile}\n' == po_input + assert stdout == po_input + assert stderr == ( + f'Found empty msgstr at {filename}:6\n' + ) po_input = '''# msgid "" From c3b2a058962552f5f81bc3ac0fc45694399a2381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar=20Rubio?= Date: Sat, 16 Nov 2024 23:53:36 +0100 Subject: [PATCH 2/2] Minor change --- src/mdpo/po.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mdpo/po.py b/src/mdpo/po.py index 8377833..61ebb8b 100644 --- a/src/mdpo/po.py +++ b/src/mdpo/po.py @@ -288,4 +288,4 @@ def parse_empty_msgstr_from_content_lines( ) and not content_lines[i - 1].startswith(b'msgid ""'): next_i = i + 1 if next_i == n_lines or not content_lines[next_i].strip(): - yield f'{location_prefix}{i + 1}' + yield f'{location_prefix}{next_i}'