A maneira de digitar a verificação de 4 milhões de linhas de código Python. Parte 3

Apresentamos a sua atenção a terceira parte da tradução do material no caminho que o Dropbox seguiu, introduzindo um sistema para verificar o tipo de código Python.



→ Peças anteriores: primeira e segunda

Atingindo 4 milhões de linhas de código digitado


Outra tarefa importante (esse foi o segundo problema mais popular que preocupou aqueles que participaram de pesquisas internas) foi aumentar a quantidade de código no Dropbox coberto por verificações de tipo. Tentamos várias abordagens para resolver esse problema - desde o crescimento natural do volume da base de códigos digitados até o foco dos esforços da equipe mypy na inferência estática e dinâmica de tipos automatizados. Como resultado, parecia que não havia uma estratégia vencedora simples, mas conseguimos um rápido crescimento no volume de código anotado combinando várias abordagens.

Como resultado, em nosso maior repositório Python (com código de back-end), o número de linhas de código anotado atingiu quase 4 milhões. O trabalho de digitação estática do código foi realizado em cerca de três anos. O Mypy agora suporta vários tipos de relatórios de cobertura de código que facilitam o monitoramento do progresso da digitação. Em particular, podemos gerar relatórios sobre código com incertezas em tipos, como, por exemplo, o uso explícito do tipo Any nas anotações que não podem ser verificadas ou a importação de bibliotecas de terceiros nas quais não há anotações de tipo. Como parte de um projeto para aumentar a precisão da verificação de tipo no Dropbox, contribuímos para melhorar as definições de tipo (os chamados arquivos stub) para algumas bibliotecas populares de código aberto no repositório Python centralizado e datilografado.

Implementamos (e padronizamos em PEPs subseqüentes) novos recursos do sistema de tipos, que nos permitem usar tipos mais precisos para alguns padrões específicos de Python. Um exemplo notável disso é TypeDict , que fornece tipos para dicionários do tipo JSON que possuem um conjunto fixo de chaves de cadeia, cada uma com um valor de seu próprio tipo. Continuaremos a expandir o sistema de tipos. Provavelmente, nosso próximo passo será melhorar o suporte à capacidade do Python de trabalhar com números.


Número de linhas de código anotado: servidor


Número de linhas de código anotado: cliente


O número total de linhas do código anotado

Aqui está uma visão geral dos principais recursos das ações que realizamos para aumentar o volume do código anotado no Dropbox:

O rigor da anotação. Aumentamos gradualmente os requisitos para o rigor da anotação do novo código. Começamos com dicas de linter que sugeriam adicionar anotações a arquivos que já possuem algumas anotações. Agora exigimos anotações de tipo nos novos arquivos Python e na maioria dos arquivos existentes.

Digitando relatórios. Enviamos relatórios semanais às equipes sobre o nível de digitação do código e damos dicas sobre o que deve ser anotado em primeiro lugar.

Popularizando mypy. Falamos sobre mypy em vários eventos e nos comunicamos com as equipes para ajudá-las a começar a usar anotações de tipo.

Pesquisas. Realizamos pesquisas periódicas com os usuários para identificar os principais problemas. Estamos prontos para ir longe o suficiente para resolver esses problemas (até criar uma nova linguagem para acelerar o mypy!).

Performance. Melhoramos bastante o desempenho do mypy através do uso do daemon e do mypyc. Isso foi feito para amenizar os inconvenientes que surgiram durante o processo de anotação e para poder trabalhar com grandes quantidades de código.

Integração com editores. Criamos ferramentas para apoiar o lançamento do mypy em editores populares no Dropbox. Isso inclui PyCharm, Vim e VS Code. Isso simplificou bastante o processo de anotação do código e verificação de seu desempenho. Tais ações geralmente são típicas ao anotar código existente.

Análise estática Criamos uma ferramenta para gerar assinaturas de funções usando ferramentas de análise estática. Essa ferramenta pode funcionar apenas em situações relativamente simples, mas nos ajudou a aumentar a cobertura de tipos com pouco esforço.

Suporte para bibliotecas de terceiros. Muitos de nossos projetos usam o kit de ferramentas SQLAlchemy. Ele usa os recursos dinâmicos do Python, que os tipos PEP 484 não podem modelar diretamente. De acordo com o PEP 561, criamos o arquivo stub correspondente e escrevemos um plug-in para mypy ( código aberto ) que aprimora o suporte ao SQLAlchemy.

As dificuldades que encontramos


O caminho para 4 milhões de linhas de código digitado nem sempre foi fácil para nós. Dessa maneira, encontramos muitos buracos e cometemos alguns erros. Aqui estão alguns dos problemas que encontramos. Esperamos que a história sobre eles ajude outras pessoas a evitar esses problemas.

