Extensión Mypy con complementos

Buenas tardes amigos. Y seguimos aumentando la intensidad del lanzamiento de nuevos cursos y ahora nos complace anunciar que las clases en el curso "Desarrollador web en Python" comenzarán a fines de abril. En este sentido, tradicionalmente compartimos la traducción de material útil. Empecemos

Python es conocido por ser un lenguaje de escritura dinámico. Es muy fácil escribir marcos similares a DSL que son difíciles de analizar con herramientas de comprobación de tipos estáticos. A pesar de esto, con las últimas innovaciones funcionales de mypy , como protocolos y tipos literales , así como soporte básico para metaclases y descriptores, a menudo podemos obtener tipos exactos, pero aún es difícil evitar falsos positivos y otros factores negativos. Para resolver este problema y evitar la necesidad de personalizar el sistema de tipos para cada marco, mypy admite un sistema de complemento . Los complementos son módulos en Python que proporcionan enlaces de complementos que mypy llamará al verificar los tipos de clases y funciones que interactúan con una biblioteca o marco. Por lo tanto, es posible identificar con mayor precisión el tipo de la función devuelta, que de otro modo es extremadamente difícil de expresar, o generar automáticamente algunos métodos de clase para reflejar los efectos del decorador. Para obtener más información sobre la arquitectura del sistema de complemento y ver la lista completa de características, consulte la documentación .



Complementos relacionados para la biblioteca estándar

Mypy viene con complementos predeterminados para implementar funciones y clases básicas, así como ctypes , contextlib y dataclasses . También incluye complementos para attrs (históricamente ha sido el primer complemento de terceros escrito para mypy ). Estos complementos le permiten a mypy determinar con mayor precisión los tipos y verificar correctamente el código para el tipo utilizando estas funciones de biblioteca. Para mostrar esto con un ejemplo, eche un vistazo a un fragmento 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') 

Arriba, se llama a get_class_decorator_hook() cuando se define la clase. Esto agrega métodos generados automáticamente, incluido __init__() , al cuerpo de la función. Mypy utiliza dicho constructor para calcular correctamente TaggedVector[int] como el tipo de position . Como puede ver en el ejemplo, los complementos funcionan incluso con clases genéricas.

Aquí hay otra pieza de código:

 from contextlib import contextmanager @contextmanager def timer(title: str) -> Iterator[float]: ... with timer(9000) as tm: ... 

Aquí get_function_hook() proporciona el tipo de retorno exacto para el decorador de contextmanager , por lo que las llamadas a la función decorada pueden verificarse para el cumplimiento de un tipo específico. Ahora mypy puede reconocer el error: el argumento para timer() debería ser una cadena.

Una combinación de complementos y talones

Además de usar funciones dinámicas de Python, los marcos a menudo se encuentran con el problema de tener API grandes. Mypy necesita archivos de código auxiliar para que las bibliotecas prueben el código que usa estas bibliotecas (solo si la biblioteca no contiene anotaciones incorporadas, lo que no es tan común). La distribución de stubs para marcos grandes con typehed no es una práctica común:

  • Typehed tiene un ciclo de liberación relativamente lento (enviado con mypy ).
  • Los talones incompletos pueden provocar llamadas falsas, lo que será extremadamente difícil de evitar.
  • No solo mezcle trozos de diferentes versiones con tipografía .

Los paquetes de código auxiliar presentados en PEP 561 hacen lo siguiente:

  • Los desarrolladores pueden lanzar paquetes de código auxiliar tantas veces como lo deseen.
  • Los usuarios que no hayan elegido usar el paquete no verán falsos positivos.
  • Puede instalar de forma segura versiones arbitrarias de varios paquetes de código auxiliar diferentes.

Además, pip permite combinar varios stubs para bibliotecas y los complementos mypy correspondientes en una sola distribución. Los apéndices para el marco mypy o el complemento correspondiente pueden desarrollarse fácilmente y agruparse en una distribución, lo cual es extremadamente útil ya que los complementos completan definiciones faltantes o inexactas en apéndices.

