{"id":38479,"date":"2019-10-31T22:23:56","date_gmt":"2019-10-31T19:23:56","guid":{"rendered":"https:\/\/prohoster.info\/blog\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\/"},"modified":"2019-10-31T22:23:56","modified_gmt":"2019-10-31T19:23:56","slug":"put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","status":"publish","type":"post","link":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Chemin vers la v\u00e9rification des types de 4 millions de lignes de code Python. Partie 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Aujourd'hui, nous publions la deuxi\u00e8me partie de la traduction d'un article sur la fa\u00e7on dont Dropbox a organis\u00e9 le contr\u00f4le des types de plusieurs millions de lignes de code Python.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Chemin vers la v\u00e9rification des types de 4 millions de lignes de code Python. Partie 2\" src=\"\/wp-content\/uploads\/2019\/09\/3f8f396d724153a0374a3fadca190d55.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>\u2192 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468233\/\">Lire la premi\u00e8re partie<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Support officiel des types (PEP 484)<\/h2>\n<p>\nNous avons men\u00e9 nos premi\u00e8res exp\u00e9riences s\u00e9rieuses avec mypy chez Dropbox pendant la Hack Week 2014. La Hack Week est un \u00e9v\u00e9nement organis\u00e9 par Dropbox durant une semaine o\u00f9 les employ\u00e9s peuvent travailler sur tout ce qu'ils souhaitent ! Certains des projets technologiques les plus c\u00e9l\u00e8bres de Dropbox ont commenc\u00e9 lors de ces \u00e9v\u00e9nements. \u00c0 l'issue de cette exp\u00e9rience, nous avons conclu que mypy semblait prometteur, bien que ce projet ne soit pas encore pr\u00eat pour une utilisation g\u00e9n\u00e9ralis\u00e9e.<\/p>\n<p>\u00c0 l'\u00e9poque, l'id\u00e9e de normaliser les syst\u00e8mes de suggestions de types Python flottait dans l'air. Comme je l'ai mentionn\u00e9, depuis Python 3.0, il \u00e9tait possible d'utiliser des annotations de types pour les fonctions, mais il ne s'agissait que d'expressions al\u00e9atoires, sans syntaxe et s\u00e9mantique d\u00e9finies. Pendant l'ex\u00e9cution du programme, ces annotations \u00e9taient pour la plupart simplement ignor\u00e9es. Apr\u00e8s la Hack Week, nous avons commenc\u00e9 \u00e0 travailler sur la normalisation de la s\u00e9mantique. Ce travail a conduit \u00e0 la cr\u00e9ation de <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (ce document a \u00e9t\u00e9 \u00e9labor\u00e9 en collaboration avec Guido van Rossum, \u0141ukasz Langa et moi-m\u00eame).<\/p>\n<p>Nos motivations pouvaient \u00eatre consid\u00e9r\u00e9es sous deux angles. Premi\u00e8rement, nous esp\u00e9rions que tout l'\u00e9cosyst\u00e8me Python pourrait adopter une approche commune pour l'utilisation des suggestions de types (type hints \u2014 terme utilis\u00e9 en Python comme \u00e9quivalent des \u00ab annotations de types \u00bb). Cela, malgr\u00e9 les risques possibles, serait mieux que d'utiliser de nombreuses approches incompatibles. Deuxi\u00e8mement, nous souhaitions discuter ouvertement des m\u00e9canismes d'annotation des types avec de nombreux repr\u00e9sentants de la communaut\u00e9 Python. En partie, ce d\u00e9sir \u00e9tait dict\u00e9 par le fait que nous ne voulions pas appara\u00eetre comme des \u00ab ren\u00e9gats \u00bb des id\u00e9es fondamentales du langage aux yeux de la grande masse des programmeurs Python. C'est un langage \u00e0 typage dynamique, connu pour sa \u00ab typage de canard \u00bb. Au sein de la communaut\u00e9, au d\u00e9but, il y avait sans doute une certaine suspicion vis-\u00e0-vis de l'id\u00e9e de typage statique. Mais cette attitude a finalement diminu\u00e9 \u2014 une fois qu'il a \u00e9t\u00e9 clair que le typage statique ne serait pas rendu obligatoire (et apr\u00e8s que les gens ont compris qu'il \u00e9tait r\u00e9ellement utile).<\/p>\n<p>La syntaxe des annotations de types qui a finalement \u00e9t\u00e9 adopt\u00e9e ressemblait beaucoup \u00e0 celle prise en charge par mypy \u00e0 l'\u00e9poque. Le document PEP 484 est sorti avec Python 3.5 en 2015. Python n'\u00e9tait plus un langage qui ne supportait que la typage dynamique. J'aime consid\u00e9rer cet \u00e9v\u00e9nement comme une \u00e9tape importante dans l'histoire de Python.<\/p>\n<h2>D\u00e9but de la migration<\/h2>\n<p>\n\u00c0 la fin de 2015, une \u00e9quipe de trois personnes a \u00e9t\u00e9 cr\u00e9\u00e9e chez Dropbox pour travailler sur mypy. Elle comprenait Guido van Rossum, Greg Price et David Fisher. \u00c0 partir de ce moment, la situation a \u00e9volu\u00e9 tr\u00e8s rapidement. Le premier obstacle \u00e0 la croissance de mypy a \u00e9t\u00e9 la performance. Comme je l'ai d\u00e9j\u00e0 \u00e9voqu\u00e9, durant la phase de d\u00e9veloppement initiale du projet, j'envisageais de traduire l'impl\u00e9mentation de mypy en langage C, mais cette id\u00e9e a \u00e9t\u00e9 mise de c\u00f4t\u00e9 pour le moment. Nous \u00e9tions limit\u00e9s par le fait que le syst\u00e8me \u00e9tait ex\u00e9cut\u00e9 sur l'interpr\u00e9teur CPython, qui n'a pas une vitesse suffisante pour des outils comme mypy. (Le projet PyPy, une impl\u00e9mentation alternative de Python avec un compilateur JIT, ne nous a pas non plus aid\u00e9s.)<\/p>\n<p>Heureusement, certaines am\u00e9liorations algorithmiques nous ont aid\u00e9s. La premi\u00e8re am\u00e9lioration majeure a \u00e9t\u00e9 l'impl\u00e9mentation de la v\u00e9rification incr\u00e9mentielle. L'id\u00e9e de cette am\u00e9lioration \u00e9tait simple : si toutes les d\u00e9pendances d'un module n'avaient pas chang\u00e9 depuis le dernier lancement de mypy, nous pouvions utiliser, au cours du travail sur les d\u00e9pendances, les donn\u00e9es mises en cache lors de la session pr\u00e9c\u00e9dente. Nous devions simplement effectuer une v\u00e9rification des types dans les fichiers modifi\u00e9s et dans ceux qui en d\u00e9pendaient. Mypy est all\u00e9 m\u00eame un peu plus loin : si l'interface externe d'un module ne changeait pas, mypy consid\u00e9rait qu'il n'\u00e9tait pas n\u00e9cessaire de v\u00e9rifier \u00e0 nouveau d'autres modules qui importent ce module.<\/p>\n<p>La v\u00e9rification incr\u00e9mentielle nous a \u00e9norm\u00e9ment aid\u00e9s lors de l'annotation de grandes quantit\u00e9s de code existant. En effet, ce processus n\u00e9cessite g\u00e9n\u00e9ralement de nombreuses ex\u00e9cutions it\u00e9ratives de mypy, car les annotations sont progressivement ajout\u00e9es au code et am\u00e9lior\u00e9es au fil du temps. La premi\u00e8re ex\u00e9cution de mypy \u00e9tait encore tr\u00e8s lente, car il fallait v\u00e9rifier de nombreuses d\u00e9pendances. Pour am\u00e9liorer la situation, nous avons mis en place un m\u00e9canisme de mise en cache \u00e0 distance. Si mypy d\u00e9tecte que le cache local est probablement obsol\u00e8te, il t\u00e9l\u00e9charge l'instantan\u00e9 actuel du cache de l'ensemble de la base de code depuis un d\u00e9p\u00f4t centralis\u00e9. Ensuite, il ex\u00e9cute une v\u00e9rification incr\u00e9mentielle en utilisant cet instantan\u00e9. Cela nous a fait faire un pas significatif vers l'augmentation des performances de mypy.<\/p>\n<p>Cela a \u00e9t\u00e9 une p\u00e9riode d'adoption rapide et naturelle du syst\u00e8me de v\u00e9rification de types chez Dropbox. \u00c0 la fin de 2016, nous avions d\u00e9j\u00e0 environ 420000 lignes de code Python annot\u00e9es. De nombreux utilisateurs ont accueilli avec enthousiasme la v\u00e9rification des types. \u00c0 Dropbox, mypy \u00e9tait de plus en plus utilis\u00e9 par les \u00e9quipes de d\u00e9veloppement.<\/p>\n<p>Tout semblait alors bien, mais nous avions encore beaucoup \u00e0 faire. Nous avons commenc\u00e9 \u00e0 r\u00e9aliser des sondages internes p\u00e9riodiques aupr\u00e8s des utilisateurs pour identifier les points probl\u00e9matiques du projet et comprendre quelles questions devaient \u00eatre r\u00e9solues en priorit\u00e9 (cette pratique est encore utilis\u00e9e dans l'entreprise aujourd'hui). Deux t\u00e2ches se sont r\u00e9v\u00e9l\u00e9es \u00eatre les plus importantes. La premi\u00e8re \u00e9tait d'augmenter la couverture du code par des types, et la seconde \u00e9tait d'acc\u00e9l\u00e9rer le fonctionnement de mypy. Il \u00e9tait clair que notre travail pour acc\u00e9l\u00e9rer mypy et le mettre en \u0153uvre dans les projets de l'entreprise \u00e9tait encore loin d'\u00eatre termin\u00e9. Conscients de l'importance de ces deux t\u00e2ches, nous nous sommes attel\u00e9s \u00e0 leur r\u00e9solution.<\/p>\n<h2>Plus de performance !<\/h2>\n<p>\nLes v\u00e9rifications incr\u00e9mentielles ont acc\u00e9l\u00e9r\u00e9 mypy, mais cet outil n'\u00e9tait toujours pas assez rapide. De nombreuses v\u00e9rifications incr\u00e9mentielles prenaient environ une minute. La raison en \u00e9tait des importations cycliques. Cela ne surprendra probablement pas ceux qui ont d\u00e9j\u00e0 travaill\u00e9 avec de grandes bases de code \u00e9crites en Python. Nous avions des ensembles de centaines de modules, chacun d'eux important indirectement tous les autres. Si un fichier dans la boucle d'importations \u00e9tait modifi\u00e9, mypy devait traiter tous les fichiers inclus dans cette boucle, et souvent, aussi tous les modules important des modules de cette boucle. L'une de ces boucles \u00e9tait le tristement c\u00e9l\u00e8bre \u00ab n\u0153ud de d\u00e9pendances \u00bb, qui a caus\u00e9 de nombreux d\u00e9sagr\u00e9ments chez Dropbox. Un jour, cette structure contenait plusieurs centaines de modules, import\u00e9s, directement ou indirectement, par de nombreux tests, et elle \u00e9tait \u00e9galement utilis\u00e9e dans le code de production.<\/p>\n<p>Nous avons envisag\u00e9 de \u00ab d\u00e9m\u00ealer \u00bb les d\u00e9pendances cycliques, mais nous n'avions pas les ressources n\u00e9cessaires pour cela. Il y avait trop de code avec lequel nous n'\u00e9tions pas familiers. Finalement, nous avons opt\u00e9 pour une approche alternative. Nous avons d\u00e9cid\u00e9 de faire en sorte que mypy fonctionne rapidement m\u00eame en pr\u00e9sence de \u00ab n\u0153uds de d\u00e9pendances \u00bb. Nous avons atteint cet objectif gr\u00e2ce \u00e0 un d\u00e9mon mypy. Un d\u00e9mon est un processus serveur qui met en \u0153uvre deux fonctionnalit\u00e9s int\u00e9ressantes. Premi\u00e8rement, il conserve en m\u00e9moire des informations sur toute la base de code. Cela signifie que chaque fois que mypy est lanc\u00e9, il n'est pas n\u00e9cessaire de charger les donn\u00e9es mises en cache concernant des milliers de d\u00e9pendances import\u00e9es. Deuxi\u00e8mement, il analyse minutieusement, au niveau des unit\u00e9s structurelles, les d\u00e9pendances entre les fonctions et d'autres entit\u00e9s. Par exemple, si une fonction <code>foo<\/code> appelle la fonction <code>bar<\/code>, alors il existe une d\u00e9pendance <code>foo<\/code> \u00e0 partir de <code>bar<\/code>. Lorsque le fichier est modifi\u00e9, le d\u00e9mon traite d'abord, de mani\u00e8re isol\u00e9e, uniquement le fichier modifi\u00e9. Ensuite, il examine les modifications de ce fichier, visibles de l'ext\u00e9rieur, telles que les signatures de fonctions modifi\u00e9es. Le d\u00e9mon utilise des informations d\u00e9taill\u00e9es sur les imports uniquement pour rev\u00e9rifier les fonctions qui utilisent r\u00e9ellement la fonction modifi\u00e9e. En g\u00e9n\u00e9ral, avec cette approche, il y a tr\u00e8s peu de fonctions \u00e0 v\u00e9rifier.<\/p>\n<p>La mise en \u0153uvre de tout cela n'a pas \u00e9t\u00e9 une t\u00e2che facile, car la version initiale de mypy \u00e9tait fortement ax\u00e9e sur le traitement d'un seul fichier \u00e0 la fois. Nous avons d\u00fb faire face \u00e0 de nombreuses situations limites, dont l'apparition n\u00e9cessitait des v\u00e9rifications r\u00e9p\u00e9t\u00e9es lorsqu'une modification \u00e9tait apport\u00e9e au code. Par exemple, cela se produit lorsque l'on assigne une nouvelle classe de base \u00e0 une classe. Une fois que nous avons r\u00e9alis\u00e9 ce que nous voulions, nous avons pu r\u00e9duire le temps d'ex\u00e9cution de la plupart des v\u00e9rifications incr\u00e9mentielles \u00e0 quelques secondes. Cela nous semblait \u00eatre une grande victoire.<\/p>\n<h2>Encore plus de performances !<\/h2>\n<p>\nAvec la mise en cache distante dont j'ai parl\u00e9 pr\u00e9c\u00e9demment, le d\u00e9mon mypy a pratiquement r\u00e9solu les probl\u00e8mes rencontr\u00e9s lorsque le programmeur lance fr\u00e9quemment une v\u00e9rification de type en effectuant des modifications dans un nombre limit\u00e9 de fichiers. Cependant, la performance du syst\u00e8me dans le sc\u00e9nario le moins favorable demeurait encore loin d'\u00eatre optimale. Un d\u00e9marrage propre de mypy pouvait prendre plus de 15 minutes. C'\u00e9tait bien plus que ce que nous aurions accept\u00e9. Chaque semaine, la situation empirait, car les programmeurs continuaient d'\u00e9crire du nouveau code et d'ajouter des annotations au code existant. Nos utilisateurs aspiraient toujours \u00e0 une plus grande performance, et nous \u00e9tions heureux de pouvoir satisfaire cette demande.<\/p>\n<p>Nous avons d\u00e9cid\u00e9 de revenir \u00e0 l'une des premi\u00e8res id\u00e9es concernant mypy. Plus pr\u00e9cis\u00e9ment, \u00e0 la conversion du code Python en code C. Les exp\u00e9riences avec Cython (qui est un syst\u00e8me permettant de traduire du code \u00e9crit en Python en code C) ne nous ont pas apport\u00e9 d'acc\u00e9l\u00e9ration visible, c'est pourquoi nous avons d\u00e9cid\u00e9 de revitaliser l'id\u00e9e d'\u00e9crire notre propre compilateur. Comme la base de code de mypy (\u00e9crite en Python) contenait d\u00e9j\u00e0 toutes les annotations de types n\u00e9cessaires, il nous a sembl\u00e9 pertinent d'essayer d'utiliser ces annotations pour acc\u00e9l\u00e9rer le fonctionnement du syst\u00e8me. J'ai rapidement cr\u00e9\u00e9 un prototype pour tester cette id\u00e9e. Il a montr\u00e9, sur diff\u00e9rents micro-benchmarks, une augmentation de performance de plus de 10 fois. Notre id\u00e9e \u00e9tait de compiler des modules Python en modules C \u00e0 l'aide de Cython, et de transformer les annotations de types en v\u00e9rifications de types effectu\u00e9es lors de l'ex\u00e9cution du programme (en g\u00e9n\u00e9ral, les annotations de types sont ignor\u00e9es pendant l'ex\u00e9cution des programmes et ne sont utilis\u00e9es que par les syst\u00e8mes de v\u00e9rification de types). En fait, nous pr\u00e9voyions de traduire l'impl\u00e9mentation de mypy de Python vers un langage qui serait statiquement typ\u00e9, qui semblerait (et fonctionnerait dans sa majeure partie) exactement comme Python. (Cette forme de migration interlangage est devenue en quelque sorte une tradition du projet mypy. L'impl\u00e9mentation initiale de mypy a \u00e9t\u00e9 \u00e9crite en Alore, puis un hybride syntaxique de Java et de Python a \u00e9t\u00e9 utilis\u00e9).<\/p>\n<p>L'orientation vers l'API des extensions CPython a \u00e9t\u00e9 la cl\u00e9 pour ne pas perdre de capacit\u00e9s en mati\u00e8re de gestion de projet. Nous n'avions pas besoin de mettre en \u0153uvre une machine virtuelle ni les biblioth\u00e8ques n\u00e9cessaires \u00e0 mypy. De plus, nous aurions toujours acc\u00e8s \u00e0 tout l'\u00e9cosyst\u00e8me Python, tous les outils (comme pytest) seraient disponibles. Cela signifiait que nous pourrions continuer \u00e0 utiliser le code Python interpr\u00e9t\u00e9 au cours du d\u00e9veloppement, ce qui nous permettrait de travailler avec un processus tr\u00e8s rapide de modification du code et de test, plut\u00f4t que d'attendre la compilation du code. Cela semblait fonctionner comme si nous r\u00e9ussissions \u00e0, disons, rester sur deux chaises, et cela nous plaisait.<\/p>\n<p>Le compilateur que nous avons nomm\u00e9 mypyc (car il utilise mypy pour l'analyse des types en tant que frontend) s'est r\u00e9v\u00e9l\u00e9 \u00eatre un projet tr\u00e8s r\u00e9ussi. En g\u00e9n\u00e9ral, nous avons atteint une acc\u00e9l\u00e9ration d'environ quatre fois pour les lancements fr\u00e9quents de mypy sans utiliser de mise en cache. Le d\u00e9veloppement du noyau du projet mypyc a dur\u00e9 environ 4 mois civils pour une petite \u00e9quipe compos\u00e9e de Michael Sullivan, Ivan Levkiysky, Hugh Han et moi-m\u00eame. Ce volume de travail \u00e9tait beaucoup moins important que celui qui aurait \u00e9t\u00e9 n\u00e9cessaire pour r\u00e9\u00e9crire mypy, par exemple, en C++ ou en Go. En plus, les changements que nous avons d\u00fb apporter au projet \u00e9taient bien moindres que ceux requis pour une r\u00e9\u00e9criture dans un autre langage. Nous esp\u00e9rions \u00e9galement pouvoir amener mypyc \u00e0 un niveau o\u00f9 d'autres programmeurs de Dropbox pourraient l'utiliser pour compiler et acc\u00e9l\u00e9rer leur code.<\/p>\n<p>Pour atteindre un tel niveau de performance, nous avons d\u00fb appliquer certaines solutions d'ing\u00e9nierie int\u00e9ressantes. Par exemple, le compilateur peut acc\u00e9l\u00e9rer de nombreuses op\u00e9rations gr\u00e2ce \u00e0 l'utilisation de constructions bas niveau rapides en C. Ainsi, l'appel d'une fonction compil\u00e9e est traduit en appel d'une fonction C. Et cet appel s'ex\u00e9cute beaucoup plus rapidement qu'un appel de fonction interpr\u00e9t\u00e9e. Certaines op\u00e9rations, comme la recherche dans les dictionnaires, se basaient encore sur l'utilisation des appels C-API classiques depuis CPython, qui, apr\u00e8s compilation, n'\u00e9taient qu'un peu plus rapides. Nous avons pu \u00e9liminer la surcharge suppl\u00e9mentaire sur le syst\u00e8me cr\u00e9\u00e9e par l'interpr\u00e9tation, mais cela n'a donn\u00e9 qu'un l\u00e9ger gain de performance dans ce cas.<\/p>\n<p>Pour identifier les op\u00e9rations \u00ab lentes \u00bb les plus courantes, nous avons effectu\u00e9 un profilage du code. Arm\u00e9s des donn\u00e9es recueillies, nous avons tent\u00e9 soit d'ajuster mypyc pour qu'il g\u00e9n\u00e8re un code C plus rapide pour de telles op\u00e9rations, soit de r\u00e9\u00e9crire le code Python correspondant en utilisant des op\u00e9rations plus rapides (et parfois nous n'avions tout simplement pas de solution simple pour tel ou tel probl\u00e8me). La r\u00e9\u00e9criture du code Python s'est souvent r\u00e9v\u00e9l\u00e9e \u00eatre une solution plus facile que la mise en \u0153uvre d'une ex\u00e9cution automatique de la m\u00eame transformation dans le compilateur. \u00c0 long terme, nous souhaitions automatiser de nombreuses transformations, mais \u00e0 ce moment-l\u00e0, notre objectif \u00e9tait d'acc\u00e9l\u00e9rer mypy avec le minimum d'efforts. Et \u00e0 l'approche de cet objectif, nous avons coup\u00e9 quelques coins.<\/p>\n<p>\u00c0 suivre...<\/p>\n<p><b>Chers lecteurs !<\/b> Quelles impressions le projet mypy a-t-il suscit\u00e9es chez vous lorsque vous avez pris connaissance de son existence ?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Chemin vers la v\u00e9rification des types de 4 millions de lignes de code Python. Partie 2\" src=\"\/wp-content\/uploads\/2019\/09\/e3c1dd40228b56d0196fd65e4df22a59.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Chemin vers la v\u00e9rification des types de 4 millions de lignes de code Python. Partie 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u2192 \u0427\u0438\u0442\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0438\u043f\u043e\u0432 (PEP 484) \u041c\u044b \u043f\u0440\u043e\u0432\u0435\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b \u0441 mypy \u0432 Dropbox \u0432\u043e \u0432\u0440\u0435\u043c\u044f Hack Week 2014. Hack Week \u2014 \u044d\u0442\u043e \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u0435, \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c\u043e\u0435 Dropbox \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0439 \u043d\u0435\u0434\u0435\u043b\u0438. \u0412 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-38479","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Le chemin vers la v\u00e9rification des types de 4 millions de lignes de code Python. Partie 2 | ProHoster","description":"Aujourd'hui, nous publions la deuxi\u00e8me partie de la traduction d'un article sur la fa\u00e7on dont Dropbox a organis\u00e9 le contr\u00f4le des types de plusieurs millions de lignes de code Python.","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster","og:description":"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.","og:url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:23:56+00:00","article:modified_time":"2019-10-31T19:23:56+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"38479","title":null,"description":null,"keywords":null,"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 22:13:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:08:32","updated":"2026-08-11 12:50:31","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}