Qué cambia con el fin de vida de Python 3.10
La guía del desarrollador de Python publica el calendario de cada rama. Python 3.10, lanzada en octubre de 2021, figura allí como fin de vida desde el 1 de octubre de 2026. En la práctica, la rama ya no recibe ninguna corrección, ni siquiera de seguridad: un fallo descubierto en el intérprete o en su biblioteca estándar seguirá abierto.
Su código sigue funcionando y sus pruebas pasan, y eso es lo que hace engañosa la situación. El fin de vida no rompe nada el mismo día. Retira la red de seguridad para todos los días siguientes, mientras las distribuciones, las imágenes base y las bibliotecas de terceros se desentienden poco a poco.
Lo que cuesta un intérprete congelado
- Fallos sin corrección — toda vulnerabilidad del intérprete publicada tras el fin de vida sigue abierta en la rama 3.10; solo una subida de versión la cierra.
- Dependencias que se descuelgan — tarde o temprano las bibliotecas suben su versión mínima de Python; una instalación nueva acaba resolviendo versiones antiguas, o fallando.
- Imágenes base congeladas — una imagen de contenedor construida sobre la rama 3.10 descansa en un intérprete sin correcciones de origen, y sus análisis de vulnerabilidades lo señalan en cada build.
- Auditorías bloqueadas — un intérprete sin soporte es una línea roja en las revisiones de seguridad y los cuestionarios de proveedores de sus clientes.
- Migración forzada — de 3.10 a 3.12 hay dos versiones menores de diferencia; cuanto más espere, más larga la lista de roturas, y un proyecto planificado cuesta menos que una urgencia tras un incidente.
Calendario de soporte de las ramas de Python
Desplace la tabla
| Versión | Fin de soporte | Lectura para su VPS |
|---|---|---|
| 3.9 | octubre de 2025 | Sin soporte desde hace más de un año: abandónela sin esperar |
| 3.10 | octubre de 2026 | Sin soporte desde el 1 de octubre: ninguna corrección más |
| 3.11 | octubre de 2027 | Escalón mínimo: resuelve el problema de hoy, no el del año próximo |
| 3.12 | octubre de 2028 | Destino recomendado: dos años de margen y un ecosistema asentado |
| 3.13 | octubre de 2029 | Con soporte aún más largo; adóptela cuando sus dependencias estén validadas |
| 3.14 | octubre de 2030 | Rama más reciente de esta tabla, con el fin de soporte más lejano |
Qué versión de destino elegir
Python 3.12 es el destino que recomendamos para una aplicación existente: su fin de soporte está previsto para octubre de 2028, dos años de margen, y los frameworks habituales la admiten. Python 3.11 es un escalón mínimo que le devuelve a la misma migración en octubre de 2027.
Python 3.13 tiene soporte aún más largo, hasta octubre de 2029. No la consideramos inadecuada: solo supone que cada una de sus dependencias publique paquetes para esa versión, algo que se comprueba en su entorno de pruebas. Recordatorio de la guía oficial: las fechas de las ramas aún activas son planificadas y pueden ajustarse, solo las de 3.9 y 3.10 están cerradas. Vuelva a leer la página antes de fijar su calendario.
Saber qué intérprete sirve realmente la producción
python3 --version en su shell no dice qué intérprete sirve la aplicación. El servicio puede arrancar un entorno virtual creado hace años, un Python compilado a mano o una imagen congelada en un FROM python:3.10.
Inventaríe cada fuente: la línea ExecStart de la unidad systemd (systemctl cat miapp), el resultado de /srv/miapp/venv/bin/python --version, un grep -rn "FROM python" . sobre sus Dockerfiles, el campo requires-python de pyproject.toml y la versión que usa su CI. Anote también los workers y las tareas programadas: suelen tener su propio entorno y se quedan con el intérprete antiguo mucho después del sitio.
Requisitos antes de empezar
Esta migración no exige una máquina especial, pero sí acceso completo al servidor y sitio para dos entornos en paralelo: el segundo ocupa tanto disco como el primero, más una caché de compilación si alguna dependencia no tiene paquete precompilado.
Necesita un archivo de dependencias bloqueado con versiones exactas, una suite de pruebas ejecutable fuera de producción, una copia reciente de la base de datos y acceso root para instalar, si sus dependencias compiladas lo exigen, un compilador y las cabeceras de desarrollo. Una primera pasada en un VPS de pruebas, aunque sea efímero, vale más que un ensayo en el servidor de producción.
Migrar sin interrumpir el servicio
Congelar lo existente
Desde el entorno actual, exporte las versiones exactas:
/srv/miapp/venv/bin/pip freeze > requirements.lock.txt. Este archivo describe con precisión lo que funciona hoy; es su punto de retorno. Versiónelo antes de continuar.Instalar Python 3.12 sin tocar el sistema
uv python install 3.12descarga un intérprete 3.12 gestionado por uv, sin sustituir elpython3de la distribución, del que dependen sus propias herramientas. Si prefiere los paquetes de su distribución, instale la rama 3.12 desde un repositorio que la ofrezca y usepython3.12 -m venv.Crear el segundo entorno junto al primero
uv venv --python 3.12 /srv/miapp/venv312crea el entorno nuevo; sin uv,python3.12 -m venv /srv/miapp/venv312hace el mismo trabajo. El antiguo sigue sirviendo el tráfico: nada se modifica. Un entorno virtual no se mueve, por eso el nuevo lleva otro nombre.Reinstalar desde el archivo bloqueado
VIRTUAL_ENV=/srv/miapp/venv312 uv pip sync requirements.lock.txtinstala exactamente las versiones listadas. Con pip:/srv/miapp/venv312/bin/pip install -r requirements.lock.txt. Si una versión fijada no existe para 3.12 o se niega a compilar, la instalación se detiene aquí: es el objetivo, descubre la rotura antes de producción. Anote cada paquete que haya que subir.Volver a ejecutar la suite de pruebas
Lance las pruebas con el intérprete nuevo:
/srv/miapp/venv312/bin/python -m pytest, o/srv/miapp/venv312/bin/python manage.py testpara Django, precedido demanage.py check. Lea el resumen de advertencias de obsolescencia: anuncian las roturas de la versión siguiente. Las pruebas deben pasar con los dos intérpretes antes de continuar.Subir solo lo que se rompió
Liste los paquetes implicados, súbalos uno a uno y regenere el archivo bloqueado desde el entorno nuevo:
/srv/miapp/venv312/bin/pip freeze > requirements.312.txt. Conserve las dos listas, una por intérprete, mientras dure el cambio.Arrancar la aplicación en paralelo en un puerto local
Lánzela con el entorno nuevo en un puerto libre, por ejemplo
/srv/miapp/venv312/bin/gunicorn miapp.wsgi --bind 127.0.0.1:8001(ouvicornpara FastAPI). Envíele peticiones reales concurl: página de inicio, inicio de sesión, una escritura en base de datos. El tráfico público sigue en el proceso antiguo; solo un resultado idéntico justifica pasar al cambio.
Cambiar y volver atrás en una línea
El cambio debe modificar una sola ruta. Haga que la unidad systemd apunte a un enlace en lugar de al propio entorno: ExecStart=/srv/miapp/venv-activo/bin/gunicorn miapp.wsgi --bind 127.0.0.1:8000. Cree primero el enlace al estado actual con ln -sfn /srv/miapp/venv /srv/miapp/venv-activo, luego systemctl daemon-reload y un reinicio que no cambia nada.
El cambio a 3.12 pasa a ser ln -sfn /srv/miapp/venv312 /srv/miapp/venv-activo && systemctl restart miapp, y la vuelta atrás es la misma línea con la ruta antigua. Para una aplicación en contenedor, la palanca es la imagen base: pase de FROM python:3.10-slim a FROM python:3.12-slim, reconstruya con docker compose up -d --build y vuelva atrás restableciendo la línea anterior.
En ambos casos, conserve el entorno o la imagen antiguos unos días y siga los registros con journalctl -u miapp -f durante la primera hora.
Los puntos de rotura que hay que revisar
Entre 3.10 y 3.12, la rotura rara vez viene de su código y casi siempre de lo que lo rodea. Vuelven tres familias: los módulos retirados de la biblioteca estándar, las dependencias compiladas y la versión mínima de sus frameworks. Las notas de versión de Python 3.12 listan lo que desaparece; léalas con su archivo bloqueado abierto al lado.
Lista de control antes del cambio
- Módulos retirados — Python 3.12 elimina
distutils,asynchat,asyncoreeimp;grep -rnE "distutils|asyncore|asynchat|import imp" .localiza sus usos directos, la lista de dependencias revela los indirectos. - setuptools ya no viene preinstalado — los entornos creados con
venvya no lo incluyen en 3.12; un paquete que importapkg_resourcessin declararlo falla hasta que ejecutepip install setuptools. - Paquetes precompilados — las dependencias compiladas (controladores de base de datos, tratamiento de imagen, criptografía) deben publicar un paquete para 3.12;
pip install --only-binary=:all: -r requirements.lock.txtfalla rápido en lugar de lanzar una compilación. - Versiones mínimas de los frameworks — Django 4.2 solo admite Python 3.12 a partir de la 4.2.8, y Django 5.0 y las siguientes lo aceptan; para FastAPI, relea los classifiers de Python de la versión exacta que fije.
- Advertencias de obsolescencia —
datetime.utcnow()ydatetime.utcfromtimestamp()están obsoletas en 3.12; sustitúyalas pordatetime.now(datetime.UTC)ydatetime.fromtimestamp(ts, datetime.UTC). - Bibliotecas del sistema compartidas — el entorno paralelo aísla los paquetes de Python, no las bibliotecas del sistema operativo (el cliente de PostgreSQL, por ejemplo): anótelas, una actualización del sistema puede cambiarlas bajo sus pies.
Solución de problemas: los errores que se repiten
Estos mensajes aparecen desde la primera ejecución de las pruebas con Python 3.12. Cada uno tiene una causa conocida y un remedio corto.
Mensaje observado y remedio
- ModuleNotFoundError: No module named 'distutils' — el módulo salió de la biblioteca estándar. Instale
setuptoolsen el entorno, que sigue ofreciendo una versión, y luego suba la dependencia culpable. - ModuleNotFoundError: No module named 'imp' —
impestá retirado yimportliblo sustituye. Si la importación viene de un paquete de terceros, suba ese paquete a una versión que ya no lo use. - ModuleNotFoundError: No module named 'pkg_resources' — setuptools ya no se instala de oficio en los entornos venv de 3.12.
pip install setuptoolsrestablece el módulo hasta que el paquete culpable prescinda de él. - DeprecationWarning: datetime.datetime.utcnow() is deprecated — no es un error sino una señal: sustituya la llamada por un objeto con zona horaria.
- Failed building wheel for <paquete> — este paquete no tiene versión precompilada para 3.12 y pip intenta compilarlo. Súbalo, o instale compilador y cabeceras de desarrollo si no es posible subirlo.
Fijar el destino, no solo migrar
Aproveche la migración para fijar el intérprete en lugar de padecerlo: requires-python = ">=3.12" en pyproject.toml, una etiqueta de imagen precisa en lugar de python:3 y la versión de Python en la matriz de su CI. Añada 3.13 a esa matriz como simple observación: sabrá de antemano si la próxima subida será sencilla.
Hacer de la subida de versión un gesto habitual
Una rama de Python llega al fin de vida cada año, en octubre. El método descrito aquí (entorno paralelo, archivo bloqueado, pruebas, cambio reversible) no depende de ninguna versión concreta: se repite tal cual para 3.11 en octubre de 2027 y para 3.12 en octubre de 2028, según el calendario actual.
Si su aplicación aún no está en un servidor cuyo intérprete controle, nuestra guía de despliegue de Django en un VPS cubre el primer despliegue. Las guías sobre el fin de vida de Node.js y de PHP siguen la misma lógica para los demás runtimes de un parque.