{"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\/pt\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Caminho para a verifica\u00e7\u00e3o de tipos em 4 milh\u00f5es de linhas de c\u00f3digo Python. Parte 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hoje publicamos a segunda parte da tradu\u00e7\u00e3o do material sobre como o Dropbox organizou o controle de tipos em v\u00e1rios milh\u00f5es de linhas de c\u00f3digo Python.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Caminho para a verifica\u00e7\u00e3o de tipos em 4 milh\u00f5es de linhas de c\u00f3digo Python. Parte 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\/\">Leia a primeira parte<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Suporte oficial a tipos (PEP 484)<\/h2>\n<p>\nRealizamos os primeiros experimentos s\u00e9rios com o mypy no Dropbox durante a Hack Week de 2014. A Hack Week \u00e9 um evento realizado pelo Dropbox durante uma semana, onde os funcion\u00e1rios podem trabalhar em qualquer projeto que desejarem! Alguns dos mais famosos projetos tecnol\u00f3gicos do Dropbox come\u00e7aram em eventos como este. Como resultado desse experimento, conclu\u00edmos que o mypy parece promissor, embora o projeto ainda n\u00e3o estivesse pronto para uso generalizado.<\/p>\n<p>Naquela \u00e9poca, havia uma ideia no ar sobre a padroniza\u00e7\u00e3o dos sistemas de sugest\u00f5es de tipos em Python. Como mencionei, a partir do Python 3.0, era poss\u00edvel utilizar anota\u00e7\u00f5es de tipos para fun\u00e7\u00f5es, mas eram apenas express\u00f5es arbitr\u00e1rias, sem uma sintaxe ou sem\u00e2ntica definidas. Durante a execu\u00e7\u00e3o do programa, essas anota\u00e7\u00f5es eram, na maioria das vezes, simplesmente ignoradas. Ap\u00f3s a Hack Week, come\u00e7amos a trabalhar na padroniza\u00e7\u00e3o da sem\u00e2ntica. Esse trabalho levou \u00e0 cria\u00e7\u00e3o <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (este documento foi elaborado em conjunto por Guido van Rossum, \u0141ukasz Langa e eu).<\/p>\n<p>Nossos motivos podem ser vistos de duas maneiras. Em primeiro lugar, esper\u00e1vamos que toda a ecossistema Python pudesse adotar uma abordagem comum para a utiliza\u00e7\u00e3o de sugest\u00f5es de tipos (type hints \u2014 termo usado em Python como equivalente a 'anota\u00e7\u00f5es de tipos'). Isso, considerando os riscos potenciais, seria melhor do que o uso de muitas abordagens mutuamente incompat\u00edveis. Em segundo lugar, quer\u00edamos discutir abertamente os mecanismos de anota\u00e7\u00e3o de tipos com muitos membros da comunidade Python. Em parte, esse desejo surgiu do fato de que n\u00e3o quer\u00edamos parecer 'hereges' em rela\u00e7\u00e3o \u00e0s ideias fundamentais da linguagem aos olhos da vasta maioria dos programadores Python. Essa \u00e9 uma linguagem de tipagem din\u00e2mica, conhecida pela 'tipagem de pato'. No in\u00edcio, a comunidade n\u00e3o podia deixar de ter uma certa desconfian\u00e7a em rela\u00e7\u00e3o \u00e0 ideia de tipagem est\u00e1tica. Mas essa atitude acabou se amenizando \u2014 depois que ficou claro que a tipagem est\u00e1tica n\u00e3o seria obrigat\u00f3ria (e depois que as pessoas perceberam que ela realmente \u00e9 \u00fatil).<\/p>\n<p>A sintaxe de anota\u00e7\u00f5es de tipos adotada acabou sendo muito semelhante \u00e0 que era suportada pelo mypy na \u00e9poca. O documento PEP 484 foi publicado junto com o Python 3.5 em 2015. O Python deixou de ser uma linguagem que suportava apenas tipagem din\u00e2mica. Gosto de pensar neste evento como um marco significativo na hist\u00f3ria do Python.<\/p>\n<h2>In\u00edcio da migra\u00e7\u00e3o<\/h2>\n<p>\nNo final de 2015, uma equipe de tr\u00eas pessoas foi formada no Dropbox para trabalhar no mypy. Ela inclu\u00eda Guido van Rossum, Greg Price e David Fisher. A partir deste momento, a situa\u00e7\u00e3o come\u00e7ou a se desenvolver rapidamente. O primeiro obst\u00e1culo no caminho do crescimento do mypy foi o desempenho. Como mencionei anteriormente, no in\u00edcio do desenvolvimento do projeto, considerei a possibilidade de reescrever a implementa\u00e7\u00e3o do mypy na linguagem C, mas essa ideia foi descartada por enquanto. Fomos obrigados a usar o interpretador CPython, que n\u00e3o tem a velocidade suficiente para ferramentas como o mypy. (O projeto PyPy, uma implementa\u00e7\u00e3o alternativa do Python com compilador JIT, tamb\u00e9m n\u00e3o nos ajudou.)<\/p>\n<p>Felizmente, algumas melhorias algor\u00edtmicas vieram em nosso socorro. O primeiro \"acelerador\" poderoso foi a implementa\u00e7\u00e3o da verifica\u00e7\u00e3o incremental. A ideia dessa melhoria era simples: se todas as depend\u00eancias de um m\u00f3dulo desde a \u00faltima execu\u00e7\u00e3o do mypy n\u00e3o mudaram, podemos usar, durante o processamento das depend\u00eancias, dados que foram armazenados em cache na sess\u00e3o anterior. Precis\u00e1vamos apenas realizar a verifica\u00e7\u00e3o de tipos nos arquivos alterados e nos arquivos que dependiam deles. O mypy foi um pouco al\u00e9m: se a interface externa do m\u00f3dulo n\u00e3o mudasse, o mypy considerava que outros m\u00f3dulos que importavam esse m\u00f3dulo n\u00e3o precisavam ser verificados novamente.<\/p>\n<p>A verifica\u00e7\u00e3o incremental ajudou muito na anota\u00e7\u00e3o de grandes volumes de c\u00f3digo existente. O fato \u00e9 que esse processo normalmente envolve v\u00e1rias execu\u00e7\u00f5es iterativas do mypy, \u00e0 medida que as anota\u00e7\u00f5es s\u00e3o gradualmente adicionadas ao c\u00f3digo e melhoradas. A primeira execu\u00e7\u00e3o do mypy ainda era muito lenta, pois exigia a verifica\u00e7\u00e3o de muitas depend\u00eancias. Para melhorar a situa\u00e7\u00e3o, implementamos um mecanismo de cache remoto. Se o mypy detecta que o cache local provavelmente est\u00e1 desatualizado, ele carrega a vers\u00e3o atual do cache da base de c\u00f3digo de um reposit\u00f3rio centralizado. Em seguida, ele executa, usando essa vers\u00e3o, uma verifica\u00e7\u00e3o incremental. Isso nos deu um passo significativo em dire\u00e7\u00e3o ao aumento do desempenho do mypy.<\/p>\n<p>Foi um per\u00edodo de ado\u00e7\u00e3o r\u00e1pida e natural do sistema de verifica\u00e7\u00e3o de tipos no Dropbox. At\u00e9 o final de 2016, j\u00e1 t\u00ednhamos cerca de 420.000 linhas de c\u00f3digo Python com anota\u00e7\u00f5es de tipo. Muitos usu\u00e1rios acolheram a verifica\u00e7\u00e3o de tipos com entusiasmo. No Dropbox, o mypy era utilizado por cada vez mais equipes de desenvolvedores.<\/p>\n<p>Naquele momento, tudo parecia bem, mas ainda havia muito a ser feito. Come\u00e7amos a realizar pesquisas internas peri\u00f3dicas com os usu\u00e1rios para identificar os pontos problem\u00e1ticos do projeto e entender quais quest\u00f5es precisavam ser resolvidas primeiro (essa pr\u00e1tica ainda \u00e9 usada na empresa hoje em dia). As duas quest\u00f5es mais importantes, como ficou claro, eram: primeiro, precis\u00e1vamos de uma cobertura de c\u00f3digo maior com tipos; segundo, era necess\u00e1rio que o mypy funcionasse mais r\u00e1pido. Era evidente que nosso trabalho para acelerar o mypy e integr\u00e1-lo nos projetos da empresa ainda estava longe de ser conclu\u00eddo. Com plena consci\u00eancia da import\u00e2ncia dessas duas tarefas, come\u00e7amos a solucion\u00e1-las.<\/p>\n<h2>Mais desempenho!<\/h2>\n<p>\nVerifica\u00e7\u00f5es incrementais aceleraram o mypy, mas essa ferramenta ainda n\u00e3o era r\u00e1pida o suficiente. Muitas verifica\u00e7\u00f5es incrementais levavam cerca de um minuto. A causa disso eram as importa\u00e7\u00f5es c\u00edclicas. Isso provavelmente n\u00e3o surpreender\u00e1 aqueles que j\u00e1 trabalharam com grandes bases de c\u00f3digo escritas em Python. Tivemos conjuntos de centenas de m\u00f3dulos, cada um dos quais importava indiretamente todos os outros. Se algum arquivo no ciclo de importa\u00e7\u00f5es fosse modificado, o mypy tinha que processar todos os arquivos dentro desse ciclo, e muitas vezes, tamb\u00e9m quaisquer m\u00f3dulos que importavam m\u00f3dulos desse ciclo. Um desses ciclos era o infame \u201cnovelo de depend\u00eancias\u201d, que causou muitos problemas no Dropbox. Uma vez, essa estrutura continha v\u00e1rias centenas de m\u00f3dulos, que eram importados, direta ou indiretamente, por muitos testes, e era usada tamb\u00e9m no c\u00f3digo de produ\u00e7\u00e3o.<\/p>\n<p>Consideramos a possibilidade de \"desvincular\" as depend\u00eancias c\u00edclicas, mas n\u00e3o disp\u00fanhamos de recursos para isso. Havia c\u00f3digo demais com o qual n\u00e3o est\u00e1vamos familiarizados. No final das contas, optamos por uma abordagem alternativa. Decidimos fazer com que o mypy funcionasse rapidamente mesmo na presen\u00e7a de \"novelos de depend\u00eancias\". Alcan\u00e7amos esse objetivo por meio de um daemon mypy. O daemon \u00e9 um processo de servidor que implementa duas funcionalidades interessantes. Primeiro \u2014 ele mant\u00e9m na mem\u00f3ria informa\u00e7\u00f5es sobre toda a base de c\u00f3digo. Isso significa que a cada execu\u00e7\u00e3o do mypy, n\u00e3o \u00e9 necess\u00e1rio carregar dados armazenados em cache referentes a milhares de depend\u00eancias importadas. Em segundo lugar \u2014 ele analisa cuidadosamente, em n\u00edvel de pequenas unidades estruturais, as depend\u00eancias entre fun\u00e7\u00f5es e outras entidades. Por exemplo, se uma fun\u00e7\u00e3o <code>foo<\/code> chama a fun\u00e7\u00e3o <code>bar<\/code>, ent\u00e3o existe uma depend\u00eancia <code>foo<\/code> de <code>bar<\/code>. Quando um arquivo muda, o daemon primeiro processa, de forma isolada, apenas o arquivo modificado. Em seguida, ele observa as altera\u00e7\u00f5es desse arquivo vis\u00edveis externamente, como as assinaturas de fun\u00e7\u00f5es alteradas. O daemon utiliza informa\u00e7\u00f5es detalhadas sobre as importa\u00e7\u00f5es apenas para reavaliar as fun\u00e7\u00f5es que realmente utilizam a fun\u00e7\u00e3o modificada. Normalmente, com essa abordagem, \u00e9 necess\u00e1rio verificar apenas um pequeno n\u00famero de fun\u00e7\u00f5es.<\/p>\n<p>A implementa\u00e7\u00e3o de tudo isso n\u00e3o foi uma tarefa f\u00e1cil, pois a implementa\u00e7\u00e3o original do mypy estava muito focada em processar um arquivo por vez. Tivemos que enfrentar uma s\u00e9rie de situa\u00e7\u00f5es limites, cuja ocorr\u00eancia exigia verifica\u00e7\u00f5es repetidas sempre que algo no c\u00f3digo era alterado. Por exemplo, isso acontece quando se atribui uma nova classe base a uma classe. Depois que conseguimos fazer o que desej\u00e1vamos, conseguimos reduzir o tempo de execu\u00e7\u00e3o da maioria das verifica\u00e7\u00f5es incrementais para apenas alguns segundos. Para n\u00f3s, isso parecia uma grande vit\u00f3ria.<\/p>\n<h2>Ainda mais desempenho!<\/h2>\n<p>\nJunto com o cache remoto que mencionei anteriormente, o daemon mypy praticamente resolveu os problemas que surgiam quando o programador frequentemente executava a verifica\u00e7\u00e3o de tipos, fazendo mudan\u00e7as em um pequeno n\u00famero de arquivos. No entanto, o desempenho do sistema, na situa\u00e7\u00e3o menos favor\u00e1vel de uso, ainda estava longe do ideal. Uma execu\u00e7\u00e3o limpa do mypy poderia levar mais de 15 minutos. E isso era muito mais do que t\u00ednhamos como aceit\u00e1vel. A cada semana, a situa\u00e7\u00e3o piorava, \u00e0 medida que os programadores continuavam a escrever novo c\u00f3digo e adicionar anota\u00e7\u00f5es ao c\u00f3digo existente. Nossos usu\u00e1rios ainda ansiavam por mais desempenho, e est\u00e1vamos felizes em atend\u00ea-los.<\/p>\n<p>Decidimos voltar a uma das ideias iniciais sobre o mypy. Ou seja, a transforma\u00e7\u00e3o de c\u00f3digo Python em c\u00f3digo C. As experi\u00eancias com o Cython (um sistema que permite transcrever c\u00f3digo escrito em Python para c\u00f3digo C) n\u00e3o nos deram um aumento vis\u00edvel de desempenho, ent\u00e3o decidimos ressuscitar a ideia de escrever nosso pr\u00f3prio compilador. Como a base de c\u00f3digo do mypy (escrita em Python) j\u00e1 continha todas as anota\u00e7\u00f5es de tipo necess\u00e1rias, parecia uma tentativa v\u00e1lida usar essas anota\u00e7\u00f5es para acelerar o funcionamento do sistema. Rapidamente criei um prot\u00f3tipo para testar essa ideia. Ele mostrou um aumento de desempenho em mais de 10 vezes em v\u00e1rios micro-benchmarks. Nossa ideia era compilar m\u00f3dulos Python em m\u00f3dulos C usando Cython, e transformar anota\u00e7\u00f5es de tipo em verifica\u00e7\u00f5es de tipo realizadas em tempo de execu\u00e7\u00e3o (normalmente, as anota\u00e7\u00f5es de tipo s\u00e3o ignoradas durante a execu\u00e7\u00e3o dos programas e s\u00e3o usadas apenas por sistemas de verifica\u00e7\u00e3o de tipos). Na verdade, planej\u00e1vamos transferir a implementa\u00e7\u00e3o do mypy de Python para uma linguagem que fosse estaticamente tipada, que se parecesse (e, em grande parte, funcionasse) exatamente como o Python. (Esse tipo de migra\u00e7\u00e3o entre linguagens tornou-se uma esp\u00e9cie de tradi\u00e7\u00e3o no projeto mypy. A implementa\u00e7\u00e3o original do mypy foi escrita em Alore, e depois houve um h\u00edbrido sint\u00e1tico de Java e Python).<\/p>\n<p>A orienta\u00e7\u00e3o para a API de extens\u00f5es do CPython foi fundamental para n\u00e3o perdermos a capacidade de gerenciar o projeto. N\u00e3o precis\u00e1vamos implementar uma m\u00e1quina virtual ou qualquer biblioteca da qual o mypy precisasse. Al\u00e9m disso, ainda ter\u00edamos acesso a todo o ecossistema Python, e todas as ferramentas (como pytest) estariam dispon\u00edveis. Isso significava que poder\u00edamos continuar usando c\u00f3digo Python interpret\u00e1vel durante o desenvolvimento, o que nos permitiria trabalhar com um esquema muito \u00e1gil de edi\u00e7\u00e3o de c\u00f3digo e testes, em vez de esperar a compila\u00e7\u00e3o do c\u00f3digo. Parecia que est\u00e1vamos conseguindo, por assim dizer, nos equilibrar em dois bancos, e est\u00e1vamos gostando disso.<\/p>\n<p>O compilador que chamamos de mypyc (j\u00e1 que ele utiliza mypy para an\u00e1lise de tipos como frontend) se revelou um projeto bastante bem-sucedido. No geral, conseguimos uma acelera\u00e7\u00e3o de aproximadamente 4 vezes em execu\u00e7\u00f5es frequentes do mypy sem o uso de cache. O desenvolvimento do n\u00facleo do projeto mypyc levou cerca de 4 meses, com uma pequena equipe composta por Michael Sullivan, Ivan Levkivsky, Hugh Han e eu. Esse volume de trabalho foi muito menor do que o necess\u00e1rio para reescrever o mypy, por exemplo, em C++ ou em Go. Al\u00e9m disso, as mudan\u00e7as que tivemos que fazer no projeto foram muito menores do que seriam necess\u00e1rias se o reescrevessemos em outra linguagem. Esper\u00e1vamos tamb\u00e9m conseguir levar o mypyc a um n\u00edvel onde outros desenvolvedores da Dropbox pudessem utiliz\u00e1-lo para compilar e acelerar seu c\u00f3digo.<\/p>\n<p>Para alcan\u00e7ar esse n\u00edvel de desempenho, tivemos que aplicar algumas solu\u00e7\u00f5es de engenharia interessantes. Assim, o compilador pode acelerar a execu\u00e7\u00e3o de muitas opera\u00e7\u00f5es ao utilizar constru\u00e7\u00f5es de baixo n\u00edvel em C. Por exemplo, a chamada de uma fun\u00e7\u00e3o compilada \u00e9 traduzida em uma chamada de fun\u00e7\u00e3o C. E uma chamada dessa \u00e9 executada muito mais r\u00e1pido do que uma fun\u00e7\u00e3o interpretada. Algumas opera\u00e7\u00f5es, como a busca em dicion\u00e1rios, ainda dependiam de chamadas convencionais da API C do CPython, que, ap\u00f3s a compila\u00e7\u00e3o, se mostraram apenas um pouco mais r\u00e1pidas. Conseguimos eliminar a sobrecarga adicional no sistema causada pela interpreta\u00e7\u00e3o, mas isso, neste caso, resultou em um ganho de desempenho apenas modesto.<\/p>\n<p>Para identificar as opera\u00e7\u00f5es 'lentas' mais comuns, realizamos um perfilamento de c\u00f3digo. Armados com os dados obtidos, tentamos ou ajustar o mypyc para que ele gerasse um c\u00f3digo C mais r\u00e1pido para essas opera\u00e7\u00f5es, ou reescrever o c\u00f3digo Python correspondente usando opera\u00e7\u00f5es mais r\u00e1pidas (e, \u00e0s vezes, simplesmente n\u00e3o t\u00ednhamos uma solu\u00e7\u00e3o simples para determinados problemas). Reescrever o c\u00f3digo Python frequentemente se mostrava uma solu\u00e7\u00e3o mais f\u00e1cil do que implementar a mesma transforma\u00e7\u00e3o autom\u00e1tica no compilador. A longo prazo, quer\u00edamos automatizar muitas dessas transforma\u00e7\u00f5es, mas naquele momento est\u00e1vamos focados em acelerar o mypy com o m\u00ednimo de esfor\u00e7o. E, ao nos movermos em dire\u00e7\u00e3o a esse objetivo, cortamos algumas arestas.<\/p>\n<p>Continua\u2026<\/p>\n<p><b>Prezados leitores!<\/b> Quais foram suas impress\u00f5es sobre o projeto mypy quando voc\u00ea soube de sua exist\u00eancia?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Caminho para a verifica\u00e7\u00e3o de tipos em 4 milh\u00f5es de linhas de c\u00f3digo Python. Parte 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=\"Caminho para a verifica\u00e7\u00e3o de tipos em 4 milh\u00f5es de linhas de c\u00f3digo Python. Parte 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Fonte: <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.3 - 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\/pt\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\/pt\/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\udd47Caminho para a verifica\u00e7\u00e3o de tipos de 4 milh\u00f5es de linhas de c\u00f3digo Python. Parte 2 | ProHoster","description":"Hoje publicamos a segunda parte da tradu\u00e7\u00e3o do material sobre como o Dropbox organizou o controle de tipos em v\u00e1rios milh\u00f5es de linhas de c\u00f3digo Python.","canonical_url":"https:\/\/prohoster.info\/pt\/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":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}