slick-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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nehz
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: slick-cli
3
+ Version: 0.1.0
4
+ Summary: Slick, type-hint driven command-line apps built on the Python standard library.
5
+ Author: nehz
6
+ License-Expression: MIT
7
+ Keywords: cli,command-line,argparse,decorator,type-hints,terminal
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.10
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.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # slick-cli
27
+
28
+ **Write a function, get a command-line app.** `slick-cli` turns ordinary, type-hinted
29
+ Python functions into polished CLIs: arguments, options, flags, choices, subcommands,
30
+ help text and colored output, with **zero dependencies** (it is a thin, friendly layer
31
+ over `argparse`).
32
+
33
+ ```python
34
+ from slick_cli import App, echo
35
+
36
+ app = App("greeter", "Friendly greetings.", version="1.0")
37
+
38
+ @app.command
39
+ def hello(name: str, count: int = 1, shout: bool = False) -> None:
40
+ """Greet someone.
41
+
42
+ Args:
43
+ name: Who to greet.
44
+ count: How many times.
45
+ shout: Use UPPERCASE.
46
+ """
47
+ for _ in range(count):
48
+ echo(name.upper() if shout else f"Hello, {name}!")
49
+
50
+ if __name__ == "__main__":
51
+ app()
52
+ ```
53
+
54
+ ```console
55
+ $ python greeter.py hello Ada --count 2
56
+ Hello, Ada!
57
+ Hello, Ada!
58
+ $ python greeter.py hello --help
59
+ usage: greeter hello [-h] [--count COUNT] [--shout | --no-shout] NAME
60
+ ...
61
+ ```
62
+
63
+ ## Features
64
+
65
+ - **Decorator-based commands**: `@app.command` registers a function and returns it unchanged, so it stays directly callable and testable.
66
+ - **Type hints drive parsing**: `int`, `float`, `Path`, `bool` flags, `list[T]`, `*args`, `Literal[...]` and `Enum` choices, and `T | None`.
67
+ - **Help from docstrings**: the summary, description and Google-style `Args:` entries become `--help` output, with defaults and env vars listed.
68
+ - **Subcommand groups**: nest apps with `app.group(...)` or mount an existing one with `app.add_app(...)`; command aliases are supported.
69
+ - **Per-parameter tweaks** via `Annotated[T, Arg(...)]`: short flags, custom names, metavars and environment-variable fallbacks.
70
+ - **Colored output that behaves**: `style`/`secho` emit ANSI color only to TTYs, and respect `NO_COLOR` and `FORCE_COLOR`.
71
+ - **Clean errors and exit codes**: `abort("msg")` prints `Error: msg` and exits non-zero, with no traceback. Return values become exit codes.
72
+ - **In-process testing**: `slick_cli.testing.invoke` captures exit code, stdout and stderr.
73
+ - Standard library only, Python 3.10+, fully typed (`py.typed`).
74
+
75
+ ## Install
76
+
77
+ ```console
78
+ pip install slick-cli
79
+ ```
80
+
81
+ Or from a checkout: `pip install .`
82
+
83
+ ## Quickstart
84
+
85
+ ```python
86
+ # todo.py
87
+ from pathlib import Path
88
+ from typing import Annotated, Literal
89
+
90
+ from slick_cli import App, Arg, abort, secho
91
+
92
+ app = App("todo", "A tiny todo manager.")
93
+ FILE = Path("todo.txt")
94
+
95
+ @app.command
96
+ def add(text: list[str], *, priority: Literal["low", "high"] = "low") -> None:
97
+ """Add an item."""
98
+ with FILE.open("a") as f:
99
+ f.write(f"[{priority}] {' '.join(text)}\n")
100
+ secho("added", fg="green")
101
+
102
+ @app.command(name="list", aliases=["ls"])
103
+ def list_items(*, high_only: Annotated[bool, Arg(short="-H")] = False) -> None:
104
+ """Show items."""
105
+ if not FILE.exists():
106
+ abort("nothing to do yet")
107
+ for line in FILE.read_text().splitlines():
108
+ if not high_only or line.startswith("[high]"):
109
+ print(line)
110
+
111
+ if __name__ == "__main__":
112
+ app()
113
+ ```
114
+
115
+ ```console
116
+ $ python todo.py add buy milk --priority high
117
+ added
118
+ $ python todo.py ls -H
119
+ [high] buy milk
120
+ ```
121
+
122
+ A runnable tour lives in [`examples/demo.py`](examples/demo.py):
123
+
124
+ ```console
125
+ python3 examples/demo.py --help
126
+ python3 examples/demo.py hello Ada --count 2 --shout --color magenta
127
+ python3 examples/demo.py sum 1.5 2 3.25 -p 1
128
+ python3 examples/demo.py files ls --path . --ext .py
129
+ DEMO_TOKEN=secret python3 examples/demo.py deploy prod -y
130
+ ```
131
+
132
+ ## How parameters map to the command line
133
+
134
+ | Python parameter | Command line |
135
+ |----------------------------------------|------------------------------------------------|
136
+ | `name: str` (no default) | positional `NAME` |
137
+ | `count: int = 1` (has a default) | option `--count COUNT` |
138
+ | `*, user: str` (keyword-only, no default) | required option `--user USER` |
139
+ | `verbose: bool = False` | flag `--verbose / --no-verbose` |
140
+ | `files: list[Path]` (no default) | positional taking one or more values |
141
+ | `*, tag: list[str] \| None = None` | repeatable option `--tag a --tag b` |
142
+ | `*rest: int` | positional taking zero or more values |
143
+ | `mode: Literal["fast", "slow"]` | choices `{fast,slow}` |
144
+ | `color: Color` (an `Enum`) | choices from member values (names also accepted, case-insensitive) |
145
+ | `out: Path \| None = None` | same as `Path`; `None` when omitted |
146
+ | `when: SomeType` | `SomeType(string)` is used as the converter |
147
+
148
+ Underscores in names become dashes (`dry_run` becomes `--dry-run`). Unannotated
149
+ parameters are strings. A `bool` with no default is a flag defaulting to `False`.
150
+ `**kwargs` is rejected with `TypeError`.
151
+
152
+ ## API overview
153
+
154
+ Everything below is importable from `slick_cli` unless noted.
155
+
156
+ ### `App(name=None, help=None, *, version=None)`
157
+
158
+ A collection of commands and nested groups.
159
+
160
+ - `@app.command` / `@app.command(name=None, *, help=None, aliases=())`: register a function. The name defaults to the function name with `_` replaced by `-` (leading/trailing underscores stripped). `help` defaults to the docstring summary. Duplicate names or aliases raise `ValueError`.
161
+ - `app.group(name, help=None) -> App`: create and mount a nested app for subcommands.
162
+ - `app.add_app(other, name=None) -> App`: mount an existing app (uses `other.name` if `name` is omitted).
163
+ - `app.run(argv=None) -> int`: parse `argv` (default `sys.argv[1:]`), run the command, return the exit code. Never raises `SystemExit`.
164
+ - `app.main(argv=None)` and `app(argv=None)`: `run`, then `sys.exit` with the code.
165
+ - `app.build_parser(prog=None) -> argparse.ArgumentParser`: the underlying parser, if you need it.
166
+ - `app.commands`: dict of registered `Command` objects and nested `App`s, by name.
167
+
168
+ Running an app (or a group) with no command prints its help and exits 0.
169
+ `--version` is added when `version` is set.
170
+
171
+ ### `run(func, argv=None, *, prog=None, version=None) -> int`
172
+
173
+ Run a single function as a whole CLI with no subcommands.
174
+
175
+ ### Exit codes
176
+
177
+ | Outcome | Exit code |
178
+ |-------------------------------------------------|----------------|
179
+ | command returns `None` | `0` |
180
+ | command returns an `int` | that int |
181
+ | command returns `True` / `False` | `0` / `1` |
182
+ | command returns anything else | printed, `0` |
183
+ | `CliError(msg, code)` / `abort(msg, code)` | `code` (default `1`) |
184
+ | usage error (bad value, missing argument) | `2` |
185
+ | `Ctrl-C` | `130` |
186
+
187
+ ### `Arg(help=None, short=None, name=None, metavar=None, env=None)`
188
+
189
+ Per-parameter metadata, attached with `typing.Annotated`:
190
+
191
+ ```python
192
+ def login(*, user: Annotated[str, Arg(short="-u", env="APP_USER", help="Account name")]): ...
193
+ ```
194
+
195
+ - `help` overrides the docstring entry.
196
+ - `short` adds a short flag (options and flags only).
197
+ - `name` replaces the long option name.
198
+ - `metavar` sets the usage placeholder.
199
+ - `env` names an environment variable used when the option is not given. Booleans accept `1/true/yes/on` and `0/false/no/off`. List options split the variable on commas. A bad value is a usage error (exit 2).
200
+
201
+ ### Errors
202
+
203
+ - `CliError(message, exit_code=1)`: raise it for expected failures. It prints `Error: <message>` to stderr.
204
+ - `abort(message, exit_code=1)`: shorthand that raises `CliError`.
205
+
206
+ ### Output
207
+
208
+ - `style(text, fg=None, bg=None, *, bold=False, dim=False, italic=False, underline=False) -> str`: wrap text in ANSI codes. The colors are `black red green yellow blue magenta cyan white`, each also with a `bright_` prefix. An unknown color raises `ValueError`.
209
+ - `unstyle(text) -> str`: strip ANSI codes.
210
+ - `echo(message="", *, err=False, nl=True, color=None)`: print to stdout or stderr. Styling is stripped unless `should_color(stream)` is true or `color=True` is passed.
211
+ - `secho(message="", *, err=False, nl=True, color=None, **styles)`: `style` and `echo` in one call.
212
+ - `should_color(stream) -> bool`: returns `False` if `NO_COLOR` is set, otherwise `True` if `FORCE_COLOR` is set, otherwise whether `stream` is a TTY.
213
+ - `confirm(prompt, default=False) -> bool`: a yes/no prompt on stdin. An empty answer or end of input returns `default`.
214
+
215
+ ### Lower-level pieces
216
+
217
+ - `Command(func, name=None, *, help=None, aliases=())`: the object behind each registered command. It has `.name`, `.help`, `.description`, `.aliases` and `.params`.
218
+ - `slick_cli.docstrings.parse_docstring(doc) -> DocInfo` returns `summary`, `description` and `params`.
219
+
220
+ ### Testing: `slick_cli.testing`
221
+
222
+ ```python
223
+ from slick_cli.testing import invoke
224
+
225
+ result = invoke(app, ["hello", "Ada"], env={"APP_USER": "me"}, input="y\n")
226
+ assert result.exit_code == 0
227
+ assert result.stdout == "Hello, Ada!\n"
228
+ ```
229
+
230
+ `invoke(target, args=(), *, env=None, input=None) -> Result` accepts an `App` or a plain
231
+ function. `Result` has `exit_code`, `stdout` and `stderr`.
232
+
233
+ ## Development
234
+
235
+ ```console
236
+ PYTHONPATH=src python3 -m unittest discover -s tests -v
237
+ # or, if pytest is installed:
238
+ python3 -m pytest
239
+ ```
240
+
241
+ ## License
242
+
243
+ MIT
@@ -0,0 +1,218 @@
1
+ # slick-cli
2
+
3
+ **Write a function, get a command-line app.** `slick-cli` turns ordinary, type-hinted
4
+ Python functions into polished CLIs: arguments, options, flags, choices, subcommands,
5
+ help text and colored output, with **zero dependencies** (it is a thin, friendly layer
6
+ over `argparse`).
7
+
8
+ ```python
9
+ from slick_cli import App, echo
10
+
11
+ app = App("greeter", "Friendly greetings.", version="1.0")
12
+
13
+ @app.command
14
+ def hello(name: str, count: int = 1, shout: bool = False) -> None:
15
+ """Greet someone.
16
+
17
+ Args:
18
+ name: Who to greet.
19
+ count: How many times.
20
+ shout: Use UPPERCASE.
21
+ """
22
+ for _ in range(count):
23
+ echo(name.upper() if shout else f"Hello, {name}!")
24
+
25
+ if __name__ == "__main__":
26
+ app()
27
+ ```
28
+
29
+ ```console
30
+ $ python greeter.py hello Ada --count 2
31
+ Hello, Ada!
32
+ Hello, Ada!
33
+ $ python greeter.py hello --help
34
+ usage: greeter hello [-h] [--count COUNT] [--shout | --no-shout] NAME
35
+ ...
36
+ ```
37
+
38
+ ## Features
39
+
40
+ - **Decorator-based commands**: `@app.command` registers a function and returns it unchanged, so it stays directly callable and testable.
41
+ - **Type hints drive parsing**: `int`, `float`, `Path`, `bool` flags, `list[T]`, `*args`, `Literal[...]` and `Enum` choices, and `T | None`.
42
+ - **Help from docstrings**: the summary, description and Google-style `Args:` entries become `--help` output, with defaults and env vars listed.
43
+ - **Subcommand groups**: nest apps with `app.group(...)` or mount an existing one with `app.add_app(...)`; command aliases are supported.
44
+ - **Per-parameter tweaks** via `Annotated[T, Arg(...)]`: short flags, custom names, metavars and environment-variable fallbacks.
45
+ - **Colored output that behaves**: `style`/`secho` emit ANSI color only to TTYs, and respect `NO_COLOR` and `FORCE_COLOR`.
46
+ - **Clean errors and exit codes**: `abort("msg")` prints `Error: msg` and exits non-zero, with no traceback. Return values become exit codes.
47
+ - **In-process testing**: `slick_cli.testing.invoke` captures exit code, stdout and stderr.
48
+ - Standard library only, Python 3.10+, fully typed (`py.typed`).
49
+
50
+ ## Install
51
+
52
+ ```console
53
+ pip install slick-cli
54
+ ```
55
+
56
+ Or from a checkout: `pip install .`
57
+
58
+ ## Quickstart
59
+
60
+ ```python
61
+ # todo.py
62
+ from pathlib import Path
63
+ from typing import Annotated, Literal
64
+
65
+ from slick_cli import App, Arg, abort, secho
66
+
67
+ app = App("todo", "A tiny todo manager.")
68
+ FILE = Path("todo.txt")
69
+
70
+ @app.command
71
+ def add(text: list[str], *, priority: Literal["low", "high"] = "low") -> None:
72
+ """Add an item."""
73
+ with FILE.open("a") as f:
74
+ f.write(f"[{priority}] {' '.join(text)}\n")
75
+ secho("added", fg="green")
76
+
77
+ @app.command(name="list", aliases=["ls"])
78
+ def list_items(*, high_only: Annotated[bool, Arg(short="-H")] = False) -> None:
79
+ """Show items."""
80
+ if not FILE.exists():
81
+ abort("nothing to do yet")
82
+ for line in FILE.read_text().splitlines():
83
+ if not high_only or line.startswith("[high]"):
84
+ print(line)
85
+
86
+ if __name__ == "__main__":
87
+ app()
88
+ ```
89
+
90
+ ```console
91
+ $ python todo.py add buy milk --priority high
92
+ added
93
+ $ python todo.py ls -H
94
+ [high] buy milk
95
+ ```
96
+
97
+ A runnable tour lives in [`examples/demo.py`](examples/demo.py):
98
+
99
+ ```console
100
+ python3 examples/demo.py --help
101
+ python3 examples/demo.py hello Ada --count 2 --shout --color magenta
102
+ python3 examples/demo.py sum 1.5 2 3.25 -p 1
103
+ python3 examples/demo.py files ls --path . --ext .py
104
+ DEMO_TOKEN=secret python3 examples/demo.py deploy prod -y
105
+ ```
106
+
107
+ ## How parameters map to the command line
108
+
109
+ | Python parameter | Command line |
110
+ |----------------------------------------|------------------------------------------------|
111
+ | `name: str` (no default) | positional `NAME` |
112
+ | `count: int = 1` (has a default) | option `--count COUNT` |
113
+ | `*, user: str` (keyword-only, no default) | required option `--user USER` |
114
+ | `verbose: bool = False` | flag `--verbose / --no-verbose` |
115
+ | `files: list[Path]` (no default) | positional taking one or more values |
116
+ | `*, tag: list[str] \| None = None` | repeatable option `--tag a --tag b` |
117
+ | `*rest: int` | positional taking zero or more values |
118
+ | `mode: Literal["fast", "slow"]` | choices `{fast,slow}` |
119
+ | `color: Color` (an `Enum`) | choices from member values (names also accepted, case-insensitive) |
120
+ | `out: Path \| None = None` | same as `Path`; `None` when omitted |
121
+ | `when: SomeType` | `SomeType(string)` is used as the converter |
122
+
123
+ Underscores in names become dashes (`dry_run` becomes `--dry-run`). Unannotated
124
+ parameters are strings. A `bool` with no default is a flag defaulting to `False`.
125
+ `**kwargs` is rejected with `TypeError`.
126
+
127
+ ## API overview
128
+
129
+ Everything below is importable from `slick_cli` unless noted.
130
+
131
+ ### `App(name=None, help=None, *, version=None)`
132
+
133
+ A collection of commands and nested groups.
134
+
135
+ - `@app.command` / `@app.command(name=None, *, help=None, aliases=())`: register a function. The name defaults to the function name with `_` replaced by `-` (leading/trailing underscores stripped). `help` defaults to the docstring summary. Duplicate names or aliases raise `ValueError`.
136
+ - `app.group(name, help=None) -> App`: create and mount a nested app for subcommands.
137
+ - `app.add_app(other, name=None) -> App`: mount an existing app (uses `other.name` if `name` is omitted).
138
+ - `app.run(argv=None) -> int`: parse `argv` (default `sys.argv[1:]`), run the command, return the exit code. Never raises `SystemExit`.
139
+ - `app.main(argv=None)` and `app(argv=None)`: `run`, then `sys.exit` with the code.
140
+ - `app.build_parser(prog=None) -> argparse.ArgumentParser`: the underlying parser, if you need it.
141
+ - `app.commands`: dict of registered `Command` objects and nested `App`s, by name.
142
+
143
+ Running an app (or a group) with no command prints its help and exits 0.
144
+ `--version` is added when `version` is set.
145
+
146
+ ### `run(func, argv=None, *, prog=None, version=None) -> int`
147
+
148
+ Run a single function as a whole CLI with no subcommands.
149
+
150
+ ### Exit codes
151
+
152
+ | Outcome | Exit code |
153
+ |-------------------------------------------------|----------------|
154
+ | command returns `None` | `0` |
155
+ | command returns an `int` | that int |
156
+ | command returns `True` / `False` | `0` / `1` |
157
+ | command returns anything else | printed, `0` |
158
+ | `CliError(msg, code)` / `abort(msg, code)` | `code` (default `1`) |
159
+ | usage error (bad value, missing argument) | `2` |
160
+ | `Ctrl-C` | `130` |
161
+
162
+ ### `Arg(help=None, short=None, name=None, metavar=None, env=None)`
163
+
164
+ Per-parameter metadata, attached with `typing.Annotated`:
165
+
166
+ ```python
167
+ def login(*, user: Annotated[str, Arg(short="-u", env="APP_USER", help="Account name")]): ...
168
+ ```
169
+
170
+ - `help` overrides the docstring entry.
171
+ - `short` adds a short flag (options and flags only).
172
+ - `name` replaces the long option name.
173
+ - `metavar` sets the usage placeholder.
174
+ - `env` names an environment variable used when the option is not given. Booleans accept `1/true/yes/on` and `0/false/no/off`. List options split the variable on commas. A bad value is a usage error (exit 2).
175
+
176
+ ### Errors
177
+
178
+ - `CliError(message, exit_code=1)`: raise it for expected failures. It prints `Error: <message>` to stderr.
179
+ - `abort(message, exit_code=1)`: shorthand that raises `CliError`.
180
+
181
+ ### Output
182
+
183
+ - `style(text, fg=None, bg=None, *, bold=False, dim=False, italic=False, underline=False) -> str`: wrap text in ANSI codes. The colors are `black red green yellow blue magenta cyan white`, each also with a `bright_` prefix. An unknown color raises `ValueError`.
184
+ - `unstyle(text) -> str`: strip ANSI codes.
185
+ - `echo(message="", *, err=False, nl=True, color=None)`: print to stdout or stderr. Styling is stripped unless `should_color(stream)` is true or `color=True` is passed.
186
+ - `secho(message="", *, err=False, nl=True, color=None, **styles)`: `style` and `echo` in one call.
187
+ - `should_color(stream) -> bool`: returns `False` if `NO_COLOR` is set, otherwise `True` if `FORCE_COLOR` is set, otherwise whether `stream` is a TTY.
188
+ - `confirm(prompt, default=False) -> bool`: a yes/no prompt on stdin. An empty answer or end of input returns `default`.
189
+
190
+ ### Lower-level pieces
191
+
192
+ - `Command(func, name=None, *, help=None, aliases=())`: the object behind each registered command. It has `.name`, `.help`, `.description`, `.aliases` and `.params`.
193
+ - `slick_cli.docstrings.parse_docstring(doc) -> DocInfo` returns `summary`, `description` and `params`.
194
+
195
+ ### Testing: `slick_cli.testing`
196
+
197
+ ```python
198
+ from slick_cli.testing import invoke
199
+
200
+ result = invoke(app, ["hello", "Ada"], env={"APP_USER": "me"}, input="y\n")
201
+ assert result.exit_code == 0
202
+ assert result.stdout == "Hello, Ada!\n"
203
+ ```
204
+
205
+ `invoke(target, args=(), *, env=None, input=None) -> Result` accepts an `App` or a plain
206
+ function. `Result` has `exit_code`, `stdout` and `stderr`.
207
+
208
+ ## Development
209
+
210
+ ```console
211
+ PYTHONPATH=src python3 -m unittest discover -s tests -v
212
+ # or, if pytest is installed:
213
+ python3 -m pytest
214
+ ```
215
+
216
+ ## License
217
+
218
+ MIT
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "slick-cli"
7
+ version = "0.1.0"
8
+ description = "Slick, type-hint driven command-line apps built on the Python standard library."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "nehz" }]
14
+ keywords = ["cli", "command-line", "argparse", "decorator", "type-hints", "terminal"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Topic :: Software Development :: User Interfaces",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = []
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.setuptools.package-data]
36
+ slick_cli = ["py.typed"]
37
+
38
+ [tool.pytest.ini_options]
39
+ testpaths = ["tests"]
40
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,40 @@
1
+ """slick_cli: ergonomic, type-hint driven command-line apps on the standard library.
2
+
3
+ Quick example::
4
+
5
+ from slick_cli import App, echo
6
+
7
+ app = App("hello")
8
+
9
+ @app.command
10
+ def greet(name: str, count: int = 1, shout: bool = False) -> None:
11
+ '''Greet someone.'''
12
+ for _ in range(count):
13
+ echo(name.upper() if shout else name)
14
+
15
+ if __name__ == "__main__":
16
+ app()
17
+ """
18
+
19
+ from .app import App, Command, run
20
+ from .errors import CliError, abort
21
+ from .params import Arg
22
+ from .style import confirm, echo, secho, should_color, style, unstyle
23
+
24
+ __version__ = "0.1.0"
25
+
26
+ __all__ = [
27
+ "App",
28
+ "Arg",
29
+ "CliError",
30
+ "Command",
31
+ "abort",
32
+ "confirm",
33
+ "echo",
34
+ "run",
35
+ "secho",
36
+ "should_color",
37
+ "style",
38
+ "unstyle",
39
+ "__version__",
40
+ ]