simplibs-cli 0.1.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.
- simplibs_cli-0.1.0/LICENSE +21 -0
- simplibs_cli-0.1.0/PKG-INFO +436 -0
- simplibs_cli-0.1.0/README.md +403 -0
- simplibs_cli-0.1.0/pyproject.toml +78 -0
- simplibs_cli-0.1.0/setup.cfg +4 -0
- simplibs_cli-0.1.0/src/simplibs/cli/__init__.py +87 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/__init__.py +21 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/_helpers/__init__.py +15 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/_helpers/is_terminal.py +34 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/_validations/__init__.py +15 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/_validations/raise_not_interactive_error.py +42 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/is_interactive.py +41 -0
- simplibs_cli-0.1.0/src/simplibs/cli/interaction/require_interactive.py +41 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/__init__.py +43 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/__init__.py +27 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/build_rejection_message.py +43 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/describe_range.py +36 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/format_default_hint.py +33 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/parse_keys.py +30 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/print_problem.py +23 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/read_answer.py +44 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_helpers/unquote_answer.py +32 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/__init__.py +23 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/raise_create_needs_kind_error.py +31 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/raise_default_not_in_options_error.py +38 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/raise_no_answer_available_error.py +41 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/raise_options_empty_error.py +28 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/_validations/raise_range_empty_error.py +41 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_choice.py +87 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_confirm.py +73 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_int.py +99 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_multi_choice.py +107 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_path.py +153 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_secret.py +71 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/prompt_text.py +90 -0
- simplibs_cli-0.1.0/src/simplibs/cli/prompts/resolve_or_prompt.py +74 -0
- simplibs_cli-0.1.0/src/simplibs/cli/py.typed +1 -0
- simplibs_cli-0.1.0/src/simplibs/cli/reporting/__init__.py +18 -0
- simplibs_cli-0.1.0/src/simplibs/cli/reporting/print_choices.py +58 -0
- simplibs_cli-0.1.0/src/simplibs/cli/reporting/report_failures.py +64 -0
- simplibs_cli-0.1.0/src/simplibs/cli/verbosity/__init__.py +16 -0
- simplibs_cli-0.1.0/src/simplibs/cli/verbosity/resolve_verbosity.py +76 -0
- simplibs_cli-0.1.0/src/simplibs_cli.egg-info/PKG-INFO +436 -0
- simplibs_cli-0.1.0/src/simplibs_cli.egg-info/SOURCES.txt +45 -0
- simplibs_cli-0.1.0/src/simplibs_cli.egg-info/dependency_links.txt +1 -0
- simplibs_cli-0.1.0/src/simplibs_cli.egg-info/requires.txt +9 -0
- simplibs_cli-0.1.0/src/simplibs_cli.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dalibor Sova (Sudip2708)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simplibs-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The small, repeatable parts of a command-line program that talks to a person: prompts that ask again until the answer is usable, a check whether anybody can answer, option lists, failure summaries and verbosity flags resolved to a logging level.
|
|
5
|
+
Author-email: "Dalibor Sova (Sudip2708)" <daliborsova@seznam.cz>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/simplibs/simplibs-cli
|
|
8
|
+
Project-URL: Repository, https://github.com/simplibs/simplibs-cli
|
|
9
|
+
Project-URL: Issues, https://github.com/simplibs/simplibs-cli/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/simplibs/simplibs-cli/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: cli,command-line,prompt,prompts,input,interactive,terminal,verbosity,argparse,simplibs,simplibs-cli,developer-tools
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: simplibs-exception>=1.1.4
|
|
25
|
+
Requires-Dist: simplibs-validate>=0.3.2
|
|
26
|
+
Requires-Dist: simplibs-rules>=0.4.0
|
|
27
|
+
Requires-Dist: simplibs-path>=0.1.1
|
|
28
|
+
Requires-Dist: simplibs-style>=0.1.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=9.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# 💬 `simplibs-cli`
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/simplibs-cli/)
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
[](https://github.com/simplibs/simplibs-cli/blob/main/LICENSE)
|
|
39
|
+
[](https://github.com/simplibs/simplibs-cli/actions)
|
|
40
|
+
|
|
41
|
+
**The conversation with the person at the terminal — asked once, asked right.**
|
|
42
|
+
|
|
43
|
+
A small Python library that replaces the `while True: input()` loop every command-line program
|
|
44
|
+
writes by hand. A prompt **asks again until the answer is acceptable**, so what it returns needs no
|
|
45
|
+
second check; a typo is answered with one warning line, a wrong call with a card, and a program with
|
|
46
|
+
nobody to ask with a clear message instead of a hang. It starts where `argparse`, `click` or `typer`
|
|
47
|
+
hand over the values, and it does nothing but the conversation.
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from simplibs.cli import prompt_int, prompt_confirm
|
|
51
|
+
|
|
52
|
+
workers = prompt_int("Workers", default=4, minimum=1, maximum=64)
|
|
53
|
+
# Workers [4]: 100
|
|
54
|
+
# Please enter a whole number from 1 to 64. <- asked again, nothing raised
|
|
55
|
+
# Workers [4]: <- Enter: the default
|
|
56
|
+
|
|
57
|
+
if prompt_confirm("Start?", default=False): # a hurried Enter means "no"
|
|
58
|
+
print(f"Starting {workers} workers")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
> Colors come from [`simplibs-style`](https://pypi.org/project/simplibs-style/) and disappear on their
|
|
62
|
+
> own when the output is a pipe or a file.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🧭 The Core Philosophy
|
|
67
|
+
|
|
68
|
+
Every command-line program that asks a question ends up with the same loop: read a line, strip it,
|
|
69
|
+
parse it, check the range, print a complaint, repeat. Each copy forgets something — the empty Enter,
|
|
70
|
+
the default that is not an option, the input that has ended and turns into a bare `EOFError` in CI.
|
|
71
|
+
`simplibs-cli` writes the loop once, and writes it carefully.
|
|
72
|
+
|
|
73
|
+
* **Ask until it is usable.** A prompt returns only an acceptable answer — the right type, within the
|
|
74
|
+
limits, one of the options, satisfying the rule. What comes back needs no second check.
|
|
75
|
+
* **A typo is not an exception.** A refused answer is explained in one warning line and the question
|
|
76
|
+
is repeated. A *wrong call* — an empty option list, a default that is not an option, limits that
|
|
77
|
+
exclude everything — is the programmer's mistake and raises a card before the first question.
|
|
78
|
+
* **A default is a real answer.** Pressing Enter returns it, and it goes through the same checks as
|
|
79
|
+
anything typed, so the result always satisfies what the caller asked for.
|
|
80
|
+
* **The command line first.** `resolve_or_prompt` uses a value from the arguments when it is good and
|
|
81
|
+
asks only when it is missing or refused — the program reads the same either way.
|
|
82
|
+
* **Nobody there is an answer too.** `is_interactive()` and `require_interactive()` turn "the program
|
|
83
|
+
hangs in CI" into a card that names the option to pass.
|
|
84
|
+
* **It starts where the parser stops.** No argument parsing, no logging setup — `resolve_verbosity`
|
|
85
|
+
only returns the level. There are no classes, rules or actions: a prompt is an interaction, not a
|
|
86
|
+
step of a data pipeline.
|
|
87
|
+
* **Layers that only look left.** `interaction`, `reporting` and `verbosity` stand alone ← `prompts`
|
|
88
|
+
— every layer imports only from those before it.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
prompt_choice("Format", {}) # -> raises: nothing to choose from
|
|
92
|
+
prompt_int("Workers", minimum=10, maximum=1) # -> raises: the limits exclude everything
|
|
93
|
+
prompt_choice("Format", {"json": "…"}, default="xml") # -> raises: the default is not an option
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 📦 Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install simplibs-cli
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Requires Python 3.11+. The runtime dependencies (installed automatically) are other simplibs
|
|
105
|
+
libraries: [`simplibs-exception`](https://pypi.org/project/simplibs-exception/) for the structured
|
|
106
|
+
error cards, [`simplibs-validate`](https://pypi.org/project/simplibs-validate/) for `@validate_call` on
|
|
107
|
+
the public functions, [`simplibs-rules`](https://pypi.org/project/simplibs-rules/) for the message of
|
|
108
|
+
a refusing rule, [`simplibs-path`](https://pypi.org/project/simplibs-path/) for `prompt_path` and
|
|
109
|
+
[`simplibs-style`](https://pypi.org/project/simplibs-style/) for colors that disappear outside a
|
|
110
|
+
terminal.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 🚀 Quick Start in 60 Seconds
|
|
115
|
+
|
|
116
|
+
### Level 1: Ask and get a usable answer
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from simplibs.cli import prompt_text, prompt_int, prompt_confirm
|
|
120
|
+
|
|
121
|
+
name = prompt_text("Project name", default="demo")
|
|
122
|
+
workers = prompt_int("Workers", default=4, minimum=1, maximum=64)
|
|
123
|
+
|
|
124
|
+
if prompt_confirm("Create the project?", default=True):
|
|
125
|
+
print(f"Creating {name} with {workers} workers")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Every question shows its default in brackets, and Enter takes it. An answer that is not accepted is
|
|
129
|
+
explained in one line and the question comes again.
|
|
130
|
+
|
|
131
|
+
### Level 2: Choices and paths
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from simplibs.cli import prompt_choice, prompt_multi_choice, prompt_path
|
|
135
|
+
|
|
136
|
+
fmt = prompt_choice("Format", {"json": "Machine-readable", "text": "For people"}, default="text")
|
|
137
|
+
extras = prompt_multi_choice("Extras", {"tests": "Test folder", "docs": "Docs folder"}, allow_empty=True)
|
|
138
|
+
target = prompt_path("Output folder", kind="dir", create_if_missing=True)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
json - Machine-readable
|
|
143
|
+
text - For people
|
|
144
|
+
|
|
145
|
+
Format [text]: xml
|
|
146
|
+
'xml' is not a valid option. Please try again.
|
|
147
|
+
Format [text]:
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`fmt` is always one of the keys, `extras` is a tuple (possibly empty), and `target` is a normalized
|
|
151
|
+
`Path` of a folder that exists — created on the spot when it did not.
|
|
152
|
+
|
|
153
|
+
### Level 3: The command line first, the question second
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
import argparse
|
|
157
|
+
from pathlib import Path
|
|
158
|
+
from simplibs.cli import prompt_path, require_interactive, resolve_or_prompt
|
|
159
|
+
|
|
160
|
+
parser = argparse.ArgumentParser()
|
|
161
|
+
parser.add_argument("--folder")
|
|
162
|
+
args = parser.parse_args()
|
|
163
|
+
|
|
164
|
+
if args.folder is None:
|
|
165
|
+
require_interactive("--folder is missing") # in CI: a card that names the option, not a hang
|
|
166
|
+
|
|
167
|
+
folder = resolve_or_prompt(
|
|
168
|
+
args.folder,
|
|
169
|
+
is_valid=lambda value: Path(value).is_dir(),
|
|
170
|
+
prompt=lambda: prompt_path("Folder", kind="dir", must_exist=True),
|
|
171
|
+
invalid_message=f"'{args.folder}' is not a folder.",
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
A good `--folder` is used as it is. A bad one is mentioned and replaced by a question. A missing one
|
|
176
|
+
is asked for — or, without a terminal, refused with a card.
|
|
177
|
+
|
|
178
|
+
### Level 4: A whole program — verbosity, a batch and an exit code
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import argparse
|
|
182
|
+
import logging
|
|
183
|
+
from simplibs.cli import report_failures, resolve_verbosity
|
|
184
|
+
|
|
185
|
+
def process(name: str) -> bool:
|
|
186
|
+
return name.endswith(".txt") # stands in for the real work
|
|
187
|
+
|
|
188
|
+
parser = argparse.ArgumentParser()
|
|
189
|
+
parser.add_argument("-q", "--quiet", action="store_true")
|
|
190
|
+
parser.add_argument("-v", "--verbose", action="count", default=0)
|
|
191
|
+
parser.add_argument("files", nargs="*")
|
|
192
|
+
args = parser.parse_args()
|
|
193
|
+
|
|
194
|
+
logging.basicConfig(level=resolve_verbosity(quiet=args.quiet, verbose=args.verbose))
|
|
195
|
+
|
|
196
|
+
results = [(name, process(name)) for name in args.files] # (item, succeeded) pairs
|
|
197
|
+
ok = report_failures(results, describe=lambda name: f"{name} could not be processed")
|
|
198
|
+
raise SystemExit(0 if ok else 1)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`-q` shows only errors, `-v` adds progress, `-vv` shows everything. The batch runs to the end, the
|
|
202
|
+
failures are listed once, and the exit code follows.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 🛠️ Architecture & Package Structure
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
src/simplibs/cli/
|
|
210
|
+
├── interaction/ ◄── Is there somebody to ask? (is_interactive, require_interactive)
|
|
211
|
+
├── reporting/ ◄── Output that is not a question (print_choices, report_failures)
|
|
212
|
+
├── verbosity/ ◄── Flags to a logging level (resolve_verbosity)
|
|
213
|
+
└── prompts/ ◄── The questions (seven prompts and resolve_or_prompt, with the helpers
|
|
214
|
+
and the diagnostic raisers behind them)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The root of the package exports every function, so one import line is all a program needs. The
|
|
218
|
+
folders are plain packages for the code's own organization; nothing has to be imported from them.
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from simplibs.cli import prompt_text, prompt_path, resolve_or_prompt # the questions
|
|
222
|
+
from simplibs.cli import is_interactive, report_failures, resolve_verbosity
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🙋 Prompts — Ask a Person
|
|
228
|
+
|
|
229
|
+
Seven prompts that ask at a terminal and ask again until the answer is acceptable, plus
|
|
230
|
+
`resolve_or_prompt`, which puts one of them behind a value from the command line. They share four
|
|
231
|
+
rules: an empty answer takes the default (or is refused), a refused answer is explained in one line
|
|
232
|
+
and asked again, a wrong call raises a `ParamError` before the first question, and an input that has
|
|
233
|
+
ended raises a `StateError`.
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
name = prompt_text("Project name", default="demo")
|
|
237
|
+
port = prompt_int("Port", minimum=1024, maximum=65535)
|
|
238
|
+
ok = prompt_confirm("Overwrite?", default=False)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
➡️ [README_CLI_PROMPTS](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_PROMPTS.md)
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 🚦 Interaction — Is Anybody There?
|
|
246
|
+
|
|
247
|
+
Two functions that answer one question before anything is asked: can this program talk to a person?
|
|
248
|
+
A person needs a way to answer and a way to read the question, so both standard input and standard
|
|
249
|
+
output must be terminals. `is_interactive()` returns the answer; `require_interactive()` turns it
|
|
250
|
+
into a card that says why an answer was needed.
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
if not is_interactive():
|
|
254
|
+
print("Pass --name, there is nobody to ask.")
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
➡️ [README_CLI_INTERACTION](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_INTERACTION.md)
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 📋 Reporting — Say Something That Is Not a Question
|
|
262
|
+
|
|
263
|
+
Two functions for output that is not a question: `print_choices` prints an aligned
|
|
264
|
+
`key - description` list (the prompts use it too, so a list looks the same everywhere), and
|
|
265
|
+
`report_failures` prints the failed items of a batch and returns whether everything succeeded — ready
|
|
266
|
+
for `SystemExit`.
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
print_choices({"json": "Machine-readable", "text": "For people"}, title="Formats:")
|
|
270
|
+
# Formats:
|
|
271
|
+
# json - Machine-readable
|
|
272
|
+
# text - For people
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
➡️ [README_CLI_REPORTING](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_REPORTING.md)
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 🔊 Verbosity — Flags to a Level
|
|
280
|
+
|
|
281
|
+
One function that turns `--quiet` and a counted `-v` / `-vv` into one `logging` level. `quiet` wins
|
|
282
|
+
over `verbose`, and a single `-v` never makes the output quieter than the default. It only returns
|
|
283
|
+
the level; the program's own logging applies it.
|
|
284
|
+
|
|
285
|
+
| Flags | Level |
|
|
286
|
+
|----------------------|-----------------------------|
|
|
287
|
+
| none | `WARNING` (the default) |
|
|
288
|
+
| `-v` | `INFO` |
|
|
289
|
+
| `-vv` | `DEBUG` |
|
|
290
|
+
| `-q` (with or without `-v`) | `ERROR` |
|
|
291
|
+
|
|
292
|
+
➡️ [README_CLI_VERBOSITY](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_VERBOSITY.md)
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 📖 Quick Reference
|
|
297
|
+
|
|
298
|
+
### 🙋 `prompts/` — Ask a Person
|
|
299
|
+
|
|
300
|
+
| Function | Description / Parameters |
|
|
301
|
+
|-----------------------|-------------------------------------------------------------------------------------------------------|
|
|
302
|
+
| `prompt_text` | A line of text, optionally judged by a rule (`message`, `default`, `allow_empty=False`, `rule`, `invalid_message`). |
|
|
303
|
+
| `prompt_int` | A whole number, optionally within limits (`message`, `default`, `minimum`, `maximum`). |
|
|
304
|
+
| `prompt_secret` | A line typed without being shown (`message`, `allow_empty=False`). |
|
|
305
|
+
| `prompt_confirm` | A yes/no question; always returns a `bool` (`message`, `default`). |
|
|
306
|
+
| `prompt_choice` | One key from a described set (`message`, `options`, `default`). |
|
|
307
|
+
| `prompt_multi_choice` | One or more comma-separated keys, in the order typed, as a tuple (`message`, `options`, `default=()`, `allow_empty=False`). |
|
|
308
|
+
| `prompt_path` | A normalized `Path` of a given kind, existing or created (`message`, `kind="any"`, `must_exist=False`, `create_if_missing=False`, `default`, `rule`, `invalid_message`). |
|
|
309
|
+
| `resolve_or_prompt` | Uses a value from the command line, asks only when it is missing or refused (`value`, `is_valid`, `prompt`, `invalid_message`). |
|
|
310
|
+
|
|
311
|
+
➡️ [README_CLI_PROMPTS](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_PROMPTS.md)
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
### 🚦 `interaction/` — Is Anybody There?
|
|
316
|
+
|
|
317
|
+
| Function | Description / Parameters |
|
|
318
|
+
|-----------------------|--------------------------------------------------------------------------------|
|
|
319
|
+
| `is_interactive` | `True` when both standard input and standard output are terminals. |
|
|
320
|
+
| `require_interactive` | Raises `StateError` when there is nobody to ask (`reason=None`). |
|
|
321
|
+
|
|
322
|
+
➡️ [README_CLI_INTERACTION](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_INTERACTION.md)
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### 📋 `reporting/` — Say Something That Is Not a Question
|
|
327
|
+
|
|
328
|
+
| Function | Description / Parameters |
|
|
329
|
+
|-------------------|------------------------------------------------------------------------------------------|
|
|
330
|
+
| `print_choices` | Prints an aligned `key - description` list (`options`, `title=None`). |
|
|
331
|
+
| `report_failures` | Prints the failed items of a batch, returns whether all succeeded (`results`, `describe`, `title`). |
|
|
332
|
+
|
|
333
|
+
➡️ [README_CLI_REPORTING](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_REPORTING.md)
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
### 🔊 `verbosity/` — Flags to a Level
|
|
338
|
+
|
|
339
|
+
| Function | Description / Parameters |
|
|
340
|
+
|---------------------|-------------------------------------------------------------------------------------------|
|
|
341
|
+
| `resolve_verbosity` | Resolves `--quiet` and `-v` / `-vv` into a logging level (`quiet=False`, `verbose=0`, `default_level=logging.WARNING`). |
|
|
342
|
+
|
|
343
|
+
➡️ [README_CLI_VERBOSITY](https://github.com/simplibs/simplibs-cli/blob/main/docs/README_CLI_VERBOSITY.md)
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 🔌 Pipes, CI and Ctrl+C
|
|
348
|
+
|
|
349
|
+
How the library behaves where there is no person at the keyboard — or where the person gives up:
|
|
350
|
+
|
|
351
|
+
* **Piped answers work.** `echo y | tool` drives the prompts, and so do tests. The prompts do not check
|
|
352
|
+
for a terminal themselves; whether a *missing* answer is an error is the program's decision, made
|
|
353
|
+
with `require_interactive()`.
|
|
354
|
+
* **A dead input is a card.** When the input has ended (`tool < /dev/null`, a CI job), the prompt
|
|
355
|
+
raises `CLI_NO_ANSWER_AVAILABLE` — what was asked, and what to do — instead of a bare `EOFError`.
|
|
356
|
+
* **Secrets are not echoed, but they can be piped.** Without a terminal `prompt_secret` falls back to
|
|
357
|
+
standard input with a warning, unhidden. A program that must never read a secret from a pipe checks
|
|
358
|
+
`is_interactive()` first.
|
|
359
|
+
* **`Ctrl+C` is never swallowed.** `KeyboardInterrupt` goes up unchanged — it is the user saying
|
|
360
|
+
"stop", and a prompt that caught it would be a trap.
|
|
361
|
+
* **Everything goes to standard output.** Questions, warnings, lists and reports share one stream, so
|
|
362
|
+
colors are decided once and never leak into a redirected file.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## ⚠️ Exceptions
|
|
367
|
+
|
|
368
|
+
Every error is a structured [`simplibs.exception`](https://pypi.org/project/simplibs-exception/) card,
|
|
369
|
+
and every error name starts with `CLI_`. A wrong call is a `ParamError`, raised before the first
|
|
370
|
+
question; a missing terminal or a dead input is a `StateError`. Each error is documented next to the
|
|
371
|
+
function that raises it.
|
|
372
|
+
|
|
373
|
+
| Error name | Card | Wraps | Raised when |
|
|
374
|
+
|------------------------------|--------------|----------------|--------------------------------------------------------------------|
|
|
375
|
+
| `CLI_OPTIONS_EMPTY` | `ParamError` | `ValueError` | `prompt_choice` / `prompt_multi_choice` got no options. |
|
|
376
|
+
| `CLI_DEFAULT_NOT_IN_OPTIONS` | `ParamError` | `ValueError` | A default (or a key of it) is not one of the options. |
|
|
377
|
+
| `CLI_RANGE_EMPTY` | `ParamError` | `ValueError` | `prompt_int` got a `minimum` greater than the `maximum`. |
|
|
378
|
+
| `CLI_CREATE_NEEDS_KIND` | `ParamError` | `ValueError` | `prompt_path` got `create_if_missing=True` with `kind="any"`. |
|
|
379
|
+
| `CLI_NOT_INTERACTIVE` | `StateError` | `RuntimeError` | `require_interactive` found input or output that is not a terminal. |
|
|
380
|
+
| `CLI_NO_ANSWER_AVAILABLE` | `StateError` | `EOFError` | The input ended before a prompt got its answer. |
|
|
381
|
+
|
|
382
|
+
A mistake in the *type* of an argument is refused by `@validate_call` (from
|
|
383
|
+
[`simplibs-validate`](https://pypi.org/project/simplibs-validate/)) before the function runs.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 🔗 Related libraries
|
|
388
|
+
|
|
389
|
+
* **[`simplibs-exception`](https://pypi.org/project/simplibs-exception/)** — the structured cards behind every error here.
|
|
390
|
+
* **[`simplibs-validate`](https://pypi.org/project/simplibs-validate/)** — `@validate_call`, which checks the arguments of every public function.
|
|
391
|
+
* **[`simplibs-rules`](https://pypi.org/project/simplibs-rules/)** — the `Rule` objects that `prompt_text` and `prompt_path` accept, and whose `describe()` becomes the refusal message.
|
|
392
|
+
* **[`simplibs-path`](https://pypi.org/project/simplibs-path/)** — normalizing and creating the path in `prompt_path`, and the path rules to hand it.
|
|
393
|
+
* **[`simplibs-style`](https://pypi.org/project/simplibs-style/)** — the colors of warnings, headings and keys, plain outside a terminal.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## ☯️ About simplibs
|
|
398
|
+
|
|
399
|
+
All libraries in the **simplibs** (Simple Libraries) ecosystem share a common
|
|
400
|
+
engineering philosophy:
|
|
401
|
+
|
|
402
|
+
* **Dyslexia-friendly:**
|
|
403
|
+
We actively minimize cognitive load. Code is atomized into small, self-contained units,
|
|
404
|
+
files are named directly after the logical task they perform, and explanations describe
|
|
405
|
+
*why* something is designed, not just *what* it is.
|
|
406
|
+
* **Programmer's Zen:**
|
|
407
|
+
Nothing should be missing, and nothing should be superfluous. We value clean execution
|
|
408
|
+
paths and robust, understandable code architectures over rushed, messy feature sets.
|
|
409
|
+
* **Defensive Style:**
|
|
410
|
+
We actively anticipate edge cases and failure modes so that only safe operational paths
|
|
411
|
+
remain. Our code is built to degrade gracefully rather than crash unexpectedly.
|
|
412
|
+
* **Minimalism:**
|
|
413
|
+
Find the most direct path to the goal in as few operational steps as possible without
|
|
414
|
+
taking shortcuts on safety, readability, or completeness.
|
|
415
|
+
* **Code as Craft:**
|
|
416
|
+
Code should be pleasant to look at, readable at a glance, and evoke structural harmony.
|
|
417
|
+
We treat software engineering as a precision trade.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
### 🤝 Contributing & Community
|
|
422
|
+
|
|
423
|
+
This is an **open-source project** built with love and care. We strongly believe in
|
|
424
|
+
community collaboration and welcome any feedback, bug reports, or feature ideas!
|
|
425
|
+
|
|
426
|
+
* **Want to contribute?** Feel free to open an Issue or submit a Pull Request.
|
|
427
|
+
* **Want to get in touch?** If you'd like to discuss the project further, collaborate,
|
|
428
|
+
or just say hello, feel free to open a GitHub Issue or start a Discussion.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### 📝 License
|
|
433
|
+
|
|
434
|
+
This library is released under the **MIT License**. Build great things!
|
|
435
|
+
|
|
436
|
+
---
|