-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
154: Practice Exercise: Building a Robust Input Validation Layer
By now, you know how to get input from a user, but in a real-world production environment, the golden rule is: never trust the user. If you expect an integer and they give you a string, or if you expect a positive number and they give you -500, your program shouldn't just crash with a traceback. It should handle it gracefully.
I want to show you how to build a validation layer for a user profile system. Instead of sprinkling if/else blocks all over your main logic, we're going to isolate the validation so the rest of your app can assume the data it receives is already clean.
Drafting the Naive Validation Logic
Let's start with a simple requirement: we need a username (3-15 characters, alphanumeric) and an age (18-100). My first instinct is usually to just write a function that returns a boolean. It's quick, but it's a bit limited because it doesn't tell the user why the input failed.
def validate_user_data(username, age):
if len(username) < 3 or len(username) > 15:
return False
if not username.isalnum():
return False
if age < 18 or age > 100:
return False
return True
This works, but it's "silent." If this returns False, the user just knows something is wrong, but they don't know if it was their username or their age. That's a terrible user experience.
Oops—I Forgot About Type Casting
Here is where I usually trip up when I'm rushing a prototype. I'll start calling my function like this:
# Imagine this is coming from an input() call
user_name = "DevDan"
user_age = "25"
if validate_user_data(user_name, user_age):
print("Success!")
else:
print("Invalid input.")
If you run this, Python is going to throw a TypeError. Why? Because user_age is a string, and I'm trying to compare it using < and > against integers in my function. I forgot that input() always returns a string. In a real app, this crash is exactly what we're trying to avoid.
To fix this, I need to handle the type conversion inside the validation layer, wrapped in a try/except block, so the app doesn't blow up if the user types "twenty-five" instead of "25".
Creating a Dedicated Validation Engine
To make this truly robust, I'm going to move away from booleans and start using custom exceptions. This allows us to pass a specific error message back up the chain. I'll create a specialized ValidationError class. This is a common pattern in professional Python frameworks like Django or Pydantic.
class ValidationError(Exception):
"""Custom exception for input validation errors."""
pass
def validate_profile(data):
# Validate Username
username = data.get("username", "")
if not (3 <= len(username) <= 15):
raise ValidationError("Username must be between 3 and 15 characters.")
if not username.isalnum():
raise ValidationError("Username must be alphanumeric.")
# Validate Age
try:
age = int(data.get("age", 0))
except ValueError:
raise ValidationError("Age must be a valid number.")
if not (18 <= age <= 100):
raise ValidationError("Age must be between 18 and 100.")
return True
Now, the logic is separated. The validate_profile function doesn't care where the data comes from—it just cares that the data follows the rules. If it fails, it tells us exactly why.
Integrating the Layer into the Main Loop
The final piece is the "retry loop." We don't want the program to end just because of one typo. We'll wrap the validation call in a while loop that only breaks once the ValidationError is no longer being raised.
while True:
user_input = {
"username": input("Enter username: "),
"age": input("Enter age: ")
}
try:
validate_profile(user_input)
print("Profile validated successfully!")
break
except ValidationError as e:
print(f"Input Error: {e}")
print("Please try again.\n")
By isolating the validation into its own layer, we've made the code modular. If we later decide that usernames can include underscores, we only have to change one line in the validate_profile function, and the rest of our application remains untouched.
📋 Practical Task
Exercise: Building a Secure Password and Email Validator
Your task is to expand the validation layer we built. Create a program that asks a user for an email address and a password, and validates them using a custom ValidationError class.
Your validation layer must enforce the following rules:
- Email: Must contain at least one "@" symbol and at least one "." (dot).
- Password: Must be at least 8 characters long and contain at least one digit.
Requirements:
- Define a custom
ValidationErrorexception. - Create a function
validate_credentials(credentials)that takes a dictionary and raises the custom exception with a specific message for each rule violation. - Implement a
whileloop that repeatedly prompts the user for their email and password until both are valid. - Ensure that the program does not crash if the user provides empty inputs.
There are no comments for now.