El módulo linecache permite acceder eficientemente a líneas concretas de archivos de texto, con soporte especial para código fuente Python. Herramientas como traceback, debuggers, inspectores y analizadores lo utilizan para recuperar la línea asociada a un frame sin abrir y recorrer el archivo completo en cada consulta.
La API es pequeña, pero su comportamiento incluye un cache global del proceso, invalidación, reglas de encoding y soporte para módulos cargados mediante import hooks. Comprender estos detalles ayuda a construir diagnósticos, navegadores de código, editores y sistemas de informes más fiables.
Lee una línea
La función principal es linecache.getline(filename, lineno). La numeración comienza en 1.
import linecache
linea = linecache.getline("aplicacion.py", 12)
print(linea)
Un resultado correcto suele incluir la nueva línea final. Cuando el archivo o número no puede leerse, la función normalmente devuelve una string vacía en lugar de lanzar errores comunes de archivo.
La numeración comienza en uno
A diferencia de las listas Python, la primera línea es la línea 1. Esto coincide con tracebacks, editores, mensajes del compilador y posiciones de AST.
primera = linecache.getline("app.py", 1)
Valida números proporcionados por usuarios para evitar confusiones con cero, negativos o valores enormes.
Interpreta el resultado vacío
Una string vacía puede significar que el archivo no existe, la línea está fuera del rango, la lectura falló o el fuente no está disponible. La API está orientada a diagnósticos, donde no mostrar el fragmento es preferible a ocultar la excepción original.
Si necesitas distinguir causas, valida la ruta y lee el archivo directamente con manejo explícito de errores.
El cache global
Después de cargar un archivo, linecache mantiene información en un cache global del proceso. Las consultas posteriores evitan reabrir y recorrer el mismo archivo.
Esto resulta útil cuando un traceback necesita varias líneas. Las herramientas de larga duración deben considerar archivos desactualizados y memoria retenida.
Actualiza archivos modificados
checkcache() compara metadatos y elimina entradas aparentemente antiguas.
linecache.checkcache("aplicacion.py")
linea = linecache.getline("aplicacion.py", 12)
Sin filename, revisa las entradas apropiadas. Un editor o servidor de desarrollo puede ejecutarlo antes de mostrar fuente que quizá cambió.
Limpia el cache
clearcache() elimina todas las líneas almacenadas.
linecache.clearcache()
Es útil después de un análisis grande, al cambiar de workspace o cuando un proceso largo necesita liberar fuente.
No limpies en cada consulta
Limpiar constantemente elimina el beneficio de rendimiento. Invalida cuando exista un cambio conocido, al cerrar un proyecto o cuando las métricas muestren presión real de memoria.
Un file watcher puede llamar a checkcache() para paths modificados.
Usa module_globals cuando corresponda
getline() acepta un argumento opcional module_globals. Puede ayudar a encontrar fuente para módulos cuyo loader proporciona código sin archivo convencional.
linea = linecache.getline(
filename,
numero,
module_globals=globals_modulo,
)
Esto importa para import hooks, módulos frozen, zip imports y loaders personalizados.
Loaders y get_source
Cuando existe un loader compatible, linecache puede pedir el código al sistema de importación, normalmente mediante get_source(). Por eso un traceback puede mostrar fuente aunque el filename no sea una ruta normal.
Los loaders personalizados deberían implementar correctamente los contratos para cooperar con debuggers y diagnósticos.
Fuente dentro de ZIP
Las aplicaciones distribuidas como ZIP o archivos .pyz pueden ejecutar módulos sin extraerlos. Un loader todavía puede exponer el fuente.
Consulta zipapp en Python para crear archivos ejecutables.
Código generado
El código compilado dinámicamente puede utilizar un filename simbólico en compile(). Si no existe archivo, cache o loader asociado, linecache no podrá recuperar la línea.
Los frameworks que generan código deberían conservar un mapeo controlado entre filenames simbólicos y fuente cuando el diagnóstico lo requiera.
Tracebacks
El módulo traceback usa linecache para mostrar la línea correspondiente a cada frame.
try:
ejecutar()
except Exception:
traceback.print_exc()
Si el archivo cambia después de cargar el código, la línea mostrada puede no coincidir con el bytecode en ejecución. Esto ocurre en deploys que sustituyen archivos sin reiniciar procesos.
Mantén código y fuente alineados
Evita modificar archivos usados por un proceso vivo. El runtime ejecuta code objects antiguos mientras el diagnóstico puede leer texto nuevo.
Deploys atómicos con directorios versionados y restart controlado conservan tracebacks correctos.
Encoding de fuente Python
Los archivos Python pueden declarar encoding. Linecache coopera con la maquinaria de lectura de fuente del intérprete.
Para leer un archivo completo explícitamente, usa tokenize.open(). Consulta tokenize en Python.
Nueva línea final
Las líneas devueltas normalmente incluyen \n. Elimina solamente esa nueva línea cuando la interfaz lo necesite.
texto = linecache.getline(ruta, numero).rstrip("\n")
No uses strip() indiscriminadamente porque elimina la indentación significativa.
Conserva la indentación
El whitespace muestra la estructura de bloques y afecta la sintaxis. Eliminarlo dificulta el diagnóstico y desalineará marcadores de columna.
Mantén la línea original y renderiza indicadores por separado.
Marcadores de columna
Linecache recupera texto, pero no interpreta offsets. SyntaxError, tokens y AST pueden proporcionar posiciones.
linea = linecache.getline(error.filename, error.lineno)
marcador = " " * (error.offset - 1) + "^"
Tabs, Unicode y offsets en bytes requieren cuidado. Usa tokenizer o AST para integración precisa con editores.
Muestra contexto cercano
Un diagnóstico puede incluir líneas antes y después.
def contexto(ruta, linea, radio=2):
inicio = max(1, linea - radio)
fin = linea + radio
return [
(numero, linecache.getline(ruta, numero))
for numero in range(inicio, fin + 1)
]
Detén la salida al obtener valores vacíos más allá del archivo y limita el radio para reducir ruido.
Diagnósticos más claros
Linters y validadores pueden combinar filename, línea, columna, mensaje y fragmento.
config.py:18:7: valor inválido
timeout = -1
^No copies archivos completos a logs. Un contexto corto es más legible y seguro.
Seguridad de rutas
No aceptes una ruta arbitraria de usuario y la pases directamente a linecache. La función puede leer archivos accesibles por el proceso.
Restringe consultas a un workspace conocido, resuelve la ruta y confirma que el resultado permanece dentro de la raíz permitida.
from pathlib import Path
raiz = Path("proyecto").resolve()
objetivo = (raiz / ruta_usuario).resolve()
if objetivo != raiz and raiz not in objetivo.parents:
raise ValueError("archivo fuera del proyecto")
Considera symlinks, permisos y cambios entre validación y lectura.
Privacidad
Las líneas de código pueden contener endpoints internos, nombres de clientes, consultas o secretos mal almacenados. Trata los fragmentos como datos sensibles.
Sanitiza informes enviados a servicios externos y aplica controles de acceso y retención.
Concurrencia
El cache es global al proceso. Las threads pueden consultar, pero invalidación y fuente mutable implican que el resultado no es un snapshot transaccional.
Para análisis consistente, lee el archivo una vez y conserva una copia inmutable propia.
Procesos separados
Cada proceso tiene su cache. Limpiar en el padre no afecta a los workers.
Los analizadores distribuidos deberían devolver diagnósticos compactos en lugar de asumir fuente compartida.
Uso de memoria
Analizar miles de archivos puede retener mucho texto. Llama a clearcache() al terminar un repositorio o lote.
Mide antes de optimizar: para tracebacks normales, el cache puede ser pequeño y útil.
Archivos enormes
Linecache está diseñado para fuente, no para consultas aleatorias sobre datasets gigantes. La carga puede mantener una lista completa de líneas.
Para logs enormes usa índices, seek, mmap o formatos de acceso aleatorio.
Archivos temporales
Si un archivo temporal se elimina después de entrar al cache, las consultas pueden seguir devolviendo líneas antiguas hasta una invalidación.
Esto puede preservar un traceback tardío, pero también confundir una herramienta que espera el filesystem actual.
Limitaciones de metadatos
La validación se basa en metadatos disponibles. Filesystems con resolución temporal baja, sincronización de red o sustituciones rápidas pueden producir casos inesperados.
Cuando la identidad exacta importa, usa paths de build inmutables o hashes en tu propia capa.
Notebooks y código interactivo
Los entornos interactivos generan filenames simbólicos y mantienen caches adicionales. Un nombre entre signos de menor y mayor puede no representar un archivo real.
Trata la disponibilidad del fuente como opcional en notebooks, REPLs y funciones dinámicas.
Integración con inspect
inspect.getsource() también depende de información de archivo y cache para recuperar funciones y clases.
Builtins, extensiones nativas y código generado pueden no tener fuente. Maneja la ausencia como resultado normal.
Integración con faulthandler
faulthandler produce pilas mínimas durante fallos graves. Después, las herramientas pueden usar linecache para enriquecer filenames y líneas cuando el fuente correspondiente existe.
Consulta faulthandler en Python.
Pruebas
Prueba primera y última línea, archivo ausente, número fuera del rango, encoding no UTF, archivo modificado, cache limpio, loader personalizado, módulo ZIP y ruta hostil.
Usa archivos temporales controlados y limpia el cache entre tests para evitar dependencia de orden.
No dependas de internals
El diccionario interno de cache existe, pero su formato exacto no es una API estable. Prefiere getline(), checkcache() y clearcache().
Aísla cualquier integración con fuente virtual y pruébala en cada versión soportada.
Cuándo leer directamente
Usa Path.read_text() o tokenize.open() cuando necesites todo el archivo, errores explícitos, snapshot inmutable o control completo de encoding.
Usa linecache para recuperar rápidamente una línea de diagnóstico.
Errores comunes
Los fallos frecuentes son usar índices desde cero, interpretar todo resultado vacío como línea vacía real, eliminar indentación, olvidar cache antiguo, aceptar rutas arbitrarias, usar el módulo para datos enormes y asumir que el fuente actual coincide con el código ejecutado.
Conclusión
linecache es una pieza pequeña pero esencial del diagnóstico de Python. Usa getline() para líneas puntuales, checkcache() cuando los archivos cambian y clearcache() después de lotes grandes.
Conserva indentación, protege paths y no trates el cache global como snapshot inmutable. Consulta la documentación oficial de linecache y tokenize en Python.







