Hoy te presentamos la primera parte de la traducción del material sobre cómo en Dropbox se realiza el control de tipos en el código Python.
En Dropbox se escribe mucho en Python. Este es un lenguaje que usamos de manera extremadamente amplia, tanto para servicios de backend como para aplicaciones cliente de escritorio. También utilizamos en grandes cantidades Go, TypeScript y Rust, pero Python es nuestro principal lenguaje. Teniendo en cuenta nuestra escala, que abarca millones de líneas de código Python, resultó que la tipificación dinámica de este código complicó innecesariamente su comprensión y comenzó a afectar seriamente la productividad. Para mitigar este problema, comenzamos un proceso de transición gradual de nuestro código a una verificación de tipos estática utilizando mypy. Probablemente, esta sea la sistema de verificación de tipos independiente más popular para Python. Mypy es un proyecto de código abierto, y sus principales desarrolladores trabajan en Dropbox.
Dropbox fue una de las primeras compañías en implementar verificaciones estáticas de tipos en el código Python a tal escala. Hoy en día, mypy se utiliza en miles de proyectos. Esta herramienta ha sido 'probada en combate' innumerables veces. Para llegar a donde estamos hoy, hemos recorrido un largo camino. En este trayecto, hemos enfrentado muchos comienzos fallidos y experimentos que no triunfaron. Este material narra la historia de la verificación estática de tipos en Python, desde sus inicios complicados, que formaron parte de mi proyecto de investigación, hasta el día de hoy, cuando las verificaciones de tipos y las sugerencias sobre tipos se han vuelto comunes para innumerables desarrolladores que programan en Python. Estos mecanismos ahora son compatibles con muchas herramientas, como IDEs y analizadores de código.
→
¿Por qué es necesaria la verificación de tipos?
Si alguna vez has utilizado Python, que es un lenguaje de tipado dinámico, puede que no comprendas por qué últimamente ha surgido tanto ruido en torno a la tipificación estática y mypy. O quizás te guste Python precisamente por su tipificación dinámica, y lo que está sucediendo te frustra. La clave del valor de la tipificación estática radica en la escala de las soluciones: cuanto más grande sea tu proyecto, más te inclinarás hacia la tipificación estática, y, al final, más realmente lo necesitarás.
Supongamos que un proyecto ha alcanzado decenas de miles de líneas y ha resultado que varias programadoras están trabajando en él. Al considerar un proyecto así, podemos decir, basándonos en nuestra experiencia, que entender su código será fundamental para mantener la productividad de los desarrolladores. Sin anotaciones de tipos, puede resultar complicado averiguar, por ejemplo, qué argumentos deben pasarse a una función, o qué tipos de valores puede devolver cierta función. Aquí hay preguntas típicas a las que a menudo es difícil responder sin utilizar anotaciones de tipos:
- ¿Puede esta función devolver
None? - ¿Qué debería ser este argumento?
items? - ¿Cuál es el tipo del atributo?
id:int¿es este,str, o tal vez algún tipo personalizado? - ¿Debería este argumento ser una lista? ¿Se puede pasar una tupla?
Si observamos el siguiente fragmento de código, que incluye anotaciones de tipos, y tratamos de responder preguntas similares, resulta que es una tarea muy sencilla:
class Resource:
id: bytes
...
def read_metadata(self,
items: Sequence[str]) -> Dict[str, MetadataItem]:
...read_metadatano devuelveNone, ya que el tipo devuelto no esOptional[…].- El argumento
itemses una secuencia de cadenas. No se puede iterar en un orden aleatorio. - El atributo
ides una cadena de bytes.
En un mundo ideal, podríamos esperar que todos esos matices estuvieran documentados en la documentación incorporada (docstring). Pero la experiencia ofrece muchos ejemplos de que tal documentación a menudo está ausente en el código con el que se trabaja. Incluso si dicha documentación existe, no se puede confiar en su absoluta precisión. Esta documentación puede ser confusa, inexacta y dejar muchas oportunidades para su mala interpretación. En equipos grandes o en grandes proyectos, este problema puede volverse extremadamente agudo.
Aunque Python se desempeña muy bien en las etapas iniciales o intermedias de los proyectos, en un momento determinado, los proyectos y compañías exitosos que utilizan Python pueden enfrentarse a la cuestión crítica: '¿Necesitamos reescribir todo en un lenguaje de tipo estático?'.
Los sistemas de verificación de tipos como mypy abordan el problema mencionado anteriormente al ofrecer al desarrollador un lenguaje formal para describir tipos, y al verificar que las descripciones de tipos correspondan a la implementación del programa (y, opcionalmente, verificar su existencia). En general, se puede decir que estos sistemas nos proporcionan algo similar a documentación exhaustivamente comprobada.
Además, el uso de tales sistemas tiene otras ventajas, y ya no son triviales:
- Un sistema de verificación de tipos puede detectar algunos errores menores (así como errores no tan menores). Un ejemplo típico es cuando se olvida manejar un valor
Noneo alguna otra condición especial. - El refactorizado de código se simplifica considerablemente, ya que el sistema de verificación de tipos a menudo indica con gran precisión qué código necesita ser modificado. Al mismo tiempo, no tenemos que depender de una cobertura del 100 % del código con pruebas, lo cual, en cualquier caso, suele ser inalcanzable. No es necesario estudiar en profundidad los informes de seguimiento de pila para descubrir la causa del fallo.
- Incluso en proyectos grandes, mypy a menudo puede realizar una verificación completa de tipos en fracciones de segundo. La ejecución de pruebas generalmente toma decenas de segundos o incluso minutos. El sistema de verificación de tipos brinda al programador retroalimentación instantánea y le permite trabajar más rápido. Ya no necesita escribir pruebas modulares frágiles y difíciles de mantener, que sustituyen entidades reales con mocks y parches solo para obtener resultados de pruebas de código más rápidamente.
Los IDE y editores, como PyCharm o Visual Studio Code, utilizan las capacidades de las anotaciones de tipos para ofrecer a los desarrolladores funciones de autocompletado de código, resaltado de errores y soporte para construcciones de lenguaje comúnmente utilizadas. Y estas son solo algunas de las ventajas que ofrece la tipificación. Para algunos programadores, todo esto es el principal argumento a favor de la tipificación. Es algo que beneficia de inmediato tras su implementación. Este uso de tipos no requiere la aplicación de un sistema de verificación de tipos separado, como mypy, aunque cabe señalar que mypy ayuda a mantener la conformidad entre las anotaciones de tipos y el código.
Historia de mypy
La historia de mypy comenzó en el Reino Unido, en Cambridge, varios años antes de que me uniera a Dropbox. Estaba investigando en el marco de un doctorado sobre la unificación de lenguajes estáticamente tipados y dinámicos. Me inspiró un artículo sobre tipificación gradual de Jeremy Siek y Walid Taha, así como el proyecto Typed Racket. Intentaba encontrar formas de usar el mismo lenguaje de programación para diferentes proyectos, desde pequeños scripts hasta bases de código que constan de millones de líneas. Al mismo tiempo, quería que en un proyecto de cualquier escala no hubiera que comprometer demasiado. Una parte importante de todo esto fue la idea de una transición gradual de un prototipo no tipado a un producto terminado y completamente probado con tipos estáticos. Hoy en día, estas ideas se consideran en gran medida como un hecho, pero en 2010 era un problema que aún se investigaba activamente.
Mi trabajo inicial en verificación de tipos no estaba dirigido a Python. En su lugar, utilicé un pequeño lenguaje 'hecho a mano'. Aquí hay un ejemplo que te ayudará a entender de qué se trata (las anotaciones de tipo aquí son opcionales):
def Fib(n as Int) as Int
if n <= 1
return n
else
return Fib(n - 1) + Fib(n - 2)
end
endEl uso de un lenguaje simplificado de desarrollo propio es un enfoque común en la investigación científica. Esto se debe en gran parte a que permite realizar experimentos rápidamente, así como a que se puede ignorar libremente lo que no está relacionado con la investigación. Los lenguajes de programación realmente utilizados suelen ser fenómenos complejos con implementaciones complejas, lo que ralentiza los experimentos. Sin embargo, cualquier resultado basado en un lenguaje simplificado puede parecer un poco sospechoso, ya que al obtener estos resultados, el investigador puede haber sacrificado consideraciones importantes para el uso práctico de los lenguajes.
Mi herramienta de verificación de tipos para Alore parecía muy prometedora, pero quería probarla realizando experimentos con código real, del cual, se podría decir, no había en Alore. Afortunadamente, el lenguaje Alore se basaba en gran medida en las mismas ideas que Python. Fue bastante fácil adaptar la herramienta de verificación de tipos para que pudiera funcionar con la sintaxis y semántica de Python. Esto permitió intentar realizar la verificación de tipos en código Python de código abierto. Además, escribí un transpilador para convertir el código escrito en Alore a código Python y lo utilicé 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 admitía un subconjunto de Python, ¡una especie de variante de este lenguaje! (Ciertas decisiones arquitectónicas que tenían sentido para Alore no encajaban bien en Python, esto todavía se nota en algunas partes de la base de código de mypy.)
En realidad, el lenguaje compatido por mi sistema de tipos en ese momento no se podía considerar completamente Python: era una variante de Python debido a algunas limitaciones de la sintaxis de las anotaciones de tipo de 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 utilizar anotaciones de tipos para mejorar el rendimiento mediante la compilación de esta variante de Python a C, o quizás a bytecode de la JVM. Avancé hasta la etapa de escribir un prototipo de compilador, pero abandoné esta aventura, ya que la verificación de tipos por sí sola parecía bastante útil.
Al final, presenté mi proyecto en la conferencia PyCon 2013 en Santa Clara. También hablé de esto con Guido van Rossum, el generoso dictador vitalicio de Python. Me convenció de que abandonara mi propia sintaxis y que me atuviera a la sintaxis estándar de Python 3. Python 3 soporta anotaciones de funciones, por lo que mi ejemplo podía reescribirse como se muestra a continuación, resultando en un programa Python normal:
def fib(n: int) -> int:
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)Tuve que hacer algunos compromisos (primero quiero recalcar que inventé mi propia sintaxis precisamente por eso). En particular, Python 3.3, la versión más reciente del lenguaje en ese momento, no soportaba anotaciones de variables. Discutí con Guido por correo electrónico varias opciones de formato sintáctico para tales anotaciones. Decidimos usar comentarios para las variables con indicaciones de tipos. Esto permitía alcanzar el objetivo propuesto, aunque parecía un poco torpe (Python 3.6 nos dio una sintaxis más agradable):
products = [] # type: List[str] # EwwLos comentarios con tipos también resultaron útiles para mantener la compatibilidad con Python 2, que no tiene soporte integrado para anotaciones de tipos:
f fib(n):
# type: (int) -> int
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)Resulta que estos (y otros) compromisos, en realidad, no importaban mucho: las ventajas de la tipificación estática llevaron a que los usuarios pronto olvidaran la sintaxis no del todo perfecta. Dado que en el código de Python, que controlaba los tipos, no se aplicaban construcciones sintácticas especiales, las herramientas y procesos existentes para el manejo de código en Python continuaron funcionando normalmente, lo que facilitó significativamente la adopción de la nueva herramienta por parte de los desarrolladores.
Guido, además, me convenció de unirme a Dropbox después de que defendí mi tesis. Aquí es donde comienza la parte más interesante de la historia de mypy.
Continuará...
¡Estimados lectores! Si utilizas Python, cuéntanos sobre los proyectos de qué escala estás desarrollando en este idioma.
Fuente: habr.com
