Where the write went
Here is a common and genuinely spooky surprise: a program calls write() on a file, and the data is not in the file yet.
Asking the OS to move bytes to disk is expensive, since unit 6 showed that giving up the CPU and waiting on hardware costs real time. So Python does not send every little write() to the OS. It collects writes in a buffer, a chunk of memory inside the process, and hands the whole batch over later.
| Buffer is handed to the OS when | Trigger |
|---|---|
| the buffer fills | automatic |
flush() is called | explicit |
| the file is closed | automatic, including at the end of a with block |
Batching turns thousands of tiny expensive trips into a few big ones. The price is a window of time where the program thinks it wrote and the file has not changed, and that window is where a surprising number of production mysteries live.
A write nobody else can see yet
The file stays open while a second reader peeks at it.
f = open("buffered.txt", "w") f.write("hello") with open("buffered.txt", "r") as reader: print("before flush:", repr(reader.read())) f.flush() with open("buffered.txt", "r") as reader: print("after flush: ", repr(reader.read())) f.close()
Output
before flush: '' after flush: 'hello'
repr() shows the string with quotes, so an empty read is visibly '' rather than a blank line. The five bytes sat in the process's buffer, invisible to everyone else, until flush() pushed them out.
The reader here is a second file handle in the same program, but a completely separate process would see exactly the same nothing. The buffer lives in the writing process's private memory, which lesson 3-1 established nobody else can reach.
When this bites in real life
Crashes lose buffered data. If a program dies before flushing, the buffered writes never reach the file, which is why log lines sometimes vanish right before a crash, and they are exactly the lines that were needed.
Another process reads too early. Program A writes a status file, program B reads it and sees nothing, as in the demo above.
with open(...) is the fix for most cases. Closing a file flushes it, and the with block guarantees the close even on exceptions, which is the habit used throughout this course.
| Layer | Held in | Forced by |
|---|---|---|
| Python buffer | process memory | flush() or close |
| OS page cache | kernel memory | os.fsync |
| disk | the device | the write completing |
One level deeper, flush() hands bytes to the OS, which keeps its own cache before the physical disk. Databases that absolutely cannot lose data call os.fsync to force that layer too, at the cost of real slowness. It is buffers all the way down, and each layer trades durability for speed.
Making a log line visible without closing the file
Real loggers keep the file open, so they need an explicit push.
f = open("log.txt", "w") f.write("event 1\n") f.flush() with open("log.txt", "r") as r: print(repr(r.read())) f.close()
Output
'event 1\n'f.flush() sends the buffered bytes to the OS without closing the file, which is the only reason the reader below it sees anything.
This is the trade every logging library exposes as a setting. Flushing per record makes logs trustworthy during a crash and costs a system call on every line, while buffering is fast and can lose the tail of the log at the worst possible moment.
Why the last log lines vanished
When a script crashes and its final log lines are missing, the lines were most likely still in the write buffer and were lost when the process died before flushing.
Buffered writes live in process memory, and process memory evaporates on death. Lesson 1-1 established that RAM is volatile, and lesson 3-3 that dead processes are cleaned up entirely.
| Where the log line was | Survives a crash |
|---|---|
| in the Python buffer | no |
| flushed to the OS | yes, in almost all crashes |
| fsynced to disk | yes, even on power loss |
Loggers that must survive crashes flush after every record, or use unbuffered or line-buffered output. That is also why the most important log line, the one describing the failure, is the one most likely to be missing from a badly configured logger.