Python // Object-oriented programming // Flask demo

Python Email System

Historical context

This page preserves the original learning exercise. Domain Mail has since evolved into a versioned service with a synthetic public showcase and a separate private live-mail mode. Read the Domain Mail v1.2 release note and its linked release history for the current architecture and security boundaries.

A small object-oriented Python email simulation built with three cooperating classes: Email, User and Inbox. The original exercise models how two users can send, list and read messages while keeping the message state inside the objects. It now also has a browser demo deployed as a small Flask application.

What this program does

The program creates two users dynamically, gives each user an inbox and lets them exchange messages. Each email stores sender, receiver, subject, body, timestamp and read/unread status.

  • Email: stores message data, marks messages as read and formats email output.
  • User: owns an inbox and exposes high-level actions such as sending and reading email.
  • Inbox: stores received emails, lists them, validates indexes and handles deletion.
  • Web demo: exposes the same learning idea through a browser interface deployed on Render.
python_email_system.py
import datetime

class Email:
    def __init__(self, sender, receiver, subject, body):
        self.sender = sender
        self.receiver = receiver
        self.subject = subject
        self.body = body
        self.timestamp = datetime.datetime.now()
        self.read = False

    def mark_as_read(self):
        self.read = True

    def display_full_email(self):
        self.mark_as_read()
        print("\n--- Email ---")
        print(f"From: {self.sender.name}")
        print(f"To: {self.receiver.name}")
        print(f"Subject: {self.subject}")
        print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}")
        print(f"Body: {self.body}")
        print("------------\n")

    def __str__(self):
        status = "Read" if self.read else "Unread"
        return (
            f"[{status}] From: {self.sender.name} | "
            f"Subject: {self.subject} | "
            f"Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}"
        )

class User:
    def __init__(self, name):
        self.name = name
        self.inbox = Inbox()

    def send_email(self, receiver, subject, body):
        email = Email(sender=self, receiver=receiver, subject=subject, body=body)
        receiver.inbox.receive_email(email)
        print(f"\nEmail sent from {self.name} to {receiver.name}!\n")

    def check_inbox(self):
        print(f"\n======== {self.name}'s Inbox ========")
        self.inbox.list_emails()

    def read_email(self, index):
        self.inbox.read_email(index)

    def delete_email(self, index):
        self.inbox.delete_email(index)

class Inbox:
    def __init__(self):
        self.emails = []

    def receive_email(self, email):
        self.emails.append(email)

    def list_emails(self):
        if not self.emails:
            print("Your inbox is empty.\n")
            return
        for i, email in enumerate(self.emails, start=1):
            print(f"{i}. {email}")
        print("==================================\n")

    def read_email(self, index):
        if not self.emails:
            print("Inbox is empty.\n")
            return
        actual_index = index - 1
        if actual_index < 0 or actual_index >= len(self.emails):
            print("Invalid email number.\n")
            return
        self.emails[actual_index].display_full_email()

    def delete_email(self, index):
        if not self.emails:
            print("Inbox is empty.\n")
            return
        actual_index = index - 1
        if actual_index < 0 or actual_index >= len(self.emails):
            print("Invalid email number.\n")
            return
        del self.emails[actual_index]
        print("Email deleted.\n")

Development notes

This exercise is useful because the responsibilities are separated clearly: Email represents the message, Inbox manages a collection of messages and User acts as the interface used by the rest of the program.

The browser demo extends the console exercise into a small deployed prototype. It keeps the learning scope simple while introducing a more realistic application boundary: frontend interaction, backend routes and persistence through Supabase.