Files and Folders with pathlib
Automating starts with handing repetitive file handling over to Python: sorting exports, renaming reports, merging the month’s files. The standard pathlib module makes these operations simple and portable.
Portable paths
from pathlib import Path
dossier = Path("rapports") / "2026" # the / operator joins the pieces
fichier = dossier / "ventes_novembre.csv"
print(fichier) # rapports/2026/ventes_novembre.csv (or with \ on Windows)
print(fichier.name) # ventes_novembre.csv
print(fichier.stem) # ventes_novembre
print(fichier.suffix) # .csv
print(fichier.parent) # rapports/2026
print(fichier.with_suffix(".xlsx").name) # ventes_novembre.xlsx
Path.cwd() # current working directory
Path.home() # the user's home directory
No more concatenating strings with "/" or "\\": pathlib picks the right separator for the system.
Reading and writing text
notes = Path("notes.txt")
notes.write_text("Première ligne\nDeuxième ligne\n", encoding="utf-8")
contenu = notes.read_text(encoding="utf-8")
print(contenu.splitlines()) # ['Première ligne', 'Deuxième ligne']
# To read or write as you go, line by line:
with notes.open("a", encoding="utf-8") as f: # "a": append to the end of the file
f.write("Troisième ligne\n")
with notes.open(encoding="utf-8") as f: # read by default ("r")
for ligne in f:
print(ligne.rstrip("\n"))
The with block closes the file automatically, even on error. Always specify encoding="utf-8": before Python 3.15, the default encoding depends on the system (often different on Windows), a classic source of unreadable accented characters. Python 3.15 uses UTF-8 by default everywhere, but specifying it remains the rule: the intent is explicit and the code behaves the same way with earlier versions. The modes: "r" read, "w" write (overwrites!), "a" append, "x" create (error if the file exists).
Browsing a folder
exports = Path("exports")
for chemin in sorted(exports.glob("*.csv")): # .csv files in the folder
print(chemin.name, chemin.stat().st_size, "bytes")
tous = list(exports.rglob("*.csv")) # rglob: also in subfolders
dossiers = [p for p in exports.iterdir() if p.is_dir()]
Creating, moving, organizing
archive = Path("archive") / "2026-11"
archive.mkdir(parents=True, exist_ok=True) # also creates parents, no error if it exists
for chemin in Path("exports").glob("*.csv"):
destination = archive / chemin.name
chemin.replace(destination) # moves (and overwrites if the target exists)
chemin.unlink() deletes a file; for a non-empty folder, the shutil module offers shutil.rmtree and shutil.copy2 to copy. Be careful: a delete or move loop acts on hundreds of files in a second. Always test your script on a temporary folder:
import tempfile
with tempfile.TemporaryDirectory() as tmp:
base = Path(tmp)
(base / "essai.csv").write_text("a,b\n1,2\n", encoding="utf-8")
print([p.name for p in base.iterdir()])
# the temporary folder and its contents are deleted on leaving the block
This is exactly what the exercises in this lesson do.
Key takeaways
Path+/for portable paths;name,stem,suffix,parent.read_text/write_textorwith open(...), always withencoding="utf-8".glob,mkdir(parents=True, exist_ok=True),replace; test on a temporary folder.
Exercises
Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.
Decompose and build paths
From
source, compute:nom: the file name with extension;base: the name without extension;extension: the extension (with the dot);sortie: aPathto the same name with.xlsx, placed in therapportssubfolder ofsource’s parent folder.
from pathlib import Path source = Path("donnees") / "2026" / "ventes_novembre.csv" nom = ... base = ... extension = ... sortie = ...Merge the text files in a folder
Write
fusionner(dossier, motif="*.txt")that reads, in alphabetical order of names, all the files in the folder matching the pattern, and returns the list of all their non-empty lines (without the newline). Also writeecrire_lignes(chemin, lignes)that writes the lines to a UTF-8 file, one per line, creating parent folders if needed.The test code creates a temporary folder; do not touch the disk outside it.
from pathlib import Path def fusionner(dossier, motif="*.txt"): ... def ecrire_lignes(chemin, lignes): ...Opening mode
You want to add a line to the end of an existing log without erasing its contents. Which mode should you use?
- "w"
- "a"
- "r"
- "x"
End-of-lesson quiz
What is
Path("a/rapport.final.csv").suffix?- ".final.csv"
- ".csv"
- "csv"
Why use
with open(...) as f:?- It is faster
- The file is closed automatically, even on error
- It is required for reading
What does
Path("out/2026").mkdir(parents=True, exist_ok=True)do if the folder already exists?- Raises an error
- Does nothing, with no error
- Empties the folder
The rest of the course
- Reading and Writing CSV and JSON20 min
- Automating Excel with pandas and openpyxl20 min
- Querying a Web API20 min
- An End-to-End Automated Script20 min