Skip to main content
PROGRAMMING / PYTHON / LESSON 07

Files, Exceptions & Modules.

Turn scripts into practical data workflows. Read and write files, parse CSV and JSON, recover from expected errors, and organize reusable Python tools.

Beginner → Intermediate75–95 minutesInteractive File PipelineReal Python in-browser
00

What you'll learn

We can already create functions that summarize inventory records. Now those records will come from files, and real-world data can be missing, malformed or inconsistent. Learn how Python handles each stage while preserving an honest account of failed records.

  • Read and write UTF-8 text with pathlib and with open(), choosing file modes intentionally.
  • Process line-oriented logs and CSV records without confusing them with raw text splitting.
  • Load and save JSON, distinguishing a parse error from an unexpected data structure.
  • Handle expected failures with targeted except branches and meaningful warnings.
  • Explain try, except, else and finally without suppressing unrelated errors.
  • Organize code using standard-library imports, functions and the __main__ guard.
  • Build a fictional inventory-to-report workflow and validate its output.
  • Create a small helper .py file in the temporary filesystem and import it from another script.
How to use the lesson: Read the written explanation, predict each code result, then try the File Pipeline Explorer before opening the real Python coding tasks.

About files in this lesson: The live Python editor uses a temporary, isolated browser filesystem. If you completed the optional local Python setup in Lesson 01, try these same exercises in your own practice folder and compare the file output. Files you create with open() stay inside that runtime—not on your laptop, and not in the JoeLearns server. Review the optional local Python setup → They are not durable downloads. Keep examples fictional, and never paste secrets or customer files.

FILES LIVE AT PATHS
01

Read a file without guessing where it lives

Many automation scripts begin with a file: an exported device list, a ticket attachment, or a previously generated report. A path identifies that file within a filesystem, while the current working directory determines how relative paths are resolved. Python's pathlib.Path helps express paths across operating systems. Here the workbench uses a temporary browser-only virtual filesystem, not the files on your computer. Every example creates its own fictional sample so the lesson works without downloads.

Relative versus absolute

Path('devices.txt') refers to a file relative to the current working directory. An absolute path names its location from the filesystem root. A path is not evidence that the file exists.

Explicit encoding

Open text files with encoding='utf-8' for a predictable text representation. UnicodeDecodeError may occur if the bytes are not actually UTF-8.

PYTHON / PREDICT THE RESULTRead before you run
from pathlib import Path
path = Path('devices.txt')
path.write_text('SW-01,online\nAP-02,offline\n', encoding='utf-8')
text = path.read_text(encoding='utf-8')
print(text.count('offline'))
Does Path('devices.txt') open an existing file, and what does the final line print? Reveal explanation after you predict.

Constructing Path does not open a file. write_text creates or replaces the temporary sample file; read_text loads it, so count('offline') prints 1.

Remember: Paths locate files, not content. Use deliberate filenames and encoding, and remember the browser workbench filesystem is temporary.
READ, WRITE, APPEND
02

Use with open() and choose the correct mode

Opening a file gives Python a handle that should be closed when the work is complete. A with block handles closing even when an exception interrupts processing. 'r' reads, 'w' creates or overwrites, 'a' appends and 'x' creates only if the file does not already exist. Accidentally using 'w' on an existing report can destroy its previous content. Use explicit modes and encodings, and consider temporary output and review before replacing important data on a real machine.

with means predictable cleanup

A context manager closes the handle on leaving the block, even if an error occurs. It does not undo earlier writes or make writes atomic.

Overwrite is a real risk

Use 'w' only when replacement is intentional. 'a' adds to the end. 'x' raises FileExistsError rather than silently replacing an existing file.

PYTHON / PREDICT THE RESULTRead before you run
with open('sample.log', 'w', encoding='utf-8') as file:
    file.write('Start\n')
with open('sample.log', 'a', encoding='utf-8') as file:
    file.write('Complete\n')
with open('sample.log', 'r', encoding='utf-8') as file:
    print(file.read().strip())
Why is the initial mode 'w' but the second 'a'? What two lines print? Reveal explanation after you predict.

The first operation creates or replaces sample.log; the second appends without erasing Start. Reading then prints Start followed by Complete on separate lines.

Remember: Use with for reliable cleanup. Choose read, write or append intentionally; closing a file is not the same as rolling back an overwrite.
PROCESS A RECORD AT A TIME
03

Iterate through log lines

