Learn the concept
User input, configuration, and API payloads arrive with uncertain types and values. Validation converts that uncertainty into an explicit contract. Check shape first, then convert, then apply domain limits. A field can be syntactically numeric but still outside the allowed range.
Raise a specific exception when a caller supplies invalid input. Keep error messages actionable without echoing sensitive values. Do not catch every possible exception and return a plausible default, because that can conceal programming errors.
Python’s bool is a subclass of int. If a field must be an actual integer rather than true or false, a simple isinstance(value, int) check may be too permissive. Choose validation behavior intentionally. The boundary should reject bad inputs before expensive work begins.
Run and inspect
def read_limit(value):
if type(value) is not int:
raise ValueError("Limit must be an integer")
if not 1 <= value <= 100:
raise ValueError("Limit must be between 1 and 100")
return value
assert read_limit(10) == 10
Your exercise
Test 1, 100, 0, 101, True, "10", and None. Decide whether your application wants to convert text or reject it, and document that choice.
Check your understanding
All invalid cases fail before downstream processing; boundary values succeed.
Guided workshop: validate a batch size
A CLI batch size starts as text. The application needs a whole number between 1 and 100. Separate three decisions: remove surrounding whitespace, parse the number, and enforce the allowed range. Reporting all failures as “the program crashed” hides useful information.
# academy-check
def batch_size(raw):
try:
value = int(raw.strip())
except ValueError as error:
raise ValueError("batch size must be an integer") from error
if not 1 <= value <= 100:
raise ValueError("batch size must be between 1 and 100")
return value
assert batch_size(" 12 ") == 12
for raw in ["", "two", "1.5", "0", "101"]:
try:
batch_size(raw)
except ValueError:
pass
else:
raise AssertionError(raw)
print("six cases passed")
The first except handles parsing. The range check runs only after parsing succeeds. Raising from the original exception retains the cause for debugging. At the CLI boundary you would catch the final ValueError, display its message, and return a nonzero exit code. Do not catch every exception here: a misspelled variable is a programming defect, not invalid user input.
Exercise: accept a second input, a document title. Strip surrounding whitespace, reject an empty title, and reject titles longer than 80 characters. Preserve internal spaces and the original letter case. Try " Invoice A ", whitespace only, and 81 x characters.
Worked answer: assign title = raw.strip(), then reject when not title or len(title) > 80. The first case becomes Invoice A; replacing every run of spaces would make an additional content transformation that the requirement did not authorize.
Check your reasoning: would bool("false") be false? No: a nonempty string is truthy. For a configuration switch, explicitly map allowed values such as true and false. Does validation make user content trustworthy instructions? No: it establishes the properties you checked, not its meaning or authority.