lib-cli-exit-tools 1.4.0__tar.gz → 1.5.0__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.
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/CHANGELOG.md +4 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/PKG-INFO +12 -8
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/README.md +4 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/docs/system-design/module_reference.md +5 -1
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/notebooks/Quickstart.ipynb +73 -1
- lib_cli_exit_tools-1.5.0/notebooks/Quickstart.ipynb.Backup +281 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/packaging/brew/Formula/lib-cli-exit-tools.rb +1 -1
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/packaging/conda/recipe/meta.yaml +1 -1
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/packaging/nix/flake.nix +1 -1
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/pyproject.toml +8 -8
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/__init__.py +2 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/application/runner.py +4 -3
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/cli.py +25 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/lib_cli_exit_tools.py +22 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/tests/application/test_runner.py +18 -4
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/tests/cli/test_cli_entrypoints.py +16 -1
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/.env.example +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/.github/workflows/ci.yml +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/.github/workflows/codeql.yml +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/.github/workflows/release.yml +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/.gitignore +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/AGENTS.md +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/CONTRIBUTING.md +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/DEVELOPMENT.md +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/INSTALL.md +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/LICENSE +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/Makefile +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/codecov.yml +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/packaging/README.md +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/qlty.toml +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/__init__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/_utils.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/build.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/bump.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/bump_major.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/bump_minor.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/bump_patch.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/bump_version.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/clean.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/dev.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/generate_nix_flake.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/install.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/menu.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/push.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/release.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/run_cli.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/test.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/scripts/version_current.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/__init__conf__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/__main__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/adapters/__init__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/adapters/signals.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/application/__init__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/__init__.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/configuration.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/exit_codes.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/py.typed +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/tests/adapters/test_signals.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/tests/conftest.py +0 -0
- {lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/tests/core/test_exit_codes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_cli_exit_tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
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
|
|
@@ -18,15 +18,15 @@ Classifier: Typing :: Typed
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Requires-Dist: rich-click>=1.9.1
|
|
20
20
|
Provides-Extra: dev
|
|
21
|
-
Requires-Dist: build>=1.
|
|
21
|
+
Requires-Dist: build>=1.3; extra == 'dev'
|
|
22
22
|
Requires-Dist: codecov>=2.1.13; extra == 'dev'
|
|
23
23
|
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
24
|
-
Requires-Dist: pytest-cov>=
|
|
25
|
-
Requires-Dist: pytest>=8.
|
|
26
|
-
Requires-Dist: ruff>=0.
|
|
27
|
-
Requires-Dist: textual>=0
|
|
28
|
-
Requires-Dist: tomli>=2.
|
|
29
|
-
Requires-Dist: twine>=
|
|
24
|
+
Requires-Dist: pytest-cov>=7.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=8.4; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.13; extra == 'dev'
|
|
27
|
+
Requires-Dist: textual>=6.1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: tomli>=2.2.1; extra == 'dev'
|
|
29
|
+
Requires-Dist: twine>=6.2; extra == 'dev'
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
# lib_cli_exit_tools
|
|
@@ -65,6 +65,7 @@ Console script:
|
|
|
65
65
|
lib_cli_exit_tools --help
|
|
66
66
|
cli-exit-tools --help # alias
|
|
67
67
|
lib_cli_exit_tools info
|
|
68
|
+
lib_cli_exit_tools fail # intentionally trigger RuntimeError to test error paths
|
|
68
69
|
```
|
|
69
70
|
|
|
70
71
|
### Embed in your own CLI
|
|
@@ -189,6 +190,9 @@ Parameters:
|
|
|
189
190
|
- `length_limit`: Maximum characters for summary output.
|
|
190
191
|
- `stream`: Target text stream; defaults to `sys.stderr`.
|
|
191
192
|
|
|
193
|
+
### `i_should_fail()`
|
|
194
|
+
Deterministically raise `RuntimeError('i should fail')` to exercise error-handling paths. Useful for smoke-testing exit-code translation, CLI traceback toggles, and log formatting without inventing ad-hoc failing commands.
|
|
195
|
+
|
|
192
196
|
### `flush_streams() -> None`
|
|
193
197
|
Best-effort flush of `sys.stdout` and `sys.stderr`, ensuring buffered output is written before exit.
|
|
194
198
|
|
|
@@ -34,6 +34,7 @@ Console script:
|
|
|
34
34
|
lib_cli_exit_tools --help
|
|
35
35
|
cli-exit-tools --help # alias
|
|
36
36
|
lib_cli_exit_tools info
|
|
37
|
+
lib_cli_exit_tools fail # intentionally trigger RuntimeError to test error paths
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
### Embed in your own CLI
|
|
@@ -158,6 +159,9 @@ Parameters:
|
|
|
158
159
|
- `length_limit`: Maximum characters for summary output.
|
|
159
160
|
- `stream`: Target text stream; defaults to `sys.stderr`.
|
|
160
161
|
|
|
162
|
+
### `i_should_fail()`
|
|
163
|
+
Deterministically raise `RuntimeError('i should fail')` to exercise error-handling paths. Useful for smoke-testing exit-code translation, CLI traceback toggles, and log formatting without inventing ad-hoc failing commands.
|
|
164
|
+
|
|
161
165
|
### `flush_streams() -> None`
|
|
162
166
|
Best-effort flush of `sys.stdout` and `sys.stderr`, ensuring buffered output is written before exit.
|
|
163
167
|
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/docs/system-design/module_reference.md
RENAMED
|
@@ -59,7 +59,7 @@ Automations and downstream CLIs rely on `lib_cli_exit_tools` for deterministic e
|
|
|
59
59
|
### Module: lib_cli_exit_tools/lib_cli_exit_tools.py
|
|
60
60
|
* **Purpose:** Provide a façade that re-exports configuration, signal helpers, and orchestration utilities so existing imports stay valid after the refactor.
|
|
61
61
|
* **Location:** `src/lib_cli_exit_tools/lib_cli_exit_tools.py`
|
|
62
|
-
* **Notes:**
|
|
62
|
+
* **Notes:** Aggregates symbols from the layered modules to minimise breaking changes and exposes `i_should_fail()` as a deterministic failure helper for exercising error-handling paths.
|
|
63
63
|
|
|
64
64
|
### Module: lib_cli_exit_tools/core/configuration.py
|
|
65
65
|
* **Purpose:** Own the mutable runtime configuration shared across CLI executions.
|
|
@@ -131,6 +131,10 @@ Automations and downstream CLIs rely on `lib_cli_exit_tools` for deterministic e
|
|
|
131
131
|
#### Function: `cli_info() -> None`
|
|
132
132
|
* **Purpose:** Emit project metadata by delegating to `__init__conf__.print_info()`.
|
|
133
133
|
|
|
134
|
+
#### Function: `cli_fail() -> None`
|
|
135
|
+
* **Purpose:** Invoke `lib_cli_exit_tools.i_should_fail()` so operators can validate error-path handling from the packaged CLI without crafting bespoke failing commands.
|
|
136
|
+
* **Notes:** Surfaces the stable `RuntimeError('i should fail')` message, making it safe for scripted assertions and support playbooks.
|
|
137
|
+
|
|
134
138
|
#### Function: `main(argv=None) -> int`
|
|
135
139
|
* **Purpose:** Compose the CLI invocation by delegating to `lib_cli_exit_tools.run_cli`, returning exit codes instead of exiting directly.
|
|
136
140
|
|
|
@@ -191,6 +191,78 @@
|
|
|
191
191
|
"except Exception:\n",
|
|
192
192
|
" lib_cli_exit_tools.print_exception_message(trace_back=True)"
|
|
193
193
|
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"cell_type": "markdown",
|
|
197
|
+
"id": "d56cb190",
|
|
198
|
+
"metadata": {},
|
|
199
|
+
"source": [
|
|
200
|
+
"## 7) Registered Command Fail"
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"cell_type": "code",
|
|
205
|
+
"execution_count": null,
|
|
206
|
+
"id": "b2eb015b",
|
|
207
|
+
"metadata": {},
|
|
208
|
+
"outputs": [],
|
|
209
|
+
"source": [
|
|
210
|
+
"!lib_cli_exit_tools fail"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"cell_type": "markdown",
|
|
215
|
+
"id": "6c546140",
|
|
216
|
+
"metadata": {},
|
|
217
|
+
"source": [
|
|
218
|
+
"## 8) Registered Command Fail with Traceback"
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"cell_type": "code",
|
|
223
|
+
"execution_count": null,
|
|
224
|
+
"id": "db445316",
|
|
225
|
+
"metadata": {},
|
|
226
|
+
"outputs": [],
|
|
227
|
+
"source": [
|
|
228
|
+
"!lib_cli_exit_tools --traceback fail"
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"cell_type": "markdown",
|
|
233
|
+
"id": "24c157fa",
|
|
234
|
+
"metadata": {},
|
|
235
|
+
"source": [
|
|
236
|
+
"## 9) Script Command Fail"
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"cell_type": "code",
|
|
241
|
+
"execution_count": null,
|
|
242
|
+
"id": "81a4fa19",
|
|
243
|
+
"metadata": {},
|
|
244
|
+
"outputs": [],
|
|
245
|
+
"source": [
|
|
246
|
+
"!{sys.executable} -m lib_cli_exit_tools fail"
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"cell_type": "markdown",
|
|
251
|
+
"id": "ab2685a2",
|
|
252
|
+
"metadata": {},
|
|
253
|
+
"source": [
|
|
254
|
+
"## 10) Script Command Fail with Traceback"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"cell_type": "code",
|
|
259
|
+
"execution_count": null,
|
|
260
|
+
"id": "ea02859a",
|
|
261
|
+
"metadata": {},
|
|
262
|
+
"outputs": [],
|
|
263
|
+
"source": [
|
|
264
|
+
"!{sys.executable} -m lib_cli_exit_tools --traceback fail"
|
|
265
|
+
]
|
|
194
266
|
}
|
|
195
267
|
],
|
|
196
268
|
"metadata": {
|
|
@@ -206,4 +278,4 @@
|
|
|
206
278
|
},
|
|
207
279
|
"nbformat": 4,
|
|
208
280
|
"nbformat_minor": 5
|
|
209
|
-
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "7fb27b941602401d91542211134fc71a",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# lib_cli_exit_tools Quickstart\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"Use this notebook to install the package, explore the CLI, and observe how rich-formatted tracebacks behave when you enable debugging output.\n"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"cell_type": "markdown",
|
|
15
|
+
"id": "acae54e37e7d407bbb7b55eff062a284",
|
|
16
|
+
"metadata": {},
|
|
17
|
+
"source": [
|
|
18
|
+
"## 1) Install latest from GitHub (default)\n"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"cell_type": "code",
|
|
23
|
+
"execution_count": null,
|
|
24
|
+
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
|
|
25
|
+
"metadata": {},
|
|
26
|
+
"outputs": [],
|
|
27
|
+
"source": [
|
|
28
|
+
"# update pip and setuptools\n",
|
|
29
|
+
"import sys\n",
|
|
30
|
+
"\n",
|
|
31
|
+
"!{sys.executable} -m pip install --upgrade pip\n",
|
|
32
|
+
"!{sys.executable} -m pip install --upgrade setuptools\n",
|
|
33
|
+
"\n",
|
|
34
|
+
"# install lib_cli_exit_tools from GitHub (default)\n",
|
|
35
|
+
"!{sys.executable} -m pip install --upgrade git+https://github.com/bitranox/lib_cli_exit_tools.git"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"cell_type": "markdown",
|
|
40
|
+
"id": "8dd0d8092fe74a7c96281538738b07e2",
|
|
41
|
+
"metadata": {},
|
|
42
|
+
"source": [
|
|
43
|
+
"## Or: Install from PyPI (alternative)\n",
|
|
44
|
+
"Uncomment and run the cell below if you prefer the PyPI release.\n"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"cell_type": "code",
|
|
49
|
+
"execution_count": null,
|
|
50
|
+
"id": "72eea5119410473aa328ad9291626812",
|
|
51
|
+
"metadata": {},
|
|
52
|
+
"outputs": [],
|
|
53
|
+
"source": [
|
|
54
|
+
"# !{sys.executable} -m pip install --upgrade lib_cli_exit_tools\n"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"cell_type": "markdown",
|
|
59
|
+
"id": "8edb47106e1a46a883d545849b8ab81b",
|
|
60
|
+
"metadata": {},
|
|
61
|
+
"source": [
|
|
62
|
+
"## 2) CLI: version and help\n"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"cell_type": "code",
|
|
67
|
+
"execution_count": null,
|
|
68
|
+
"id": "10185d26023b46108eb7d9f57d49d2b3",
|
|
69
|
+
"metadata": {},
|
|
70
|
+
"outputs": [],
|
|
71
|
+
"source": [
|
|
72
|
+
"!lib_cli_exit_tools --version\n",
|
|
73
|
+
"!lib_cli_exit_tools --help\n",
|
|
74
|
+
"!lib_cli_exit_tools info"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"cell_type": "markdown",
|
|
79
|
+
"id": "8763a12b2bbd4a93a75aff182afb95dc",
|
|
80
|
+
"metadata": {},
|
|
81
|
+
"source": [
|
|
82
|
+
"## 3) Demo command for failure handling\n",
|
|
83
|
+
"The helper below builds a tiny Click command that purposely raises an error so you can compare short messages with rich tracebacks.\n"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"cell_type": "code",
|
|
88
|
+
"execution_count": null,
|
|
89
|
+
"id": "7623eae2785240b9bd12b16a66d81610",
|
|
90
|
+
"metadata": {},
|
|
91
|
+
"outputs": [],
|
|
92
|
+
"source": [
|
|
93
|
+
"import click\n",
|
|
94
|
+
"from lib_cli_exit_tools.lib_cli_exit_tools import config, run_cli\n",
|
|
95
|
+
"\n",
|
|
96
|
+
"\n",
|
|
97
|
+
"@click.command()\n",
|
|
98
|
+
"def quickstart_fail() -> None:\n",
|
|
99
|
+
" \"\"\"Raise a demo error to exercise lib_cli_exit_tools.\"\"\"\n",
|
|
100
|
+
" raise RuntimeError(\"demo failure from Quickstart\")\n",
|
|
101
|
+
"\n",
|
|
102
|
+
"\n",
|
|
103
|
+
"def run_quickstart_demo(traceback_enabled: bool) -> int:\n",
|
|
104
|
+
" \"\"\"Execute the demo command with the desired traceback mode.\n",
|
|
105
|
+
"\n",
|
|
106
|
+
" The helper keeps tests deterministic by disabling signal handlers\n",
|
|
107
|
+
" in notebook contexts and resets global flags after execution.\n",
|
|
108
|
+
" \"\"\"\n",
|
|
109
|
+
"\n",
|
|
110
|
+
" original_flag = config.traceback\n",
|
|
111
|
+
" config.traceback = traceback_enabled\n",
|
|
112
|
+
" try:\n",
|
|
113
|
+
" return run_cli(quickstart_fail, argv=[], install_signals=False)\n",
|
|
114
|
+
" finally:\n",
|
|
115
|
+
" config.traceback = original_flag"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"cell_type": "markdown",
|
|
120
|
+
"id": "7cdc8c89c7104fffa095e18ddfef8986",
|
|
121
|
+
"metadata": {},
|
|
122
|
+
"source": [
|
|
123
|
+
"## 4) CLI failure with short message (default)\n",
|
|
124
|
+
"Tracebacks are suppressed by default; the CLI emits a concise summary and returns a deterministic exit code.\n"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"cell_type": "code",
|
|
129
|
+
"execution_count": null,
|
|
130
|
+
"id": "b118ea5561624da68c537baed56e602f",
|
|
131
|
+
"metadata": {},
|
|
132
|
+
"outputs": [],
|
|
133
|
+
"source": [
|
|
134
|
+
"exit_code = run_quickstart_demo(False)\n",
|
|
135
|
+
"print(f\"Exit code: {exit_code}\")"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"cell_type": "markdown",
|
|
140
|
+
"id": "938c804e27f84196a10c8828c723f798",
|
|
141
|
+
"metadata": {},
|
|
142
|
+
"source": [
|
|
143
|
+
"## 5) CLI failure with rich traceback\n",
|
|
144
|
+
"Enable tracebacks to surface a full Rich-formatted stack trace for debugging.\n"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"cell_type": "code",
|
|
149
|
+
"execution_count": null,
|
|
150
|
+
"id": "504fb2a444614c0babb325280ed9130a",
|
|
151
|
+
"metadata": {},
|
|
152
|
+
"outputs": [],
|
|
153
|
+
"source": [
|
|
154
|
+
"import lib_cli_exit_tools\n",
|
|
155
|
+
"\n",
|
|
156
|
+
"try:\n",
|
|
157
|
+
" run_quickstart_demo(True)\n",
|
|
158
|
+
"except RuntimeError:\n",
|
|
159
|
+
" lib_cli_exit_tools.print_exception_message(trace_back=True)"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"cell_type": "markdown",
|
|
164
|
+
"id": "59bbdb311c014d738909a11f9e486628",
|
|
165
|
+
"metadata": {},
|
|
166
|
+
"source": [
|
|
167
|
+
"## 6) Library usage: exit codes and traceback formatting\n",
|
|
168
|
+
"Use the functional API to translate exceptions into exit codes and to opt in to rich tracebacks programmatically.\n"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"cell_type": "code",
|
|
173
|
+
"execution_count": null,
|
|
174
|
+
"id": "b43b363d81ae4b689946ece5c682cd59",
|
|
175
|
+
"metadata": {},
|
|
176
|
+
"outputs": [],
|
|
177
|
+
"source": [
|
|
178
|
+
"import lib_cli_exit_tools\n",
|
|
179
|
+
"\n",
|
|
180
|
+
"# Short message (default)\n",
|
|
181
|
+
"try:\n",
|
|
182
|
+
" raise FileNotFoundError(\"missing.txt\")\n",
|
|
183
|
+
"except Exception as exc:\n",
|
|
184
|
+
" exit_code = lib_cli_exit_tools.get_system_exit_code(exc)\n",
|
|
185
|
+
" lib_cli_exit_tools.print_exception_message(trace_back=False)\n",
|
|
186
|
+
" print(f\"Exit code would be: {exit_code}\")\n",
|
|
187
|
+
"\n",
|
|
188
|
+
"# Rich traceback\n",
|
|
189
|
+
"try:\n",
|
|
190
|
+
" raise ValueError(\"boom\")\n",
|
|
191
|
+
"except Exception:\n",
|
|
192
|
+
" lib_cli_exit_tools.print_exception_message(trace_back=True)"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"cell_type": "markdown",
|
|
197
|
+
"id": "d56cb190",
|
|
198
|
+
"metadata": {},
|
|
199
|
+
"source": [
|
|
200
|
+
"## 7) Registered Command Fail"
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"cell_type": "code",
|
|
205
|
+
"execution_count": null,
|
|
206
|
+
"id": "b2eb015b",
|
|
207
|
+
"metadata": {},
|
|
208
|
+
"outputs": [],
|
|
209
|
+
"source": [
|
|
210
|
+
"!lib_cli_exit_tools fail"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"cell_type": "markdown",
|
|
215
|
+
"id": "6c546140",
|
|
216
|
+
"metadata": {},
|
|
217
|
+
"source": [
|
|
218
|
+
"## 8) Registered Command Fail with Traceback"
|
|
219
|
+
]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"cell_type": "code",
|
|
223
|
+
"execution_count": null,
|
|
224
|
+
"id": "db445316",
|
|
225
|
+
"metadata": {},
|
|
226
|
+
"outputs": [],
|
|
227
|
+
"source": [
|
|
228
|
+
"!lib_cli_exit_tools --traceback fail"
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"cell_type": "markdown",
|
|
233
|
+
"id": "24c157fa",
|
|
234
|
+
"metadata": {},
|
|
235
|
+
"source": [
|
|
236
|
+
"## 9) Script Command Fail"
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"cell_type": "code",
|
|
241
|
+
"execution_count": null,
|
|
242
|
+
"id": "81a4fa19",
|
|
243
|
+
"metadata": {},
|
|
244
|
+
"outputs": [],
|
|
245
|
+
"source": [
|
|
246
|
+
"!{sys.executable} -m lib_cli_exit_tools fail"
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"cell_type": "markdown",
|
|
251
|
+
"id": "ab2685a2",
|
|
252
|
+
"metadata": {},
|
|
253
|
+
"source": [
|
|
254
|
+
"## 10) Script Command Fail with Traceback"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"cell_type": "code",
|
|
259
|
+
"execution_count": null,
|
|
260
|
+
"id": "ea02859a",
|
|
261
|
+
"metadata": {},
|
|
262
|
+
"outputs": [],
|
|
263
|
+
"source": [
|
|
264
|
+
"!{sys.executable} -m lib_cli_exit_tools --traceback fail"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
],
|
|
268
|
+
"metadata": {
|
|
269
|
+
"kernelspec": {
|
|
270
|
+
"display_name": "Python 3",
|
|
271
|
+
"language": "python",
|
|
272
|
+
"name": "python3"
|
|
273
|
+
},
|
|
274
|
+
"language_info": {
|
|
275
|
+
"name": "python",
|
|
276
|
+
"version": "3"
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"nbformat": 4,
|
|
280
|
+
"nbformat_minor": 5
|
|
281
|
+
}
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/packaging/brew/Formula/lib-cli-exit-tools.rb
RENAMED
|
@@ -3,7 +3,7 @@ class LibCliExitTools < Formula
|
|
|
3
3
|
|
|
4
4
|
desc "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
homepage "https://github.com/bitranox/lib_cli_exit_tools"
|
|
6
|
-
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.
|
|
6
|
+
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.5.0.tar.gz"
|
|
7
7
|
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
8
8
|
license "MIT"
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_cli_exit_tools"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0"
|
|
4
4
|
description = "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -24,15 +24,15 @@ Issues = "https://github.com/bitranox/lib_cli_exit_tools/issues"
|
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
dev = [
|
|
27
|
-
"pytest>=8.
|
|
28
|
-
"pytest-cov>=
|
|
29
|
-
"ruff>=0.
|
|
27
|
+
"pytest>=8.4",
|
|
28
|
+
"pytest-cov>=7.0",
|
|
29
|
+
"ruff>=0.13",
|
|
30
30
|
"pyright>=1.1",
|
|
31
|
-
"build>=1.
|
|
32
|
-
"twine>=
|
|
31
|
+
"build>=1.3",
|
|
32
|
+
"twine>=6.2",
|
|
33
33
|
"codecov>=2.1.13",
|
|
34
|
-
"textual>=0
|
|
35
|
-
"tomli>=2.
|
|
34
|
+
"textual>=6.1.0",
|
|
35
|
+
"tomli>=2.2.1",
|
|
36
36
|
]
|
|
37
37
|
|
|
38
38
|
[project.scripts]
|
|
@@ -25,6 +25,7 @@ from .lib_cli_exit_tools import (
|
|
|
25
25
|
handle_cli_exception,
|
|
26
26
|
install_signal_handlers,
|
|
27
27
|
print_exception_message,
|
|
28
|
+
i_should_fail,
|
|
28
29
|
run_cli,
|
|
29
30
|
)
|
|
30
31
|
|
|
@@ -42,5 +43,6 @@ __all__ = [
|
|
|
42
43
|
"default_signal_specs",
|
|
43
44
|
"install_signal_handlers",
|
|
44
45
|
"handle_cli_exception",
|
|
46
|
+
"i_should_fail",
|
|
45
47
|
"run_cli",
|
|
46
48
|
]
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/application/runner.py
RENAMED
|
@@ -240,8 +240,8 @@ def handle_cli_exception(
|
|
|
240
240
|
Returns:
|
|
241
241
|
Integer exit code suitable for :func:`sys.exit`.
|
|
242
242
|
Side Effects:
|
|
243
|
-
May write to stderr, invoke :func:`print_exception_message`,
|
|
244
|
-
|
|
243
|
+
May write to stderr, invoke :func:`print_exception_message`, and render
|
|
244
|
+
rich tracebacks when requested.
|
|
245
245
|
"""
|
|
246
246
|
|
|
247
247
|
specs = list(default_signal_specs() if signal_specs is None else signal_specs)
|
|
@@ -265,7 +265,8 @@ def handle_cli_exception(
|
|
|
265
265
|
return 1
|
|
266
266
|
|
|
267
267
|
if config.traceback:
|
|
268
|
-
|
|
268
|
+
print_exception_message(trace_back=True)
|
|
269
|
+
return get_system_exit_code(exc)
|
|
269
270
|
|
|
270
271
|
print_exception_message()
|
|
271
272
|
return get_system_exit_code(exc)
|
|
@@ -6,6 +6,7 @@ Purpose:
|
|
|
6
6
|
Contents:
|
|
7
7
|
* :func:`cli` group exposing shared options.
|
|
8
8
|
* :func:`cli_info` subcommand reporting distribution metadata.
|
|
9
|
+
* :func:`cli_fail` subcommand triggering a deterministic failure for testing error paths.
|
|
9
10
|
* :func:`main` entry point used by console scripts and ``python -m``.
|
|
10
11
|
System Integration:
|
|
11
12
|
The CLI mutates :data:`lib_cli_exit_tools.config` based on the ``--traceback``
|
|
@@ -125,6 +126,30 @@ def cli_info() -> None:
|
|
|
125
126
|
__init__conf__.print_info()
|
|
126
127
|
|
|
127
128
|
|
|
129
|
+
@cli.command("fail", context_settings=CLICK_CONTEXT_SETTINGS)
|
|
130
|
+
def cli_fail() -> None:
|
|
131
|
+
"""Intentionally raise a failure to validate error handling.
|
|
132
|
+
|
|
133
|
+
Why:
|
|
134
|
+
Exercise the error-reporting path so engineers can confirm exit-code
|
|
135
|
+
translation and optional traceback behaviour without crafting custom
|
|
136
|
+
failing commands.
|
|
137
|
+
Side Effects:
|
|
138
|
+
Delegates to :func:`lib_cli_exit_tools.i_should_fail`, which raises
|
|
139
|
+
``RuntimeError`` for the caller to handle.
|
|
140
|
+
Examples:
|
|
141
|
+
>>> from click.testing import CliRunner
|
|
142
|
+
>>> runner = CliRunner()
|
|
143
|
+
>>> result = runner.invoke(cli, ["fail"])
|
|
144
|
+
>>> result.exit_code != 0
|
|
145
|
+
True
|
|
146
|
+
>>> "i should fail" in result.output or "i should fail" in result.stderr
|
|
147
|
+
True
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
lib_cli_exit_tools.i_should_fail()
|
|
151
|
+
|
|
152
|
+
|
|
128
153
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
129
154
|
"""Run the CLI with :func:`lib_cli_exit_tools.run_cli` wiring.
|
|
130
155
|
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/lib_cli_exit_tools.py
RENAMED
|
@@ -9,6 +9,7 @@ Contents:
|
|
|
9
9
|
* ``get_system_exit_code`` from :mod:`lib_cli_exit_tools.core.exit_codes`.
|
|
10
10
|
* ``handle_cli_exception`` and ``run_cli`` from
|
|
11
11
|
:mod:`lib_cli_exit_tools.application.runner`.
|
|
12
|
+
* :func:`i_should_fail` defined here for intentionally exercising error paths.
|
|
12
13
|
* Signal helpers from :mod:`lib_cli_exit_tools.adapters.signals`.
|
|
13
14
|
System Integration:
|
|
14
15
|
The CLI adapter (:mod:`lib_cli_exit_tools.cli`) and external consumers
|
|
@@ -49,5 +50,26 @@ __all__ = [
|
|
|
49
50
|
"default_signal_specs",
|
|
50
51
|
"install_signal_handlers",
|
|
51
52
|
"handle_cli_exception",
|
|
53
|
+
"i_should_fail",
|
|
52
54
|
"run_cli",
|
|
53
55
|
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def i_should_fail() -> None:
|
|
59
|
+
"""Raise :class:`RuntimeError` to exercise error-handling flows.
|
|
60
|
+
|
|
61
|
+
Why:
|
|
62
|
+
Provide a deterministic failure path so engineers can verify traceback
|
|
63
|
+
toggles, exit-code translation, and structured error reporting during
|
|
64
|
+
manual or automated testing.
|
|
65
|
+
Raises:
|
|
66
|
+
RuntimeError: Always raised with a stable message for assertion-friendly
|
|
67
|
+
diagnostics.
|
|
68
|
+
Examples:
|
|
69
|
+
>>> i_should_fail()
|
|
70
|
+
Traceback (most recent call last):
|
|
71
|
+
...
|
|
72
|
+
RuntimeError: i should fail
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
raise RuntimeError("i should fail")
|
|
@@ -78,11 +78,25 @@ def test_handle_cli_exception_generic_delegates(monkeypatch: pytest.MonkeyPatch)
|
|
|
78
78
|
assert calls == {"printed": True, "exc": err}
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
def
|
|
82
|
-
"""Traceback mode
|
|
81
|
+
def test_handle_cli_exception_prints_rich_traceback(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
82
|
+
"""Traceback mode renders rich tracebacks and returns exit codes."""
|
|
83
|
+
calls: dict[str, object] = {}
|
|
84
|
+
|
|
85
|
+
def _fake_print(*_args, **kwargs) -> None:
|
|
86
|
+
calls["called"] = True
|
|
87
|
+
calls["trace_back"] = kwargs.get("trace_back")
|
|
88
|
+
|
|
89
|
+
def _fake_exit(exc: BaseException) -> int:
|
|
90
|
+
calls["exc"] = exc
|
|
91
|
+
return 17
|
|
92
|
+
|
|
93
|
+
monkeypatch.setattr("lib_cli_exit_tools.application.runner.print_exception_message", _fake_print)
|
|
94
|
+
monkeypatch.setattr("lib_cli_exit_tools.application.runner.get_system_exit_code", _fake_exit)
|
|
95
|
+
|
|
83
96
|
config.traceback = True
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
err = RuntimeError("boom")
|
|
98
|
+
assert handle_cli_exception(err) == 17
|
|
99
|
+
assert calls == {"called": True, "trace_back": True, "exc": err}
|
|
86
100
|
|
|
87
101
|
|
|
88
102
|
def test_print_exception_message_outputs_summary() -> None:
|
|
@@ -19,10 +19,16 @@ from _pytest.capture import CaptureFixture
|
|
|
19
19
|
|
|
20
20
|
import pytest
|
|
21
21
|
|
|
22
|
-
from lib_cli_exit_tools import run_cli
|
|
22
|
+
from lib_cli_exit_tools import run_cli, i_should_fail
|
|
23
23
|
from lib_cli_exit_tools.cli import cli as root_cli, main
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def test_i_should_fail_helper_raises_runtime_error() -> None:
|
|
27
|
+
"""The public helper always raises RuntimeError with a stable message."""
|
|
28
|
+
with pytest.raises(RuntimeError, match="^i should fail$"):
|
|
29
|
+
i_should_fail()
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
def test_cli_info_command_outputs_metadata(capsys: CaptureFixture[str]) -> None:
|
|
27
33
|
"""The info subcommand surfaces package metadata without stderr output."""
|
|
28
34
|
exit_code = main(["info"])
|
|
@@ -32,6 +38,15 @@ def test_cli_info_command_outputs_metadata(capsys: CaptureFixture[str]) -> None:
|
|
|
32
38
|
assert err == ""
|
|
33
39
|
|
|
34
40
|
|
|
41
|
+
def test_cli_fail_command_emits_runtime_error(capsys: CaptureFixture[str]) -> None:
|
|
42
|
+
"""The fail subcommand intentionally raises RuntimeError for error-path tests."""
|
|
43
|
+
exit_code = main(["fail"])
|
|
44
|
+
out, err = capsys.readouterr()
|
|
45
|
+
assert exit_code == 1
|
|
46
|
+
assert out == ""
|
|
47
|
+
assert "RuntimeError: i should fail" in err
|
|
48
|
+
|
|
49
|
+
|
|
35
50
|
def test_cli_unknown_option_returns_usage_error(capsys: CaptureFixture[str]) -> None:
|
|
36
51
|
"""Unknown options trigger Click usage errors with exit code 2."""
|
|
37
52
|
exit_code = main(["--does-not-exist"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/__init__conf__.py
RENAMED
|
File without changes
|
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/adapters/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/adapters/signals.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/application/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/configuration.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-1.4.0 → lib_cli_exit_tools-1.5.0}/src/lib_cli_exit_tools/core/exit_codes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|