powrap/powrap/powrap.py

178 lines
6.0 KiB
Python
Raw Normal View History

2022-04-26 12:43:54 +00:00
"""Fix style of uncommitted po files, or all if --all is given."""
2017-05-24 20:36:06 +00:00
2019-12-10 09:09:21 +00:00
import argparse
import sys
import os
2019-12-10 09:09:21 +00:00
from typing import Iterable
2020-12-03 12:00:02 +00:00
import difflib
from pathlib import Path
2020-09-29 15:04:53 +00:00
from subprocess import check_output, run, CalledProcessError
2019-03-14 21:29:52 +00:00
from tempfile import NamedTemporaryFile
2017-05-24 20:36:06 +00:00
from tqdm import tqdm
2019-03-14 21:29:52 +00:00
from powrap import __version__
2017-05-24 20:36:06 +00:00
2020-12-03 12:00:02 +00:00
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
2019-03-14 21:29:52 +00:00
for po_path in tqdm(po_files, desc="Checking wrapping of po files", disable=quiet):
2020-09-29 15:04:53 +00:00
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:
2019-03-14 21:29:52 +00:00
args = ["msgcat", "-", "-o", tmpfile.name]
if no_wrap:
args[1:1] = ["--no-wrap"]
2020-09-29 15:04:53 +00:00
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)
2019-03-14 21:29:52 +00:00
new_po_content = tmpfile.read()
if po_content != new_po_content:
2020-12-03 12:00:02 +00:00
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)
2020-12-03 12:00:02 +00:00
return errors
2019-03-14 21:29:52 +00:00
def fix_style(po_files, no_wrap=False, quiet=False):
2020-09-29 15:04:53 +00:00
"""Fix style of given po_files."""
2019-03-14 21:29:52 +00:00
for po_path in tqdm(po_files, desc="Fixing wrapping of po files", disable=quiet):
2018-11-10 14:40:26 +00:00
with open(po_path, encoding="UTF-8") as po_file:
po_content = po_file.read()
2018-11-10 14:40:26 +00:00
args = ["msgcat", "-", "-o", po_path]
if no_wrap:
2018-11-10 14:40:26 +00:00
args[1:1] = ["--no-wrap"]
2020-09-29 15:04:53 +00:00
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)
2017-05-24 20:36:06 +00:00
def parse_args():
2020-09-29 15:04:53 +00:00
"""Parse powrap command line arguments."""
2018-11-10 14:40:26 +00:00
def path(path_str):
path_obj = Path(path_str)
if not path_obj.exists():
2023-03-30 21:19:29 +00:00
raise argparse.ArgumentTypeError(f"File {path_str!r} does not exists.")
if not path_obj.is_file():
2023-03-30 21:19:29 +00:00
raise argparse.ArgumentTypeError(f"{path_str!r} is not a file.")
try:
path_obj.read_text(encoding="utf-8")
2020-09-29 15:04:53 +00:00
except PermissionError as read_error:
raise argparse.ArgumentTypeError(
2023-03-30 21:19:29 +00:00
"{path_str!r}: Permission denied."
2020-09-29 15:04:53 +00:00
) from read_error
return path_obj
2017-05-24 20:36:06 +00:00
parser = argparse.ArgumentParser(
2018-11-11 20:28:34 +00:00
prog="powrap",
description="Ensure po files are using the standard gettext format",
2020-09-29 15:04:53 +00:00
epilog="""exit code:
0:nothing to do
1:would rewrap
127:error running msgcat""",
2018-11-10 14:40:26 +00:00
)
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(),
2018-11-10 14:40:26 +00:00
)
2019-03-14 20:59:27 +00:00
parser.add_argument(
"--quiet", "-q", action="store_true", help="Do not show progress bar."
)
2020-12-03 12:00:02 +00:00
parser.add_argument(
"--diff",
"-d",
action="store_true",
2020-12-03 12:10:26 +00:00
help="Don't write the files back, just output a diff for each file on stdout "
"(implies --check).",
2020-12-03 12:00:02 +00:00
)
2019-03-14 21:29:52 +00:00
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.",
)
2019-03-14 21:05:03 +00:00
parser.add_argument(
"--version", action="version", version="%(prog)s " + __version__
)
2018-11-10 14:40:26 +00:00
parser.add_argument(
"--no-wrap",
action="store_true",
2022-04-26 12:43:54 +00:00
help="see `man msgcat`, useful to sed right after.",
2018-11-10 14:40:26 +00:00
)
parser.add_argument("po_files", nargs="*", help="po files.", type=path)
2017-05-24 20:36:06 +00:00
args = parser.parse_args()
if not args.po_files and not args.modified:
parser.print_help()
2019-12-10 09:09:21 +00:00
sys.exit(1)
if args.po_files and args.modified:
parser.print_help()
sys.exit(1)
return args
def main():
2020-09-29 15:04:53 +00:00
"""Powrap main entrypoint (parsing command line and all)."""
args = parse_args()
if args.git_root:
os.chdir(args.git_root)
2019-03-14 21:29:52 +00:00
if args.modified:
git_status = check_output(
["git", "status", "--porcelain", "--no-renames"],
encoding="utf-8",
)
2019-03-14 21:29:52 +00:00
git_status_lines = [
line.split(maxsplit=2) for line in git_status.split("\n") if line
]
args.po_files.extend(
Path(filename)
2019-03-14 21:29:52 +00:00
for status, filename in git_status_lines
if filename.endswith(".po") and status != "D"
2019-03-14 21:29:52 +00:00
)
if not args.po_files:
print("Nothing to do, exiting.")
sys.exit(0)
2020-12-03 12:00:02 +00:00
if args.check or args.diff:
errors = check_style(args.po_files, args.no_wrap, args.quiet, args.diff)
sys.exit(errors > 0)
2019-03-14 21:29:52 +00:00
else:
fix_style(args.po_files, args.no_wrap, args.quiet)