__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.

Putting as few statements as possible in the block below if __name__ == '__main__' can improve code clarity and correctness. Most often, a function named main encapsulates the program's primary behavior:

# 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.

By proactively following this convention ourselves, our module will have the same behavior when run directly (i.e. python3 echo.py) as it will have if we later package it as a console script entry-point in a pip-installable package.

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[1] 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

The content of __main__.py typically isn't fenced with an if __name__ == '__main__' block. Instead, those files are kept short and import functions to execute from other modules. Those other modules can then be easily unit-tested and are properly reusable.

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 ?

Python inserts an empty __main__ module in sys.modules at interpreter startup, and populates it by running top-level code. In our example this is the start module which runs line by line and imports namely. In turn, namely imports __main__ (which is really start). That's an import cycle! Fortunately, since the partially populated __main__ module is present in sys.modules, Python passes that to namely. See Special considerations for __main__ in the import system's reference for details on how this works.

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.