Skip to main content

Declarative `ArgumentParser` definition with `dataclass` notation.

Project description

classparse

Coverage Status

Declarative ArgumentParser definition with dataclass notation.

  • No ArgumentParser boilerplate code
  • IDE autocompletion and type hints

Install

pip install classparse==0.1.4

Simple Example

This is a simple example of the most basic usage of this library.

# examples/simple.py
from dataclasses import dataclass

from classparse import classparser

@classparser
@dataclass
class SimpleArgs:
    """My simple program's arguments"""

    retries: int = 5  # number of retries
    eps: float = 1e-3  # epsilon

if __name__ == "__main__":
    print(SimpleArgs.parse_args())
$ python examples/simple.py --help
usage: simple.py [-h] [--retries RETRIES] [--eps EPS]

My simple program's arguments

options:
  -h, --help         show this help message and exit
  --retries RETRIES  number of retries
  --eps EPS          epsilon
$ python examples/simple.py --retries 10 --eps 1e-6
SimpleArgs(retries=10, eps=1e-06)

Exhaustive Usage Example

This example demonstrates all the usage scenarios of this library.

# examples/usage.py
import dataclasses
from dataclasses import dataclass
from enum import Enum, auto
from pathlib import Path
from typing import List, Literal, Optional, Tuple, Union

from classparse import arg, classparser, no_arg, pos_arg, to_arg_name, to_var_name

class Action(Enum):
    Initialize = "init"
    Execute = "exec"

class Animal(Enum):
    Cat = auto()
    Dog = auto()

@classparser(
    prog="my_program.py",  # Keyword arguments are passed to the parser init.
    default_argument_args=dict(help="(type: %(type)s)"),  # Set default arguments for each call of add_argument().
)
@dataclass(frozen=True)
class AllOptions:
    """
    Class doc string ==> parser description.
    The fields' inline/above comment ==> argument's help.
    """

    pos_arg_1: str  # Field with no explicit default ==> positional arguments (default=%(default)s)
    pos_arg_2: int = pos_arg(
        5,
        nargs="?",
        help=(
            "pos_arg() is a wrapper around dataclasses.field()."
            "The first argument (optional) is the argument default (default=%(default)s)."
            "The following keyword arguments can be any argparse.add_argument() parameter."
        ),
    )  # When the help field is specified explicitly, the inline comment is ignored
    int_arg: int = 1  # Field's type and default are applied to the parser (type=%(type)s, default=%(default)s)
    str_enum_choice_arg: Action = Action.Initialize  # StrEnum ==> choice argument (type=%(type)s, default=%(default)s)
    int_enum_choice_arg: Animal = Animal.Cat  # IntEnum ==> choice argument (type=%(type)s, default=%(default)s)
    literal_arg: Literal["a", "b", "c"] = None  # Literal ==> choice argument (type=%(type)s, default=%(default)s)
    literal_int_arg: Literal[1, 2, 3] = None  # Literal's type is automatically inferred (type=%(type)s)
    mixed_literal: Literal[1, 2, "3", "4", True, Animal.Cat] = None  # We can mix multiple literal types (type=%(type)s)
    optional_arg: Optional[int] = None  # Optional can be used for type hinting (type=%(type)s)
    just_optional_arg: Optional = None  # Bare optional also works (type=%(type)s)
    optional_choice_arg: Optional[Action] = None  # Nested types are supported (type=%(type)s)
    union_arg: Union[int, float, bool] = None  # Tries to convert to type in order until first success (type=%(type)s)
    path_arg: Path = None
    flag_arg: int = arg(
        "-f",
        help=(
            "arg() is a wrapper around dataclasses.field()."
            "The first argument (optional) is the short argument name."
            "The following keyword arguments can be any argparse.add_argument() parameter."
        ),
        default=1,
    )
    required_arg: float = arg("-r", required=True)  # E.g., required=%(required)s
    metavar_arg: str = arg(metavar="M")  # E.g., metavar=%(metavar)s
    int_list: List[int] = (1,)  # List type hint ==> nargs="+" (type=%(type)s)
    int_2_list: Tuple[int, int] = (1, 2)  # Tuple type hint ==> nargs=<tuple length> (nargs=%(nargs)s, type=%(type)s)
    multi_type_tuple: Tuple[int, float, str] = (1, 1e-3, "a")  # We can use multiple types (type=%(type)s)
    actions: List[Action] = ()  # List[Enum] ==> choices with nargs="+" (nargs=%(nargs)s, type=%(type)s)
    animals: List[Animal] = ()  # List[Enum] ==> choices with nargs="+" (nargs=%(nargs)s, type=%(type)s)
    literal_list: List[Literal["aa", "bb", 11, 22, Animal.Cat]] = ("aa",)  # List[Literal] ==> choices with nargs="+"
    union_list: List[Union[int, float, str, bool]] = ()
    union_with_literal: List[Union[Literal["a", "b", 1, 2], float, bool]] = ()
    typeless_list: list = ()  # If list type is unspecified, then it uses argparse default (type=%(type)s)
    typeless_typing_list: List = ()  # typing.List or list are supported
    none_bool_arg: bool = None  # boolean args ==> argparse.BooleanOptionalAction (type=%(type)s)
    true_bool_arg: bool = True  # We can set any default value
    false_bool_arg: bool = False
    complex_arg: complex = complex(1, -1)

    # no_arg() is used to not include this argument in the parser.
    # The first argument (optional) sets the default value.
    # The following keyword arguments is forwarded to the dataclasses.field() method.
    no_arg: int = no_arg(0)

    # We used this argument for the README example.
    # Note that comments above the arg are also included in the help of the argument.
    # This is a convenient way to include long help messages.
    show: List[str] = arg("-s", default=())

    def __repr__(self):
        """Print only the specified fields"""
        fields = self.show or list(dataclasses.asdict(self))
        return "\n".join([f"{to_arg_name(k)}: {getattr(self, to_var_name(k))}" for k in fields])

