Olá pessoal! Hoje vou contar a história do desenvolvimento da digitação no exemplo de um dos projetos no
Ostrovok.ru .

Essa história começou muito antes do
hype de digitação no
python3.5 , além disso, começou dentro de um projeto escrito em
python2.7 .
2013 : recentemente, houve o lançamento do
python3.3 , não havia sentido em migrar para a nova versão, uma vez que não
incluía nenhum recurso específico, e haveria muita dor e sofrimento durante a transição.
Eu participei do projeto Partners no Ostrovok.ru - este serviço foi responsável por tudo relacionado a integrações de parceiros, reservas, estatísticas e uma conta pessoal. Usamos APIs internas para outros microsserviços da empresa e uma API externa para nossos parceiros.
Em algum momento, a equipe formou a seguinte abordagem para escrever manipuladores HTTP ou algum tipo de lógica de negócios:
1) os dados de entrada e saída devem ser descritos por uma estrutura (classe),
2) o conteúdo das instâncias de estruturas deve ser validado de acordo com a descrição,
3) uma função que pega uma estrutura na entrada e fornece a estrutura na saída deve verificar os tipos de dados na entrada e na saída, respectivamente.
Não vou me debruçar sobre cada ponto em detalhes; o exemplo abaixo deve ser suficiente para entender o que está em jogo.
Exemplo.
import datetime as dt from contracts import new_contract, contract from schematics.models import Model from schematics.types import IntType, DateType
O exemplo usa bibliotecas:
esquemas e
pycontracts .
*
esquemas - uma maneira de descrever e validar dados.
pycontracts - uma maneira de verificar a entrada / saída de uma função em tempo de execução.
Essa abordagem permite:
- é mais fácil escrever testes - problemas com a validação não surgem e apenas a lógica comercial é coberta.
- para garantir o formato e a qualidade da resposta na API - uma estrutura rígida aparece para o que estamos prontos para aceitar e o que podemos oferecer.
- é mais fácil entender / refatorar o formato de resposta se for uma estrutura complexa com diferentes níveis de aninhamento.
É importante entender que a verificação de tipo (não a validação) funciona apenas em
tempo de execução , e isso é conveniente para o desenvolvimento local, executando testes no IC e verificando a liberação do candidato em um ambiente de
preparação . Em um ambiente de produção, isso deve ser desativado, caso contrário, o servidor diminuirá a velocidade.
Anos se passaram, nosso projeto cresceu, mais lógica de negócios nova e complexa apareceu, o número de identificadores de API pelo menos não diminuiu.
Em algum momento, comecei a perceber que o lançamento do projeto já levava alguns segundos perceptíveis - isso era irritante, porque toda vez que eu editava o código e fazia os testes, eu precisava sentar e esperar por um longo tempo. Quando essa espera começou a levar de 8 a 10 segundos, finalmente decidimos descobrir o que estava acontecendo sob o capô.
De fato, tudo acabou sendo bastante simples. Ao iniciar um projeto, a biblioteca pycontracts analisa todas as
doutrinas cobertas pelo
@contract para registrar todas as estruturas na memória e depois verificá-las corretamente. Quando o número de estruturas em um projeto chega a milhares, tudo começa a diminuir.
O que fazer sobre isso? A resposta correta é procurar outras soluções, felizmente no quintal já é
2018 (
python3.5 -
python3.6 ) e já migramos nosso projeto para
python3.6 .
Comecei a estudar soluções alternativas e pensar em como migrar um projeto de "
pycontracts + descrição do tipo na
documentação " para "algo + descrição do tipo na
anotação de digitação ". Descobriu-se que, se você atualizar
pycontracts para a versão mais recente, poderá descrever os tipos no estilo de
anotação de digitação , por exemplo, pode ser assim:
@contract def get_order_info(data_in: OrderInfoData) -> OrderInfoResult: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) )
Os problemas começam se você precisar usar estruturas de
digitação , por exemplo,
Opcional ou
União , pois os
pycontracts NÃO sabem como trabalhar com eles:
from typing import Optional @contract def get_order_info(data_in: OrderInfoData) -> Optional[OrderInfoResult]: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) )
Comecei a procurar bibliotecas alternativas para verificação de tipo em
tempo de execução :
*
impor*
Typeguard*
pytypesA aplicação naquele momento não suportava
python3.7 , mas já atualizamos, os
pytypes não gostaram da sintaxe, como resultado, a escolha recaiu sobre o
typeguard .
from typeguard import typechecked @typechecked def get_order_info(data_in: OrderInfoData) -> Optional[OrderInfoResult]: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) )
Aqui estão exemplos de um projeto real:
@typechecked def view( request: HttpRequest, data_in: AffDeeplinkSerpIn, profile: Profile, contract: Contract, ) -> AffDeeplinkSerpOut: ... @typechecked def create_contract( user: Union[User, AnonymousUser], user_uid: Optional[str], params: RegistrationCreateSchemaIn, account_manager: Manager, support_manager: Manager, sales_manager: Optional[Manager], legal_entity: LegalEntity, partner: Partner, ) -> tuple: ... @typechecked def get_metaorder_ids_from_ordergroup_orders( orders: Tuple[OrderGroupOrdersIn, ...], contract: Contract ) -> list: ...
Como resultado, após um longo processo de refatoração, conseguimos transferir completamente o projeto para
anotações de digitação com digitação +.
Que resultados alcançamos:
- O projeto começa em 2-3 segundos, o que não é pelo menos irritante.
- a legibilidade do código melhorou.
- o projeto ficou menor tanto no número de linhas quanto nos arquivos, pois não há mais registros de estrutura via @new_contract .
- os IDEs inteligentes do PyCharm tornaram-se melhores na indexação de um projeto e em sugestões diferentes, porque agora não são comentários, mas importações honestas.
- Você pode usar analisadores estáticos como mypy e pyre-check , pois eles suportam o trabalho com anotações de digitação .
- A comunidade python como um todo está se movendo para digitar de uma forma ou de outra, ou seja, as ações atuais são investimentos no futuro do projeto.
- Às vezes, há problemas com importações cíclicas, mas existem poucas e podem ser negligenciadas.
Espero que este artigo seja útil para você!
Referências:
*
impor*
Typeguard*
pytypes*
pycontracts*
esquemas