A log file can contain thousands of lines. Calling read() loads its entire contents into one string, whereas iterating directly over a text file yields lines one at a time. A trailing newline is part of a line, so strip() or rstrip('\n') may be needed before comparing. Be careful: strip removes all surrounding whitespace, which might matter in structured data. For simple troubleshooting notes, line iteration is a useful first step toward stream processing.

Do not load everything by default

for line in file avoids creating a full list of log entries. Small educational samples can still use read() for simplicity.

Preserve meaning

rstrip('\n') removes a trailing newline without deleting meaningful leading whitespace. For CSV or JSON, prefer a format-aware parser instead of manually splitting every line.

PYTHON / PREDICT THE RESULTRead before you run
with open('events.log', 'w', encoding='utf-8') as f:
    f.write('INFO online\nWARN offline\n')
alerts = []
with open('events.log', 'r', encoding='utf-8') as f:
    for line in f:
        if line.startswith('WARN'):
            alerts.append(line.rstrip('\n'))
print(alerts)
Why is the result a one-item list rather than two, and what happens to the newline? Reveal explanation after you predict.

Only the line beginning with WARN matches the condition. rstrip('\n') removes its trailing newline, producing ['WARN offline'].

Remember: For large text files, iterate lines rather than loading everything. Keep parsing logic matched to the data format.
CSV IS STRUCTURED TEXT
04

Parse CSV by column name, not by guesswork

CSV often appears in exports from inventory, ticketing and monitoring tools. The standard csv module understands quoted commas and column boundaries that a naive split(',') does not. csv.DictReader uses the first row as column names and yields dictionaries for subsequent rows. Decide what to do with missing columns and blank values rather than silently treating them as healthy. When writing CSV, use newline='' so the module controls row delimiters, especially across operating systems.

Quoted commas are data

A site field such as "HQ, Building A" is one CSV value when quoted correctly. split(',') would incorrectly turn it into two.

Headers are part of the contract

DictReader maps keys to columns. Missing expected headers need explicit validation. get('status', 'unknown') handles a missing key but not every malformed row.

PYTHON / PREDICT THE RESULTRead before you run
import csv
from io import StringIO
sample = 'host,status\nSW-01,online\nAP-02,offline\n'
reader = csv.DictReader(StringIO(sample))
offline = [row['host'] for row in reader if row['status'] == 'offline']
print(offline)
What is a DictReader row, and what value does offline hold? Reveal explanation after you predict.

Each row is a dictionary keyed by the CSV header. Only AP-02 has offline status, so offline contains ['AP-02'].

Remember: Use the standard csv module for CSV. Identify required columns and test malformed or missing fields.
OBJECTS AND ARRAYS TRAVEL AS JSON
05

Convert JSON text into Python collections

APIs and configuration exports commonly represent data as JSON. json.loads() parses a JSON string; json.load() reads JSON from a file object. json.dumps() produces text; json.dump() writes to an open file. JSON objects become dictionaries, arrays become lists, and true/false/null become True/False/None. Invalid JSON raises json.JSONDecodeError. Never assume a successful parse means all expected keys or data types are present.

Parse versus validate

A payload can be valid JSON but still miss 'devices' or contain the wrong type. Check important fields before trusting them.

Keep output deliberate

json.dumps(data, sort_keys=True) can make a simple report easier to compare. JSON is data, not Python source code; do not use eval() to parse it.

PYTHON / PREDICT THE RESULTRead before you run
import json
raw = '{"site": "HQ", "devices": ["SW-01", "AP-02"]}'
record = json.loads(raw)
print(record['site'])
print(len(record['devices']))
Does json.loads create a Python dictionary or a JSON text string? Reveal explanation after you predict.

It parses the text into a Python dictionary containing the site string and devices list. The program prints HQ then 2.

Remember: Use json.load/loads to parse and dump/dumps to generate JSON. Validate the shape after parsing.
FAILURES HAVE NAMES
06

Handle the specific error you expect

Files can be missing, numeric fields can be invalid, and JSON can be malformed. Python raises exceptions when a normal operation cannot continue. Place only the operation likely to fail inside try, then catch a specific exception such as FileNotFoundError, ValueError or json.JSONDecodeError. A broad except: hides programming mistakes and can even intercept system-level signals. Logging an error is different from treating the failed record as valid; preserve the distinction.

FileNotFoundError

Opening a missing path in read mode raises FileNotFoundError. A useful handler reports the missing input without creating fake content.

ValueError

int('443') works but int('N/A') raises ValueError. Catch just that failure while processing an inventory field.

PYTHON / PREDICT THE RESULTRead before you run
values = ['443', 'N/A', '8080']
ports = []
for value in values:
    try:
        ports.append(int(value))
    except ValueError:
        print('Skipped invalid port:', value)
