lib-cli-exit-tools 2.2.0__py3-none-any.whl → 2.2.2__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.
@@ -24,7 +24,7 @@ name = "lib_cli_exit_tools"
24
24
  #: Human-readable summary shown in CLI help output.
25
25
  title = "CLI exit handling helpers: clean signals, exit codes, and error printing"
26
26
  #: Current release version pulled from ``pyproject.toml`` by automation.
27
- version = "2.2.0"
27
+ version = "2.2.2"
28
28
  #: Repository homepage presented to users.
29
29
  homepage = "https://github.com/bitranox/lib_cli_exit_tools"
30
30
  #: Author attribution surfaced in CLI output.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lib_cli_exit_tools
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: CLI exit handling helpers: clean signals, exit codes, and error printing
5
5
  Project-URL: Homepage, https://github.com/bitranox/lib_cli_exit_tools
6
6
  Project-URL: Repository, https://github.com/bitranox/lib_cli_exit_tools.git
@@ -23,15 +23,15 @@ Requires-Dist: build>=1.3.0; extra == 'dev'
23
23
  Requires-Dist: codecov-cli>=11.2.6; extra == 'dev'
24
24
  Requires-Dist: coverage[toml]>=7.13.0; extra == 'dev'
25
25
  Requires-Dist: hypothesis>=6.148.7; extra == 'dev'
26
- Requires-Dist: import-linter>=2.8; extra == 'dev'
26
+ Requires-Dist: import-linter>=2.9; extra == 'dev'
27
27
  Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
28
28
  Requires-Dist: pyright>=1.1.407; extra == 'dev'
29
29
  Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
30
30
  Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
31
31
  Requires-Dist: pytest>=9.0.2; extra == 'dev'
32
- Requires-Dist: ruff>=0.14.8; extra == 'dev'
32
+ Requires-Dist: rtoml>=0.13.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.14.9; extra == 'dev'
33
34
  Requires-Dist: textual>=6.8.0; extra == 'dev'
34
- Requires-Dist: tomli>=2.3.0; (python_version < '3.11') and extra == 'dev'
35
35
  Requires-Dist: twine>=6.2.0; extra == 'dev'
36
36
  Description-Content-Type: text/markdown
37
37
 
@@ -55,6 +55,20 @@ Small helpers for robust CLI exit handling:
55
55
  - Consistent exception → exit code mapping
56
56
  - Concise error printing with optional traceback and subprocess stdout/stderr capture
57
57
 
58
+ ## Why lib_cli_exit_tools?
59
+
60
+ Building CLI applications that behave correctly in shell pipelines, CI/CD systems, and across platforms is surprisingly tricky. This library solves five common pain points:
61
+
62
+ 1. **Correct Exit Codes** — Exceptions are automatically mapped to platform-appropriate exit codes (POSIX errno or BSD sysexits). `FileNotFoundError` returns `2`, `PermissionError` returns `13` on POSIX, and `KeyboardInterrupt` returns `130` — matching shell conventions without manual bookkeeping.
63
+
64
+ 2. **Portable Signal Handling** — SIGINT (Ctrl+C), SIGTERM, and Windows SIGBREAK are translated into structured Python exceptions with deterministic exit codes. Your CLI behaves consistently whether terminated by a user, a process manager, or a CI runner.
65
+
66
+ 3. **Clean Error Output** — Uncaught exceptions produce concise, coloured error messages by default. Toggle `--traceback` for full Rich-formatted stack traces during debugging — no code changes required.
67
+
68
+ 4. **Pipeline-Friendly** — `BrokenPipeError` is handled gracefully (exit 141 by default, matching `128 + SIGPIPE`), so piping output to `head` or other truncating tools doesn't produce noisy tracebacks.
69
+
70
+ 5. **Zero Boilerplate** — Wrap any Click command with `run_cli()` and get signal handling, exit-code translation, and stream flushing in one call. Configuration is centralised and test-friendly via context managers.
71
+
58
72
  ## Install
59
73
 
60
74
  Requires Python 3.10 or newer.
