Advanced

Failures that matter

Real files fail in the middle. A half-written file is worse than no file, so the plan has to say what a failure leaves behind.

What you will see

2

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Plan a safe write.

1

Rows kept.

rows = ["a", "b", "c"] half = rows[:1] print(len(half))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
1
2

Rename runs.

import os os.replace("a", "a") print("moved")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
moved
3

Bytes written.

out = "data" print(len(out))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
4
4

Backup grew.

rows = ["a"] backup = list(rows) backup.append("b") print(len(backup))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
2

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A partial file is worse than none. Write to a temp name, then rename, so the switch is one atomic step.
1 build the new text
↓
2 write a temp file
↓
3 rename into place
↓
4 report a failure
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

rows = ["a", "b", "c"] sets nothing on screen
half = rows[:1] sets nothing on screen
print(len(half)) 1
Why this works
Atomic means one switch

A rename is the point where old becomes new. Everything before it can fail with no visible change.

Two states are valid

While a write runs, both the old file and the new one are readable. Readers must accept either, not assume one.

Examples

Half a file

rows = ["a", "b", "c"]
half = rows[:1]
print(len(half))

Writing row by row can stop in the middle. The reader then sees a truncated file.

Write to a temp name

import os
os.replace("a", "a")
print("moved")

Write a temp file, then rename it into place. A rename is the switch point.

Flush is not enough

out = "data"
print(len(out))

A flush pushes bytes out. The file is only complete once the name is swapped in.

Read the same rules

import os
print(os.path.exists("a"))

A reader must handle the old file and the new one. Both are valid states at once.

Keep the old one

rows = ["a"]
backup = list(rows)
backup.append("b")
print(len(backup))

Write the new version before removing the old. A crash then leaves a working file.

Say what failed

import sys
print("out.txt: cannot write", file=sys.stderr)

Name the path and the reason. A silent partial file is a wrong answer.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Finding many files
Previous Lesson
Finding many files
Next Lesson
Atomic replace
Atomic replace →