Batu Lab NotesPractical developer guides

Write an atomic-looking staging file within one directory

By Batu · English technical notes

Also published in our Blogger archive.

Write an atomic-looking staging file within one directory

A useful update pattern writes complete replacement content to a staging filename beside the destination, then replaces the destination. Keeping report.txt and report.txt.stage under the same temporary parent avoids deliberately choosing different directory trees for the two steps. The example starts with old, writes new to staging, and calls staging.replace(target).

Path.replace() renames the staging path to the target and unconditionally replaces an existing file or empty directory at that target. The returned path is asserted to equal target; the next assertion reads the committed content, and the final one checks that the staging name no longer exists. Stdout is the new target text, including its newline.

“Atomic-looking” is deliberately modest language. The code does not establish crash durability: it does not flush file or directory metadata, model power loss, coordinate concurrent writers, or validate that the staging content is acceptable. Replacement details can also depend on the operating system and filesystem. A same-directory staging convention is a practical constraint, not proof of an atomic transaction. Path.replace() exists with pathlib from Python 3.4; this version requires Python 3.8 or later because it asserts the method's returned target path, a return value added in 3.8. See pathlib's replace reference and TemporaryDirectory's lifecycle documentation.

AI assistance disclosure: This article was drafted with AI assistance and should be reviewed in the context of its application.

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    parent = Path(directory)
    target = parent / "report.txt"
    staging = parent / "report.txt.stage"

    target.write_text("old\n", encoding="utf-8")
    staging.write_text("new\n", encoding="utf-8")
    committed = staging.replace(target)

    assert committed == target
    assert target.read_text(encoding="utf-8") == "new\n"
    assert not staging.exists()
    print(target.read_text(encoding="utf-8"), end="")
new