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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## \[Unreleased\]

### Changed

- Replace tomlkit with tomlrt as the TOML reader/writer
- Read and write toml files in binary mode, so that CRLF line endings are preserved

## \[0.8.2\]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ requires-python = ">=3.10"
dependencies = [
"jmespath>=1.0.1",
"regex>=2020.7.14",
"tomlkit>=0.13.3",
"tomlrt>=2.2.2",
"typer>=0.16.0",
]
authors = [{ name = "Marc Rijken", email = "marc@rijken.org" }]
Expand Down
4 changes: 2 additions & 2 deletions tests/toml_cli/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def test_add_section(tmp_path: pathlib.Path):

result = runner.invoke(app, ["add_section", "--toml-path", str(test_toml_path), "address.work"])
assert result.exit_code == 0
assert "[address]\n[address.work]" in test_toml_path.read_text()
assert "[address]\n\n[address.work]" in test_toml_path.read_text()


def test_set_in_out_of_order_table(tmp_path: pathlib.Path):
Expand Down Expand Up @@ -308,4 +308,4 @@ def test_unset(tmp_path: pathlib.Path):

result = runner.invoke(app, ["unset", "--toml-path", str(test_toml_path), "person"])
assert result.exit_code == 0
assert len(test_toml_path.read_text()) == 1 # just a newline
assert test_toml_path.read_text() == ""
46 changes: 28 additions & 18 deletions toml_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
import pathlib
import re

import tomlkit
import tomlkit.exceptions
import tomlrt
import typer

import jmespath
Expand All @@ -21,14 +20,24 @@ def path_callback(value: pathlib.Path):
return value


def read_toml(toml_path: pathlib.Path) -> tomlrt.Document:
with toml_path.open("rb") as toml_file:
return tomlrt.load(toml_file)


def write_toml(document: tomlrt.Document, toml_path: pathlib.Path) -> None:
with toml_path.open("wb") as toml_file:
tomlrt.dump(document, toml_file)


@app.command("get")
def get(
key: str | None = typer.Argument(None),
toml_path: pathlib.Path = typer.Option(DEFAULT_TOML_PATH, callback=path_callback),
default: str | None = typer.Option(None),
):
"""Get a value from a toml file."""
toml_part = tomlkit.parse(toml_path.read_text())
toml_part = read_toml(toml_path)

if key is not None:
for key_part in key.split("."):
Expand Down Expand Up @@ -64,7 +73,7 @@ def get(
typer.echo(f"error: key '{key_part}' not found", err=True)
raise typer.Exit(code=1) from err

typer.echo(toml_part.unwrap() if hasattr(toml_part, "unwrap") else toml_part)
typer.echo(toml_part)


@app.command("set")
Expand All @@ -81,12 +90,12 @@ def set_(
),
):
"""Set a value to a toml file."""
toml_part = toml_file = tomlkit.parse(toml_path.read_text())
toml_part = toml_file = read_toml(toml_path)

for key_part in key.split(".")[:-1]:
try:
toml_part = toml_part[key_part]
except tomlkit.exceptions.NonExistentKey as err:
except KeyError as err:
typer.echo(
f"error: non-existent key '{key}' can not be set to value '{value}'",
err=True,
Expand Down Expand Up @@ -115,7 +124,7 @@ def set_(
array = match.group("array")
try:
toml_part = toml_part[array]
except tomlkit.exceptions.NonExistentKey as err:
except KeyError as err:
typer.echo(f"error: non-existent array '{array}'", err=True)
raise typer.Exit(code=1) from err

Expand All @@ -127,7 +136,7 @@ def set_(
else:
toml_part[last_key] = parsed_value

toml_path.write_text(tomlkit.dumps(toml_file))
write_toml(toml_file, toml_path)


@app.command("add_section")
Expand All @@ -136,14 +145,15 @@ def add_section(
toml_path: pathlib.Path = typer.Option(DEFAULT_TOML_PATH, callback=path_callback),
):
"""Add a section with the given key."""
toml_part = toml_file = tomlkit.parse(toml_path.read_text())
toml_file = read_toml(toml_path)

for key_part in key.split("."):
if key_part not in toml_part:
toml_part[key_part] = tomlkit.table()
toml_part = toml_part[key_part]
try:
toml_file.ensure_table(key)
except tomlrt.TOMLError as err:
typer.echo(f"error: can not add section '{key}' - {err}", err=True)
raise typer.Exit(code=1) from err

toml_path.write_text(tomlkit.dumps(toml_file))
write_toml(toml_file, toml_path)


@app.command("unset")
Expand All @@ -152,17 +162,17 @@ def unset(
toml_path: pathlib.Path = typer.Option(DEFAULT_TOML_PATH, callback=path_callback),
):
"""Unset a value from a toml file."""
toml_part = toml_file = tomlkit.parse(toml_path.read_text())
toml_part = toml_file = read_toml(toml_path)

for key_part in key.split(".")[:-1]:
try:
toml_part = toml_part[key_part]
except tomlkit.exceptions.NonExistentKey:
except KeyError:
typer.echo(f"Key {key} can not unset", err=True)

del toml_part[key.split(".")[-1]]

toml_path.write_text(tomlkit.dumps(toml_file))
write_toml(toml_file, toml_path)


@app.command("search")
Expand All @@ -171,7 +181,7 @@ def search(
toml_path: pathlib.Path = typer.Option(DEFAULT_TOML_PATH, callback=path_callback),
):
"""Search for a value in a toml file using JMESPath query."""
toml_data = tomlkit.parse(toml_path.read_text())
toml_data = read_toml(toml_path)

try:
result = jmespath.search(jmespath_expression, toml_data) or "No result found"
Expand Down
15 changes: 9 additions & 6 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.