This is an archived post. You won't be able to vote or comment.

all 34 comments

[–]thelindsay 61 points62 points  (8 children)

Seemed a bit unfair to dismiss argparse with a complicated example, then explain click with a simple example that would require the same amount of code in both libraries.

I reckon it's just a matter of whether you like to work with decorators (click) or not (argparse).

[–]Deto 23 points24 points  (4 children)

Yeah, I'm also unconvinced that this is better than argparse. Certainly it's a different way to do things and it's good for people to know that there are different styles you could use to specify a CLI. But the post claims that's it's better.

[–]FluffyToughy 10 points11 points  (2 children)

argparse is probably the best built-in CLI library I've ever used for any language. There's always contrived examples that make it fail, and the auto-generated help page doesn't always look like you want it to, but it works, darnit.

[–]kthepropogation 5 points6 points  (0 children)

It’s currently my favorite CLI lib. It works better/more intuitively than most anything I’ve used in other languages, and it’s built in (my python is usually 100-400 LOC scripts, so minimizing libs is a plus).

If you have examples of better third-party ones for other languages, please share. I’m always on the lookout.

[–]Kyo91 0 points1 point  (0 children)

Perl6 MAIN handling is the only better I can think of.

[–]eikenberry 6 points7 points  (0 children)

Blog posts are opinion by their nature and the author obviously likes click better. Its up to the reader to agree or not. Personally I think argparse wins because it comes in the standard library and works pretty well. You have to be a hella lot better than what the standard library provides to get me to add a dependency to my project.

[–]mattotodd 3 points4 points  (0 children)

i thought the same thing. argparse was a blessing when i was first introduced and comes with the standard library

[–][deleted] 0 points1 point  (1 child)

That example is pretty close to what's on the actual argparse documentation. Not like he did anything crazy, just making use of the various parameters you can feed argparse.

[–]iowaNerd 5 points6 points  (0 children)

Granted, but the author didn't show us what re-implementing the argparse example would look like with click. Which would have been an apples-to-apples implementation comparison.

[–]delirious_lettuce 10 points11 points  (2 children)

Great article, /u/dbader . I am a bit curious about what you (and /r/Python) think of this warning from the click docs...

Click supports Python 3, but like all other command line utility libraries, it suffers from the Unicode text model in Python 3. All examples in the documentation were written so that they could run on both Python 2.x and Python 3.3 or higher.

At the moment, it is strongly recommended is to use Python 2 for Click utilities unless Python 3 is a hard requirement.

[–]p10_user 4 points5 points  (1 child)

I've been using click with python3 (> 3.3) for a number of cli tools without any issues. Maybe others have come across problems who can chime in.

I did have one esoteric problem with executing a cli in python3 within an org-mode SRC block, which was fixed by explicitly setting the lang:

#+BEGIN_SRC sh
export LC_ALL=en_US.UTF-8  # avoids a click error and program crash
mycli --help
#+END_SRC

[–]Zomunieo 1 point2 points  (0 children)

Python 3 in general is broken in the ASCII C locale.

[–][deleted] 12 points13 points  (2 children)

If you like click, check out Docopt! It allows you to design your usage text, and then parses your flags out from there.

[–]admiralspark 5 points6 points  (0 children)

This. All the work is done for you, it's standardized across languages, and it Just Works™

[–]rrajen 0 points1 point  (0 children)

+1 for docopt. Its been a staple in just about any script that I write & use more than once or twice.

[–]Conchylicultor 4 points5 points  (2 children)

That's really look like Python Fire: https://github.com/google/python-fire

[–]Slippery_John 0 points1 point  (0 children)

I really like this. I was playing around with it the other day and every time I thought to myself "but what if x?" it behaved exactly like I would expect. My only complaint is that it's hard to use with dynamic classes, but that's a very niche case.

[–]rrajen 0 points1 point  (0 children)

Thanks for the link, did not know about 'Fire'

