In this lesson, we're going to talk about errors and exceptions.
I'll show you what happens when a program encounters
different types of errors and how you can write code to handle these situations.
To begin, I've created a brand new project called day-30
and I've created my main.py file. Now notice how inside my project,
there is only one code file at the moment. There is no data files,
no text files, nothing. In this situation
if I was to try and open up a particular file that doesn't exist and I tried to
read it, what do you think would happen?
So we're going to use our normal syntax with open,
and let's just make up a file name. So a_file.txt.
And I'm going to save that as file
and I'm going to take this file and try to read from it. Right now
if I run this code, then this is what happens.
I get a trace back and I get a error
which is the file not found error, which kind of makes sense
given that there really is no such file existing inside our folder where we're
telling it to search. That's not great.
And if this happens somewhere within our actual program, so here
I've got the password manager completed project from yesterday.
If in this case, I made a typo in this word
so instead of data.txt, I wrote daata.txt.
And instead of the append mode, I had the read mode. Well,
in this case, what would happen is exactly the same thing.
So I could have a website, have a password,
but as soon as I hit add, then we get our file
not found error. And notice how as soon as that error happens,
it actually doesn't continue because on the next few lines,
we've actually got these two lines of code
which deletes the entry inside the website entry and also the password entry.
But those lines of code are not being carried out because it's hit a stumbling
block.
It has not found the file that we're trying to open and we're trying to read.
So I'm going to restore the code to how it was previously,
but this is a good point to start thinking about errors because we've come
across a lot of errors, right? Including errors like key error.
So for example,
if we have a dictionary that has just one key-value pair and we somehow try to
get a value from that dictionary
by tapping into a key that doesn't exist,
then when we run this code, we will get a key error, right?
It can't actually pick out the value from this dictionary because this key that
we provided does not exist in that dictionary. Now,
some of the other popular ones that you've seen are the index error.
So in this case
we have a list and we basically are trying to get hold of an item from this list
at an index that doesn't exist. So remember the index starts at 0, 1,
2, and 3. There is nothing at 3. So again, when I run this,
we get an error and this is an index error.
And the final error that we've probably been used to seeing is the type error
where we're trying to do something with a particular piece of data,
but we cannot do that thing with a particular data type.
Let's say we have a piece of text abc
and we try to print it this text plus the number five,
so a string plus an integer. And again,
when I hit run it, again we get a type error.
All of these types of errors we've actually come across already and
all that we've done so far is
we've just used it as an indicator to tell us, wait a minute,
something's not quite right. We got to go and fix our code.
But life doesn't really work out
so neatly most of the time. In a lot of cases, it actually follows Morphy's law
which states that anything that can go wrong probably will, eventually at some
point, go wrong.
So we have to plan for these eventualities just as, you know,
your car is probably not going to break down,
but you need breakdown cover just in case that it does happen. In programming
what we can do is we can catch these exceptions. When something goes wrong
and in that moment we catch that exception,
then it doesn't have to fail catastrophically.
We can actually fail more gracefully or we can decide that something else should
happen.
Here's what the code looks like when we're dealing with these exceptions.
We have try, except, else and finally.
These are the four keywords that are really important when it comes to handling
exceptions.
Now the first keyword try comes for a block of code where you're executing
something that might cause an exception.
So basically you're trying to execute a piece of line. In most cases,
it probably will work, but sometimes it just might not.
Now the next step is to define the except block.
So this is the block of code that you want the computer to execute
if there were more was an exception.
If something went catastrophically wrong and it was not the way that you
expected it to go, then carry out this piece of code.
Now the else keyword allows you to find some code to execute
if there were no exceptions. If you tried this thing that might fail
but actually it didn't fail. You succeeded and there were no problems.
Well then, in this case, you're going to do whatever is inside the else block.
And then finally, we have the finally keyword
which basically is just the block of code to carry out
no matter what happens. If this thing that you tried failed
or if it succeeded, Honeybadger, I mean, finally doesn't actually care.
So no matter what happens with trying this line of code,
this finally block is always going to be executed
and it's usually used for cleaning things up or tidying things up at the end of
some sort of code execution. Let's take our file not found exception
and let's see how we can make this a lot safer by catching that exception.
The thing that we're going to try is to open up this file. Instead of using the
with format,
I'm actually just gonna straight up create a file and set it to open this
particular file path. So a_file.txt.
Now this is the line of code that can cause an error and it will cause an error
in our case because we don't have a file called a_file.txt.
So this line of code is going to go inside a try block.
So let's indent that. So this is the line of code that we're going to try. Now,
the next thing we'll define is the except block. Basically,
once we've tried running this line of code,
and if there was an exception that was thrown when we were running it like the
type error or in this case, it would be a file not found error,
well then, in that case, we're going to do something different.
We're going to let's say print, there was an error.
So now let's run this code and you can see straight away,
there was an error because this file doesn't exist.
So this failed and therefore this line of code was executed.
It's almost a little bit like we have an if statement and the
if statement is checking to see if something fails, well,
in that case, this is where it looks to see what it should do next.
Now just printing there was an error is kind of pointless.
What we actually want to do is to make sure that we don't actually fail so that
whatever happens, we succeed. So if there is a good alternative,
then that's what we're going to put inside the except block.
What we're going to do instead
if this file doesn't exist and we can't open it is we're just simply going to
create it. To create a new file,
remember that you can open up the file
and you can open it using the write mode because when you're inside write mode,
then what this open method is going to do is it's going to try and find this
file and open it. But if it doesn't exist,
it's just going to create it. Right now if I run this code again,
I want you to watch inside the day-30 folder because as soon as I hit run,
you'll notice that a_file.txt gets created because we tried opening it.
It didn't exist.
So it went into the except block and it actually just went ahead and created this
file from scratch.
Now this file is of course completely empty because we haven't told it to do
anything, but we could, in fact, get our file and write something to it.
We can put all of that inside the except block if we want to. Now,
one of the things that's giving us a warning here with the except keyword is if
you hover over it, it tells you that this is too broad an exception clause.
And according to the PEP 8 recommendations,
it tells us you should never use a bare except
and the reason for this is because when you have an except clause,
then it's actually going to ignore all errors. For example,
if inside here
let's say I decided to do something that was also going to create an error,
but not the same kind of error.
So now I have a dictionary with a key and a value,
but let's say I tried to print some sort of value from that dictionary
and I use a non-existent key.
So you can see that this dictionary does not have a key that is named this
so this line is going to actually give us a key error.
But now, notice what happens when I hit run.
Absolutely nothing. I don't get any errors.
And the reason for this is because inside the try block
it's first trying to open up this file and indeed that file exists.
So it moves on to the next line. It creates a dictionary called a_dictionary
and that it tries to get hold of this value with this key
from that dictionary.
Now this line actually fails and creates an exception,
but that exception is caught right here
and it simply tells it to go ahead and create a file called a_file.txt
which it will go ahead and do. So this is not what we want at all.
Instead,
we want our exception to catch a specific situation.
We want to say that in the exception that we have a file not found error,
then this is what we want to do. And now if I run the code again,
you can see when now getting that key error
and it's telling us that this thing worked, so it didn't generate a file
not found error.
But the next thing that it tried when it tried to print this value,
it got a key error.
So we can actually have multiple exceptions. Instead of just except file
not found I can also say except key error and in this case,
I'm just going to print that key does not exist. Now, if I hit run,
I get no errors, but I do catch that exception
and I have this print statement executing. In addition to simply catching an
exception using
except, you can also get hold of the error message that would have normally
printed had we not had the exception called.
Normally, you would get key error and it would give you a message that tells you
which key was the problem.
If we catch our exception and we still want to get hold of that error message,
then we can say
except key error as error message.
And this means we can get hold of the error message that was generated from this
exception if it does occur. Instead of saying that key does not exist,
we can now take this as an f-string and we can say, the key, and we pass in the
error message right here.
So now I hit run again and it tells me the key 'sdfsdf' does not exist,
which is a lot more useful than simply saying the key does not exist.
So you can try, you can catch exceptions, you can catch other exceptions,
you can also get hold of the error message and use it when you catch the
exception. Now, the next keyword we mentioned was the else keyword
and this block of code is going to execute when the thing that you're trying all
succeeds. So if it managed to open up the file,
it managed to print this item from the dictionary
and there were no exceptions that were thrown from this block of code,
then it's going to jump to the else block. So what else do you want to do?
Well, maybe I actually want to read from this file,
so file.read,
and we save this as our content.
And then we go ahead and print our content. Now,
remember, if this file doesn't actually exist,
this else block is never going to be triggered.
So if I go ahead and simply delete this file
and I hit run,
you can see that this else block does not occur because it tried to do this,
it failed
and so it ended up jumping into the except block and it generated that file.
But now that that file has been created, the next time I hit run,
then this line of code is going to succeed and now it catches the next error in
that try block. So if I change this to a key
that it actually will recognize, then that next line also succeeds
and finally it gets to this block and it actually prints out the thing that's
inside a_file.txt which is just the word something.
So we've now got try, except, else,
and the last thing I want to show you is the keyword finally.
So this finally is basically some code that's gonna run no matter what happens.
And in our case,
the most appropriate thing to do here is actually to close down the file because
we're not using the with keyword
so our file would actually stay open if we actually had an exception. And I'm
going to go ahead and print
File was closed.
Now our code basically will open up this file
and no matter if it succeeded or if it failed,
it's going to close down that file so that we don't end up with an open file
that we're not doing anything with. So finally is not often used,
but sometimes it can be useful when you want some code to execute
no matter if the code you are trying is succeeded or failed. In the next lesson,
I'll show you how you can raise your own exceptions.