Un fichier .py contenant quelques fonctions constitue techniquement un module Python. La difficulté ne réside pas dans la création de ce fichier, mais dans la façon dont il sera importé, testé et maintenu au fil du temps. Un module qui mélange logique métier, configuration et point d’entrée CLI devient rapidement un bloc monolithique difficile à réutiliser.
Anatomie d’un py module propre : séparer logique métier et point d’entrée
La plupart des tutoriels montrent un fichier unique qui contient à la fois des fonctions et un bloc if __name__ == "__main__". Cette approche fonctionne pour un script personnel, mais pose un problème dès que le module est importé ailleurs : le point d’entrée doit être séparé de la logique réutilisable.
A voir aussi : Créer un site e-commerce avec CodeIgniter en utilisant PHP
Un module réutilisable ne devrait jamais exécuter de code au moment de l’import. Les guides d’architecture Python récents recommandent de garder la logique métier indépendante des frameworks d’I/O et de centraliser le câblage dans un composition root unique. Le fichier contenant vos fonctions ou classes ne fait que les définir. Un second fichier (ou un __main__.py dans un package) s’occupe de l’orchestration.
Cette séparation rend le module testable sans configuration particulière. Vos tests importent directement les fonctions sans déclencher d’effets de bord, sans devoir simuler des arguments en ligne de commande ni patcher des appels réseau cachés dans le corps du module.
A voir aussi : La conversion de 250 Go en Mo simplifiée pour tous
Structure de fichiers d’un package Python installable
Un module seul (un fichier .py) suffit pour des cas simples. Dès que le code grandit, la conversion en package s’impose. Voici la structure minimale qui permet une installation propre avec pip :
| Fichier / Dossier | Rôle |
|---|---|
mon_package/__init__.py |
Déclare le dossier comme package, expose l’API publique |
mon_package/core.py |
Logique métier (fonctions, classes) |
mon_package/__main__.py |
Point d’entrée CLI (optionnel) |
pyproject.toml |
Métadonnées du projet, dépendances, système de build |
tests/ |
Répertoire de tests, hors du package distribué |

Le fichier pyproject.toml remplace les anciens setup.py et setup.cfg. Il centralise la déclaration du nom, de la version et des dépendances. Un projet structuré de cette façon s’installe en mode éditable (pip install -e .), ce qui élimine le besoin de bricoler PYTHONPATH ou sys.path pour que les imports fonctionnent.
Contrôler la surface publique avec __all__ et le fichier __init__.py
Un piège fréquent consiste à laisser le fichier __init__.py vide ou, à l’inverse, à y importer la totalité des sous-modules. Les deux approches posent problème. Un __init__.py vide oblige les consommateurs à connaître la structure interne du package. Un __init__.py qui importe tout crée des dépendances circulaires et ralentit le chargement.
__all__ sert de contrat public explicite pour le module. En le définissant, vous déclarez précisément quels noms font partie de l’API stable. Tout le reste devient un détail d’implémentation que vous pouvez refactoriser sans casser le code des utilisateurs.
Un __init__.py bien écrit ressemble à ceci :
- Il importe uniquement les fonctions et classes destinées à l’usage externe depuis les sous-modules internes.
- Il définit
__all__comme une liste contenant exactement ces noms publics. - Il ne contient aucune logique métier, aucune constante de configuration, aucun effet de bord.
Cette discipline empêche le module de devenir un fourre-tout. Chaque ajout à __all__ est une décision consciente d’élargir la surface publique.
Imports propres : éviter les chemins bricolés qui cassent en production
Ajouter sys.path.append ou forcer PYTHONPATH=. dans la documentation d’un module sont des signaux d’alerte. Les imports de chemins bricolés ne remplacent pas un packaging correct. Si un import ne fonctionne pas sans manipulation de chemin, c’est que la structure du projet ou les métadonnées de packaging sont incorrectes.
La solution consiste à rendre le projet installable. Un fichier pyproject.toml minimal avec la section [project] et un système de build (comme hatchling, setuptools ou flit) suffit. Une fois le package installé localement, tous les imports fonctionnent de façon identique en développement, dans les tests et en production.
Les imports relatifs (avec un point : from .core import ma_fonction) sont la norme à l’intérieur d’un package. En revanche, les imports absolus restent préférables dans les scripts de point d’entrée et les tests, car ils reflètent la façon dont le package sera utilisé par des tiers.
Py module et testabilité : le critère qui départage les architectures
Un module bien conçu se teste sans configuration spéciale. Si vos tests nécessitent de mocker l’intégralité d’un sous-système pour tester une simple fonction utilitaire, c’est que le module mélange des responsabilités.
- Chaque fichier du package correspond à un domaine fonctionnel précis (
parsing.py,validation.py,export.py) et non à un fourre-toututils.pyqui grossit indéfiniment. - Les fonctions prennent leurs données en paramètre et retournent un résultat. Elles ne lisent pas de fichier, ne font pas de requête réseau, ne modifient pas d’état global.
- Les dépendances lourdes (base de données, API externe) sont injectées via des paramètres, pas importées directement dans le corps des fonctions métier.
Un module réutilisable est un module dont chaque fonction se teste en trois lignes : un appel, une assertion, pas de setup complexe. Ce critère simple permet de détecter très tôt les problèmes d’architecture.

Publier un module sur PyPI avec pyproject.toml et build
La publication sur PyPI transforme un module local en package installable par quiconque via pip install. Le processus repose sur deux outils : python -m build pour générer les archives de distribution, et twine pour les envoyer sur le dépôt.
Le fichier pyproject.toml doit contenir au minimum le nom du projet, la version, une description et la liste des dépendances. Déclarer les dépendances dans pyproject.toml garantit leur installation automatique. Les utilisateurs n’auront pas à deviner quels packages tiers votre module requiert.
Avant de publier, testez l’installation dans un environnement virtuel vierge. Créez un venv, installez le package depuis l’archive générée, et vérifiez que les imports fonctionnent. Cette étape détecte les fichiers manquants, les dépendances oubliées et les imports cassés qui ne se manifestent pas dans votre environnement de développement habituel.
La qualité d’un module Python se mesure moins à la quantité de fonctions qu’il expose qu’à la rigueur de sa surface publique et à la propreté de ses imports. Un package avec trois fonctions bien isolées, un __all__ explicite et un pyproject.toml complet sera adopté plus facilement qu’une bibliothèque tentaculaire où chaque import déclenche une cascade de dépendances.

