Package: typedparse
TypedparseWhat is typedparse? It is a lightweight parser for command-line options based on type hints. Typedparse uses the argparse
package as the backend. So you don't need to define parsers and subparsers by yourself anymore. Just write clean, typed,
and documented code; typedparse will do all work for you.importtypedparsefromtypingimportOptionaldefmain(file:str,number:Optional[int]=6):"""Display first lines of a file.Args:file: file to displaynumber: number of lines to display"""withopen(file,"r")asf:foriinrange(0,number):print(f.readline(),end="")if__name__=="__main__":typedparse.parse(main,generate_short_flags=True)Here is a small example that emulates the standardheadcommand. Here you need only specify types and default values
for the function's formal parameters and pass them into theparsefunction. You also have to specify the docstring for
the function it will be used to generate help. In this case we also usegenerate_short_flags=Trueto generate short flags.
That's it.If we run the program with one argument-hor--help, it produces the following output:usage: head.py [-h] [--number [NUMBER]] file

positional arguments:
  file                  file to display

optional arguments:
  -h, --help            show this help message and exit
  --number [NUMBER], -n [NUMBER]
                        number of lines to displayTypedparse heavily relies on not only type hints butdocstring_parserpackage. So it supports all popular documentation styles, but you need to be sure your docstring is well-formed
according to the style you chose. This means you can skip the long description but can't skip the short one and
parameters' description.InstallationTypedparse is available as a regular PyPI package. To install it, simply type:pipinstalltypedparsePositional and optional argumentsThe separation of positional and optional arguments in the function definition and command-line arguments is slightly
different because it allows for a default value. In typedparse, we use Optional type to distinguish positional and
optional arguments:fromtypingimportOptionaldefmain(foo:str,bar:Optional[str]="bar"):...In this example,foois a positional argument, andbaris optional with the default value "bar". You can add default
value for positional arguments too, e.g.def main(foo: str = "foo", ...).Types of argumentsFor any argument, we must specify the type using a type hint for a certain formal parameter. It can be the built-in
Python type likeint,floatorstr. Or any user-defined class with a constructor that accepts a string argument.
For example, we can usePathinstead ofstrtype for filenames and paths.Short flagsTo introduce a short flag, one can use a decoratoroptionsfor the functions:importtypedparsefromtypingimportOptional@typedparse.options(bar="-b")defmain(foo:str,bar:Optional[str]="bar"):...Of course, it makes sense only for optional arguments. Now it will work with both--barand-bflags.To use only the short flag, the simplest way to do it is@options(bar=["-b"]). You can also use another name for the
long flag by the same trick:bar=["--box"]or use bothbar=["-b", "--box"]. It will affect only the command line arguments but not the function's formal
parameters.There is an option to generate short flags automatically for all optional arguments. To do so
usegenerate_short_flags=Trueas the second argument inparsefunction.Of course, you can combine both methods. In this case, the generator will use information about used flags during the
generating process. The general algorithm tries to use the first character of the formal parameter as a short flag. If
the flag is in use, it tries to use the second character and so on.SubparsersTypedparse supports a hierarchy of parsers in a very natural way. You can combine functions in classes, classes in
lists. Consider the following example:importtypedparsefromtypingimportOptionalclassCliExample:defadd(self,name:str,email:Optional[str]=None):...defremove(self,name:str):...if__name__=="__main__":typedparse.parse(CliExample())There are two commands here:addandremovewith their own set of arguments.Methods started from underscore will be ignored by the parser. Typical usage will look like:pythoncommands.pyaddjohn--emailjohn@mycompany.comSo, thenameparameter will be bound tojohn, and theemailwill be bound tojohn@mycompany.com.
If the class construction doesn't have parameters, you can pass the class itself
into theparsefunction instead of the object:typedparse.parse(CliExample).MethodsActually, you don't need a class if you want to create sub-commands. You can use
a list of functions instead:importtypedparsefromtypingimportOptionaldefadd(name:str,email:Optional[str]=None):...defremove(name:str):...if__name__=="__main__":typedparse.parse([add,remove])List argumentsList arguments are supported out of the box in typedparse:fromtypingimportListdefmain(numbers:List[int]):...But there are two possible issues here:By default, the number of arguments in the list is at least one. This is equivalent tonargs="+"in argparse
package.It may be inconvenient to have the displayed name
(meta variable in terms of argparse) the same as the corresponding formal parameter. It will look
likenumbers [numbers ...]if we would try to display help.How to manage that? For the second case, we can rename the formal parameter or use@options(flags=["number"])to make
it singular. If we want to allow an empty list, we can specify it in the following
way@options(numbers={"nargs": "*"}). We also can combine these two options if we want:importtypedparsefromtypingimportList@typedparse.options(numbers={"flags":["number"],"nargs":"*"})defmain(numbers:List[int]):"""My brand new CLIArgs:numbers: a list of numbers"""...Custom typesIf you want to parse arguments to your own types, you can do that in the following manner:importtypedparsefromtypingimportOptionaldefto_int(s:str)->int:ifs.startswith("0x"):returnint(s,16)elifs.startswith("0"):returnint(s,8)returnint(s,10)@typedparse.options(test={"type":to_int})defmain(test:Optional[int]=0):...In this example, we use a custom function to convert string arguments to integers, which supports hexadecimal and octal
representations.Kebab case vs. snake case argumentsFrom the version 0.2 typedparse uses kebab case for long optional flags, somy_long_flag: Optional[bool]will become--my-long-flag. You can use snake case,
if you want by settingsnake_case_flagsto true in theparsefunction.