Skip to content

Simplify command line argument parsing & validation with type hints

License

Notifications You must be signed in to change notification settings

ajatkj/typed_argparser

Repository files navigation

Description of the image

Test Coverage Package version Supported Python versions

typed-argparser

typed-argparser is a python package to create command line programs by leveraging python type hints. It uses Python's type hints to provide a convenient way of parsing and validating command line arguments.

Features

âś“ Type Hinting: Uses type hints to parse and validate command-line arguments.
âś“ Modular Interface: Easily define arguments and commands in a clear, organized way.
âś“ Reusable Arguments: Easily reuse arguments across multiple commands.
âś“ Nested Commands: Supports complex applications with any level of nested commands.
âś“ File Handling: Provides out-of-the-box support for handling files.
âś“ Customizable Help Messages: Fully customizable, giving you control over usage and help messages.
âś“ Built-in Validators: Comes with many built-in validators, such as time range and length validators.
âś“ Custom Types: You can define your own custom types to validate and convert command-line arguments.
âś“ Multiple Commands: Supports multiple commands in a single CLI (experimental).

Installation

You can install typed_argparser using pip:

pip install typed-argparser

Argument Type

All built-in types are supported as argument type annotations. You can also define your own custom types.
Like argparse, you can define your own custom types to validate and convert command-line arguments to specific types.
Read more about custom types here.

Quickstart

type-hint nargs input argument type
T - --opt <value> T
bool - --opt bool
Union[T1, T2, ..., TN] - --opt <value> T1 | T2 | ... | TN
Union[T, bool] - --opt bool
- --opt <value> T
List[T] int, +, * --opt <value>... [T]
- --opt <value1> --opt <value2>... [T]
Tuple[T1, T2, ..., TN] N --opt <value1> <value2> ... <valueN> (T1, T2, ..., TN)
Tuple[T, ...] N > 2 --opt <value1> <value2>... <valueN> (T, ...)
Dict[K, V] - --opt <key1=value1> --opt <key2=value2>... {K: V}
List[Dict[K, V]] - --opt <key1=value1> --opt <key2=value2>... [{K: V, ...}]
N > 2 --opt <key1=value1> <key3=value3> --opt <key2=value2>... [{K: V, ...}, {K: V}, ...]
datetime - --opt <value> datetime
date - --opt <value> date
time - --opt <value> time
Path - --opt <value> Path
Url - --opt <value> Url*

* Url is a special type that allows users to provide url as a string. It is not a built-in type but a custom type.

The possibilities are endless but remember some rules:

  1. Union types are resolved using the first type that matches the value. So be careful to not use str as the first type since everything will match a string.
  2. nargs is only applicable for List and Tuple types.
  3. List without nargs allows users to use the option multiple times.
  4. List with nargs allows users to use multiple values (depending on nargs) for a single option.
  5. Dict allows users to use the option multiple times.
  6. Dict and List[Dict] behaves similar to each other except that the later supports nargs.

Arguments to Argument Types (or Runtime Arguments)

Some argument types allow you to pass arguments to them to customize their behavior.
For example, you can pass mode to Path type to specify the mode in which the file should be opened.
These values are passed using python's Annotated type. A special Args class is used to pass the arguments to the type. For example, to pass mode to Path type, you can use path: Annotated[Path, Args(mode="r")] = argfield(...).

Following tables lists all available arguments to the types.

type arguments description
Path* mode mode in which the file should be opened
Path* buffering buffering for the file
Path* encoding encoding for the file
Path* errors errors for the file
Path* newline newline for the file
Date format format for the date. defaults to "%Y-%m-%d"
DateTime format format for the datetime. defaults to "%Y-%m-%dT%H:%M:%S"
Time format format for the time. defaults to "%H:%M:%S"
Url allowed_schemes list of allowed schemes for the url
Url host_required whether the host is required or not
Url port_required whether the port is required or not

* All arguments to pathlib.Path are supported.

