Python - Try & Except
Author(s) | Helena Rasche Donny Vrins Bazante Sanders |
Reviewers |
OverviewQuestions:Objectives:
How do I try to execute code, knowing it might fail?
What are some situations where this is important?
How can I write my own exceptions.
Requirements:
catch an exception
raise your own exception
Time estimation: 20 minutesLevel: Introductory IntroductorySupporting Materials:Published: Apr 25, 2022Last modification: Feb 13, 2023License: Tutorial Content is licensed under Creative Commons Attribution 4.0 International License. The GTN Framework is licensed under MITpurl PURL: https://gxy.io/GTN:T00086version Revision: 3
Best viewed in a Jupyter NotebookThis tutorial is best viewed in a Jupyter notebook! You can load this notebook one of the following ways
Run on the GTN with JupyterLite (in-browser computations)
Launching the notebook in Jupyter in Galaxy
- Instructions to Launch JupyterLab
- Open a Terminal in JupyterLab with File -> New -> Terminal
- Run
wget https://training.galaxyproject.org/training-material/topics/data-science/tutorials/python-exceptions/data-science-python-exceptions.ipynb
- Select the notebook that appears in the list of files on the left.
Downloading the notebook
- Right click one of these links: Jupyter Notebook (With Solutions), Jupyter Notebook (Without Solutions)
- Save Link As..
Try/except are a construct in Python used to catch a potential exception. Sometimes things go wrong in your code! Or in someone else’s code in a module. Sometimes some errors might be expected like when you try and read a user supplied file, maybe it isn’t available because they’ve specified the wrong path.
AgendaIn this tutorial, we will cover:
Raise
When you’re writing code, sometimes there are errors you might need to handle:
- If you’re calculating the mean of a set of numbers, and the list is empty. You don’t want to divide by
len(numbers)
and trigger a Zero Division Error. - If you’re opening a file, there maybe a chance that the file is not found.
Using try/excepts allow you to:
- provide friendlier and more useful error messages
- handle expected error cases
For instance, returning to the mean()
function example, what should mean([])
return for a hypothetical mean function that calculates the average of a list of numbers. Should it return 0? It probably should return an error. Let’s look at some example code:
def mean(numbers):
return sum(numbers) / len(numbers)
mean([1, 2, 3])
mean([])
This raises a ZeroDivisionError
but we can make this a more friendly error message by raising our own exception.
def mean(numbers):
if not numbers:
raise ValueError("You are calculating the mean of an empty list, which is not possible.")
return sum(numbers) / len(numbers)
mean([1, 2, 3])
mean([])
There are loads of different types of exception codes! The python documentation has a large list of exceptions and some descriptions for when or why those exceptions might be raised.
Now we get a much more useful error message from the function! Using raise
is especially important for library authors, people writing python modules that we all use. If they provide useful error messages, it helps you as an end user understand what’s happening.
Try & Except
Let’s look at how you would handle one of these exceptions, we’ll continue with the mean()
example above.
numbers = []
try:
result = mean(numbers)
print(f"Result is {result}")
except ValueError:
print("We cannot calculate the mean")
Here we use try:
to setup a new block, and this code is tried, Python attempts to execute it. Below are one or more except:
blocks which catch specific errors. Here we have specifically said we know a ValueError can happen, and decided to handle it.
Or for another example, accessing a user supplied file. Oftentimes users will call your program and supply a non-existent, or inacessible file. Here you can use multiple except
blocks to catch all of those potential errors.
user_supplied_file = 'does-not-exist.txt'
try:
open(user_supplied_file, 'r')
except FileNotFoundError:
print(f"The path you supplied ({user_supplied_file}) doesn't exist, please double check it!")
except PermissionError:
print(f"You supplied a valid file, but it is unreadable. Try changing it's permissions with `chmod +r {user_supplied_file}`")
Failing to open a file raises a FileNotFoundError
which indicates the file isn’t available, and PermissionError
indicates that a file is unreadable. However in practice, sometimes you’ll see something like this:
# Bad!
try:
doSomething()
except:
print("error")
This is called a bare exception, and will catch any exception, compared with except ValueError
which only catches value errors. People consider this generally a bad idea, termed code smell. (Because it smells (appears) bad!)
Finally
The last portion of the try:
/except:
block is finally:
, a third block which lets us do cleanup. It’s often very nice to your users that if your program fails halfway through, that you cleanup after yourself.
import os
try:
with open('gene_query.fa', 'w') as handle:
handle.write(">some fasta sequence we want to search against a database")
# runQuery('gene_query.fa')
# But here we have an error! Something goes wrong!
1 / 0
except:
# And now our results are invalid.
print("Something went wrong! Oh no! Check your inputs.")
finally:
# So we should cleanup this temporary file we created, so it doesn't cause
# problems or distract the user from the results file.
# This function will delete a file from your computer:
os.remove('gene_query.fa')
Fallback
Sometimes we can use try
/except
to have a fallback option. Consider the pseudocode below:
try:
runBlast()
except BlastNotAvailable:
try:
runBLAT()
except BLATnotAvailable:
print("Neither Blast nor BLAT were available.")
Sometimes you have a fallback option, some other tool you can use in its place. When that’s possible, you can use try
and except
to handle those cases and work around potential issues. But this isn’t always the case, sometimes you just need to print your error message and stop executing code.