{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [ "raises-exception" ] }, "source": [ "# Dealing with errors\n", "\n", "```{attention}\n", "Finnish university students are encouraged to use the CSC Notebooks platform.
\n", "\"CSC\n", "\n", "Others can follow the lesson and fill in their student notebooks using Binder.
\n", "\"Binder\n", "```\n", "\n", "## Interpreting error messages\n", "\n", "So far in the course we have encountered a number of different types of error messages in Python, but have not really discussed how to understand what the computer is trying to tell you when you get an error message.\n", "We'll do that below.\n", "For most Python errors you will see and exception raised when the error is encountered, providing some insight into what went wrong and where to look to fix it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading error messages\n", "\n", "Let's imagine you've written the code below called to convert wind speeds from km/hr to m/s and you're dying to figure out how windy it is in [Halifax, Nova Scotia, Canada](https://www.theweathernetwork.com/ca/weather/nova-scotia/halifax) where they report wind speeds in km/hr.\n", "\n", "Unfortunately, when you run your script you observe the following:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "SyntaxError", "evalue": "EOL while scanning string literal (2231829726.py, line 4)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/2231829726.py\"\u001b[0;36m, line \u001b[0;32m4\u001b[0m\n\u001b[0;31m print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], "source": [ "wind_speed_km = 50\n", "wind_speed_ms = wind_speed_km * 1000 / 3600\n", "\n", "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's break this example down and see what the error message says.\n", "\n", "![Syntax error](img/error-message-annotated.png)\n", "*A SyntaxError, annotated*\n", "\n", "As you can see, there is quite a bit of useful information here. We have some information about script that was run to execute this code cell and which line of the cell was a problem. We also have the type of error, a `SyntaxError` in this case, as well as where it occurred on the line, and a bit more information about its meaning. The location on the line won't always be correct, but Python makes its best guess for where you should look to solve the problem. Clearly, this is handy information.\n", "\n", "Let's consider another example, where you have fixed the `SyntaxError` above and now have made a function for calculating a wind speeds in m/s.\n", "\n", "When you run this script you encounter a new and bigger error message:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for /: 'str' and 'int'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/2952723812.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mwind_speed_km\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'30'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mwind_speed_ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconvert_wind_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwind_speed_km\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/2952723812.py\u001b[0m in \u001b[0;36mconvert_wind_speed\u001b[0;34m(speed)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mconvert_wind_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mspeed\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mspeed\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m1000\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m3600\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mwind_speed_km\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'30'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mwind_speed_ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconvert_wind_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwind_speed_km\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for /: 'str' and 'int'" ] } ], "source": [ "def convert_wind_speed(speed):\n", " return speed * 1000 / 3600\n", "\n", "wind_speed_km = '30'\n", "wind_speed_ms = convert_wind_speed(wind_speed_km)\n", "\n", "print(f\"A wind speed of {wind_speed_km} km/hr is {wind_speed_ms} m/s.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case we see a `TypeError` that is part of a *traceback*, where the problem in the code arises from something other than on the line where the code was run. In this case, we have a `TypeError` where we try to divide a character string by a number, something Python cannot do. Hence, the `TypeError` indicating the data types are not compatible. That error, however, does not occur when the code is run until the point where the function is used. Thus, we see the traceback showing that not only does the error occur when the function is called, but also that the problem is in the function definition.\n", "\n", "The traceback above may look a bit scarier, but if you take your time and read through what is output, you will again find that the information is helpful in finding the problem in your code. After all, the purpose of the error message is to help the user find a problem :)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Common errors and exceptions\n", "\n", "Now that we have some idea of how to read an error message, let's have a look at a few different types of common Python exceptions that are displayed for different program errors.\n", "\n", "#### IndexErrors\n", "\n", "An `IndexError` occurs when you attempt to reference a value with an index outside the range of values.\n", "We can easily produce an `IndexError` by trying to access the value at index 5 in the following list of cities: `cities = ['Paris', 'Berlin', 'London']`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "cities = [\"Paris\", \"Berlin\", \"London\"]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "IndexError", "evalue": "list index out of range", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/3963876545.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcities\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: list index out of range" ] } ], "source": [ "cities[5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we get the rather clear error message that the index used for the list `cities` is out of the range of index values for that list." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### NameErrors\n", "\n", "A `NameError` occurs when you reference a variable that has not been defined. We can produce a `NameError` by trying `station_id = stations[1]`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "NameError", "evalue": "name 'stations' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/2799791259.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mstation_id\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstations\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'stations' is not defined" ] } ], "source": [ "station_id = stations[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this instance we receive a `NameError` because the list `stations` has not been defined, and we're thus not able to access a value in that list." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### IndentationErrors\n", "\n", "An `IndentationError` is raised whenever a code block is expected to be indented and is either not, or is indented inconsistently. Let's consider two examples below." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "IndentationError", "evalue": "unindent does not match any outer indentation level (, line 3)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m3\u001b[0m\n\u001b[0;31m print(city)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m unindent does not match any outer indentation level\n" ] } ], "source": [ "for city in cities:\n", " city = city + \" is a city in Europe\"\n", " print(city)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "IndentationError", "evalue": "expected an indented block (1297196788.py, line 2)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/1297196788.py\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m city = city + \" is a city in Europe\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" ] } ], "source": [ "for city in cities:\n", "city = city + \" is a city in Europe\"\n", "print(city)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In both of the examples above, an `IndentationError` is raised. In the first case, the indentation level is inconsistent. In case two, indentation is expected for the code below a `for` statement." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### TypeErrors\n", "\n", "A `TypeError` is raised whenever two incompatible data types are used together. For example, if we try to divide a character string by a number or add a number to a boolean variable, a `TypeError` will be raised." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for /: 'str' and 'int'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/lp/cjwc88bd3w10sg327y_4ghg0fsk7jj/T/ipykernel_68439/2693491430.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcities\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for /: 'str' and 'int'" ] } ], "source": [ "cities[0] / 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, the `TypeError` is because it is not possible to divide a character string by a number." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Other kinds of errors\n", "\n", "There are certainly [other kinds of errors and exceptions in Python](https://docs.python.org/3/tutorial/errors.html), but this list comprises those you're most likely to encounter.\n", "As you can see, knowing the name of each error can be helpful in trying to figure out what has gone wrong, and knowing what these common error types mean will save you time trying to fix your programs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### More information on errors\n", "\n", "You can find a bit more information about reading error messages on the [Software Carpentry](https://swcarpentry.github.io/python-novice-inflammation/09-errors/index.html) and [Python Software Foundation](https://docs.python.org/3/tutorial/errors.html) webpages.\n", "\n", "Also, Python 3.10 was just released in October 2021. Among the changes in this release are [improvements in the error messages](https://docs.python.org/3.10/whatsnew/3.10.html#better-error-messages) that are generated for many of the cases above. We will not be using Python 3.10 in the course, but you can feel free to test it out if you like." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 4 }