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 :
- Résolvez et affichez la cible exacte.
- Vérifiez qu'elle se trouve sous la racine étroite prévue.
- Refusez un chemin vide, la racine du système, le répertoire personnel ou la racine de l'espace de travail.
- Préférez un déplacement récupérable ou une sauvegarde lorsque c'est pratique.
- 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.