Aller au contenu principal

Chemins et opérations sur le système de fichiers

Un chemin décrit un emplacement ; il ne prouve pas qu'un fichier existe. Préférez pathlib.Path dans le code applicatif courant, car il réunit construction des chemins et opérations de fichiers dans une interface multiplateforme.

from pathlib import Path

root = Path("data")
report = root / "reports" / "summary.csv"

if report.is_file():
size = report.stat().st_size

Les chemins relatifs sont interprétés depuis le répertoire de travail courant du processus, choisi par l'appelant et potentiellement différent de celui du script. Faites de cette base une configuration ou un argument explicite.

Pour une ressource volontairement placée à côté d'un script autonome :

script_dir = Path(__file__).resolve().parent
template = script_dir / "templates" / "report.txt"

Pour les données d'un paquet installé, utilisez importlib.resources ; un paquet n'existe pas nécessairement sous forme de fichiers adjacents ordinaires.

Expansion et résolution

configured = Path("~/exports").expanduser()
absolute = configured.resolve()

expanduser() traite le marqueur du répertoire personnel. resolve() rend un chemin absolu, le normalise et résout les liens symboliques. Ces opérations ont des sémantiques différentes et ne sont pas des nettoyages de chaîne interchangeables.

N'utilisez pas un test de préfixe textuel pour prouver l'inclusion : /safe-backup commence par /safe sans s'y trouver. Résolvez la racine et la cible prévues, puis employez des relations de chemins comme relative_to. Même ce contrôle ne forme qu'une partie de la frontière de sécurité, car liens et état du système peuvent changer entre vérification et utilisation.

Découverte

markdown_files = sorted(root.rglob("*.md"))

L'ordre des résultats d'un glob n'est pas un contrat portable ; triez lorsque le traitement déterministe compte. Un parcours peut rencontrer des refus d'accès, des liens brisés, des cycles dus au suivi des liens et des fichiers qui disparaissent pendant l'analyse.

Opérations

Path.mkdir, rename, replace, unlink et rmdir couvrent les opérations courantes sur un chemin. Utilisez shutil.copy2, copytree, move et rmtree pour copier, déplacer ou supprimer des arbres. Leur comportement concernant les métadonnées et le passage entre systèmes de fichiers diffère ; consultez le contrat plutôt que de supposer qu'un renommage ou une copie est atomique.

Avant de supprimer ou d'écraser :

  1. Résolvez et affichez la cible exacte.
  2. Vérifiez qu'elle se trouve sous la racine étroite prévue.
  3. Refusez un chemin vide, la racine du système, le répertoire personnel ou la racine de l'espace de travail.
  4. Préférez un déplacement récupérable ou une sauvegarde lorsque c'est pratique.
  5. Prévoyez les courses entre vérification et utilisation si d'autres processus peuvent modifier la cible.

Un test d'existence suivi d'une opération ne garantit rien. Préférez l'opération elle-même et traitez son exception précise. Évitez except OSError à moins que toutes ses causes ne partagent réellement la même politique de récupération.

Construire un chemin ne le valide pas

Le contrat de pathlib permet à un opérande droit absolu de remplacer le chemin précédent. Path("") désigne le répertoire courant : refusez une entrée brute vide avant de construire un Path. Path suit les règles du système hôte ; utilisez PureWindowsPath ou PurePosixPath pour examiner la syntaxe d'une autre plateforme sans accéder à son système de fichiers.

from pathlib import Path, PurePosixPath
from tempfile import TemporaryDirectory

assert PurePosixPath("/safe") / "/other" == PurePosixPath("/other")
assert Path("") == Path(".")
with TemporaryDirectory() as directory:
root = Path(directory)
reports = root / "reports"
reports.mkdir(parents=True, exist_ok=True)
draft = reports / "draft.txt"
draft.write_text("ready", encoding="utf-8")
final = draft.replace(reports / "final.txt")
assert final.name == "final.txt"
assert final.stem == "final" and final.suffix == ".txt"
assert final.read_text(encoding="utf-8") == "ready"
final.unlink()
reports.rmdir()

resolve() utilise strict=False par défaut : il résout ce qui peut l'être et peut renvoyer un chemin dont les composants restants n'existent pas. Avec strict=True, un composant manquant lève FileNotFoundError, sans garantir l'existence future. __file__ n'est pas disponible dans certains contextes interactifs.

mkdir(exist_ok=True) échoue encore si la cible n'est pas un répertoire. rmdir() exige un répertoire vide ; unlink() supprime un fichier ou le lien symbolique lui-même, pas sa cible. replace() peut écraser un fichier existant ; le comportement de rename() lorsque la cible existe dépend de la plateforme. L'exemple limite ces opérations à son propre répertoire temporaire.

Sources

Explorer les liensOuvrir le réseau