linecache — Lectura eficiente de archivos de texto

Propósito:Recupera líneas de texto de archivos o módulos Python importados, manteniendo un caché de los resultados para hacer del leer muchas líneas del mismo archivo más eficiente.

El módulo linecache se usa en otras partes de la biblioteca estándar de Python cuando se trata con archivos fuente de Python. La implementación de la memoria caché mantiene el contenido de los archivos, analizados en líneas separadas, en memoria. La interfaz devuelve las líneas solicitadas por índice en una list, y ahorra tiempo al leer repetidamente el archivo y analizando las líneas para encontrar la deseada. Esto es especialmente útil cuando se buscan varias líneas del mismo archivo, como cuando se produce un rastreo de un informe de error.

Datos de prueba

Este texto producido por un generador de Lorem Ipsum se utiliza como entrada de muestra.

linecache_data.py
import os
import tempfile

lorem = '''Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.  Vivamus eget elit. In posuere mi non
risus. Mauris id quam posuere lectus sollicitudin
varius. Praesent at mi. Nunc eu velit. Sed augue massa,
fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur
eros pede, egestas at, ultricies ac, apellentesque eu,
tellus.

Sed sed odio sed mi luctus mollis. Integer et nulla ac augue
convallis accumsan. Ut felis. Donec lectus sapien, elementum
nec, condimentum ac, interdum non, tellus. Aenean viverra,
mauris vehicula semper porttitor, ipsum odio consectetuer
lorem, ac imperdiet eros odio a sapien. Nulla mauris tellus,
aliquam non, egestas a, nonummy et, erat. Vivamus sagittis
porttitor eros.'''


def make_tempfile():
    fd, temp_file_name = tempfile.mkstemp()
    os.close(fd)
    with open(temp_file_name, 'wt') as f:
        f.write(lorem)
    return temp_file_name


def cleanup(filename):
    os.unlink(filename)

Lectura de líneas específicas

Los números de línea de los archivos leídos por el módulo linecache empiezan con 1, pero normalmente las listas comienzan a indexar la matriz desde 0.

linecache_getline.py
import linecache
from linecache_data import *

filename = make_tempfile()

# Pick out the same line from source and cache.
# (Notice that linecache counts from 1)
print('SOURCE:')
print('{!r}'.format(lorem.split('\n')[4]))
print()
print('CACHE:')
print('{!r}'.format(linecache.getline(filename, 5)))

cleanup(filename)

Cada línea devuelta incluye una nueva línea al final.

$ python3 linecache_getline.py

SOURCE:
'fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur'

CACHE:
'fermentum id, nonummy a, nonummy sit amet, ligula. Curabitur\n'

Manipulación de líneas en blanco

El valor de retorno siempre incluye la nueva línea al final de la línea, así que si la línea está vacía, el valor de retorno es solo la nueva línea.

linecache_empty_line.py
import linecache
from linecache_data import *

filename = make_tempfile()

# Blank lines include the newline
print('BLANK : {!r}'.format(linecache.getline(filename, 8)))

cleanup(filename)

La línea ocho del archivo de entrada no contiene texto.

$ python3 linecache_empty_line.py

BLANK : '\n'

Manejo de errores

Si el número de línea solicitado cae fuera del rango de líneas válidas en el archivo, getline() devuelve una cadena vacía.

linecache_out_of_range.py
import linecache
from linecache_data import *

filename = make_tempfile()

# The cache always returns a string, and uses
# an empty string to indicate a line which does
# not exist.
not_there = linecache.getline(filename, 500)
print('NOT THERE: {!r} includes {} characters'.format(
    not_there, len(not_there)))

cleanup(filename)

El archivo de entrada solo tiene 15 líneas, por lo que solicitar la línea 500 es como tratar de leer más allá del final del archivo.

$ python3 linecache_out_of_range.py

NOT THERE: '' includes 0 characters

La lectura de un archivo que no existe se maneja de la misma manera.

linecache_missing_file.py
import linecache

# Errors are even hidden if linecache cannot find the file
no_such_file = linecache.getline(
    'this_file_does_not_exist.txt', 1,
)
print('NO FILE: {!r}'.format(no_such_file))

El módulo nunca genera una excepción cuando la persona que llama intenta leer datos.

$ python3 linecache_missing_file.py

NO FILE: ''

Lectura de archivos fuente de Python

Como linecache se usa mucho cuando se producen rastreos, una de sus características clave es la capacidad de encontrar módulos fuente de Python en la ruta de importación especificando el nombre base del módulo.

linecache_path_search.py
import linecache
import os

# Look for the linecache module, using
# the built in sys.path search.
module_line = linecache.getline('linecache.py', 3)
print('MODULE:')
print(repr(module_line))

# Look at the linecache module source directly.
file_src = linecache.__file__
if file_src.endswith('.pyc'):
    file_src = file_src[:-1]
print('\nFILE:')
with open(file_src, 'r') as f:
    file_line = f.readlines()[2]
print(repr(file_line))

El código para poblar el caché en linecache busca en sys.path el módulo nombrado si no puede encontrar un archivo con ese nombre en el directorio actual. Este ejemplo busca linecache.py. Como no hay una copia en el directorio actual, el archivo de la biblioteca estándar es encontrado en su lugar.

$ python3 linecache_path_search.py

MODULE:
'This is intended to read lines from modules imported -- hence
if a filename\n'

FILE:
'This is intended to read lines from modules imported -- hence
if a filename\n'