{"id":"errors-kinds","slug":"kinds-of-errors","title":"Different kinds of errors","text":"Each kind of error has its own exception, and its name tells what went wrong. Here are the ones you'll meet most often, in the Python interpreter:\n\n```pycon\n>>> int(\"3.5\")\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nValueError: invalid literal for int() with base 10: '3.5'\n>>> \"Age: \" + 30\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nTypeError: can only concatenate str (not \"int\") to str\n>>> [1, 2, 3][5]\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nIndexError: list index out of range\n>>> {\"a\": 1}[\"b\"]\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nKeyError: 'b'\n>>> 10 / 0\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nZeroDivisionError: division by zero\n```\n\n- **ValueError**: a value of the right type, but with contents that don't work. `int(\"3.5\")` gets a string, as it should, but not one that's a whole number; `float(\"twelve\")` fails the same way.\n- **TypeError**: a value of the wrong type for what the code does with it, such as adding a number to a string, or repeating a string by a string, `\"ab\" * \"3\"`.\n- **IndexError**: an index that a list or a string doesn't have.\n- **KeyError**: a key that a dictionary doesn't have.\n- **ZeroDivisionError**: dividing by zero, with `/`, `//` or `%`. The zero usually comes from data: a number the user typed, or one read from a file.\n- **FileNotFoundError**: opening a file that doesn't exist. (Its cousin **PermissionError** comes when the file is there but the program may not use it, such as a file that belongs to another user of the computer.)\n\n### Several except blocks\n\nOne `try` can have several `except` blocks, one for each kind of error it is ready for. Python checks them in order and runs the first one that matches. Only one of them runs:\n\n```python\ncolors = [\"red\", \"green\", \"blue\"]\ntry:\n    index = int(input(\"Which color (0-2)? \"))\n    print(\"You picked\", colors[index])\nexcept ValueError:\n    print(\"That's not a number.\")\nexcept IndexError:\n    print(\"There's no color with that number.\")\n```\n\n```input\n5\n```\n\n```output\nWhich color (0-2)? 5\nThere's no color with that number.\n```\n\nA missing file is handled the same way. Print a message of your own that says what happened:\n\n```python\ntry:\n    with open(\"settings.txt\") as file:\n        print(file.read())\nexcept FileNotFoundError:\n    print(\"No settings.txt, using the default settings.\")\n```\n\n```output\nNo settings.txt, using the default settings.\n```\n\n### A bare except hides mistakes\n\n`except:` without a name catches every exception there is. That's tempting, but it can't tell the user's mistakes from the program's own. This program has a bug: the days are numbered from 1, but list indexes start at 0, so it should look up `days[number - 1]`:\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\ntry:\n    number = int(input(\"Day number (1-7): \"))\n    print(\"That's\", days[number])\nexcept:\n    print(\"Please type a number from 1 to 7.\")\n```\n\n```input\n7\n```\n\n```output\nDay number (1-7): 7\nPlease type a number from 1 to 7.\n```\n\nThe user did type a number from 1 to 7. `days[7]` raised an `IndexError`, and the bare `except` turned the program's bug into a message that blames the user. Name the exception you expect from the user instead, here `ValueError`, and the bug shows itself:\n\n```python\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\ntry:\n    number = int(input(\"Day number (1-7): \"))\n    print(\"That's\", days[number])\nexcept ValueError:\n    print(\"Please type a number from 1 to 7.\")\n```\n\n```input\n7\n```\n\n```output\nDay number (1-7): 7\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 4, in <module>\n    print(\"That's\", days[number])\n                    ~~~~^^^^^^^^\nIndexError: list index out of range\n```\n\nThe traceback names the line with the wrong index. So catch only the exceptions you expect, and let real bugs show their traceback.\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"values = [\"10\", \"x\", \"0\", \"4\"]\nfor value in values:\n    try:\n        print(20 // int(value))\n    except ValueError:\n        print(\"not a number\")\n    except ZeroDivisionError:\n        print(\"zero\")\n","solution":"values = [\"10\", \"x\", \"0\", \"4\"]\nfor value in values:\n    try:\n        print(20 // int(value))\n    except ValueError:\n        print(\"not a number\")\n    except ZeroDivisionError:\n        print(\"zero\")\n","hints":["Go through the list one value at a time. Each round has its own `try`, so one error doesn't stop the loop.","`int(\"x\")` raises a `ValueError`, and `20 // 0` a `ZeroDivisionError`. The other two values divide normally."],"tests":[{"name":"Output","stdin":"","expect":"2\nnot a number\nzero\n5","hidden":false}],"require":[],"forbid":[],"lines":["2","not a number","zero","5"],"more":[{"task":"Write a program that asks for the name of a file of numbers, one per line, and prints their average. It must not crash, whatever the file:\n\n- a file that doesn't exist: print `No file called ` and the name;\n- a line that isn't a number: print `The file has a line that isn't a number`;\n- an empty file (no numbers, so the average divides by zero): print `The file is empty`.\n\nWith this `weights.txt`\n\n```text\n70\n73\n```\n\na run looks like this:\n\n```\nFile: weights.txt\nAverage: 71.5\n```\n","kind":"code","seed":"filename = input(\"File: \")\n","solution":"filename = input(\"File: \")\ntry:\n    numbers = []\n    with open(filename) as file:\n        for line in file:\n            numbers.append(float(line))\n    print(\"Average:\", sum(numbers) / len(numbers))\nexcept FileNotFoundError:\n    print(\"No file called\", filename)\nexcept ValueError:\n    print(\"The file has a line that isn't a number\")\nexcept ZeroDivisionError:\n    print(\"The file is empty\")\n","hints":["Read the numbers into a list with `float`, and print `sum(numbers) / len(numbers)`. All of that goes in one `try` block.","Add one `except` block for each error: `FileNotFoundError`, `ValueError` and `ZeroDivisionError`.","Print your own message in each block. Don't print the exception itself."],"tests":[{"name":"Test 1","stdin":"weights.txt\n","files":{"weights.txt":"70\n73\n"},"expect":"File: weights.txt\nAverage: 71.5","hidden":false},{"name":"Test 2","stdin":"weight.txt\n","files":{"weights.txt":"70\n73\n"},"expect":"File: weight.txt\nNo file called weight.txt","hidden":true},{"name":"Test 3","stdin":"data.txt\n","files":{"data.txt":"5\nfive\n6\n"},"expect":"File: data.txt\nThe file has a line that isn't a number","hidden":true},{"name":"Test 4","stdin":"empty.txt\n","files":{"empty.txt":""},"expect":"File: empty.txt\nThe file is empty","hidden":true},{"name":"Test 5","stdin":"temps.txt\n","files":{"temps.txt":"-2.5\n0\n4\n"},"expect":"File: temps.txt\nAverage: 0.5","hidden":true}],"require":[],"forbid":[]},{"task":"Write a function `to_minutes(text)` that turns a time like `08:30` into the number of minutes since midnight (here 510). If the text isn't a valid time, it returns -1 instead. Invalid texts include `8h30` (no colon, so the split gives only one piece), `ab:30` (not numbers), `24:00` and `12:60` (hours must be 0 to 23 and minutes 0 to 59).\n\nLet `int` and the index raise their exceptions, and catch them, instead of checking every character yourself.\n\nFor example, `to_minutes(\"08:30\")` returns `510` and `to_minutes(\"8h30\")` returns `-1`.\n","kind":"code","seed":"def to_minutes(text):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(to_minutes(\"08:30\"))\n    print(to_minutes(\"8h30\"))\n","solution":"def to_minutes(text):\n    try:\n        parts = text.split(\":\")\n        hours = int(parts[0])\n        minutes = int(parts[1])\n    except ValueError:\n        return -1\n    except IndexError:\n        return -1\n    if hours < 0 or hours > 23 or minutes < 0 or minutes > 59:\n        return -1\n    return hours * 60 + minutes\n\n\nif __name__ == \"__main__\":\n    print(to_minutes(\"08:30\"))\n    print(to_minutes(\"8h30\"))\n","hints":["`text.split(\":\")` gives the hours and the minutes as strings. Convert both with `int` inside a `try` block.","`8h30` splits into one piece, so `parts[1]` raises an `IndexError`. `ab` raises a `ValueError` in `int`. Return -1 from both `except` blocks.","After the `try`, check the ranges of the hours and the minutes, and return `hours * 60 + minutes` when they're fine."],"tests":[{"name":"to_minutes(\"08:30\")","stdin":"","call":"to_minutes(\"08:30\")\n","expect":"510","hidden":false,"returns":true},{"name":"to_minutes(\"8h30\")","stdin":"","call":"to_minutes(\"8h30\")\n","expect":"-1","hidden":false,"returns":true},{"name":"to_minutes(\"23:59\")","stdin":"","call":"to_minutes(\"23:59\")\n","expect":"1439","hidden":true,"returns":true},{"name":"to_minutes(\"ab:30\")","stdin":"","call":"to_minutes(\"ab:30\")\n","expect":"-1","hidden":true,"returns":true},{"name":"to_minutes(\"24:00\")","stdin":"","call":"to_minutes(\"24:00\")\n","expect":"-1","hidden":true,"returns":true},{"name":"to_minutes(\"12:60\")","stdin":"","call":"to_minutes(\"12:60\")\n","expect":"-1","hidden":true,"returns":true},{"name":"to_minutes(\"00:00\")","stdin":"","call":"to_minutes(\"00:00\")\n","expect":"0","hidden":true,"returns":true},{"name":"to_minutes(\"7:5\")","stdin":"","call":"to_minutes(\"7:5\")\n","expect":"425","hidden":true,"returns":true}],"require":[],"forbid":[]}]}