El último ejemplo de dicho paquete son los stubs y el complemento SQLAlchemy , con el primer lanzamiento público de la versión 0.1, que se publicó hace algún tiempo en PyPI. A pesar de que este proyecto se encuentra en la versión Alpha inicial, podemos usarlo con seguridad en DropBox para mejorar la verificación de tipos. El complemento comprende las declaraciones básicas de 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) 

En el fragmento de código anterior, el complemento usa get_dynamic_class_hook() para decirle a mypy que Base es una clase base válida, incluso si no lo parece. Luego se llama a get_base_class_hook() para definir Usuario y agrega varios atributos generados automáticamente. A continuación, creamos una instancia del modelo:

user = User(id=42, name=42)

get_function_hook() , por lo que mypy puede indicar un error: se recibe un valor integer lugar del nombre de usuario.

Los apéndices definen Column como un descriptor genérico , para que los atributos del modelo obtengan los tipos correctos:

 id_col = User.id # Inferred type is "Column[int]" name = user.name # Inferred type is "Optional[str]" 

Damos la bienvenida a los RP que agregan tipos más precisos a los apéndices ( aquí se realiza un seguimiento del progreso de los módulos principales).

Aquí hay algunas trampas que descubrimos mientras trabajábamos en enchufes:

  • Use __getattr__() para evitar falsos positivos en las primeras etapas cuando no se completen los apéndices (esto evita errores mypy si faltan atributos del módulo). También puede usar esto en archivos __init__.py si faltan submódulos.
  • Los descriptores a menudo ayudan con definiciones de tipo más precisas para el acceso a atributos personalizados (como en el ejemplo de Columna que revisamos anteriormente). El uso de descriptores está bien incluso si la implementación real del tiempo de ejecución utiliza un mecanismo más complejo, que incluye una metaclase, por ejemplo.
  • Sin dudarlo, declare las clases marco como generalizadas. A pesar de que no son tales en tiempo de ejecución, esta técnica le permite determinar con mayor precisión el tipo de algunos elementos del marco, mientras que los errores de tiempo de ejecución pueden eludirse fácilmente. (Esperamos que los marcos agreguen gradualmente soporte incorporado para tipos genéricos, heredando explícitamente las clases correspondientes de typing.Generic ).

Complementos mypy lanzados recientemente

Ya hay varios complementos disponibles para los populares marcos de Python. Además del complemento SQLAlchemy mencionado anteriormente, otros paquetes de muestra notables con apéndices y el complemento mypy incorporado incluyen apéndices para las interfaces Django y Zope . Se está trabajando activamente en estos proyectos.

Instalar y conectar stub y paquetes de complementos

Use pip para instalar el paquete de complemento para mypy y / o stub en un entorno virtual donde mypy ya está instalado :

  $ pip install sqlalchemy-stubs 

Mypy detectará automáticamente los talones instalados. Para conectar los complementos instalados, inclúyalos directamente en mypy.ini (o en el archivo de configuración del usuario):

 [mypy] plugins = sqlmypy, mypy_django_plugin.main 

Desarrollo de complementos mypy y talones de escritura

Si desea desarrollar un paquete de apéndices y complementos para el marco que usa, podemos usar el repositorio sqlalchemy-stubs como plantilla. Incluye un setup.py , pruebas de infraestructura con pruebas basadas en datos y una clase de complemento de ejemplo con un conjunto de ganchos para el complemento (ganchos de complemento). Recomendamos usar stubgen para generar automáticamente los stubs que vienen con mypy para comenzar a usarlos. Stubgen ha mejorado un mypy 0.670 en mypy 0.670 .

Consulte la documentación si desea obtener más información sobre el sistema de complementos mypy . También puede buscar en Internet los códigos fuente de los complementos discutidos en el artículo. Si tiene preguntas, puede hacerlas aquí .

El 15 de abril será un seminario web abierto gratuito sobre el curso, que será realizado por uno de los organizadores de la comunidad de Python de Moscú: Vladimir Filonov , regístrese, será interesante. Y ahora estamos esperando sus comentarios sobre el material traducido.

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


All Articles