El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3

Presentamos a su atención la tercera parte de la traducción del material sobre el camino que tomó Dropbox al implementar un sistema de verificación de tipos para código Python.

El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3

→ Partes anteriores: primero и segundo

Alcanzando 4 millones de líneas de código mecanografiado

Otro desafío importante (y la segunda preocupación más común entre los encuestados internamente) fue aumentar la cantidad de código cubierto por las verificaciones de tipo en Dropbox. Hemos probado varios enfoques para resolver este problema, desde aumentar naturalmente el tamaño del código base escrito hasta centrar los esfuerzos del equipo mypy en la inferencia de tipos automatizada estática y dinámica. Al final, parecía que no había una estrategia ganadora sencilla, pero pudimos lograr un rápido crecimiento en el volumen de código anotado combinando muchos enfoques.

Como resultado, nuestro repositorio de Python más grande (con código backend) tiene casi 4 millones de líneas de código anotado. El trabajo sobre escritura de código estático se completó en aproximadamente tres años. Mypy ahora admite varios tipos de informes de cobertura de código que facilitan el seguimiento del progreso de escritura. En particular, podemos generar informes sobre código con ambigüedades en los tipos, como, por ejemplo, el uso explícito de un tipo. Any en anotaciones que no se pueden verificar, o con cosas como importar bibliotecas de terceros que no tienen anotaciones de tipo. Como parte de un proyecto para mejorar la precisión de la verificación de tipos en Dropbox, contribuimos a mejorar las definiciones de tipos (los llamados archivos stub) para algunas bibliotecas populares de código abierto en un repositorio centralizado de Python. mecanografiado.

Implementamos (y estandarizamos en PEP posteriores) nuevas características del sistema de tipos que permiten tipos más precisos para algunos patrones específicos de Python. Un ejemplo notable de esto es TypeDict, que proporciona tipos para diccionarios tipo JSON que tienen un conjunto fijo de claves de cadena, cada una con un valor de su propio tipo. Continuaremos ampliando el sistema de tipos. Nuestro próximo paso probablemente será mejorar el soporte para las capacidades numéricas de Python.

El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3
Número de líneas de código anotado: servidor

El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3
Número de líneas de código anotado: cliente

El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3
Número total de líneas de código anotado

Aquí hay una descripción general de las características principales de lo que hicimos para aumentar la cantidad de código anotado en Dropbox:

Rigor de anotación. Aumentamos gradualmente los requisitos para el rigor de anotar código nuevo. Comenzamos con sugerencias de linter que sugerían agregar anotaciones a archivos que ya tenían algunas anotaciones. Ahora requerimos anotaciones de tipo en los nuevos archivos Python y en la mayoría de los archivos existentes.

Mecanografía de informes. Enviamos a los equipos informes semanales sobre el nivel de escritura de su código y brindamos consejos sobre lo que se debe anotar primero.

Popularización de mypy. Hablamos de mypy en eventos y hablamos con los equipos para ayudarlos a comenzar con las anotaciones de tipo.

Centro. Realizamos encuestas periódicas a los usuarios para identificar los principales problemas. Estamos dispuestos a llegar bastante lejos para resolver estos problemas (¡incluso crear un nuevo lenguaje para acelerar mypy!).

Actuación. Hemos mejorado enormemente el rendimiento de mypy utilizando el demonio y mypyc. Esto se hizo para suavizar los inconvenientes que surgen durante el proceso de anotación y para poder trabajar con grandes cantidades de código.

Integración con editores. Hemos creado herramientas para admitir la ejecución de mypy en editores que son populares en Dropbox. Esto incluye PyCharm, Vim y VS Code. Esto simplificó enormemente el proceso de anotar el código y comprobar su funcionalidad. Este tipo de acciones son comunes al anotar código existente.

Análisis estático. Creamos una herramienta para inferir firmas de funciones utilizando herramientas de análisis estático. Esta herramienta solo puede funcionar en situaciones relativamente simples, pero nos ayudó a aumentar nuestra cobertura de tipos de código sin mucho esfuerzo.

Soporte para bibliotecas de terceros. Muchos de nuestros proyectos utilizan el kit de herramientas SQLAlchemy. Aprovecha las capacidades dinámicas de Python que los tipos PEP 484 no pueden modelar directamente. Nosotros, de acuerdo con PEP 561, creamos el archivo resguardo correspondiente y escribimos un complemento para mypy (fuente abierta), que mejora el soporte de SQLAlchemy.

Dificultades que encontramos

El camino hacia 4 millones de líneas de código escrito no siempre ha sido fácil para nosotros. En este camino encontramos muchos baches y cometimos varios errores. Estos son algunos de los problemas que encontramos. Esperamos que contarlos ayude a otros a evitar problemas similares.

