O caminho para a verificação de tipo 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 sobre o caminho que o Dropbox percorreu ao implementar um sistema de verificação de tipo para código Python.

O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3

→ Partes anteriores: primeiro и segundo

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

Outro grande desafio (e a segunda preocupação mais comum entre os pesquisados ​​internamente) foi aumentar a quantidade de código coberto pelas verificações de tipo no Dropbox. Tentamos várias abordagens para resolver esse problema, desde aumentar naturalmente o tamanho da base de código digitada até concentrar os esforços da equipe mypy na inferência automatizada de tipos estática e dinâmica. No final, parecia que não havia uma estratégia simples e vencedora, mas conseguimos alcançar um rápido crescimento no volume de código anotado combinando muitas abordagens.

Como resultado, nosso maior repositório Python (com código backend) possui quase 4 milhões de linhas de código anotado. O trabalho de digitação de código estático foi concluído em aproximadamente três anos. Mypy agora oferece suporte a 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ódigos com ambiguidades nos tipos, como, por exemplo, uso explícito de um tipo Any em anotações que não podem ser verificadas ou em coisas como importar bibliotecas de terceiros que não possuem anotações de tipo. Como parte de um projeto para melhorar 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 em um repositório Python centralizado. digitado.

Implementamos (e padronizamos em PEPs subsequentes) novos recursos do sistema de tipos que permitem tipos mais precisos para alguns padrões específicos do Python. Um exemplo notável disso é TypeDict, que fornece tipos para dicionários semelhantes a JSON que possuem um conjunto fixo de chaves de string, cada uma com um valor de seu próprio tipo. Continuaremos a expandir o sistema de tipos. Nosso próximo passo provavelmente será melhorar o suporte aos recursos numéricos do Python.

O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3
Número de linhas de código anotado: servidor

O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3
Número de linhas de código anotado: cliente

O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3
Número total de linhas de código anotado

Aqui está uma visão geral dos principais recursos do que fizemos para aumentar a quantidade de código anotado no Dropbox:

Rigor de anotação. Aumentamos gradualmente os requisitos de rigor na anotação de novos códigos. Começamos com dicas de linter que sugeriam adicionar anotações em arquivos que já possuíam algumas anotações. Agora exigimos anotações de tipo em novos arquivos Python e na maioria dos arquivos existentes.

Digitação de relatórios. Enviamos às equipes relatórios semanais sobre o nível de digitação de seu código e orientamos sobre o que deve ser anotado primeiro.

Popularização do mypy. Falamos sobre mypy em eventos e conversamos com as equipes para ajudá-las a começar com anotações de tipo.

Pesquisas. Realizamos pesquisas periódicas com os usuários para identificar os principais problemas. Estamos prontos para ir muito longe na solução desses problemas (até mesmo criando uma nova linguagem para acelerar o mypy!).

Desempenho. Melhoramos muito o desempenho do mypy usando o daemon e o mypyc. Isso foi feito para amenizar os inconvenientes que surgem durante o processo de anotação e para poder trabalhar com grandes quantidades de código.

Integração com editores. Criamos ferramentas para oferecer suporte à execução 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 sua funcionalidade. Esses tipos de ações são comuns ao anotar código existente.

Análise estática. Criamos uma ferramenta para inferir assinaturas de funções usando ferramentas de análise estática. Essa ferramenta só funciona em situações relativamente simples, mas nos ajudou a aumentar nossa cobertura de tipos de código sem muito esforço.

Suporte para bibliotecas de terceiros. Muitos de nossos projetos usam o kit de ferramentas SQLAlchemy. Ele aproveita os recursos dinâmicos do Python que os tipos PEP 484 não conseguem modelar diretamente. Nós, de acordo com PEP 561, criamos o arquivo stub correspondente e escrevemos um plugin para mypy (Código aberto), o que melhora o suporte SQLAlchemy.

Dificuldades que encontramos

O caminho para 4 milhões de linhas de código digitado nem sempre foi fácil para nós. Neste caminho encontramos muitos buracos e cometemos vários erros. Esses são alguns dos problemas que encontramos. Esperamos que contar sobre eles ajude outras pessoas a evitar problemas semelhantes.

