Le PYTHON PATH
Par Benjamin Delmée le 17.10.2019
Le mécanisme d'import en Python est simple à utiliser, voire même ludique. Mais quand arrive le moment de développer un projet d'envergure, plus rien ne fonctionne. À de nombreuses reprises dans l'histoire de Python ses concepteurs ont fait des choix techniques différents et le mécanisme d'import ne fait pas exception. Un peu déroutant au départ, tout rentre dans l'ordre une fois qu'on a compris le fonctionnement du PYTHON PATH, clé de voute du système d'import de Python.
Tout au long de cet article, nous supposerons l'existence d'un projet demo
organisé de la manière suivante :
demo/ <-- répertoire du projet
├── pckg_A/ <-- sous-répertoire
│ ├── apple.py
│ └── apricot.py
├── pckg_B/ <-- sous-répertoire
│ ├── banana.py
│ └── blueberries.py
├── mytest <-- sous-répertoire
│ └── testing.py
└── main.py
Un peu de vocabulaire en passant :
- Les répertoires
pckg_A
,pckg_B
etmytest
sont appelés des packages. - Les fichiers
main.py
,apple.py
,apricot.py
,banana.py
,blueberries.py
ettesting.py
sont appelés des modules.
Particularité, en Python tous les packages sont des modules mais tous les modules ne sont pas des packages (cf. la documentation). Autrement dit, les packages sont des modules spéciaux. On peut donc importer des packages de la même manière qu'on importe des modules.
Fonctionnement du PYTHON PATH
Lorsque l'instruction import module
est invoquée, l'interpréteur Python va :
- Rechercher le module
module
en explorant une liste prédéfinie de répertoires (c'est cette liste qu'on appelle le PYTHON PATH) - Charger le code du module et le mettre à disposition dans une variable du même nom
Bien qu'étant la moins complexe des deux, l'étape n°1 est celle qui pose le plus de problèmes aux nouveaux développeurs Python. Détaillons donc son fonctionnement.
Commençons par observer le contenu du PYTHON PATH lorsqu'on exécute le fichier demo/main.py
:
# $> cat demo/main.py
import sys
print(sys.path) # cette ligne permet d'afficher le PYTHON PATH
# $> python demo/main.py
[
'/home/bdelmee/working-directory/demo',
'/home/bdelmee/miniconda3/lib/python37.zip',
'/home/bdelmee/miniconda3/lib/python3.7',
'/home/bdelmee/miniconda3/lib/python3.7/lib-dynload',
'/home/bdelmee/working-directory/demo/venv/lib/python3.7/site-packages'
]
- La première ligne correspond au répertoire qui contient le point d'entrée du programme (dans cet exemple le fichier
main.py
). Il ne correspond pas au répertoire courant ($PWD
) du terminal depuis lequel la commande Python est lancée. - Les trois lignes suivantes correspondent aux répertoires où sont stockés les modules de la librairie standard. On peut voir ici que la librairie installée par miniconda est utilisée à la place de celle fournie par la distribution Linux (sans impact pour le reste des explications).
- La dernière ligne correspond aux sites-packages. C'est ici que sont stockés les modules installés via
pip
. À titre d'information, c'est ce répertoire que modifievenv
pour créer des environnements virtuels.
Il est possible d'importer les modules présents dans le répertoir demo
puisque ce dernier est inclus dans le PYTHON PATH :
# $> cat demo/main.py
from pckg_A import apple # pckg_A est un sous-module de demo -> OK
from pckg_B import banana # pckg_B est un sous-module de demo -> OK
from mytest import testing # mytest est un sous-module de demo -> OK
Observons maintenant le contenu de notre PYTHON PATH lorsqu'on exécute le fichier demo/mytest/testing.py
:
# $> cat demo/mytest/testing.py
import sys
print(sys.path)
# $> python demo/test/testing.py
[
'/home/bdelmee/working-directory/demo/mytest',
'/home/bdelmee/miniconda3/lib/python37.zip',
'/home/bdelmee/miniconda3/lib/python3.7',
'/home/bdelmee/miniconda3/lib/python3.7/lib-dynload',
'/home/bdelmee/working-directory/demo/venv/lib/python3.7/site-packages'
]
- On remarque que la première ligne a changé, le répertoire
demo
a été remplacé par le répertoiredemo/mytest
. En effet le point d'entrée du programme n'est plusdemo/main.py
maisdemo/mytest/testing.py
, d'où la mise à jour du PYTHON PATH. C'est ce comportement qui déroute le plus les nouveaux développeurs Python. Le PYTHON PATH dépend (en partie) du point d'entrée du programme.
La conséquence indésirée de ce changement, c'est que les modules présents dans les répertoires demo/pckg_A
et demo/pckg_B
ne sont plus accessibles. Lorsqu'on essaie de les importer :
# $> cat demo/mytest/testing.py
import testing # testing est un sous-module de test -> OK
from pckg_A import apple # pckg_A n'est pas un sous-module de pckg_C -> Erreur
from pckg_B import banana # pckg_B n'est pas un sous-module de pckg_C -> Erreur
# $> python demo/mytest/testing.py
ModuleNotFoundError: No module named 'pckg_A'
Heureusement ce problème n'est pas définitif, voyons maintenant comment importer les modules pckg_A
et pckg_B
depuis le module mytest
.
Méthode n°1 : Modifier le PYTHON PATH
Une manière de résoudre ce problème d'import est de modifier le PYTHON PATH en ajoutant le répertoire demo
. Les modules pckg_A
et pckg_B
seront de nouveaux accessibles depuis le module mytest
(et aussi depuis n'importe quel autre emplacement du programme).
On peut ajouter des répertoires au PYTHON PATH en modifiant la variable d'environnement $PYTHONPATH
. Dans l'exemple, cela donnerait :
$> export PYTHONPATH="/home/bdelmee/working-directory/demo"
On peut aussi modifier le PYTHON PATH directement depuis python. Cela a l'avantage de ne pas nécessiter d'intervention côté système. Dans l'exemple, cela donnerait :
# $> cat demo/mytest/testing.py
import sys
sys.path.append("/home/bdelmee/working-directory/demo")
from pckg_A import apple # OK
Méthode n°2 : Utiliser un point d'entrée unique
Une autre méthode consiste à s'assurer que le PYTHON PATH inclut toujours le répertoire demo
en utilisant un script unique comme point d'entrée du programme. En plaçant ce script à la racine du projet, le répertoire racine sera automatiquement placé dans le PYTHON PATH et les sous-modules seront alors accessibles. Dans notre exemple, ce rôle pourrait être joué par le fichier main.py
.
À retenir :
- Vous ne pouvez pas importer un module s'il ne se trouve pas dans l'un des répertoires/sous-répertoires du PYTHON PATH.
- Le répertoire contenant le point d'entrée du programme est automatiquement ajouté au PYTHON PATH.
- Il est possible de modifier le PYTHON PAHT via la variable d'environnement
$PYTHONPATH
ou directement dans le code Python viasys.path