if __name__ == "__main__":
    print(AllOptions.parse_args())
$ python examples/usage.py --help
usage: my_program.py [-h] [--int-arg INT_ARG]
                     [--str-enum-choice-arg {Initialize/init,Execute/exec}]
                     [--int-enum-choice-arg {Cat/1,Dog/2}]
                     [--literal-arg {a,b,c}] [--literal-int-arg {1,2,3}]
                     [--mixed-literal {1,2,3,4,True,Cat/1}]
                     [--optional-arg OPTIONAL_ARG]
                     [--just-optional-arg JUST_OPTIONAL_ARG]
                     [--optional-choice-arg {Initialize/init,Execute/exec}]
                     [--union-arg UNION_ARG] [--path-arg PATH_ARG]
                     [-f FLAG_ARG] -r REQUIRED_ARG [--metavar-arg M]
                     [--int-list INT_LIST [INT_LIST ...]]
                     [--int-2-list INT_2_LIST INT_2_LIST]
                     [--multi-type-tuple MULTI_TYPE_TUPLE MULTI_TYPE_TUPLE MULTI_TYPE_TUPLE]
                     [--actions {Initialize/init,Execute/exec} [{Initialize/init,Execute/exec} ...]]
                     [--animals {Cat/1,Dog/2} [{Cat/1,Dog/2} ...]]
                     [--literal-list {aa,bb,11,22,Cat/1} [{aa,bb,11,22,Cat/1} ...]]
                     [--union-list UNION_LIST [UNION_LIST ...]]
                     [--union-with-literal UNION_WITH_LITERAL [UNION_WITH_LITERAL ...]]
                     [--typeless-list TYPELESS_LIST [TYPELESS_LIST ...]]
                     [--typeless-typing-list TYPELESS_TYPING_LIST [TYPELESS_TYPING_LIST ...]]
                     [--none-bool-arg | --no-none-bool-arg]
                     [--true-bool-arg | --no-true-bool-arg]
                     [--false-bool-arg | --no-false-bool-arg]
                     [--complex-arg COMPLEX_ARG] [-s SHOW [SHOW ...]]
                     pos-arg-1 [pos-arg-2]

Class doc string ==> parser description. The fields' inline/above comment ==>
argument's help.

positional arguments:
  pos-arg-1             Field with no explicit default ==> positional
                        arguments (default=None)
  pos-arg-2             pos_arg() is a wrapper around dataclasses.field().The
                        first argument (optional) is the argument default
                        (default=5).The following keyword arguments can be any
                        argparse.add_argument() parameter.

