Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3

Nous présentons à votre attention la troisième partie de la traduction de documents sur le chemin emprunté par Dropbox lors de la mise en œuvre d'un système de vérification de type pour le code Python.

Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3

→ Parties précédentes : premier и deuxième

Atteindre 4 millions de lignes de code tapé

Un autre défi majeur (et la deuxième préoccupation la plus courante parmi les personnes interrogées en interne) était l'augmentation de la quantité de code couverte par les vérifications de type dans Dropbox. Nous avons essayé plusieurs approches pour résoudre ce problème, allant de l'augmentation naturelle de la taille de la base de code typée à la concentration des efforts de l'équipe mypy sur l'inférence de type automatisée statique et dynamique. En fin de compte, il semblait qu'il n'y avait pas de stratégie gagnante simple, mais nous avons réussi à obtenir une croissance rapide du volume de code annoté en combinant de nombreuses approches.

En conséquence, notre plus grand référentiel Python (avec code backend) contient près de 4 millions de lignes de code annoté. Les travaux sur le typage statique du code ont été achevé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 du code présentant des ambiguïtés dans les types, comme par exemple l'utilisation explicite d'un type Any dans des annotations qui ne peuvent pas être vérifiées, ou avec des choses comme l'importation de bibliothèques tierces qui n'ont pas d'annotations de type. Dans le cadre d'un projet visant à améliorer la précision de la vérification des types dans Dropbox, nous avons contribué à l'amélioration des définitions de types (appelés fichiers stub) pour certaines bibliothèques open source populaires dans un référentiel Python centralisé. dactylographié.

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

Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3
Nombre de lignes de code annoté : serveur

Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3
Nombre de lignes de code annoté : client

Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3
Nombre total de lignes de code annoté

Voici un aperçu des principales fonctionnalités des mesures que nous avons prises pour augmenter la quantité de code annoté dans Dropbox :

Rigueur des annotations. Nous avons progressivement augmenté les exigences de rigueur dans l'annotation du nouveau code. Nous avons commencé avec des astuces linter qui suggéraient d'ajouter des annotations aux fichiers comportant déjà des annotations. Nous exigeons désormais des annotations de type dans les nouveaux fichiers Python et dans la plupart des fichiers existants.

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

Vulgarisation de mypy. Nous parlons de mypy lors d'événements et discutons avec les équipes pour les aider à démarrer avec les annotations de type.

Les 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 dans la résolution de ces problèmes (même en créant un nouveau langage pour accélérer mypy !).

Performance. Nous avons considérablement amélioré les performances de mypy en utilisant le démon et mypyc. Cela a été fait pour atténuer les inconvénients qui surviennent lors du processus d'annotation et pour pouvoir travailler avec de grandes quantités de code.

Intégration avec les éditeurs. Nous avons créé des outils pour prendre en charge l'exécution 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 sa fonctionnalité. Ces types d'actions sont courants lors de l'annotation de code existant.

Analyse statique. Nous avons créé un outil pour déduire des signatures de fonctions à l'aide d'outils d'analyse statique. Cet outil ne peut fonctionner que dans des situations relativement simples, mais il nous a aidé à augmenter notre couverture de types de code sans trop d'effort.

Prise en charge des bibliothèques tierces. Beaucoup de nos projets utilisent la boîte à outils SQLAlchemy. Il profite des capacités dynamiques de Python que les types PEP 484 ne peuvent pas modéliser directement. Conformément à la 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.

Difficultés que nous avons 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é de nombreux nids-de-poule et commis plusieurs erreurs. Ce sont quelques-uns des problèmes que nous avons rencontrés. Nous espérons qu’en parler aidera d’autres à éviter des problèmes similaires.

Fichiers manquants. Nous avons commencé notre travail en vérifiant seulement une petite quantité de fichiers. Tout ce qui n'était pas inclus dans ces fichiers n'a pas été vérifié. Les fichiers ont été ajoutés à la liste d'analyse lorsque les premières annotations y sont apparues. Si quelque chose était importé d'un module situé en dehors de la portée de la vérification, nous parlions alors de travailler avec des valeurs telles que Any, qui n’ont pas été testés du tout. Cela a entraîné une perte significative de précision de frappe, en particulier dans les premiers stades de la migration. Cette approche a étonnamment bien fonctionné jusqu'à présent, même si une situation typique est que l'ajout de fichiers à la portée de l'examen 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 ont été fusionnées, dans lesquelles, indépendamment l'une de l'autre, des types étaient déjà vérifiés, il s'est avéré que les types de ces zones étaient incompatibles entre eux. Cela a conduit à la nécessité d'apporter de nombreuses modifications aux annotations. Avec le recul, nous réalisons que nous aurions dû ajouter plus tôt les modules de bibliothèque de base à la zone de vérification de type de mypy. Cela rendrait notre travail beaucoup plus prévisible.

