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?
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.
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.
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 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.
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!
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.
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.
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.
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.
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.
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.
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.
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?