Python Foundations · Day 6

Files, Error Handling & Modules

What you will cover

Opening, reading and writing files, CSVs without pandas, understanding tracebacks, try/except/else/finally, raising exceptions, and core standard library modules.

How to use this day

Plan about 2 hours. Create real files on disk and intentionally trigger errors to read and understand tracebacks.

DAY 6 - Files, Error Handling & Modules

6.1 Reading and Writing Files

Data lives in files. Before you use pandas, it is important to understand how Python handles files at the fundamental level.

Opening a file

file = open("data.txt", "r")   # "r" = read mode
content = file.read()
file.close()

Always close a file after you are done with it. Forgetting to close files can cause data loss or locked files. The better approach is the with statement.

The with statement (context manager)

with open("data.txt", "r") as file:
    content = file.read()
# File is automatically closed here, even if an error occurs

Always use with when working with files.

File modes

ModeDescription
"r"Read (default). Error if file does not exist.
"w"Write. Creates file if not exists. Overwrites existing content.
"a"Append. Creates file if not exists. Adds to the end.
"r+"Read and write.

Reading methods

with open("data.txt", "r") as f:
    content = f.read()          # read entire file as one string
    
with open("data.txt", "r") as f:
    lines = f.readlines()       # read into a list of lines (each with \n)
    
with open("data.txt", "r") as f:
    for line in f:              # iterate line by line (memory-efficient)
        print(line.strip())     # .strip() removes the \n

Writing to a file

with open("output.txt", "w") as f:
    f.write("Line 1\n")
    f.write("Line 2\n")

# Append without overwriting
with open("output.txt", "a") as f:
    f.write("Line 3\n")

Working with CSV files (manual approach)

CSV (Comma-Separated Values) is one of the most common data formats. Here is how to read and write them without any library:

# Writing a CSV
headers = ["name", "age", "city"]
rows = [
    ["Alice", 28, "London"],
    ["Bob", 34, "Manchester"],
    ["Charlie", 22, "Edinburgh"],
]

with open("people.csv", "w") as f:
    f.write(",".join(headers) + "\n")
    for row in rows:
        f.write(",".join(str(item) for item in row) + "\n")

# Reading a CSV
with open("people.csv", "r") as f:
    lines = f.readlines()

headers = lines[0].strip().split(",")
print("Headers:", headers)

for line in lines[1:]:
    values = line.strip().split(",")
    record = dict(zip(headers, values))
    print(record)

Output:

Headers: ['name', 'age', 'city']
{'name': 'Alice', 'age': '28', 'city': 'London'}
{'name': 'Bob', 'age': '34', 'city': 'Manchester'}
{'name': 'Charlie', 'age': '22', 'city': 'Edinburgh'}

Understanding this manual process will make pandas feel like magic when you meet it in your course.

6.2 Error Handling

Errors are inevitable. A file might not exist. A user might type text where a number is expected. A network request might fail. Professional code anticipates these problems and handles them gracefully instead of crashing.

Types of errors

  • SyntaxError - invalid Python code (found before the programme runs)
  • TypeError - wrong type used in an operation: "hello" + 5
  • ValueError - right type, wrong value: int("hello")
  • KeyError - dictionary key does not exist: d["missing"]
  • IndexError - list index out of range: lst[99]
  • FileNotFoundError - file does not exist
  • ZeroDivisionError - dividing by zero

Reading a traceback

When Python encounters an error, it prints a traceback - the trail of function calls that led to the error. Always read it from the bottom up: the last line tells you what went wrong, the lines above tell you where.

Traceback (most recent call last):
  File "script.py", line 8, in <module>
    result = divide(10, 0)
  File "script.py", line 3, in divide
    return a / b
ZeroDivisionError: division by zero

try / except

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Error: Cannot divide by zero.")

Handling multiple exception types

def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "Error: Division by zero."
    except TypeError:
        return "Error: Both inputs must be numbers."

print(safe_divide(10, 2))    # 5.0
print(safe_divide(10, 0))    # Error: Division by zero.
print(safe_divide(10, "x"))  # Error: Both inputs must be numbers.

else and finally

try:
    number = int(input("Enter a number: "))
except ValueError:
    print("That is not a valid number.")
else:
    print(f"You entered: {number}")   # runs only if no exception occurred
finally:
    print("This always runs.")        # runs regardless of success or failure

finally is typically used to release resources (closing files, database connections) - though with with statements this is usually handled automatically.

raise - raising your own exceptions

def set_age(age):
    if age < 0 or age > 120:
        raise ValueError(f"Invalid age: {age}. Must be between 0 and 120.")
    return age

try:
    set_age(-5)