print(ports)
Which value causes the exception, and does the loop keep processing? Reveal explanation after you predict.

N/A causes ValueError during int conversion. The handler prints a warning and iteration continues; the resulting list is [443, 8080].

Remember: Catch expected exceptions narrowly and keep invalid data visibly separate from valid results.
RECOVERY WITH CLEAR CONTROL FLOW
07

Understand try / except / else / finally

The else block of a try statement runs only when the try block finishes without an exception. The finally block runs when leaving the try statement, whether an exception was raised or not. Use else for operations dependent on successful parsing; use finally for cleanup that cannot use a context manager. A finally block does not erase an exception automatically, and return statements inside finally can accidentally mask failures. A with block remains the preferred way to close ordinary files.

Only successful work reaches else

Put follow-up work in else when it should happen only if parsing or reading succeeded.

finally is not a success signal

finally runs after either outcome. Use it for cleanup, not to claim that a failed import or read operation was successful.

PYTHON / PREDICT THE RESULTRead before you run
try:
    port = int('N/A')
except ValueError:
    print('Invalid port')
else:
    print('Port:', port)
finally:
    print('Checked input')
Which messages print, and why does the else block not run? Reveal explanation after you predict.

Invalid port and Checked input print. Conversion raises ValueError, so the except handles it; the else branch is skipped, while finally still runs.

Remember: try handles risky work, except handles named failures, else handles success and finally handles unconditional cleanup.
ORGANIZE AND REUSE CODE
08

Import modules without confusing names and side effects

A module is a Python file whose definitions can be imported by other files. The standard library already provides csv, json, pathlib and many other useful modules. import json keeps the module namespace clear; from pathlib import Path brings in a specific name. Imports execute top-level module code the first time a module is loaded, so keep reusable helpers separate from demo runs. The if __name__ == '__main__' guard lets a file run as a script without running its demo every time another module imports it.

Prefer explicit imports

import json followed by json.loads(...) makes dependencies clear. Avoid names such as json.py or csv.py for your own scripts because they can shadow standard-library modules.

Main guard separates reuse from execution

Functions defined above the guard can be imported. Code inside if __name__ == '__main__': normally runs only when the file is executed as the entry-point script.

PYTHON / PREDICT THE RESULTRead before you run
import statistics

def average_latency(samples):
    return statistics.mean(samples)

if __name__ == '__main__':
    print(average_latency([20, 40, 60]))
What prints when the file runs directly, and what happens on import? Reveal explanation after you predict.

Running the file directly prints 40. Importing it defines average_latency without running the guarded demo. The import itself can still run other unguarded top-level statements.

Remember: Use modules to separate concerns, explicit imports to show dependencies, and a main guard for demonstration or entry-point code.
YOUR OWN TWO-FILE SCRIPT
09

Create a helper module and import it

A module does not have to come from the standard library: you can create your own file ending in .py and import its functions. A common arrangement is helpers.py for reusable logic and report.py for the entry-point script. In this temporary browser workspace, writing a .py file with pathlib lets you practice the same idea without installing an editor or creating anything on your computer. An import runs the module's top-level statements the first time it is loaded; keep demonstrations under a main guard.

The filename becomes an importable name

If your file is named status_helpers.py, import status_helpers (without .py). In a real project, Python searches for importable modules on sys.path, including your configured project location.

Functions can be shared without copying

A helper with one defined behavior can be called by several scripts. Keep secrets and machine-specific paths out of a reusable module. For larger projects, Python packaging becomes a separate topic.

PYTHON / PREDICT THE RESULTRead before you run
from pathlib import Path
Path('status_helpers.py').write_text(
    "def is_offline(status):\n    return status.strip().lower() == 'offline'\n",
    encoding='utf-8'
)
import status_helpers
print(status_helpers.is_offline(' OFFLINE '))
Which file defines is_offline, and does importing it automatically call that function? Reveal explanation after you predict.

status_helpers.py defines the function. The import makes it available as status_helpers.is_offline; the print line explicitly calls it and prints True. Importing a module executes unguarded top-level code but does not call a function just because it is defined.

Remember: Create a small .py file for reusable helpers, import its module name from another script, and put demo execution under a main guard.
PUT IT ALL TOGETHER
10

From CSV input to a verified JSON report

