https://github.com/oir/startle
Instantly start a CLI from a function, functions, or a class
https://github.com/oir/startle
argparse argument-parser cli command-line python shell typehints
Last synced: 16 days ago
JSON representation
Instantly start a CLI from a function, functions, or a class
- Host: GitHub
- URL: https://github.com/oir/startle
- Owner: oir
- License: apache-2.0
- Created: 2024-08-31T18:07:41.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-11-06T00:44:24.000Z (2 months ago)
- Last Synced: 2025-11-27T23:58:11.542Z (about 1 month ago)
- Topics: argparse, argument-parser, cli, command-line, python, shell, typehints
- Language: Python
- Homepage: https://oir.github.io/startle/
- Size: 436 KB
- Stars: 11
- Watchers: 1
- Forks: 0
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

_Give your code a start._ ⚡👀

[](https://coveralls.io/github/oir/startle)
[](https://oir.github.io/startle/)
[](https://pypi.org/project/startle/)
[](https://pypi.org/project/startle/)
[](https://github.com/astral-sh/ruff)
[](https://github.com/microsoft/pyright)
> [!WARNING]
> **Startle** is _alpha_ and should be considered unstable as its interface is fluid 😅, consider pinning to a version.
**Startle** lets you transform a python function (or functions) into a command line entry point, e.g:
`wc.py`:
```python
from pathlib import Path
from typing import Literal
from startle import start
def word_count(
fname: Path, /, kind: Literal["word", "char"] = "word", *, verbose: bool = False
) -> None:
"""
Count the number of words or characters in a file.
Args:
fname: The file to count.
kind: Whether to count words or characters.
verbose: Whether to print additional info.
"""
text = open(fname).read()
count = len(text.split()) if kind == "word" else len(text)
print(f"{count} {kind}s in {fname}" if verbose else count)
start(word_count)
```

When you invoke `start()`, it will construct an argparser (based on type hints and docstring),
parse the arguments, and invoke `word_count`.

You can also invoke `start()` with a list of functions instead of a single function.
In this case, functions are made available as _commands_ with their own arguments
and options in your CLI. See [here](https://oir.github.io/startle/#/function-interface?id=commands).
---
**Startle** also allows you to transform a class (possibly a dataclass) into a command line parser:
```python
import random
from dataclasses import dataclass
from typing import Literal
from startle import parse
@dataclass
class Config:
"""
Configuration for the dice program.
Attributes:
sides: The number of sides on the dice.
count: The number of dice to throw.
kind: Whether to throw a single die or a pair of dice.
"""
sides: int = 6
count: int = 1
kind: Literal["single", "pair"] = "single"
def throw_dice(cfg: Config) -> None:
"""
Throw the dice according to the configuration.
"""
if cfg.kind == "single":
for _ in range(cfg.count):
print(random.randint(1, cfg.sides))
else:
for _ in range(cfg.count):
print(random.randint(1, cfg.sides), random.randint(1, cfg.sides))
if __name__ == "__main__":
cfg = parse(Config, brief="A program to throw dice.")
throw_dice(cfg)
```
Then `dice.py` can be executed like:


---
**Startle** is inspired by [Typer](https://github.com/fastapi/typer), [Fire](https://github.com/google/python-fire),
and [HFArgumentParser](https://github.com/huggingface/transformers/blob/main/src/transformers/hf_argparser.py),
but aims to be _non-intrusive_, to have stronger type support, and to have saner defaults.
Thus, some decisions are done differently:
- Use of positional-only or keyword-only argument separators (`/`, `*`, see PEP 570, 3102) are naturally translated into positional arguments or options.
See above example ([wc.py](https://github.com/oir/startle/blob/main/examples/wc.py)).
- Like Typer and unlike Fire, type hints strictly determine how the individual arguments are parsed and typed.
- Short forms (e.g. `-k`, `-v` above) are automatically provided based on the initial of the argument.
- Variable length arguments are more intuitively handled.
You can use `--things a b c` (in addition to `--things=a --things=b --things=c`).
See [example](https://github.com/oir/startle/blob/main/examples/cat.py).
- Like Typer and unlike Fire, help is simply printed and not displayed in pager mode by default, so you can keep referring to it as you type your command.
- Like Fire and unlike Typer, docstrings determine the description of each argument in the help text, instead of having to individually add extra type annotations. This allows for a very non-intrusive design, you can adopt (or un-adopt) **Startle** with no changes to your functions.
- `*args` but also `**kwargs` are supported, to parse unknown arguments as well as unknown options (`--unk-key unk-val`).
See [example](https://github.com/oir/startle/blob/main/examples/search_gh.py).
See all [examples](https://github.com/oir/startle/tree/main/examples),
or the [documentation](https://oir.github.io/startle/) for more.