import gradio as gr import io import contextlib import traceback # ---------- Python runner ---------- def run_python(code: str) -> str: """ Execute user-provided Python code and capture stdout / errors. NOTE: This is for teaching demos; do NOT use this pattern in a multi-user production environment without sandboxing. """ stdout_buffer = io.StringIO() try: # Provide a very simple, mostly empty environment local_env = {} global_env = {"__builtins__": __builtins__} with contextlib.redirect_stdout(stdout_buffer): exec(code, global_env, local_env) output = stdout_buffer.getvalue() if not output.strip(): output = "[No output produced]" return output except Exception: error_text = stdout_buffer.getvalue() error_text += "\n" + traceback.format_exc() return error_text # ---------- Course content ---------- lessons = [ { "title": "Lesson 1: Setup & Simple Apps", "description": """ ### Lesson 1: Setting Up Python and Developing a Simple Application **Topics** - Topic A: Set Up the Development Environment - Topic B: Write Python Statements - Topic C: Create a Python Application - Topic D: Prevent Errors **You’ll practice:** - Using `print()` and variables - Reading user input with `input()` - Building a tiny interactive console app **Mini-project idea:** Create a **“Profile Card”** script that asks for your name, age, and hobby, then prints a nicely formatted profile. """, "starter_code": '''# Lesson 1: Simple Hello & Input print("👋 Welcome to Lesson 1: Setup & Simple Apps") name = input("What is your name? ") age = input("How old are you? ") print(f"Hello, {name}! You are {age} years old.") # Try: # 1. Ask for a favorite color and food and add them to the message. # 2. Create a small "profile card" printed with multiple lines, e.g.: # Name: ... # Age: ... # Favorite color: ... ''' }, { "title": "Lesson 2: Simple Data Types", "description": """ ### Lesson 2: Processing Simple Data Types **Topics** - Topic A: Process Strings and Integers - Topic B: Process Decimals, Floats, and Mixed Number Types **You’ll practice:** - Combining strings - Doing arithmetic with integers and floats - Using `round()` and basic numeric conversions **Mini-project idea:** Create a **BMI & Health Stats** script that asks for height, weight, and age, calculates BMI, and prints a short health summary. """, "starter_code": '''# Lesson 2: Strings, Integers, Floats print("✨ Lesson 2: Simple Data Types") first_name = "Alice" last_name = "Smith" age = 30 height_m = 1.70 weight_kg = 65.0 full_name = first_name + " " + last_name bmi = weight_kg / (height_m ** 2) print("Full name:", full_name) print("Age next year:", age + 1) print("BMI (unrounded):", bmi) print("BMI (rounded to 2 decimals):", round(bmi, 2)) # Try: # 1. Ask the user for their name and birth year; compute their age. # 2. Convert a temperature from Fahrenheit to Celsius: # celsius = (f - 32) * 5/9 # 3. Ask for two prices and print the subtotal, tax, and total. ''' }, { "title": "Lesson 3: Data Structures", "description": """ ### Lesson 3: Processing Data Structures **Topics** - Topic A: Process Ordered Data Structures (lists, tuples) - Topic B: Process Unordered Data Structures (sets, dictionaries) **You’ll practice:** - Storing groups of values in lists and tuples - Using sets to deduplicate data - Using dictionaries to store labeled data **Mini-project idea:** Build a tiny **“Student Gradebook”** using a dictionary of names and scores. Compute averages and print a report. """, "starter_code": '''# Lesson 3: Lists, Tuples, Sets, Dictionaries print("📚 Lesson 3: Data Structures") # Ordered: lists and tuples fruits = ["apple", "banana", "cherry"] scores = (88, 92, 79) print("Fruits:", fruits) print("First fruit:", fruits[0]) print("Scores:", scores) print("Average score:", sum(scores) / len(scores)) # Unordered: sets and dictionaries numbers_with_duplicates = [1, 2, 2, 3, 3, 3] unique_numbers = set(numbers_with_duplicates) print("Original numbers:", numbers_with_duplicates) print("Unique numbers:", unique_numbers) student = { "name": "Alice", "age": 20, "grade": "A" } print("Student name:", student["name"]) print("\nAll student key/value pairs:") for key, value in student.items(): print(f"{key}: {value}") # Try: # 1. Add a new key "major" to the student dictionary, then print it. # 2. Create a list of 5 movies and print the first and last. # 3. Create a "gradebook" dict: student_name -> list of scores, # then print each student's average. ''' }, { "title": "Lesson 4: Conditionals & Loops", "description": """ ### Lesson 4: Writing Conditional Statements and Loops **Topics** - Topic A: Write a Conditional Statement (`if`, `elif`, `else`) - Topic B: Write a Loop (`for`, `while`) **You’ll practice:** - Making decisions in code - Repeating actions until some condition is met - Combining `if` with loops for basic logic **Mini-project idea:** Create a **“Number Guessing Game”** that picks a random number and gives the user several attempts with hints (too high / too low). """, "starter_code": '''# Lesson 4: Conditionals & Loops print("🔁 Lesson 4: Conditionals and Loops") # Conditional example score = int(input("Enter a score between 0 and 100: ")) if score >= 90: grade = "A" elif score >= 80: grade = "B" elif score >= 70: grade = "C" elif score >= 60: grade = "D" else: grade = "F" print(f"Your grade is: {grade}") # Loop example: print even numbers 2-20 print("\nEven numbers from 2 to 20:") for n in range(2, 21, 2): print(n, end=" ") print() # newline # Try: # 1. Use a while loop to ask for a password until it is 'secret'. # 2. Write an if/elif/else to classify a number as positive, negative, or zero. # 3. (Challenge) Make a number guessing game (1-20) that tells the user if # their guess is too high or too low. ''' }, { "title": "Lesson 5: Functions, Classes, Modules", "description": """ ### Lesson 5: Structuring Code for Reuse **Topics** - Topic A: Define and Call a Function - Topic B: Define and Instantiate a Class - Topic C: Import and Use a Module **You’ll practice:** - Creating functions with parameters and return values - Creating simple classes with attributes and methods - Using built-in modules like `math`, `random` **Mini-project idea:** Create a **“Banking Simulator”** with a `BankAccount` class that supports deposit, withdraw, and showing balance. """, "starter_code": '''# Lesson 5: Functions, Classes, and Modules print("🧩 Lesson 5: Reusable Code") # --- Functions --- def add(a, b): """Return the sum of a and b.""" return a + b result = add(10, 5) print("10 + 5 =", result) # --- Classes --- class Car: def __init__(self, make, model, year): self.make = make self.model = model self.year = year def description(self): return f"{self.year} {self.make} {self.model}" my_car = Car("Toyota", "Corolla", 2022) print("My car:", my_car.description()) # --- Modules --- import math import random print("Square root of 16:", math.sqrt(16)) print("Random integer from 1 to 6:", random.randint(1, 6)) # Try: # 1. Write a function is_even(n) that returns True if n is even. # 2. Create a BankAccount class with deposit and withdraw methods, # and test it with a few transactions. # 3. Use random.randint to simulate rolling two dice and print their sum. ''' }, { "title": "Lesson 6: Files & Directories", "description": """ ### Lesson 6: Writing Code to Process Files and Directories **Topics** - Topic A: Write to a Text File - Topic B: Read from a Text File - Topic C: Get the Contents of a Directory - Topic D: Manage Files and Directories ⚠️ In a hosted environment, file access is temporary and sandboxed, but the patterns you learn here transfer directly to your local machine. **You’ll practice:** - Creating directories and files - Reading and writing text data - Listing directory contents **Mini-project idea:** Create a simple **“Log Writer”** that appends timestamped messages to a log file, then reads and prints the last N lines. """, "starter_code": '''# Lesson 6: Files & Directories print("📂 Lesson 6: Files and Directories") from pathlib import Path # Create a folder folder = Path("demo_folder") folder.mkdir(exist_ok=True) # Write to a file file_path = folder / "example.txt" file_path.write_text("Hello from Lesson 6!\\nThis is a test file.") print("Wrote file:", file_path) # Read from a file content = file_path.read_text() print("\\nFile contents:") print(content) # List directory contents print("\\nContents of demo_folder:") for item in folder.iterdir(): print(" -", item.name) # Try: # 1. Add another file with different text. # 2. Read both files and print their contents. # 3. Add a timestamp to each line you write into a 'log.txt' file. ''' }, { "title": "Lesson 7: Exceptions", "description": """ ### Lesson 7: Dealing with Exceptions **Topics** - Topic A: Handle Exceptions - Topic B: Raise Exceptions **You’ll practice:** - Wrapping risky code in `try/except` - Catching specific errors like `ValueError` and `ZeroDivisionError` - Raising your own exceptions when inputs are invalid **Mini-project idea:** Create a **“Safe Calculator”** that: - Accepts user input for operations - Handles invalid inputs gracefully - Prevents divide-by-zero errors """, "starter_code": '''# Lesson 7: Exceptions print("⚠️ Lesson 7: Exceptions") def safe_divide(a, b): try: return a / b except ZeroDivisionError: print("Error: cannot divide by zero!") return None x = float(input("Enter numerator: ")) y = float(input("Enter denominator: ")) result = safe_divide(x, y) print("Result:", result) # Raising your own exception def set_age(age): if age < 0 or age > 120: raise ValueError("Age must be between 0 and 120.") return age try: age_value = int(input("Enter an age (0-120): ")) print("Valid age:", set_age(age_value)) except ValueError as e: print("Invalid age:", e) # Try: # 1. Wrap more user input in try/except to catch ValueError. # 2. Write a function that raises ValueError if a discount is # not between 0 and 100. # 3. Build a small "safe calculator" that handles bad input gracefully. ''' }, ] appendix_markdown = """ ## 📎 References & Appendices This tab consolidates extra reference material for the course. --- ### Appendix A: Major Differences Between Python 2 and 3 Modern development uses **Python 3**, but here are some historical differences: - `print` - Python 2: `print "Hello"` - Python 3: `print("Hello")` - Integer division - Python 2: `5/2` → `2` (integer when both are ints) - Python 3: `5/2` → `2.5`, use `5//2` for integer division. - Text and bytes - Python 2: `str` (bytes) vs `unicode`. - Python 3: `str` is Unicode by default; `bytes` is a separate type. - `input()` - Python 2: `input()` evaluates, `raw_input()` returns string. - Python 3: `input()` always returns a string. - Standard library layout - Some modules reorganized (e.g., `urllib` family). If you’re starting now, you almost never need Python 2, but you might see old code in the wild. --- ### Appendix B: Mini Python Style Guide (PEP 8 Highlights) Follow these conventions to keep your code clean and readable: **Indentation** - Use **4 spaces** per indentation level. - Avoid mixing tabs and spaces. **Naming** - Variables / functions: `snake_case` (e.g., `total_price`, `calculate_bmi`). - Classes: `CapWords` (e.g., `BankAccount`). - Constants: `UPPER_CASE` (e.g., `PI = 3.14159`). **Spacing** - Around operators: `x = a + b`, not `x=a+b`. - After commas: `func(a, b, c)`. **Imports** - At the **top** of the file. - One import per line is preferred. **Comments & Docstrings** - Use `#` for short explanations above a line or block. - Use triple-quoted strings for docstrings, for example: `\"\"\"This is a multi-line docstring.\"\"\"`. **Structure** - Break large scripts into functions and classes. - For larger programs, use: ```python if __name__ == "__main__": main() with gr.Tabs(): # Main lesson tabs for lesson in lessons: with gr.Tab(lesson["title"]): gr.Markdown(lesson["description"]) code = gr.Code( value=lesson["starter_code"], language="python", label="✏️ Edit and run your Python code here", interactive=True, ) with gr.Row(): run_button = gr.Button("▶️ Run code", variant="primary") clear_button = gr.Button("🧹 Clear output") output = gr.Textbox( label="📤 Output (stdout and errors)", lines=14, show_copy_button=True, ) # Bind buttons run_button.click(run_python, inputs=code, outputs=output) clear_button.click(lambda: "", outputs=output) # References / Appendices tab with gr.Tab("References & Appendices"): gr.Markdown(appendix_markdown) gr.Markdown( """