https://github.com/zigai/interfacy
Framework for building command-line interfaces from Python functions, classes, and class instances
https://github.com/zigai/interfacy
argparse argparse-alternative argument-parser cli cli-framework interfacy python python3 type-hints
Last synced: about 1 month ago
JSON representation
Framework for building command-line interfaces from Python functions, classes, and class instances
- Host: GitHub
- URL: https://github.com/zigai/interfacy
- Owner: zigai
- License: mit
- Created: 2022-03-17T17:39:32.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2026-04-16T10:02:42.000Z (about 2 months ago)
- Last Synced: 2026-04-16T12:13:05.503Z (about 2 months ago)
- Topics: argparse, argparse-alternative, argument-parser, cli, cli-framework, interfacy, python, python3, type-hints
- Language: Python
- Homepage:
- Size: 1.13 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Interfacy
[](https://github.com/zigai/interfacy/actions/workflows/tests.yml)
[](https://badge.fury.io/py/interfacy)

[](https://pepy.tech/project/interfacy)
[](https://github.com/zigai/interfacy/blob/main/LICENSE)
Interfacy is a CLI framework that turns Python functions, classes, and class instances into command-line interfaces. It derives the CLI from signatures, type annotations, and docstrings instead of making you define it twice.
## Features
- Generate CLIs from functions, classes, class methods, and class instances.
- Nested subcommands and manual command groups with aliases.
- Type-driven parsing from annotations, with support for custom parsers.
- Model expansion for dataclasses, Pydantic models, and plain classes.
- `--help` text generated from docstrings.
- Highly customizable help output with multiple layouts, color themes, and configurable ordering.
- Stdin piping support with configurable routing to parameters.
- Optional tab completion via `argcomplete`.
## Installation
### From PyPI
```bash
pip install interfacy
```
```bash
uv add interfacy
```
### From source
```bash
pip install git+https://github.com/zigai/interfacy.git
```
```bash
uv add "git+https://github.com/zigai/interfacy.git"
```
## Quick Start
```python
from interfacy import Interfacy
def greet(name: str, times: int = 1) -> None:
"""Print a greeting."""
print(" ".join([f"Hello, {name}!" for _ in range(times)]))
if __name__ == "__main__":
Interfacy().run(greet)
```
```text
$ python app.py Ada
Hello, Ada!
$ python app.py Ada --times 2
Hello, Ada! Hello, Ada!
```
By default, required non-boolean parameters become positional arguments and optional parameters become flags.
## Class-Based Commands
Classes become command namespaces. `__init__` parameters live at the command level and public methods become subcommands.
```python
from interfacy import Interfacy
class Calculator:
def __init__(self, precision: int = 2) -> None:
self.precision = precision
def add(self, a: float, b: float) -> float:
return round(a + b, self.precision)
def mul(self, a: float, b: float) -> float:
return round(a * b, self.precision)
if __name__ == "__main__":
Interfacy(print_result=True).run(Calculator)
```
```text
$ python app.py --precision 3 add 1.2345 2.3445
3.579
```
## Structured Parameters
Dataclasses, Pydantic models, and plain classes with typed `__init__` parameters can be expanded into nested flags and reconstructed before execution.
```python
from dataclasses import dataclass
from interfacy import Interfacy
@dataclass
class Address:
city: str
postal_code: int
@dataclass
class User:
name: str
age: int
address: Address | None = None
def greet(user: User) -> str:
return f"Hello {user.name}, age {user.age}"
if __name__ == "__main__":
Interfacy(print_result=True).run(greet)
```
```text
$ python app.py --user.name Ada --user.age 32
Hello Ada, age 32
```
## Manual Groups
Use `CommandGroup` when your command tree is not naturally rooted in one callable:
```python
from interfacy import CommandGroup, Interfacy
def clone(url: str) -> str:
return f"clone:{url}"
class Releases:
def cut(self, version: str) -> str:
return f"cut:{version}"
ops = CommandGroup("ops", description="Operational commands")
ops.add_command(clone)
ops.add_command(Releases)
if __name__ == "__main__":
Interfacy(print_result=True).run(ops)
```
## Interfacy CLI Entrypoint
Interfacy also ships a CLI that can run an existing function, class, or class instance directly from a module or Python file:
```text
$ interfacy app.py:greet Ada
$ interfacy app.py:greet --help
$ interfacy package.cli:Calculator add 1 2
```
The entrypoint supports configuration via TOML, loaded from `~/.config/interfacy/config.toml` or `INTERFACY_CONFIG`.
```text
usage: interfacy [--help] [--version] [--config-paths] [TARGET] ...
Interfacy is a framework for building CLIs from Python callables.
positional arguments:
TARGET Python file or module with a function/class/instance symbol (e.g. main.py:main, pkg.cli:App, pkg.cli:service).
ARGS Arguments passed through to the target command.
options:
--help show this help message and exit
--version show version and exit.
--config-paths print config file search paths and exit.
Use 'interfacy TARGET --help' to display the help text for the target.
```
## Backend Selection
`Interfacy` uses the argparse backend by default. To use the Click backend,
install the optional dependency and pass `backend="click"`:
```bash
pip install "interfacy[click]"
```
```python
from interfacy import Interfacy
Interfacy(backend="click", print_result=True).run(greet)
```
## License
[MIT License](https://github.com/zigai/interfacy/blob/main/LICENSE)