{"id":"errors-raise","slug":"raise","title":"Raising errors yourself","text":"Your own code can raise an exception too, with the **`raise`** statement. Causing errors on purpose may sound odd, but it's the clearest way for a function to say \"I can't do this with these arguments\". A function that only prints an error message is easy to miss, especially when its result goes on to other code, and one that returns a special value, such as -1, risks having that value used as if it were a real result. An exception can't be missed: it stops the program unless the caller handles it.\n\n```python\ndef hours_to_minutes(hours):\n    if hours < 0:\n        raise ValueError(f\"hours can't be negative: {hours}\")\n    return hours * 60\n\n\nprint(hours_to_minutes(2))\nprint(hours_to_minutes(-3))\n```\n\n```output\n120\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 8, in <module>\n    print(hours_to_minutes(-3))\n          ~~~~~~~~~~~~~~~~^^^^\n  File \"main.py\", line 3, in hours_to_minutes\n    raise ValueError(f\"hours can't be negative: {hours}\")\nValueError: hours can't be negative: -3\n```\n\nAfter `raise` comes the exception: its kind, with a message in parentheses. `ValueError` is the usual choice for a value that isn't acceptable, and `TypeError` for a value of the wrong type. The message is shown in the traceback, so make it say what was wrong.\n\n### Catching your own exceptions\n\nThe caller handles a raised exception like any other. With `as`, the `except` line also gives the exception a name, and printing it prints its message:\n\n```python\ndef booking(guests, nights):\n    if guests < 1 or guests > 6:\n        raise ValueError(f\"a room takes 1 to 6 guests, not {guests}\")\n    if nights < 1:\n        raise ValueError(f\"a stay is at least one night, not {nights}\")\n    return (guests, nights)\n\n\ntry:\n    print(\"Booked:\", booking(2, 3))\n    print(\"Booked:\", booking(9, 1))\nexcept ValueError as error:\n    print(\"Can't book:\", error)\n```\n\n```output\nBooked: (2, 3)\nCan't book: a room takes 1 to 6 guests, not 9\n```\n\nThis is a good division of work. The function checks its arguments and raises an exception when they're wrong; it doesn't need to know who called it or how to talk to the user. The code that talks to the user catches the exception and decides what to say.\n","task":"Fill in the blanks so that `set_volume(level)` raises a `ValueError` with the message `volume must be 0 to 10` when the level is outside 0 to 10, and otherwise returns the level. This code\n\n```python\nprint(set_volume(7))\ntry:\n    set_volume(11)\nexcept ValueError as error:\n    print(\"Error:\", error)\n```\n\nprints:\n\n```\n7\nError: volume must be 0 to 10\n```\n","kind":"fill","seed":"def set_volume(level):\n    if level < 0 or level > 10:\n        [[raise]] [[ValueError]](\"volume must be 0 to 10\")\n    return level\n\n\nif __name__ == \"__main__\":\n    print(set_volume(7))\n","solution":"def set_volume(level):\n    if level < 0 or level > 10:\n        raise ValueError(\"volume must be 0 to 10\")\n    return level\n\n\nif __name__ == \"__main__\":\n    print(set_volume(7))\n","hints":["The keyword that causes an exception comes first, then the kind of exception, with the message in parentheses.","The line is `raise ValueError(\"volume must be 0 to 10\")`."],"tests":[{"name":"Test 1","stdin":"","call":"print(set_volume(7))\ntry:\n    set_volume(11)\nexcept ValueError as error:\n    print(\"Error:\", error)\n","expect":"7\nError: volume must be 0 to 10","hidden":false},{"name":"set_volume(0)","stdin":"","call":"set_volume(0)\n","expect":"0","hidden":true,"returns":true},{"name":"Test 3","stdin":"","call":"try:\n    set_volume(-1)\nexcept ValueError as error:\n    print(\"Error:\", error)\n","expect":"Error: volume must be 0 to 10","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Write a function `withdraw(balance, amount)` that returns the balance left after taking `amount` out of an account. It raises a `ValueError`:\n\n- with the message `amount must be positive` if the amount is 0 or less;\n- with the message `not enough money` if the amount is more than the balance.\n\nFor example, `withdraw(100, 30)` returns `70`, and `withdraw(100, 130)` raises a `ValueError` whose message is `not enough money`.\n","kind":"code","seed":"def withdraw(balance, amount):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(withdraw(100, 30))\n","solution":"def withdraw(balance, amount):\n    if amount <= 0:\n        raise ValueError(\"amount must be positive\")\n    if amount > balance:\n        raise ValueError(\"not enough money\")\n    return balance - amount\n\n\nif __name__ == \"__main__\":\n    print(withdraw(100, 30))\n","hints":["Check the two problems first, each with its own `if`, and return `balance - amount` at the end.","Each `if` raises its error: `raise ValueError(\"not enough money\")`."],"tests":[{"name":"withdraw(100, 30)","stdin":"","call":"withdraw(100, 30)\n","expect":"70","hidden":false,"returns":true},{"name":"withdraw(100, 130)","stdin":"","call":"try:\n    withdraw(100, 130)\nexcept ValueError as error:\n    print(\"ValueError:\", error)\n","expect":"ValueError: not enough money","hidden":false},{"name":"withdraw(50, 50)","stdin":"","call":"withdraw(50, 50)\n","expect":"0","hidden":true,"returns":true},{"name":"Test 4","stdin":"","call":"try:\n    withdraw(50, 0)\nexcept ValueError as error:\n    print(\"ValueError:\", error)\n","expect":"ValueError: amount must be positive","hidden":true},{"name":"Test 5","stdin":"","call":"try:\n    withdraw(20, -5)\nexcept ValueError as error:\n    print(\"ValueError:\", error)\n","expect":"ValueError: amount must be positive","hidden":true}],"require":[],"forbid":[]},{"task":"The starter code has a function `parse_size(text)` that turns a size like `3x4` into a tuple `(3, 4)`, and raises a `ValueError` when the text isn't a valid size. Write the main program: it goes through the lines of the file `sizes.txt` and prints the area of each valid size, or `Skipped: ` and the exception's message for an invalid one.\n\nWith this `sizes.txt`\n\n```text\n3x4\n10x2\n5x\n0x8\n2x3x4\n```\n\nthe program prints:\n\n```\n3x4: area 12\n10x2: area 20\nSkipped: invalid literal for int() with base 10: ''\nSkipped: 0x8 has a side of zero or less\nSkipped: 2x3x4 needs exactly one x\n```\n","kind":"code","seed":"def parse_size(text):\n    parts = text.split(\"x\")\n    if len(parts) != 2:\n        raise ValueError(f\"{text} needs exactly one x\")\n    width = int(parts[0])\n    height = int(parts[1])\n    if width <= 0 or height <= 0:\n        raise ValueError(f\"{text} has a side of zero or less\")\n    return (width, height)\n\n\n# Write the main program below.\n","solution":"def parse_size(text):\n    parts = text.split(\"x\")\n    if len(parts) != 2:\n        raise ValueError(f\"{text} needs exactly one x\")\n    width = int(parts[0])\n    height = int(parts[1])\n    if width <= 0 or height <= 0:\n        raise ValueError(f\"{text} has a side of zero or less\")\n    return (width, height)\n\n\n# Write the main program below.\nwith open(\"sizes.txt\") as file:\n    for line in file:\n        text = line.strip()\n        try:\n            size = parse_size(text)\n            print(f\"{text}: area {size[0] * size[1]}\")\n        except ValueError as error:\n            print(\"Skipped:\", error)\n","hints":["Go through the file line by line and strip each line before you give it to `parse_size`.","The `try` goes inside the loop, so one bad line doesn't stop the others.","`except ValueError as error:` names the exception, and `print(\"Skipped:\", error)` prints its message. A `ValueError` from `int` inside `parse_size` is caught there too."],"tests":[{"name":"Test 1","stdin":"","files":{"sizes.txt":"3x4\n10x2\n5x\n0x8\n2x3x4\n"},"expect":"3x4: area 12\n10x2: area 20\nSkipped: invalid literal for int() with base 10: ''\nSkipped: 0x8 has a side of zero or less\nSkipped: 2x3x4 needs exactly one x","hidden":false},{"name":"Test 2","stdin":"","files":{"sizes.txt":"7x7\nax2\n1x100\n"},"expect":"7x7: area 49\nSkipped: invalid literal for int() with base 10: 'a'\n1x100: area 100","hidden":true}],"require":[],"forbid":[]}]}