Chapters
Python114 chapters

Files and errorsChapter 82 of 114

Delete Files

Remove files and folders, and check first without a race.

Removing a file

Python
import os

with open("scratch.txt", "w", encoding="utf-8") as f:
    f.write("temporary")

print(os.path.exists("scratch.txt"))
os.remove("scratch.txt")
print(os.path.exists("scratch.txt"))

Output

True
False

os.unlink is the same function under its other name.

It is not a recycle bin

os.remove deletes. There is no undo, and nothing goes to the trash. Treat every call as final.

Missing files raise

Python
import os

try:
    os.remove("never-existed.txt")
except FileNotFoundError:
    print("nothing to delete")

Output

nothing to delete

Checking first looks tidier and is worse:

Python
if os.path.exists(path):
    os.remove(path)          # the file can vanish between the two lines

That gap is a race condition. Catching the error has no gap. pathlib offers a third option that says exactly what you mean:

Python
from pathlib import Path

Path("gone.txt").unlink(missing_ok=True)
print("no error even though it was not there")

Output

no error even though it was not there

Folders

os.rmdir removes a folder, and only an empty one:

Python
import os

os.mkdir("empty-folder")
os.rmdir("empty-folder")
print(os.path.exists("empty-folder"))

Output

False
Python
import os

os.mkdir("full-folder")
with open("full-folder/file.txt", "w", encoding="utf-8") as f:
    f.write("x")

try:
    os.rmdir("full-folder")
except OSError:
    print("refused: the folder is not empty")

Output

refused: the folder is not empty

That refusal is a safety feature.

Deleting a folder and everything in it

Python
import os
import shutil

os.makedirs("tree/inner", exist_ok=True)
with open("tree/inner/file.txt", "w", encoding="utf-8") as f:
    f.write("x")

shutil.rmtree("tree")
print(os.path.exists("tree"))

Output

False

Deleting several

Python
import os
from pathlib import Path

for name in ["a.tmp", "b.tmp", "keep.txt"]:
    Path(name).write_text("x", encoding="utf-8")

for path in Path(".").glob("*.tmp"):
    path.unlink()

print(sorted(p.name for p in Path(".").glob("*.t*")))

Output

['keep.txt']

glob matches by pattern, so this removed both .tmp files and left the rest alone.

Emptying rather than deleting

Sometimes you want the file to stay and its contents to go:

Python
with open("log.txt", "w", encoding="utf-8") as f:
    f.write("old entries\n")

open("log.txt", "w", encoding="utf-8").close()

with open("log.txt", encoding="utf-8") as f:
    print(repr(f.read()))

Output

''

Opening in "w" mode truncates. Here that behaviour is the point rather than a trap.

Temporary files

When you only need a file for the length of the program, let Python clean up:

Python
import tempfile
import os

with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
    f.write("scratch data")
    name = f.name

with open(name, encoding="utf-8") as f:
    print(f.read())

os.remove(name)
print(os.path.exists(name))

Output

scratch data
False

Test yourself

2 questions

Why is catching FileNotFoundError better than checking os.path.exists first?

Show the answer

The file can disappear between the check and the delete — That gap is a race condition. Path.unlink(missing_ok=True) says the same thing in one call.

What does os.rmdir do to a folder with files in it?

Show the answer

Refuses, raising OSError — That refusal is a safety feature. shutil.rmtree is the one that deletes everything, without asking.

Next chapter

Try...Except

Handle the errors you expect, and let the rest surface.