Day 10 – Exception Handling and Debugging

 

🐞 Day 10 – Exception Handling and Debugging

1. Learning Objectives

By the end of Day 10, you will be able to:

  • Use try / except / else / finally blocks to handle runtime errors gracefully.
  • Handle specific exception types (FileNotFoundError, ValueError, ZeroDivisionError, KeyError).
  • Write robust code that doesn’t crash on messy input data or missing files.
  • Use basic debugging techniques: print() debugging, reading tracebacks, and using pdb.
  • Apply exception handling to real AEC scenarios (missing spreadsheet cells, division by zero in calculations, file import errors).

2. Concept Explanation

2.1 What Are Exceptions?

Exceptions are errors that occur during program execution. When an exception is not handled, the program crashes with a traceback. Common AEC exceptions:

ExceptionCommon Cause in AEC
FileNotFoundErrorTrying to open a CSV/Excel file that doesn’t exist
ValueErrorUser enters text where a number is expected
ZeroDivisionErrorDividing by zero (e.g., span = 0 in moment calc)
KeyErrorAccessing a dictionary key that doesn’t exist
IndexErrorAccessing a list index out of range
TypeErrorAdding a string and number without casting

2.2 The try/except/else/finally Structure

try:
    # Code that might raise an exception
    risky_operation()
except SomeExceptionType as e:
    # Code to handle the error
    print(f"Error occurred: {e}")
else:
    # Code that runs if no exception occurred (optional)
    print("Operation succeeded!")
finally:
    # Code that always runs, even if exception occurs (optional)
    cleanup_action()

2.3 Catching Multiple Exceptions

try:
    value = float(input("Enter a number: "))
    result = 100 / value
    print(f"Result: {result}")
except ValueError:
    print("Please enter a valid number.")
except ZeroDivisionError:
    print("Cannot divide by zero.")
except Exception as e:
    print(f"Unexpected error: {e}")

2.4 Raising Your Own Exceptions

You can raise exceptions to signal invalid conditions:

def calculate_stress(force, area):
    if area <= 0:
        raise ValueError("Area must be positive")
    return force / area

2.5 Debugging Basics

  • print() debugging: Insert print statements to inspect variable values.
  • Reading tracebacks: The error message tells you the file, line number, and type of error.
  • Using pdb (Python Debugger): Insert import pdb; pdb.set_trace() to pause execution and inspect interactively.

3. Code Examples

Example 1: Robust user input for a beam span

def get_positive_float(prompt):
    """Safely get a positive float from user input."""
    while True:
        try:
            value = float(input(prompt))
            if value <= 0:
                print("Value must be positive. Try again.")
                continue
            return value
        except ValueError:
            print("Invalid input. Please enter a number.")

# Usage
span = get_positive_float("Enter beam span (m): ")
print(f"Span: {span:.2f} m")

Example 2: Safe file reading with error handling

def read_member_schedule(filename):
    """Read a CSV member schedule with error handling."""
    import csv
    members = []
    try:
        with open(filename, "r") as f:
            reader = csv.DictReader(f)
            for row in reader:
                try:
                    member = {
                        "id": row["Member"],
                        "span": float(row["Span_m"]),
                        "load": float(row["Load_kNm"]),
                    }
                    members.append(member)
                except (ValueError, KeyError) as e:
                    print(f"Skipping invalid row: {row} – {e}")
    except FileNotFoundError:
        print(f"ERROR: File '{filename}' not found.")
        return []
    except Exception as e:
        print(f"Unexpected error reading file: {e}")
        return []
    else:
        print(f"Successfully read {len(members)} members from {filename}")
    return members

# Test
data = read_member_schedule("beams.csv")
if data:
    print(f"Loaded {len(data)} beams.")

Example 3: Handling division by zero in stress calculations

def calculate_bending_stress(moment, section_modulus):
    """
    Calculate bending stress = M / S.
    Returns None if section_modulus is zero to avoid division error.
    """
    try:
        stress = moment / section_modulus
        return stress
    except ZeroDivisionError:
        print("WARNING: Section modulus is zero. Cannot compute stress.")
        return None

# Test cases
print(calculate_bending_stress(100000, 500))      # 200.0
print(calculate_bending_stress(100000, 0))         # Warning, returns None

Example 4: Using else and finally

def process_design_data():
    file = None
    try:
        file = open("design_data.txt", "r")
        content = file.read()
        # ... process data ...
        print("Data processed successfully.")
    except FileNotFoundError:
        print("Design data file missing.")
    except Exception as e:
        print(f"Processing error: {e}")
    else:
        print("No errors occurred during processing.")
    finally:
        if file:
            file.close()
            print("File closed.")

process_design_data()

Example 5: Debugging with print() and traceback

# Problematic code snippet
def compute_deflection(load, span, E, I):
    print(f"DEBUG: load={load}, span={span}, E={E}, I={I}")
    w = load * 1000     # N/m
    L = span * 1000     # mm
    deflection = 5 * w * L**4 / (384 * E * I)
    return deflection

