Basic Error Handling
00:00
In this lesson, I’ll show you how to do some basic error handling as you use the sys.argv
list to process command line arguments.
00:10
Let’s take a look at some actual Python scripts that make use of sys.argv
, and I’m going to try to point out possible problems that might arise and ways to deal with them—though, of course, I can’t promise that I’ll identify every possible issue you’ll ever run into with sys.argv
. I’m still running into problems when I use it in my own code, and I’ve been working with this for ages.
00:31
So, taking a look at this argv.py
script, this is very simple. All it does is take in arguments and then print them back out.
00:41 And it just prints them back out using this new debugging specifier, which is new in Python 3.8. If you don’t have 3.8, you can just delete this equal sign and it will actually run exactly the same way. It just won’t print out the name of the variable as well as its value.
00:58
It’ll just print out the value alone. But all of that is notwithstanding the actual thing that it’s doing here, which is just pointing out that the zeroth index of sys.argv
always contains the filename that’s running, whereas the rest always contains the arguments. So of course, if you run this with no arguments, the rest—the actual args—will just be an empty list.
01:20
I’ll show you as I move over into reverse.py
how that can be a problem.
01:26
This reverse.py
script just takes in one argument, accessed via sys.argv
at the
01:33
first index, and then it prints out the reversed version of that argument using Python’s list slicing operator. So I’ll show you how it works. I’ll say python reverse.py
and then I’ll put in my name, Liam
.
01:47
And as you can see, it outputs maiL
, which is my name in reverse. However, being the conscientious Python programmers that you are, you have certainly realized that this doesn’t do any error handling at all.
01:59
And of course, whenever you assume there’s at least one element in a list, that’s a recipe for disaster. So, if I call this with no arguments whatsoever, as you might guess I get an IndexError: list index out of range
because there are zero elements in that list.
02:14
So, one way to handle this is by using a try
and except
block. I can simply say try
this, and then I can say except IndexError
.
02:24
And it’s always good practice to write the specific kind of error that you’re excepting, rather than just a more general error. Then, I can print out "Expected
1 argument, got 0"
and raise SystemExit
.
02:42
And this will work just fine, on this case. It will at least give your user a little bit more verbose in output so that they can know what’s wrong here. Otherwise, this IndexError
doesn’t say why something went wrong.
02:53 It just says that something did in fact go wrong.
02:57 Now, there’s another problem with this too, which you might have noticed, which is that there’s a little bit of an unexpected behavior if I enter in more than one argument.
03:04
So, say I put in Real Python
. All I actually get out is the reverse of Real
, and that’s because these two words are being interpreted as two separate arguments. Now, if that’s the behavior that you want—if you want to only ever reverse the first argument without dealing with any of the others—then this might just be fine.
03:25
But you might also want to put in some conditional logic in here to check whether there’s more than one argument and just say, huh, maybe the user thinks that they’re entering, for example, a string, like "Real Python"
in quotes, but in fact, they’re actually entering multiple arguments.
03:42
So, that might be something to watch for, and it’s not necessarily an error, but it can be a problem in the logic of your program. Now, one more thing to notice is just that I wrapped in quotes this "Real Python"
in order to reverse the whole thing. I can also wrap in single quotes…
03:59 and I can even not use any quotes at all and I can use the backslash to escape this space character, which means, essentially, don’t treat it as a whitespace character—treat it as just part of this string.
04:12 So, those are the things you can do to make multiple arguments into one, essentially, or into one string argument. Otherwise, any whitespace will delimit a new argument just based on how Bash shells work, and that’s the same in Windows with PowerShell and things like that.
04:27
So, that’s something really important to note. Next up, I’ll show you how the global nature of sys.argv
can cause some issues.
04:36
argv_pop.py
just uses the sys
module to print out the arguments to the program before and after popping
04:45
from the sys.argv
list. And of course, .pop()
just gets and removes the last element of a list, so this is a case of directly modifying sys.argv
.
04:57
Now, this can be troublesome because when I run this with some arguments—so I can just say Some Stuff
, here—as you’ll notice, before I pop from this list, sys.argv
has one set of contents, and after, it has a different one.
05:12
This can definitely be a problem when you have a large codebase and a lot of programmers working on one project, because sys.argv
is always global to your Python invocation. So, if multiple different programmers want to access it, then they might start to run into issues whereby if one programmer modifies it, the next doesn’t actually get the real arguments, but some kind of truncated or modified version of it.
05:36
So, best practice is to say something like args = sys.argv
at 1
, on—
05:43
and you could also do the whole, you know, you could just copy sys.argv
if you also needed the filename, but often you really just need the arguments. And then, when you modify things, you should actually just modify the version that you’ve copied over.
05:56
So of course, when I run this, sys.argv
always remains constant. This is a very powerful programming practice because it lets anyone anywhere in your code have access to the original system arguments, but you can also just pass around whatever version of this you want to pass around, as long as everyone in your codebase is on the same page.
06:16
So, that’s an important thing to remember: that sys.argv
is global and any modifications to it will be propagated throughout your Python invocation.
06:23 Finally, I want to show you how to work with files as command line input.
06:29
This program cat.py
uses the sys
module to get an access to an argument and then open that argument in read mode as a file and then prints the contents of the file.
06:41
This is an attempt to mimic the Linux utility cat
in a very basic form. The Linux utility cat
just prints out the contents of some number of files. To demonstrate this, I’ll use some other Linux command line utilities to make a file—so, I’ll say touch test.txt
—and then I’ll use echo
to put some actual text into test.txt
.
07:03
So, I’ll say "Here is some test text"
, and then I can put that into test.txt
using terminal operators. And then finally, I’ll use the Linux utility cat
to cat test.txt
and show the
07:19
contents of the file. Now, let me show you the Python version of this, where I say python cat.py
, and then I can just put in test.txt
.
07:28
As you can see, it does almost the same thing. It adds a newline because that’s how Python’s print()
works, but other than that it’s exactly the same behavior here. Of course, cat
is much more powerful.
07:39
It can do any number of files and it can give you some interesting information about them too, but this is just a basic version. But of course, the problem here is that if I run this on some file that doesn’t exist, right—doesnotexist.txt
—then I’ll get a FileNotFoundError
. And again, this is an issue because your user, if they try to use this, will know that there was a problem and they’ll know where it occurred, but they might not know why there was a problem, right?
08:06
And so what you can do here to fix this is, as always, you can wrap this in a try
and except
block.
08:15
So, you can say try
this stuff—that’s there—and then except FileNotFoundError
. And then you could print a useful message. I’ll use an f-string to make it a little more informative.
08:29
f"The file {arg} does not exist,
try a different file"
. Something like that. And then, as always, raise SystemExit
, here.
08:41
And now, you can see if I do the same call again, then it tells me a little bit more helpfully, "The file doesnotexist.txt does not exist, try a different file"
. Of course, in this case, you should be able to guess from the filename that it doesn’t exist, but in other cases, that won’t be so easy. Now, as an exercise to you, I want you to fix this part of the code, which is the same as what we used in reverse.py
, and so you can use similar strategies to enforce the fact that this takes just one argument at this moment. So, if I call this with no arguments, again, I’ll get an error.
09:16
Another interesting thing that you could do would be to try to mimic the behavior of cat
a little bit more closely and print out any number of arguments, or any number of file contents passed in as arguments.
09:30
You now know how to handle a lot of the common errors that you might find when you work with sys.argv
in Python. In the next lesson, I’m going to pivot a little bit and show you a little bit more about how Python works as an actual command line interface itself, because there’s a lot more to it than just saying python
and then a Python file.
Become a Member to join the conversation.
andresfmesad on Nov. 5, 2021
Here is my solution: