TATECHATLAS
◎ Français
Programmation

Chemins relatifs en Python : terminal, IDE et lancement de services

La résolution des chemins relatifs en Python dépend du répertoire de travail du processus, qui varie selon le lanceur. Ce guide explique comment inspecter le répertoire de travail, choisir et documenter une base de chemin explicite, et comprendre pourquoi __file__ ne peut pas toujours être fiable.

Dans ce guide

Un chemin relatif tel que Path("data/config.json") est résolu par rapport au répertoire de travail actuel du processus, et non à l'emplacement de votre script. Le répertoire de travail est ce que os.getcwd() renvoie lorsque l'interpréteur démarre, et les lanceurs de terminal, d'IDE et de services les définissent couramment différemment. Affichez Path.cwd() au démarrage pour voir la valeur réelle. Ensuite, choisissez une base explicite délibérément : le répertoire de travail pour les fichiers fournis par l'utilisateur, le parent de __file__ pour les ressources empaquetées, ou un chemin absolu configuré pour les services. Comme __file__ est un attribut optionnel et peut être non défini pour certains modules, protégez-le avec getattr. absolute() et resolve() de pathlib diffèrent : resolve() élimine aussi les '..' et suit les liens symboliques. Il n'existe pas de valeur par défaut universelle qui fonctionne partout ; vérifiez chaque environnement de lancement et enregistrez à la fois le répertoire de travail et le chemin résolu.

Pourquoi le répertoire de travail régit les chemins relatifs

Un chemin comme data/config.json n'est pas interprété par Python lui-même ; il est transmis au système d'exploitation, qui le résout par rapport au répertoire de travail actuel du processus. Ce répertoire est défini une fois au démarrage du processus et ne suit pas votre script. pathlib.Path.cwd() renvoie un nouvel objet chemin représentant le répertoire actuel, tel que renvoyé par os.getcwd(). Le même répertoire peut donc donner un résultat dans un terminal, un autre dans un IDE, et un troisième sous un gestionnaire de services. La première étape pour diagnostiquer une erreur de fichier introuvable n'est jamais de deviner l'emplacement du script, mais d'enregistrer ce que le processus voit réellement comme son répertoire.

import os
from pathlib import Path

# 1. Inspecter ce que le lanceur a réellement défini.
print("cwd:", os.getcwd())
print("cwd path:", Path.cwd())
print("script dir:", Path(__file__).resolve().parent)

Choisir une base explicite pour chaque contexte de lancement

Une fois que vous connaissez le répertoire de travail, décidez délibérément de la base que vous souhaitez. Pour les entrées fournies par l'utilisateur, le répertoire de travail est souvent le bon choix car l'utilisateur s'attend à ce que les chemins soient relatifs à l'endroit où il a lancé l'outil. Pour les ressources empaquetées qui voyagent avec votre code, basez le chemin sur l'emplacement du script ou du package. Pour les services longs, préférez un chemin absolu configuré afin que le service se comporte de la même manière indépendamment de la façon dont le gestionnaire de processus le démarre. Documentez la convention choisie près du code qui ouvre le fichier, car les mainteneurs futurs supposeront sinon que l'emplacement du script est la valeur par défaut.

Utiliser __file__ avec précaution lorsqu'il est disponible

L'attribut __file__ du module peut pointer vers le fichier qui a défini le code actuel, ce qui le rend utile pour localiser les ressources empaquetées. La documentation du modèle de données met en garde que __file__ est optionnel et peut être non défini pour certains modules, y compris les modules C liés statiquement ou les modules chargés depuis des sources inhabituelles. Protégez l'accès avec getattr pour que le code ne lève pas d'exception lorsque l'attribut est absent. Lorsque __file__ est présent, résolvez-le vers une forme absolue avant de dériver des chemins frères, car un __file__ relatif dépendrait toujours du répertoire de travail.

absolute() versus resolve() dans pathlib

pathlib propose deux façons de rendre un chemin absolu, et elles ne sont pas interchangeables. Path.absolute() rend le chemin absolu sans normalisation ni résolution des liens symboliques, ce qui est proche de os.path.abspath() mais conserve encore les segments '..' pour la sécurité. Path.resolve() rend le chemin absolu, élimine les composants '..', et suit les liens symboliques, ce qui est proche de os.path.realpath(). Si vous avez besoin de l'emplacement réel d'un fichier existant, resolve() est généralement le meilleur choix. Si vous avez seulement besoin d'une forme absolue stable sans toucher au système de fichiers, absolute() peut être préférable.