# This will raise an error if I is zero
try:
    d = compute_deflection(20, 6, 200000, 0)
    print(f"Deflection: {d:.2f} mm")
except ZeroDivisionError as e:
    print(f"Calculation failed: {e}. Check moment of inertia value.")

4. Hands‑on Exercises (3–5 Problems)

Problem 1 – Safe integer input
Write a function safe_int_input(prompt) that keeps asking until the user enters a valid integer. Use try/except to catch ValueError.

Problem 2 – File reading with fallback
Write a script that tries to open design_data.csv. If the file doesn’t exist, print a friendly message and create an empty file with headers "Item, Value". Handle both FileNotFoundError and other exceptions.

Problem 3 – Divide two numbers from user input
Ask the user for two numbers (numerator and denominator). Use exception handling for:

  • ValueError (non-numeric input)
  • ZeroDivisionError (denominator zero)

Print the result or an appropriate error message.

Problem 4 – Dictionary lookup with KeyError handling
You have a dictionary of material densities:
densities = {"Steel": 7850, "Concrete": 2400, "Aluminium": 2700}
Ask the user for a material name. Use try/except to safely look up the density. If the material is not found, print a default message.

Solutions (attempt first):

# P1
def safe_int_input(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            print("Invalid input. Please enter a whole number.")

age = safe_int_input("Enter floor count: ")

# P2
try:
    with open("design_data.csv", "r") as f:
        print("File found. Contents:")
        print(f.read())
except FileNotFoundError:
    print("design_data.csv not found. Creating a new file.")
    with open("design_data.csv", "w") as f:
        f.write("Item, Value\n")
except Exception as e:
    print(f"Unexpected error: {e}")

# P3
try:
    num = float(input("Numerator: "))
    den = float(input("Denominator: "))
    result = num / den
    print(f"Result: {result:.2f}")
except ValueError:
    print("Please enter numbers only.")
except ZeroDivisionError:
    print("Cannot divide by zero.")

# P4
densities = {"Steel": 7850, "Concrete": 2400, "Aluminium": 2700}
material = input("Enter material: ").strip().title()
try:
    density = densities[material]
    print(f"Density of {material}: {density} kg/m³")
except KeyError:
    print(f"Material '{material}' not found in database.")

5. Applied Challenge Task

Task: Robust Construction Data Importer

You are given a CSV file site_measurements.csv with the following structure (but it may contain errors):

Element, Length_m, Width_m, Height_m, Material
Beam1, 6.0, 0.3, 0.5, Concrete
Column1, 0.4, 0.4, 3.5, Steel
Slab1, 8.0, 5.0, , Concrete          # missing height
Beam2, seven, 0.3, 0.5, Timber        # non-numeric length
Column2, 0.5, 0.5, 3.0,               # missing material

Write a script that:

  1. Reads the CSV file using csv.DictReader inside a try/except block.

  2. For each row, attempts to:

    • Convert Length_m, Width_m, Height_m to floats.
    • Check that Material is not empty.
  3. If a row has errors, catch the specific exception (ValueError, KeyError), print a warning with the row number and the error, but continue processing the next rows.

  4. For valid rows, compute the volume and store the element in a list of dictionaries.

  5. At the end, print:

    • Total number of rows processed.
    • Number of valid elements.
    • Number of rows with errors.
    • Total volume of all valid elements.
  6. Write a clean CSV file validated_elements.csv with only the valid rows, including a calculated Volume_m3 column.

Why this matters:
In real AEC projects, imported data is almost never clean. This task trains you to write scripts that handle real‑world messiness gracefully – a critical skill for production‑ready tools.


6. Brief Review Summary

  • try/except prevents crashes from expected errors (file not found, bad input, division by zero).
  • Catch specific exceptions (ValueError, FileNotFoundError) rather than a bare except.
  • else runs if no exception; finally always runs (cleanup).
  • Use print() debugging to inspect values; read tracebacks carefully.
  • Raising your own exceptions (raise ValueError(...)) signals invalid conditions.

Key takeaway:
Exception handling makes your scripts robust and professional. In AEC, where data is often messy and inputs unpredictable, handling errors gracefully is essential for trust and reliability.


7. Preview of Next Topic (Day 11)

Tomorrow we’ll cover List Comprehensions, Lambda, map/filter.
You’ll learn:

  • Writing concise list comprehensions (e.g., [s for s in spans if s > 6]).
  • Anonymous functions with lambda.
  • Using map() and filter() for functional‑style processing.
  • Practical AEC examples: filtering beams longer than 6m, converting all lengths to mm, extracting unique material names.

These techniques will make your code more expressive and efficient for data‑processing tasks.

Comments

EARN AT THE COMFORT OF YOUR HOME

Sponsored content

Popular posts from this blog

Day 5 – Functions, Scope, and Docstrings

Day 1 – Python Foundations for AEC Professionals