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 intosys.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 referencesys.argv
inside of theif __name__ == "__main__":
block. This will save you from a lot of headaches in the future.