lib-cli-exit-tools 2.1.1__tar.gz → 2.2.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.
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.github/workflows/ci.yml +11 -3
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/CHANGELOG.md +23 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/INSTALL.md +1 -1
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/PKG-INFO +76 -26
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/README.md +72 -23
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/pyproject.toml +15 -5
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/__init__conf__.py +1 -1
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_metadata.py +6 -1
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.codacy/cli-config.yaml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.codacy/cli.sh +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.codacy/codacy.yaml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.codacy/tools-configs/lizard.yaml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.codacy/tools-configs/trivy.yaml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.devcontainer/devcontainer.json +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.devcontainer/settings.json +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.env.example +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.github/dependabot.yml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.github/workflows/codeql.yml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.github/workflows/release.yml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.gitignore +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.qlty/qlty.toml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/.snyk +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/AGENTS.md +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/AGENTS_README.md +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/CONTRIBUTING.md +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/DEVELOPMENT.md +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/LICENSE +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/Makefile +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/codecov.yml +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/docs/system-design/module_reference.md +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/notebooks/Quickstart.ipynb +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/__init__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/__main__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/_utils.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/build.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/bump.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/bump_major.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/bump_minor.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/bump_patch.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/bump_version.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/clean.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/cli.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/dependencies.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/dev.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/help.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/install.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/menu.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/push.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/release.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/run_cli.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/target_metadata.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/test.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/toml_config.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/scripts/version_current.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/__init__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/__main__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/adapters/__init__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/adapters/signals.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/application/__init__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/application/runner.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/cli.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/__init__.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/configuration.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/exit_codes.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/lib_cli_exit_tools.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/py.typed +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/conftest.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_adapters_signals.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_application_runner.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_behaviors.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_cli.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_core_configuration.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_core_exit_codes.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_module_entry.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_scripts.py +0 -0
- {lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/tests/test_signals_integration.py +0 -0
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
matrix:
|
|
19
19
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
20
20
|
# Test against Python 3.13 and the latest 3.x available on Actions runners
|
|
21
|
-
python: ["3.13", "3.x"]
|
|
21
|
+
python: ["3.10","3.11","3.12","3.13", "3.x"]
|
|
22
22
|
env:
|
|
23
23
|
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
|
|
24
24
|
PYTHONIOENCODING: "utf-8"
|
|
@@ -37,9 +37,13 @@ jobs:
|
|
|
37
37
|
shell: python
|
|
38
38
|
run: |
|
|
39
39
|
import os
|
|
40
|
-
import tomllib
|
|
41
40
|
from pathlib import Path
|
|
42
41
|
|
|
42
|
+
try:
|
|
43
|
+
import tomllib
|
|
44
|
+
except ModuleNotFoundError:
|
|
45
|
+
import pip._vendor.tomli as tomllib
|
|
46
|
+
|
|
43
47
|
data = tomllib.loads(Path('pyproject.toml').read_text('utf-8'))
|
|
44
48
|
project = data['project']['name']
|
|
45
49
|
module = project.replace('-', '_')
|
|
@@ -123,9 +127,13 @@ jobs:
|
|
|
123
127
|
shell: python
|
|
124
128
|
run: |
|
|
125
129
|
import os
|
|
126
|
-
import tomllib
|
|
127
130
|
from pathlib import Path
|
|
128
131
|
|
|
132
|
+
try:
|
|
133
|
+
import tomllib
|
|
134
|
+
except ModuleNotFoundError:
|
|
135
|
+
import pip._vendor.tomli as tomllib
|
|
136
|
+
|
|
129
137
|
data = tomllib.loads(Path('pyproject.toml').read_text('utf-8'))
|
|
130
138
|
project = data['project']['name']
|
|
131
139
|
module = project.replace('-', '_')
|
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
## [2.2.1] - 2025-12-11
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- "Why lib_cli_exit_tools?" section in README highlighting five key benefits: correct exit codes, portable signal handling, clean error output, pipeline-friendly behaviour, and zero boilerplate.
|
|
10
|
+
- CLI Reference section in README with global options table and command descriptions.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Replaced all `--verbose` references in README examples with `--traceback` to match actual CLI option.
|
|
14
|
+
- Updated `cli_session` documentation to include missing `restore` parameter.
|
|
15
|
+
- Converted config field reference to table format with types and defaults.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Added missing `traceback_force_color` configuration field to documentation.
|
|
19
|
+
|
|
20
|
+
## [2.2.0] - 2025-12-11
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Lowered minimum Python version from 3.13 to 3.10 for broader compatibility.
|
|
24
|
+
- Updated ruff target-version to py310 to match the new baseline.
|
|
25
|
+
|
|
26
|
+
### Security
|
|
27
|
+
- Added setuptools vulnerability IDs (PYSEC-2022-43012, CVE-2024-6345, PYSEC-2025-49) to pip-audit ignore list. These are build-time dependencies managed by the CI environment, not runtime dependencies.
|
|
28
|
+
|
|
6
29
|
## [2.1.1] - 2025-12-08
|
|
7
30
|
|
|
8
31
|
### Changed
|
|
@@ -27,7 +27,7 @@ The sections below cover more specialised workflows, from editable installs to a
|
|
|
27
27
|
|
|
28
28
|
## Supported Python Versions
|
|
29
29
|
|
|
30
|
-
`lib_cli_exit_tools` targets Python 3.
|
|
30
|
+
`lib_cli_exit_tools` targets Python 3.10 and newer. Wheels are published for CPython on Linux, macOS, and Windows. For alternative interpreters (PyPy) install from source using `pip install lib_cli_exit_tools`.
|
|
31
31
|
|
|
32
32
|
## Installation Flows
|
|
33
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_cli_exit_tools
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.1
|
|
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
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
19
|
Requires-Dist: rich-click>=1.9.4
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: bandit>=1.9.2; extra == 'dev'
|
|
@@ -23,7 +23,7 @@ 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.
|
|
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'
|
|
@@ -31,6 +31,7 @@ Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
|
31
31
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
32
32
|
Requires-Dist: ruff>=0.14.8; extra == 'dev'
|
|
33
33
|
Requires-Dist: textual>=6.8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: tomli>=2.3.0; (python_version < '3.11') and extra == 'dev'
|
|
34
35
|
Requires-Dist: twine>=6.2.0; extra == 'dev'
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
36
37
|
|
|
@@ -54,9 +55,23 @@ Small helpers for robust CLI exit handling:
|
|
|
54
55
|
- Consistent exception → exit code mapping
|
|
55
56
|
- Concise error printing with optional traceback and subprocess stdout/stderr capture
|
|
56
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
|
+
|
|
57
72
|
## Install
|
|
58
73
|
|
|
59
|
-
Requires Python 3.
|
|
74
|
+
Requires Python 3.10 or newer.
|
|
60
75
|
|
|
61
76
|
```bash
|
|
62
77
|
pip install lib_cli_exit_tools
|
|
@@ -76,6 +91,35 @@ lib-cli-exit-tools fail # intentionally trigger RuntimeError to test error path
|
|
|
76
91
|
# Aliases are also generated: `cli-exit-tools`, `lib_cli_exit_tools`
|
|
77
92
|
```
|
|
78
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
|
+
|
|
79
123
|
### Examples
|
|
80
124
|
|
|
81
125
|
The snippets below move from a minimal “hello world” through a production-ready
|
|
@@ -108,7 +152,7 @@ Run it with:
|
|
|
108
152
|
python hello.py
|
|
109
153
|
```
|
|
110
154
|
|
|
111
|
-
#### 2.
|
|
155
|
+
#### 2. Traceback-mode helper using `cli_session`
|
|
112
156
|
|
|
113
157
|
```python
|
|
114
158
|
from __future__ import annotations
|
|
@@ -119,22 +163,22 @@ from lib_cli_exit_tools import cli_session, run_cli
|
|
|
119
163
|
|
|
120
164
|
|
|
121
165
|
@click.command()
|
|
122
|
-
@click.option("--
|
|
123
|
-
def cli(
|
|
124
|
-
with cli_session(overrides={"traceback":
|
|
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:
|
|
125
169
|
return execute(failable, argv=[])
|
|
126
170
|
|
|
127
171
|
|
|
128
172
|
@click.command()
|
|
129
173
|
def failable() -> None:
|
|
130
|
-
raise RuntimeError("toggle --
|
|
174
|
+
raise RuntimeError("toggle --traceback to see the full traceback")
|
|
131
175
|
|
|
132
176
|
|
|
133
177
|
if __name__ == "__main__":
|
|
134
178
|
raise SystemExit(run_cli(cli))
|
|
135
179
|
```
|
|
136
180
|
|
|
137
|
-
Invoking `python cli.py --
|
|
181
|
+
Invoking `python cli.py --traceback` enables coloured tracebacks just for that
|
|
138
182
|
run and resets the configuration afterwards.
|
|
139
183
|
|
|
140
184
|
#### 3. Full-featured multi-command CLI
|
|
@@ -153,21 +197,21 @@ from lib_cli_exit_tools import SignalSpec, cli_session, default_signal_specs
|
|
|
153
197
|
@dataclass(slots=True)
|
|
154
198
|
class Settings:
|
|
155
199
|
pretty: bool
|
|
156
|
-
|
|
200
|
+
traceback: bool
|
|
157
201
|
|
|
158
202
|
|
|
159
203
|
@click.group()
|
|
160
204
|
@click.option("--pretty/--no-pretty", default=True)
|
|
161
|
-
@click.option("--
|
|
205
|
+
@click.option("--traceback", is_flag=True, help="Show full tracebacks on error")
|
|
162
206
|
@click.pass_context
|
|
163
|
-
def cli(ctx: click.Context, pretty: bool,
|
|
164
|
-
ctx.obj = Settings(pretty=pretty,
|
|
207
|
+
def cli(ctx: click.Context, pretty: bool, traceback: bool) -> None:
|
|
208
|
+
ctx.obj = Settings(pretty=pretty, traceback=traceback)
|
|
165
209
|
|
|
166
210
|
|
|
167
211
|
@cli.command()
|
|
168
212
|
@click.pass_obj
|
|
169
213
|
def info(settings: Settings) -> None:
|
|
170
|
-
payload = {"
|
|
214
|
+
payload = {"traceback": settings.traceback, "pretty": settings.pretty}
|
|
171
215
|
click.echo(json.dumps(payload, indent=2 if settings.pretty else None))
|
|
172
216
|
|
|
173
217
|
|
|
@@ -307,9 +351,12 @@ config.broken_pipe_exit_code = 0 # treat BrokenPipeError as a benign truncat
|
|
|
307
351
|
|
|
308
352
|
Field reference:
|
|
309
353
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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. |
|
|
313
360
|
|
|
314
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:
|
|
315
362
|
|
|
@@ -396,22 +443,25 @@ Parameters:
|
|
|
396
443
|
- `exception_handler`: Callable receiving the raised exception and returning an exit code; defaults to ``handle_cli_exception``.
|
|
397
444
|
- `signal_installer`: Callable mirroring ``install_signal_handlers`` for embedding scenarios.
|
|
398
445
|
|
|
399
|
-
### `cli_session(*, summary_limit=500, verbose_limit=10_000, overrides=None)`
|
|
400
|
-
Context manager that snapshots
|
|
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
|
|
401
448
|
applies temporary overrides, and yields a callable compatible with
|
|
402
|
-
|
|
449
|
+
`run_cli`.
|
|
403
450
|
|
|
404
451
|
Parameters:
|
|
405
|
-
- `summary_limit
|
|
406
|
-
- `verbose_limit
|
|
407
|
-
- `overrides
|
|
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.
|
|
408
456
|
|
|
409
457
|
Use it to restore configuration automatically—even when the wrapped command
|
|
410
458
|
raises:
|
|
411
459
|
|
|
412
460
|
```python
|
|
461
|
+
from lib_cli_exit_tools import cli_session
|
|
462
|
+
|
|
413
463
|
with cli_session(overrides={"traceback": True}) as run:
|
|
414
|
-
exit_code = run(
|
|
464
|
+
exit_code = run(my_click_command, argv=["--help"])
|
|
415
465
|
```
|
|
416
466
|
|
|
417
467
|
### `handle_cli_exception(exc, *, signal_specs=None, echo=None) -> int`
|
|
@@ -582,7 +632,7 @@ When installed, the generated console scripts (`lib-cli-exit-tools`, `cli-exit-t
|
|
|
582
632
|
|
|
583
633
|
## Modern Python Toolchain
|
|
584
634
|
|
|
585
|
-
- Targets Python 3.
|
|
635
|
+
- Targets Python 3.10+ for modern language features and broad compatibility.
|
|
586
636
|
- Development extras track the latest stable releases published on PyPI so quality gates match local and CI environments.
|
|
587
637
|
- GitHub Actions workflows rely on the current major releases of `actions/checkout`, `actions/setup-python`, and `astral-sh/setup-uv`, aligning the automation stack with the 2025 runner images.
|
|
588
638
|
|
|
@@ -18,9 +18,23 @@ Small helpers for robust CLI exit handling:
|
|
|
18
18
|
- Consistent exception → exit code mapping
|
|
19
19
|
- Concise error printing with optional traceback and subprocess stdout/stderr capture
|
|
20
20
|
|
|
21
|
+
## Why lib_cli_exit_tools?
|
|
22
|
+
|
|
23
|
+
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:
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
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.
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
21
35
|
## Install
|
|
22
36
|
|
|
23
|
-
Requires Python 3.
|
|
37
|
+
Requires Python 3.10 or newer.
|
|
24
38
|
|
|
25
39
|
```bash
|
|
26
40
|
pip install lib_cli_exit_tools
|
|
@@ -40,6 +54,35 @@ lib-cli-exit-tools fail # intentionally trigger RuntimeError to test error path
|
|
|
40
54
|
# Aliases are also generated: `cli-exit-tools`, `lib_cli_exit_tools`
|
|
41
55
|
```
|
|
42
56
|
|
|
57
|
+
## CLI Reference
|
|
58
|
+
|
|
59
|
+
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`.
|
|
60
|
+
|
|
61
|
+
### Global Options
|
|
62
|
+
|
|
63
|
+
| Option | Default | Description |
|
|
64
|
+
|--------|---------|-------------|
|
|
65
|
+
| `--traceback` / `--no-traceback` | `False` | Show full Python traceback on errors |
|
|
66
|
+
| `--version` | — | Show program version and exit |
|
|
67
|
+
| `-h`, `--help` | — | Show help message and exit |
|
|
68
|
+
|
|
69
|
+
### Commands
|
|
70
|
+
|
|
71
|
+
#### `info`
|
|
72
|
+
Display package metadata (name, version, homepage, author).
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
lib-cli-exit-tools info
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### `fail`
|
|
79
|
+
Intentionally raise `RuntimeError('i should fail')` to validate error handling paths.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
lib-cli-exit-tools fail
|
|
83
|
+
lib-cli-exit-tools --traceback fail # show full traceback
|
|
84
|
+
```
|
|
85
|
+
|
|
43
86
|
### Examples
|
|
44
87
|
|
|
45
88
|
The snippets below move from a minimal “hello world” through a production-ready
|
|
@@ -72,7 +115,7 @@ Run it with:
|
|
|
72
115
|
python hello.py
|
|
73
116
|
```
|
|
74
117
|
|
|
75
|
-
#### 2.
|
|
118
|
+
#### 2. Traceback-mode helper using `cli_session`
|
|
76
119
|
|
|
77
120
|
```python
|
|
78
121
|
from __future__ import annotations
|
|
@@ -83,22 +126,22 @@ from lib_cli_exit_tools import cli_session, run_cli
|
|
|
83
126
|
|
|
84
127
|
|
|
85
128
|
@click.command()
|
|
86
|
-
@click.option("--
|
|
87
|
-
def cli(
|
|
88
|
-
with cli_session(overrides={"traceback":
|
|
129
|
+
@click.option("--traceback", is_flag=True, help="Show full tracebacks on error")
|
|
130
|
+
def cli(traceback: bool) -> int:
|
|
131
|
+
with cli_session(overrides={"traceback": traceback}) as execute:
|
|
89
132
|
return execute(failable, argv=[])
|
|
90
133
|
|
|
91
134
|
|
|
92
135
|
@click.command()
|
|
93
136
|
def failable() -> None:
|
|
94
|
-
raise RuntimeError("toggle --
|
|
137
|
+
raise RuntimeError("toggle --traceback to see the full traceback")
|
|
95
138
|
|
|
96
139
|
|
|
97
140
|
if __name__ == "__main__":
|
|
98
141
|
raise SystemExit(run_cli(cli))
|
|
99
142
|
```
|
|
100
143
|
|
|
101
|
-
Invoking `python cli.py --
|
|
144
|
+
Invoking `python cli.py --traceback` enables coloured tracebacks just for that
|
|
102
145
|
run and resets the configuration afterwards.
|
|
103
146
|
|
|
104
147
|
#### 3. Full-featured multi-command CLI
|
|
@@ -117,21 +160,21 @@ from lib_cli_exit_tools import SignalSpec, cli_session, default_signal_specs
|
|
|
117
160
|
@dataclass(slots=True)
|
|
118
161
|
class Settings:
|
|
119
162
|
pretty: bool
|
|
120
|
-
|
|
163
|
+
traceback: bool
|
|
121
164
|
|
|
122
165
|
|
|
123
166
|
@click.group()
|
|
124
167
|
@click.option("--pretty/--no-pretty", default=True)
|
|
125
|
-
@click.option("--
|
|
168
|
+
@click.option("--traceback", is_flag=True, help="Show full tracebacks on error")
|
|
126
169
|
@click.pass_context
|
|
127
|
-
def cli(ctx: click.Context, pretty: bool,
|
|
128
|
-
ctx.obj = Settings(pretty=pretty,
|
|
170
|
+
def cli(ctx: click.Context, pretty: bool, traceback: bool) -> None:
|
|
171
|
+
ctx.obj = Settings(pretty=pretty, traceback=traceback)
|
|
129
172
|
|
|
130
173
|
|
|
131
174
|
@cli.command()
|
|
132
175
|
@click.pass_obj
|
|
133
176
|
def info(settings: Settings) -> None:
|
|
134
|
-
payload = {"
|
|
177
|
+
payload = {"traceback": settings.traceback, "pretty": settings.pretty}
|
|
135
178
|
click.echo(json.dumps(payload, indent=2 if settings.pretty else None))
|
|
136
179
|
|
|
137
180
|
|
|
@@ -271,9 +314,12 @@ config.broken_pipe_exit_code = 0 # treat BrokenPipeError as a benign truncat
|
|
|
271
314
|
|
|
272
315
|
Field reference:
|
|
273
316
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
317
|
+
| Field | Type | Default | Description |
|
|
318
|
+
|-------|------|---------|-------------|
|
|
319
|
+
| `traceback` | `bool` | `False` | When `True`, `handle_cli_exception` renders a full Rich traceback to stderr. The bundled CLI toggles this via `--traceback/--no-traceback`. |
|
|
320
|
+
| `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.). |
|
|
321
|
+
| `broken_pipe_exit_code` | `int` | `141` | Exit status for `BrokenPipeError` (default mirrors `128 + SIGPIPE`). Set to `0` to treat truncation as success. |
|
|
322
|
+
| `traceback_force_color` | `bool` | `False` | Force Rich to emit ANSI-coloured tracebacks even when stderr is not a TTY. Useful for CI logs. |
|
|
277
323
|
|
|
278
324
|
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:
|
|
279
325
|
|
|
@@ -360,22 +406,25 @@ Parameters:
|
|
|
360
406
|
- `exception_handler`: Callable receiving the raised exception and returning an exit code; defaults to ``handle_cli_exception``.
|
|
361
407
|
- `signal_installer`: Callable mirroring ``install_signal_handlers`` for embedding scenarios.
|
|
362
408
|
|
|
363
|
-
### `cli_session(*, summary_limit=500, verbose_limit=10_000, overrides=None)`
|
|
364
|
-
Context manager that snapshots
|
|
409
|
+
### `cli_session(*, summary_limit=500, verbose_limit=10_000, overrides=None, restore=True)`
|
|
410
|
+
Context manager that snapshots `lib_cli_exit_tools.config`, optionally
|
|
365
411
|
applies temporary overrides, and yields a callable compatible with
|
|
366
|
-
|
|
412
|
+
`run_cli`.
|
|
367
413
|
|
|
368
414
|
Parameters:
|
|
369
|
-
- `summary_limit
|
|
370
|
-
- `verbose_limit
|
|
371
|
-
- `overrides
|
|
415
|
+
- `summary_limit` (`int`, default `500`): Character budget when tracebacks are disabled.
|
|
416
|
+
- `verbose_limit` (`int`, default `10_000`): Character budget when tracebacks are enabled.
|
|
417
|
+
- `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.
|
|
418
|
+
- `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.
|
|
372
419
|
|
|
373
420
|
Use it to restore configuration automatically—even when the wrapped command
|
|
374
421
|
raises:
|
|
375
422
|
|
|
376
423
|
```python
|
|
424
|
+
from lib_cli_exit_tools import cli_session
|
|
425
|
+
|
|
377
426
|
with cli_session(overrides={"traceback": True}) as run:
|
|
378
|
-
exit_code = run(
|
|
427
|
+
exit_code = run(my_click_command, argv=["--help"])
|
|
379
428
|
```
|
|
380
429
|
|
|
381
430
|
### `handle_cli_exception(exc, *, signal_specs=None, echo=None) -> int`
|
|
@@ -546,7 +595,7 @@ When installed, the generated console scripts (`lib-cli-exit-tools`, `cli-exit-t
|
|
|
546
595
|
|
|
547
596
|
## Modern Python Toolchain
|
|
548
597
|
|
|
549
|
-
- Targets Python 3.
|
|
598
|
+
- Targets Python 3.10+ for modern language features and broad compatibility.
|
|
550
599
|
- Development extras track the latest stable releases published on PyPI so quality gates match local and CI environments.
|
|
551
600
|
- GitHub Actions workflows rely on the current major releases of `actions/checkout`, `actions/setup-python`, and `astral-sh/setup-uv`, aligning the automation stack with the 2025 runner images.
|
|
552
601
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_cli_exit_tools"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.2.1"
|
|
4
4
|
description = "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
7
|
dependencies = ["rich-click>=1.9.4"]
|
|
8
8
|
license = { text = "MIT" }
|
|
9
9
|
authors = [{ name = "bitranox", email = "bitranox@gmail.com" }]
|
|
@@ -28,7 +28,7 @@ dev = [
|
|
|
28
28
|
"codecov-cli>=11.2.6",
|
|
29
29
|
"coverage[toml]>=7.13.0",
|
|
30
30
|
"hypothesis>=6.148.7",
|
|
31
|
-
"import-linter>=2.
|
|
31
|
+
"import-linter>=2.9",
|
|
32
32
|
"bandit>=1.9.2",
|
|
33
33
|
"pip-audit>=2.10.0",
|
|
34
34
|
"pyright>=1.1.407",
|
|
@@ -37,6 +37,7 @@ dev = [
|
|
|
37
37
|
"pytest-cov>=7.0.0",
|
|
38
38
|
"ruff>=0.14.8",
|
|
39
39
|
"textual>=6.8.0",
|
|
40
|
+
"tomli>=2.3.0;python_version<'3.11'",
|
|
40
41
|
"twine>=6.2.0"
|
|
41
42
|
]
|
|
42
43
|
|
|
@@ -56,7 +57,7 @@ include = ["src/lib_cli_exit_tools/py.typed"]
|
|
|
56
57
|
|
|
57
58
|
[tool.ruff]
|
|
58
59
|
line-length = 160
|
|
59
|
-
target-version = "
|
|
60
|
+
target-version = "py310"
|
|
60
61
|
|
|
61
62
|
[tool.ruff.lint.per-file-ignores]
|
|
62
63
|
# ignore the "unused import sys" in the jupyter notebook - which we need (it is a linter hickup)
|
|
@@ -155,4 +156,13 @@ forbidden_modules = [
|
|
|
155
156
|
# Known vulnerabilities to ignore (unfixable or accepted risk)
|
|
156
157
|
# GHSA-4xh5-x5gv-qwph: py library ReDoS (no fix available, low risk)
|
|
157
158
|
# PYSEC-2022-42969: same as above (alias)
|
|
158
|
-
|
|
159
|
+
# PYSEC-2022-43012, CVE-2024-6345, PYSEC-2025-49: setuptools vulnerabilities
|
|
160
|
+
# These are build-time dependencies only, not runtime. The virtual environment
|
|
161
|
+
# setuptools version is managed by the CI environment/pip itself.
|
|
162
|
+
ignore-vulns = [
|
|
163
|
+
"GHSA-4xh5-x5gv-qwph",
|
|
164
|
+
"PYSEC-2022-42969",
|
|
165
|
+
"PYSEC-2022-43012",
|
|
166
|
+
"CVE-2024-6345",
|
|
167
|
+
"PYSEC-2025-49",
|
|
168
|
+
]
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/__init__conf__.py
RENAMED
|
@@ -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.
|
|
27
|
+
version = "2.2.1"
|
|
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.
|
|
@@ -8,10 +8,15 @@ Each test verifies exactly one metadata behavior:
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import runpy
|
|
11
|
-
import
|
|
11
|
+
import sys
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, cast
|
|
14
14
|
|
|
15
|
+
if sys.version_info >= (3, 11):
|
|
16
|
+
import tomllib
|
|
17
|
+
else:
|
|
18
|
+
import tomli as tomllib # type: ignore[import-not-found,no-redef]
|
|
19
|
+
|
|
15
20
|
import pytest
|
|
16
21
|
|
|
17
22
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
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-2.1.1 → lib_cli_exit_tools-2.2.1}/docs/system-design/module_reference.md
RENAMED
|
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-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/adapters/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/adapters/signals.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/application/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/application/runner.py
RENAMED
|
File without changes
|
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/__init__.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/configuration.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/core/exit_codes.py
RENAMED
|
File without changes
|
{lib_cli_exit_tools-2.1.1 → lib_cli_exit_tools-2.2.1}/src/lib_cli_exit_tools/lib_cli_exit_tools.py
RENAMED
|
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
|