Archivos perdidos. Comenzamos nuestro trabajo verificando solo una pequeña cantidad de archivos. Todo lo que no esté incluido en estos archivos no fue verificado. Los archivos se agregaron a la lista de escaneo cuando aparecieron las primeras anotaciones en ellos. Si se importó algo desde un módulo ubicado fuera del alcance de la verificación, entonces estábamos hablando de trabajar con valores como Any, que no fueron probados en absoluto. Esto provocó una pérdida significativa de precisión al escribir, especialmente en las primeras etapas de la migración. Este enfoque ha funcionado sorprendentemente bien hasta ahora, aunque una situación típica es que agregar archivos al alcance de la revisión revela problemas en otras partes del código base. En el peor de los casos, cuando se fusionaron dos áreas de código aisladas, en las que ya se habían verificado tipos independientemente uno del otro, resultó que los tipos de estas áreas eran incompatibles entre sí. Esto llevó a la necesidad de realizar muchos cambios en las anotaciones. Mirando hacia atrás, nos damos cuenta de que deberíamos haber agregado antes los módulos de la biblioteca principal al área de verificación de tipos de mypy. Esto haría nuestro trabajo mucho más predecible.

Anotando código antiguo. Cuando empezamos, teníamos alrededor de 4 millones de líneas de código Python existente. Estaba claro que anotar todo este código no era una tarea fácil. Hemos creado una herramienta llamada PyAnnotate que puede recopilar información de tipo a medida que se ejecutan las pruebas y puede agregar anotaciones de tipo a su código en función de la información recopilada. Sin embargo, no hemos notado una adopción particularmente generalizada de esta herramienta. La recopilación de información sobre el tipo era lenta y las anotaciones generadas automáticamente a menudo requerían muchas ediciones manuales. Pensamos en ejecutar esta herramienta automáticamente cada vez que revisamos el código, o en recopilar información de tipo basada en el análisis de un pequeño volumen de solicitudes de red reales, pero decidimos no hacerlo porque cualquiera de los enfoques era demasiado arriesgado.

Como resultado, se puede observar que la mayor parte del código fue anotado manualmente por sus propietarios. Para guiar este proceso en la dirección correcta, preparamos informes sobre módulos y funciones particularmente importantes que deben anotarse. Por ejemplo, es importante proporcionar anotaciones tipográficas para un módulo de biblioteca que se utiliza en cientos de lugares. Pero ya no es tan importante anotar un servicio antiguo que está siendo reemplazado por uno nuevo. También estamos experimentando con el uso de análisis estático para generar anotaciones de tipo para código heredado.

Importaciones cíclicas. Más arriba hablé de las importaciones cíclicas (los “enredos de dependencia”), cuya existencia dificultaba acelerar mypy. También tuvimos que trabajar duro para que mypy admitiera todo tipo de modismos causados ​​por estas importaciones cíclicas. Recientemente completamos un importante proyecto de rediseño del sistema que solucionó la mayoría de los problemas de mypy relacionados con las importaciones circulares. Estos problemas en realidad surgieron desde los primeros días del proyecto, desde Alore, el lenguaje educativo en el que se centró originalmente el proyecto mypy. La sintaxis de Alore facilita la resolución de problemas con comandos de importación cíclicos. El mypy moderno ha heredado algunas limitaciones de su implementación anterior y sencilla (que encajaba perfectamente con Alore). Python dificulta el trabajo con importaciones circulares, principalmente porque las expresiones son ambiguas. Por ejemplo, una operación de asignación puede definir realmente un alias de tipo. Mypy no siempre es capaz de detectar cosas como esta hasta que se haya procesado la mayor parte del ciclo de importación. No hubo tales ambigüedades en Alore. Las malas decisiones tomadas en las primeras etapas del desarrollo del sistema pueden presentar una sorpresa desagradable al programador muchos años después.

Resultados: el camino hacia 5 millones de líneas de código y nuevos horizontes

El proyecto mypy ha recorrido un largo camino: desde los primeros prototipos hasta un sistema que controla 4 millones de líneas de tipos de código de producción. A medida que mypy evolucionó, las sugerencias de tipo de Python se estandarizaron. Hoy en día, se ha desarrollado un poderoso ecosistema en torno a escribir código Python. Tiene un lugar para soporte de biblioteca, contiene herramientas auxiliares para IDE y editores, tiene varios sistemas de control de tipos, cada uno de los cuales tiene sus pros y sus contras.

Aunque la verificación de tipos ya es un hecho en Dropbox, creo que todavía estamos en los primeros días de escribir código Python. Creo que las tecnologías de verificación de tipos seguirán evolucionando y mejorando.

Si aún no ha utilizado la verificación de tipos en su proyecto Python a gran escala, sepa que ahora es un muy buen momento para comenzar a pasar a la escritura estática. He hablado con quienes han hecho una transición similar. Ninguno de ellos se arrepintió. La verificación de tipos hace de Python un lenguaje mucho más adecuado para desarrollar proyectos grandes que el "Python normal".

Estimados lectores! ¿Utiliza la verificación de tipos en sus proyectos de Python?

El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3
El camino para verificar el tipo de 4 millones de líneas de código Python. Parte 3

Fuente: habr.com

Añadir un comentario