- Sous l’effet des tendances récentes du développement IA, l’auteur s’est sérieusement mis à apprendre et à utiliser Python, et se dit désormais très satisfait de son écosystème
- Python a évolué pour devenir un langage bien plus rapide et moderne qu’auparavant, et les gains de performance via Cython illustrent cette progression rapide
- Des outils et bibliothèques de développement modernes comme uv, ruff, pytest et Pydantic ont été activement intégrés à son workflow pour améliorer la productivité
- Il applique aussi une structure de projet et des mécanismes d’automatisation afin de réduire l’écart entre les environnements de production et le développement basé sur des notebooks/scripts Jupyter
- Avec GitHub Actions, Docker, etc., il met en place efficacement la CI/CD, les tests et la gestion d’infrastructure
Résumé de I’m Switching to Python and Actually Liking It
Pourquoi passer à Python
- Dans un environnement de développement centré sur l’IA, Python s’est imposé comme le langage standard de facto
- Autrefois utilisé seulement pour écrire de petits scripts, il est désormais employé sérieusement pour créer des applications concrètes autour du RAG, des agents et de l’IA générative
- Ce faisant, l’auteur a constaté à quel point l’écosystème Python avait énormément évolué par rapport au passé
3 points forts de Python
- Un écosystème riche en bibliothèques et outils : spécialisé dans le traitement des données, l’analyse, le web et l’IA
- Des performances améliorées grâce à Cython, etc. : possibilité d’optimisations basées sur la compilation
- Une meilleure lisibilité de la syntaxe : des syntaxes héritées comme
__init__, __new__ sont davantage masquées, au profit d’une écriture plus intuitive
Structure du projet (basée sur un monorepo)
Principaux outils et réglages
-
uv
- Gestionnaire de paquets Python et outil de build moderne proposé par Astral
- Gère rapidement la plupart des tâches : dépendances, création d’environnements virtuels, initialisation de projet, etc.
pyproject.toml est le fichier de configuration central, qui regroupe toutes les métadonnées et dépendances
- Les commandes
uv init, uv add, uv sync permettent de configurer rapidement l’environnement du projet
-
ruff
- Linter Python ultra-rapide et formateur de code
- Outil unifié qui intègre
isort, flake8, autoflake, etc.
ruff check, ruff format pour le linting et les corrections automatiques
- Prise en charge native du guide de style PEP 8
-
ty
- Vérificateur de types statique pour Python créé par Astral
- Combiné à
typing, il est efficace pour l’analyse statique et la prévention précoce des bugs
- Bien qu’encore en phase initiale, il est déjà assez stable pour être utilisé
-
pytest
- Framework de test Python de référence pour les tests unitaires et un environnement de test extensible
- Grâce à des conventions simples de nommage de fichiers et à une seule commande, il permet d’intégrer immédiatement des tests
- Structurer les tests sous
test_*.py, puis exécuter avec uv run pytest
- Syntaxe concise et riche écosystème de plugins
-
Pydantic
- Bibliothèque de validation de données et de gestion de configuration d’environnement
- Chargement de configuration à partir de variables d’environnement
.env avec validation des types
- La classe
BaseSettings permet de gérer en toute sécurité les clés API, URL de base de données, etc.
-
MkDocs
- Permet de générer simplement des sites statiques et de la documentation pour les projets Python
- Facilite l’adoption rapide d’un design soigné de style projet open source
- Intégration facile avec GitHub Pages
-
FastAPI
- Framework rapide pour construire des API RESTful
- Atouts : validation et documentation automatiques, hautes performances, intégration simple avec Pydantic
- Basé sur Starlette et Pydantic, il offre une bonne sûreté de typage et de solides performances
-
Dataclasses
- Fonctionnalité standard de Python permettant de définir facilement des classes centrées sur les données
- La génération automatique de méthodes spéciales réduit fortement le code boilerplate
Gestion de versions et automatisation
-
GitHub Actions
- Mise en place de pipelines CI séparés pour
project-api et project-ui
- Fournit des workflows bien adaptés à la mise en place de pipelines CI sur différents OS
- Grâce à un environnement de test basé sur Docker, il est possible d’effectuer des tests dans les mêmes conditions que la production
-
Dependabot
- Automatise la mise à jour des dépendances et la gestion des correctifs de sécurité
-
Gitleaks
- Outil de prévention des fuites d’informations sensibles (mots de passe, clés API, etc.) exécutant des contrôles de sécurité avant les commits git
-
Pre-commit Hooks
- Outil pour le linting, le formatting et les contrôles de sécurité automatiques avant commit
- Utilisé avec ruff, gitleaks, etc. pour préserver la cohérence et la qualité du code
Automatisation de l’infrastructure
-
Make
- Assure un workflow de développement cohérent avec des commandes comme
make test, make infrastructure-up
- Un Makefile est présent à la racine du projet ainsi que dans
project-api
-
Docker & Docker Compose
project-api et project-ui sont exécutés séparément dans des conteneurs
- Une seule commande
docker compose up --build -d permet de lancer toute l’application
- Le
Dockerfile inclut l’installation de uv et la commande de lancement de l’application FastAPI
Conclusion
- Comme on le voit, l’environnement moderne de développement Python permet de construire un workflow de production efficace et robuste
- On peut largement bénéficier de la croissance de l’écosystème Python et des progrès de ses outils dans des domaines variés comme l’IA, la data ou le développement web
- Structure monorepo, outils d’automatisation, linter et vérificateur de types, environnement de test immédiat, documentation et orchestration de l’infrastructure : tout cela permet de mettre en place une culture de développement intégrée
6 commentaires
Python a l’avantage d’avoir un riche écosystème de bibliothèques et de frameworks, mais son inconvénient est une gestion des versions de paquets peu fiable et des conflits fréquents.
Cela ressemble, par ses avantages et ses inconvénients, au Java d’autrefois.
Le
uvmentionné dans l’article est vraiment excellent. Non seulement c’est rapide, mais il gère aussi très bien les versions et les dépendances, un peu comme npm, donc je suis en train d’adopteruv.Mais ces derniers temps, on dirait qu'avec uv et poetry, la gestion des versions et les conflits sont en grande partie résolus.
Est-ce aussi adapté pour couvrir l’écosystème, y compris des aspects comme React ?
L’interopérabilité directe avec React peut être difficile puisque les langages sont différents, mais selon ce que vous souhaitez faire, certaines choses semblent possibles.
Personnellement, je pense que le développement frontend via Python n’est pas un domaine particulièrement répandu.
Avis Hacker News
Dans le code, afficher un message du type « YOUTUBE_API_KEY ou YOUTUBE_CHANNEL_ID est manquant » avec un OR quand une seule valeur manque est une façon d’agacer inutilement l’utilisateur dans un cas où l’OR n’est pas nécessaire. Il est bien préférable de vérifier chaque valeur séparément et d’indiquer clairement laquelle manque. La différence en temps de développement est quasi nulle, donc je recommande de le faire ainsi
C’est pinailler sur un petit détail, mais je trouve que ce genre de cas est parfait pour l’opérateur := (opérateur walrus). Par exemple, on peut écrire directement :
if not (API_KEY := os.getenv("API_KEY")):. Personnellement, pour les outils internes, je laisse simplementos.environ["API_KEY"]lever unKeyError. Je trouve ça aussi suffisamment clairMieux encore, je pense qu’il vaut bien mieux vérifier les conditions une par une et, s’il en manque au moins une, toutes les signaler. Cela évite l’inconfort de lancer le programme pour une variable manquante, puis de voir ensuite une autre erreur sur une autre variable. Selon les cas, ce n’est pas toujours simple à faire, mais autant que possible, mieux vaut tout afficher d’un coup
Le mieux, c’est de récupérer toutes les variables d’environnement, puis de signaler en une seule fois celles qui manquent
On peut aussi utiliser un flag booléen pour ne faire qu’un seul
exit(1)à la fin. Cela permet d’afficher d’un coup toutes les variables d’environnement manquantesOn peut aussi faire quelque chose comme
exit("Missing ..."), qui affiche directement le message puis termine avec le code 1Si vous cherchez un outil qui génère automatiquement la structure d’un projet, je recommande cookiecutter. J’ai quelques templates que j’utilise souvent, comme python-lib, click-app, datasette-plugin, llm-plugin. On peut l’utiliser ainsi :
uvx cookiecutter gh:simonw/python-libJ’ai créé quelque chose en Ruby qui s’appelle baker. baker ne copie pas un repo de template : il construit plutôt une checklist des tâches à effectuer (une liste d’étapes impératives) et permet de mélanger des tâches manuelles (récupérer et configurer une clé API) et automatiques (
uv init, etc.). On peut utiliser du Markdown, l’interpolation de chaînes Ruby et même du bash. Je l’ai créé parce que j’en avais vraiment assez des configs en YAMLLe truc à la mode en ce moment, c’est Copier. Voir la documentation de Copier pour les détails
J’aime plutôt bien configurer un nouveau projet. Je n’ai pas spécialement envie d’automatiser ça
Je pense que ce domaine des outils d’automatisation de structure colle justement très bien aux workflows de développement actuels basés sur des agents LLM
Dire que « Python est plus humain parce qu’il est installé par défaut sur la plupart des Unix » me semble un peu optimiste. Dès qu’on dépasse
import json, on tombe vite dans l’enfer de virtualenv. Pour faire tourner du Python 3.13.x sur Ubuntu 22.04 ou 24.04, Rocky 9, etc.,venv, les conteneurs et les gestionnaires de version deviennent de toute façon indispensablesMême une bibliothèque standard comme
import jsondevrait être installée séparément dans un langage qui ne l’inclut pas, alors qu’avec Python, la standard library offre une bonne productivité au départ. Bien sûr, pour les gros projets, la bibliothèque standard ne suffit pas, mais j’ai réellement déployé plusieurs codes de production uniquement avec elle, sans problème de déploiement ni de gestion de la sécurité. La gestion devenvn’est plus aussi difficile qu’avant, et les package managers ont progresséUne de mes théories à moitié humoristiques, c’est que si Docker et les conteneurs se sont diffusés si vite, c’est pour moitié parce qu’ils permettaient de surmonter l’enfer des dépendances Python. Ma première expérience avec Python, c’était en 2012, en installant un service Python sur un serveur : enfer des dépendances, commandes
venv, configuration d’environnement pénible, c’était vraiment horrible. Avecpip,brewet l’environnement macOS, je ne faisais que galérer, au point d’éviter Python à vue. Mais récemment, grâce àuv, j’ai l’impression que Python est devenu bien plus accessible, même du point de vue d’un débutant.uv init,uv addetuv runsuffisent déjà largementJe pense qu’il faut toujours utiliser virtualenv. Au final, ce n’est qu’un répertoire, et aujourd’hui, si on essaie d’installer globalement avec
pip, on reçoit même des avertissements ; ce n’est plus aussi difficile qu’avantIl vaut mieux absolument utiliser un virtualenv ou un container. Même si cela semble pénible à gérer, cela évite d’impacter tout le système à cause d’une mise à jour ou d’un changement de version de bibliothèque
Autrefois, beaucoup de systèmes n’incluaient que Python2 par défaut, et comme le système lui-même dépendait parfois de ce Python2, c’était au contraire plus risqué
J’ai l’impression que Python est à la fois verbeux et insuffisant. Pour faire quelque chose de simple, soit il faut ajouter 500 dépendances, soit on se retrouve avec des dizaines, voire des centaines de lignes pour une tâche triviale. Du coup, j’évite Python parce qu’il impose trop de friction inutile. Je préfère Perl, avec lequel on peut boucler le travail bien plus vite et de façon bien plus concise. Avec Python, on a l’impression de faire de la programmation pour programmer plutôt que pour accomplir la tâche
Je fais aussi beaucoup de projets sans dépendances. On peut vraiment faire énormément de choses avec un seul fichier et la bibliothèque standard. Tant que Python est installé, on peut télécharger avec
curlet exécuter directement. Par exemple, j’ai un outil CLI de gestion d’argent de 2000 lignes, plutus, qui n’utilise qu’une douzaine de modules standard. Environ 25 % du code sert au parsing des commandes avecargparse. J’aime écrire les choses clairement, avec par exemple une ligne par paramètreTu dis que Perl est plus rapide et plus puissant que Python, mais j’aimerais bien avoir des exemples concrets
Ce qui est pratique avec Python, c’est qu’on peut imbriquer des structures de données sans trop y réfléchir. On peut mélanger librement listes, tuples, dictionnaires, etc., et y accéder avec une syntaxe cohérente. Perl est clairement plus malin et plus amusant, mais justement, ça me noue plus facilement le cerveau. Python est peut-être un peu ennuyeux, mais sa clarté est telle qu’on peut encore comprendre son code cinq ans plus tard
Je pense que Python est déjà tout à fait utilisable rien qu’avec sa bibliothèque standard
Je suis partisan des monorepos, mais dans une ancienne entreprise où j’ai travaillé, cette approche avait fini par produire une structure gigantesque et obèse que plus personne n’osait toucher de peur de casser le code des autres équipes. Le cœur du problème n’était pas vraiment le repo lui-même, mais plutôt le fait de gérer un unique
requirements.txtpour tout le repo, ou d’avoir des scripts de build devenus ingérables. En théorie, une seule mise à jour des dépendances devrait suffire à sécuriser tout le code avec les derniers correctifs ; en pratique, personne n’osait y toucher. Les monorepos ne fonctionnent vraiment bien que dans des organisations très NIH (avec une forte tendance à tout fabriquer elles-mêmes, comme Google). Cette expérience m’a conduit à mieux considérer les architectures microservices où chaque service correspond à la structure des équipes dans l’organisation. Voir aussi la loi de ConwayPython est le langage qui se comporte le plus comme le pseudo-code que j’écris. Chaque fois que je considère quelque chose comme clair dans ma tête, Python me fournit en pratique une abstraction intuitive correspondante. Comme je viens d’un background en mathématiques, ça m’a beaucoup parlé. Bien sûr, aujourd’hui j’aime aussi d’autres langages, mais Python garde toujours son charme
Je construis mes projets avec presque exactement le même schéma. C’est tellement similaire que c’en est effrayant. Je me demande si l’écosystème des développeurs Python ne converge pas de plus en plus vers un style unique. Avant, je pensais que mes choix étaient originaux, mais si tout le monde fait pareil, je me demande où est passée ma liberté de choix. Ça me fait penser au phénomène des prénoms de bébé : on croit avoir choisi quelque chose d’unique, et en fait c’était juste le 2e plus populaire
Cette structure est populaire en Python depuis déjà dix ans. Au fond, on dirait que plusieurs ingénieurs raisonnables finissent naturellement par converger vers ce pattern après y avoir bien réfléchi
J’ai l’impression que l’ego humain est comme une onde quantique pilote qui couvre tout le spectre, puis se transforme en existence. becoming-being, ça me fait rire rien qu’à l’écrire
Ça me fait plaisir de voir que d’autres personnes en viennent à aimer Python. À l’origine, je préférais Ruby, mais à cause des demandes clients, j’ai dû me mettre à Python malgré moi. Ruby était très lent à l’époque, mais à force d’apprendre Python sous la contrainte, je m’y suis habitué peu à peu, et maintenant j’y prends un certain plaisir. J’ai quelques réserves sur l’usage de Make : si on n’utilise aucune dépendance, ce n’est guère différent d’un script avec une instruction
case... je dis ça à moitié pour plaisanter, mais voir que la jeune génération n’est plus familière avec Make me rend un peu mélancolique. Une sorte de « de mon temps », en sommeRuby a une syntaxe bien plus élégante. Le fait que Python délimite la portée uniquement par l’indentation n’est pas du tout mon style
J’ai commencé avec un script à base de
case, puis ça a fini par évoluer en Makefile plat. Un Makefile est plus standard et plus lisible qu’un script bricolé au hasardJe me demande quand utiliser Dataclass ou Pydantic Basemodel. Si on utilise déjà Pydantic, ne vaudrait-il pas mieux tout unifier avec Pydantic ? Je me demande s’il reste vraiment une raison d’utiliser Dataclass
Le projet attrs a un excellent article de comparaison. Il y a la comparaison officielle d’attrs ; il y a sans doute un léger biais, mais je trouve l’argumentation solide. Et ce billet de blog aide aussi
Dataclass ne prend pas en charge la validation des objets imbriqués. Donc pour une structure simple et plate servant à passer des arguments à une fonction, mieux vaut utiliser dataclass. C’est plus clair que de passer une liste interminable d’arguments
Il y a aussi un problème de performance lié à la validation des données à la création. Il existe des alternatives bien plus légères et rapides, comme msgspec
Si on n’a pas besoin de validation ni de sérialisation, Pydantic représente plutôt un surcoût inutile. Ma règle, c’est : Pydantic si j’ai besoin de sérialisation, sinon dataclass
On peut utiliser directement une dataclass existante avec
TypeAdapter(MyDataclass), donc je ne vois pas bien pourquoi il faudrait créer en plus un modèle Pydantic séparéCes derniers temps, je suis plutôt parti de Python vers d’autres langages, et j’en suis plus satisfait. J’ai résumé mon point de vue sur Python dans ce texte. Si j’ai à nouveau l’occasion d’utiliser Python plus tard, j’essaierai absolument des outils comme
uv,ruffettyasyncio, mais il y a plusieurs approches concurrentes et aucun standard clair ne s’est imposé. En JS, tout converge vers une seule façon de faire, et ça m’a paru bien plus confortable. En ajoutant aussi tous les petits détails, l’écart devient significatif : la portée par indentation en Python, les problèmes de chemins relatifs dans les imports, la syntaxe des objets en JS, tout cela me paraît plus agréable côté JS. Voir cette explication liée