Additionally, args from Annotated metadata are also automatically supplied to the validators as runtime arguments.
For example, the format argument for datetime type is also supplied to the DateTimeRangeValidator validator so that the validator uses the correct format.

Usage

examples/example1.py

# This is a complete example and should work as is

import os
import sys
from typing import Dict, List, Optional, Tuple, Union

from typed_argparser import ArgumentClass, argfield


class Example1(ArgumentClass):
    """This example shows how to use some of the basic types in typed_argparser."""

    # Positional arguments do not generate short or long options
    opt1: Union[int, str] = argfield(help="opt1 is a mandatory argument which can be an integer or a string")
    opt2: List[str] = argfield(help="opt2 is a mandatory argument and can be used multiple times")
    # Optional arguments generate only long option by default if no short option is provided
    opt3: Optional[str] = argfield(help="this is an optional argument.")
    # Use Dict type to accept multiple key value pairs
    opt4: Optional[Dict[str, int]] = argfield(help="accept key value pairs. can be used multiple times.")
    # Use Tuple type to accept exactly n no. of arguments
    opt5: Optional[Tuple[str, ...]] = argfield("-o", "--option5", nargs=4, help="accept multiple options")


cli = Example1()

"""
usage: example1 [--opt3 <value>] [--opt4 <key=value>] [-o <value1> <value2> <value3> <value4>] [--help]
                <opt1> <opt2>

description:
This example shows how to use some of the basic types in typed_argparser.

positional:
<opt1>                                             opt1 is a mandatory argument which can be an integer or a string [(int|str)]
<opt2>                                             opt2 is a mandatory argument and can be used multiple times [str]
                                                   (multiple allowed)

options:
    --opt3 <value>                                 this is an optional argument. [str]
    --opt4 <key=value>                             accept key value pairs. can be used multiple times. [str, int]
                                                   (multiple allowed)
-o, --option5 <value1> <value2> <value3> <value4>  accept multiple options [(str,...)]

miscellaneous:
    --help                                         show this help message and exit
"""

cli.parse("--opt4 abc=10 --opt4 xyz=20 --option5 a b c d 20 abc")

"""
ParsedExample1(opt1=20, opt2=['abc'], opt3=None, opt4={'abc': 10, 'xyz': 20}, opt5=('a', 'b', 'c', 'd'))
"""
print(cli)

Check examples directory for more examples. For more complex examples, check git_cli example.

API Reference

This section provides an overview of the API and classes used in typed_argparser.

ArgumentClass

ArgumentClass is the main class in typed_argparser.
It is used to define the command line interface and defines how the arguments are parsed.
To use ArgumentClass, you need to define a subclass of it and define the arguments using the argfield function.

Class Variables

All class variables are optional and can be used to customize the CLI program.

  • __program__: The name of the CLI program.
  • __description__:The description of the CLI program.
  • __epilog__: The epilog of the CLI program.
  • __usage__: The usage message of the CLI program.
  • __version__: The version of the CLI program.
class CLI(ArgumentClass):
    __program__ = "ls"
    __description__ = "Lists the files in a directory."
    __version__ = "1.0.0"

    long_format: Optional[bool] = argfield("-l", "--long", help="List files in long format.")
    recursive: Optional[bool] = argfield("-r", "--recursive", help="List files recursively.")
    directory: List[str] = argfield("-d", nargs="+", help="Directory to list files in.")

Methods

  • ArgumentClass.parse(args: None)
    Parses the command line arguments and updates the class attributes.

  • ArgumentClass.execute(immediately = True, open_files = True)
    Decorator to execute a function when all positional arguments of the function are provided via CLI arguments.

ArgumentConfig

ArgumentConfig is a class that holds the configuration for the ArgumentClass. Pass this object to the ArgumentClass constructor to customize the CLI program.

