Skip to main content

Automatically generate CLI from Pydantic models

Project description

pydantic-autocli

Automatically generate CLI applications from Pydantic models.

Installation

pip install pydantic-autocli

Features

  • Automatically generate CLI commands from class methods
  • Map Pydantic model fields to CLI arguments
  • Customize CLI arguments with short/long forms and other options
  • Automatically handle help text generation
  • Support for common arguments across all commands
  • Support for async commands
  • Support for array arguments (list[str], list[int], list[float], etc.)

Basic Usage

pydantic-autocli provides multiple ways to define CLI arguments and commands.

from pydantic import BaseModel
from pydantic_autocli import AutoCLI, param

class MyCLI(AutoCLI):
    # Standard Pydantic notation
    class SimpleArgs(BaseModel):
        # Required parameter (no default value)
        required_value: int
        
        # Optional parameter (with default value)
        optional_value: int = 123
        
        # Array parameter
        names: list[str] = []
    
    # This method will automatically use SimpleArgs
    # Args class selection rule: run_simple -> SimpleArgs (by naming convention)
    def run_simple(self, args):
        """Execute simple command"""
        print(f"Required: {args.required_value}")
        print(f"Optional: {args.optional_value}")
        print(f"Names: {args.names}")
        return True  # Indicates success (exit code 0)
    
    # Notation using the param function
    class CommonArgs(AutoCLI.CommonArgs):
        # Common arguments for all commands
        verbose: bool = param(False, l="--verbose", s="-v", description="Enable detailed output")
    
    class AdvancedArgs(CommonArgs):
        # Specify short and long forms
        name: str = param(..., l="--name", s="-n")
        
        # Restrict choices
        mode: str = param("read", l="--mode", choices=["read", "write", "append"])
        
        # Array parameters (with type specification)
        input_paths: list[str] = param(..., l="--in", s="-i")
        numbers: list[int] = param([1, 2, 3], l="--nums")

    # This method will automatically use AdvancedArgs 
    # Args class selection rule: run_advanced -> AdvancedArgs (by naming convention)
    def run_advanced(self, args):
        """Execute advanced command"""
        print(f"Name: {args.name}, Mode: {args.mode}")
        print(f"Input paths: {args.input_paths}")
        print(f"Numbers: {args.numbers}")
        if args.verbose:
            print("Verbose mode enabled")
        return True  # Indicates success (exit code 0)
    
    # Example with multi-word command and parameters
    class ShowFileInfoArgs(CommonArgs):
        # Parameter names with underscores become kebab-case in CLI
        # file_path becomes --file-path in command line
        file_path: str = param(..., l="--file-path", s="-f")
        
        # show_lines becomes --show-lines in command line
        show_lines: bool = param(False, l="--show-lines")
        
        # line_count becomes --line-count in command line 
        line_count: int = param(10, l="--line-count")
    
    # Multi-word command: run_show_file_info becomes "show-file-info" command
    # Args selection rule: run_show_file_info -> ShowFileInfoArgs
    def run_show_file_info(self, args):
        """Show information about a file"""
        print(f"File: {args.file_path}")
        if args.show_lines:
            print(f"Showing {args.line_count} lines")
        return True  # Indicates success (exit code 0)
    
    # Example that returns failure
    class ErrorArgs(CommonArgs):
        code: int = param(1, l="--code", s="-c")
    
    # This method will automatically use ErrorArgs
    # Args class selection rule: run_error -> ErrorArgs (by naming convention)
    def run_error(self, args):
        """Example command that returns an error"""
        print(f"Simulating error with code {args.code}")
        return False  # Indicates failure (exit code 1)
        # Or return a specific exit code: return args.code

if __name__ == "__main__":
    cli = MyCLI()
    cli.run()

Command-line execution examples

# Execute simple command
$ python mycli.py simple --required-value 42 --names Alice Bob Charlie
Required: 42
Optional: 123
Names: ['Alice', 'Bob', 'Charlie']

# Execute advanced command
$ python mycli.py advanced --name test --mode write --in file1.txt file2.txt --nums 5 10 15 -v
Name: test, Mode: write
Input paths: ['file1.txt', 'file2.txt']
Numbers: [5, 10, 15]
Verbose mode enabled

# Execute multi-word command (note the kebab-case)
# run_show_file_info method becomes show-file-info command
# Parameter names also use kebab-case (--file-path, --show-lines, --line-count)
$ python mycli.py show-file-info --file-path example.txt --show-lines --line-count 5
File: example.txt
Showing 5 lines

