As someone who's used argparse recently, this would have been helpful. argparse is great, but it's (personally) rather cryptic especially if you're not intimately familiar with it.
Because it's great that you have a library to parse args... but that's only about half of what you actually want when you're building a snazzy CLI tool.
For things that don't require the complexity/features of argparse, I use sys.argv. Doesn't require any extra packages and other programmers have no trouble understanding the code.
Also it avoids a pallets package, which is good because I prefer to avoid any unnecessary exposure to its maintainer.
Moreover, click provides a lot of additional CLI-related stuff (printing with colors, asking for input, progress bars) with IMHO pretty reasonable default behaviour. And it's really well-documented!
I normally use Click but sometimes argparse is great for CLI tools that have zero dependencies and aren't massive in scope.
Since most systems have Python, distributing your argparse driven file means curling it down and making it executable instead of having to pip install things.
Have to plug Typer[1] here - it's built on top of click, but uses the info already there type annotations, optional keyword arguments, default arguments, etc., to create the most "common sense" CLI for a function. For all my internal tools, has saved me a ton of time and gruntwork.
I've been using Typer for my projects recently and I have to agree, it's great! My only gripe with it is that its autocompletion-installation arguments can't be turned off, so it "pollutes" your help text in a way.
It's not a big deal, but especially for smaller tools with small CLIs this annoys me a bit.
Better solution would be to clean up the argparse API. For example why is the explanatory text argument “description” when you call the parser constructor (argparse.ArgumentParser) but “help” when you call the argument constructor (parser.add_argument)? Also why are the docs for nargs (whose options I always forget) so buried in the argparse docs?
No, I don't see any functional difference between the explanatory text for the argument and the explanatory text for the program. They could both accurately be called "help" or "description," but for some reason the original author did not want to choose. (I agree the dictionary definition of the words are different, but in this context there is no functional difference.)
Also, your first sentence explains why I don't do what you suggest in your second sentence :-) Some people may not agree with my reasoning (which is obviously their right), not worth the trouble to figure out how to submit a patch and then risk having it rejected.
I'd really love some kind of unified interface for program input.
Like, a Python program, a Ruby program, an online form, a REST API, and so on, all specify the input they need in ways specific to each tool (selecting from a predefined list is `CHOICES` param for argparse, a dropdown for a web form, etc). Why can't we just have some JSON spec for "input"? A list of params, each with a name, a description, a type, perhaps a list of possible choices -- the same sort of thing `argparse` exposes as its API.
Then, depending on the need, we can render that into a web page, or a parser, or whatever, and we can build libraries that can always expect to get input the same way without needing to redefine it all for each interface. Does something like this exist already and I just haven't seen it?
I have been thinking of building something like this for some time. It would be great to have something that automatically generated a:
* CUI
* TUI
* Web UI
* REST API
* Config files
* Environment Variables
* GUI
There are pieces of this in various places. There are a number of command line libraries that integrate config files, command line options, and environment variables, including Traitlets (used in Jupyter). The original Enthought Traits did this for GUIs (declare a set of of classes with datatypes, call .configure_traits() on them, and voila, an autogenerated GUI for them. The Django admin interface is very similar for the web. What's needed is to bring this into a single package.
I also very much like this idea, as I find writing CUI/GUI code kind of boilerplate, annoying, and often feels like this is sth which could be automated.
Sounds nice in theory, but with the state of modern development, this kind of thing will be implemented as a CLI frontend for an electron app.
But seriously though, the reason why this kind of standardised interface doesn't exist is because there's no one-size-fits-all solution. Everyone will "need" an extension to the spec, or start adding metasyntax to fit their specific use case.
Neat. The way I solved this about a decade ago is to create a base template script for my editor. When I hit Ctrl-N a new main script opens with heading, main(), status code return, CLI parsing, and basic logging set up already.
New argparse params are either uncommented and/or cut and pasted from existing ones. It works pretty well.
Looking at that link, argparse is very similar to structopt. It just uses the syntax and idioms of a different language. The structopt attributes appear to be approx. the same complexity as argparse functions.
Indeed. My guess would be that structopt's design was based on Python's argparse, which itself derived from lessons learned from the earlier Python module optparse, which was created around 20 years ago. I'm not sure where declarative CLI argument parsing originated, but I first encountered it in optparse.
There are multiple alternatives for argparse if you look for third-party libraries.
The thing that makes argparse particularly useful is because it is in stdlib, so if you use you can create a Python script without external dependencies.
I use argparse daily. One CLI script I have uses over 1000 LOC to define all the commands/arguments/flags. Am I doing something wrong? Is there a way to unclutter my CLI script but still have the commands? Import them from another file, perhaps?
As another commenter here mentioned, you might want to consider fire [1], depending on the complexity of your argparse-usage it might check 90% of your boxes for you.
Haha I had a similar problem with argparse so I built a declarative cli and I documented the hell out of it, I added all the recipes in the README. For me it's just ctrl+f in the README and I get what I need. So far it's worked well and people contributing to another project using it haven't complained.
Admittedly, the way it works is kind of black magic, so it's probably not for everyone, but it's so simple: just annotate an existing variable with Option and it becomes a command-line argument. You can define options where they are used in the program instead of defining them in one spot and then using them somewhere else.
The argparse builder, this looks like sth you get for free by any advanced IDE, like PyCharm, right? Any IDE will always tell you what options / parameters there are, and their type, and documentation, so it doesn't matter if you forget them.
47 comments
[ 2.8 ms ] story [ 102 ms ] threadhttps://github.com/erikhuda/thor
Because it's great that you have a library to parse args... but that's only about half of what you actually want when you're building a snazzy CLI tool.
[1] https://click.palletsprojects.com/en/7.x/
Also it avoids a pallets package, which is good because I prefer to avoid any unnecessary exposure to its maintainer.
Nearly every script I write only requires something like the following:
In the very rare occasion that I have an actually large command line project, argparse is built in and works fine.or -- -v (-v is forced to be interpreted as positional in this case since it comes after --).
To be honest, the extent of my command line parsing is almost always just this:
Since most systems have Python, distributing your argparse driven file means curling it down and making it executable instead of having to pip install things.
[1] https://typer.tiangolo.com/
The docs are open source, you can fix it and send a request a pull!
Also, your first sentence explains why I don't do what you suggest in your second sentence :-) Some people may not agree with my reasoning (which is obviously their right), not worth the trouble to figure out how to submit a patch and then risk having it rejected.
Like, a Python program, a Ruby program, an online form, a REST API, and so on, all specify the input they need in ways specific to each tool (selecting from a predefined list is `CHOICES` param for argparse, a dropdown for a web form, etc). Why can't we just have some JSON spec for "input"? A list of params, each with a name, a description, a type, perhaps a list of possible choices -- the same sort of thing `argparse` exposes as its API.
Then, depending on the need, we can render that into a web page, or a parser, or whatever, and we can build libraries that can always expect to get input the same way without needing to redefine it all for each interface. Does something like this exist already and I just haven't seen it?
https://github.com/oilshell/oil/wiki/Shellac-Protocol-Propos...
OpenAPI (Swagger) kind of, although AFAIK it doesn't lend itself to specifying how to map things onto a CLI. A good next step for the project?
* CUI
* TUI
* Web UI
* REST API
* Config files
* Environment Variables
* GUI
There are pieces of this in various places. There are a number of command line libraries that integrate config files, command line options, and environment variables, including Traitlets (used in Jupyter). The original Enthought Traits did this for GUIs (declare a set of of classes with datatypes, call .configure_traits() on them, and voila, an autogenerated GUI for them. The Django admin interface is very similar for the web. What's needed is to bring this into a single package.
In fact, I also implemented a similar idea to automatic GUI generation for my music player: https://github.com/albertz/music-player
I tried to summarize the idea here: https://github.com/albertz/wiki/blob/master/automatic-user-i...
Maybe you have some further good references?
But seriously though, the reason why this kind of standardised interface doesn't exist is because there's no one-size-fits-all solution. Everyone will "need" an extension to the spec, or start adding metasyntax to fit their specific use case.
https://github.com/docopt/docopt
It is a simple DSL with implementations available in multiple languages. I've used it in python, GoLang and in Java too. Lots of examples too.
New argparse params are either uncommented and/or cut and pasted from existing ones. It works pretty well.
Now that I look closer, I actually have to use the File menu, it doesn't have a default shortcut assigned.
https://github.com/google/python-fire
Just comment your code and get pretty arguments.
It's a great example of a project that only works (without codegen / build toolchain-support) for a dynamic language.
Isn't there anything like structopt https://docs.rs/structopt/0.3.19/structopt/ for Python?
The thing that makes argparse particularly useful is because it is in stdlib, so if you use you can create a Python script without external dependencies.
[1]: https://github.com/google/python-fire
https://github.com/Woile/decli
But nowadays I would recommend something like typer: https://typer.tiangolo.com/ it's quite nice and has autocomplete built in.
Admittedly, the way it works is kind of black magic, so it's probably not for everyone, but it's so simple: just annotate an existing variable with Option and it becomes a command-line argument. You can define options where they are used in the program instead of defining them in one spot and then using them somewhere else.