2 puntos por GN⁺ 2023-07-31 | 1 comentarios | Compartir por WhatsApp
  • 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 requests y rich debajo 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 mypy tarda 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 dmypy y, 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

 
GN⁺ 2023-07-31
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

    • Como la parte difícil ya está hecha, antes de reescribir la API HTTP consideraría primero estas optimizaciones
      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úcleo
      Para múltiples hilos, con gunicorn + workers de gevent [2] se obtiene de inmediato capacidad multiproceso y multihilo para trabajos centrados en entrada/salida. No es perfecto, pero en ciertas situaciones funciona muy bien
      Por ú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/
    • La respuesta es multiprocessing. Puedes usar el módulo multiprocessing de Python o levantar varios procesos detrás de WSGI
      Si 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 GIL
      Claro 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 general es correcto. Una respuesta técnicamente no equivocada sería algo como: “si el cuello de botella no es la CPU, la aplicación debe paralelizarse con WSGI, y no deberías tener todos esos datos en memoria, así que varios procesos de Python deberían poder manejar muchas solicitudes centradas en entrada/salida sin problema”
      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”
    • Parece una situación en la que hay una gran estructura de datos única que no es fácil de compartir entre varios procesos. Aun así, me pregunto si no se podría usar multiprocessing
      También existe la opción de mapear la estructura de datos a un archivo y hacer mmap desde varios procesos, o envolver todo en una base de datos en lugar de usar un diccionario anidado enorme
      Pero 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
    • Suena a algo centrado en entrada/salida, pero si dices que está centrado en CPU, entonces en esos casos creo que Python no es la herramienta adecuada para el trabajo. Aunque quizá se pueda aguantar con multiprocessing
  • 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

    • Es el eterno vaivén del péndulo
      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
    • Probablemente sean distintos grupos hablando en distintos momentos. Es difícil creer que quienes se entusiasmaron al principio hayan cambiado de actitud de repente y ahora lo odien
    • Cuando parece muy probable que algo fracase, es más fácil ignorar las desventajas y concentrarse en las ventajas. Como inesperadamente sí se adoptó, ahora toca evaluar ambos lados de forma más realista
    • Parece que en internet no hay una sola persona
    • Siento que los aficionados al diseño de lenguajes están metiendo este tipo de rupturas de compatibilidad hacia atrás en Python. Precisamente porque ellos no son usuarios del lenguaje
      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 getopt y optparse pasan a estar soft deprecated
    Cython 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.

    • La gran diferencia obvia es que el trabajo relacionado con GIL vs. no-GIL ocurre en segundo plano, y el desarrollador promedio de Python puede simplemente ignorarlo si quiere.
      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.
    • Antes me ganaba la vida con PHP, y mientras Python 2 tardaba una eternidad en desaparecer, PHP completó dos migraciones grandes, de 5.2 a 5.3 y luego de 5.6 a 7.0.
      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?
    • Pensaba que no-GIL solo se activaría cuando todas las bibliotecas importadas lo soportaran. Es decir, que una biblioteca tendría que estar marcada como lista para no-GIL; de lo contrario, el import lanzaría una excepción.
      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.
    • Sobre el problema de la larga cola de módulos de extensión abandonados, como dicen que planean mantener el GIL, probablemente seguirán funcionando tal cual durante años.
      Lo que preocupa es que algunas bibliotecas simplemente abandonen el soporte para GIL, pero también espero que eso no ocurra.
    • Dicen que está diseñado para que, en un build no-GIL, si importas aunque sea una extensión C que usa el GIL, vuelva automáticamente al GIL. Así que no es una situación 2→3 donde el código incompatible se rompe.
      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.

    • Todavía no fue eliminado. Se aprobó PEP 703 y ya existe un camino hacia no-GIL.
      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...
    • Hubo un anuncio de que probablemente decidirán iniciar un plan de desarrollo que, si todo sale bien, podría llevar a la eliminación del GIL más adelante.
      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”.
    • Correcto. Esta vez quise transmitir en pocas palabras la sensación de que el GIL definitivamente será eliminado. Pero soy francés y no encontré una mejor expresión.
      “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.
    • Usar el pasado en el título se pasó de la raya: es clickbait.
  • 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.

    • El gran problema está en la última etapa de “dejen que el desarrollador elija”, cuando se empuja no-GIL como única opción.
      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-run podrí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 antes npx en 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.

    • Lamentablemente, hay muchísimos desarrolladores que no saben o no les importa la eficiencia. Ejecutan sin pensarlo comandos que consumen muchos recursos, y a veces ni siquiera se les ocurre que eso está pasando.
      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. Ahora optparse está 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.

    • Si mal no recuerdo, optparse estaba 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.
    • Intenté que me gustara argparse, pero es demasiado complejo. Cada vez que lo uso siempre tengo que volver a leer la documentación.
      getopt tiene su propia simplicidad brutal.
  • ¿Por qué la comunidad de Python no eliminó el GIL al pasar de Python 2 a Python 3?

    • Porque en la época de la migración de 2→3 el paralelismo no se consideraba tan importante como hoy.
    • Porque recién hace unos años apareció alguien lo bastante inteligente y motivado como para hacerlo.