Arquivos ignorados. Começamos verificando apenas uma pequena quantidade de arquivos. Tudo o que não está incluído no número desses arquivos não foi verificado. Os arquivos foram adicionados à lista de verificação quando as primeiras anotações apareceram neles. Se algo foi importado de um módulo localizado fora do escopo da verificação, estaríamos falando sobre trabalhar com valores do tipo Any , que não foram verificados. Isso levou a uma perda significativa da precisão da digitação, especialmente nos estágios iniciais da migração. Até agora, essa abordagem funcionou surpreendentemente bem, embora fosse típico que adicionar arquivos à área de varredura revelasse problemas em outras partes da base de código. Na pior das hipóteses, quando duas áreas isoladas de código foram combinadas, nas quais, independentemente uma da outra, os tipos já foram verificados, verificou-se que os tipos dessas áreas eram incompatíveis entre si. Isso tornou necessário fazer muitas alterações nas anotações. Agora, olhando para trás, entendemos que devemos adicionar os módulos básicos da biblioteca à área de verificação do tipo mypy o mais cedo possível. Isso tornaria nosso trabalho muito mais previsível.

Anotando código antigo. Quando começamos, tínhamos cerca de 4 milhões de linhas de código Python existente. Ficou claro que anotar todo esse código não era uma tarefa fácil. Criamos uma ferramenta chamada PyAnnotate, que pode coletar informações de tipo durante a execução do teste e adicionar anotações de tipo ao código com base nas informações coletadas. No entanto, não notamos uma introdução particularmente ampla dessa ferramenta. As informações de digitação sobre os tipos eram lentas, e as anotações geradas automaticamente exigiam muitas edições manuais. Pensamos em iniciar automaticamente essa ferramenta toda vez que você verificar o código ou em coletar informações de tipo com base na análise de uma pequena quantidade de solicitações reais da rede, mas decidimos não fazê-lo, porque qualquer uma dessas abordagens é muito arriscada.

Como resultado, pode-se observar que a maior parte do código foi anotada manualmente por seus proprietários. Para direcionar esse processo na direção certa, estamos preparando relatórios sobre módulos e funções especialmente importantes que precisam ser anotados. Por exemplo, é importante fornecer anotações de tipo com o módulo de biblioteca usado em centenas de locais. Mas o serviço antigo, que está sendo substituído por um novo, a anotação não é mais tão importante. Também estamos experimentando o uso de análise estática para gerar anotações de tipo para código antigo.

Importações de loop. Anteriormente, falei sobre importações cíclicas ("emaranhado de dependências"), cuja existência complicou a aceleração do mypy. Além disso, tivemos que trabalhar duro para fornecer ao mypy suporte para todos os tipos de idiomas causados ​​por essas importações cíclicas. Recentemente, concluímos um grande projeto de redesenho do sistema que corrigia a maioria dos problemas de importação cíclica do mypy. Esses problemas, de fato, surgiram desde os primórdios do projeto, desde Alore, a linguagem educacional para a qual o mypy foi originalmente orientado. A sintaxe Alore facilita a solução dos problemas dos comandos de importação cíclica. O mypy moderno herdou algumas limitações de sua implementação inicial e engenhosa (que funcionou muito bem para a Alore). O Python dificulta o trabalho com importações circulares, principalmente devido à ambiguidade de expressões. Por exemplo, durante uma operação de atribuição, um alias de tipo pode realmente ser determinado. O Mypy nem sempre é capaz de detectar essas coisas até que a maior parte do ciclo de importação tenha sido processada. Alore não tinha tais ambiguidades. As decisões malsucedidas tomadas nos estágios iniciais do desenvolvimento do sistema podem apresentar uma surpresa desagradável para o programador depois de muitos anos.

Resumo: O caminho para 5 milhões de linhas de código e novos horizontes


O projeto mypy já percorreu um longo caminho - desde os primeiros protótipos até um sistema que controla os tipos de código de produção com um volume de 4 milhões de linhas. À medida que mypy progredia, as dicas de tipo eram padronizadas em Python. Um poderoso ecossistema evoluiu ao redor da digitação do código Python atualmente. Encontrou um local para suportar bibliotecas, contém ferramentas auxiliares para IDEs e editores, possui vários sistemas de controle de tipo, cada um com seus prós e contras.

Apesar do fato de que a verificação de tipo já é um dado adquirido no Dropbox, tenho certeza de que ainda vivemos no início da digitação do código Python. Penso que as tecnologias de verificação de tipo continuarão a evoluir e melhorar.

Se você não usou verificações de tipo em seu projeto Python em larga escala, deve saber que agora é um bom momento para iniciar a transição para a digitação estática. Conversei com aqueles que fizeram uma transição semelhante. Nenhum deles se arrependeu. O controle de tipo transforma o Python em uma linguagem muito melhor que o "Python normal" para o desenvolvimento de grandes projetos.

Caros leitores! Você usa controle de tipo em seus projetos Python?


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


All Articles