Hoy llamamos su atención sobre la primera parte de la traducción del material sobre cómo Dropbox participa en el control de tipo de código Python.

Dropbox escribe mucho en Python. Este es un lenguaje que utilizamos ampliamente, tanto para servicios de fondo como para aplicaciones de cliente de escritorio. También usamos Go, TypeScript y Rust en grandes volúmenes, pero Python es nuestro lenguaje principal. Teniendo en cuenta nuestra escala, y estamos hablando de millones de líneas de código Python, resultó que la escritura dinámica de dicho código complicó innecesariamente su comprensión y comenzó a afectar seriamente la productividad. Para mitigar este problema, comenzamos a traducir gradualmente nuestro código a la verificación de tipos estáticos usando mypy. Este es probablemente el sistema de verificación de tipo independiente más popular para Python. Mypy es un proyecto de código abierto; sus principales desarrolladores trabajan en Dropbox.
Dropbox fue una de las primeras compañías en implementar la verificación de tipos estáticos en el código Python en una escala similar. Hoy en día, mypy se usa en miles de proyectos. Esta herramienta es innumerables veces, como dicen, "probada en la batalla". Nosotros, para llegar a donde estamos ahora, tuvimos que recorrer un largo camino. En este camino hubo muchas empresas fallidas y experimentos fallidos. Este material trata sobre la historia de la verificación de tipos estáticos en Python, desde su comienzo muy difícil, que fue parte de mi proyecto de investigación científica, hasta la actualidad, cuando las verificaciones de tipos y las sugerencias de tipos se han familiarizado con innumerables desarrolladores que escriben en Python. Estos mecanismos ahora son compatibles con muchas herramientas, como IDEs y analizadores de código.
→
Leer la segunda parte¿Por qué es necesaria la verificación de tipo?
Si alguna vez ha usado Python con tipeo dinámico, es posible que tenga alguna confusión sobre por qué recientemente se ha hecho tal zumbido en torno al tipeo estático y mypy. O puede ser que te guste Python precisamente por su escritura dinámica, y lo que está sucediendo te molesta. La clave del valor de la escritura estática es la escala de decisiones: cuanto más grande es su proyecto, más tiende a la escritura estática y, al final, más lo necesita realmente.
Supongamos que un proyecto llega a decenas de miles de líneas y resulta que varios programadores están trabajando en él. Considerando tal proyecto, en base a nuestra experiencia, podemos decir que comprender su código será la clave para apoyar la productividad del desarrollador. Sin anotaciones de tipo, no es fácil descubrir, por ejemplo, qué argumentos necesita pasar a una función, o qué valores de qué tipos puede devolver una función. Aquí hay preguntas típicas que a menudo son difíciles de responder sin usar anotaciones de tipo:
- ¿Puede esta función devolver
None
? - ¿Cuál debería ser este argumento de los
items
? - ¿Cuál es el tipo de atributo
id
: int
, es str
o quizás algún tipo personalizado? - ¿Debería este argumento ser una lista? ¿Es posible pasarle una tupla?
Si observa el siguiente fragmento de código, equipado con anotaciones de tipo, e intenta responder a tales preguntas, resulta que esta es la tarea más simple:
class Resource: id: bytes ... def read_metadata(self, items: Sequence[str]) -> Dict[str, MetadataItem]: ...
read_metadata
no devuelve None
, porque el tipo de retorno no es Optional[…]
.- El argumento de los
items
es una secuencia de cadenas. No se puede repetir en ningún orden. - El atributo
id
es una cadena de bytes.
En un mundo ideal, uno esperaría que todas esas sutilezas se describieran en la documentación incorporada (docstring). Pero la experiencia da muchos ejemplos del hecho de que a menudo no se observa dicha documentación en el código con la que tiene que trabajar. Incluso si dicha documentación está presente en el código, no se puede contar con su corrección absoluta. Esta documentación puede ser poco clara, imprecisa, dejando muchas posibilidades para su malentendido. En equipos grandes o en proyectos grandes, este problema puede llegar a ser extremadamente agudo.
Aunque Python se desempeña bien en las etapas iniciales o intermedias de los proyectos, en algún momento los proyectos exitosos y las compañías que usan Python pueden plantear una pregunta vital: "¿Necesitamos reescribir todo en un lenguaje estáticamente escrito?"
Los sistemas de verificación de tipos como mypy resuelven el problema antes mencionado al proporcionar al desarrollador un lenguaje formal para describir los tipos y al verificar que las descripciones de los tipos sean consistentes con las implementaciones del programa (y, opcionalmente, verificar su existencia). En general, podemos decir que estos sistemas nos dan algo como documentación cuidadosamente revisada.
El uso de tales sistemas tiene otras ventajas, y ya son completamente no triviales:
- Un sistema de verificación de tipos puede detectar algunos errores pequeños (así como no muy pequeños). Un ejemplo típico es cuando se olvidan de procesar el valor
None
o alguna otra condición especial. - La refactorización del código se simplifica enormemente ya que el sistema de verificación de tipos a menudo informa con mucha precisión qué código debe cambiarse. En este caso, no necesitamos esperar una cobertura del 100% del código con las pruebas, lo que, en cualquier caso, suele ser imposible. No necesitamos examinar la profundidad de los informes de seguimiento de la pila para descubrir la causa del problema.
- Incluso en proyectos grandes, mypy a menudo puede hacer una verificación de tipo completo en una fracción de segundo. Y la ejecución de pruebas generalmente toma decenas de segundos o incluso minutos. El sistema de verificación de tipos le da al programador información instantánea y le permite hacer su trabajo más rápido. Ya no necesita escribir pruebas unitarias frágiles y pesadas de soporte que reemplacen entidades reales con mokas y parches solo para obtener los resultados de la prueba de código más rápido.
Los IDE y los editores, como PyCharm o Visual Studio Code, utilizan funciones de anotación de tipo para proporcionar a los desarrolladores la capacidad de completar automáticamente el código, resaltar errores y admitir construcciones de lenguaje de uso común. Y estas son solo algunas de las ventajas que ofrece la escritura. Para algunos programadores, todo esto es el argumento principal a favor de escribir. Esto es lo que se beneficia inmediatamente después de la implementación. Este caso de uso de tipo no requiere un sistema de verificación de tipo separado, como mypy, aunque debe tenerse en cuenta que mypy ayuda a mantener las anotaciones de tipo y el código coherentes.
Fondo Mypy
La historia mypy comenzó en el Reino Unido, en Cambridge, unos años antes de que me uniera a Dropbox. Como parte de mi investigación doctoral, me ocupé de la unificación de lenguajes dinámicos y de tipo estático. Me inspiró un artículo sobre la tipificación gradual de Jeremy Siek y Walid Taha, así como el proyecto Typed Racket. Traté de encontrar formas de utilizar el mismo lenguaje de programación para varios proyectos, desde pequeños scripts hasta bases de código que constan de muchos millones de líneas. Al mismo tiempo, me gustaría que un proyecto de cualquier escala no tuviera que hacer compromisos demasiado grandes. Una parte importante de todo esto fue la idea de una transición gradual de un proyecto prototipo sin tipo a un producto terminado de tipo estático probado exhaustivamente. Hoy en día, estas ideas se dan en gran medida por sentado, pero en 2010 era un problema que todavía se estaba explorando activamente.
Mi trabajo inicial en el campo de la verificación de tipos no estaba dirigido a Python. En cambio, usé el pequeño lenguaje "casero"
Alore . Aquí hay un ejemplo que le permitirá comprender lo que está en juego (las anotaciones de tipo son opcionales aquí):
def Fib(n as Int) as Int if n <= 1 return n else return Fib(n - 1) + Fib(n - 2) end end
El uso de un lenguaje simplificado de nuestro propio diseño es un enfoque común utilizado en la investigación científica. Esto se debe principalmente al hecho de que esto le permite realizar experimentos rápidamente, y también al hecho de que lo que no tiene nada que ver con la investigación puede ignorarse libremente. Los lenguajes de programación realmente utilizados son fenómenos a gran escala con implementaciones complejas, y esto ralentiza los experimentos. Sin embargo, cualquier resultado basado en un lenguaje simplificado parece un poco sospechoso, ya que cuando se obtuvieron los resultados, el investigador pudo haber sacrificado consideraciones que son importantes para el uso práctico de los idiomas.
Mi herramienta de verificación de tipos para Alore parecía muy prometedora, pero quería probarla experimentando con código real, que, podría decirse, no estaba escrito en Alore. Afortunadamente, Alore se basó en gran medida en las mismas ideas que Python. Fue lo suficientemente simple como para rehacer la herramienta de verificación de tipos para que pudiera funcionar con la sintaxis y la semántica de Python. Esto nos permitió intentar la verificación de tipos en código Python de código abierto. Además, escribí un transportador para convertir el código escrito en Alore a código Python y lo usé para traducir el código de mi herramienta de verificación de tipos. ¡Ahora tenía un sistema de verificación de tipos escrito en Python que soportaba un subconjunto de Python, algún tipo de lenguaje! (Ciertas soluciones arquitectónicas que tenían sentido para Alore eran poco adecuadas para Python, que todavía se nota en partes de la base de código mypy).
De hecho, el lenguaje admitido por mi sistema de tipos en ese momento no podría haberse llamado Python: era una variante de Python debido a algunas limitaciones de la sintaxis de la sintaxis de anotación de tipo Python 3.
Parecía una mezcla de Java y Python:
int fib(int n): if n <= 1: return n else: return fib(n - 1) + fib(n - 2)
Una de mis ideas en ese momento era usar anotaciones de tipo para mejorar el rendimiento compilando este tipo de Python en C, o posiblemente en el código de bytes JVM. Avancé a la etapa de escribir el prototipo del compilador, pero dejé esta idea, ya que la verificación de tipo en sí misma parecía bastante útil.
Al final, presenté mi proyecto en la conferencia PyCon 2013 en Santa Clara. También hablé sobre esto con Guido van Rossum, el generoso dictador de toda la vida de Python. Me convenció de que abandonara mi propia sintaxis y me apegara a la sintaxis estándar de Python 3. Python 3 admite anotaciones de funciones, como resultado, mi ejemplo podría reescribirse como se muestra a continuación, obteniendo un programa Python normal:
def fib(n: int) -> int: if n <= 1: return n else: return fib(n - 1) + fib(n - 2)
Necesitaba hacer algunos compromisos (en primer lugar, quiero señalar que inventé mi propia sintaxis por este motivo). En particular, Python 3.3, la versión más reciente del lenguaje en ese momento, no admitía anotaciones variables. He discutido con Guido por correo electrónico las diversas posibilidades para sintaxis de tales anotaciones. Decidimos usar comentarios de tipo para las variables. Esto nos permitió lograr nuestro objetivo, pero parecía un poco engorroso (Python 3.6 nos dio una sintaxis más agradable):
products = []
Los comentarios de tipo también fueron útiles para el soporte de Python 2, que carece de soporte incorporado para anotaciones de tipo:
f fib(n):
Resultó que estos (y otros) compromisos, de hecho, no tenían mucha importancia: las ventajas del tipeo estático llevaron al hecho de que los usuarios pronto olvidaron la sintaxis no tan perfecta. Como no se utilizaron construcciones de sintaxis especiales en el código de Python en el que se controlaban los tipos, las herramientas de Python y los procesos de procesamiento de código existentes continuaron funcionando normalmente, lo que facilitó enormemente el desarrollo de la nueva herramienta por parte de los desarrolladores.
Guido también me convenció de unirme a Dropbox después de defender mi graduación. Aquí es donde comienza la parte divertida de la historia mypy.
Continuará ...
Estimados lectores! Si usa Python, cuéntenos sobre la escala de los proyectos que está desarrollando en este lenguaje.