# Execute error command
$ python mycli.py error --code 42
Simulating error with code 42
# Exits with code 42

Argument Resolution

Using Naming Convention

You can specify argument classes for CLI commands using naming conventions:

class MyCLI(AutoCLI):
    # Naming convention:
    # run_command → CommandArgs
    # run_foo_bar → FooBarArgs
    
    # Single-word command example
    class CommandArgs(AutoCLI.CommonArgs):
        name: str = param("default", l="--name", s="-n")
    
    def run_command(self, args):
        print(f"Name: {args.name}")
        return True  # Indicates success (exit code 0)
        
    # Two-word command example
    class FooBarArgs(AutoCLI.CommonArgs):
        option: str = param("default", l="--option")
    
    def run_foo_bar(self, args):
        print(f"Option: {args.option}")
        return True  # Indicates success (exit code 0)

Command-line execution examples:

$ python mycli.py command --name test
Name: test

$ python mycli.py foo-bar --option custom
Option: custom

Using Type Annotations

You can directly specify the argument class using type annotations:

from pydantic import BaseModel
from pydantic_autocli import AutoCLI, param

class MyCLI(AutoCLI):
    class CustomArgs(BaseModel):
        value: int = param(42, l="--value", s="-v")
        flag: bool = param(False, l="--flag", s="-f")
    
    # Use type annotation to specify args class
    def run_command(self, args: CustomArgs):
        print(f"Value: {args.value}")
        if args.flag:
            print("Flag is set")
        return True

Resolution Priority

pydantic-autocli uses the following priority order to determine which argument class to use:

  1. Type annotation on the method parameter
  2. Naming convention (CommandArgs class for run_command method)
  3. Fall back to CommonArgs

When both naming convention and type annotation could apply to a method, the type annotation takes precedence (as per the priority above). In such cases, a warning is displayed about the conflict:

class MyCLI(AutoCLI):
    # Args class that follows naming convention
    class CommandArgs(BaseModel):
        name: str = param("default", l="--name")
    
    # Different args class specified by type annotation
    class CustomArgs(BaseModel):
        value: int = param(42, l="--value")
    
    # Type annotation takes precedence over naming convention
    # A warning will be displayed about the conflict
    def run_command(self, args: CustomArgs):
        # Uses CustomArgs even though CommandArgs exists
        print(f"Value: {args.value}")
        return True

This command will use CustomArgs (from type annotation) instead of CommandArgs (from naming convention), with a warning about the detected conflict. It's generally recommended to avoid such conflicts for code clarity.

Common Arguments Base Class

AutoCLI.CommonArgs is a class that inherits from Pydantic's BaseModel. This means you can use it interchangeably with BaseModel while getting the benefits of common arguments across commands.

Development and Testing

# Install development dependencies
uv sync --dev

# Run tests
uv run pytest

# Or using taskipy
uv run task test

Examples

To run the example CLI:

python examples/example.py greet --verbose

# Or using taskipy
uv run task example file --file README.md

License

See LICENSE file.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydantic_autocli-0.1.4.tar.gz (55.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydantic_autocli-0.1.4-py3-none-any.whl (11.3 kB view details)

Uploaded Python 3

File details

Details for the file pydantic_autocli-0.1.4.tar.gz.

File metadata

  • Download URL: pydantic_autocli-0.1.4.tar.gz
  • Upload date:
  • Size: 55.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for pydantic_autocli-0.1.4.tar.gz
Algorithm Hash digest
SHA256 694de3f1dfbec78280c3fa48b3ea20783a40a553a49e0733e656d36b9dddbe94
MD5 46fc013ccff7dc3d083fdbe3350aee7b
BLAKE2b-256 36cdc18e4ae554e6d8c2848a60169ff806a318c509e1362b3aad1003c0b84399

See more details on using hashes here.

File details

Details for the file pydantic_autocli-0.1.4-py3-none-any.whl.

File metadata

File hashes

Hashes for pydantic_autocli-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 829364a6ee16cd14b3fc6e8ee058abc66448c572bfa658d4a07a3c8f29648a51
MD5 8df1c74b97188a7974627a403663705e
BLAKE2b-256 e9af6d8b5b809174926fec41c0b56a3783a7acd5709323f179d47ecef39006c6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page