@@ -77,6 +91,35 @@ lib-cli-exit-tools fail # intentionally trigger RuntimeError to test error path
77
91
  # Aliases are also generated: `cli-exit-tools`, `lib_cli_exit_tools`
78
92
  ```
79
93
 
94
+ ## CLI Reference
95
+
96
+ The library installs three equivalent console scripts: `lib-cli-exit-tools` (primary), `cli-exit-tools`, and `lib_cli_exit_tools`. All invoke `lib_cli_exit_tools.cli:main`.
97
+
98
+ ### Global Options
99
+
100
+ | Option | Default | Description |
101
+ |--------|---------|-------------|
102
+ | `--traceback` / `--no-traceback` | `False` | Show full Python traceback on errors |
103
+ | `--version` | — | Show program version and exit |
104
+ | `-h`, `--help` | — | Show help message and exit |
105
+
106
+ ### Commands
107
+
108
+ #### `info`
109
+ Display package metadata (name, version, homepage, author).
110
+
111
+ ```bash
112
+ lib-cli-exit-tools info
113
+ ```
114
+
115
+ #### `fail`
116
+ Intentionally raise `RuntimeError('i should fail')` to validate error handling paths.
117
+
118
+ ```bash
119
+ lib-cli-exit-tools fail
120
+ lib-cli-exit-tools --traceback fail # show full traceback
121
+ ```
122
+
80
123
  ### Examples
81
124
 
82
125
  The snippets below move from a minimal “hello world” through a production-ready
@@ -109,7 +152,7 @@ Run it with:
109
152
  python hello.py
110
153
  ```
111
154
 
112
- #### 2. Verbose-mode helper using `cli_session`
155
+ #### 2. Traceback-mode helper using `cli_session`
113
156
 
114
157
  ```python
115
158
  from __future__ import annotations
@@ -120,22 +163,22 @@ from lib_cli_exit_tools import cli_session, run_cli
120
163
 
121
164
 
122
165
  @click.command()
123
- @click.option("--verbose", is_flag=True, help="Show full tracebacks on error")
124
- def cli(verbose: bool) -> int:
125
- with cli_session(overrides={"traceback": verbose}) as execute:
166
+ @click.option("--traceback", is_flag=True, help="Show full tracebacks on error")
167
+ def cli(traceback: bool) -> int:
168
+ with cli_session(overrides={"traceback": traceback}) as execute:
126
169
  return execute(failable, argv=[])
127
170
 
128
171
 
129
172
  @click.command()
130
173
  def failable() -> None:
131
- raise RuntimeError("toggle --verbose to see the full traceback")
174
+ raise RuntimeError("toggle --traceback to see the full traceback")
132
175
 
133
176
 
134
177
  if __name__ == "__main__":
135
178
  raise SystemExit(run_cli(cli))
136
179
  ```
137
180
 
138
- Invoking `python cli.py --verbose` enables coloured tracebacks just for that
181
+ Invoking `python cli.py --traceback` enables coloured tracebacks just for that
139
182
  run and resets the configuration afterwards.
140
183
 
141
184
  #### 3. Full-featured multi-command CLI
@@ -154,21 +197,21 @@ from lib_cli_exit_tools import SignalSpec, cli_session, default_signal_specs
154
197
  @dataclass(slots=True)
155
198
  class Settings:
156
199
  pretty: bool
157
- verbose: bool
200
+ traceback: bool
158
201
 
159
202
 
160
203
  @click.group()
161
204
  @click.option("--pretty/--no-pretty", default=True)
162
- @click.option("--verbose", is_flag=True)
205
+ @click.option("--traceback", is_flag=True, help="Show full tracebacks on error")
163
206
  @click.pass_context
164
- def cli(ctx: click.Context, pretty: bool, verbose: bool) -> None:
165
- ctx.obj = Settings(pretty=pretty, verbose=verbose)
207
+ def cli(ctx: click.Context, pretty: bool, traceback: bool) -> None:
208
+ ctx.obj = Settings(pretty=pretty, traceback=traceback)
166
209
 
167
210
 
168
211
  @cli.command()
169
212
  @click.pass_obj
170
213
  def info(settings: Settings) -> None:
