I want to build a CLI code-gen tool that essentially adds bits of boiler-plate code to your project. Think wrappers for 3rd party integrations and so on. Any de-facto standards?
clypi - Your all-in-one beautiful, lightweight, type-safe, (and now) prod-ready CLIs
The best Python CLI library, arguably.
The best Python CLI library, arguably.
What is your go-to CLI library?
TLDR: check out clypi - A lightweight, intuitive, pretty out of the box, and production ready CLI library. After >250 commits and a month of development and battle testing, clypi is now stable, ready, and full of new features that no other CLI libraries offer.
---
Hey Reddit, I heard your feedback on my previous post. After a month of development, clypi is stable, ready to be used, and full of new features that no other CLI library offers.
Comparison:
I've been working with Python-based CLIs for several years with many users and strict quality requirements and always run into the sames problems with the go-to packages:
-
Argparse is the builtin solution for CLIs, but, as expected, it's functionality is very restrictive. It is not very extensible, it's UI is not pretty and very hard to change, lacks type checking and type parsers, and does not offer any modern UI components that we all love.
-
Rich is too complex and verbose. The vast catalog of UI components they offer is amazing, but it is both easy to get wrong and break the UI, and too complicated/verbose to onboard coworkers to. It's prompting functionality is also quite limited and it does not offer command-line arguments parsing.
-
Click is too restrictive. It enforces you to use decorators, which is great for locality of behavior but not so much if you're trying to reuse arguments across your application. It is also painful to deal with the way arguments are injected into functions and very easy to miss one, misspell, or get the wrong type. Click is also fully untyped for the core CLI functionality and hard to test.
-
Typer seems great! I haven't personally tried it, but I have spent lots of time looking through their docs and code. I think the overall experience is a step up from click's but, at the end of the day, it's built on top of it. Hence, many of the issues are the same: testing is hard, shared contexts are untyped, their built-in type parsing is quite limited, and it does not offer modern features like suggestions on typos. Using
Annotatedtypes is also very verbose inside function definitions.
What My Project Does:
Here are clypi's key features:
-
Type safe: making use of dataclass-like commands, you can easily specify the types you want for each argument and clypi automatically parses and validates them.
-
Asynchronous: clypi is built to run asynchronously to provide the best performance possible when re-rendering.
-
Easily testable: thanks to being type checked and to using it's own parser, clypi let's you test each individual step. From from parsing command-line arguments to running your commands in tests just like a user would.
-
Composable: clypi lets you easily reuse arguments across subcommands without having to specify them again.
-
Configurable: clypi lets you configure almost everything you'd like to configure. You can create your own themes, help pages, error messages, and more!
Please, check out the GitHub repo or docs for a showcase and let me know your thoughts and what you think of it when you give it a go!
Target Audience
clypi can be used by anyone who is building or wants to build a CLI and is willing to try a new project that might provide a better user experience than the existing ones. My peers seem very happy with the safety guarantees it provides and how truly customizable the entire library is.
I tried to make the best CLI library out there - been working on it for a few weeks now. This is my first serious attempt at an open source project with proper testing and docs - wanted to make something people could actually use.
Would love any thoughts on arguably!
https://github.com/treykeown/arguably
A small example:
#!/usr/bin/env python3
import arguably
@arguably.command
def some_function(required, not_required=2, *others: int, option: float = 3.14):
"""
this function is on the command line!
Args:
required: a required parameter
not_required: this one isn't required, since it has a default
*others: all the other positional arguments go here
option: [-x] an option, short name is in brackets
"""
if __name__ == "__main__":
arguably.run()becomes
user@machine:~$ ./readme-1.py -h usage: readme-1.py [-h] [-x OPTION] required [not-required] [others ...] this function is on the command line! positional arguments: required a required parameter (type: str) not-required this one isn't required, since it has a default (type: int, default: 2) others all the other positional arguments go here (type: int) options: -h, --help show this help message and exit -x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
It can also easily hand some wild cases, like passing in QEMU-style arguments to build classes:
user@machine:~$ ./readme-2.py --nic tap,model=e1000 --nic user,hostfwd=tcp::10022-:22 nic=[TapNic(model='e1000'), UserNic(hostfwd='tcp::10022-:22')]
What My Project Does
https://github.com/treykeown/arguably
arguably makes it super simple to define complex CLIs. It uses your function signatures and docstrings to set everything up. Here's how it works:
-
Adding the
@arguably.commanddecorator to a function makes it appear on the CLI. -
If multiple functions are decorated, they'll all be set up as subcommands. You can even set up multiple levels of subcommands.
-
The function name, signature, and docstring are used to automatically set up the CLI
-
Call
arguably.run()to parse the arguments and invoke the appropriate command
A small example:
#!/usr/bin/env python3
import arguably
@arguably.command
def some_function(required, not_required=2, *others: int, option: float = 3.14):
"""
this function is on the command line!
Args:
required: a required argument
not_required: this one isn't required, since it has a default value
*others: all the other positional arguments go here
option: [-x] keyword-only args are options, short name is in brackets
"""
print(f"{required=}, {not_required=}, {others=}, {option=}")
if __name__ == "__main__":
arguably.run()becomes
user@machine:~$ ./readme-1.py -h usage: readme-1.py [-h] [-x OPTION] required [not-required] [others ...] this function is on the command line! positional arguments: required a required parameter (type: str) not-required this one isn't required, since it has a default (type: int, default: 2) others all the other positional arguments go here (type: int) options: -h, --help show this help message and exit -x, --option OPTION an option, short name is in brackets (type: float, default: 3.14)
It can easily hand some very complex cases, like passing in QEMU-style arguments to automatically instantiated different types of classes:
user@machine:~$ ./readme-2.py --nic tap,model=e1000 --nic user,hostfwd=tcp::10022-:22 nic=[TapNic(model='e1000'), UserNic(hostfwd='tcp::10022-:22')]
You can also auto-generate a CLI for your script through python3 -m arguably your_script.py, more on that here.
Target Audience
If you're writing a script or tool, and you need a quick and effective way to run it from the command line, arguably was made for you. It's great for things where a CLI is essential, but doesn't need tons of customization. arguably makes some opinionated decisions that keep things simple for you, but doesn't expose ways of handling things like error messages.
I put in the work to create GitHub workflows, documentation, and proper tests for arguably. I want this to be useful for the community at large, and a tool that you can rely on. Let me know if you're having trouble with your use case!
Comparison
There are plenty of other tools for making CLIs out there. My goal was to build one that's unobtrusive and easy to integrate. I wrote a whole page on the project goals here: https://treykeown.github.io/arguably/why/
A quick comparison:
-
argparse- this is whatarguablyuses under the hood. The end user experience should be similar -arguablyjust aims to make it easy to set up. -
click- a powerhouse with all the tools you'd ever want. Use this if you need extensive customization and don't mind some verbosity. -
typer- also a great option, and some aspects are similar design-wise. It also uses functions with a decorator to set up commands, and also uses the function signature. A bit more verbose, though likeclick, has more customization options. -
fire- super easy to generate CLIs.arguablytries to improve on this by utilizing type hints for argument conversion, and being a little more of a middle ground between this and the more traditional ways of writing CLIs in Python.
This project has been a labor of love to make CLI generation as easy as it should be. Thanks for checking it out!
I have been using simple argparse to create different tools, and from time to time I came across different "frameworks" or libraries that can help create very pretty command line interfaces.
I'm about start working on a new tool that basically wraps a set of REST API calls, and wondering what is your go to framework when making Cli tools. I'd hope to get a nice help menu as well as possibly some autocomplete support.
I'm working on a side project that converts a python CLI command into both a web form and API using AWS Lambda under the hood. In order to create the API and form, I need metadata about the CLI command (arguments, options, types, etc). Right now, I'm building it specifically for the Click library because it provides a way to export command metadata. However, I'd be interested to know if there's any other CLI libraries that I should think about supporting. Do you use a library other than Click? Which one?
Thanks!!