Parameters

  • show_default_in_help (bool): Whether to show default values in help messages. Defaults to True.
  • show_none_default (bool): Whether to show default values even if they are None. Defaults to False.
  • show_type_in_help (bool): Whether to show argument types in help messages. Defaults to True.
  • metavar_transform (Callable[[str], str]): Function for transforming metavar strings. This is applied after all sources of metavar have been resolved.
  • heading_transform (Callable[[str], str]): Function for transforming heading strings.
  • add_help (bool): Whether to include help option in the parser. Defaults to True.
  • groups_sort_order (List[str] | None): Order in which argument groups should be sorted in help message.
    ` "*" is a wildcard which signifies all groups. Defaults to ["positional", "optional", "*", "commands", "miscellaneous"].
    Note: names should be an exact match to the headings.
  • compact_usage (bool): Whether to use compact usage message. Defaults to False.
    Note: You can use command_metavar and option_metavar to change the metavar for commands and optional arguments respectively.
  • default_options_group_heading (str): Default heading for options groups. Defaults to "options".
    Note: heading_transform will be applied to this parameter.
  • default_positional_group_heading (str): Default heading for positional groups. Defaults to "positional".
    Note: heading_transform will be applied to this parameter
  • default_commands_group_heading (str): Default heading for commands groups. Defaults to "commands".
    Note: heading_transform will be applied to this parameter
  • default_miscellaneous_group_heading (str): Default heading for miscellaneous groups. Defaults to "miscellaneous".
    Note: heading_transform will be applied to this parameter
  • default_description_heading (str | None): Default heading for descriptions. Defaults to "description". Set this to None to hide the description heading.
    Note: heading_transform will be applied to this parameter
  • default_usage_prefix (str): Default prefix for usage messages. Defaults to "usage: ".
    Note: this value will be used as is without any transformation
  • command_metavar (str): Metavar string for commands. Defaults to "commmand".
    Note: command_metavar is used for both compact_usage and default usage generated by ArgumentClass. Metavar transformation transform_metavar is only applied for default usage. For compact_usage, the value is used as is.
  • option_metavar (str): Metavar string for options. Defaults to "option".
    Note: no transformation is applied to this value.
  • extra_arguments (Literal["allow", "ignore", "error"]): Behavior for handling extra arguments. Defaults to "error".
    When set to allow a new property extra_fields is added to the instance of ArgumentClass.
  • allow_multiple_commands (bool) (experimental): Allow multiple commands. Defaults to False.

argfield

Returns an ArgumentField object, which represents an argument in the command line interface.

Function Arguments

  • default (T | None): The default value for the argument. It must of the same type as the argument (defined by the type hint).
  • nargs (int | Literal["*", "+", "?"] | None): The number of times the argument can be provided.
  • const (T | None): A constant value for the argument. It must of the same type as the argument (defined by the type hint).
  • dest (str | None): Specify the name of the class attribute to which the argument is assigned. Specifically used to assign multiple arguments to a single class attribute using const.
  • metavar (str | None): Alternate display name for the argument in the help message.
  • aliases (Set[str] | None): A list of alternative names for a command (not applicable for arguments).
  • validator (ArgumentValidator | None): An optional validator for the argument. Read more about validators here.
  • counter (bool | None): Whether the argument is a counter. This is used to count the number of times an argument is provided (ex. -vvv).
  • help (str | None): A help message for the argument. Set to SUPPRESS to hide the argument in the help message.

ArgumentGroup

ArgumentGroup is a class that represents a group of arguments in the command line interface. It is used to organize arguments into logical sections.
To define a group, you need to create a subclass of ArgumentGroup and define the arguments using the argfield function.

Class Variables

  • __title__ (str): The title of the group.
  • __group_description__ (str | None): An optional description of the group.
  • __hide_title__ (bool): Whether to hide the title of the group in the help message.
  • __exclusive__ (bool): If set to True, only one argument in the group can be provided at a time.
  • __required__ (bool): If set to True, at least one argument in the mutually exclusive group must be provided.

Validators

Validators provide easy way to validate your arguments as soon as they are parsed. typed_argparser provides certain built-in validators but you can as easily implement your own validators (check Custom Validators).

  1. LengthValidator

    Validates if the argument is of the specified length.
    Parameters:

    • min (int | None): Minimum length of the argument.
    • max (int | None): Maximum length of the argument.
  2. RangeValidator

    Validates if a numeric argument is within the specified range.
    Parameters:

    • min (int | float | None): Minimum value of the argument.
    • max (int | float | None): Maximum value of the argument.
  3. DateTimeRangeValidator

    Validates if the argument is within the specified date, time or datetime range.
    Parameters:

    • min (str | None): Minimum value of datetime argument.
    • max (str | None): Maximum value of datetime argument.
    • format (str | None): Datetime format. Defaults to "%Y-%m-%d" for date, "%H:%M:%S" for time and "%Y-%m-%dT%H:%M:%S" for datetime.
  4. PathValidator

    Validates if the argument is a valid path.
    Parameters:

    • is_absolute (bool | None): Whether the path is absolute or not.
    • is_dir (bool | None): Whether the path is a directory or not.
    • is_file (bool | None): Whether the path is a file or not.
    • exists (bool | None): Whether the path exists or not.
  5. UrlValidator

    Validates if the argument is a valid url.
    Parameters:

    • allowed_schemes (List[str] | None): List of allowed schemes for the url.
    • host_required (bool | None): Whether the host is required or not.
    • port_required (bool | None): Whether the port is required or not.
  6. RegexValidator

    Validates the argument against a regex pattern.
    Parameters:

    • pattern (str | None): Regex pattern to validate the argument.
  7. ConfirmationValidator

    This is a special validator that asks for confirmation before proceeding with the execution of the program.
    If the answer does not match any of the answers provided, the program will exit with an error.
    Parameters:

    • message (str | None): Message to display before asking for confirmation.
    • abort_message (str | None): Message to display when confirmation is aborted.
    • answers (List[str] | None): List of answers to accepted for confirmation.
    • ignore_case (bool | None): Whether to ignore case when comparing answers. Defaults to True.

Commands

Defining commands in typed_argparser is similar to defining arguments.
You can create a subclass of ArgumentClass and define the arguments using the argfield function. Then use the command class as typehint for the command argument.

class InitCommand(ArgumentClass):
    quiet: Optional[bool] = argfield(
        "-q", help="Only print error and warning messages; all other output will be suppressed."
    )
    branch: Optional[Path]= argfield("--initial-branch", "-b", help="Use the specified name for the initial branch in the newly created repository.")

class Git(ArgumentClass):
    init: InitCommand = argfield(help="Initialize a new Git repository.")

Advanced

Custom Types

To be updated soon.

Custom Validators

To define a custom validator, you need to create a subclass of ArgumentValidator and define 2 methods: __init__ and validator.
__init__ accepts the validator arguments as keyword arguments and is used to initialize the validator.
validator accepts the value to be validated as a parameter and is used to validate the value. Any runtime arguments are passed to the validator as keyword arguments.

Simple example of a custom validator that validates if a numnber is within a range:

class MyValidator(ArgumentValidator):
    def __init__(self, min: Optional[int] = None, max: Optional[int] = None) -> None:
        if (min and max and min >= max) or (min is None and max is None):
            raise ValidatorInitError("invalid range provided", validator=self)
        self.min = min
        self.max = max

    def validator(self, value: str) -> None:
        if self.min and self.max and (len(value) < self.min or len(value) > self.max):
            raise ValidationError(f"string length should be between {self.min} and {self.max}", validator=self)
        if self.min and len(value) < self.min:
            raise ValidationError(f"string length should be greater than {self.min}", validator=self)
        if self.max and len(value) > self.max:
            raise ValidationError(f"string length should be less than {self.max}", validator=self)

License

MIT License

Contribution

If you are interested in contributing to typed_argparser, please take a look at the contributing guidelines.

About

Simplify command line argument parsing & validation with type hints

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages