HOWTO · Python

Module d'importation à partir d'un sous-répertoire en Python

Ce tutoriel montre comment importer un fichier à partir d'un sous-répertoire en Python

Sur cette page

Pour importer un module situé dans un sous-répertoire d’un projet, rendez son paquet parent importable, puis utilisez le nom du module sous forme de chemin pointé. Pour un script ponctuel, vous pouvez aussi ajouter le répertoire du module à sys.path. Les imports relatifs s’utilisent avec les modules chargés dans le cadre d’un paquet.

Comment Python trouve les modules

Lorsqu’il traite un import, Python recherche le module demandé dans les emplacements de sys.path, une liste propre au processus de l’interpréteur en cours d’exécution. Les entrées initiales dépendent du mode de lancement de Python ; le répertoire de travail courant et l’emplacement du script peuvent influer sur cette liste. Un sous-répertoire du projet n’est pas automatiquement importable simplement parce qu’il se trouve à côté d’un autre fichier du projet.

PYTHONPATH est une variable d’environnement qui peut ajouter des emplacements au démarrage de Python. Elle est distincte de sys.path : modifier sys.path dans le code agit sur le processus en cours, tandis que définir PYTHONPATH configure le démarrage des processus lancés avec cet environnement. Pour la plupart des applications, un import de paquet donne au projet une structure plus claire qu’un chemin dépendant d’une machine particulière.

Importer un module depuis un sous-répertoire de paquet

Supposons que le projet contienne project/reports/summary.py et que summary.py définisse une fonction nommée label. Si la racine du projet est accessible à Python, importez le module avec le nom project.reports.summary. Les fichiers __init__.py peuvent contenir du code d’initialisation ; dans cet exemple, ils indiquent que les répertoires sont des paquets classiques. Cette disposition rend la structure du paquet explicite, sans prétendre que tous les paquets Python doivent utiliser ces fichiers.

Exécutez cet exemple :
Le code et les libellés ou sorties affichés dans cette section restent en anglais.

from pathlib import Path
from tempfile import TemporaryDirectory
import sys

with TemporaryDirectory() as folder:
    root = Path(folder)
    reports = root / "project" / "reports"
    reports.mkdir(parents=True)
    (root / "project" / "__init__.py").write_text("")
    (reports / "__init__.py").write_text("")
    (reports / "summary.py").write_text("def label():\n    return 'Monthly summary'\n")
    sys.path.insert(0, str(root))
    from project.reports.summary import label
    print(label())

L’exemple crée les fichiers du paquet dans une racine de projet temporaire, ajoute cette racine aux emplacements de recherche du processus en cours, puis importe la fonction par son nom de paquet. Dans un projet réel, placez le répertoire du paquet sous la racine du projet et lancez Python depuis un contexte où cette racine est accessible à l’import. Si l’import indique qu’un module est introuvable, vérifiez son nom pointé, les noms des fichiers et des répertoires, ainsi que la présence de la racine du projet parmi les emplacements utilisés par ce processus.

Ajouter un répertoire à sys.path

Pour un module autonome qui n’est pas organisé en paquet, ajoutez le répertoire qui le contient avant de l’importer. sys.path.insert(0, ...) place cet emplacement près du début de l’ordre de recherche du processus en cours ; il passe donc avant les entrées suivantes. Utilisez cette solution ciblée pour un script ou un contexte d’exécution maîtrisé : elle n’installe pas le module et ne configure pas les futurs processus Python.

Exécutez cet exemple :
Le code et les libellés ou sorties affichés dans cette section restent en anglais.

from pathlib import Path
from tempfile import TemporaryDirectory
import sys

with TemporaryDirectory() as folder:
    module_dir = Path(folder) / "helpers"
    module_dir.mkdir()
    (module_dir / "formatting.py").write_text("def title():\n    return 'Project report'\n")
    sys.path.insert(0, str(module_dir))
    import formatting
    print(formatting.title())

Comme cette méthode ajoute un répertoire entier aux chemins de recherche des imports, Python peut sélectionner de façon inattendue un autre fichier portant le même nom de module. Pour du code de projet réutilisable, privilégiez un paquet au nom distinctif. PYTHONPATH est une autre possibilité lorsqu’un environnement de démarrage doit fournir un répertoire à plusieurs processus ; ce nom ne désigne pas la liste sys.path, qui est propre au processus.

Utiliser les imports relatifs au sein d’un paquet

Un point initial dans un import est résolu à partir du paquet courant. Par exemple, project/app.py peut importer project/reports/summary.py avec from .reports.summary import label lorsque app est chargé sous le nom project.app. Un script de premier niveau lancé directement avec python project/app.py ne dispose pas de contexte de paquet pour cet import relatif commençant par un point ; il échoue donc. Lancez le module depuis le répertoire contenant project avec python -m project.app, ou utilisez plutôt un import absolu du paquet.

Exécutez cet exemple :
Le code et les libellés ou sorties affichés dans cette section restent en anglais.

from pathlib import Path
from tempfile import TemporaryDirectory
import subprocess
import sys

with TemporaryDirectory() as folder:
    root = Path(folder)
    reports = root / "project" / "reports"
    reports.mkdir(parents=True)
    (root / "project" / "__init__.py").write_text("")
    (reports / "__init__.py").write_text("")
    (reports / "summary.py").write_text("def label():\n    return 'Monthly summary'\n")
    (root / "project" / "app.py").write_text(
        "from .reports.summary import label\nprint(label())\n"
    )
    result = subprocess.run(
        [sys.executable, "-m", "project.app"],
        cwd=root,
        check=True,
        capture_output=True,
        text=True,
    )
    print(result.stdout, end="")

Ici, Python lance l’application comme un module de paquet ; l’import relatif dispose donc d’un paquet parent connu. Les imports relatifs sont utiles pour les modules étroitement liés qui peuvent être déplacés ensemble ; les imports absolus sont souvent plus faciles à repérer lorsqu’on lit du code situé hors de ce paquet.

Choisir la bonne méthode

Pour le code courant d’un projet, utilisez des imports de paquets : ils rendent visible la hiérarchie prévue des modules et évitent de dépendre de l’ajout ponctuel d’un répertoire. Au sein d’un paquet, choisissez les imports relatifs lorsque les modules voisins doivent être référencés par rapport à leur paquet commun ; choisissez les imports absolus lorsque le nom complet du paquet clarifie la dépendance. Ajoutez un répertoire à sys.path pour un besoin précis lié à un script ou à l’exécution, en gardant à l’esprit que sa position peut influer sur le module trouvé lorsque plusieurs portent le même nom.

Pour importer des modules depuis plusieurs sous-répertoires, donnez à chaque zone un chemin de paquet et importez chaque module avec son nom qualifié par le paquet, par exemple project.reports.summary et project.data.reader. Vérifiez que la racine du projet est accessible au processus. Évitez d’ajouter plusieurs répertoires génériques à sys.path, car leurs noms peuvent entrer en collision et rendre les imports plus difficiles à retracer. Ces exemples créent leurs fichiers localement pour rendre visible chaque configuration d’import ; adaptez les mêmes noms de paquets et le même contexte de lancement à votre projet.