Skip to content

Commit 125f7ee

Browse files
authored
Allow for reading TOML files from stdin. (#239)
1 parent 7f59a07 commit 125f7ee

File tree

3 files changed

+34
-24
lines changed

3 files changed

+34
-24
lines changed

src/pyproject_fmt/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def _handle_one(config: Config) -> bool:
3838
formatted = format_toml(config.toml, config.settings)
3939
before = config.toml
4040
changed = before != formatted
41-
if config.stdout: # stdout just prints new format to stdout
41+
if config.pyproject_toml is None or config.stdout: # when reading from stdin or writing to stdout, print new format
4242
print(formatted, end="") # noqa: T201
4343
return changed
4444

src/pyproject_fmt/cli.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,23 @@ class PyProjectFmtNamespace(Namespace):
4141
class Config:
4242
"""Configuration flags for the formatting."""
4343

44-
pyproject_toml: Path
45-
stdout: bool # push to standard out
44+
pyproject_toml: Path | None # path to the toml file or None if stdin
45+
toml: str # the toml file content
46+
stdout: bool # push to standard out, implied if reading from stdin
4647
check: bool # check only
4748
no_print_diff: bool # don't print diff
4849
settings: Settings
4950

50-
@property
51-
def toml(self) -> str:
52-
""":return: the toml files content"""
53-
return self.pyproject_toml.read_text(encoding="utf-8")
5451

55-
56-
def pyproject_toml_path_creator(argument: str) -> Path:
52+
def pyproject_toml_path_creator(argument: str) -> Path | None:
5753
"""
5854
Validate that pyproject.toml can be formatted.
5955
6056
:param argument: the string argument passed in
61-
:return: the pyproject.toml path
57+
:return: the pyproject.toml path or None if stdin
6258
"""
59+
if argument == "-":
60+
return None # stdin, no further validation needed
6361
path = Path(argument).absolute()
6462
if path.is_dir():
6563
path /= "pyproject.toml"
@@ -105,7 +103,7 @@ def _build_cli() -> ArgumentParser:
105103

106104
mode_group = parser.add_argument_group("run mode")
107105
mode = mode_group.add_mutually_exclusive_group()
108-
msg = "print the formatted TOML to the stdout"
106+
msg = "print the formatted TOML to the stdout, implied if reading from stdin"
109107
mode.add_argument("-s", "--stdout", action="store_true", help=msg)
110108
msg = "check and fail if any input would be formatted, printing any diffs"
111109
mode.add_argument("--check", action="store_true", help=msg)
@@ -141,7 +139,7 @@ def _build_cli() -> ArgumentParser:
141139
help="latest Python version the project supports (e.g. 3.13)",
142140
)
143141

144-
msg = "pyproject.toml file(s) to format"
142+
msg = "pyproject.toml file(s) to format, use '-' to read from stdin"
145143
parser.add_argument(
146144
"inputs",
147145
nargs="+",
@@ -167,21 +165,22 @@ def cli_args(args: Sequence[str]) -> list[Config]:
167165
indent = opt.indent
168166
keep_full_version = opt.keep_full_version
169167
max_supported_python = opt.max_supported_python
170-
with pyproject_toml.open("rb") as file_handler:
171-
config = tomllib.load(file_handler)
172-
if "tool" in config and "pyproject-fmt" in config["tool"]:
173-
for key, entry in config["tool"]["pyproject-fmt"].items():
174-
if key == "column_width":
175-
column_width = int(entry)
176-
elif key == "indent":
177-
indent = int(entry)
178-
elif key == "keep_full_version":
179-
keep_full_version = bool(entry)
180-
elif key == "max_supported_python":
181-
max_supported_python = _version_argument(entry)
168+
raw_pyproject_toml = sys.stdin.read() if pyproject_toml is None else pyproject_toml.read_text(encoding="utf-8")
169+
config = tomllib.loads(raw_pyproject_toml)
170+
if "tool" in config and "pyproject-fmt" in config["tool"]:
171+
for key, entry in config["tool"]["pyproject-fmt"].items():
172+
if key == "column_width":
173+
column_width = int(entry)
174+
elif key == "indent":
175+
indent = int(entry)
176+
elif key == "keep_full_version":
177+
keep_full_version = bool(entry)
178+
elif key == "max_supported_python":
179+
max_supported_python = _version_argument(entry)
182180
res.append(
183181
Config(
184182
pyproject_toml=pyproject_toml,
183+
toml=raw_pyproject_toml,
185184
stdout=opt.stdout,
186185
check=opt.check,
187186
no_print_diff=opt.no_print_diff,

tests/test_cli.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import io
34
import os
45
import sys
56
from importlib.metadata import version
@@ -13,6 +14,8 @@
1314
if TYPE_CHECKING:
1415
from pathlib import Path
1516

17+
from pytest_mock import MockerFixture
18+
1619

1720
def test_cli_version(capsys: pytest.CaptureFixture[str]) -> None:
1821
with pytest.raises(SystemExit) as context:
@@ -65,6 +68,14 @@ def test_cli_inputs_ok(tmp_path: Path) -> None:
6568
assert len(result) == 3
6669

6770

71+
def test_cli_pyproject_toml_stdin(mocker: MockerFixture) -> None:
72+
mocker.patch("pyproject_fmt.cli.sys.stdin", io.StringIO(""))
73+
result = cli_args(["-"])
74+
assert len(result) == 1
75+
assert result[0].pyproject_toml is None
76+
assert not result[0].toml
77+
78+
6879
def test_cli_pyproject_toml_not_exists(
6980
tmp_path: Path,
7081
capsys: pytest.CaptureFixture[str],

0 commit comments

Comments
 (0)