Boa tarde amigos E continuamos a aumentar a intensidade do lançamento de novos cursos e agora temos o prazer de anunciar que as aulas no curso
"Desenvolvedor da Web em Python" começarão no final de abril. A esse respeito, tradicionalmente compartilhamos a tradução de material útil. Vamos começar.
O Python é conhecido por ser uma linguagem de digitação dinâmica. É muito fácil escrever estruturas do tipo DSL que são difíceis de analisar com ferramentas de verificação de tipo estático. Apesar disso, com
as mais recentes inovações funcionais
do mypy , como
protocolos e
tipos literais , além de suporte básico para metaclasses e suporte a descritores, geralmente podemos obter tipos exatos, mas ainda é difícil evitar falsos positivos e outros fatores negativos. Para resolver esse problema e evitar a necessidade de personalizar o sistema de tipos para cada estrutura, o
mypy suporta um sistema de
plug-in . Plugins são módulos em Python que fornecem ganchos de plugins que o
mypy chamará ao verificar os tipos de classes e funções que interagem com uma biblioteca ou estrutura. Assim, é possível distinguir com mais precisão o tipo da função retornada, que é extremamente difícil de expressar ou gerar automaticamente alguns métodos de classe para refletir os efeitos do decorador. Para saber mais sobre a arquitetura do sistema de plug-in e ver a lista completa de recursos, consulte a
documentação .
Plugins relacionados para a biblioteca padrãoO Mypy vem com plugins padrão para implementar funções e classes básicas, bem como
dataclasses
ctypes
,
contextlib
e
dataclasses
. Ele também inclui plugins para
attrs
(historicamente, foi o primeiro plug-in de terceiros
criado para
mypy ). Esses plugins permitem
ao mypy determinar com mais precisão os tipos e verificar o código corretamente, usando essas funções da biblioteca. Para mostrar isso com um exemplo, dê uma olhada em um trecho de código:
from dataclasses import dataclass from typing import Generic, TypeVar @dataclass class TaggedVector(Generic[T]): data: List[T] tag: str position = TaggedVector([0, 0, 0], 'origin')
Acima,
get_class_decorator_hook()
é chamado quando a classe é definida. Isso adiciona métodos gerados automaticamente, incluindo
__init__()
, ao corpo da função.
Mypy usa esse construtor para calcular corretamente
TaggedVector[int]
como o tipo de
position
. Como você pode ver no exemplo, os plugins funcionam mesmo com classes genéricas.
Aqui está outro pedaço de código:
from contextlib import contextmanager @contextmanager def timer(title: str) -> Iterator[float]: ... with timer(9000) as tm: ...
Aqui,
get_function_hook()
fornece o tipo de retorno exato para o decorador do
contextmanager
, para que as chamadas para a função decorada possam ser verificadas quanto à conformidade com um tipo específico. Agora
mypy pode reconhecer o erro: o argumento para
timer()
deve ser uma string.
Uma combinação de plugins e stubsAlém de usar funções dinâmicas Python, as estruturas frequentemente enfrentam o problema de ter grandes APIs.
Mypy precisa de arquivos
stub para bibliotecas para testar o código que usa essas bibliotecas (apenas se a biblioteca não contiver anotações internas, o que não é tão comum). Distribuir stubs para grandes estruturas com
datilografado não
é uma prática comum:
- Typeshed tem um ciclo de liberação relativamente lento (enviado com mypy ).
- Os stubs incompletos podem levar a chamadas falsas, o que será extremamente difícil de evitar.
- Não basta misturar stubs de diferentes versões datilografadas .
Os pacotes de stub introduzidos no
PEP 561 fazem o seguinte:
- Os desenvolvedores podem liberar pacotes stub quantas vezes quiserem.
- Os usuários que não optaram por usar o pacote não verão falsos positivos.
- Você pode instalar com segurança versões arbitrárias de vários pacotes stub diferentes.
Além disso, o
pip
permite combinar vários stubs para bibliotecas e os plugins
mypy correspondentes em uma distribuição. Os
stubs da estrutura
mypy ou do plug-in correspondente podem ser facilmente desenvolvidos e agrupados em uma distribuição, o que é extremamente útil, pois os plug-ins preenchem definições ausentes ou imprecisas nos stubs.
O exemplo mais recente desse pacote é o
stub e o plug-in SQLAlchemy , com o primeiro lançamento público da versão 0.1, publicado há algum tempo no PyPI. Apesar de esse projeto estar na versão Alpha inicial, podemos usá-lo com segurança no DropBox para melhorar a verificação de tipo. O plug-in entende as declarações básicas do ORM:
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String)
No trecho de código acima, o plug-in usa
get_dynamic_class_hook()
para informar ao
mypy que Base é uma classe base válida, mesmo que não pareça. Em seguida,
get_base_class_hook()
é chamado para definir Usuário e adiciona vários atributos gerados automaticamente. Em seguida, criamos uma instância do modelo:
user = User(id=42, name=42)
get_function_hook()
chamado, então
mypy pode indicar um erro: um valor
integer
é recebido em vez do nome de usuário.
Os stubs definem a
Column
como um descritor
genérico , para que os atributos do modelo obtenham os tipos corretos:
id_col = User.id
Congratulamo-nos com PRs que adicionam tipos mais precisos aos stubs (o progresso dos módulos principais é rastreado
aqui ).
Aqui estão algumas armadilhas que descobrimos ao trabalhar em plugues:
- Use
__getattr__()
para evitar falsos positivos nos estágios iniciais quando os stubs não forem concluídos (isso evita erros mypy se houver falta de atributos do módulo). Você também pode usar isso em arquivos __init__.py
se algum sub-módulo estiver ausente. - Os descritores geralmente ajudam com definições de tipo mais precisas para acesso a atributos personalizados (como no exemplo da coluna que analisamos acima). O uso de descritores é bom, mesmo que a implementação real do tempo de execução use um mecanismo mais complexo, incluindo uma metaclasse, por exemplo.
- Sem hesitar, declare as classes de estrutura como generalizadas. Apesar do fato de que eles não são assim no tempo de execução, essa técnica permite determinar com mais precisão o tipo de alguns elementos da estrutura, enquanto os erros de tempo de execução podem ser facilmente contornados . (Esperamos que as estruturas adicionem gradualmente suporte
typing.Generic
para tipos genéricos, herdando explicitamente as classes correspondentes da typing.Generic
.)
Plugins mypy lançados recentementeJá existem vários plugins disponíveis para as estruturas populares do Python. Além do plug-in
SQLAlchemy mencionado acima, outros pacotes de amostra dignos de nota com stubs e o plug-in
mypy embutido incluem stubs para as interfaces
Django e
Zope . O trabalho ativo está em andamento nesses projetos.
Instalando e Conectando Pacotes de Stub e PluginsUse pip para instalar o pacote de plugins para
mypy e / ou stub em um ambiente virtual em que
mypy já
esteja instalado :
$ pip install sqlalchemy-stubs
O Mypy detectará automaticamente os stubs instalados. Para conectar plug-ins instalados, inclua-os diretamente no mypy.ini (ou no arquivo de configuração do usuário):
[mypy] plugins = sqlmypy, mypy_django_plugin.main
Desenvolvendo plugins
mypy e escrevendo stubs
Se você deseja desenvolver um pacote de stubs e plugins para a estrutura que você usa, podemos usar
o repositório sqlalchemy-stubs como modelo. Ele inclui um
setup.py
, teste de infraestrutura usando testes controlados por dados e um exemplo de classe de plug-in com um conjunto de ganchos para o plug-in (ganchos de plug-in). Recomendamos o uso do
stubgen para gerar automaticamente os stubs que acompanham o
mypy para começar a usá-los.
Stubgen
melhorou um
mypy 0.670
em
mypy 0.670
.
Confira a
documentação se você quiser saber mais sobre o
sistema de plugins
mypy . Você também pode pesquisar na Internet os códigos-fonte dos plug-ins discutidos no artigo. Se você tiver dúvidas, pode perguntar
aqui .
O dia 15 de abril será um
webinar gratuito e
aberto sobre o curso, que será realizado por um dos organizadores da comunidade Python de Moscou -
Vladimir Filonov , inscreva-se, será interessante. E agora estamos aguardando seus comentários sobre o material traduzido.