Skip to content

[CRITICAL] Add Input Validation to Prevent Program Crashes #2052

@hackdartstorm

Description

@hackdartstorm

🔒 CRITICAL: Add Input Validation to Prevent Program Crashes

📖 The Real Problem

Issue: Beginner Python programs throughout the codebase crash when users enter invalid input, teaching bad practices and creating terrible user experiences.

Difficulty: Beginner
Category: Error Handling, Security, Best Practices
Impact: CRITICAL - Affects 50+ programs used by beginners
Files: basics/02_variables_types/*.py, basics/03_control_flow/*.py, basics/04_functions/*.py


💡 Real-World Impact

What Happens Now:

$ python basics/02_variables_types/01_arithmetic.py
Enter the Number : abc
Traceback (most recent call last):
  File "arithmetic.py", line 2, in <module>
    a = int(input("Enter the Number : "))
ValueError: invalid literal for int() with base 10: 'abc'

Student Experience:

  1. 😊 Excited to learn Python
  2. 😕 Tries example program
  3. 😨 Sees scary error message
  4. 😢 Thinks "programming is too hard"
  5. ❌ Quits learning

What Should Happen:

$ python basics/02_variables_types/01_arithmetic.py
Enter the Number : abc
❌ Invalid input! Please enter a whole number.
Enter the Number : 5
Enter the Number : 3
✅ Sum: 8

Student Experience:

  1. 😊 Excited to learn Python
  2. 😕 Makes mistake (normal!)
  3. 🙂 Gets helpful error message
  4. 😊 Fixes mistake, succeeds
  5. ✅ Continues learning!

🧠 Key Concepts

1. Input Validation

Definition: Checking user input BEFORE processing to ensure it meets expected criteria.

Why it matters:

  • Prevents crashes
  • Improves user experience
  • Teaches best practices
  • Professional code standard

2. Error Handling

Definition: Gracefully managing unexpected situations without crashing.

Python patterns:

  • try-except blocks
  • Input validation loops
  • Custom error messages

3. Defense in Depth

Definition: Multiple layers of validation for robust code.

Layers:

  1. Validate at input
  2. Validate in functions
  3. Validate before processing

💻 Solution Approaches

Approach 1: Basic Try-Except (Minimum Fix)

# BEFORE (crashes)
a = int(input("Enter the Number : "))

# AFTER (handles errors)
while True:
    try:
        a = int(input("Enter the Number : "))
        break
    except ValueError:
        print("❌ Invalid input! Please enter a whole number.")
    except KeyboardInterrupt:
        print("\n\n👋 Program interrupted by user.")
        exit(0)

Pros:

  • ✅ Simple to implement
  • ✅ Prevents crashes
  • ✅ Good for beginners

Cons:

  • ❌ No range checking
  • ❌ Repetitive code

Approach 2: Reusable Validation Function (Recommended)

def get_valid_integer(prompt="Enter a number: ", min_val=None, max_val=None):
    """
    Get validated integer input from user.
    
    Args:
        prompt: Input prompt to display
        min_val: Minimum allowed value (optional)
        max_val: Maximum allowed value (optional)
        
    Returns:
        Validated integer
        
    Example:
        >>> age = get_valid_integer("Enter age: ", min_val=1, max_val=100)
        Enter age: abc
        ❌ Invalid input! Please enter a whole number.
        Enter age: 150
        ❌ Value must be at most 100
        Enter age: 25
        >>> age
        25
    """
    while True:
        try:
            value = int(input(prompt))
            
            # Check range if specified
            if min_val is not None and value < min_val:
                print(f"❌ Value must be at least {min_val}")
                continue
            if max_val is not None and value > max_val:
                print(f"❌ Value must be at most {max_val}")
                continue
                
            return value
            
        except ValueError:
            print("❌ Invalid input! Please enter a whole number.")
        except KeyboardInterrupt:
            print("\n\n👋 Program interrupted by user.")
            exit(0)


# Usage in arithmetic program
a = get_valid_integer("Enter the first number: ")
b = get_valid_integer("Enter the second number: ")
result = a + b
print(f"✅ Sum: {result}")

Pros:

  • ✅ Reusable across files
  • ✅ Range checking
  • ✅ Clean code
  • ✅ Professional pattern

Cons:

  • ❌ More code initially

Approach 3: Comprehensive Testing

def test_input_validation():
    """Test all validation scenarios."""
    from io import StringIO
    import sys
    
    print("Testing input validation...\n")
    
    # Test 1: Valid input
    print("Test 1: Valid integer input")
    sys.stdin = StringIO("5\n")
    result = get_valid_integer("Enter: ")
    assert result == 5
    print("✓ Test 1 passed\n")
    
    # Test 2: Invalid then valid
    print("Test 2: Invalid then valid input")
    sys.stdin = StringIO("abc\n123\n")
    result = get_valid_integer("Enter: ")
    assert result == 123
    print("✓ Test 2 passed\n")
    
    # Test 3: Range validation (too low)
    print("Test 3: Range validation (too low)")
    sys.stdin = StringIO("0\n25\n")
    result = get_valid_integer("Enter: ", min_val=1, max_val=100)
    assert result == 25
    print("✓ Test 3 passed\n")
    
    # Test 4: Range validation (too high)
    print("Test 4: Range validation (too high)")
    sys.stdin = StringIO("150\n50\n")
    result = get_valid_integer("Enter: ", min_val=1, max_val=100)
    assert result == 50
    print("✓ Test 4 passed\n")
    
    # Test 5: Empty input
    print("Test 5: Empty input handling")
    sys.stdin = StringIO("\n10\n")
    result = get_valid_integer("Enter: ")
    assert result == 10
    print("✓ Test 5 passed\n")
    
    # Test 6: Negative numbers
    print("Test 6: Negative numbers")
    sys.stdin = StringIO("-5\n")
    result = get_valid_integer("Enter: ")
    assert result == -5
    print("✓ Test 6 passed\n")
    
    # Test 7: Float input rejected
    print("Test 7: Float input rejected for integer")
    sys.stdin = StringIO("12.5\n20\n")
    result = get_valid_integer("Enter: ")
    assert result == 20
    print("✓ Test 7 passed\n")
    
    # Test 8: Whitespace handling
    print("Test 8: Whitespace handling")
    sys.stdin = StringIO("  15  \n")
    result = get_valid_integer("Enter: ")
    assert result == 15
    print("✓ Test 8 passed\n")
    
    # Test 9: Special characters rejected
    print("Test 9: Special characters rejected")
    sys.stdin = StringIO("12@3\n30\n")
    result = get_valid_integer("Enter: ")
    assert result == 30
    print("✓ Test 9 passed\n")
    
    # Test 10: Zero handling
    print("Test 10: Zero handling")
    sys.stdin = StringIO("0\n")
    result = get_valid_integer("Enter: ")
    assert result == 0
    print("✓ Test 10 passed\n")
    
    # Restore stdin
    sys.stdin = sys.__stdin__
    
    print("\n🎉 All 10 tests passed!")


if __name__ == "__main__":
    test_input_validation()

🧪 Files to Update

Priority 1: Arithmetic Programs

  • basics/02_variables_types/01_arithmetic.py
  • basics/02_variables_types/02_average.py
  • basics/02_variables_types/03_comparison_greater.py

Priority 2: Calculator Programs

  • basics/02_variables_types/04_simple_interest.py
  • basics/02_variables_types/05_temperature_converter.py

Priority 3: All User Input

  • Search: grep -r "int(input" basics/
  • Search: grep -r "float(input" basics/
  • Update all found files

📚 Learning Resources

Must Read:

  1. Python Input Validation - Real Python (30 min)
  2. Error Handling - Official Docs (20 min)

Videos:

  1. Python Error Handling - Corey Schafer (15 min)
  2. Input Validation Patterns - Python Engineer (12 min)

Practice:


❓ Common Questions

Q1: Why not just use try-except everywhere?

A: Try-except is reactive. Proactive validation is better:

# Reactive (try-except only)
try:
    age = int(input())
except ValueError:
    print("Invalid")

# Proactive (validation + try-except)
def get_age():
    while True:
        age = input()
        if age.isdigit() and 0 <= int(age) <= 150:
            return int(age)
        print("Enter valid age (0-150)")

Q2: Isn't validation annoying for users?

A: Good validation is HELPFUL:

# Annoying
"Invalid input!"

# Helpful  
"Please enter a number between 1 and 100. You entered: abc"

Q3: How much validation is enough?

A: Context-dependent:

  • Calculator: Valid numbers only
  • Age input: Range 0-150
  • Email: Format validation
  • Password: Length + complexity

Q4: What about performance?

A: Validation overhead is negligible (~0.001s). Crash recovery costs much more!

Q5: Should I validate in functions too?

A: YES! Defense in Depth:

# At input
def get_age():
    while True:
        age = input()
        if age.isdigit():
            return int(age)

# In function
def calculate_retirement(age):
    if not isinstance(age, int) or age < 0:
        raise ValueError("Invalid age")

Q6: How do I test validation code?

A: Use StringIO:

from io import StringIO
import sys

sys.stdin = StringIO("invalid\n5\n")
result = get_valid_integer()
assert result == 5

Q7: What about GUI applications?

A: Same principles, different implementation:

# Tkinter
def validate_number(new_value):
    return new_value == "" or new_value.isdigit()

vcmd = (root.register(validate_number), '%P')
entry = ttk.Entry(validatecommand=vcmd)

✅ Acceptance Criteria

Before submitting your PR, ensure:

  • All int(input()) calls have validation
  • All float(input()) calls have validation
  • Reusable validation function created
  • 10+ test cases pass
  • Error messages are helpful and clear
  • Range validation where appropriate (age, percentage, etc.)
  • Type hints included
  • Docstrings complete
  • Code follows PEP 8
  • No crashes on any invalid input
  • Files updated (minimum 10 files in basics/)

🤝 How to Contribute

Step 1: Fork and Clone

git clone https://github.com/YOUR_USERNAME/Python.git
cd Python

Step 2: Create Branch

git checkout -b issue-2052-input-validation

Step 3: Find Files Needing Fixes

# Find all int(input) calls
grep -r "int(input" basics/

# Find all float(input) calls  
grep -r "float(input" basics/

# Find all bare input() calls
grep -rn "input(" basics/ | grep -v "#" | head -20

Step 4: Create Validation Function

Add to a new file basics/utils/validation.py:

def get_valid_integer(prompt, min_val=None, max_val=None):
    # Implementation from Approach 2
    pass

Step 5: Update Programs

Replace each int(input()) with validation:

# Before
a = int(input("Enter the Number : "))

# After
from utils.validation import get_valid_integer
a = get_valid_integer("Enter the first number: ")

Step 6: Test Each Program

# Test with valid input
python basics/02_variables_types/01_arithmetic.py
# Enter: 5, 3 → Should work

# Test with invalid input
python basics/02_variables_types/01_arithmetic.py  
# Enter: abc → Should show error, allow retry

# Test with edge cases
python basics/02_variables_types/01_arithmetic.py
# Enter: Ctrl+C → Should exit gracefully

Step 7: Run Test Suite

python basics/utils/test_validation.py
# All 10 tests should pass

Step 8: Commit

git add basics/
git commit -m "Fix #2052: Add input validation to prevent crashes

- Created reusable validation function
- Updated 10+ programs in basics/
- Added 10 comprehensive test cases
- All programs now handle invalid input gracefully"

Step 9: Push and Create PR

git push origin issue-2052-input-validation
# Go to GitHub, create PR referencing #2052

🎯 Impact & Priority

Attribute Value
Difficulty Beginner
Priority CRITICAL
Category Error Handling, Security, Education
Issue #2052
Files Affected 50+ in basics/
Time Estimate 3-4 hours
Impact HIGH - Improves all beginner programs

📝 Notes

  • Start with one file as example (01_arithmetic.py)
  • Create reusable validation function in utils/
  • Test thoroughly with ALL edge cases
  • Update documentation in each file
  • Add comments explaining validation

Ready to make our codebase crash-proof? Submit your PR! 🚀

Happy Coding! 💻

Metadata

Metadata

Assignees

No one assigned

    Labels

    beginner-friendlyGood for beginnersbugSomething isn't workingcriticalCritical priority

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions