Lesson 3: Validation & Errors — Data Safety, Type Checking

Learn how to validate command-line arguments in Python using argparse—type checking, required flags, choices, and custom validators with real examples.

Lesson 3: Validation & Errors — Data Safety, Type Checking

Goal: Make sure your CLI tool only accepts valid, well-typed input — and throws helpful errors when users do dumb things.

Why Validation Matters

Your CLI tool is only as good as the inputs it accepts. If someone passes "banana" when you’re expecting a number, things break fast.

Validation protects your logic from crashing, keeps output clean, and makes your script behave like a real tool, not a fragile toy. It’s the armor between your code and the chaos.

What You’ll Learn

  • Type enforcement (type=int, type=float, etc.)
  • Limiting input with choices=
  • Writing custom validators (ranges, positive-only, patterns)
  • Validating ranges and simple patterns
  • Defensive coding with try/except (optional but useful)
  • Using required=True to force input
  • Setting up mutually exclusive flags
  • Getting clean, automatic error messages (argparse does the heavy lifting)

Core Concepts

1) type=: Convert + Validate in One Shot

type= converts the input and validates at the same time.

parser.add_argument("--age", type=int)

If someone runs:

python app.py --age banana

They’ll get:

error: argument --age: invalid int value: 'banana'

Boom. Safety net.

2) Custom Validation Functions

When built-in types aren’t enough, write your own validator. Great for ranges, positive-only values, “must be a file”, regex checks, etc.

import argparse

def positive_number(value):
    ivalue = int(value)
    if ivalue <= 0:
        raise argparse.ArgumentTypeError("Must be a positive number.")
    return ivalue

parser = argparse.ArgumentParser(description="Validate positive numbers")
parser.add_argument("--level", type=positive_number)

args = parser.parse_args()
print(args.level)

Try:

python app.py --level -5

…and it gets smacked down.

3) choices=: Only Allow Certain Values

Perfect for “modes”, “environments”, categories, enums, etc.

parser.add_argument("--color", choices=["red", "green", "blue"])

If they try something else, argparse rejects it automatically.

4) required=True: Force a Value

Want to require a flag?

parser.add_argument("--username", required=True)

No --username? argparse yells at them. With love.

5) Mutually Exclusive Flags

If only one option is allowed (like --verbose or --quiet), use a mutually exclusive group:

group = parser.add_mutually_exclusive_group()

group.add_argument("--verbose", action="store_true")

group.add_argument("--quiet", action="store_true")

Passing both becomes an immediate ❌ error.

Sample Code: Clean Input or No Input

import argparse
parser = argparse.ArgumentParser(description="Process numeric input safely")
# Type enforcement
parser.add_argument("--age", type=int, help="Your age in years")
parser.add_argument("--rate", type=float, help="Conversion rate")
# Limited input choices
parser.add_argument(
    "--mode",
    choices=["easy", "medium", "hard"],
    default="easy",
    help="Choose difficulty level (easy, medium, hard)"
)
args = parser.parse_args()
print(f"Age: {args.age}")
print(f"Rate: {args.rate}")
print(f"Mode: {args.mode}")

Try it out

$ python3 validate.py --age 30 --rate 1.25 --mode medium

Age: 30

Rate: 1.25

Mode: medium

Bad inputs:

$ python3 validate.py --age banana

# error: argument --age: invalid int value: 'banana'

$ python3 validate.py --mode nightmare

# error: argument --mode: invalid choice: 'nightmare' (choose from 'easy', 'medium', 'hard')

Beautiful. Clean. Practically idiot-proof.

Pro Tips

  • Use type= for basic conversion + safety.
  • Use custom validators for logic (range checks, positive-only, etc.).
  • Use choices= for fixed modes/options.
  • Use required=True when missing input should be a hard failure.
  • Test invalid inputs on purpose. Your future self will thank you.
  • Don’t assume users will follow instructions — enforce rules instead.

Real-World Use Cases

  • Fixed modes: --env dev / --env prod
  • User inputs like age, tax rate, discount amount (always type-check)
  • Sensitive automation flags like --risk, --region, --priority

Bonus: Price Calculator with Validation

import argparse
def positive_float(val):
    f = float(val)
    if f <= 0:
        raise argparse.ArgumentTypeError("Value must be greater than zero.")
    return f
parser = argparse.ArgumentParser(description="Calculate price with tax")
parser.add_argument("--price", type=positive_float, required=True)
parser.add_argument("--tax", type=positive_float, default=0.2)
args = parser.parse_args()
total = args.price * (1 + args.tax)
print(f"Total: ${total:.2f}")

Coming Up Next

Part 4: Grouping & Subcommands — we’ll organize commands like a pro, split logic into subtools, and make your CLI feel like a real app.

🔁 New here? Catch up from Lesson 1:
Lesson 1: Getting Started with argparse

Got questions or want to share your CLI builds? Drop a comment.


Note
If you find any part of this post unclear or technically inaccurate, I would appreciate hearing from you. Improving the precision of these explanations is an ongoing process, and your feedback helps strengthen the material.