# Don't let errors crash your program
# try / except — handle errors gracefully

# ─────────────────────────────────────────────
# The problem — no error handling
# ─────────────────────────────────────────────

# age = int(input("Enter your age: "))    # user types "abc"
# ValueError: invalid literal for int() with base 10: 'abc'

# ─────────────────────────────────────────────
# Your first try / except
# ─────────────────────────────────────────────

try:
    age = int(input("Enter your age: "))
    print(f"Your age is {age}.")
except:
    print("That's not a valid number.")

# user types "abc" — int() fails — except catches it — no crash

# ─────────────────────────────────────────────
# Catching a specific error
# ─────────────────────────────────────────────

try:
    age = int(input("Enter your age: "))
    print(f"Your age is {age}.")
except ValueError:
    print("Numbers only, please.")

# ValueError — value can't be converted
# other unexpected errors still crash — which is often what you want

# ─────────────────────────────────────────────
# try / except inside a function
# ─────────────────────────────────────────────

def get_age():
    try:
        age = int(input("Enter your age: "))
        return age
    except ValueError:
        print("Numbers only, please.")
        return None

age = get_age()
if age is not None:
    print(f"Your age is {age}.")

# ─────────────────────────────────────────────
# Common error types
# ─────────────────────────────────────────────

# ValueError       — value can't be converted (int("abc"))
# ZeroDivisionError — division by zero (10 / 0)
# TypeError        — wrong type (len(5))

# ─────────────────────────────────────────────
# Quick reference
# ─────────────────────────────────────────────

# try:               — code that might fail
#     block
# except ValueError: — catches specific error
#     block
#
# try succeeds — except skipped entirely
# try fails    — except runs, program continues
# catch specific errors — bare except catches everything