Arquivos ausentes. Iniciamos nosso trabalho verificando apenas uma pequena quantidade de arquivos. Qualquer coisa não incluída nesses arquivos não foi verificada. 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 de verificação, então estávamos falando sobre trabalhar com valores como Any, que não foram testados. Isto levou a uma perda significativa de precisão da digitação, especialmente nos estágios iniciais da migração. Essa abordagem funcionou surpreendentemente bem até agora, embora uma situação típica seja que a adição de arquivos ao escopo da revisão revele problemas em outras partes da base de código. Na pior das hipóteses, quando duas áreas isoladas de código foram mescladas, nas quais, independentemente uma da outra, os tipos já foram verificados, descobriu-se que os tipos dessas áreas eram incompatíveis entre si. Isso levou à necessidade de fazer muitas alterações nas anotações. Olhando para trás agora, percebemos que deveríamos ter adicionado os módulos principais da biblioteca à área de verificação de tipo do mypy mais cedo. 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 à medida que os testes são executados e pode adicionar anotações de tipo ao seu código com base nas informações coletadas. No entanto, não notámos uma adopção particularmente generalizada desta ferramenta. A coleta de informações de tipo era lenta e as anotações geradas automaticamente geralmente exigiam muitas edições manuais. Pensamos em executar essa ferramenta automaticamente sempre que revisarmos o código ou em coletar informações de tipo com base na análise de um pequeno volume de solicitações de rede reais, mas decidimos não fazê-lo porque qualquer uma das abordagens era muito arriscada.

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

Importações cíclicas. Acima, falei sobre importações cíclicas (os “emaranhados de dependência”), cuja existência dificultou a aceleração do mypy. Também tivemos que trabalhar duro para fazer com que o mypy suportasse todos os tipos de expressões idiomáticas causadas por essas importações cíclicas. Concluímos recentemente um grande projeto de redesenho do sistema que corrigiu a maioria dos problemas do mypy em relação às importações circulares. Na verdade, esses problemas surgiram desde os primeiros dias do projeto, vindo de Alore, a linguagem educacional na qual o projeto mypy foi originalmente focado. A sintaxe Alore facilita a solução de problemas com comandos de importação cíclicos. O mypy moderno herdou algumas limitações de sua implementação anterior e simplória (que foi uma ótima opção para Alore). Python dificulta o trabalho com importações circulares, principalmente porque as expressões são ambíguas. Por exemplo, uma operação de atribuição pode, na verdade, definir um alias de tipo. Mypy nem sempre é capaz de detectar coisas assim até que a maior parte do loop de importação tenha sido processada. Não existiam tais ambigüidades em Alore. Decisões erradas tomadas nos estágios iniciais do desenvolvimento do sistema podem apresentar uma surpresa desagradável ao programador muitos anos depois.

Resultados: o caminho para 5 milhões de linhas de código e novos horizontes

O projeto mypy percorreu um longo caminho - desde os primeiros protótipos até um sistema que controla 4 milhões de linhas de tipos de código de produção. À medida que o mypy evoluiu, as dicas de tipo do Python foram padronizadas. Atualmente, um ecossistema poderoso se desenvolveu em torno da digitação do código Python. Possui local para suporte de biblioteca, contém ferramentas auxiliares para IDEs e editores, possui diversos sistemas de controle de tipos, cada um com seus prós e contras.

Embora a verificação de tipo já seja um dado adquirido no Dropbox, acredito que ainda estamos nos primeiros dias da digitação de código Python. Acho que as tecnologias de verificação de tipo continuarão a evoluir e melhorar.

Se você ainda não usou a verificação de tipo em seu projeto Python de grande escala, saiba que agora é um bom momento para começar a migrar para a digitação estática. Conversei com aqueles que fizeram uma transição semelhante. Nenhum deles se arrependeu. A verificação de tipo torna o Python uma linguagem muito mais adequada para o desenvolvimento de grandes projetos do que o “Python normal”.

Caros leitores! Você usa verificação de tipo em seus projetos Python?

O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3
O caminho para a verificação de tipo de 4 milhões de linhas de código Python. Parte 3

Fonte: habr.com

Adicionar um comentário