Building from Pieces

Validation and Errors


Learning Objectives

  • You can distinguish text that cannot be converted to an integer from an integer that is outside an allowed range.
  • You can handle ValueError with a try block focused on the conversion.
  • You can trace how continue starts another attempt in an input loop.
  • You can write an input function that asks again until it can return a valid value.

Part 2 used input loops such as:

while True:
text = input("Value, or done: ").strip().lower()
if text == "done":
break
value = float(text)

Those examples assumed that anything other than done was numeric. Real users may enter something else.

This chapter focuses on user-input validation: converting a user’s text, checking whether the resulting value is acceptable for the task, and asking again when it is not.

What happens here?

value = float("hello")

Python cannot convert this text. It raises ValueError; if nothing handles the error, the program stops with a traceback.

Exceptions Report That an Operation Could Not Be Completed

An exception signals that an operation failed while the program was running. For example:

int("hello")

cannot perform the requested conversion.

Different problems produce different exception types. You do not need to memorize them; the important point is to catch the specific failure your program knows how to recover from. This chapter focuses on invalid conversion and ValueError.

Catching a ValueError

Use try and except when the program can respond meaningfully to a particular failure:

No files opened Select a file to edit

Try this operation. If it raises ValueError, run the recovery block instead of ending the program.

On success, number refers to the integer and except is skipped. The retry loop below shows how to use number only after success.

If the conversion fails, Python jumps to:

except ValueError:

and runs its block.

An optional sign is valid integer text: for example, int("-4") produces -4. Conversion answers whether the text represents an integer; a program can separately decide later whether a negative integer is acceptable for its purpose.

Catch the Error You Expect

Different operations can fail in different ways. An except ValueError handler responds to that exception type; it does not handle every error raised inside its try block.

Consider this version:

try:
number = int(input("Number: "))
result = 100 / number
print(result)
except ValueError:
print("Not an integer")

The handler is intended for invalid integer text, but try also contains division and printing. Input 0 converts successfully, then the division raises ZeroDivisionError. The ValueError handler does not catch it: zero is an integer, but it cannot be used as the divisor here.

Keep only the conversion inside the try block so it is clear which failure the handler is meant to recover from. If another operation in a larger block also raised ValueError, the same handler would catch it and could mistake a programming error for invalid input.

Use except ValueError rather than a bare except: for this recovery. A bare handler also catches unrelated errors, which can hide a programming mistake behind an invalid-input message.

The retry-loop example below keeps the block focused and uses number only after a successful conversion.

Trace several focused conversion handlers before combining one with a loop:

Ask Again Until the Input Is Valid

The while True pattern from Part 2 now becomes especially useful. Python’s continue statement immediately starts the next iteration of the surrounding loop:

No files opened Select a file to edit

Step through the same loop with two fixed inputs: hello, then 42. Watch text in the state panel to see each response. The walkthrough leaves out the Integer: prompts; its output panel shows the messages from print.

Code

Step 1 of 11

1 / 11
Formatting...

State

main

No variables

Output

No output yet

The first conversion fails before assigning number. Using that name at this point would raise NameError, even after the handler printed its message. Here, continue skips break and starts another attempt. The next conversion succeeds, so execution reaches break and then prints the accepted number.

Unlike a sentinel loop, this loop ends when conversion succeeds.

Put Reusable Validation in a Function

If several parts of a program need integer input, copying the loop each time creates duplication.

Give the behavior a name:

def read_integer(prompt):
while True:
text = input(prompt)
try:
number = int(text)
except ValueError:
print("Please enter an integer.")
continue
return number

Now:

age = read_integer("Age: ")
score = read_integer("Score: ")

Each call asks until it can return an integer. Failure reaches continue; success reaches return, which ends the call and gives the integer to the caller.

This is a useful combination of ideas from the part:

  • a function owns one responsibility: obtaining a valid integer;
  • while True repeats until success;
  • try handles a failed conversion;
  • return hands the valid result back to the caller.

Conversion Validation and Domain Validation Are Different

Suppose read_integer successfully returns -5.

The input was valid as an integer, but -5 may still be invalid for a particular purpose such as age or number of study minutes.

For example:

def read_nonnegative_integer(prompt):
while True:
text = input(prompt)
try:
number = int(text)
except ValueError:
print("Please enter an integer.")
continue
if number >= 0:
return number
print("Please enter zero or a positive integer.")

The function handles two requirements:

  • conversion validation: can this text become the requested type?
  • domain validation: is the resulting value acceptable for this task?

Conditions handle the second; exceptions help with the first.

Testing Validation Logic

A call that waits for input(...) needs someone to supply input. A calculation function can be tested by giving it arguments and checking its result with an assertion.

For example, a rule about acceptable ages can be placed in an ordinary calculation function:

def valid_age(age):
return age >= 0 and age <= 130
assert valid_age(25) == True
assert valid_age(0) == True
assert valid_age(130) == True
assert valid_age(-1) == False
assert valid_age(131) == False

These assertions are development-time tests of the age rule. They do not validate what a user types. The try, except, and if statements in read_age perform that job while the program is running.

The input function can use the same rule:

def read_age():
while True:
text = input("Age: ")
try:
age = int(text)
except ValueError:
print("Please enter an integer.")
continue
if valid_age(age):
return age
print("Please enter an age between 0 and 130.")

Put rules expressible as ordinary inputs and outputs into ordinary functions, because those functions are easy to test.

Set the launch level

0 / 25 points

Complete read_bounded_integer(prompt, minimum, maximum). It must keep asking until the user enters an integer from minimum through maximum, including both boundaries. You may assume that minimum and maximum are integers with minimum <= maximum; a range may include zero or negative numbers.

Use input(prompt) for each attempt. If the text cannot be converted with int, print Please enter an integer. and ask again. If the integer is outside the range, print Please enter a value from MINIMUM to MAXIMUM. with the actual bounds and ask again. Return the first accepted integer without reading another response.

Keep the conversion in a focused try block and catch ValueError specifically. Check the converted value separately. The retry loop and continue from the chapter are useful here.

Keep main(). It must call read_bounded_integer("", 1, 5) and print the accepted value as:

Level: 4

Calls to input() have no visible prompt in this program. For the inputs high, -1, 6, and 5, the complete output is:

Please enter an integer.
Please enter a value from 1 to 5.
Please enter a value from 1 to 5.
Level: 5

Check Your Understanding

  1. What does a ValueError mean in the context of int(text) or float(text)?
  2. What happens when the code inside a try block raises the exception named by its except block?
  3. How can while True, try, continue, and return work together in an input function?
  4. What is the difference between checking whether text can be converted to an integer and checking whether the resulting integer is a valid age?
  5. Why is except ValueError usually preferable to a bare except for numeric input conversion?

AI Study Aid

Create a chapter diagram

We're looking into to what extent AI-generated diagrams could help with studying.

Use this study aid to generate an AI-generated visual summary of the material you just studied. Each diagram style emphasizes the content in a different way, so you can choose the focus that feels most useful.

Using the diagram generator is voluntary and does not affect your course progress. We encourage you to try it out and see if it helps, but it's totally up to you! Your answers help us understand how to make better study aids in the future.

Diagrams tried: 0