Convert Python functions to CLI commands automatically
Project description
Runpy - Convert Python Functions to CLI Commands
Runpy automatically converts your Python functions into command-line interfaces (CLIs) with minimal code changes. Simply decorate your functions, and Runpy generates a fully-featured CLI with argument parsing, help text, and type validation.
Features
- 🚀 Zero Configuration: Convert functions to CLI commands with a single decorator
- 📝 Automatic Help Generation: Docstrings become help text automatically
- 🔍 Type Validation: Automatic type checking based on type hints
- 📊 Rich Output: Support for structured output (JSON, tables, etc.)
- 🏗️ Command Groups: Organize commands in hierarchical groups
- 🔧 Flexible Configuration: YAML/JSON config file support
- 📦 Pydantic Integration: Full support for Pydantic models as parameters
- 🌐 Multiple Input Formats: Accept JSON, Python dict, and TypeScript object notation
- 🎨 Customizable: Extensive customization options for advanced use cases
Installation
pip install runpycli
Quick Start
Create a file mycli.py:
from runpycli import Runpy
# Create a Runpy instance
cli = Runpy()
# Register functions as commands
@cli.register
def hello(name: str = "World") -> str:
"""Say hello to someone"""
return f"Hello, {name}!"
@cli.register
def add(x: int, y: int) -> int:
"""Add two numbers"""
return x + y
if __name__ == "__main__":
cli.app()
Run your CLI:
python mycli.py hello --name Alice
# Output: Hello, Alice!
python mycli.py add --x 5 --y 3
# Output: 8
Advanced Features
Pydantic Models as Parameters
from pydantic import BaseModel, Field
from typing import List
class UserInput(BaseModel):
"""User information"""
name: str = Field(..., description="User's full name")
age: int = Field(..., ge=0, le=150, description="User's age")
emails: List[str] = Field(default_factory=list, description="Email addresses")
@cli.register
def create_user(user: UserInput) -> dict:
"""Create a new user from the provided data"""
return {"status": "created", "user": user.model_dump()}
Usage with multiple input formats:
# JSON format (standard)
python mycli.py create-user --user '{"name": "John Doe", "age": 30, "emails": ["john@example.com"]}'
# Python dict format
python mycli.py create-user --user "{'name': 'John Doe', 'age': 30, 'emails': ['john@example.com']}"
# TypeScript/JavaScript object format
python mycli.py create-user --user '{name: "John Doe", age: 30, emails: ["john@example.com"]}'
Command Groups
# Create command groups
cli = Runpy(name="myapp/db")
@cli.register
def migrate():
"""Run database migrations"""
pass
@cli.register
def seed():
"""Seed the database"""
pass
Configuration Files
Create a config.json:
{
"defaults": {
"environment": "development",
"debug": true
},
"shortcuts": {
"env": "e",
"debug": "d"
}
}
Use in your CLI:
cli = Runpy(config_file="config.json")
@cli.register
def deploy(environment: str, debug: bool = False):
"""Deploy the application"""
print(f"Deploying to {environment} (debug: {debug})")
Register Multiple Functions
import math
# Register all public functions from a module
cli.register_module(math)
# Or selectively register functions
cli.register(math.sin)
cli.register(math.cos)
Documentation Commands
Runpy automatically adds two special commands:
docs Command
View detailed documentation for any command:
python mycli.py docs create-user
schema Command
Generate OpenAPI-style schema documentation:
python mycli.py schema
python mycli.py schema --json # JSON output
python mycli.py schema --save api-docs.json # Save to file
Type Support
Runpy supports a wide range of Python types:
- Basic types:
str,int,float,bool - Container types:
List[T],Dict[K, V],Set[T],Tuple[T, ...] - Optional types:
Optional[T],Union[T1, T2] - Pydantic models: Any class inheriting from
pydantic.BaseModel - Enums:
Enumsubclasses - File types:
Path,FilePath,DirectoryPath
Boolean Parameters
Boolean parameters are handled as regular options, not flags:
# Correct usage
python mycli.py command --bool-param true
python mycli.py command --bool-param false
# NOT as flags (this is not supported)
python mycli.py command --bool-param # ❌
Optional Parameters
Parameters with default values (including None) are automatically optional:
def process(
required_param: str, # Required: must provide --required-param
optional_str: Optional[str] = None, # Optional: can omit
optional_int: Optional[int] = None, # Optional: can omit
optional_with_default: str = "default" # Optional: uses default if omitted
):
pass
Best Practices
- Use Type Hints: Always add type hints to get automatic type validation
- Write Docstrings: Function and parameter docstrings become help text
- Set Defaults: Default values make parameters optional
- Return Values: Return values are automatically displayed
- Use Pydantic: For complex inputs, Pydantic models provide validation
Examples
Simple Calculator
from runpycli import Runpy
import math
cli = Runpy(name="calc", version="1.0.0")
cli.register(math.sin)
cli.register(math.cos)
cli.register(math.sqrt)
@cli.register
def divide(x: float, y: float) -> float:
"""Divide x by y"""
if y == 0:
raise ValueError("Cannot divide by zero")
return x / y
if __name__ == "__main__":
cli.app()
Task Manager
from runpycli import Runpy
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime
cli = Runpy(name="tasks")
class Task(BaseModel):
title: str
description: Optional[str] = None
due_date: Optional[datetime] = None
tags: List[str] = []
tasks_db = []
@cli.register
def add(task: Task) -> dict:
"""Add a new task"""
task_dict = task.model_dump()
task_dict["id"] = len(tasks_db) + 1
tasks_db.append(task_dict)
return task_dict
@cli.register
def list_tasks(tag: Optional[str] = None) -> List[dict]:
"""List all tasks, optionally filtered by tag"""
if tag:
return [t for t in tasks_db if tag in t.get("tags", [])]
return tasks_db
if __name__ == "__main__":
cli.app()
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file runpycli-1.2.0.tar.gz.
File metadata
- Download URL: runpycli-1.2.0.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4706ea9a0c07eaf95553891d91577f09702ffaa8d6c0787a56b456553bd9177
|
|
| MD5 |
c8f7a098f7739bd2985392487daac8da
|
|
| BLAKE2b-256 |
0ab1457dc5661b2803f349b22f723259c07fb0ce0e9f7c999e0caf5e2e522542
|
File details
Details for the file runpycli-1.2.0-py3-none-any.whl.
File metadata
- Download URL: runpycli-1.2.0-py3-none-any.whl
- Upload date:
- Size: 25.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
857f59a2480b7b201f185572e7da076355390f7ca5559df68874037953af333b
|
|
| MD5 |
0cdcd6eac60ea798d3af431075131664
|
|
| BLAKE2b-256 |
881da7a4579f0aa10f36a8c1afb130af7bd925c4d119a5466993633167b3d5c1
|