Package: pydargs
pydargsPydargs allows configuring a (Pydantic) dataclass through command line arguments.InstallationPydargs can be installed with your favourite package manager. For example:pip install pydargsUsageA minimal usage example would be:fromdataclassesimportdataclassfrompydargsimportparse@dataclassclassConfig:number:intsome_string:str="abc"if__name__=="__main__":config=parse(Config)After which this entrypoint can be called withentrypoint--number42orentrypoint--number42--some-stringabcdArgumentParser argumentsIt's possible to pass additional arguments to the underlyingargparse.ArgumentParserinstance by providing them
as keyword arguments to theparsefunction. For example:config=parse(Config,prog="myprogram",allow_abbrev=False)will disable abbreviations for long options and set the program name tomyprogramin help messages. For an extensive list of accepted arguments, seethe argparse docs.Supported Field TypesThe dataclass can have fields of the base types:int,float,str,bool, as well as:Literalscomprised of those types.Enums, although these
arenot recommendedas they do not play nice in the help
messages. Only the enumnameis accepted as a valid input, not thevalue.Bytes, with an optionalencodingmetadata field:a_value: bytes = field(metadata=dict(encoding="ascii")), which defaults to utf-8.Dateanddatetime, with an optionaldate_formatmetadata
field:your_date: date = field(metadata=dict(date_format="%m-%d-%Y")). When not
provided dates in ISO 8601 format are accepted.Listsof those types, either denoted as e.g.list[int]orSequence[int].
Multiple arguments to anumbers: list[int]field can be provided as--numbers 1 2 3.
A list-field without a default will require at least a single value to be provided.
If a default is provided, it will be completely replaced by any arguments, if provided.Optional types, denoted as e.g.typing.Optional[int]orint | None(for Python 3.10 and above).
Any argument passed is assumed to be of the provided type and can never beNone.Unions of types, denoted as e.g.typing.Union[int, str]orint | str. Each argument
will be parsed into the first type that returns a valid result. Note that this means
thatstr | intwillalwaysresult in a value of typestr.Any other type that can be instantiated from a string, such asPath.Dataclasses that, in turn, contain fields of supported types. SeeNested Dataclasses.A union of multiple dataclasses, that in turn contain fields of supported types,
which will be parsed inSubparsers.MetadataAdditional options can be provided to the dataclass field metadata.The following metadata fields are supported:positionalSetpositional=Trueto create a positional argument instead of an option.fromdataclassesimportdataclass,field@dataclassclassConfig:argument:str=field(metadata=dict(positional=True))as_flagsSetas_flags=Truefor a boolean field:fromdataclassesimportdataclass,field@dataclassclassConfig:verbose:bool=field(default=False,metadata=dict(as_flags=True))which would create the arguments--verboseand--no-verboseto
set the value ofverbosetoTrueorFalserespectively, instead
of a single option that requires a value like--verbose True.parserProvide a custom type converter that parses the argument into the desired type. For example:fromdataclassesimportdataclass,fieldfromjsonimportloads@dataclassclassConfig:list_of_numbers:list[int]=field(metadata=dict(parser=loads))This would parse--list-of-numbers [1, 2, 3]into the list[1, 2, 3]. Note that the error message returned
when providing invalid input is lacking any details. Also, no validation is performed to verify that the returned
type matches the field type. In the above example,--list-of-numbers '{"a": "b"}'would result inlist_of_numbersbeing the dictionary{"a": "b"}without any kind of warning.short_optionProvide a short option for a field, which can be used as an alternative to the long option.
For example,fromdataclassesimportdataclass,field@dataclassclassConfig:a_field_with_a_long_name:int=field(metadata=dict(short_option="-a"))would allow using-a 42as an alternative to--a-field-with-a-long-name 42.Ignoring fieldsFields can be ignored by adding theignore_argmetadata field:@dataclassclassConfig:number:intignored:str=field(metadata=dict(ignore_arg=True))When indicated, this field is not added to the parser and cannot be overridden with an argument.helpProvide a brief description of the field, used in the help messages generated by argparse.
For example, callingyour_program -hwith the dataclass below,fromdataclassesimportdataclass,field@dataclassclassConfig:an_integer:int=field(metadata=dict(help="any integer you like"))would result in a message like:usage: your_program [-h] [--an-integer AN_INTEGER]

optional arguments:
  -h, --help               show this help message and exit
  --an-integer AN_INTEGER  any integer you likemetavarOverride the displayed name of an argument in the help messages generated by argparse,
as documentedhere.For example, with the following dataclass,fromdataclassesimportdataclass,field@dataclassclassConfig:an_integer:int=field(metadata=dict(metavar="INT"))callingyour_program -hwould result in a message like:usage: your_program [-h] [--an-integer INT]

optional arguments:
  -h, --help        show this help message and exit
  --an-integer INTNested DataclassesDataclasses may be nested; the type of a dataclass field may be another dataclass type:fromdataclassesimportdataclass@dataclassclassConfig:field_a:intfield_b:str="abc"@dataclassclassBase:config:Configverbose:bool=FalseArgument names of fields of the nested dataclass are prefixed with the field name of the nested dataclass in the base
dataclass. Callingpydargs.parse(Base, ["-h"])will result in something like:usage: your_program.py [-h] --config-field-a CONFIG_FIELD_A
                            [--config-field-b CONFIG_FIELD_B]
                            [--verbose VERBOSE]

options:
  -h, --help            show this help message and exit
  --verbose VERBOSE     (default: False)

config:
  --config-field-a CONFIG_FIELD_A
  --config-field-b CONFIG_FIELD_B
                        (default: abc)Please be aware of the following:The default (factory) of fields with a dataclass type is ignored by pydargs, which may yield unexpected results.
E.g., in the example above,config: Config = field(default_factory=lambda: Config(field_b="def"))will not result in a default of "def" for field_b when parsed by pydargs.
Instead, setfield_b: str = "def"in the definition ofConfig.
If you must add a default, for example for instantiating your dataclass elsewhere, doconfig: Config = field(default_factory=Config), assuming that all fields inConfighave a default.Nested dataclasses can not be positional (althoughfields ofthe nested dataclass can be).Argument names must not collide. In the example above, theBaseclass should not contain a field namedconfig_field_a.SubparsersDataclasses can contain a field with a union-of-dataclasses type, e.g.:fromdataclassesimportdataclass,fieldfromtypingimportUnion@dataclassclassCommand1:field_a:intfield_b:str="abc"@dataclassclassCommand2:field_c:str=field(metadata=dict(positional=True))@dataclassclassBase:command:Union[Command1,Command2]verbose:bool=FalseThis will result insub commandswhich allow calling your entrypoint asentrypoint --verbose Command1 --field-a 12.Callingpydargs.parse(Base, ["-h"])will result in something like:usage: your_program.py [-h] [--verbose VERBOSE] {Command1,command1,Command2,command2} ...

options:
  -h, --help            show this help message and exit
  --verbose VERBOSE     (default: False)

action:
  {Command1,command1,Command2,command2}Note that:Also lower-case command names are accepted.Any dataclass can not contain more than one subcommand-field.Sub-commands can be nested and mixed with nested dataclasses.Any positional fields defined after a subcommand-field can not be parsed.Subparsers handle all arguments that come after the command; so all global arguments must come before the command.
In the above example this means thatentrypoint --verbose Command2 stringis valid butentrypoint Command2 string --verboseis not.