Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ instead of these invalid entries:

.. _tomli: https://pypi.org/project/tomli/

Reading arguments from file
---------------------------

Additional arguments can be read from a file with ``@PATH``. Arguments are
extracted using ``shlex.split()``.


pre-commit hook
---------------

Expand Down
23 changes: 22 additions & 1 deletion codespell_lib/_codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import itertools
import os
import re
import shlex
import sys
import textwrap
from collections.abc import Iterable, Sequence
Expand Down Expand Up @@ -388,7 +389,27 @@ def _supports_ansi_colors() -> bool:
def parse_options(
args: Sequence[str],
) -> tuple[argparse.Namespace, argparse.ArgumentParser, list[str]]:
parser = argparse.ArgumentParser(formatter_class=NewlineHelpFormatter)
# Split lines read from `@PATH` using shlex.split(), otherwise default
# behaviour is to have one arg per line. See:
# https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.convert_arg_line_to_args
class ArgumentParser2(argparse.ArgumentParser):
def convert_arg_line_to_args(self, arg_line: str) -> list[str]:
if sys.platform == "win32":
# On Windows, shlex.split() seems to be messed up by back
# slashes. Temporarily changing them to forward slashes seems
# to make things work better.
arg_line = arg_line.replace("\\", "/")
ret = shlex.split(arg_line)
ret = [p.replace("/", "\\") for p in ret]
else:
ret = shlex.split(arg_line)
return ret

parser = ArgumentParser2(
formatter_class=NewlineHelpFormatter,
fromfile_prefix_chars="@",
epilog="Use @PATH to read additional arguments from file PATH.",
)

parser.set_defaults(colors=_supports_ansi_colors())
parser.add_argument("--version", action="version", version=VERSION)
Expand Down
36 changes: 36 additions & 0 deletions codespell_lib/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1458,3 +1458,39 @@ def test_stdin(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
code, stdout, _ = result
assert stdout == "1: Thsi ==> This\n"
assert code == 1


def test_args_from_file(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
import textwrap

print()
fname1 = tmp_path / "tmp1"
fname2 = tmp_path / "tmp2"
fname3 = tmp_path / "tmp3"
fname_list = tmp_path / "tmp_list"
fname_list.write_text(f"{fname1} {fname2}\n{fname3}")
fname1.write_text("abandonned\ncode")
fname2.write_text("exmaple\n")
fname3.write_text("abilty\n")
print(f"{fname_list=}")
args = ["codespell", f"@{fname_list}"]
print(f"Running: {args=}")
cp = subprocess.run( # noqa: S603
args,
check=False,
text=True,
capture_output=True,
)
code = cp.returncode
stdout = cp.stdout
stderr = cp.stderr
print(f"{code=}")
print(f"stdout:\n{textwrap.indent(stdout, ' ')}")
print(f"stderr:\n{textwrap.indent(stderr, ' ')}")
assert "tmp1:1: abandonned ==> abandoned\n" in stdout, f"{stdout=}"
assert "tmp2:1: exmaple ==> example\n" in stdout, f"{stdout=}"
assert "tmp3:1: abilty ==> ability\n" in stdout, f"{stdout=}"
assert code, f"{code=}"
Loading