Utilisez pathlib au lieu d’os.path ou glob

par Vincent Poulailleau - 3 minutes de lecture - 457 mots

Régulièrement, en programmant en Python, nous sommes amenés à utiliser des chemins de fichiers, de dossiers, et à les manipuler. Python fournit de quoi les manipuler simplement. Contrairement à de la vieille documentation (et sauf raisons valables), vous n’êtes censés utiliser que la nouvelle API fournie par pathlib.

À noter aussi que vous n’avez plus à vous préoccuper des slashs ou anti-slashs selon les systèmes d’exploitation, mettez ce que vous voulez, pathlib s’occupe du reste !

« Un exemple de code » me direz-vous ? Hé bien voici :

1
2
3
4
import os

in_file = os.path.join(os.path.join(os.getcwd(), "in"), "input.xlsx")
out_file = os.path.join(os.path.join(os.getcwd(), "out"), "output.xlsx")

Devient avec pathlib :

1
2
3
4
from pathlib import Path

in_file = Path.cwd() / "in" / "input.xlsx"
out_file = Path.cwd() / "out" / "output.xlsx"

Ceci a le mérite d’être facile à lire, et fonctionne grâce à la surcharge de l’opérateur de division.

Et si, par exemple, vous avez des projets django, vous avez dû écrire des configurations comme :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
    }
}

En utilisant pathlib, ce code devient plus lisible :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / "..."
BASE_DIR = Path(__file__).resolve().parent.parent

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

De plus pathlib fournit les différentes fonctions très pratiques.

1
2
3
4
5
import os
import os.path

os.makedirs(os.path.join("src", "__pypackages__"), exist_ok=True)
os.rename(".editorconfig", os.path.join("src", ".editorconfig"))

Devient

1
2
3
4
5
6
7
8
9
from pathlib import Path

Path("src/__pypackages__").mkdir(parents=True, exist_ok=True)
Path(".editorconfig").rename("src/.editorconfig")

# ou encore

Path("src", "__pypackages__").mkdir(parents=True, exist_ok=True)
Path(".editorconfig").rename(Path("src", ".editorconfig"))

Ou encore

1
2
3
4
5
6
import glob

configfiles = glob.glob(
    "/home/vincent/dossier1/**/*.ini",
    recursive=True,
)

Devient

1
2
3
from pathlib import Path

configfiles = Path("/home/vincent/dossier1/").rglob("*.ini")

Ou encore

1
2
3
4
5
6
from glob import glob

file_contents = []
for filename in glob("**/*.py", recursive=True):
    with open(filename) as python_file:
        file_contents.append(python_file.read())

Devient

1
2
3
4
5
6
from pathlib import Path

file_contents = [
    path.read_text()
    for path in Path.cwd().rglob("*.py")
]

Enfin, plein de bonnes choses sont accessibles, par exemple :

1
2
if Path("docs/README.md").is_file():
    ...

Bref, je vous conseille de jeter un coup d’œil à ce fameux pathlib.

Il m’intriguait depuis longtemps. Un bon moyen de savoir ce qu’il fait a été, pour moi, de traduire la documentation en français. Elle est maintenant accessible à cette adresse : https://docs.python.org/fr/3/library/pathlib.html.

Vous pourrez en savoir plus en lisant :

L’idée de ce billet me vient de https://pythonbytes.fm/episodes/show/111/loguru-python-logging-made-simple.