__main__ — Environnement d'exécution principal


En Python, le nom __main__ a une fonction particulière. Il intervient dans deux cas :

  1. c'est le nom de l'environnement d'exécution principal, ce qui donne lieu au test courant __name__ == '__main__' ;

  2. c'est aussi le nom du fichier __main__.py dans les paquets Python.

Les deux sont liés aux modules Python, à la manière de s'en servir en tant qu'utilisateur et à la manière dont ils interagissent entre eux. Cette page contient des détails sur les modules Python. Si vous ne les avez jamais utilisés, commencez par la section qui leur est consacrée dans le tutoriel : Modules.

__name__ == '__main__'

Lorsqu'un module ou un paquet Python est importé, son attribut __name__ est défini à son nom, qui est la plupart du temps le nom du fichier qui le contient sans l'extension .py :

>>> import configparser
>>> configparser.__name__
'configparser'

Si le fichier fait partie d'un paquet, __name__ donne tout le chemin d'accès :

>>> from concurrent.futures import process
>>> process.__name__
'concurrent.futures.process'

En revanche, si le module est exécuté dans l'environnement d'exécution principal, la variable __name__ vaut '__main__'.

Qu'est-ce que l'« environnement d'exécution principal » ?

L'environnement principal a pour nom __main__. Il s'agit du premier module Python dont l'exécution a été demandée par l'utilisateur final. On le qualifie de principal car c'est lui qui importe tous les autres modules nécessités par le programme. On l'appelle parfois le point d'entrée de l'application.

L'environnement principal peut prendre diverses formes :

  • l'environnement d'une invite de commande interactive :

    >>> __name__
    '__main__'
    
  • le module passé directement en tant que fichier à la commande de l'interpréteur Python :

    $ python3 helloworld.py
    Hello, world!
    
  • le module ou paquet passé à l'interpréteur avec l'option -m :

    $ python3 -m tarfile
    usage: tarfile.py [-h] [-v] (...)
    
  • le code lu par l'interpréteur depuis l'entrée standard :

    $ echo "import this" | python3
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    
  • le code passé à l'interpréteur avec l'option -c :

    $ python3 -c "import this"
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    ...
    

Dans chacun de ces cas, l'attribut __name__ du module principal est mis à '__main__'.

Un module peut donc savoir s'il est exécuté dans l'environnement principal en vérifiant son __name__, ce qui permet typiquement d'exécuter du code lorsque le module est initialisé d'une manière autre que l'importation :

if __name__ == '__main__':
    # Execute when the module is not initialized from an import statement.
    ...

Voir aussi

Pour plus de détails sur la manière dont __name__ est défini dans les divers cas, voir la section Modules dans le tutoriel.

Utilisation idiomatique

Il arrive qu'un module contienne du code qui ne doit s'exécuter que lorsque le module est utilisé comme script. On peut penser à l'analyse des arguments passés en ligne de commande, ou bien à la lecture de l'entrée standard. Il ne faudrait pas que ces opérations soient effectuées lorsque le module est importé depuis un autre module, comme pour les tests.

C'est dans ces situations que sert la construction if __name__ == '__main__'. Le code mis à l'intérieur du bloc n'est exécuté que si le module est dans l'environnement principal.

Il vaut mieux mettre aussi peu de code que possible sous le if __name__ == '__main__' pour garder le code clair et limiter les risques d'erreur. La plupart du temps, on écrit une fonction main qui contient tout le code spécifique à l'utilisation comme script :

# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """A dummy wrapper around print."""
   # for demonstration purposes, you can imagine that there is some
   # valuable and reusable logic inside this function
   print(phrase)

