BYU logo Computer Science

Best practices for using sys.argv

If you are struggling understanding what sys.argv is, check out the guide on program arguments first. This guide is meant to be a deeper dive into sys.argv and how to keep best practices in mind when using it.

Only reference sys.argv inside of the if __name__ == "__main__" block.

Without getting into too much detail, the if __name__ == "__main__" block allows python to determine whether a script is being run from the file it is in. Since this is the first block of code that is run when a script is run, it’s best practice to only reference sys.argv inside of this block.

First, let’s show a bad example of a file, bad_example.py, that references sys.argv outside of the if __name__ == "__main__" block:

bad_example.py

import sys

def main():
    print(f"This prints sys.argv[1], {sys.argv[1]}, inside of the main function!")

if __name__ == "__main__":
    print("This is the bad example!")
    main()

Now, here’s a good example, good_example.py, that only references sys.argv inside of the __main__ block. We encourage you to model your code after this example.

good_example.py

import sys

def main(argument: str):
    print(f"This prints the argument, {argument}, that was passed to the main function!")

if __name__ == "__main__":
    print("This is the good example")
    main(sys.argv[1])  # This keeps all references to sys.argv inside of the __main__ block

Running the program:

python good_example.py argument1

produces the following output:

This is the good_example!
This prints the argument, argument1, that was passed to the main function!

While the output of both the good and bad examples are nearly the same, keeping all references to sys.argv in the if __name__ == "__main__": block will make a critical difference in the future as you learn about modularization and testing. Following the better design now will help you avoid trouble later.

What happens when you don’t pass in the correct number of arguments?

A common error that occurs when dealing with sys.argv is an IndexError. This occurs when the user doesn’t provide enough arguments to sys.argv and the program tries to reference an argument that doesn’t exist.

Using the bad example above, running the program:

python bad_example.py

results in the following error message:

This is the bad example!
Traceback (most recent call last):
  File "bad_example.py", line 5, in main
    print(f"This prints sys.argv[1], {sys.argv[1]}, inside of the main function!")
                                      ~~~~~~~~^^^
IndexError: list index out of range

Here, we see that the error occured on line 5 of bad_example.py in the main function:

File "bad_example.py", line 5, in main

The tilde ~ and carrot ^ symbols point to where the error occured. In this case, the error occured when trying to reference {sys.argv[1]} inside of the print statement:

print(f"This prints sys.argv[1], {sys.argv[1]}, inside of the main function!")
                                  ~~~~~~~~^^^

Finally, the error message tells us that the error was an IndexError. This means that we tried to access an index of a list that doesn’t exist. In this case, sys.argv only has one item at sys.argv[0], "bad_example.py", so the program throws an error when it tries to access sys.argv[1].

Check the length of sys.argv before referencing it.

It is common for a program to check the number of arguments before trying to access them. If the user doesn’t provide enough arguments, the program can print a helpful message to help the user understand which arguments they need to provide.

For example, including a check for the length of sys.argv before referencing it will prevent the IndexError we saw above.

import sys

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("You didn't provide the correct number of arguments!")
    else:
        ## Access system arguments here and pass them into a main function.

In this course, the tests will always provide the correct number of arguments to your programs, so you don’t need to worry about checking the length of sys.argv in your code.

What’s the best way to handle program arguments?

A common practice for handling program arguments would be to use argparse instead of sys to handle program arguments. Instead of creating a simple list of arguments as sys.argv does, argparse allows you to name arguments, specify their type, and provide a help message for each argument.

Here is good_example.py rewritten to use argparse instead of sys.argv:

import argparse


def main(argument: str):
    print(f"This prints the argument, {argument}, that was passed to the main function!")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-a",  # A short name for the argument
        "--argument",  # A long name for the argument
        help="The primary input to the program; this will be printed in a helpful statement",
        type=str,  # The type of the argument, in this case a string
        required=True,  # Whether the argument is required or not
    )
    args = parser.parse_args()
    main(**vars(args))  # This passes any number of arguments from the parser to main

To run this program, we first provide the name of the argument, then the argument itself:

python good_example.py -a argument1

If we don’t provide the argument:

python good_example.py

we get this helpful message that tells us to provide the required argument:

usage: good_example.py [-h] -a ARGUMENT
good_example.py: error: the following arguments are required: -a/--argument

And if we add the -h flag, we get a helpful message about the program and its arguments:

python good_example.py -h

Produces the following output:

usage: good_example.py [-h] -a ARGUMENT

options:
  -h, --help            show this help message and exit
  -a ARGUMENT, --argument ARGUMENT
                        The primary input to the program; this will be printed in a helpful statement

As you can see, argparse is very helpful for handling program arguments, as it adds help messages for people who are unfamiliar with your program and allows you to specify the type of each argument.

In this class, you are not expected to use argparse for handling program arguments, but if you would like to learn more about it, check out the official documentation.

In conclusion, remember to always include an if __name__ == "__main__": block in your scripts and only reference sys.argv inside of the if __name__ == "__main__": block. This will save you from a lot of headaches in the future.