I’m fiddling around a bit with Python. I’m planning to write a little script that pings a host or hosts and logs it to a file. Should be pretty basic but it’s good practice for me to take user input, read from a file, write to a file and so on. I wanted to support having arguments in the script to do things like a continuous ping or ping with x number of packets and then stop. To help the user pick the arguments I planned to do a little help text that printed out the arguments. Something like:

The problem with doing this manually is that you have to parse the arguments and build the logic yourself. For example the script should not allow someone to input both -c and -i since those arguments should be mutually exclusive. Either you run a continuous ping or you run it for x number of packets. It’s kind of tedious to build that logic yourself than to focus that time and effort on building the core part of the script.

There is a module in Python called Argparse. The Argparse module’s sole purpose in life is to help build this logic in a simple way so that users can build scripts that require arguments and make arguments mandatory or optional and mutually exclusive etc.

The first step to start using this module is to import it into Python.

We must then create an ArgumentParser object.

Then we tell this object to parse the arguments. Arguments will be converted to the correct type and the appropriate action will be taken.

This is the code we have so far.

If we run the script without any arguments there is nothing output but if we run it with -h we get my description and that -h should be used to print this help.

Now that this first part is built we want to add arguments to the script and make some of them required. Arguments in Argparse can either be positional or optional. A positional argument is something that is expected at a certain place in the command such as using “copy src dest” where it’s expected that the source is specified first and then the destination. It’s not possible to leave out any arguments. Arguments can also be optional meaning that they may be included or not.

Arguments in Argparse are created by using the add_argument() method.

This is an optional argument. The following is a positional argument.

The optional argument is detected by the – or — prefix.

Different actions can then be taken on the arguments where the default action is to store the value. It’s also possible to store constants, print the version of the program and print the help text. This link shows all of the actions that can be taken.

Let’s add the version of the script to Argparse.

The %(prog)s will get the script name from sys.argv[0]. Let’s see if this works.

Let us then add some arguments to the script. The first argument will be -c for count, which is the number of packets to be sent.

Argparse will by default store the arguments as strings but we want to store this value as an integer. Let’s check that we can use the argument and get help for it.

Now let’s add some more arguments. The following arguments should be added.

-s packetsize in bytes
-t ttl
-w timeout in seconds

All optional arguments are stored as integers. Let’s see if this works so far.

This looks good so far. We aren’t doing any checking to see if the arguments are reasonable though. What if the user tries to set the TTL to 1000?

The script is accepting this obviously not valid value. Let’s restrict the values that the user can input. This is done by using the choices container.

Running this produces quite an ugly output though…

Also the help text is not very pretty…

We can change the help text by using the metavar value which changes the displayed name of the argument.

This looks better now.

Unfortunately this doesn’t help with the output from when the user enters a faulty value.

Unfortunately I haven’t found a solution for changing this error message yet. If you know how to, please post in the comments.

It’s also possible to supply default values for the arguments. Something like.

It’s also possible to make an argument required. For a ping script we would expect an IP to be input by the user.

If we then don’t input an IP we will get this message.

Let’s try it again with an IP address.

This is the final Argparse code so far.

As a final touch let’s add that we print a message to use the -h flag if the user runs the script without any arguments. To do this we must first import sys.

If the length of sys.argv is less than two then the user didn’t enter any arguments and we print the text above. Otherwise run the main function.

Finally let’s print the arguments from the script to see what values have been stored.

This is then the output we get.

The final Argparse code here:

I hope this post has given you a good introduction into what can be done with Argparse. I’m trying to find out if there’s a better way to display the error message with Argparse in combination with using choices. The code in this post is not formatted according to PEP standards so some lines should be split in the final version of the code.

Python – Introduction to Argparse
Tagged on:         

2 thoughts on “Python – Introduction to Argparse

  • January 19, 2017 at 6:35 am
    Permalink

    Hi Daniel,

    Have you figured out the solution for the ugly error ouput yet? I haven’t, but thought that we could use a simple way to format the output like the way argparse normally shows us. Something like:

    parser.add_argument(“-t”, metavar=”ttl”, help=”ttl for icmp packets (accepted values: 1-255)”)
    args = parser.parse_args()
    if not 1 <= int(args.t) <= 255:
    print("usage: pingscript.py [-h] [-version] [-c C] [-s S] [-t T] [-w W]")
    print("pingscript.py: error: argument -t: invalid choice: %s (choose from 1-255)" % args.t)

    Reply
    • January 19, 2017 at 9:21 am
      Permalink

      I have a few options I will blog about when I find the time. I had some colleagues with much more experience in Python give me some suggestions. Stay tuned!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: