Python : le fichier __main__.py

Tout développeur Python connait le nom spécial __main__ utilisé dans la structure

if __name__ == "__main__":
    ...

En passant, ce code est bien valide, pour ceux qui auraient raté mon post, il utilise l’objet Ellipsis.

Ce nom spécial __main__ est le nom que l’interpréteur attribue au module principal, celui que vous exécutez lorsque vous exécutez un programme Python.

Mais savez-vous que ce nom peut aussi être utilisé pour nommer un module particulier : __main__.py ?

Dans quel cas faut-il l’utiliser ? C’est ce que nous allons voir dans cet article.

Rappel sur la structure d’un projet

Comment structurer un projet est la question qui revient régulièrment pour tout projet informatique. En Python il n’y a pas de spécification officielle (pas de PEP) qui décrit comment structurer un projet. Il y a cependant une recommandation proposée par Kenneth Reitz en 2013 et qui est toujours d’actualité. Dans son livre The Hitchhiker’s Guide to Python, Kenneth Reitz a développé la proposition de cette structure. Je ne la reprends pas ici, elle est bien détaillée dans les liens précédents. Cette structure est depuis largement adoptée par la communauté.

Structure du projet sous forme de script
Notre projet contenu dans un script.

Ainsi, un projet simple qui tient en un seul script ne contiendra à sa racine que ce script. C’est la structure de notre projet d’application de lancer de dés.

Un projet plus important, décomposé en modules, doit avoir pour racine un package qui définira le namespace racine du projet. Lorsque vous concevez un projet d’envergure, vous avez alors votre script à la racine de ce package.

Mais se pose alors un problème : comment faire évoluer le code lorsque vous passez d’un simple script à un projet plus complexe ? Car il y a déjà une habitude sur la manière de lancer votre programme. Dans notre cas, c’était par python -m diceroller. Dites vous que des scripts de lancement peuvent contenir de type d’appel et nous souhaitons éviter d’avoir à les modifier.

Le fichier __main__.py

Le fichier __main__.py fournit à un package une interface en ligne de commande. Ainsi, en faisant évoluer notre projet de lancer de dés d’un script à une arborescence de packages, nous allons nommer le package racine du même nom que notre script, diceroller, et nous transférerons tout le code que contenait notre script dans le fichier __main__.py à la racine de notre arborescence de packages.

Vous pouvez donc exécuter le projet exactement comme précédemment, python -m diceroller.

Utilisation idiomatique

L’objectif ce fichier __main__.py est de fournir un point d’entrée à votre programme, point d’entrée similaire à un script. Ce fichier doit rester court et fait appel aux ressources organisés dans des modules.

Bien que ce soit possible, il ne contient pas la structure idiomatique if name == '__main__': mais fait directement appel aux fonctions dans d’autres modules.

En adaptant notre projet, le fichier __main__.py dans le package diceroller contient :

from .cli import interaction_loop

interaction_loop()
Structure du projet sous forme d'arborescence de modules.
Notre projet décomposé en modules.

La fonction interaction_loop() est donc déplacée dans un module cli dans le package racine. Les deux autres fonctions sont également déplacées dans des modules adaptés.

La structure générale du projet devient celle de l’illustration ci-contre.

Note sur l’import

La PEP 8 recommande que les imports soient absolus. De même, il est conseillé d’importer un namespace, c’est à dire un module qui permettrait d’identifier où est déclaré la fonction.

Mais dans le cas présent, nous sommes dans un fichier court qui servira, dans une approche plus complexe, au routage de l’appel. Cela fait donc parti, à mon sens, des cas où les appels relatifs ont leur sens.

Une structure stable et évolutive

Ce qui m’intéresse dans ce type de composant, c’est la possibilité de faire évoluer un projet tout en le maintenant stable dans le sens où l’évolution du code ne nécessite aucune adaptation de la production. Nous pouvons ainsi, à partir d’un script, faire évoluer notre projet vers une structure plus complexe sans modifier l’usage que nous avions jusqu’ici.

Nous allons maintenant pouvoir réutiliser les fonctions définies pour cette application en les important de manière cohérente.

En conclusion

L’usage de fichier __main__.py est assez rare en Python, en tout cas, je ne l’ai pas vu jusqu’à récemment. Néanmoins, c’est le parfait point d’entrée d’un programme Python qui lève toute ambiguïté, qui évite de se poser la question « quel module est le module principal ? ». Pensez-y lors de vos créations de projets.

Comme d’habitude, le code source est disponible sur mon GitHub.

Si vous avez aimé ce post, n’hésitez pas à laisser un commentaire ci-dessous ou sur la page Facebook 😉

À propos de... Darko Stankovski

iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.

Vous aimerez aussi...

Laisser un commentaire