Annotation de l'ancien code. Lorsque nous avons commencé, nous avions environ 4 millions de lignes de code Python existant. Il était clair qu’annoter tout ce code n’était pas une tâche facile. Nous avons créé un outil appelé PyAnnotate qui peut collecter des informations de type lors de l'exécution des tests et ajouter des annotations de type à votre code en fonction des informations collectées. Cependant, nous n’avons pas constaté une adoption particulièrement répandue de cet outil. La collecte d'informations sur le type était lente et les annotations générées automatiquement nécessitaient souvent de nombreuses modifications manuelles. Nous avons pensé à exécuter cet outil automatiquement chaque fois que nous examinons le code ou à collecter des informations de type sur la base de l'analyse d'un petit volume de requêtes réseau réelles, mais nous avons décidé de ne pas le faire car l'une ou l'autre approche était trop risquée.

De ce fait, on peut noter que la majeure partie du code a été annotée manuellement par ses propriétaires. Afin d'orienter ce processus dans la bonne direction, nous préparons des rapports sur les modules et fonctions particulièrement importants qui doivent être annotés. Par exemple, il est important de fournir des annotations de type pour un module de bibliothèque utilisé à des centaines d'endroits. Mais un ancien service remplacé par un nouveau n’est plus aussi important à annoter. Nous expérimentons également l'utilisation de l'analyse statique pour générer des annotations de type pour le code existant.

Importations cycliques. Ci-dessus, j'ai parlé des importations cycliques (les « enchevêtrements de dépendances »), dont l'existence rendait difficile l'accélération de mypy. Nous avons également dû travailler dur pour que mypy prenne en charge toutes sortes d'idiomes provoqués par ces importations cycliques. Nous avons récemment achevé un projet majeur de refonte du système qui a résolu la plupart des problèmes de mypy concernant les importations circulaires. Ces problèmes provenaient en fait des tout premiers jours du projet, depuis Alore, la langue éducative sur laquelle le projet mypy était initialement axé. La syntaxe Alore facilite la résolution des problèmes liés aux commandes d'importation cycliques. Mypy moderne a hérité de certaines limitations de son implémentation antérieure et simple (qui convenait parfaitement à Alore). Python rend difficile le travail avec les importations circulaires, principalement parce que les expressions sont ambiguës. Par exemple, une opération d'affectation peut en fait définir un alias de type. Mypy n'est pas toujours capable de détecter de telles choses tant que la majeure partie de la boucle d'importation n'a pas été traitée. Il n’y avait pas de telles ambiguïtés à Alore. De mauvaises décisions prises dès les premiers stades du développement du système peuvent présenter une surprise désagréable au programmeur plusieurs années plus tard.

Résultats : le chemin vers 5 millions de lignes de code et de nouveaux horizons

Le projet mypy a parcouru un long chemin - depuis les premiers prototypes jusqu'à un système contrôlant 4 millions de lignes de types de codes de production. Au fur et à mesure de l'évolution de mypy, les indications de type de Python ont été standardisées. De nos jours, un puissant écosystème s’est développé autour de la saisie de code Python. Il a une place pour le support des bibliothèques, il contient des outils auxiliaires pour les IDE et les éditeurs, il dispose de plusieurs systèmes de contrôle de types, chacun ayant ses propres avantages et inconvénients.

Même si la vérification de type est déjà une évidence chez Dropbox, je pense que nous en sommes encore aux premiers jours 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 encore utilisé la vérification de type dans votre projet Python à grande échelle, sachez que c'est le moment idéal pour commencer à passer au typage statique. J'ai parlé à ceux qui ont fait une transition similaire. Aucun d’eux ne l’a regretté. La vérification de type fait de Python un langage bien mieux adapté au développement de grands projets que le « Python classique ».

Chers lecteurs, Utilisez-vous la vérification de type dans vos projets Python ?

Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3
Le chemin vers la vérification de type de 4 millions de lignes de code Python. Partie 3

Source: habr.com

Ajouter un commentaire