tomlrt-cli 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tomlrt_cli/__init__.py +1 -0
- tomlrt_cli/cli.py +139 -0
- tomlrt_cli/py.typed +0 -0
- tomlrt_cli-1.0.1.dist-info/METADATA +173 -0
- tomlrt_cli-1.0.1.dist-info/RECORD +7 -0
- tomlrt_cli-1.0.1.dist-info/WHEEL +4 -0
- tomlrt_cli-1.0.1.dist-info/entry_points.txt +3 -0
tomlrt_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""tomlrt-cli."""
|
tomlrt_cli/cli.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for tomlrt-cli.
|
|
3
|
+
|
|
4
|
+
This module is the sole user-facing interface. It wires argument parsing to
|
|
5
|
+
tomlrt operations so that shell scripts and CI pipelines can read or
|
|
6
|
+
manipulate TOML files without writing Python.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
from importlib.metadata import version
|
|
13
|
+
|
|
14
|
+
import tomlrt
|
|
15
|
+
|
|
16
|
+
# ------------------------------------------------------------------------------
|
|
17
|
+
# PRIVATE FUNCTIONS
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _parse_path(path: str) -> tuple[str, ...]:
|
|
21
|
+
"""
|
|
22
|
+
Convert a user-supplied key path string into a tuple of literal key
|
|
23
|
+
segments that tomlrt can traverse.
|
|
24
|
+
|
|
25
|
+
tomlrt's `install()` accepts either a dot-separated string (which it
|
|
26
|
+
naively splits on `.`) or a tuple of literal segments. Naive splitting
|
|
27
|
+
breaks when a key itself contains a dot (e.g., version "3.14"). This
|
|
28
|
+
parser exists to let users express such keys with quoting or bracket
|
|
29
|
+
notation while still producing the tuple that tomlrt needs.
|
|
30
|
+
"""
|
|
31
|
+
segments: list[str] = []
|
|
32
|
+
i = 0
|
|
33
|
+
|
|
34
|
+
while i < len(path):
|
|
35
|
+
# Dots are separators, not content — skip them.
|
|
36
|
+
if path[i] == ".":
|
|
37
|
+
i += 1
|
|
38
|
+
|
|
39
|
+
elif path[i] == "[":
|
|
40
|
+
# Bracket notation (version["3.14"]) lets users reference keys
|
|
41
|
+
# that would be ambiguous in dotted form because the key value
|
|
42
|
+
# itself contains dots or special characters.
|
|
43
|
+
i += 1
|
|
44
|
+
|
|
45
|
+
if i < len(path) and path[i] in ('"', "'"):
|
|
46
|
+
quote = path[i]
|
|
47
|
+
i += 1
|
|
48
|
+
|
|
49
|
+
end = path.index(quote, i)
|
|
50
|
+
segments.append(path[i:end])
|
|
51
|
+
i = end + 1
|
|
52
|
+
|
|
53
|
+
if i < len(path) and path[i] == "]":
|
|
54
|
+
i += 1
|
|
55
|
+
|
|
56
|
+
else:
|
|
57
|
+
# Unquoted bracket content — treat everything up to `]`
|
|
58
|
+
# as the literal key name.
|
|
59
|
+
end = path.index("]", i)
|
|
60
|
+
segments.append(path[i:end])
|
|
61
|
+
i = end + 1
|
|
62
|
+
|
|
63
|
+
elif path[i] in ('"', "'"):
|
|
64
|
+
# Inline quoted key (version."3.14".alpine) — the quotes
|
|
65
|
+
# protect the enclosed value from being split on dots.
|
|
66
|
+
quote = path[i]
|
|
67
|
+
i += 1
|
|
68
|
+
|
|
69
|
+
end = path.index(quote, i)
|
|
70
|
+
segments.append(path[i:end])
|
|
71
|
+
i = end + 1
|
|
72
|
+
|
|
73
|
+
else:
|
|
74
|
+
# Bare key — consume everything until the next delimiter.
|
|
75
|
+
match = re.match(r'[^.\["\'\]]+', path[i:])
|
|
76
|
+
|
|
77
|
+
if match:
|
|
78
|
+
segments.append(match.group())
|
|
79
|
+
i += match.end()
|
|
80
|
+
|
|
81
|
+
return tuple(segments)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ------------------------------------------------------------------------------
|
|
85
|
+
# PUBLIC FUNCTIONS
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main() -> int:
|
|
89
|
+
"""
|
|
90
|
+
Parse CLI arguments and perform the requested TOML read operation.
|
|
91
|
+
|
|
92
|
+
Designed to behave like jq for TOML: read a file, optionally drill into a
|
|
93
|
+
specific path, and emit the result to stdout or a file.
|
|
94
|
+
"""
|
|
95
|
+
parser = argparse.ArgumentParser(prog="tomlrt-cli", description="tomlrt-cli")
|
|
96
|
+
parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {version('tomlrt-cli')}")
|
|
97
|
+
parser.add_argument("-o", "--output", help="output file path (default: stdout)")
|
|
98
|
+
parser.add_argument("-p", "--path", help="TOML key path")
|
|
99
|
+
parser.add_argument("-v", "--value", help="value to assign to the TOML key path")
|
|
100
|
+
parser.add_argument("input", help="input file")
|
|
101
|
+
args = parser.parse_args()
|
|
102
|
+
|
|
103
|
+
with open(args.input, "rb") as f:
|
|
104
|
+
doc = tomlrt.load(f)
|
|
105
|
+
|
|
106
|
+
if args.path:
|
|
107
|
+
path = _parse_path(args.path)
|
|
108
|
+
node = doc
|
|
109
|
+
|
|
110
|
+
# Walk intermediate segments to reach the parent table. We stop one
|
|
111
|
+
# short so we can index the final key for its value rather than
|
|
112
|
+
# descending into it as a table.
|
|
113
|
+
for segment in path[:-1]:
|
|
114
|
+
node = node[segment]
|
|
115
|
+
|
|
116
|
+
if args.value:
|
|
117
|
+
# Write mode: set the value at the path, then emit the full modified
|
|
118
|
+
# document. The caller controls where it lands via --output (file)
|
|
119
|
+
# or stdout (default).
|
|
120
|
+
node[path[-1]] = args.value
|
|
121
|
+
result = tomlrt.dumps(doc)
|
|
122
|
+
else:
|
|
123
|
+
result = str(node[path[-1]])
|
|
124
|
+
else:
|
|
125
|
+
# No path specified — emit the entire document, preserving comments and
|
|
126
|
+
# formatting via tomlrt's round-trip serialiser.
|
|
127
|
+
result = tomlrt.dumps(doc)
|
|
128
|
+
|
|
129
|
+
if args.output:
|
|
130
|
+
with open(args.output, "w") as f:
|
|
131
|
+
f.write(result)
|
|
132
|
+
else:
|
|
133
|
+
sys.stdout.write(result)
|
|
134
|
+
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
sys.exit(main())
|
tomlrt_cli/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tomlrt-cli
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Read and write values in TOML files while preserving comments and formatting.
|
|
5
|
+
Keywords: cli,config,edit,rust,toml
|
|
6
|
+
Author: Ryan Parman
|
|
7
|
+
Author-email: Ryan Parman <ryan@ryanparman.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: tomlrt>=1.7.2,<2
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Project-URL: Homepage, https://github.com/northwood-labs/tomlrt-cli
|
|
22
|
+
Project-URL: Issues, https://github.com/northwood-labs/tomlrt-cli/issues
|
|
23
|
+
Project-URL: Repository, https://github.com/northwood-labs/tomlrt-cli
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# tomlrt-cli
|
|
27
|
+
|
|
28
|
+
A command-line tool for reading and writing values in TOML files while preserving comments, formatting, and key ordering. Think `jq`, but for TOML.
|
|
29
|
+
|
|
30
|
+
## Why use this?
|
|
31
|
+
|
|
32
|
+
Most TOML libraries either can't write (Python's `tomllib`) or destroy comments and formatting when they do (`tomli-w`). `tomlrt-cli` uses a round-trip parser under the hood, so you can update a single value in a TOML file from a shell script or CI pipeline without mangling the rest of the document.
|
|
33
|
+
|
|
34
|
+
Common use cases:
|
|
35
|
+
|
|
36
|
+
* Bumping version numbers in `pyproject.toml` from CI
|
|
37
|
+
* Pinning container digests in config files
|
|
38
|
+
* Reading specific values for shell variable assignment
|
|
39
|
+
* Scripted config modifications that respect hand-crafted formatting
|
|
40
|
+
|
|
41
|
+
## Prerequisites
|
|
42
|
+
|
|
43
|
+
* Python 3.12 or newer
|
|
44
|
+
* [uv](https://docs.astral.sh/uv/) (for development and running from source)
|
|
45
|
+
|
|
46
|
+
No prerequisites for end users — install and run directly with `uvx`:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uvx tomlrt-cli --help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
### Read an entire file
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
tomlrt-cli config.toml
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Outputs the full document to stdout (comments and formatting preserved).
|
|
61
|
+
|
|
62
|
+
### Read a specific value
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
tomlrt-cli --path project.version pyproject.toml
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Outputs just the value at that path.
|
|
69
|
+
|
|
70
|
+
### Write a value (to stdout)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
tomlrt-cli --path project.version --value "2.0.0" pyproject.toml
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Outputs the full modified document to stdout. The original file is not changed.
|
|
77
|
+
|
|
78
|
+
### Write a value (to file)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
tomlrt-cli --path project.version --value "2.0.0" --output pyproject.toml pyproject.toml
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Writes the modified document to the output file. Use the same path for input and output to edit in-place.
|
|
85
|
+
|
|
86
|
+
### Path syntax
|
|
87
|
+
|
|
88
|
+
Keys are separated by dots. Keys that contain dots or special characters can be quoted or bracketed:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Simple dotted path
|
|
92
|
+
tomlrt-cli --path version.v26.alpine config.toml
|
|
93
|
+
|
|
94
|
+
# Quoted key (key "3.14" contains a dot)
|
|
95
|
+
tomlrt-cli --path 'version."3.14".alpine' config.toml
|
|
96
|
+
|
|
97
|
+
# Bracket notation (same result)
|
|
98
|
+
tomlrt-cli --path 'version["3.14"].alpine' config.toml
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
All three notations are interchangeable and can be mixed in a single path.
|
|
102
|
+
|
|
103
|
+
### Flags
|
|
104
|
+
|
|
105
|
+
| Flag | Short | Description |
|
|
106
|
+
|-------------|-------|---------------------------------------------|
|
|
107
|
+
| `--path` | `-p` | TOML key path to read or write |
|
|
108
|
+
| `--value` | `-v` | Value to assign at the given path |
|
|
109
|
+
| `--output` | `-o` | Write result to this file (default: stdout) |
|
|
110
|
+
| `--version` | `-V` | Print version and exit |
|
|
111
|
+
| `--help` | `-h` | Print help and exit |
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
### Setup
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
git clone <repo-url>
|
|
119
|
+
cd tomlrt-cli
|
|
120
|
+
uv sync
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Running tests
|
|
124
|
+
|
|
125
|
+
This project uses [Tryke](https://tryke.dev), a Rust-based test runner with a Jest-style API:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
task test
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The test suite has two files:
|
|
132
|
+
|
|
133
|
+
* `tests/test_cli.py` — Integration tests that exercise `main()` end-to-end with temporary TOML files. Verifies argument parsing, file I/O, and output routing.
|
|
134
|
+
* `tests/test_parse_path.py` — Unit tests for the path parser covering dotted, quoted, and bracket notation. Includes a [Hypothesis](https://hypothesis.readthedocs.io/) property test that fuzzes bare-key round-tripping to catch edge cases.
|
|
135
|
+
|
|
136
|
+
### Linting and formatting
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
task lint
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Build and publish
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
task build # Produces sdist + wheel in dist/
|
|
146
|
+
task publish # Upload to PyPI
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Troubleshooting
|
|
150
|
+
|
|
151
|
+
### `KeyError` when using `--path`
|
|
152
|
+
|
|
153
|
+
The path does not exist in the document. Double-check key names — TOML keys are case-sensitive. Run without `--path` to see the full document structure.
|
|
154
|
+
|
|
155
|
+
### Quoted keys not working
|
|
156
|
+
|
|
157
|
+
Make sure your shell isn't stripping the quotes. Wrap the entire `--path` value in single quotes:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
tomlrt-cli --path 'version."3.14".alpine' config.toml
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `--value` didn't change the file
|
|
164
|
+
|
|
165
|
+
Without `--output`, changes go to stdout only. Add `--output <file>` to persist:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
tomlrt-cli --path key --value new --output config.toml config.toml
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `ModuleNotFoundError: tomlrt`
|
|
172
|
+
|
|
173
|
+
Run via `uv run tomlrt-cli` (which manages the virtualenv) or install with `uv sync` first.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
tomlrt_cli/__init__.py,sha256=RFovh0Lz3SS_BQ0MMQyuPCpWgbx8v6vJbNH42qlq-50,18
|
|
2
|
+
tomlrt_cli/cli.py,sha256=tIEa6ykI5es0EkSIqpNsJ5o453Wtr5RRp3BC1QMDSS8,4537
|
|
3
|
+
tomlrt_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
tomlrt_cli-1.0.1.dist-info/WHEEL,sha256=LlB9zUOn921TC3CC5yCeS6O5jsLlxqKqIpg_Zk6XXcQ,81
|
|
5
|
+
tomlrt_cli-1.0.1.dist-info/entry_points.txt,sha256=am2FX0_hx05Hmui2b0LPG9n4_9jEhzcV4MkHcL23wGw,52
|
|
6
|
+
tomlrt_cli-1.0.1.dist-info/METADATA,sha256=hBLkO_AcLqf15SuLIgzYud5INWtYnqR9SOskJ1nkeCk,5194
|
|
7
|
+
tomlrt_cli-1.0.1.dist-info/RECORD,,
|