A practical automation workflow reads structured records, validates fields, skips or reports malformed rows, builds a reliable summary, and writes a clear output artifact. Compose small helpers rather than one giant try block. Check required headers, preserve unknowns instead of reclassifying them, and only publish an output after confirming it is correct. This browser lesson uses fictional data and temporary virtual files, so it cannot overwrite anything on a learner's computer.

Separate parse, validate and report

A parsing problem should be reported as a bad input, not as a healthy device. Keep invalid row counts independent from offline counts.

Verify what was saved

After writing JSON, read it back and parse it to confirm the report's shape and content. Real operations also benefit from atomic replacement and backups.

PYTHON / PREDICT THE RESULTRead before you run
import csv, json
with open('inventory.csv', 'w', encoding='utf-8', newline='') as f:
    f.write('host,status\nSW-01,online\nAP-02,offline\n')
with open('inventory.csv', newline='', encoding='utf-8') as f:
    offline = sorted(r['host'] for r in csv.DictReader(f)
                     if r['status'] == 'offline')
report = {'offline_hosts': offline}
print(json.dumps(report, sort_keys=True))
What JSON report prints, and why are rows parsed with DictReader? Reveal explanation after you predict.

It prints {"offline_hosts": ["AP-02"]}. DictReader assigns columns by header name and handles CSV quoting, avoiding brittle comma splitting.

INTERACTIVE FILE PIPELINE / FICTIONAL DEVICE RECORDS

Trace data from file to verified report.

Edit the simulated file, inject malformed rows, and walk through how a Python-style pipeline parses, validates and summarizes records. This explorer is a visualization; the real Python workbench below executes Python.

STEP 1 / 4 — Input file

Begin with source bytes decoded as text. In the Python editor, use with open(..., encoding='utf-8') or the appropriate JSON/CSV reader.

75 charactersCSV columns and quoted fields

No file is uploaded or saved by this explorer. It uses built-in fictional samples only; never paste customer records, passwords or secrets into a coding exercise.

Remember: Build an observable pipeline: open safely, parse with the right format, validate assumptions, report errors and verify written results.
11 / APPLY IT YOURSELF
11

Read your first temporary file in Python

Now execute actual Python. The first exercise creates a sample technician note for you. Read it using a context manager, inspect your variables and console output, then continue into writing, CSV, JSON, error handling, modules and a combined report in Practice.

JOELEARNS / PYTHON WORKBENCH

Write it. Run it. Understand it.

Read and write temporary browser files, parse CSV and JSON, and handle errors. Nothing is saved to your actual computer.

Python 3.14 · in-browser
CHALLENGE 01 / 8 · BEGINNER

Read a technician note from a file

A fictional device note has already been written to a temporary browser file. Read that file with with open(..., 'r', encoding='utf-8'), remove the final newline, store the exact text in note, and print 'AP-02: offline'. Do not manually assign the answer or access any real computer files.

EXPECTED OUTPUTAP-02: offline
script.py editableCtrl / Cmd + Enter to run
Your code is stored only in session storage when enabled. Never paste secrets. Clearing this option clears saved drafts, not the editor.
Code executes only when you choose Run or Check. No account needed.
HINTS · OPTIONAL

Try solving it before asking for help. Hints never show a completed program.

PYTHON OUTPUTReady

Your printed output and errors will appear here.

VARIABLE TRACER

Run the code, then step through the executed lines to watch variable values change.

Temporary virtual files: Files made with open() live only in the browser Python runtime. They can remain between runs in this tab until the runtime resets, but are not your computer files or durable downloads. Each task creates fictional sample inputs. Do not enter secrets.

About this environment: The Python runtime is loaded on demand from a version-pinned CDN and runs inside a sandboxed, opaque-origin browser frame. Runs have a time limit. OS/network automation is outside this introductory workbench. If your code calls input(), a response panel appears to supply text to those calls. Don't paste secrets or untrusted scripts.

Next required step: complete the 8-challenge Practice milestone →
TAKE THE NEXT STEP / PRACTICE

Build a workflow that survives imperfect data.

The coding clinic has eight tasks culminating in a CSV-to-JSON health report. It checks your final results and key variables, while optional hints guide your reasoning without supplying a full solution.

Continue to all Files & Exceptions challenges →
12

Reference material

Python's standard library documentation is worth bookmarking. It explains exact file behavior, exceptions, and how readers and writers handle structured formats.

NEXT REQUIRED STEP · PYTHON FUNDAMENTALS

Complete the Practice milestone

You've reached the end of this written lesson. Your next step is practice—complete Python Files, Exceptions & Modules Coding Challenges before proceeding to a new lesson. This is milestone 14 of 18. You can revisit the explanation whenever you need it.