Skip to main content

Typed Argument Parsing with Pydantic enhanced version

Project description

Argparse Dantic

Typed Argument Parsing with Pydantic Enhanced


Requirements

Requires Python 3.10+, and is compatible with the Pydantic library. Use Rich for better console output.

Installation

Installation with pip is simple:

$ pip install argparse-dantic

Example

from argparse_dantic import ArgumentParser, BaseModel, ArgumentField

class Arguments(BaseModel):
    """Simple Command-Line Arguments."""

    # Required Args
    string: str = ArgumentField("-s", description="a required string")
    integer: int = ArgumentField("-i", description="a required integer")
    flag: bool = ArgumentField("-f", description="a required flag")

    # Optional Args
    second_flag: bool = ArgumentField("-sec", default=False, description="an optional flag")
    third_flag: bool = ArgumentField("-thi", default=True, description="an optional flag")

def main() -> None:
    """Simple Main Function."""
    # Create Parser and Parse Args
    parser = ArgumentParser(
        model_class=Arguments,
        prog="Example Program",
        description="Example Description",
        version="0.0.1",
        epilog="Example Epilog",
    )
    args = ["-h"]
    arguments = parser.parse_typed_args(args)

    # Print Args
    print(arguments)

if __name__ == "__main__":
    main()
$ python3 example.py --help
Example Description

Usage: Example Program [-h] [-v] [--s STRING] [--i INTEGER] [--f] [--sec] [--no-thi]

Optional Arguments:
  -s, --string STRING   a required string (default: None)
  -i, --integer INTEGER
                        a required integer (default: None)
  -f, --flag            a required flag (default: None)
  -sec, --second-flag   an optional flag (default: False)
  -no-thi, --no-third-flag
                        an optional flag (default: True)

Help:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit

Example Epilog
$ python3 example.py --string hello -i 42 -f
string='hello' integer=42 flag=True second_flag=False third_flag=True

Advanced Example

from typing import Annotated, Any, TypedDict, Literal
from argparse_dantic import (
    BaseModel, ArgumentParser, CommandNameBind, 
    ArgumentField, CommandField, ModelField,
    create_group
)

type LogLevels = Literal["debug", "info", "warning", "error", "critical"]

class GlobalData(TypedDict):
    verbose: bool
    quiet: bool
    logging: "LoggingModel"

MGroup =    create_group("Mutually Exclusive Group", "Mutually Exclusive Group Description").\
            create_mutually_exclusive_group(required=False)
class MutuallyGroup(BaseModel, group=MGroup):
    verbose: Annotated[bool, ArgumentField(default=False, description="Verbose Mode")]
    quiet: Annotated[bool, ArgumentField(default=False, description="Quiet Mode")]

class LoggingModel(BaseModel):
    level: Annotated[LogLevels, ArgumentField("l", default="info", description="Logging Level", env="ARGPARSE_DANTIC_TEST_LOG_LEVEL")]
    file: Annotated[str, ArgumentField("f", default="app.log", description="Log File", env="ARGPARSE_DANTIC_TEST_LOG_FILE")]

class GlobalModel(MutuallyGroup):
    global_data: GlobalData
    logging: Annotated[LoggingModel, ModelField(connect_char=".")]

class BaseCommandModel(GlobalModel):
    command_name: CommandNameBind

class BuildCommand(GlobalModel):
    name: Annotated[str, ArgumentField(description="Project Name")]
    version: Annotated[str, ArgumentField(description="Project Version")]

class ListToolsCommand(GlobalModel):
    ...
class ListInstallCommand(GlobalModel):
    ...
class ListCommand(BaseCommandModel):
    tools: Annotated[ListToolsCommand, CommandField(description="List Tools Command")]
    install: Annotated[ListInstallCommand, CommandField(description="List Install Command")]

class MainModel(BaseCommandModel):
    build: Annotated[BuildCommand, CommandField(aliases=["bd"], description="Build Command")]
    list: Annotated[ListCommand, CommandField(aliases=["ls"], description="Check Command")]

class Commands:
    def __init__(self):
        self.commands = {}
    def register(self, name = None, parent = None):
        def decorator(func):
            nonlocal name
            if name is None:
                name = func.__name__
            if parent is not None:
                name = f"{parent}.{name}"
            self.commands[name] = func
            return func
        return decorator
    def __call__(self, name, parent = None) -> Any:
        if parent:
            name = f"{parent}.{name}"
        return self.commands[name]

commands = Commands()

@commands.register()
def build(arguments: BuildCommand):
    print(f"Building {arguments.name} version {arguments.version}")
@commands.register(name="list")
def list_(arguments: ListCommand):
    commands(arguments.command_name, "list")(getattr(arguments, arguments.command_name))
@commands.register(parent="list")
def tools(arguments: ListToolsCommand):
    print("List of tools")
@commands.register(parent="list")
def install(arguments: ListInstallCommand):
    print("List of installed binaries")

if __name__ == "__main__":
    parser = ArgumentParser(
        model_class=MainModel,
        prog="Advanced Example",
        description="Advanced Example Description",
        version="1.0.0"
    )
    args = ["build", "--name", "MyProject", "--logging.level", "error", "--quiet"]
    arguments = parser.parse_typed_args(args)
    parser.console.print("Global Data:")
    parser.console.print("\tverbose:", arguments.global_data["verbose"])
    parser.console.print("\tquiet:", arguments.global_data["quiet"])
    parser.console.print("Logging Config:")
    parser.console.print("\tlevel:", arguments.global_data["logging"].level)
    parser.console.print("\tfile:", arguments.global_data["logging"].file)
    try:
        commands(arguments.command_name)(getattr(arguments, arguments.command_name))
    except KeyError:
        if arguments.command_name is None:
            parser.console.print("No command specified")
        else:
            parser.console.print(f"Command {arguments.command_name} not found")

License

This project is licensed under the terms of the MIT license.

Thanks

Thanks to Pydantic and Rich for making this project possible.

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

argparse_dantic-0.2.4.tar.gz (71.6 kB view details)

Uploaded Source

Built Distribution

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

argparse_dantic-0.2.4-py3-none-any.whl (80.8 kB view details)

Uploaded Python 3

File details

Details for the file argparse_dantic-0.2.4.tar.gz.

File metadata

  • Download URL: argparse_dantic-0.2.4.tar.gz
  • Upload date:
  • Size: 71.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for argparse_dantic-0.2.4.tar.gz
Algorithm Hash digest
SHA256 691d802ab8ea9f873f9ea6e6a34d604e22cfebb7cd68310377d147b1698bbda8
MD5 d54d71a4c1f411905c0e3bc0d624454b
BLAKE2b-256 5b7a38b2d84a422e807dcc9e320b74c8621a4be30ce59194f1973502a25fe548

See more details on using hashes here.

File details

Details for the file argparse_dantic-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: argparse_dantic-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 80.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for argparse_dantic-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 f11ed39895b04e8fa3fa5e0ffc1b28f19711890e240f402e0c7dfc1b4142f3c5
MD5 0e483d5b31f0ccfbf55b92a4fab35076
BLAKE2b-256 3f5293f221aef03d5d56ebf90a2b5051ff5291b0f7e9b6249b740c02d92cc438

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