171
- payload = {"verbose": settings.verbose, "pretty": settings.pretty}
214
+ payload = {"traceback": settings.traceback, "pretty": settings.pretty}
172
215
  click.echo(json.dumps(payload, indent=2 if settings.pretty else None))
173
216
 
174
217
 
@@ -308,9 +351,12 @@ config.broken_pipe_exit_code = 0 # treat BrokenPipeError as a benign truncat
308
351
 
309
352
  Field reference:
310
353
 
311
- - `traceback` (`bool`, default `False`): when `True`, `handle_cli_exception` renders a full Rich traceback to stderr and then returns a non-zero exit code. The original exception is not re-raised; callers should rely on the rendered output and returned status. The bundled CLI toggles this via `--traceback/--no-traceback`.
312
- - `exit_code_style` (`"errno"` or `"sysexits"`, default `"errno"`): controls the numeric mapping produced by `get_system_exit_code`. `errno` returns POSIX/Windows-style codes (e.g., `FileNotFoundError → 2`, `SIGINT → 130`); `sysexits` returns BSD-style semantic codes (`EX_NOINPUT`, `EX_USAGE`, etc.).
313
- - `broken_pipe_exit_code` (`int`, default `141`): overrides the exit status when a `BrokenPipeError` is raised (the default mirrors `128 + SIGPIPE`). Set this to `0` if you want truncation to be treated as success.
354
+ | Field | Type | Default | Description |
355
+ |-------|------|---------|-------------|
356
+ | `traceback` | `bool` | `False` | When `True`, `handle_cli_exception` renders a full Rich traceback to stderr. The bundled CLI toggles this via `--traceback/--no-traceback`. |
357
+ | `exit_code_style` | `"errno"` \| `"sysexits"` | `"errno"` | Controls exit code mapping. `errno` returns POSIX/Windows-style codes; `sysexits` returns BSD-style semantic codes (EX_USAGE, EX_NOINPUT, etc.). |
358
+ | `broken_pipe_exit_code` | `int` | `141` | Exit status for `BrokenPipeError` (default mirrors `128 + SIGPIPE`). Set to `0` to treat truncation as success. |
359
+ | `traceback_force_color` | `bool` | `False` | Force Rich to emit ANSI-coloured tracebacks even when stderr is not a TTY. Useful for CI logs. |
314
360
 
315
361
  Remember that `config` is module-level—if you call the library from multiple threads or embed it in another CLI, configure it once during bootstrap before handing control to user code. When you need temporary overrides (for tests or nested CLIs), wrap the change with the built-in context manager so state is restored automatically:
316
362
 
@@ -397,22 +443,25 @@ Parameters:
397
443
  - `exception_handler`: Callable receiving the raised exception and returning an exit code; defaults to ``handle_cli_exception``.
398
444
  - `signal_installer`: Callable mirroring ``install_signal_handlers`` for embedding scenarios.
399
445
 
400
- ### `cli_session(*, summary_limit=500, verbose_limit=10_000, overrides=None)`
401
- Context manager that snapshots :mod:`lib_cli_exit_tools.config`, optionally
446
+ ### `cli_session(*, summary_limit=500, verbose_limit=10_000, overrides=None, restore=True)`
447
+ Context manager that snapshots `lib_cli_exit_tools.config`, optionally
402
448
  applies temporary overrides, and yields a callable compatible with
403
- ``run_cli``.
449
+ `run_cli`.
404
450
 
405
451
  Parameters:
406
- - `summary_limit`: Character budget when tracebacks are disabled.
407
- - `verbose_limit`: Character budget when tracebacks are enabled.
408
- - `overrides`: Mapping of configuration field/value pairs applied during the session.
452
+ - `summary_limit` (`int`, default `500`): Character budget when tracebacks are disabled.
453
+ - `verbose_limit` (`int`, default `10_000`): Character budget when tracebacks are enabled.
454
+ - `overrides` (`Mapping[str, object] | None`, default `None`): Mapping of configuration field/value pairs applied during the session. When `traceback` is supplied and `traceback_force_color` is omitted, colour output is automatically forced.
455
+ - `restore` (`bool`, default `True`): When `True`, configuration state is restored after the session. Set to `False` to leave overrides in place once the context exits.
409
456
 
