La façon de taper la vérification de 4 millions de lignes de code Python. 3e partie

Nous présentons à votre attention la troisième partie de la traduction du matériel sur le chemin emprunté par Dropbox, introduisant un système de vérification des types de code Python.



→ Parties précédentes: première et deuxième

Atteindre 4 millions de lignes de code dactylographié


Une autre tâche importante (c'était le deuxième problème le plus populaire qui inquiétait ceux qui ont participé aux sondages internes) était d'augmenter la quantité de code dans Dropbox couverte par des vérifications de type. Nous avons essayé plusieurs approches pour résoudre ce problème - de la croissance naturelle du volume de la base de code typé à la concentration des efforts de l'équipe mypy sur l'inférence de type automatisée statique et dynamique. En conséquence, il semblait qu'il n'y ait pas de stratégie gagnante simple, mais nous avons pu atteindre une croissance rapide du volume de code annoté en combinant de nombreuses approches.

En conséquence, dans notre plus grand référentiel Python (avec code backend), le nombre de lignes de code annoté a atteint près de 4 millions. Les travaux de dactylographie statique du code ont été réalisés en trois ans environ. Mypy prend désormais en charge différents types de rapports de couverture de code qui facilitent le suivi de la progression de la frappe. En particulier, nous pouvons générer des rapports sur le code avec des incertitudes dans les types, comme, par exemple, l'utilisation explicite du type Any dans les annotations qui ne peuvent pas être vérifiées, ou comme l'importation de bibliothèques tierces dans lesquelles il n'y a pas d'annotations de type. Dans le cadre d'un projet visant à augmenter la précision de la vérification de type dans Dropbox, nous avons contribué à améliorer les définitions de type (fichiers stub) pour certaines bibliothèques open source populaires dans le référentiel Python central de typehed .

Nous avons implémenté (et normalisé dans les PEP ultérieurs) de nouvelles fonctionnalités du système de types, qui nous permettent d'utiliser des types plus précis pour certains modèles Python spécifiques. Un exemple notable de ceci est TypeDict , qui fournit des types pour les dictionnaires de type JSON qui ont un ensemble fixe de clés de chaîne, chacune ayant une valeur de son propre type. Nous continuerons d'étendre le système de typage. Notre prochaine étape sera probablement d'améliorer la prise en charge de la capacité de Python à travailler avec des nombres.


Nombre de lignes de code annoté: serveur


Nombre de lignes de code annoté: client


Le nombre total de lignes de code annoté

Voici un aperçu des principales fonctionnalités des actions que nous avons effectuées pour augmenter le volume de code annoté dans Dropbox:

La rigueur de l'annotation. Nous avons progressivement augmenté les exigences de rigueur d'annotation du nouveau code. Nous avons commencé avec des astuces de linterface qui suggéraient d'ajouter des annotations aux fichiers qui ont déjà des annotations. Maintenant, nous avons besoin d'annotations de type dans les nouveaux fichiers Python et dans la plupart des fichiers existants.

Taper des rapports. Nous envoyons des rapports hebdomadaires aux équipes sur le niveau de saisie de leur code et donnons des conseils sur ce qui doit être annoté en premier lieu.

Vulgarisation de mypy. Nous parlons de mypy lors de divers événements et communiquons avec les équipes pour les aider à commencer à utiliser les annotations de type.

Sondages. Nous menons des enquêtes périodiques auprès des utilisateurs pour identifier les problèmes majeurs. Nous sommes prêts à aller assez loin pour résoudre ces problèmes (jusqu'à la création d'un nouveau langage pour accélérer mypy!).

Performance. Nous avons considérablement amélioré les performances de mypy grâce à l'utilisation du démon et de mypyc. Cela a été fait pour aplanir les inconvénients qui surviennent au cours du processus d'annotation et afin de pouvoir travailler avec de grandes quantités de code.

Intégration avec les éditeurs. Nous avons créé des outils pour soutenir le lancement de mypy dans les éditeurs populaires sur Dropbox. Cela inclut PyCharm, Vim et VS Code. Cela a grandement simplifié le processus d'annotation du code et de vérification de ses performances. De telles actions sont généralement typiques lors de l'annotation de code existant.

Analyse statique Nous avons créé un outil pour générer des signatures de fonction à l'aide d'outils d'analyse statique. Cet outil ne peut fonctionner que dans des situations relativement simples, mais il nous a permis d'augmenter la couverture des types avec peu d'effort.

Prise en charge des bibliothèques tierces. Beaucoup de nos projets utilisent la boîte à outils SQLAlchemy. Il utilise les capacités dynamiques de Python, que les types PEP 484 ne peuvent pas modéliser directement. Selon PEP 561, nous avons créé le fichier stub correspondant et écrit un plugin pour mypy ( open source ) qui améliore la prise en charge de SQLAlchemy.

Les difficultés rencontrées


Le chemin vers 4 millions de lignes de code tapé n'a pas toujours été facile pour nous. Sur ce chemin, nous avons rencontré beaucoup de trous et fait des erreurs. Voici quelques-uns des problèmes que nous avons rencontrés. Nous espérons que leur histoire aidera les autres à éviter de tels problèmes.

Fichiers ignorés. Nous avons commencé par vérifier seulement une petite quantité de fichiers. Tout ce qui n'est pas inclus dans le nombre de ces fichiers n'a pas été vérifié. Les fichiers ont été ajoutés à la liste de contrôle lorsque les premières annotations y sont apparues. Si quelque chose a été importé d'un module situé en dehors de la portée de la vérification, alors nous parlions de travailler avec des valeurs de type Any , qui n'étaient pas vérifiées du tout. Cela a entraîné une perte importante de précision de frappe, en particulier dans les premiers stades de la migration. Jusqu'à présent, cette approche a étonnamment bien fonctionné, bien qu'il soit typique que l'ajout de fichiers à la zone d'analyse révèle des problèmes dans d'autres parties de la base de code. Dans le pire des cas, lorsque deux zones de code isolées étaient combinées, dans lesquelles, indépendamment l'une de l'autre, les types étaient déjà vérifiés, il s'est avéré que les types de ces zones étaient incompatibles. Il a donc fallu apporter de nombreuses modifications aux annotations. Maintenant, avec le recul, nous comprenons que nous devons ajouter des modules de bibliothèque de base à la zone de vérification du type mypy dès que possible. Cela rendrait notre travail beaucoup plus prévisible.

Annoter l'ancien code. Lorsque nous avons commencé, nous avions environ 4 millions de lignes de code Python existant. Il était clair que l'annotation de tout ce code n'était pas une tâche facile. Nous avons créé un outil appelé PyAnnotate, qui peut collecter des informations de type pendant l'exécution du test et peut ajouter des annotations de type au code en fonction des informations collectées. Cependant, nous n'avons pas remarqué une introduction particulièrement répandue de cet outil. La saisie des informations sur les types était lente, les annotations générées automatiquement nécessitaient souvent de nombreuses modifications manuelles. Nous avons pensé à lancer automatiquement cet outil chaque fois que vous vérifiez le code, ou à collecter des informations de type sur la base d'une analyse d'une petite quantité de demandes réseau réelles, mais nous avons décidé de ne pas le faire, car l'une de ces approches est trop risquée.

En conséquence, on peut noter que la plupart du code a été annoté manuellement par ses propriétaires. Afin d'orienter ce processus dans la bonne direction, nous préparons des rapports sur des modules et des fonctions particulièrement importants qui doivent être annotés. Par exemple, il est important de fournir des annotations de type avec le module de bibliothèque utilisé dans des centaines d'endroits. Mais l'ancien service, qui est remplacé par un nouveau, l'annotation n'est plus aussi importante. Nous expérimentons également en utilisant l'analyse statique pour générer des annotations de type pour l'ancien code.

Importation en boucle. Plus tôt, j'ai parlé des importations cycliques («enchevêtrement de dépendances»), dont l'existence compliquait l'accélération de mypy. En outre, nous avons dû travailler dur pour fournir à mypy un support pour toutes sortes d'idiomes causés par ces importations cycliques. Nous avons récemment terminé un important projet de refonte du système qui a résolu la plupart des problèmes d'importation cyclique de mypy. Ces problèmes, en fait, sont survenus dès les premiers jours du projet, de retour d'Alore, la langue éducative vers laquelle mypy était à l'origine orientée. La syntaxe Alore facilite la résolution des problèmes des commandes d'importation cycliques. Le mypy moderne a hérité de certaines limites de sa mise en œuvre ingénue précoce (qui a très bien fonctionné pour Alore). Python rend difficile le travail avec les importations circulaires, principalement en raison de l'ambiguïté des expressions. Par exemple, lors d'une opération d'affectation, un alias de type peut en fait être déterminé. Mypy n'est pas toujours en mesure de détecter de telles choses tant que la majeure partie du cycle d'importation n'a pas été traitée. Alore n'avait pas de telles ambiguïtés. Les décisions infructueuses prises au début du développement du système peuvent surprendre un programmeur après de nombreuses années.

Résumé: Le chemin vers 5 millions de lignes de code et de nouveaux horizons


Le projet mypy a parcouru un long chemin - des premiers prototypes à un système qui contrôle les types de code de production avec un volume de 4 millions de lignes. Au fur et à mesure que mypy progressait, les indices de type étaient standardisés en Python. Un écosystème puissant a évolué autour de la frappe de code Python ces jours-ci. Il a trouvé un endroit pour prendre en charge les bibliothèques, il contient des outils auxiliaires pour les IDE et les éditeurs, il a plusieurs systèmes de contrôle de type, chacun ayant ses avantages et ses inconvénients.

Malgré le fait que la vérification de type est déjà considérée comme acquise dans Dropbox, je suis sûr que nous vivons toujours à l'aube de la saisie de code Python. Je pense que les technologies de vérification de type continueront d'évoluer et de s'améliorer.

Si vous n'avez pas utilisé de vérification de type dans votre projet Python à grande échelle, vous devez savoir que le moment est bien choisi pour commencer la transition vers la saisie statique. J'ai parlé avec ceux qui ont fait une transition similaire. Aucun d'eux ne l'a regretté. Le contrôle de type transforme Python en un langage bien meilleur que "Python normal" pour développer de grands projets.

Chers lecteurs! Utilisez-vous le contrôle de type dans vos projets Python?


Source: https://habr.com/ru/post/fr468237/


All Articles