Enregistrer le répertoire de travail et le chemin résolu

Lorsqu'un fichier n'est pas trouvé, enregistrez à la fois le répertoire de travail et le chemin exact que vous avez tenté d'ouvrir. Ce couple explique généralement l'échec plus vite que deviner l'emplacement du script. Incluez le contexte du lanceur lorsque c'est possible, comme si le processus a été démarré depuis un terminal, une configuration d'exécution d'IDE, ou un gestionnaire de services. Si le chemin dépend de la configuration, enregistrez également la valeur de configuration. Ces enregistrements rendent possible la reproduction de l'environnement ultérieurement au lieu de supposer une valeur par défaut universelle.

Différences courantes entre lanceurs à vérifier

Les lancements de terminal commencent souvent avec le répertoire actuel du shell, qui peut être la racine du projet ou le dossier dans lequel vous vous êtes placé avec cd. Les configurations d'exécution d'IDE peuvent définir le répertoire de travail sur la racine du projet, le dossier du module, ou une valeur personnalisée définie dans les paramètres de lancement. Les gestionnaires de services et les points d'entrée de conteneurs peuvent démarrer le processus dans un répertoire système ou un répertoire de travail déclaré qui diffère de l'emplacement du code. Comme ces valeurs par défaut varient, testez le répertoire de démarrage réel dans chaque environnement plutôt que de vous fier au comportement d'une seule machine.

Une vérification de démarrage pratique pour le code sensible aux chemins

Pour les applications qui ouvrent des fichiers tôt, ajoutez une petite vérification de démarrage qui affiche ou enregistre le répertoire de travail et la base de chemin que vous intendez utiliser. Si la base vient de __file__, vérifiez que l'attribut existe et résolvez-le avant l'utilisation. Si la base vient de la configuration, confirmez que le chemin configuré est absolu ou documentez comment il sera interprété. Cette vérification est particulièrement utile dans les environnements automatisés où le répertoire de lancement n'est pas évident depuis le code source.

Limitations et considérations de version

Le comportement décrit ici dépend du répertoire de travail du processus et de si __file__ est défini, deux aspects qui peuvent varier selon les implémentations Python et les environnements de lancement. La normalisation de pathlib peut aussi changer la façon dont un chemin est interprété par d'autres outils, donc pathlib n'est pas un remplacement complet et transparent de os.path dans chaque scénario. Certaines méthodes de pathlib ont changé au cours des versions récentes de Python, y compris une gestion plus stricte des boucles de liens symboliques et des chemins réservés, donc vérifiez le comportement sur l'environnement d'exécution cible. Lorsque la portabilité importe, préférez des bases absolues explicites et évitez de supposer que les chemins relatifs se résoudront de la même manière partout.

Points à vérifier

  • Affichez Path.cwd() ou os.getcwd() au démarrage pour confirmer le répertoire de travail réel pour chaque lanceur.
  • Utilisez une base explicite pour les chemins relatifs : répertoire de travail pour les fichiers utilisateur, parent de __file__ pour les ressources empaquetées, ou chemin absolu configuré pour les services.
  • Protégez __file__ avec getattr car il est optionnel et peut être non défini pour certains modules.
  • Choisissez resolve() de pathlib lorsque vous avez besoin d'éliminer les '..' et de suivre les liens symboliques, et absolute() lorsque vous avez seulement besoin d'une forme absolue sans normalisation.

La résolution des chemins relatifs dépend du répertoire de travail du processus, qui diffère entre les lancements de terminal, d'IDE et de services. __file__ est optionnel et peut manquer pour certains modules. absolute() et resolve() de pathlib se comportent différemment, et la normalisation de pathlib peut changer la façon dont les chemins sont interprétés par d'autres outils. Certains comportements de pathlib ont aussi changé au cours des versions récentes de Python, donc vérifiez sur l'environnement d'exécution cible.

Sources

  1. Python: pathlib current directory and path resolution ↗
  2. Python: os working directory and environment ↗
  3. Python: module file attribute ↗
Retour en haut ↑