Python in 30 Days - Day 13

Learning Python in 30 Days by going through a refresher course. Day 13 involves context managers.

Day 13 marks the last lesson day before turning to an actual application build for the remainder of the challenge.

The Challenge:

  1. Build a context manager class called OrderSession that:

    • Takes a restaurant name on init
    • On enter: prints "Opening order session for {name}..." and returns itself
    • Has an add_item(item, price) method that appends to an internal self.items list
    • Has a get_total() method that returns the sum of all item prices
    • On exit: prints the session summary — number of items and total — then prints "Session closed."
    • On exit: if an exception occurred, prints "Session ended with error: {exc_val}" instead
  2. Build the same thing as a @contextmanager generator function called order_session (lowercase) — same behavior, different implementation style

  3. Test the class version with a normal order

  4. Test the generator version with a normal order

  5. Test the class version with an exception mid-session to prove error handling works — deliberately raise one inside the with block

My Code:

from contextlib import contextmanager


class OrderSession:
    def __init__(self, restaurant_name):
        self.restaurant_name = restaurant_name
        self.items = []

    def __enter__(self):
        print(f"Opening order session for {self.restaurant_name}...")
        return self

    def add_item(self, item, price):
        self.items.append((item, price))

    def get_total(self):
        return sum(price for _, price in self.items)

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            # An exception occurred during the session
            print(f"Session ended with error: {exc_val}")
            return False  # Propagate the exception upward

        print(f"--- Session Summary ---")
        print(f"Items ordered: {len(self.items)}")
        print(f"Total price:   ${self.get_total():.2f}")
        print("Session closed.")
        return False


class OrderSessionGeneratorHelper:

    def __init__(self):
        self.items = []

    def add_item(self, item, price):
        self.items.append((item, price))

    def get_total(self):
        return sum(price for _, price in self.items)


@contextmanager
def order_session(restaurant_name):
    print(f"Opening order session for {restaurant_name}...")
    session = OrderSessionGeneratorHelper()

    try:
        yield session

        print(f"--- Session Summary ---")
        print(f"Items ordered: {len(session.items)}")
        print(f"Total price:   ${session.get_total():.2f}")
        print("Session closed.")

    except Exception as e:
        print(f"Session ended with error: {e}")
        raise


print("=== Test 1: Class-Based Version (Normal Order) ===")
with OrderSession("The Burger Joint") as session:
    session.add_item("Classic Burger", 12.50)
    session.add_item("Fries", 4.00)
    session.add_item("Vanilla Shake", 6.00)
    print(f"Running subtotal: ${session.get_total():.2f}")


print("\n=== Test 2: Generator-Based Version (Normal Order) ===")
with order_session("Pizza Palace") as session:
    session.add_item("Large Pepperoni", 18.00)
    session.add_item("Garlic Knots", 6.50)
    print(f"Running subtotal: ${session.get_total():.2f}")


print("\n=== Test 3: Class-Based Version (With Exception Handling) ===")
try:
    with OrderSession("Sushi World") as session:
        session.add_item("Salmon Roll", 9.00)
        session.add_item("Tuna Sashimi", 14.00)

        print("... Uh oh, card machine broke mid-order! ...")
        raise RuntimeError("Payment gateway timeout.")

        session.add_item("Miso Soup", 3.00)
except RuntimeError:
    print("Main program successfully caught the propagated exception.")

Running the Code:

=== Test 1: Class-Based Version (Normal Order) === 
Opening order session for The Burger Joint... 
Running subtotal: $22.50 
--- Session Summary --- 
Items ordered: 3 
Total price: $22.50 
Session closed. 

=== Test 2: Generator-Based Version (Normal Order) === 
Opening order session for Pizza Palace... 
Running subtotal: $24.50 
--- Session Summary --- 
Items ordered: 2 
Total price: $24.50 
Session closed. 

=== Test 3: Class-Based Version (With Exception Handling) === 
Opening order session for Sushi World... 
... Uh oh, card machine broke mid-order! ... 
Session ended with error: Payment gateway timeout. 
Main program successfully caught the propagated exception.

It’s been fun doing these lessons when I have time and re-engaging the Python writing muscle, if you will.

Now we turn to the real point of learning to code, which is actually building something useful. So, I decided it would be neat to build a CLI (command line interface) tool that allows you to get a quick summary of the current headlines around AI and then have the option to open the full article.