Package: sicli-cli
SicliIntroductionsicliis a wrapper around standard library moduleargparsethat lets you create CLIs in a simple, declarative way.
It parses the signature of yourtype-hintedfunction and generates CLI from that.sicliis not intended for big and complex applications, so if you need that, you rather need to use a framework likeClickorTyper.QuickstartLet's create a simple example:importsiclifromtypingimportLiteralfromtypingimportAnnotatedasAnnfrompathlibimportPathdefcongratulate(# positional arguments go herereason:str,gift:Ann[str,"What to give them"],# help messagelanguage:Literal["en","fr"]="en",# choice of values# options go here*,output:Path=Path("./out.txt"),loud:Ann[bool,"IF ENABLED THEN SCREAM"],# flagnames:list[str]=["Maria"],# multiple arguments)->None:"""This program congratulates everyone if you haven't guessed.By the way, this doctring is the help message for the CLI."""iflanguage=="fr":print("But I don't speak french...")returnfornameinnames:ifloud:print(f"happy{reason},{name}!!1!".upper())else:print(f"happy{reason},{name}.")print(f"Here's your{gift}")print("Writing to file",output.resolve())if__name__=="__main__":sicli.run(congratulate)This produces the following output for--help:$ python3 -m examples.congrat --help
usage: congrat.py [-h] [--output [OUTPUT]] [--loud] [--names [NAMES ...]] reason gift [{en,fr}]

This program congratulates everyone if you haven't guessed. By the way, this doctring is the help message for the CLI.

positional arguments:
  reason
  gift                 What to give them
  {en,fr}

options:
  -h, --help           show this help message and exit
  --output [OUTPUT]
  --loud               IF ENABLED THEN SCREAM
  --names [NAMES ...]Let's now test out CLI:$ python3 -m examples.congrat Christmas chocolate en --loud --output ./xm.txt --names Elizabath Maria
HAPPY CHRISTMAS, ELIZABATH!!1!
Here's your chocolate
HAPPY CHRISTMAS, MARIA!!1!
Here's your chocolate
Writing to file /home/alex/repos/sicli/xm.txtUsageCreating entry pointThe main API of the library issicli.run.
If it's called with a list of functions, they will work as subcommands.
You can pass CLI arguments as the second argument to parse them instead ofsys.argv[1:].
Also, you can pass any**kwargstoargparse.ArgumentParser.Example:>>>importsicli>>>defcreate_table(*,name:str):...print("inited")...>>>defdrop_table(*,name:str):...print("dropped")...>>>sicli.run([create_table,drop_table],["drop-table","--name","students"],description="cli for your db")droppedArguments vs optionsRegular arguments are mapped to positional arguments in CLI.Keyword-only arguments (after*) are mapped to options.Default valuesDefault values for both types of arguments are mapped, as your intuition would suggest, to default arguments in CLI. For flags, default value is alwaysFalseand don't have to be set.Typestyping.Annotated[T, ...]Annotatedin Python is the way to store metadata inside a valid type.
So,siclithe following metadatafirst argument as the type and does whatever would be done with it withoutAnnotatedwrapper.argument of typestras help for this argument.argument of typelistas names for argument.argument ofsici.Argdataclass will be merged with*kwargsforargparse.ArgumentParser.add_argument(seeargparsedocs).Example:>>>importsicli>>>fromtypingimportAnnotatedasAnn>>>defdivide(...*,...numbers:Ann[...list[int],..."Numbers to divide",...["+n","+numbers"],...sicli.Arg(nargs=2),...],...):...print(numbers[0]/numbers[1])...>>>sicli.run(divide,["+numbers","1","2"],prefix_chars="-+")0.5list[T], typing.List[T], typing.Sequence[T], typing.Iterable[T]As you saw,list[T]lets you pass multiple arguments.tuple[...](for heterogeneous types) is not currently not supported.typing.LiteralLiteral[A, B, ...](of the same type) lets you restrict values.enum.EnumIt is recommended to useLiteralinstead, butenum.Enumcan work similarly. To use it, you need to create__str__method like that:classColor(Enum):red="r"black="b"def__str__(self)->str:returnself.valueboolboolis interpreted as flag. Its default value is alwaysFalseand shouldn't be set.Other typesAny other primitive type that you would pass totypeargument inargparse.ArgumentParser.add_argumentwould work. For instance,int,str,Path.Overriding typesYou can override type forargparse.ArgumentParser.add_argument:defexample(s:Annotated[str,sicli.Arg(type=ascii)],):You could've usedasciidirectly as type annotation here if you don't care that your type checker will complain.LimitationsNote that arbitrary nesting of types is not supported (Like inlist[Annotated[Literal[1, 2, 3]]]). OnlyAnnotatedcan wrap other generic types.RequirementsNo dependencies are needed, only pure Python ≥3.10.InstallationInstall from PyPI:pip install sicli-cliMotivationFor fun.AlternativesClick: The greatest CLI toolkit. Use it if you want to have a complex CLI.Typer: Cool Click wrapper, similar tosicli.Plac: Simple wrapper forargparse.