← Todos los artículos

Python 3.15: dos comprobaciones antes de migrar un backend

Guía conservadora para revisar lazy imports, UTF-8 y compatibilidad de dependencias al pasar un servicio backend de Python 3.14 a 3.15.

13 de octubre de 2026 · · 6 min de lectura
Servicio backend Python con un módulo de reportes cargado bajo demanda y un archivo de texto como entrada

Python 3.15.0 es una versión estable desde el 9 de octubre de 2026. El anuncio oficial destaca, entre otros cambios, los imports perezosos explícitos y UTF-8 como codificación predeterminada. Para un backend, la migración sensata no consiste en activar todo: consiste en aislar un módulo opcional, revisar dónde se omite encoding y comprobar la matriz de dependencias con Python 3.14 y 3.15.

1. Qué cambia realmente

Un lazy import no carga el módulo en la declaración. Python deja un proxy y carga el módulo cuando el nombre se usa por primera vez; por ello también puede desplazar un ModuleNotFoundError, un error de sintaxis o un efecto de inicialización desde el arranque hasta una petición concreta. No es una promesa de menor latencia: el resultado depende del grafo de imports y de las rutas que atienda el servicio.

La sintaxis nueva es explícita y solo es válida en el ámbito del módulo:

lazy import app.reportes

def healthcheck():
    return {"ok": True}

def generar_reporte(datos):
    return app.reportes.render(datos)

No coloques lazy import dentro de una función, clase o bloque try; tampoco lo uses con imports de asterisco ni con from __future__. Esas formas son restricciones sintácticas documentadas, no variantes equivalentes que puedas probar en producción.

Si el mismo archivo debe analizarse con Python 3.14 y 3.15, usa la opción de transición documentada:

__lazy_modules__ = ["app.reportes"]
import app.reportes

En 3.15 ese import puede diferirse cuando coincide exactamente con el nombre plenamente cualificado app.reportes; no es una coincidencia por prefijo y no convierte automáticamente sus submódulos hermanos ni toda la jerarquía en lazy. En intérpretes anteriores la variable no activa la nueva semántica y el import conserva su comportamiento eager. No escribas lazy import en una fuente que deba pasar por Python 3.14: el parser anterior no conoce esa sintaxis.

2. Audita el texto antes de cambiar el intérprete

Python 3.15 usa UTF-8 por defecto cuando una operación de E/S no recibe encoding y el modo UTF-8 del proceso está activo. Ese modo está habilitado por defecto en 3.15, pero puede desactivarse con PYTHONUTF8=0 o -X utf8=0; por eso la matriz debe registrar también esas variables y opciones del proceso. Por ejemplo, open("datos.txt") seguirá la política efectiva del intérprete. Eso no cambia el contrato de cada sistema externo: un archivo legado, una exportación de un proveedor o un protocolo que declara otra codificación siguen necesitando su propia política.

Clasifica cada lectura y escritura que omita encoding:

Caso Decisión prudente Comprobación acotada
Archivo propio definido como UTF-8 usar encoding="utf-8" explícito conservar á, ñ y € al leer y escribir
Archivo dependiente del entorno local documentar la dependencia o usar encoding="locale" de forma deliberada repetir con un locale distinto
HTTP, JSON o base de datos seguir la codificación del protocolo o cliente probar bytes y cabeceras reales
Dependencia externa no parchear a ciegas fijar versión y revisar soporte del mantenedor

La intención es que el código comunique su contrato. Usa encoding="locale" cuando quieras declarar deliberadamente la codificación del locale actual. No conviertas todas las operaciones en UTF-8 por sustitución mecánica si el formato externo exige otra codificación, y no confundas el default de archivos de Python con la negociación de un payload HTTP.

Una migración pequeña y repetible

Congela dos entornos separados con el mismo código, datos y conjunto de pruebas: Python 3.14 y Python 3.15.0. Registra intérprete, sistema operativo, framework, servidor ASGI/WSGI, extensiones nativas, versiones instaladas y la combinación efectiva de PYTHONUTF8 y -X utf8. Una dependencia que todavía no publica wheel o soporte para 3.15 es un bloqueo de esa dependencia, no evidencia de que el lenguaje sea incompatible en general.

Después elige un módulo opcional que no sea necesario para el health check. La observación que interesa es temporal:

  1. Importa el módulo principal y comprueba que el health check no necesita cargar app.reportes.
  2. Atiende la primera petición de reportes y comprueba que el módulo se resuelve en ese punto.
  3. Repite una segunda petición para separar el coste de carga inicial de la operación normal.
  4. Ejecuta la misma secuencia bajo 3.14 y 3.15 con la misma dependencia y datos.

No uses una aparición o ausencia en sys.modules como resultado universal: otra dependencia del proceso puede haber cargado el módulo antes. Si haces esa observación, registra el fixture y el orden de imports. Si una biblioteca registra plugins, rutas o señales como efecto lateral de importar, deja el import eager o expón una función de descubrimiento explícita; no apliques lazy con una búsqueda y reemplazo.

Para el texto, usa un archivo de prueba con á, ñ y €, y compara el contenido antes y después de la lectura/escritura con el encoding elegido. Esa prueba confirma el contrato de ese archivo; no demuestra que todas las entradas de la aplicación o todos los protocolos utilicen UTF-8.

Compatibilidad que 3.15 no resuelve por ti

La versión final del intérprete no garantiza que Flask, Django, FastAPI, el servidor ASGI, una extensión nativa o un cliente de base de datos estén listos. Comprueba sus matrices y wheels para 3.15, conserva una ruta de vuelta a 3.14 y revisa los errores que ahora pueden aparecer en la primera petición en vez del arranque.

La guía tampoco recomienda activar el modo global -X lazy_imports=all ni PYTHON_LAZY_IMPORTS=all como atajo: cambia el alcance de muchos imports y hace más difícil atribuir un fallo. Empieza por la sintaxis explícita o por __lazy_modules__ para un módulo concreto y conserva imports eager allí donde existan efectos laterales.

Checklist de aprobación

  • La suite se ejecuta en entornos separados con Python 3.14 y 3.15.0.
  • Las dependencias, wheels y extensiones nativas tienen una decisión documentada.
  • El módulo opcional conserva import eager o usa lazy de forma explícita, con sus errores diferidos cubiertos.
  • El health check, la primera petición de reportes y una segunda petición están incluidos en la misma secuencia.
  • Los archivos propios declaran encoding cuando tienen un contrato fijo.
  • Los fixtures incluyen caracteres no ASCII y no se confunde E/S de archivos con HTTP, JSON o bases de datos.
  • No se publican tiempos, porcentajes ni mejoras de rendimiento sin una medición reproducible del servicio.

Python 3.15 trae cambios útiles para una migración deliberada, no una garantía de que un backend sea compatible por cambiar la imagen base. La evidencia que importa es la matriz del proyecto, el momento en que carga cada módulo y el contrato explícito de cada archivo.

Fuentes oficiales

Comentarios

Cargando…