tomlrt-cli 1.0.1__tar.gz

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.
@@ -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,148 @@
1
+ # tomlrt-cli
2
+
3
+ A command-line tool for reading and writing values in TOML files while preserving comments, formatting, and key ordering. Think `jq`, but for TOML.
4
+
5
+ ## Why use this?
6
+
7
+ 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.
8
+
9
+ Common use cases:
10
+
11
+ * Bumping version numbers in `pyproject.toml` from CI
12
+ * Pinning container digests in config files
13
+ * Reading specific values for shell variable assignment
14
+ * Scripted config modifications that respect hand-crafted formatting
15
+
16
+ ## Prerequisites
17
+
18
+ * Python 3.12 or newer
19
+ * [uv](https://docs.astral.sh/uv/) (for development and running from source)
20
+
21
+ No prerequisites for end users — install and run directly with `uvx`:
22
+
23
+ ```bash
24
+ uvx tomlrt-cli --help
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Read an entire file
30
+
31
+ ```bash
32
+ tomlrt-cli config.toml
33
+ ```
34
+
35
+ Outputs the full document to stdout (comments and formatting preserved).
36
+
37
+ ### Read a specific value
38
+
39
+ ```bash
40
+ tomlrt-cli --path project.version pyproject.toml
41
+ ```
42
+
43
+ Outputs just the value at that path.
44
+
45
+ ### Write a value (to stdout)
46
+
47
+ ```bash
48
+ tomlrt-cli --path project.version --value "2.0.0" pyproject.toml
49
+ ```
50
+
51
+ Outputs the full modified document to stdout. The original file is not changed.
52
+
53
+ ### Write a value (to file)
54
+
55
+ ```bash
56
+ tomlrt-cli --path project.version --value "2.0.0" --output pyproject.toml pyproject.toml
57
+ ```
58
+
59
+ Writes the modified document to the output file. Use the same path for input and output to edit in-place.
60
+
61
+ ### Path syntax
62
+
63
+ Keys are separated by dots. Keys that contain dots or special characters can be quoted or bracketed:
64
+
65
+ ```bash
66
+ # Simple dotted path
67
+ tomlrt-cli --path version.v26.alpine config.toml
68
+
69
+ # Quoted key (key "3.14" contains a dot)
70
+ tomlrt-cli --path 'version."3.14".alpine' config.toml
71
+
72
+ # Bracket notation (same result)
73
+ tomlrt-cli --path 'version["3.14"].alpine' config.toml
74
+ ```
75
+
76
+ All three notations are interchangeable and can be mixed in a single path.
77
+
78
+ ### Flags
79
+
80
+ | Flag | Short | Description |
81
+ |-------------|-------|---------------------------------------------|
82
+ | `--path` | `-p` | TOML key path to read or write |
83
+ | `--value` | `-v` | Value to assign at the given path |
84
+ | `--output` | `-o` | Write result to this file (default: stdout) |
85
+ | `--version` | `-V` | Print version and exit |
86
+ | `--help` | `-h` | Print help and exit |
87
+
88
+ ## Development
89
+
90
+ ### Setup
91
+
92
+ ```bash
93
+ git clone <repo-url>
94
+ cd tomlrt-cli
95
+ uv sync
96
+ ```
97
+
98
+ ### Running tests
99
+
100
+ This project uses [Tryke](https://tryke.dev), a Rust-based test runner with a Jest-style API:
101
+
102
+ ```bash
103
+ task test
104
+ ```
105
+
106
+ The test suite has two files:
107
+
108
+ * `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.
109
+ * `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.
110
+
111
+ ### Linting and formatting
112
+
113
+ ```bash
114
+ task lint
115
+ ```
116
+
117
+ ### Build and publish
118
+
119
+ ```bash
120
+ task build # Produces sdist + wheel in dist/
121
+ task publish # Upload to PyPI
122
+ ```
123
+
124
+ ## Troubleshooting
125
+
126
+ ### `KeyError` when using `--path`
127
+
128
+ 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.
129
+
130
+ ### Quoted keys not working
131
+
132
+ Make sure your shell isn't stripping the quotes. Wrap the entire `--path` value in single quotes:
133
+
134
+ ```bash
135
+ tomlrt-cli --path 'version."3.14".alpine' config.toml
136
+ ```
137
+
138
+ ### `--value` didn't change the file
139
+
140
+ Without `--output`, changes go to stdout only. Add `--output <file>` to persist:
141
+
142
+ ```bash
143
+ tomlrt-cli --path key --value new --output config.toml config.toml
144
+ ```
145
+
146
+ ### `ModuleNotFoundError: tomlrt`
147
+
148
+ Run via `uv run tomlrt-cli` (which manages the virtualenv) or install with `uv sync` first.
@@ -0,0 +1,47 @@
1
+ [project]
2
+ name = "tomlrt-cli"
3
+ version = "1.0.1"
4
+ description = "Read and write values in TOML files while preserving comments and formatting."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ authors = [ { email = "ryan@ryanparman.com", name = "Ryan Parman" } ]
9
+ keywords = [ "cli", "config", "edit", "rust", "toml" ]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Environment :: Console",
13
+ "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Programming Language :: Python :: 3.14",
18
+ "Topic :: Software Development :: Libraries",
19
+ "Topic :: Utilities",
20
+ "Typing :: Typed",
21
+ ]
22
+ dependencies = [ "tomlrt>=1.7.2,<2" ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/northwood-labs/tomlrt-cli"
26
+ Issues = "https://github.com/northwood-labs/tomlrt-cli/issues"
27
+ Repository = "https://github.com/northwood-labs/tomlrt-cli"
28
+
29
+ [project.scripts]
30
+ tomlrt-cli = "tomlrt_cli.cli:main"
31
+
32
+ [dependency-groups]
33
+ dev = [ "hypothesis>=6.155.1,<7", "ruff>=0.15,<1", "tryke>=0.0.29,<1", "zuban>=0.8,<1" ]
34
+
35
+ [build-system]
36
+ requires = [ "uv_build>=0.11.17,<0.12.0" ]
37
+ build-backend = "uv_build"
38
+
39
+ [tool.ruff]
40
+ target-version = "py312"
41
+ line-length = 120
42
+
43
+ [tool.ruff.lint]
44
+ select = [ "E", "F", "I", "N", "W", "UP" ]
45
+
46
+ [tool.tryke]
47
+ src = [ ".", "src" ]
@@ -0,0 +1 @@
1
+ """tomlrt-cli."""
@@ -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())
File without changes