
Easy Python CLI with Click - ribab
http://www.codingwithricky.com/2019/08/18/easy-python-cli-with-click/
======
Areading314
The thing that bugs me about click is that now your python script has a python
dependency that you have to install every time you need to run the script.
This is fine sometimes, but a lot of the time I want a script that will be
usable with minimal setup, which is why I always use argparse, which is in the
standard library, and gets the job done despite its quirky API.

~~~
jazzkingrt
This is more of a packaging problem. There are various ways to package python
code with it's dependencies into a single executable.

In any case, comparing a stdlib library to a 3rd party one is a bit apples to
oranges. Most people first decide whether or not they want to use pypi
packages, and then start evaluating which ones are appropriate.

~~~
Areading314
sure, if you're building a CLI to do management commands for an application
that already has dependency management in place, that's all fine

but if you just have some basic script to scrape some logs or zip up files,
its nice to have it be self contained

~~~
blondin
if it's a simple basic script, i wouldn't even go through the pain of using
argparse. i would just manually check sys.argv.

~~~
kissgyorgy
You ALWAYS need a CLI framework at least for generating help automatically,
otherwise I hate you when I want to use your frugal tool and I have to look
into it to find out how it works.

~~~
ageofwant
In which case you would be using click. If I have any expectation that someone
else is going to use my script I give it a REAME.md, a requirements.txt, a
setup.py and I use click. It literally takes about 30's to do and you now have
a cli interface indistinguishable from any other cli on your system.

------
rjeli
My favorite library for this is docopt[0], which parses the docstring at the
top of your script. It’s a lot easier for people reading your code to see the
usage up front rather than scrolling to the bottom and finding your main() and
reading the argparse calls:

    
    
      #!/usr/bin/env python3
      “””
      Usage: ./myscript.py <arg> [<optionalarg>]
      “””
      from docopt import docopt
    
      if __name__ == ‘__main__’:
          args = docopt(__doc__)
          print(args[‘<arg>’], args[‘<optionalarg>’] or ‘foo’)
    