def main() -> int:
    """Echo the input arguments to standard output"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # next section explains the use of sys.exit

Si, dans le module ci-dessus, le code de main était placé directement dans le if __name__ == '__main__', la variable phrase serait globale, et d'autres fonctions pourraient s'en servir par erreur à la place d'une variable locale. Encapsuler le code dans la fonction main évite cet ennui.

De plus, la fonction echo est elle-même séparée du reste, et on peut l'importer dans un autre module. Le fait d'importer echo.py définit les fonctions main et echo, mais n'en appelle aucune, puisque __name__ != '__main__'.

Considérations liées à l'empaquetage

Les fonctions main servent souvent à créer des outils qui s'exécutent en ligne de commande. Les scripts sont ajoutés au système à l'aide de points d'entrée, qui demandent à pip de créer un exécutable. Il le fait en insérant un appel à la fonction à l'intérieur d'un modèle prédéfini où la valeur qu'elle renvoie est passée directement à sys.exit() :

sys.exit(main())

Puisque l'appel de main est encapsulé dans sys.exit(), main doit renvoyer une valeur qui convienne comme argument à sys.exit(), par exemple un code de retour sous forme d'entier. La valeur None est également acceptée, et c'est d'ailleurs celle que renvoie la fonction si elle se termine sans rencontrer d'instruction return.

Il est utile de se conformer à cette convention. De cette manière, le module se comporte de la même manière s'il est distribué comme script à l'aide des points d'entrée que lorsqu'il est exécuté directement (avec python3 echo.py).

En particulier, mieux vaut éviter de renvoyer une chaîne de caractères depuis la fonction main. En effet, si sys.exit() reçoit une chaîne, elle l'interprète comme un message d'erreur, qu'elle affiche sur sys.stderr avant de terminer le programme avec le code de retour 1 (erreur). L'exemple de echo.py ci-dessus montre la pratique recommandée.

Voir aussi

Le guide de l'empaquetage en Python (en anglais) contient plusieurs tutoriels et documents de référence autour de la distribution et de l'installation de paquets Python avec des outils modernes.

Le fichier __main__.py dans les paquets Python

Si vous n'êtes pas familier des paquets Python, lisez Les paquets dans le tutoriel. Un fichier __main__.py permet à un paquet de définir une interface en ligne de commande. Prenons pour exemple un paquet nommé bandclass :

bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py

Le fichier __main__.py qu'il contient s'exécute lorsque le paquet est appelé depuis la ligne de commande avec l'option -m de l'interpréteur, comme ceci :

$ python3 -m bandclass

Cette commande lance l'exécution de __main__.py. Il vous appartient, en tant que concepteur du paquet, de déterminer ce qu'elle doit faire. Dans notre exemple, elle pourrait rechercher un étudiant dans une base de données :

# bandclass/__main__.py

import sys
from .student import search_students

student_name = sys.argv[2] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')

Remarquez l'importation from .student import search_students. Le point avant student sert à rendre le chemin student relatif à la position du module qui l'importe. Pour plus d'informations, voir Références internes dans un paquet dans la section Modules du tutoriel.

Utilisation idiomatique

En général, on ne met pas le code de __main__.py dans un bloc if __name__ == '__main__'. Il vaut mieux définir toutes les fonctions utiles dans d'autres modules et réduire la taille du fichier __main__.py au minimum. Il est alors plus facile de tester et réutiliser ces autres modules.

Cependant, un if __name__ == '__main__', s'il est présent dans le __main__.py, fonctionne correctement. En effet, si __main__.py est importé depuis autre module, son attribut __name__ contient, avant __main__, le nom du paquet dont il fait partie :

>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'

Malgré tout, cela ne fonctionne pas pour les fichiers __main__.py à la racine d'une archive ZIP. Aussi est-il préférable d'écrire des __main__.py dans le style minimal de celui de venv mentionné ci-dessus.

Voir aussi

See venv for an example of a package with a minimal __main__.py in the standard library. It doesn't contain a if __name__ == '__main__' block. You can invoke it with python -m venv [directory].

La documentation du module runpy fournit une description complète de l'option -m de l'interpréteur.

Le module zipapp exécute des applications emballées dans une archive ZIP. Dans ce cas, l'interpréteur recherche un fichier __main__.py à la racine de l'archive.

import __main__

Quel que soit le module principal d'un programme, les autres modules peuvent accéder à l'espace de nommage dans lequel il s'exécute en important le module spécial __main__. Celui-ci ne correspond pas forcément à un fichier __main__.py. Il s'agit simplement du module qui a reçu le nom '__main__'.

Voici un exemple d'utilisation du module __main__ :

# namely.py

import __main__

def did_user_define_their_name():
    return 'my_name' in dir(__main__)

def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    if '__file__' in dir(__main__):
        print(__main__.my_name, "found in file", __main__.__file__)
    else:
        print(__main__.my_name)

Ce code s'utilise comme ceci :

# start.py

import sys

from namely import print_user_name

# my_name = "Dinsdale"

def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)

if __name__ == "__main__":
    sys.exit(main())

Le programme ci-dessus donne la sortie :

$ python3 start.py
Define the variable `my_name`!

Son code de retour est 1, ce qui signifie une erreur. En supprimant la marque de commentaire en début de ligne my_name = "Dinsdale", le programme est corrigé, et renvoie au système le code 0 car il n'y a plus d'erreur :

$ python3 start.py
Dinsdale found in file /path/to/start.py

On pourrait s'attendre à un problème au moment de l'importation de __main__ : cela ne provoque-t-il pas l'exécution anticipée du code de script sous if __name__ == '__main__' dans le module principal start ?

En fait, le déroulement de l'exécution est le suivant : au lancement de l'interpréteur, un module __main__ initialement vide est inséré dans sys.modules. Il se remplit au fur et à mesure de l'exécution du code principal. Dans notre exemple, le module principal est start, qui s'exécute ligne par ligne et en vient à importer namely. Or namely, à son tour, importe __main__, c'est-à-dire en fait start. On a donc une importation cyclique. Puisque le module __main__ est déjà présent dans sys.modules, bien qu'encore incomplet, il est directement passé à namely sans réimportation. La section Cas particulier de __main__ dans le document de référence du système d'importation explique plus avant ce fonctionnement.

L'interpréteur interactif est un autre environnement d'exécution principal possible. Toute variable qui y est définie appartient à l'espace de nommage __main__ :

>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky

Dans ce cas, il n'y a pas de variable __file__, puisque cela n'a pas de sens dans le mode interactif.

Le module __main__ est notamment employé dans les implémentations de pdb et rlcompleter.