Skip to content

Commit 0399f23

Browse files
codespell_lib/_codespell.py: read additional args from file with with… (#3841)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent e592599 commit 0399f23

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

README.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ instead of these invalid entries:
229229
230230
.. _tomli: https://pypi.org/project/tomli/
231231

232+
Reading arguments from file
233+
---------------------------
234+
235+
Additional arguments can be read from a file with ``@PATH``. Arguments are
236+
extracted using ``shlex.split()``.
237+
238+
232239
pre-commit hook
233240
---------------
234241

codespell_lib/_codespell.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import itertools
2424
import os
2525
import re
26+
import shlex
2627
import sys
2728
import textwrap
2829
from collections.abc import Iterable, Sequence
@@ -388,7 +389,27 @@ def _supports_ansi_colors() -> bool:
388389
def parse_options(
389390
args: Sequence[str],
390391
) -> tuple[argparse.Namespace, argparse.ArgumentParser, list[str]]:
391-
parser = argparse.ArgumentParser(formatter_class=NewlineHelpFormatter)
392+
# Split lines read from `@PATH` using shlex.split(), otherwise default
393+
# behaviour is to have one arg per line. See:
394+
# https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args
395+
class ArgumentParser2(argparse.ArgumentParser):
396+
def convert_arg_line_to_args(self, arg_line: str) -> list[str]:
397+
if sys.platform == "win32":
398+
# On Windows, shlex.split() seems to be messed up by back
399+
# slashes. Temporarily changing them to forward slashes seems
400+
# to make things work better.
401+
arg_line = arg_line.replace("\\", "/")
402+
ret = shlex.split(arg_line)
403+
ret = [p.replace("/", "\\") for p in ret]
404+
else:
405+
ret = shlex.split(arg_line)
406+
return ret
407+
408+
parser = ArgumentParser2(
409+
formatter_class=NewlineHelpFormatter,
410+
fromfile_prefix_chars="@",
411+
epilog="Use @PATH to read additional arguments from file PATH.",
412+
)
392413

393414
parser.set_defaults(colors=_supports_ansi_colors())
394415
parser.add_argument("--version", action="version", version=VERSION)

codespell_lib/tests/test_basic.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,3 +1483,44 @@ def test_stdin(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
14831483
code, stdout, _ = result
14841484
assert stdout == "1: Thsi ==> This\n"
14851485
assert code == 1
1486+
1487+
1488+
def test_args_from_file(
1489+
tmp_path: Path,
1490+
capsys: pytest.CaptureFixture[str],
1491+
) -> None:
1492+
import textwrap
1493+
1494+
print()
1495+
fname1 = tmp_path / "tmp1"
1496+
fname2 = tmp_path / "tmp2"
1497+
fname3 = tmp_path / "tmp3"
1498+
fname_list = tmp_path / "tmp_list"
1499+
fname_list.write_text(f"{fname1} {fname2}\n{fname3}")
1500+
fname1.write_text("abandonned\ncode")
1501+
fname2.write_text("exmaple\n")
1502+
fname3.write_text("abilty\n")
1503+
print(f"{fname_list=}")
1504+
args = ["codespell", f"@{fname_list}"]
1505+
print(f"Running: {args=}")
1506+
cp = subprocess.run( # noqa: S603
1507+
args,
1508+
check=False,
1509+
text=True,
1510+
capture_output=True,
1511+
)
1512+
code = cp.returncode
1513+
stdout = cp.stdout
1514+
stderr = cp.stderr
1515+
print(f"{code=}")
1516+
print(f"stdout:\n{textwrap.indent(stdout, ' ')}")
1517+
print(f"stderr:\n{textwrap.indent(stderr, ' ')}")
1518+
assert "tmp1:1: abandonned ==> abandoned\n" in stdout, f"{stdout=}"
1519+
assert "tmp2:1: exmaple ==> example\n" in stdout, f"{stdout=}"
1520+
assert "tmp3:1: abilty ==> ability\n" in stdout, f"{stdout=}"
1521+
assert code, f"{code=}"
1522+
1523+
# Run same test via cs_.main() so code coverage checks work.
1524+
print("Testing with direct call to cs_.main()")
1525+
r = cs_.main(*args[1:])
1526+
print(f"{r=}")

0 commit comments

Comments
 (0)