"""Fix style of uncommitted po files, or all if --all is given.""" import argparse import sys import os from typing import Iterable import difflib from pathlib import Path from subprocess import check_output, run, CalledProcessError from tempfile import NamedTemporaryFile from tqdm import tqdm from powrap import __version__ def check_style(po_files: Iterable[str], no_wrap=False, quiet=False, diff=False) -> int: """Check style of given po_files. Prints errors on stderr and returns the number of errors found. """ errors = 0 for po_path in tqdm(po_files, desc="Checking wrapping of po files", disable=quiet): try: with open(po_path, encoding="UTF-8") as po_file: po_content = po_file.read() except OSError as open_error: tqdm.write(f"Error opening '{po_path}': {open_error}") continue delete = os.name == "posix" and sys.platform != "cygwin" with NamedTemporaryFile("w+", delete=delete, encoding="utf-8") as tmpfile: args = ["msgcat", "-", "-o", tmpfile.name] if no_wrap: args[1:1] = ["--no-wrap"] try: run(args, encoding="utf-8", check=True, input=po_content) except CalledProcessError as run_error: tqdm.write(f"Error processing '{po_path}': {run_error}") continue except FileNotFoundError as run_error: tqdm.write("Error running " + " ".join(args) + f": {run_error}") sys.exit(127) new_po_content = tmpfile.read() if po_content != new_po_content: errors += 1 print("Would rewrap:", po_path, file=sys.stderr) if diff: for line in difflib.unified_diff( po_content.splitlines(keepends=True), new_po_content.splitlines(keepends=True), ): print(line, end="", file=sys.stderr) if not delete: os.remove(tmpfile.name) return errors def fix_style(po_files, no_wrap=False, quiet=False): """Fix style of given po_files.""" for po_path in tqdm(po_files, desc="Fixing wrapping of po files", disable=quiet): with open(po_path, encoding="UTF-8") as po_file: po_content = po_file.read() args = ["msgcat", "-", "-o", po_path] if no_wrap: args[1:1] = ["--no-wrap"] try: run(args, encoding="utf-8", check=True, input=po_content) except CalledProcessError as run_error: tqdm.write(f"Error processing '{po_path}': {run_error}") except FileNotFoundError as run_error: tqdm.write("Error running " + " ".join(args) + f": {run_error}") sys.exit(127) def parse_args(): """Parse powrap command line arguments.""" def path(path_str): path_obj = Path(path_str) if not path_obj.exists(): raise argparse.ArgumentTypeError(f"File {path_str!r} does not exists.") if not path_obj.is_file(): raise argparse.ArgumentTypeError(f"{path_str!r} is not a file.") try: path_obj.read_text(encoding="utf-8") except PermissionError as read_error: raise argparse.ArgumentTypeError( "{path_str!r}: Permission denied." ) from read_error return path_obj parser = argparse.ArgumentParser( prog="powrap", description="Ensure po files are using the standard gettext format", epilog="""exit code: 0:nothing to do 1:would rewrap 127:error running msgcat""", ) parser.add_argument( "--modified", "-m", action="store_true", help="Use git to find modified files instead of passing them as arguments.", ) parser.add_argument( "-C", help="To use with --modified to tell where the git " "repo is, in case it's not in the current working directory.", type=Path, dest="git_root", default=Path.cwd(), ) parser.add_argument( "--quiet", "-q", action="store_true", help="Do not show progress bar." ) parser.add_argument( "--diff", "-d", action="store_true", help="Don't write the files back, just output a diff for each file on stdout " "(implies --check).", ) parser.add_argument( "--check", action="store_true", help="Don't write the files back, just return the status. " "Return code 0 means nothing would change. " "Return code 1 means some files would be reformatted.", ) parser.add_argument( "--version", action="version", version="%(prog)s " + __version__ ) parser.add_argument( "--no-wrap", action="store_true", help="see `man msgcat`, useful to sed right after.", ) parser.add_argument("po_files", nargs="*", help="po files.", type=path) args = parser.parse_args() if not args.po_files and not args.modified: parser.print_help() sys.exit(1) if args.po_files and args.modified: parser.print_help() sys.exit(1) return args def main(): """Powrap main entrypoint (parsing command line and all).""" args = parse_args() if args.git_root: os.chdir(args.git_root) if args.modified: git_status = check_output( ["git", "status", "--porcelain", "--no-renames"], encoding="utf-8", ) git_status_lines = [ line.split(maxsplit=2) for line in git_status.split("\n") if line ] args.po_files.extend( Path(filename) for status, filename in git_status_lines if filename.endswith(".po") and status != "D" ) if not args.po_files: print("Nothing to do, exiting.") sys.exit(0) if args.check or args.diff: errors = check_style(args.po_files, args.no_wrap, args.quiet, args.diff) sys.exit(errors > 0) else: fix_style(args.po_files, args.no_wrap, args.quiet)