[0]: [https://github.com/docopt/docopt](https://github.com/docopt/docopt)

~~~
orf
For really, really basic things docopt is good. But you tend to re-invent the
wheel when you need to do something other than the basic parsing it supports.
The click docs have a good section[1] on this:

> On top of that docopt is restricted to basic parsing. It does not handle
> argument dispatching and callback invocation or types. This means there is a
> lot of code that needs to be written in addition to the basic help page to
> handle the parsing results.

1\. [http://click.palletsprojects.com/en/7.x/why/#why-not-
docopt-...](http://click.palletsprojects.com/en/7.x/why/#why-not-docopt-etc)

------
mlthoughts2018
I’ve only ever found click to be a waste of time compared to just using
argparse. The extra concision you get from decorator syntax just doesn’t
matter and you introduce another dependency and need to go into the relatively
poor click documentation (especially if you end up with a longterm dependency
on old versions of click). Just not worth it.

------
guggle
I'm not sure why click is often recommended. I've used argh for years and find
it easier:

    
    
      def hello(name):
          return "hello {}".format(name)
      
      def ping():
          return "pong"
    
      if __name__ == "__main__":
          import argh
          parser = argh.ArghParser()
          parser.add_commands([hello, ping])
          parser.dispatch()

~~~
rch
If people in your organization tend to write Flask apps, then you might find
click in standalone scrips as well.

~~~
guggle
I'm not sure why Flask is often recommended either. It seems to me that it's
somewhere between Bottle and Django, playing the strange role of a not-so-
lightweight, not-so-unopiniated framework. But I should probably reserve this
rant for another thread.

------
tony
I have 3 live projects using click. I like it.

Caveat: Click's docs were out of date and had open bugs for what feels like
years until it finally updated to 7. version 6 was "unstable"
[https://github.com/pallets/click/issues/503](https://github.com/pallets/click/issues/503).
Hopefully it's over now.

Good things:

\- More concise than standard library argparse

\- Testing via CliRunner
([http://click.palletsprojects.com/en/7.x/testing/](http://click.palletsprojects.com/en/7.x/testing/)),
example: [https://github.com/tmux-
python/tmuxp/blob/v1.5.3/tests/test_...](https://github.com/tmux-
python/tmuxp/blob/v1.5.3/tests/test_cli.py)

Feel free to study / copy from mine if you like (license MIT):

\- [https://cihai-cli.git-pull.com/](https://cihai-cli.git-pull.com/):
[https://github.com/cihai/cihai-
cli/blob/v0.5.0/cihai_cli/cli...](https://github.com/cihai/cihai-
cli/blob/v0.5.0/cihai_cli/cli.py)

\- [https://tmuxp.git-pull.com/](https://tmuxp.git-pull.com/):
[https://github.com/tmux-
python/tmuxp/blob/v1.5.3/tmuxp/cli.p...](https://github.com/tmux-
python/tmuxp/blob/v1.5.3/tmuxp/cli.py)

\- [https://vcspull-git-pull.com/](https://vcspull-git-pull.com/):
[https://github.com/vcs-
python/vcspull/blob/v1.2.0/vcspull/cl...](https://github.com/vcs-
python/vcspull/blob/v1.2.0/vcspull/cli.py)

------
btown
Does this do anything that the builtin argparse library
[https://docs.python.org/3/library/argparse.html](https://docs.python.org/3/library/argparse.html)
doesn't? Seems it just turns it into decorator syntax.

~~~
jle17
It really comes handy IMHO when you have a lot of commands with nesting and
context to share. I've used it on a CLI tool with many sub-commands
implemented in imported modules and it made things really easy while I don't
think argpase would have "scaled" without becoming a mess.

Plus it comes with some useful utilities you might need in a cli to show a
progressbar, display color, open test in a pager/an editor etc.

The official doc is really nice and gives it more justice than this blog post:
[https://click.palletsprojects.com/en/7.x](https://click.palletsprojects.com/en/7.x).

~~~
btown
Ah - makes a lot more sense when they describe Git, with its nested interfaces
and argument parsers (and reused logic therein) as a motivating example!

------
jabwork
For those of you using argparse I recommend looking at configargparse. A drop
in replacement which adds environment variable support (similar to click) and
config file support

------
kylek
I've used Fire[0] for a few projects and have been meaning to give Click a
try, but I'm not sure I like the syntax after reading through the article
(holy moly decorators). Seems unwieldy to me, but does anyone have experience
with both?

[0] [https://github.com/google/python-fire](https://github.com/google/python-
fire)

~~~
ssivark
Based on the examples in the article, explicitly writing a line for each CLI
option seems like excessive boilerplate and verbosity that Fire manages to
sidestep completely. To me, this is the principal point of using a Python
library to create CLIs. Am I missing something?

~~~
chupasaurus
Fire works with bare args, automatically supports _\--arg=value_ for
constructor's arguments and provides more features like tracing and bash
completion generator.

------
Spivak
Click unfortunately doesn't pass the smell test of a non-awkward
implementation of 'sudo'. It gets some partial credit because an
implementation is _possible_ but only because you can subclass the
click.Command object and change the parsing behavior arbitrarily.

So many of these parsing libraries seem to forget that any positional
parameter can stop options parsing not just '\--' and that that parameter
might appear arbitrarily deep in subcommands.

This is not only needed for commands like sudo and ssh but BSD style CLIs as
well which all OSX users are probably intimately familiar and annoyed with.

~~~
nesadi
Which Python library fills this requirement well?

------
jblarneyforward
Click and argparse end up with a lot of boilerplate. `defopt`
([https://defopt.readthedocs.io/en/stable/](https://defopt.readthedocs.io/en/stable/))
reduces it to almost nothing (granted only in Python 3), is only a single file
and even turns your function docstring into command help text.

With every other tool I’ve tried, I end up writing a python API and a CLI and
documenting it twice.

Defopt eliminates that almost completely (but with *args still allows for
elegant interfaces)

------
j88439h84
The decorators aren't the main thing, you dont need to use them. The overall
interface is just so much better than argparse. It's way more featureful.

------
sigmonsays
i'm sorry but this is not how decorators are supposed to be used. I also doubt
that click does decorators correctly too. I couldn't get them working on a
instance method for example. Just stick to the standard library on this. A
dependency is not worth it here.

~~~
orf
Decorators can be used in any way you want. This is actually an absolutely
fine use of decorators to add metadata/annotations to a given function.

Critiques of any library are great, but you need to expand on vague points: "I
also doubt that click does decorators correctly too", or how you would ever
expect them (or even want them??) to work on an instance method. It might be
that you're misunderstanding the library and how it should be used rather than
it being not worth using.

I find it absolutely perfect for a lot of situations.

------
rcarmo
I use click for
[https://github.com/rcarmo/piku](https://github.com/rcarmo/piku) (in fact it’s
the only dependency) and often wish something like it was part of the standard
library...

------
kissgyorgy
Here is a fairly detailed overview in the form of slides what is Click and
what it can do:
[https://slides.com/kissgyorgy/deck-10#/](https://slides.com/kissgyorgy/deck-10#/)

------
dwdz
I wish python had something similar to OCaml's Cmdliner[0]

[0]
[https://erratique.ch/software/cmdliner/doc/Cmdliner](https://erratique.ch/software/cmdliner/doc/Cmdliner)

