blob: a97d10cfe6bb6902c0b927eda2bb5556cebbbe8a [file] [log] [blame]
Georg Brandl0958a4d2012-05-06 21:39:35 +02001*****************
2Argparse Tutorial
3*****************
Ezio Melotti6cc7a412012-05-06 16:15:35 +03004
Benjamin Peterson0a2c4f52013-01-03 20:34:19 -08005:author: Tshepang Lekhonkhobe
Ezio Melotti6cc7a412012-05-06 16:15:35 +03006
7.. _argparse-tutorial:
8
9This tutorial is intended to be a gentle introduction to :mod:`argparse`, the
10recommended command-line parsing module in the Python standard library.
11
12.. note::
13
Andrew Svetlov815b0e22013-04-05 10:10:12 +030014 There are two other modules that fulfill the same task, namely
Ezio Melotti6cc7a412012-05-06 16:15:35 +030015 :mod:`getopt` (an equivalent for :c:func:`getopt` from the C
16 language) and the deprecated :mod:`optparse`.
17 Note also that :mod:`argparse` is based on :mod:`optparse`,
18 and therefore very similar in terms of usage.
19
20
21Concepts
22========
23
24Let's show the sort of functionality that we are going to explore in this
25introductory tutorial by making use of the :command:`ls` command:
26
Serhiy Storchaka46936d52018-04-08 19:18:04 +030027.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +030028
29 $ ls
30 cpython devguide prog.py pypy rm-unused-function.patch
31 $ ls pypy
32 ctypes_configure demo dotviewer include lib_pypy lib-python ...
33 $ ls -l
34 total 20
35 drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython
36 drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide
37 -rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py
38 drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy
39 -rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch
40 $ ls --help
41 Usage: ls [OPTION]... [FILE]...
42 List information about the FILEs (the current directory by default).
43 Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
44 ...
45
46A few concepts we can learn from the four commands:
47
48* The :command:`ls` command is useful when run without any options at all. It defaults
49 to displaying the contents of the current directory.
50
51* If we want beyond what it provides by default, we tell it a bit more. In
52 this case, we want it to display a different directory, ``pypy``.
53 What we did is specify what is known as a positional argument. It's named so
54 because the program should know what to do with the value, solely based on
55 where it appears on the command line. This concept is more relevant
56 to a command like :command:`cp`, whose most basic usage is ``cp SRC DEST``.
57 The first position is *what you want copied,* and the second
58 position is *where you want it copied to*.
59
60* Now, say we want to change behaviour of the program. In our example,
61 we display more info for each file instead of just showing the file names.
62 The ``-l`` in that case is known as an optional argument.
63
64* That's a snippet of the help text. It's very useful in that you can
65 come across a program you have never used before, and can figure out
Georg Brandlee82d0b2013-10-08 21:54:37 +020066 how it works simply by reading its help text.
Ezio Melotti6cc7a412012-05-06 16:15:35 +030067
68
69The basics
70==========
71
72Let us start with a very simple example which does (almost) nothing::
73
74 import argparse
75 parser = argparse.ArgumentParser()
76 parser.parse_args()
77
78Following is a result of running the code:
79
Serhiy Storchaka46936d52018-04-08 19:18:04 +030080.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +030081
82 $ python3 prog.py
83 $ python3 prog.py --help
84 usage: prog.py [-h]
85
Raymond Hettinger41b223d2020-12-23 09:40:56 -080086 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +030087 -h, --help show this help message and exit
Ezio Melotti6cc7a412012-05-06 16:15:35 +030088 $ python3 prog.py --verbose
89 usage: prog.py [-h]
90 prog.py: error: unrecognized arguments: --verbose
91 $ python3 prog.py foo
92 usage: prog.py [-h]
93 prog.py: error: unrecognized arguments: foo
94
95Here is what is happening:
96
97* Running the script without any options results in nothing displayed to
98 stdout. Not so useful.
99
100* The second one starts to display the usefulness of the :mod:`argparse`
101 module. We have done almost nothing, but already we get a nice help message.
102
103* The ``--help`` option, which can also be shortened to ``-h``, is the only
104 option we get for free (i.e. no need to specify it). Specifying anything
105 else results in an error. But even then, we do get a useful usage message,
106 also for free.
107
108
109Introducing Positional arguments
110================================
111
112An example::
113
114 import argparse
115 parser = argparse.ArgumentParser()
116 parser.add_argument("echo")
117 args = parser.parse_args()
118 print(args.echo)
119
120And running the code:
121
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300122.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300123
124 $ python3 prog.py
125 usage: prog.py [-h] echo
126 prog.py: error: the following arguments are required: echo
127 $ python3 prog.py --help
128 usage: prog.py [-h] echo
129
130 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300131 echo
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300132
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800133 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300134 -h, --help show this help message and exit
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300135 $ python3 prog.py foo
136 foo
137
138Here is what's happening:
139
140* We've added the :meth:`add_argument` method, which is what we use to specify
141 which command-line options the program is willing to accept. In this case,
142 I've named it ``echo`` so that it's in line with its function.
143
144* Calling our program now requires us to specify an option.
145
146* The :meth:`parse_args` method actually returns some data from the
147 options specified, in this case, ``echo``.
148
149* The variable is some form of 'magic' that :mod:`argparse` performs for free
150 (i.e. no need to specify which variable that value is stored in).
151 You will also notice that its name matches the string argument given
152 to the method, ``echo``.
153
154Note however that, although the help display looks nice and all, it currently
155is not as helpful as it can be. For example we see that we got ``echo`` as a
156positional argument, but we don't know what it does, other than by guessing or
157by reading the source code. So, let's make it a bit more useful::
158
159 import argparse
160 parser = argparse.ArgumentParser()
161 parser.add_argument("echo", help="echo the string you use here")
162 args = parser.parse_args()
163 print(args.echo)
164
165And we get:
166
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300167.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300168
169 $ python3 prog.py -h
170 usage: prog.py [-h] echo
171
172 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300173 echo echo the string you use here
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300174
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800175 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300176 -h, --help show this help message and exit
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300177
178Now, how about doing something even more useful::
179
180 import argparse
181 parser = argparse.ArgumentParser()
182 parser.add_argument("square", help="display a square of a given number")
183 args = parser.parse_args()
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300184 print(args.square**2)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300185
186Following is a result of running the code:
187
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300188.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300189
190 $ python3 prog.py 4
191 Traceback (most recent call last):
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300192 File "prog.py", line 5, in <module>
193 print(args.square**2)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300194 TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
195
196That didn't go so well. That's because :mod:`argparse` treats the options we
197give it as strings, unless we tell it otherwise. So, let's tell
198:mod:`argparse` to treat that input as an integer::
199
200 import argparse
201 parser = argparse.ArgumentParser()
202 parser.add_argument("square", help="display a square of a given number",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300203 type=int)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300204 args = parser.parse_args()
205 print(args.square**2)
206
207Following is a result of running the code:
208
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300209.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300210
211 $ python3 prog.py 4
212 16
213 $ python3 prog.py four
214 usage: prog.py [-h] square
215 prog.py: error: argument square: invalid int value: 'four'
216
217That went well. The program now even helpfully quits on bad illegal input
218before proceeding.
219
220
221Introducing Optional arguments
222==============================
223
Berker Peksag8526fb72017-04-20 07:29:35 +0300224So far we have been playing with positional arguments. Let us
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300225have a look on how to add optional ones::
226
227 import argparse
228 parser = argparse.ArgumentParser()
229 parser.add_argument("--verbosity", help="increase output verbosity")
230 args = parser.parse_args()
231 if args.verbosity:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300232 print("verbosity turned on")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300233
234And the output:
235
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300236.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300237
238 $ python3 prog.py --verbosity 1
239 verbosity turned on
240 $ python3 prog.py
241 $ python3 prog.py --help
242 usage: prog.py [-h] [--verbosity VERBOSITY]
243
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800244 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300245 -h, --help show this help message and exit
246 --verbosity VERBOSITY
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300247 increase output verbosity
248 $ python3 prog.py --verbosity
249 usage: prog.py [-h] [--verbosity VERBOSITY]
250 prog.py: error: argument --verbosity: expected one argument
251
252Here is what is happening:
253
254* The program is written so as to display something when ``--verbosity`` is
255 specified and display nothing when not.
256
257* To show that the option is actually optional, there is no error when running
258 the program without it. Note that by default, if an optional argument isn't
259 used, the relevant variable, in this case :attr:`args.verbosity`, is
260 given ``None`` as a value, which is the reason it fails the truth
261 test of the :keyword:`if` statement.
262
263* The help message is a bit different.
264
265* When using the ``--verbosity`` option, one must also specify some value,
266 any value.
267
268The above example accepts arbitrary integer values for ``--verbosity``, but for
269our simple program, only two values are actually useful, ``True`` or ``False``.
270Let's modify the code accordingly::
271
272 import argparse
273 parser = argparse.ArgumentParser()
274 parser.add_argument("--verbose", help="increase output verbosity",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300275 action="store_true")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300276 args = parser.parse_args()
277 if args.verbose:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300278 print("verbosity turned on")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300279
280And the output:
281
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300282.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300283
284 $ python3 prog.py --verbose
285 verbosity turned on
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300286 $ python3 prog.py --verbose 1
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300287 usage: prog.py [-h] [--verbose]
288 prog.py: error: unrecognized arguments: 1
289 $ python3 prog.py --help
290 usage: prog.py [-h] [--verbose]
291
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800292 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300293 -h, --help show this help message and exit
294 --verbose increase output verbosity
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300295
296Here is what is happening:
297
298* The option is now more of a flag than something that requires a value.
299 We even changed the name of the option to match that idea.
300 Note that we now specify a new keyword, ``action``, and give it the value
301 ``"store_true"``. This means that, if the option is specified,
302 assign the value ``True`` to :data:`args.verbose`.
303 Not specifying it implies ``False``.
304
305* It complains when you specify a value, in true spirit of what flags
306 actually are.
307
308* Notice the different help text.
309
310
311Short options
312-------------
313
314If you are familiar with command line usage,
315you will notice that I haven't yet touched on the topic of short
316versions of the options. It's quite simple::
317
318 import argparse
319 parser = argparse.ArgumentParser()
320 parser.add_argument("-v", "--verbose", help="increase output verbosity",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300321 action="store_true")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300322 args = parser.parse_args()
323 if args.verbose:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300324 print("verbosity turned on")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300325
326And here goes:
327
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300328.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300329
330 $ python3 prog.py -v
331 verbosity turned on
332 $ python3 prog.py --help
333 usage: prog.py [-h] [-v]
334
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800335 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300336 -h, --help show this help message and exit
337 -v, --verbose increase output verbosity
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300338
339Note that the new ability is also reflected in the help text.
340
341
342Combining Positional and Optional arguments
343===========================================
344
345Our program keeps growing in complexity::
346
347 import argparse
348 parser = argparse.ArgumentParser()
349 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300350 help="display a square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300351 parser.add_argument("-v", "--verbose", action="store_true",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300352 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300353 args = parser.parse_args()
354 answer = args.square**2
355 if args.verbose:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200356 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300357 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300358 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300359
360And now the output:
361
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300362.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300363
364 $ python3 prog.py
365 usage: prog.py [-h] [-v] square
366 prog.py: error: the following arguments are required: square
367 $ python3 prog.py 4
368 16
369 $ python3 prog.py 4 --verbose
370 the square of 4 equals 16
371 $ python3 prog.py --verbose 4
372 the square of 4 equals 16
373
374* We've brought back a positional argument, hence the complaint.
375
376* Note that the order does not matter.
377
378How about we give this program of ours back the ability to have
379multiple verbosity values, and actually get to use them::
380
381 import argparse
382 parser = argparse.ArgumentParser()
383 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300384 help="display a square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300385 parser.add_argument("-v", "--verbosity", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300386 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300387 args = parser.parse_args()
388 answer = args.square**2
389 if args.verbosity == 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200390 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300391 elif args.verbosity == 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200392 print(f"{args.square}^2 == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300393 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300394 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300395
396And the output:
397
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300398.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300399
400 $ python3 prog.py 4
401 16
402 $ python3 prog.py 4 -v
403 usage: prog.py [-h] [-v VERBOSITY] square
404 prog.py: error: argument -v/--verbosity: expected one argument
405 $ python3 prog.py 4 -v 1
406 4^2 == 16
407 $ python3 prog.py 4 -v 2
408 the square of 4 equals 16
409 $ python3 prog.py 4 -v 3
410 16
411
412These all look good except the last one, which exposes a bug in our program.
413Let's fix it by restricting the values the ``--verbosity`` option can accept::
414
415 import argparse
416 parser = argparse.ArgumentParser()
417 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300418 help="display a square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300419 parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300420 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300421 args = parser.parse_args()
422 answer = args.square**2
423 if args.verbosity == 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200424 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300425 elif args.verbosity == 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200426 print(f"{args.square}^2 == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300427 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300428 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300429
430And the output:
431
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300432.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300433
434 $ python3 prog.py 4 -v 3
435 usage: prog.py [-h] [-v {0,1,2}] square
436 prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
437 $ python3 prog.py 4 -h
438 usage: prog.py [-h] [-v {0,1,2}] square
439
440 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300441 square display a square of a given number
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300442
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800443 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300444 -h, --help show this help message and exit
445 -v {0,1,2}, --verbosity {0,1,2}
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300446 increase output verbosity
447
448Note that the change also reflects both in the error message as well as the
449help string.
450
451Now, let's use a different approach of playing with verbosity, which is pretty
452common. It also matches the way the CPython executable handles its own
453verbosity argument (check the output of ``python --help``)::
454
455 import argparse
456 parser = argparse.ArgumentParser()
457 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300458 help="display the square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300459 parser.add_argument("-v", "--verbosity", action="count",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300460 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300461 args = parser.parse_args()
462 answer = args.square**2
463 if args.verbosity == 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200464 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300465 elif args.verbosity == 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200466 print(f"{args.square}^2 == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300467 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300468 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300469
470We have introduced another action, "count",
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800471to count the number of occurrences of specific options.
472
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300473
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300474.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300475
476 $ python3 prog.py 4
477 16
478 $ python3 prog.py 4 -v
479 4^2 == 16
480 $ python3 prog.py 4 -vv
481 the square of 4 equals 16
482 $ python3 prog.py 4 --verbosity --verbosity
483 the square of 4 equals 16
484 $ python3 prog.py 4 -v 1
485 usage: prog.py [-h] [-v] square
486 prog.py: error: unrecognized arguments: 1
487 $ python3 prog.py 4 -h
488 usage: prog.py [-h] [-v] square
489
490 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300491 square display a square of a given number
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300492
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800493 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300494 -h, --help show this help message and exit
495 -v, --verbosity increase output verbosity
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300496 $ python3 prog.py 4 -vvv
497 16
498
499* Yes, it's now more of a flag (similar to ``action="store_true"``) in the
500 previous version of our script. That should explain the complaint.
501
502* It also behaves similar to "store_true" action.
503
504* Now here's a demonstration of what the "count" action gives. You've probably
505 seen this sort of usage before.
506
Berker Peksag53ba2d12016-09-11 13:02:27 +0300507* And if you don't specify the ``-v`` flag, that flag is considered to have
508 ``None`` value.
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300509
510* As should be expected, specifying the long form of the flag, we should get
511 the same output.
512
513* Sadly, our help output isn't very informative on the new ability our script
514 has acquired, but that can always be fixed by improving the documentation for
Georg Brandl76b2ee02016-02-28 21:09:36 +0100515 our script (e.g. via the ``help`` keyword argument).
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300516
517* That last output exposes a bug in our program.
518
519
520Let's fix::
521
522 import argparse
523 parser = argparse.ArgumentParser()
524 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300525 help="display a square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300526 parser.add_argument("-v", "--verbosity", action="count",
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300527 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300528 args = parser.parse_args()
529 answer = args.square**2
530
531 # bugfix: replace == with >=
532 if args.verbosity >= 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200533 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300534 elif args.verbosity >= 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200535 print(f"{args.square}^2 == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300536 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300537 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300538
539And this is what it gives:
540
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300541.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300542
543 $ python3 prog.py 4 -vvv
544 the square of 4 equals 16
545 $ python3 prog.py 4 -vvvv
546 the square of 4 equals 16
547 $ python3 prog.py 4
548 Traceback (most recent call last):
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300549 File "prog.py", line 11, in <module>
550 if args.verbosity >= 2:
Victor Stinner91108f02015-10-14 18:25:31 +0200551 TypeError: '>=' not supported between instances of 'NoneType' and 'int'
552
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300553
554* First output went well, and fixes the bug we had before.
555 That is, we want any value >= 2 to be as verbose as possible.
556
557* Third output not so good.
558
559Let's fix that bug::
560
561 import argparse
562 parser = argparse.ArgumentParser()
563 parser.add_argument("square", type=int,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300564 help="display a square of a given number")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300565 parser.add_argument("-v", "--verbosity", action="count", default=0,
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300566 help="increase output verbosity")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300567 args = parser.parse_args()
568 answer = args.square**2
569 if args.verbosity >= 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200570 print(f"the square of {args.square} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300571 elif args.verbosity >= 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200572 print(f"{args.square}^2 == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300573 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300574 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300575
576We've just introduced yet another keyword, ``default``.
577We've set it to ``0`` in order to make it comparable to the other int values.
578Remember that by default,
579if an optional argument isn't specified,
580it gets the ``None`` value, and that cannot be compared to an int value
581(hence the :exc:`TypeError` exception).
582
583And:
584
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300585.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300586
587 $ python3 prog.py 4
588 16
589
590You can go quite far just with what we've learned so far,
591and we have only scratched the surface.
592The :mod:`argparse` module is very powerful,
593and we'll explore a bit more of it before we end this tutorial.
594
595
596Getting a little more advanced
597==============================
598
599What if we wanted to expand our tiny program to perform other powers,
600not just squares::
601
602 import argparse
603 parser = argparse.ArgumentParser()
604 parser.add_argument("x", type=int, help="the base")
605 parser.add_argument("y", type=int, help="the exponent")
606 parser.add_argument("-v", "--verbosity", action="count", default=0)
607 args = parser.parse_args()
608 answer = args.x**args.y
609 if args.verbosity >= 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200610 print(f"{args.x} to the power {args.y} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300611 elif args.verbosity >= 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200612 print(f"{args.x}^{args.y} == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300613 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300614 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300615
616Output:
617
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300618.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300619
620 $ python3 prog.py
621 usage: prog.py [-h] [-v] x y
622 prog.py: error: the following arguments are required: x, y
623 $ python3 prog.py -h
624 usage: prog.py [-h] [-v] x y
625
626 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300627 x the base
628 y the exponent
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300629
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800630 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300631 -h, --help show this help message and exit
632 -v, --verbosity
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300633 $ python3 prog.py 4 2 -v
634 4^2 == 16
635
636
637Notice that so far we've been using verbosity level to *change* the text
638that gets displayed. The following example instead uses verbosity level
639to display *more* text instead::
640
641 import argparse
642 parser = argparse.ArgumentParser()
643 parser.add_argument("x", type=int, help="the base")
644 parser.add_argument("y", type=int, help="the exponent")
645 parser.add_argument("-v", "--verbosity", action="count", default=0)
646 args = parser.parse_args()
647 answer = args.x**args.y
648 if args.verbosity >= 2:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200649 print(f"Running '{__file__}'")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300650 if args.verbosity >= 1:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200651 print(f"{args.x}^{args.y} == ", end="")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300652 print(answer)
653
654Output:
655
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300656.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300657
658 $ python3 prog.py 4 2
659 16
660 $ python3 prog.py 4 2 -v
661 4^2 == 16
662 $ python3 prog.py 4 2 -vv
663 Running 'prog.py'
664 4^2 == 16
665
666
667Conflicting options
668-------------------
669
670So far, we have been working with two methods of an
671:class:`argparse.ArgumentParser` instance. Let's introduce a third one,
672:meth:`add_mutually_exclusive_group`. It allows for us to specify options that
Andrew Svetlove16f4dc2013-04-06 18:55:07 +0300673conflict with each other. Let's also change the rest of the program so that
674the new functionality makes more sense:
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300675we'll introduce the ``--quiet`` option,
676which will be the opposite of the ``--verbose`` one::
677
678 import argparse
679
680 parser = argparse.ArgumentParser()
681 group = parser.add_mutually_exclusive_group()
682 group.add_argument("-v", "--verbose", action="store_true")
683 group.add_argument("-q", "--quiet", action="store_true")
684 parser.add_argument("x", type=int, help="the base")
685 parser.add_argument("y", type=int, help="the exponent")
686 args = parser.parse_args()
687 answer = args.x**args.y
688
689 if args.quiet:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300690 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300691 elif args.verbose:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200692 print(f"{args.x} to the power {args.y} equals {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300693 else:
Rémi Lapeyre7efb8262020-05-21 06:22:59 +0200694 print(f"{args.x}^{args.y} == {answer}")
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300695
696Our program is now simpler, and we've lost some functionality for the sake of
697demonstration. Anyways, here's the output:
698
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300699.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300700
701 $ python3 prog.py 4 2
702 4^2 == 16
703 $ python3 prog.py 4 2 -q
704 16
705 $ python3 prog.py 4 2 -v
706 4 to the power 2 equals 16
707 $ python3 prog.py 4 2 -vq
708 usage: prog.py [-h] [-v | -q] x y
709 prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
710 $ python3 prog.py 4 2 -v --quiet
711 usage: prog.py [-h] [-v | -q] x y
712 prog.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
713
714That should be easy to follow. I've added that last output so you can see the
715sort of flexibility you get, i.e. mixing long form options with short form
716ones.
717
718Before we conclude, you probably want to tell your users the main purpose of
719your program, just in case they don't know::
720
721 import argparse
722
723 parser = argparse.ArgumentParser(description="calculate X to the power of Y")
724 group = parser.add_mutually_exclusive_group()
725 group.add_argument("-v", "--verbose", action="store_true")
726 group.add_argument("-q", "--quiet", action="store_true")
727 parser.add_argument("x", type=int, help="the base")
728 parser.add_argument("y", type=int, help="the exponent")
729 args = parser.parse_args()
730 answer = args.x**args.y
731
732 if args.quiet:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300733 print(answer)
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300734 elif args.verbose:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300735 print("{} to the power {} equals {}".format(args.x, args.y, answer))
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300736 else:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300737 print("{}^{} == {}".format(args.x, args.y, answer))
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300738
739Note that slight difference in the usage text. Note the ``[-v | -q]``,
740which tells us that we can either use ``-v`` or ``-q``,
741but not both at the same time:
742
Serhiy Storchaka46936d52018-04-08 19:18:04 +0300743.. code-block:: shell-session
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300744
745 $ python3 prog.py --help
746 usage: prog.py [-h] [-v | -q] x y
747
748 calculate X to the power of Y
749
750 positional arguments:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300751 x the base
752 y the exponent
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300753
Raymond Hettinger41b223d2020-12-23 09:40:56 -0800754 options:
Ezio Melotti9ab3fdd2012-05-06 17:05:16 +0300755 -h, --help show this help message and exit
756 -v, --verbose
757 -q, --quiet
Ezio Melotti6cc7a412012-05-06 16:15:35 +0300758
759
760Conclusion
761==========
762
763The :mod:`argparse` module offers a lot more than shown here.
764Its docs are quite detailed and thorough, and full of examples.
765Having gone through this tutorial, you should easily digest them
766without feeling overwhelmed.