La compilación sin GIL de CPython -- python3.14t -- ya dejó de ser un experimento de fin de semana. Viene en los instaladores oficiales de python.org y en pyenv install 3.14t, y en Ubuntu se puede instalar directamente desde el PPA de deadsnakes como python3.14-nogil. Sigue marcada como experimental aguas arriba, pero ya es lo bastante estable como para que "¿la activamos?" sea una pregunta real sobre un proyecto en producción, no un juguete.
La respuesta honesta, ocho meses después de que 3.14t estuviera ampliamente disponible: depende por completo de lo que tengas en tu requirements.txt. El intérprete en sí es sólido. El ecosistema a su alrededor no está portado de forma pareja, y el modo de fallo es traicionero -- el proceso no se cae, simplemente vuelve en silencio a correr con el GIL activado, y nada te avisa a menos que lo revises explícitamente.
Cómo se reactiva el GIL sin que te enteres
Una extensión en C tiene que declarar explícitamente que soporta free-threading, llamando a PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) durante la inicialización del módulo. Si no lo hace -- porque quien la mantiene todavía no la auditó para thread-safety, o simplemente no llegó a eso -- CPython imprime un RuntimeWarning y reactiva el GIL para el resto del proceso. Cualquier hilo que lances después vuelve a comportarse con semántica de GIL, aunque sys.executable siga diciendo python3.14t.
Compruébalo con sys._is_gil_enabled(), agregado en 3.13:
import sys
import sysconfig
def estado_gil():
if not sysconfig.get_config_var("Py_GIL_DISABLED"):
return "no es una build free-threaded"
if sys._is_gil_enabled():
return "build free-threaded, pero el GIL volvio a activarse"
return "free-threaded, GIL apagado"
print(estado_gil())
Qué se rompe en la práctica, a octubre de 2026
| Librería | Wheel cp314t en PyPI | Notas |
|---|---|---|
| numpy | sí, desde la 2.3 | El soporte free-threaded llegó en serio; el protocolo de buffers y los bucles de ufunc fueron la parte difícil. |
| pandas | parcial | Las operaciones centrales de DataFrame andan bien; varias rutas aceleradas que delegan en bottleneck/numexpr todavía fuerzan el GIL de vuelta. |
| psycopg (v3) | sí | La extensión en C se reescribió pensando en free-threading; psycopg2 no, y probablemente nunca lo haga. |
| lxml | no | El estado interno de libxml2 todavía no es thread-safe aguas arriba, así que el binding tampoco puede serlo. |
| Pillow | sí, desde la 11.x | La decodificación de imágenes ya liberaba el GIL en la build normal; el modo free-threaded solo lo hizo oficial. |
| uvloop | no | Cae en silencio al loop de asyncio de la stdlib, con un warning, si intentás usarlo bajo 3.14t. |
Un script de detección para meter en CI
No te fíes de leer warnings en el scroll de la terminal. Compará sys._is_gil_enabled() antes y después de cada import que le importe a tu proyecto:
# ci_check_free_threading.py
import importlib
import sys
import sysconfig
IMPORTS_CRITICOS = ["numpy", "psycopg", "PIL", "lxml", "cryptography"]
def main() -> int:
if not sysconfig.get_config_var("Py_GIL_DISABLED"):
print("se omite: no es un interprete free-threaded")
return 0
rompieron = []
for nombre in IMPORTS_CRITICOS:
antes = sys._is_gil_enabled()
try:
importlib.import_module(nombre)
except ImportError:
continue
if antes is False and sys._is_gil_enabled() is True:
rompieron.append(nombre)
if rompieron:
print("el GIL fue reactivado por:", ", ".join(rompieron))
return 1
print("todos los imports criticos siguieron free-threaded")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Lo corremos como job nocturno no bloqueante contra python3.14t, separado de la matriz de tests en 3.13 que sí bloquea los merges. No hace fallar el build -- solo nos da una línea de tendencia de qué dependencias ya se pusieron al día, para saber cuándo vale la pena reconsiderar en serio la build sin GIL, en vez de volver a probar cada librería a mano cada pocos meses.
Dónde quedamos
Producción se queda por ahora en la build normal con GIL. El bloqueo no es el intérprete, es que pandas y un par de nuestras dependencias de procesamiento de imágenes todavía reactivan el GIL en silencio, lo que significa que cualquier código con thread-pool que escribiéramos "para free-threading" terminaría degradando igual a la contención normal del GIL -- solo que ahora de forma más silenciosa y más difícil de notar en un profiler. Cuando el script de detección dé limpio sobre las librerías que realmente usamos en producción, activarlo en staging es un cambio de una línea.