options:
  -h, --help            show this help message and exit
  --int-arg INT_ARG     Field's type and default are applied to the parser
                        (type=int, default=1)
  --str-enum-choice-arg {Initialize/init,Execute/exec}
                        StrEnum ==> choice argument (type=Action,
                        default=Initialize)
  --int-enum-choice-arg {Cat/1,Dog/2}
                        IntEnum ==> choice argument (type=Animal, default=Cat)
  --literal-arg {a,b,c}
                        Literal ==> choice argument (type=str, default=None)
  --literal-int-arg {1,2,3}
                        Literal's type is automatically inferred (type=int)
  --mixed-literal {1,2,3,4,True,Cat/1}
                        We can mix multiple literal types
                        (type=typing.Literal[1, 2, '3', '4', True,
                        <Animal.Cat: 1>])
  --optional-arg OPTIONAL_ARG
                        Optional can be used for type hinting (type=int)
  --just-optional-arg JUST_OPTIONAL_ARG
                        Bare optional also works (type=None)
  --optional-choice-arg {Initialize/init,Execute/exec}
                        Nested types are supported (type=Action)
  --union-arg UNION_ARG
                        Tries to convert to type in order until first success
                        (type=typing.Union[int, float, bool])
  --path-arg PATH_ARG   (type: Path)
  -f FLAG_ARG, --flag-arg FLAG_ARG
                        arg() is a wrapper around dataclasses.field().The
                        first argument (optional) is the short argument
                        name.The following keyword arguments can be any
                        argparse.add_argument() parameter.
  -r REQUIRED_ARG, --required-arg REQUIRED_ARG
                        E.g., required=True
  --metavar-arg M       E.g., metavar=M
  --int-list INT_LIST [INT_LIST ...]
                        List type hint ==> nargs="+" (type=int)
  --int-2-list INT_2_LIST INT_2_LIST
                        Tuple type hint ==> nargs=<tuple length> (nargs=2,
                        type=int)
  --multi-type-tuple MULTI_TYPE_TUPLE MULTI_TYPE_TUPLE MULTI_TYPE_TUPLE
                        We can use multiple types (type=typing.Union[int,
                        float, str])
  --actions {Initialize/init,Execute/exec} [{Initialize/init,Execute/exec} ...]
                        List[Enum] ==> choices with nargs="+" (nargs=+,
                        type=Action)
  --animals {Cat/1,Dog/2} [{Cat/1,Dog/2} ...]
                        List[Enum] ==> choices with nargs="+" (nargs=+,
                        type=Animal)
  --literal-list {aa,bb,11,22,Cat/1} [{aa,bb,11,22,Cat/1} ...]
                        List[Literal] ==> choices with nargs="+"
  --union-list UNION_LIST [UNION_LIST ...]
                        (type: typing.Union[int, float, str, bool])
  --union-with-literal UNION_WITH_LITERAL [UNION_WITH_LITERAL ...]
                        (type: typing.Union[typing.Literal['a', 'b', 1, 2],
                        float, bool])
  --typeless-list TYPELESS_LIST [TYPELESS_LIST ...]
                        If list type is unspecified, then it uses argparse
                        default (type=None)
  --typeless-typing-list TYPELESS_TYPING_LIST [TYPELESS_TYPING_LIST ...]
                        typing.List or list are supported
  --none-bool-arg, --no-none-bool-arg
                        boolean args ==> argparse.BooleanOptionalAction
                        (type=bool)
  --true-bool-arg, --no-true-bool-arg
                        We can set any default value (default: True)
  --false-bool-arg, --no-false-bool-arg
                        (type: bool) (default: False)
  --complex-arg COMPLEX_ARG
                        (type: complex)
  -s SHOW [SHOW ...], --show SHOW [SHOW ...]
                        We used this argument for the README example. Note
                        that comments above the arg are also included in the
                        help of the argument. This is a convenient way to
                        include long help messages.

Note that for Enums, we can use either the enum name or its value.

$ python examples/usage.py str-choices --actions Initialize init Execute exec -r1 -s actions
actions: [<Action.Initialize: 'init'>, <Action.Initialize: 'init'>, <Action.Execute: 'exec'>, <Action.Execute: 'exec'>]
$ python examples/usage.py int-choices --animals Cat 1 Dog 2 -r1 -s animals
animals: [<Animal.Cat: 1>, <Animal.Cat: 1>, <Animal.Dog: 2>, <Animal.Dog: 2>]

Alternatives

mivade/argparse_dataclass

  • Allow transforming dataclass to ArgumentParser.
  • Missing features:
    • Enum support
    • arg/pos_arg/no_arg functionality
    • Implicit positional argument
    • nargs support

lebrice/simple-parsing

  • Allow adding dataclass to ArgumentParser by using parser.add_arguments()
  • Requires boilerplate code to create the parser
  • Positional arguments
  • nargs support

swansonk14/typed-argument-parser

  • Creating argument parser from classes and functions
  • Rich functionality
  • Post-processing of arguments
  • Save/load arguments
  • Load from dict

License

BSD-3

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

classparse-0.1.4.tar.gz (17.5 kB view hashes)

Uploaded Source

Built Distribution

classparse-0.1.4-py3-none-any.whl (15.0 kB view hashes)

Uploaded Python 3

Supported by

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