except ValueError as e:
    print(e)   # Invalid age: -5. Must be between 0 and 120.

A practical file-reading pattern

def read_file(filepath):
    try:
        with open(filepath, "r") as f:
            return f.read()
    except FileNotFoundError:
        print(f"File not found: {filepath}")
        return None
    except PermissionError:
        print(f"No permission to read: {filepath}")
        return None

6.3 Modules and the Standard Library

A module is a Python file containing functions, classes and variables that you can import and use in your own code.

Importing modules

import math
print(math.pi)          # 3.141592653589793
print(math.sqrt(144))   # 12.0
print(math.floor(3.9))  # 3
print(math.ceil(3.1))   # 4
# Import specific items
from math import sqrt, pi
print(sqrt(25))   # 5.0
print(pi)         # 3.141592653589793

# Import with an alias
import math as m
print(m.factorial(5))   # 120

Key standard library modules

random
import random

print(random.randint(1, 100))    # random integer between 1 and 100
print(random.random())           # random float between 0.0 and 1.0
print(random.choice(["a","b","c"]))  # random item from a list

data = [1, 2, 3, 4, 5]
random.shuffle(data)             # shuffle in place
print(data)
datetime
from datetime import datetime, date, timedelta

now = datetime.now()
print(now)                              # 2025-03-17 14:32:10.123456
print(now.strftime("%d/%m/%Y %H:%M"))  # 17/03/2025 14:32

today = date.today()
birthday = date(1995, 6, 15)
age_days = (today - birthday).days
print(f"Days since birthday: {age_days}")

tomorrow = today + timedelta(days=1)
print(f"Tomorrow: {tomorrow}")
os
import os

print(os.getcwd())                # current working directory
print(os.listdir("."))            # list files in current directory
os.makedirs("output", exist_ok=True)  # create a directory safely
print(os.path.exists("data.txt")) # True or False
print(os.path.join("folder", "data.txt"))  # OS-appropriate path
sys
import sys

print(sys.version)   # Python version info
print(sys.platform)  # 'win32', 'darwin', 'linux'
sys.exit()           # terminate the programme

✏️ Day 6 Exercises

Exercise 6.1 - File Round-Trip

Write a programme that:

  1. Creates a file called students.csv with headers name,score,grade and at least 5 rows of data.
  2. Reads the file back.
  3. For each student, converts the score to an integer and prints a formatted summary line.

Hint: use with open(..., "w") to write and with open(..., "r") to read. Use f.readlines() and loop from lines[1:] to skip the header.

Exercise 6.2 - Robust Input Validator

Write a function get_positive_integer(prompt) that:

  1. Asks the user for input using the given prompt.
  2. Attempts to convert it to an integer.
  3. Checks that the integer is positive (greater than 0).
  4. If either check fails, prints an appropriate error message and asks again.
  5. Returns the valid integer once a correct value is given.

Hint: use a while True loop with try/except ValueError. Use a nested if to check positivity.

Exercise 6.3 - Date Calculator

Using the datetime module, write a programme that:

  1. Asks the user for a date in DD/MM/YYYY format.
  2. Calculates how many days until (or since) that date from today.
  3. Prints an appropriate message: “X days to go”, “Today!”, or “X days ago”.

Hint: use datetime.strptime(string, "%d/%m/%Y").date() to parse the user's input. Subtract today's date to get a timedelta. Use .days to get the integer.

Exercise 6.4 - Challenge: Log File Analyser

Create a simple log file manually (or using Python) with the following format - each line is a log entry:

2025-03-01 ERROR Database connection failed
2025-03-01 INFO  Server started
2025-03-02 WARNING Disk usage above 80%
2025-03-02 ERROR Timeout on API call
2025-03-03 INFO  Backup completed
2025-03-03 ERROR Memory limit exceeded

Write a programme that reads the file and prints:

  • Total number of log entries.
  • Count of each log level (ERROR, WARNING, INFO).
  • All ERROR entries, formatted clearly.

Hint: split each line on whitespace with a limit: line.split(None, 2) splits on any whitespace into at most 3 parts: date, level, message.

📌 Day 6 Summary

ConceptWhat you learned
open() & withOpen files safely; context manager ensures closure
File modes"r", "w", "a"
.read(), .readlines()Reading file content
.write()Writing to files
CSV manual handlingRead/write delimited data without libraries
try/except/else/finallyHandle errors gracefully
raiseRaise your own exceptions
importUse modules and the standard library
math, random, datetime, osKey standard library modules

Tomorrow is the capstone day. You will get a gentle introduction to Object-Oriented Programming, write your first complete data programme, and take a first look at the libraries waiting for you in your course.