{"id":"errors-validation","slug":"validation","title":"Checking input before using it","text":"Errors come in two kinds. A **syntax error** means Python can't read the program at all, so none of it runs; the very first module showed some. A **runtime error** happens while the program is running: a line that is perfectly good Python fails with certain data. Such a program can work a hundred times and fail the hundred and first, when someone types something unexpected. Runtime errors are what this module is about.\n\nMany of them come from **input** a program didn't expect:\n\n- a number outside what makes sense, such as a negative price or month 13;\n- text where a number was expected;\n- an empty string where a name was expected;\n- the name of a file that doesn't exist;\n- an index past the end of a list or a string.\n\n### Checking values with if\n\nThe first defense is to check a value before using it. This is called **input validation**:\n\n```python\ntickets = int(input(\"How many tickets? \"))\nif tickets >= 1 and tickets <= 10:\n    print(f\"Total: {tickets * 15} euros\")\nelse:\n    print(\"You can buy 1 to 10 tickets at a time.\")\n```\n\n```input\n12\n```\n\n```output\nHow many tickets? 12\nYou can buy 1 to 10 tickets at a time.\n```\n\n### A loop that asks again\n\nInstead of giving up, a program can keep asking until the input is valid. The loop ends with `break` only when the value passes the check:\n\n```python\nwhile True:\n    month = int(input(\"Month (1-12): \"))\n    if month >= 1 and month <= 12:\n        break\n    print(\"There's no such month.\")\nprint(\"You chose month\", month)\n```\n\n```input\n0\n13\n7\n```\n\n```output\nMonth (1-12): 0\nThere's no such month.\nMonth (1-12): 13\nThere's no such month.\nMonth (1-12): 7\nYou chose month 7\n```\n\nStrings get checked the same way: `if name == \"\":` notices an empty name, and `len(password) < 8` a password that's too short.\n\n### When the input isn't a number at all\n\nThe checks above work only once `int` has converted the input. If the user types `two`, the conversion itself fails, and the program stops before it ever gets to the `if`:\n\n```python\ntickets = int(input(\"How many tickets? \"))\nif tickets >= 1 and tickets <= 10:\n    print(f\"Total: {tickets * 15} euros\")\nelse:\n    print(\"You can buy 1 to 10 tickets at a time.\")\n```\n\n```input\ntwo\n```\n\n```output\nHow many tickets? two\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 1, in <module>\n    tickets = int(input(\"How many tickets? \"))\nValueError: invalid literal for int() with base 10: 'two'\n```\n\nA `ValueError` means a value of the right type (here a string) has contents that don't work: `int` can't make a whole number out of `two`. An `if` can't prevent this, but the next step shows how a program can catch the error and carry on.\n","task":"Fill in the blanks so the program accepts a rating from 1 to 5 stars and rejects anything else. A run looks like this:\n\n```\nRating (1-5): 4\nThanks for rating us 4\n```\n","kind":"fill","seed":"rating = int(input(\"Rating (1-5): \"))\nif rating >= 1 [[and]] rating <= [[5]]:\n    print(\"Thanks for rating us\", rating)\nelse:\n    print(\"Please give 1 to 5 stars.\")\n","solution":"rating = int(input(\"Rating (1-5): \"))\nif rating >= 1 and rating <= 5:\n    print(\"Thanks for rating us\", rating)\nelse:\n    print(\"Please give 1 to 5 stars.\")\n","hints":["Both conditions must be true at the same time, so they're joined with the word for that.","The upper limit is 5 stars: `rating >= 1 and rating <= 5`."],"tests":[{"name":"Test 1","stdin":"4\n","expect":"Rating (1-5): 4\nThanks for rating us 4","hidden":false},{"name":"Test 2","stdin":"0\n","expect":"Rating (1-5): 0\nPlease give 1 to 5 stars.","hidden":true},{"name":"Test 3","stdin":"6\n","expect":"Rating (1-5): 6\nPlease give 1 to 5 stars.","hidden":true},{"name":"Test 4","stdin":"5\n","expect":"Rating (1-5): 5\nThanks for rating us 5","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Write a program that asks for a new password until it's valid, and then prints `Password saved`. A valid password has at least 8 characters and no spaces. A password that's too short gets the message `Too short`; one with a space in it gets `No spaces, please`. (Check the length first.)\n\nA run looks like this:\n\n```\nNew password: cat\nToo short\nNew password: my secret pw\nNo spaces, please\nNew password: Tr0ub4dor\nPassword saved\n```\n","kind":"code","seed":"password = input(\"New password: \")\n","solution":"while True:\n    password = input(\"New password: \")\n    if len(password) < 8:\n        print(\"Too short\")\n    elif \" \" in password:\n        print(\"No spaces, please\")\n    else:\n        break\nprint(\"Password saved\")\n","hints":["Put the `input` inside a `while True:` loop, so a new password is asked for every round.","Check `len(password) < 8` first, then `\" \" in password` with an `elif`.","When neither problem is found, `break` out of the loop and print the message after it."],"tests":[{"name":"Test 1","stdin":"cat\nmy secret pw\nTr0ub4dor\n","expect":"New password: cat\nToo short\nNew password: my secret pw\nNo spaces, please\nNew password: Tr0ub4dor\nPassword saved","hidden":false},{"name":"Test 2","stdin":"short\n1234567\n12345678\n","expect":"New password: short\nToo short\nNew password: 1234567\nToo short\nNew password: 12345678\nPassword saved","hidden":true},{"name":"Test 3","stdin":"a b c d e f g\ncorrecthorse\n","expect":"New password: a b c d e f g\nNo spaces, please\nNew password: correcthorse\nPassword saved","hidden":true}],"require":[],"forbid":[]},{"task":"Write a function `is_valid_date(day, month)` that returns `True` if the day and month make a real date in a year that isn't a leap year, and `False` if they don't. The starter code has the number of days in each month in a list.\n\nCheck the month before you use it as an index: a month like 13 would stop the program with an `IndexError`, and month 0 would quietly give index -1, which is December.\n\nFor example, `is_valid_date(31, 1)` returns `True`, and `is_valid_date(30, 2)` returns `False`.\n","kind":"code","seed":"def is_valid_date(day, month):\n    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n\n\nif __name__ == \"__main__\":\n    print(is_valid_date(31, 1))\n    print(is_valid_date(30, 2))\n","solution":"def is_valid_date(day, month):\n    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n    if month < 1 or month > 12:\n        return False\n    return day >= 1 and day <= days_in_month[month - 1]\n\n\nif __name__ == \"__main__\":\n    print(is_valid_date(31, 1))\n    print(is_valid_date(30, 2))\n","hints":["First check the month: if it's below 1 or above 12, return `False` straight away.","January is at index 0, so a month's number of days is `days_in_month[month - 1]`.","The date is valid when the day is at least 1 and at most that number of days."],"tests":[{"name":"is_valid_date(31, 1)","stdin":"","call":"is_valid_date(31, 1)\n","expect":"True","hidden":false,"returns":true},{"name":"is_valid_date(30, 2)","stdin":"","call":"is_valid_date(30, 2)\n","expect":"False","hidden":false,"returns":true},{"name":"is_valid_date(31, 4)","stdin":"","call":"is_valid_date(31, 4)\n","expect":"False","hidden":true,"returns":true},{"name":"is_valid_date(15, 13)","stdin":"","call":"is_valid_date(15, 13)\n","expect":"False","hidden":true,"returns":true},{"name":"is_valid_date(1, 0)","stdin":"","call":"is_valid_date(1, 0)\n","expect":"False","hidden":true,"returns":true},{"name":"is_valid_date(28, 2)","stdin":"","call":"is_valid_date(28, 2)\n","expect":"True","hidden":true,"returns":true},{"name":"is_valid_date(0, 5)","stdin":"","call":"is_valid_date(0, 5)\n","expect":"False","hidden":true,"returns":true},{"name":"is_valid_date(31, 12)","stdin":"","call":"is_valid_date(31, 12)\n","expect":"True","hidden":true,"returns":true}],"require":[],"forbid":[]}]}