Depuradores, formateadores de excepciones, analizadores estáticos, editores y sistemas de observabilidad necesitan recuperar líneas concretas del código fuente sin abrir y procesar repetidamente el mismo archivo. El módulo linecache en Python proporciona esta infraestructura: recibe un nombre de archivo y un número de línea iniciado en 1, devuelve el texto correspondiente y conserva el contenido en una caché interna para acelerar consultas posteriores.
En esta guía aprenderás a usar getline(), actualizar entradas con checkcache(), liberar memoria con clearcache(), trabajar con encodings, loaders, módulos congelados y fuentes virtuales. El tema complementa nuestros artículos sobre traceback en Python, tokenize, inspect, pathlib y filecmp.
Qué problema resuelve linecache
Una solución directa consiste en leer todo el archivo y seleccionar una posición:
from pathlib import Path
lineas = Path("app.py").read_text(encoding="utf-8").splitlines(True)
linea = lineas[24]El enfoque funciona para una operación aislada, pero repite trabajo cuando muchas consultas apuntan al mismo archivo. También obliga a decidir cómo tratar encodings, archivos ausentes, números fuera del rango y módulos cuya fuente proviene de un importador. Linecache centraliza estas decisiones y el módulo traceback lo usa para mostrar la línea asociada a una excepción.
Primer uso de getline()
La función principal es linecache.getline(filename, lineno).
import linecache
texto = linecache.getline("app.py", 10)
print(texto, end="")Los números comienzan en 1, como en los editores y tracebacks. Cuando la línea existe, normalmente incluye el salto final. El parámetro end="" evita que print() añada otro.
Comportamiento ante errores
getline() no propaga la mayoría de los errores de lectura. Si el archivo no existe, la línea está fuera del rango o la fuente no puede recuperarse, devuelve una cadena vacía.
linea = linecache.getline("ausente.py", 3)
if linea == "":
print("línea no disponible")Esta tolerancia es útil en diagnósticos: una falla al mostrar el código no debe ocultar la excepción principal. Cuando una aplicación necesita diferenciar permisos, ruta inexistente o encoding inválido, debe validar el archivo por separado.
Línea en blanco frente a fallo
Una línea real que solo contiene un salto se devuelve como "\n"; una consulta fallida devuelve "".
if linea == "":
print("no encontrada")
elif linea == "\n":
print("línea vacía")La diferencia ayuda, pero linecache no es una API completa de auditoría del sistema de archivos.
Cómo ayuda la caché
Después de cargar una fuente, las consultas posteriores pueden reutilizarla.
for numero in range(100, 111):
print(linecache.getline("modulo_grande.py", numero), end="")El patrón es eficiente para mostrar contexto alrededor de una excepción, crear previews o resolver muchas referencias a un mismo módulo.
Entradas desactualizadas
Si el archivo cambia después de la primera lectura, las líneas almacenadas pueden seguir representando la versión anterior. Llama a checkcache() cuando necesites datos recientes.
linecache.checkcache("app.py")
actual = linecache.getline("app.py", 10)La función comprueba metadatos y descarta entradas que ya no parecen válidas. La siguiente consulta vuelve a cargar el archivo.
Comprobar toda la caché
Sin argumentos, checkcache() revisa todas las entradas.
linecache.checkcache()Puede ser adecuado para un editor que observa muchos archivos. En servidores grandes conviene comprobar solamente la fuente relevante.
Limpiar con clearcache()
clearcache() elimina todas las líneas guardadas.
linecache.clearcache()Úsalo después de un análisis masivo, entre pruebas aisladas o cuando muchos archivos grandes ya no sean necesarios. No elimina archivos ni descarga módulos.
Encoding del código fuente
Linecache abre archivos mediante tokenize.open(). Esta función detecta la declaración de encoding reconocida por Python y usa UTF-8 cuando no existe una declaración.
# -*- coding: latin-1 -*-
mensaje = "hola"La documentación oficial de linecache explica que la detección utiliza tokenize.detect_encoding(). Para texto que no es código Python, puede ser mejor abrirlo directamente con el encoding definido por la aplicación.
Integración con traceback
Un traceback contiene nombre de archivo y número de línea. Linecache proporciona el texto que aparece en el informe.
import traceback
try:
resultado = 10 / 0
except ZeroDivisionError:
print(traceback.format_exc())Los sistemas personalizados pueden recuperar varias líneas cercanas:
def contexto(filename, lineno, radio=2):
inicio = max(1, lineno - radio)
final = lineno + radio
return [
(n, linecache.getline(filename, n))
for n in range(inicio, final + 1)
]Números inválidos
Cero, valores negativos y números mayores que la cantidad de líneas devuelven cadena vacía.
assert linecache.getline("app.py", 0) == ""
assert linecache.getline("app.py", -1) == ""Valida entradas externas para devolver un mensaje más útil.
Rutas relativas
Cuando una ruta relativa no se encuentra directamente, linecache puede buscarla utilizando entradas de sys.path.
linea = linecache.getline("mi_paquete/modulo.py", 5)En archivos de aplicación, las rutas absolutas suelen ser más previsibles. Resuelve una base conocida antes de consultar código fuera del sistema de importación.
Fuentes proporcionadas por loaders
No todos los módulos corresponden a archivos normales. Un import loader puede implementar get_source(). Si se proporciona module_globals con un loader compatible, linecache puede solicitar el texto mediante esa interfaz.
linea = linecache.getline(
nombre_virtual,
12,
module_globals=modulo.__dict__,
)Esto soporta importadores personalizados, paquetes comprimidos, módulos generados y runtimes especializados.
lazycache()
lazycache(filename, module_globals) guarda información suficiente para obtener la fuente más tarde, sin leerla inmediatamente y sin conservar todo el diccionario global.
linecache.lazycache(nombre_virtual, modulo.__dict__)
texto = linecache.getline(nombre_virtual, 20)Es útil cuando un componente conoce el contexto del módulo, pero otro realizará la consulta posteriormente.
Módulos congelados en Python 3.14
Python 3.14 añadió soporte para nombres que comienzan con <frozen . Si los globals incluyen __file__, linecache intenta localizar la fuente real.
linea = linecache.getline(
"<frozen mi_modulo>",
8,
module_globals=globals_modulo,
)La mejora produce diagnósticos más útiles en aplicaciones congeladas o embebidas.
Código generado
Cuando se compila código con un nombre virtual, linecache no conoce automáticamente el texto original.
fuente = "def calcular():\n return 42\n"
codigo = compile(fuente, "<generado>", "exec")Los frameworks pueden proporcionar un loader o gestionar cuidadosamente entradas de fuente. Modificar directamente linecache.cache depende de detalles internos y debe quedar aislado detrás de un adaptador probado.
Seguridad de rutas externas
No aceptes una ruta arbitraria de un usuario y devuelvas líneas del servidor. Podrías exponer código, configuraciones o secretos.
from pathlib import Path
BASE = Path("/srv/app/fuentes").resolve()
solicitada = (BASE / nombre).resolve()
if solicitada != BASE and BASE not in solicitada.parents:
raise ValueError("ruta fuera del directorio permitido")Aplica autenticación, autorización, extensiones permitidas y redacción de respuestas.
Symlinks
Buscar solamente .. no es suficiente cuando existen enlaces simbólicos. Resuelve la base y la ruta solicitada antes de compararlas. En entornos de alta seguridad pueden ser necesarias protecciones específicas del sistema operativo contra condiciones de carrera.
Concurrencia y consistencia
Linecache no ofrece una transacción entre la comprobación y la lectura. El archivo puede cambiar después de checkcache() y antes de getline().
Cuando necesitas una vista exacta, lee una vez y trabaja sobre un snapshot local. Linecache está optimizado para diagnóstico conveniente.
Uso de memoria
Un proceso que consulta muchos archivos grandes puede retener más memoria de la esperada. Llama a clearcache() después de tareas puntuales y considera una caché limitada cuando el servicio funciona durante mucho tiempo.
Pruebas de actualización
def test_linea_actualizada(tmp_path):
archivo = tmp_path / "ejemplo.py"
archivo.write_text("a = 1\n", encoding="utf-8")
assert linecache.getline(str(archivo), 1) == "a = 1\n"
archivo.write_text("a = 2\n", encoding="utf-8")
linecache.checkcache(str(archivo))
assert linecache.getline(str(archivo), 1) == "a = 2\n"
linecache.clearcache()Limpia el estado compartido para que las pruebas no se afecten entre sí.
Linecache frente a lectura directa
Usa linecache para acceso repetido y aleatorio por número de línea, especialmente en tracebacks y analizadores. Usa open() o Path.read_text() cuando proceses todo el archivo, necesites errores detallados, locking o reglas propias de decodificación.
Editores y linters
Un linter puede guardar solamente archivo, línea y columna para cada diagnóstico, y recuperar el texto al generar el informe. Después de que el editor guarda un archivo, la integración debe llamar a checkcache(filename) antes de mostrar resultados actualizados.
Observabilidad en producción
Los informes antiguos pueden corresponder a una versión diferente del código presente en disco. Registra la revisión de despliegue y evita asumir que la fuente actual produjo la excepción. Para informes duraderos, captura un fragmento controlado en el momento del error o enlaza el stack trace con la revisión exacta.
Errores frecuentes
- Usar índice iniciado en cero.
- Confundir cadena vacía con línea en blanco.
- Esperar actualización automática después de guardar.
- Mantener miles de archivos en caché indefinidamente.
- Depender del directorio de trabajo.
- Exponer rutas arbitrarias en una API.
- Suponer que todo módulo tiene un archivo físico.
- Depender de la representación privada de la caché.
Buenas prácticas
- Usa
getline()para recuperación tolerante. - Valida rutas y números externos.
- Llama a
checkcache(filename)después de cambios. - Limpia la caché tras análisis extensos.
- Pasa globals para fuentes administradas por loaders.
- Trata explícitamente el retorno vacío.
- Usa snapshots cuando necesites consistencia exacta.
- Prueba módulos virtuales y versiones soportadas.
Conclusión
El módulo linecache en Python es una pieza pequeña pero importante de la infraestructura de diagnóstico. Recupera líneas por número, aplica las reglas de encoding de Python, reutiliza contenido almacenado y coopera con loaders y módulos congelados.
Usado correctamente, simplifica tracebacks, previews, linters y depuradores. Recuerda que su API es tolerante: invalida entradas antiguas, valida datos externos, controla la memoria en procesos largos y utiliza lectura directa cuando necesites errores detallados o consistencia estricta.






