This article is the 18th day of Python Advent Calender.
Recently, I've been using the command line a little more often. I've been using the default argparse, but I'm not sure about it so I'll look for other options and compare them. I will investigate and compare with click, fire which seems to be popular I will.
This article uses the following versions of the library:
pipenv --python 3.7.5
$ pipenv install click==7.0
$ pipenv install fire==0.2.1
python commands.py [command] [options] NAME
$ python commands.py hello World
Hello, World
$ python commands.py hellow World
Goodbye, World
$ python commands.py hello --greeting=Wazzup World
Whazzup, World
$ python commands.py goodbye --greeting=Later World
Later, World
$ python commands.py hello --caps World
HELLO, WORLD
$ python commands.py hello --greeting=Wazzup --caps World
WAZZUP, WORLD
In this article, we will compare the methods of each library to implement the following functions along the flow of the reference article.
Additional features (will be written later)
I will do the basic writing of various libraries.
Argparse
import argparse
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
hello_parser = subparsers.add_parser("hello")
goodbye_parser = subparsers.add_parser("goodbye")
parser.parse_args()
if __name__ == "__main__":
main()
You now have two commands and a built-in help message. The help message changes when run as an option on the command hello.
$ python argparse/commands.py --helop
usage: commands.py [-h] {hello} ...
positional arguments:
{hello}
optional arguments:
-h, --help show this help message and exit
$ python commands.py hello --help
usage: commands.py hello [-h]
optional arguments:
-h, --help show this help message and exit
Click
import click
@click.group()
def greet():
pass
@greet.command()
def hello(**kwargs):
pass
@greet.command()
def goodbye(**kwargs):
pass
if __name__ == "__main__":
greet()
You now have two commands and a built-in help message. The help message changes when run as an option on the command hello.
$ python click/commands.py --help
Usage: commands.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
goodbye
hello
$ python click/commands.py hello --help
Usage: commands.py hello [OPTIONS]
Options:
--help Show this message and exit.
Fire
import fire
def hello():
pass
def goodbye():
pass
if __name__ == "__main__":
fire.Fire({"hello": hello, "goodbye": goodbye})
You now have two commands and a built-in help message. The help message changes when run as an option on the command hello. In addition, fire provides help in man format. Fire can also be implemented in the following ways.
--How to arrange functions
import fire
def hello():
pass
def goodbye():
pass
if __name__ == "__main__":
fire.Fire()
--How to pass in class
import fire
class Greet:
def hello(self):
pass
def goodbye(self):
pass
if __name__ == "__main__":
greet = Greet()
fire.Fire(greet)
$ python fire/commands.py --help
NAME
commands.py
SYNOPSIS
commands.py COMMAND
COMMANDS
COMMAND is one of the following:
goodbye
hello
$ python fire/commands.py hello --help
NAME
commands.py hello
SYNOPSIS
commands.py hello
It's very interesting with a different approach for each library in the basic one. Next, we will add the NAME argument and the logic to output the result from each tool.
Here we will add new logic to the code we wrote above. Add a comment to the new line to indicate your purpose. The argument (position specification) is a required input. This time, we'll add an argument with name
so that the tool can greet a specific person.
Argparse
Use ʻadd_argumentto add arguments to the subcommand. Then set it with
set_defautls to execute the function. Finally, after parsing the arguments, execute the function with ʻargs.func (args)
.
import argparse
def hello(args):
print(f"Hello, {args.name}")
def goodbye(args):
print(f"Goodbye, {args.name}")
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
hello_parser = subparsers.add_parser("hello")
hello_parser.add_argument("name")
hello_parser.set_defaults(func=hello)
goodbye_parser = subparsers.add_parser("goodbye")
goodbye_parser.add_argument("name")
goodbye_parser.set_defaults(func=goodbye)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
$ python argparse/commands.py hello World
Hello, World
$ python argparse/commands.py hello --help
usage: commands.py hello [-h] name
positional arguments:
name
optional arguments:
-h, --help show this help message and
Click
Use @ click.argument
to add an argument to Click. In this case, you just pass the argument name, but there are various options.
$ python click/commands.py hello World
Hello, World
$ python click/commands.py hello --help
Usage: commands.py hello [OPTIONS] NAME
Options:
--help Show this message and exit.
Fire
Fire just adds arguments to the function. In Fire, it's basically just a function / class implementation, so it's pretty simple.
import fire
def hello(name):
print(f"Hello, {name}")
def goodbye(name):
print(f"Goodbye, {name}")
if __name__ == "__main__":
fire.Fire({"hello": hello, "goodbye": goodbye})
$ python fire/commands.py hello World
Hello, World
(test-cli) ikura4@ikura1-ThinkPad:~/test/test-cli$ python fire/commands.py hello --help
NAME
commands.py hello
SYNOPSIS
commands.py hello NAME
POSITIONAL ARGUMENTS
NAME
NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
(END)
Here we'll add the new logic again to the code we wrote above. The option is a non-required input that can be specified.
In this example, add --greeting = [greeting]
and --caps
. The default value for greeting
is Hello
or Goodbye
, and the user can pass any value. For example, if you set --greeting = Wazzup
, you will seeWazzup, [name]
. If --caps
is given, the display will be everything. For example, --caps
will displayHELLO [NAME]
.
Argparse
import argparse
#Since I started to pass greetings
#Unified to greet function
def greet(args):
output = f"{args.greeting}, {args.name}"
if args.caps:
output = output.upper()
print(output)
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
hello_parser = subparsers.add_parser("hello")
#Add name to the argument
hello_parser.add_argument("name")
#Added greeting option and default
hello_parser.add_argument("--greeting", default="Hello")
#Added flag with default False
hello_parser.add_argument("--caps", action="store_true")
hello_parser.set_defaults(func=greet)
goodbye_parser = subparsers.add_parser("goodbye")
goodbye_parser.add_argument("name")
goodbye_parser.add_argument("--greeting", default="Goodbye")
goodbye_parser.add_argument("--caps", action="store_true")
goodbye_parser.set_defaults(func=greet)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
$ python argparse/commands.py hello --greeting=Wazzup World
Wazzup, World
$ python argparse/commands.py hello --caps World
HELLO, WORLD
$ python argparse/commands.py hello --greeting=Wazzup --caps World
WAZZUP, WORLD
$ python argparse/commands.py hello --help
usage: commands.py hello \[-h\] [--greeting GREETING] [--caps] name
positional arguments:
name
optional arguments:
-h, --help show this help message and exit
--greeting GREETING
--caps
Click
Add greeting
and caps
with @ click.option
. Since there is a default value, I made one function.
import click
def greeter(name, greeting, caps):
output = f"{greeting}, {name}"
if caps:
output = output.upper()
print(output)
@click.group()
def greet():
pass
@greet.command()
@click.argument("name")
#Added greeting option and default
@click.option("--greeting", default="Hello")
#Added flag(is_flag=Can be flagged with True)
@click.option("--caps", is_flag=True)
def hello(name, greeting, caps):
greeter(name, greeting, caps)
@greet.command()
@click.argument("name")
@click.option("--greeting", default="Goodbye")
@click.option("--caps", is_flag=True)
def goodbye(name, greeting, caps):
greeter(name, greeting, caps)
if __name__ == "__main__":
greet()
$ python click/commands.py hello --greeting=Wazzup World
Wazzup, World
$ python click/commands.py hello --caps World
HELLO, WORLD
$ python click/commands.py hello --greeting=Wazzup --caps World
WAZZUP, WORLD
$ python click/commands.py hello --helpUsage: commands.py hello [OPTIONS] NAME
Options:
--greeting TEXT
--caps
--help Show this message and exit.
Fire
Add greeting
and caps
by adding arguments to the function. Also, since there is common processing, I summarized it again in greet
.
import fire
def greet(name, greeting, caps):
output = f"{greeting}, {name}"
if caps:
output = output.upper()
print(output)
def hello(name, greeting="Hello", caps=False):
greet(name, greeting, caps)
def goodbye(name, greeting="Goodbye", caps=False):
greet(name, greeting, caps)
if __name__ == "__main__":
fire.Fire({"hello": hello, "goodbye": goodbye})
There is a caveat to fire when passing a bool. The token after passing --caps
is passed to --caps.
There are three ways to specify it:
--Add the bool option at the end
$ python fire/commands.py hello World --caps
--Pass values separated by spaces$ python fire/commands.py hello --caps True World
-= Pass values separated by$ python fire/commands.py hello --caps=True World
$ python fire/commands.py hello --greeting=Wazzup World
Wazzup, World
$ python fire/commands.py hello --caps=True World
HELLO, WORLD
$ python fire/commands.py hello --greeting=Wazzup --caps=True World
WAZZUP, WORLD
$ python fire/commands.py hello --help
NAME
commands.py hello
SYNOPSIS
commands.py hello NAME <flags>
POSITIONAL ARGUMENTS
NAME
FLAGS
--greeting=GREETING
--caps=CAPS
NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
(END)
Argparser
――It has the appeal of a standard library, and it's good that it is used on the other hand, but I have the impression that it is not suitable for creating a simple CLI.Click
――I like decorators because the code is small and organized to do simple things, but when I go to see the code of black
etc., there are too many decorators and the readability is reduced, so on a small scale I get the impression that it is suitable.Most of the things I make are basically small, so I will use Fire. I'm thinking about Click next.
Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
Recommended Posts