Novedades de Python de julio de 2023: GIL opcional, nuevo compilador y deprecación suave de optparse
(bitecode.dev)- CPython inició una transición de largo plazo para volver opcional el GIL con la aceptación de PEP 703, y el cruce entre aportes de grandes empresas y discusiones de desarrolladores del núcleo hace visible un cambio importante en el runtime de Python
- La eliminación del GIL no cambiará de inmediato el comportamiento por defecto, sino que seguirá un plan gradual que va desde builds experimentales con objetivo en 3.13/3.14 hasta un build alternativo con soporte oficial y, a largo plazo, el cambio del valor por defecto
- LPython y Cython 3.0 muestran la tendencia de ejecutar código Python más rápido, especialmente con foco en cuellos de botella de cómputo numérico y en el soporte de sintaxis Python general
- Pydantic 2 bajó la barrera de adopción con el lanzamiento de 2.1 y el soporte de FastAPI, aunque por tratarse de una versión mayor inicial se sugiere validarlo primero en proyectos personales
- Con la incorporación de soft deprecation en PEP 387, optparse y getopt se mantienen sin calendario de eliminación, pero para código nuevo conviene usar alternativas como argparse, typer o click
Plan de CPython para volver opcional el GIL
- PEP 703 es una propuesta para hacer opcional el Global Interpreter Lock en CPython, y se anunció que será aceptada después de ordenar los detalles
- Meta indicó que, si se acepta PEP 703, puede comprometer tres años-persona de ingeniería para integrar nogil en CPython
- La transición avanzará en tres etapas
- Corto plazo: se publicará en paralelo una versión experimental no soportada de Python sin GIL junto con la versión normal, con objetivo en 3.13/3.14
- Mediano plazo: la versión no-GIL tendrá soporte oficial, pero se mantendrá como un build alternativo frente a Python con GIL
- Largo plazo: no-GIL pasará a ser el valor por defecto
- El cambio del valor por defecto solo ocurrirá cuando la comunidad haya mostrado suficiente apoyo, y puede tomar varios años
- Los desarrolladores del núcleo pueden revertir la decisión y detener el proyecto antes de la transición de largo plazo si concluyen que el ROI del proyecto no-GIL es malo
- El diseño contempla que, si en un build no-GIL se importa хотя бы una extensión en C que use GIL, se vuelva automáticamente al modo con GIL
- No es una situación donde código incompatible se rompa, como ocurrió al pasar de Python 2 a Python 3
- Las extensiones compatibles con no-GIL también funcionan en el intérprete existente
- El propio código Python debería funcionar de forma natural en ambos builds, aunque con GIL los hilos quedan limitados a un solo núcleo
- La razón principal para mantener builds separados es manejar riesgos desconocidos
- No se espera que no-GIL cause problemas, pero en proyectos grandes es difícil tener certeza
- La compatibilidad ABI es delicada, y las nuevas extensiones deben compilarse explícitamente para no-GIL
LPython: un nuevo compilador enfocado en cuellos de botella numéricos
- LPython es un compilador con licencia BSD 3 que transforma código Python a LLVM, C, C++, WASM
- Puede compilar programas completos, pero su objetivo principal es acelerar cuellos de botella de cómputo numérico al estilo de numba o Cython
- Los benchmarks son prometedores, y permite alternar entre Ahead-of-Time y Just-in-Time
- Para usarlo, la máquina debe tener instalada la cadena completa de compilación
- LPython prefiere código Python puro, y para llamar funciones de Python desde un snippet hay que indicarlo con un decorador
- Lo más probable es que la mayoría de usuarios lo apliquen a fragmentos de código muy específicos
Pydantic 2 y soporte de FastAPI
- Pydantic 2 se usa mucho para validación de datos y definición de esquemas, y la nueva versión apunta a mejor rendimiento
- La versión estable salió el mes anterior, pero incluso una versión mayor ya estabilizada puede seguir teniendo detalles por pulir y soporte comunitario limitado en sus primeras etapas
- Con el lanzamiento de Pydantic 2.1, se corrigieron varios errores grandes de la etapa inicial
- FastAPI anunció soporte para Pydantic 2
- Es un hito importante porque FastAPI es uno de los proyectos que más impulsa el uso de Pydantic
- Se propone probarlo primero en proyectos personales y, si funciona bien, moverlo a proyectos de trabajo unos meses después
Soft deprecation y optparse/getopt
- PEP 387 – Backwards Compatibility Policy agregó el concepto de soft deprecation
- Soft deprecation se aplica a APIs que ya no deberían usarse en código nuevo, pero que siguen siendo seguras para el código existente
- La API sigue documentada y con pruebas mantenidas
- No habrá desarrollo adicional ni mejoras
- A diferencia de una hard deprecation normal, no presupone un calendario de eliminación
- optparse y getopt quedaron marcados como soft-deprecated
- Ambos módulos cumplieron históricamente el papel de estándar de facto para parsear argumentos de scripts
- Se pueden seguir usando, pero no se recomiendan para código nuevo
- En la librería estándar, argparse es una alternativa más moderna
- Como alternativas de terceros están typer y click
Cython 3.0 y soporte para sintaxis Python general
- Cython es un compilador de Python bien conocido y lanzó la versión 3
- Entre varias mejoras, destaca especialmente el soporte para Python puro
- Antes existía la limitación de tener que usar sintaxis superset de Python para expresar algunas funciones
- Según las notas de lanzamiento, ahora debería ser posible expresar todo el código y las funciones de Cython usando sintaxis Python normal
- Esto abre la puerta a aplicar Cython también sobre codebases de Python ya existentes
PEP 722: anotación de dependencias en scripts de un solo archivo
- PEP 722 es una propuesta para formalizar una forma de anotar dependencias de un único script Python mediante sintaxis de comentarios
- El ejemplo consiste en listar paquetes como
requestsyrichdebajo de# Requirements: - El objetivo central es formalizar la sintaxis para que herramientas de terceros puedan parsear esos comentarios
- La idea no es nueva, y pip-run ya puede ejecutar scripts con dependencias escritas en comentarios
- Los paquetes necesarios se instalan en un virtual env temporal
- El entorno se elimina después de ejecutar
- Funciona de forma parecida a npx en el ecosistema JavaScript
- Este PEP no significa que Python o pip vayan a integrar esa función; por ahora se enfoca solo en la formalización de la sintaxis
Mejoras de rendimiento en el soporte de Python para VSCode
- Al usar varios linters en VSCode, el IDE puede volverse lento; en particular, el comando
mypytarda en arrancar y VSCode no estaba usando modo daemon - En el lanzamiento de julio de 2023 se ofreció una nueva extensión oficial de mypy
- Esta extensión usa el daemon
dmypyy, gracias a la mejora de velocidad, puede revisar no solo el archivo actual sino también toda la base de código - Pylance, la extensión de soporte para Python de Microsoft, ahora persiste el trabajo de indexación de librerías de terceros
- El arranque se vuelve más liviano
- En proyectos grandes, baja el tiempo de indexación y ofrece una experiencia más rápida
Paint ejecutándose en la terminal
- textual-paint es una implementación de Paint que corre en la terminal
- Usa la librería de Python textual
- Tiene buena capacidad de respuesta y maneja
Ctrl-Z - También ofrece un selector de archivos al intentar guardar el trabajo
1 comentarios
Opiniones en Hacker News
Artículo relacionado: Intent to approve PEP 703: making the GIL optional - https://news.ycombinator.com/item?id=36913328 - julio de 2023, 488 comentarios
Desde hace tiempo he creado varias veces servicios que cargan en memoria estructuras de datos de decenas a cientos de GB y montan una API HTTP encima
Cuando un servicio hecho rápido en Python se volvía popular internamente y terminaba atendiendo a cientos o miles de clientes, muchas veces había que reescribirlo en Java para poder lanzar más hilos y procesar las solicitudes
Puede que se me haya escapado algo, pero nunca encontré la forma de sacarle rendimiento multihilo a Python, así que no-GIL me parece interesante
Para múltiples procesos, con
gunicorn[1] se puede ejecutar la app en varios procesos sin tocar mucho el código. Es como levantar tantas instancias de la misma app backend como núcleos de CPU haya, completamente aisladas con un proceso backend por núcleoPara múltiples hilos, con
gunicorn+ workers degevent[2] se obtiene de inmediato capacidad multiproceso y multihilo para trabajos centrados en entrada/salida. No es perfecto, pero en ciertas situaciones funciona muy bienPor último, si el cuello de botella es la CPU, eso significa que hay algo de margen de memoria, así que se puede poner una caché LRU o cachetools [3] sobre funciones que devuelven el mismo resultado o hacen entrada/salida costosa
[1]: https://www.joelsleppy.com/blog/gunicorn-sync-workers/
[2]: https://www.joelsleppy.com/blog/gunicorn-async-workers-with-...
[3]: https://pypi.org/project/cachetools/
multiprocessingde Python o levantar varios procesos detrás de WSGISi ya está escrito con el módulo
multithreading, se puede usar casi como reemplazo. La estructura de datos queda en memoria compartida y todos los procesos pueden acceder a ella simultáneamente sin las restricciones del GILClaro que eso no resuelve el problema de que Python en sí sea, en general, muy lento. Solo permite saturar todos los núcleos en lugar de usar al 100% un único núcleo de CPU
En la práctica lo he hecho bastante y en parte es cierto, pero es muy poco flexible. No me gustan las arquitecturas o patrones cuya respuesta es “lo estás haciendo mal. Un servidor web no debería necesitar GB de memoria. Aprende a usar una cola de trabajos”
No siempre está mal, pero muchas veces es demasiado pronto para preocuparse por ese tipo de “antipatrones”
También existe la opción de mapear la estructura de datos a un archivo y hacer
mmapdesde varios procesos, o envolver todo en una base de datos en lugar de usar un diccionario anidado enormePero el multihilo parece mucho menos doloroso que las alternativas imaginables. Si la estructura de datos encaja bien, solo agregar multihilo podría mejorar más de 10 veces en el hardware actual sin demasiado trabajo adicional
Cuando todavía era un proyecto en desarrollo, la sensación era que el consenso en HN se acercaba a “es un trabajo realmente impresionante y es una lástima que el comité directivo probablemente no lo adopte”
Ahora que lo adoptan, parece que todos lo odian
Si no asumes riesgos, te acusan de que el proyecto se estancó; si asumes riesgos, te acusan de imprudente
Si no adoptas funciones nuevas, aparecen comentarios diciendo que el lenguaje se está volviendo viejo e irrelevante y que “no puedo usarlo porque le falta X”
Si rompes compatibilidad, llega una multitud diciendo que no te importan los usuarios que necesitan estabilidad. En este hilo ya aparece la expresión “Python treadmill”
Además, todo esto pasa con proyectos de código abierto a los que la mayoría ni contribuye ni les ha dado un centavo
Son un grupo distinto de las personas a las que se les rompe el código por cambios incompatibles interminables
Al menos habrá trabajo
Resumen: Python sin GIL avanza en serio, hay un nuevo compilador de Python llamado LPython y Pydantic 2 se está volviendo usable
PEP 387 define “Soft Deprecation” y
getoptyoptparsepasan a estar soft deprecatedCython 3.0 salió con mejor soporte para Python puro, y PEP 722 trata la especificación de dependencias en scripts de un solo archivo
El soporte de Python en VSCode se volvió más rápido, e incluso se incluye dibujar en la terminal
Todavía no me siento tranquilo con no-GIL. Dicen que “no quieren otra situación como Python 2→3”, pero parece haber muy pocas propuestas concretas sobre cómo evitar ese escenario.
Hace falta documentación para escribir código thread-safe, propuestas de herramientas de lint para detectar condiciones de carrera, conversaciones con bibliotecas C populares, canales de soporte dedicados para paquetes principales, y qué hacer con la larga cola de módulos de extensión que hoy funcionan pero están abandonados.
Si el intérprete detecta una extensión C que no aceptó no-GIL, ofrecerá una versión con GIL.
Esto es muy distinto de la transición de Python 2 a 3. En ese entonces, incluso quienes no tenían ningún interés tenían que cambiar su código sí o sí para usar Python 3.
El contraste entre ambos lenguajes fue sorprendente. PHP dio motivos suficientes para actualizar, minimizó los cambios que rompían compatibilidad y a menudo también ofreció formas fáciles de agregar shims para que el código viejo siguiera funcionando.
Realmente quiero que no-GIL llegue a Python, pero al mismo tiempo me preocupa qué lecciones se aprendieron de la transición de 2 a 3. ¿Tendrá esta vez el equipo de Python un plan más efectivo?
No sé exactamente cómo está implementado ahora, pero suena bastante razonable. El código compatible con no-GIL empezará en la biblioteca core y se irá expandiendo gradualmente.
Si usas una biblioteca legacy, simplemente tendrás que volver al modo GIL. Las bibliotecas que soporten no-GIL deberían funcionar al 100% también en modo GIL, así que no creo que se repita la situación de la transición Python 2→3.
Lo que preocupa es que algunas bibliotecas simplemente abandonen el soporte para GIL, pero también espero que eso no ocurra.
Esto me parece suficiente, pero me pregunto si me estoy perdiendo algo.
El título dice “GIL removed”, pero el artículo dice que “el GIL de Python será eliminado en los próximos años”.
Entiendo que el artículo es correcto y que el GIL todavía no fue eliminado, sino que hay un plan para eliminarlo en el futuro. Si no es así, agradecería que me corrigieran.
La versión no-GIL está prevista como experimental a partir de 3.13 o 3.14.
https://peps.python.org/pep-0703/
https://discuss.python.org/t/a-steering-council-notice-about...
Ese plan es PEP 703, y el fundamento factual es la frase “tenemos la intención de aceptar PEP 703, aunque los detalles de aceptación todavía se están trabajando”.
“GIL will be removed” era lo más cercano, pero demasiado largo, y sonaba como aquellas veces anteriores en que solo se prometió eliminarlo y al final no pasó.
Por eso el pretérito perfecto profético fue el mejor compromiso. Es corto y afirma una certeza casi total, y aun en el peor caso el cuerpo del texto elimina la ambigüedad.
Además, esta semana hubo noticias relacionadas en la portada de HN, así que mucha gente tenía el contexto.
Esto es exactamente lo que estaba esperando. Que permitan usar no-GIL y que yo, como desarrollador, elija.
Claro que habrá problemas, pero soy consciente de eso, y si analizas los beneficios de no-GIL, en ciertos casos de uso conviene muchísimo más.
Uno de los casos importantes es el threading fuera del contexto del sistema operativo. Si quieres usar los dos núcleos de un Cortex M0, ¿qué haces? Como no hay procesos, multiprocessing no ayuda. Si necesitas locks, puedes implementarlos directamente en la plataforma dada.
El segundo punto es que la cantidad de núcleos de CPU sigue aumentando. Usar multiprocessing vuelve los programas mucho más complejos de lo esperado. Si el sistema operativo y la CPU ya ofrecen mejores herramientas, ¿por qué tendría que hacer paso de mensajes y memoria compartida? Además ensucia los ID de proceso.
Si imaginamos que todas las aplicaciones se hicieran en Python, para usar varios núcleos terminaríamos con cientos de miles de procesos separados. Como este problema es en gran medida específico de Python, incluso aplicaciones para las que Python habría sido mejor acaban haciéndose en otros lenguajes.
Quiero un mundo donde se pueda decir para casi cualquier cosa: “simplemente usa Python”. Si a un programador nuevo le dices que para obtener el resultado que quiere debe abandonar su lenguaje favorito y usar otro, la innovación muere de inmediato. En lugar de crear ideas, termina gastando tiempo en aprender un lenguaje que considera innecesario.
no-GIL opcional está bien, pero es casi una pérdida de tiempo; hacerlo el valor predeterminado es malo.
Si quieres usar los dos núcleos de un Cortex M0, la solución para problemas que necesitan rendimiento en Python es escribir una extensión C, como Numpy. Python no es un lenguaje orientado al rendimiento.
El GIL te permite escribir código sin pensar en la complejidad del paralelismo
Herramientas como
pip-runpodrían ejecutar scripts leyendo las dependencias indicadas en los comentarios, y dicen que los paquetes se instalan en un entorno virtual temporal y luego se eliminan tras la ejecución. Es parecido a lo que hacía antesnpxen el mundo de JS.Pero me pregunto si esto es eficiente. Descargar e instalar paquetes para borrarlos unos segundos después desperdicia valiosas celdas del SSD.
Hace poco le pregunté a un desarrollador con el que trabajaba por qué, cada vez que quería consultar algo una sola vez, buscaba, descargaba y abría un PDF de decenas de MB, y luego al cerrarlo lo eliminaba; parecía no entender en absoluto el concepto.
Yo suelo tener reunida la documentación de los proyectos y dejarla casi siempre abierta, así que pensaba que eso era lo normal, pero parece que otras personas la buscan en línea cada vez, cierran el navegador y vuelven a abrirla cuando la necesitan.
Incluso públicamente, hace poco Docker y, más relacionado con esto, PyPI empezaron a preocuparse por el uso de ancho de banda: https://news.ycombinator.com/item?id=24262757 https://news.ycombinator.com/item?id=27205586
Salvo lo de
optparse, todo esto estuvo ayer en la primera página. Ahoraoptparseestá deprecated.Eso significa que se suma más trabajo, además de auditar módulos de extensión por el tema de threading. La vida en la cinta de correr de Python es maravillosa.
optparseestaba previsto para eliminarse alrededor de la versión 3.5, pero hubo mucha resistencia.La documentación tenía una advertencia de obsolescencia desde la 3.2.
Durante mucho tiempo estuvo en estado de “simplemente usen
argparse”, y esta vez solo “agregaron” una advertencia real en el código.argparse, pero es demasiado complejo. Cada vez que lo uso siempre tengo que volver a leer la documentación.getopttiene su propia simplicidad brutal.¿Por qué la comunidad de Python no eliminó el GIL al pasar de Python 2 a Python 3?