410
457
  Use it to restore configuration automatically—even when the wrapped command
411
458
  raises:
412
459
 
413
460
  ```python
461
+ from lib_cli_exit_tools import cli_session
462
+
414
463
  with cli_session(overrides={"traceback": True}) as run:
415
- exit_code = run(click_command, argv=args)
464
+ exit_code = run(my_click_command, argv=["--help"])
416
465
  ```
417
466
 
418
467
  ### `handle_cli_exception(exc, *, signal_specs=None, echo=None) -> int`
@@ -1,5 +1,5 @@
1
1
  lib_cli_exit_tools/__init__.py,sha256=Z-VXcrmjZCgJZ8NW0UCVIc0HBOsjQicCbzgHj7r22eA,1867
2
- lib_cli_exit_tools/__init__conf__.py,sha256=VHONw-78QrPiN4svMn3R_8KTwGYa4oo-vvpYX844kb8,2521
2
+ lib_cli_exit_tools/__init__conf__.py,sha256=to8gHcedeLf0Tg2RY3NEnAl6xIpKqPcC7vJCAaHJLZY,2521
3
3
  lib_cli_exit_tools/__main__.py,sha256=wODJaNu_NyO7tknvlpPArq0MaDwDum8zqyb73-qshzo,257
4
4
  lib_cli_exit_tools/cli.py,sha256=k7LC08nVfse4i9RbsOGKTc9y-rrVbR1nOSKiwBvNxrE,9322
5
5
  lib_cli_exit_tools/lib_cli_exit_tools.py,sha256=-_kjKGiWyMQ715KxOqrS6uA57SXOgJmMeowU7AmtC8A,2525
@@ -11,8 +11,8 @@ lib_cli_exit_tools/application/runner.py,sha256=AlCWX5o4VTRzsCom9EgnEK0iVCIZwyId
11
11
  lib_cli_exit_tools/core/__init__.py,sha256=aR7OIBWOQ1PsA33blOTgLrq5ffKDCuaMVBS5mbrDAZ8,531
12
12
  lib_cli_exit_tools/core/configuration.py,sha256=Ro6R2cmOIFhdOUogjK1VOxEJInx24Wgme0t-ucYJGEg,5658
13
13
  lib_cli_exit_tools/core/exit_codes.py,sha256=nqx-e6h-ji-FiECPnDV8b0yrjcLPJyU-3xf4zhfRCBE,12322
14
- lib_cli_exit_tools-2.2.0.dist-info/METADATA,sha256=IyZDjP5cbfnxpjyrvJnDTJDLE_uwY2bkdkMaEkmsFrY,23353
15
- lib_cli_exit_tools-2.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
- lib_cli_exit_tools-2.2.0.dist-info/entry_points.txt,sha256=8R-_3UT0KcQ7HQ7idDx6rlG77GhHb0E2N5QnN41dj-M,161
17
- lib_cli_exit_tools-2.2.0.dist-info/licenses/LICENSE,sha256=6s9SL2InRrwynPjJYiER7ONHBshbGj_yC5EBzfo7l9I,1072
18
- lib_cli_exit_tools-2.2.0.dist-info/RECORD,,
14
+ lib_cli_exit_tools-2.2.2.dist-info/METADATA,sha256=GCpsPdPf2U_RHvVKoIpDQ3ybxI321wYU2UkKxMrT3Dk,25942
15
+ lib_cli_exit_tools-2.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ lib_cli_exit_tools-2.2.2.dist-info/entry_points.txt,sha256=8R-_3UT0KcQ7HQ7idDx6rlG77GhHb0E2N5QnN41dj-M,161
17
+ lib_cli_exit_tools-2.2.2.dist-info/licenses/LICENSE,sha256=6s9SL2InRrwynPjJYiER7ONHBshbGj_yC5EBzfo7l9I,1072
18
+ lib_cli_exit_tools-2.2.2.dist-info/RECORD,,