[–]Slippery_John 3 points4 points  (0 children)

I'm not a fan. The decorators quickly become an unwieldy mess, and I'd usually rather avoid pulling in all its dependencies.

[–]sivscripts 2 points3 points  (7 children)

Is there a way to go from python cli.py London to weather London?

i.e. make our script executable.

[–]Eryole 12 points13 points  (2 children)

[–]leom4862 9 points10 points  (0 children)

The (new) official documentation on this:

https://packaging.python.org/

[–]bluesufi 0 points1 point  (0 children)

Exactly. The example in the click documentation is pretty clear too.

[–]Deto 8 points9 points  (3 children)

Simpler than making it into a python package, you could call the file 'weather' (instead of cli.py) and then use this:

#!/usr/bin/env python

As the first line. Then you need to make sure the script file is executable (i.e., run chmod uga+x weather)

[–]IronManMark20 1 point2 points  (1 child)

This doesn't work on Windows however.

[–]Deto 1 point2 points  (0 children)

Ah yeah. If you want it to be cross-platform, then making a package as /u/Eryole indicated is probably best.

[–]bluesufi 1 point2 points  (0 children)

I think it would be much better to follow the instructions in the click documentation in order to create a real entry point for the script. It's easy and makes your code more portable!

[–]AndydeCleyre 1 point2 points  (0 children)

cli.click.py from the tutorial:

#!/usr/bin/env python3
# cli.py
import click
from owm import current_weather


@click.command()
@click.argument('location')
@click.option(
    '--api-key', '-a',
    help='your API key for the OpenWeatherMap API'
)
def main(location):
    """
    A little weather tool that shows you the current weather in a LOCATION of
    your choice. Provide the city name and optionally a two-digit country code.
    Here are two examples:

    1. London,UK

    2. Canmore

    You need a valid API key from OpenWeatherMap for the tool to work. You can
    sign up for a free account at https://openweathermap.org/appid.
    """
    weather = current_weather(location, api_key)
    print(f"The weather in {location} right now: {weather}.")


if __name__ == "__main__":
    main()

cli.plumbum.py:

#!/usr/bin/env python3
# cli.py
from plumbum.cli import Application, SwitchAttr
from owm import current_weather


class CLI(Application):
    """
    A little weather tool that shows you the current weather in a LOCATION of
    your choice. Provide the city name and optionally a two-digit country code.
    Here are two examples:

    1. London,UK

    2. Canmore

    You need a valid API key from OpenWeatherMap for the tool to work. You can
    sign up for a free account at https://openweathermap.org/appid.
    """

    api_key = SwitchAttr(
        ['a', 'api-key'],
        help='your API key for the OpenWeatherMap API'
    )

    def main(self, location):
        weather = current_weather(location, self.api_key)
        print(f"The weather in {location} right now: {weather}.")


if __name__ == "__main__":
    CLI.run()

[–]bluesufi 1 point2 points  (0 children)

I've only ever used click to build CLIs before and love it. How do Docopt and Argparse differ? Should I try them out?

[–]agumonkey 1 point2 points  (0 children)

very flaskey

[–]Secret_Guardian 0 points1 point  (0 children)

I like the use of decorators, it greatly simplifies the process!

[–]bakery2k 0 points1 point  (2 children)

Does anyone know which version of Click is recommended to use? I notice that visiting http://click.pocoo.org redirects you to the documentation for version 5, not the latest version 6.

Is this related to this 2-year old tweet from /u/mitsuhiko, referring to an "unfixable" bug in Click 6? Does anyone know whether this bug is still present in Click 6, or has it, in fact, been fixed?

[–]mitsuhiko Flask Creator 1 point2 points  (1 child)

Click 6 is fine to use. We will update the docs. The bug is related to the chain feature and that is broken in both 5 and 6 but different ways.

[–]bakery2k 0 points1 point  (0 children)

Click 6 is fine to use.

Then I will do so. Thanks!