Files and errorsChapter 82 of 114
Delete Files
Remove files and folders, and check first without a race.
Removing a file
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
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:
if os.path.exists(path):
os.remove(path) # the file can vanish between the two linesThat gap is a race condition. Catching the error has no gap. pathlib offers a third option that says exactly what you mean:
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:
import os
os.mkdir("empty-folder")
os.rmdir("empty-folder")
print(os.path.exists("empty-folder"))Output
False
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
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
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:
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:
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 questionsWhy 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.
Try...Except
Handle the errors you expect, and let the rest surface.