Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for reading TOML files from stdin. #239

Merged
Merged
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
2 changes: 1 addition & 1 deletion src/pyproject_fmt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def _handle_one(config: Config) -> bool:
formatted = format_toml(config.toml, config.settings)
before = config.toml
changed = before != formatted
if config.stdout: # stdout just prints new format to stdout
if config.pyproject_toml is None or config.stdout: # when reading from stdin or writing to stdout, print new format
print(formatted, end="") # noqa: T201
return changed

Expand Down
45 changes: 22 additions & 23 deletions src/pyproject_fmt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,23 @@ class PyProjectFmtNamespace(Namespace):
class Config:
"""Configuration flags for the formatting."""

pyproject_toml: Path
stdout: bool # push to standard out
pyproject_toml: Path | None # path to the toml file or None if stdin
toml: str # the toml file content
stdout: bool # push to standard out, implied if reading from stdin
check: bool # check only
no_print_diff: bool # don't print diff
settings: Settings

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


def pyproject_toml_path_creator(argument: str) -> Path:
def pyproject_toml_path_creator(argument: str) -> Path | None:
"""
Validate that pyproject.toml can be formatted.

:param argument: the string argument passed in
:return: the pyproject.toml path
:return: the pyproject.toml path or None if stdin
"""
if argument == "-":
gaborbernat marked this conversation as resolved.
Show resolved Hide resolved
return None # stdin, no further validation needed
path = Path(argument).absolute()
if path.is_dir():
path /= "pyproject.toml"
Expand Down Expand Up @@ -105,7 +103,7 @@ def _build_cli() -> ArgumentParser:

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

msg = "pyproject.toml file(s) to format"
msg = "pyproject.toml file(s) to format, use '-' to read from stdin"
parser.add_argument(
"inputs",
nargs="+",
Expand All @@ -167,21 +165,22 @@ def cli_args(args: Sequence[str]) -> list[Config]:
indent = opt.indent
keep_full_version = opt.keep_full_version
max_supported_python = opt.max_supported_python
with pyproject_toml.open("rb") as file_handler:
config = tomllib.load(file_handler)
if "tool" in config and "pyproject-fmt" in config["tool"]:
for key, entry in config["tool"]["pyproject-fmt"].items():
if key == "column_width":
column_width = int(entry)
elif key == "indent":
indent = int(entry)
elif key == "keep_full_version":
keep_full_version = bool(entry)
elif key == "max_supported_python":
max_supported_python = _version_argument(entry)
raw_pyproject_toml = sys.stdin.read() if pyproject_toml is None else pyproject_toml.read_text(encoding="utf-8")
config = tomllib.loads(raw_pyproject_toml)
if "tool" in config and "pyproject-fmt" in config["tool"]:
for key, entry in config["tool"]["pyproject-fmt"].items():
if key == "column_width":
column_width = int(entry)
elif key == "indent":
indent = int(entry)
elif key == "keep_full_version":
keep_full_version = bool(entry)
elif key == "max_supported_python":
max_supported_python = _version_argument(entry)
res.append(
Config(
pyproject_toml=pyproject_toml,
toml=raw_pyproject_toml,
stdout=opt.stdout,
check=opt.check,
no_print_diff=opt.no_print_diff,
Expand Down
11 changes: 11 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import io
import os
import sys
from importlib.metadata import version
Expand All @@ -13,6 +14,8 @@
if TYPE_CHECKING:
from pathlib import Path

from pytest_mock import MockerFixture


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


def test_cli_pyproject_toml_stdin(mocker: MockerFixture) -> None:
mocker.patch("pyproject_fmt.cli.sys.stdin", io.StringIO(""))
result = cli_args(["-"])
assert len(result) == 1
assert result[0].pyproject_toml is None
assert not result[0].toml


def test_cli_pyproject_toml_not_exists(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
Expand Down
Loading