Capítulo 6 — Manejo de cambios incrementales
Commit:
7cddb8c— fix: account for modifications, added test functions (Shiven Mian, 2 febrero 2024)
¿Qué se arregló?
Este es el primer Pull Request de la comunidad. Shiven Mian identificó un problema crítico: el índice _.aifs no manejaba correctamente los cambios en los archivos.
El problema anterior
En la implementación original de search():
# ANTES — Código simplificado
index = {}
if not os.path.exists(path_to_index):
index = index_directory(path)
# Guardaba el índice aquí
else:
with open(path_to_index, 'r') as f:
index = json.load(f)
# Re-indexaba modificados, pero...
for file_path, file_index in index.items():
if os.path.getmtime(file_path) != file_index["last_modified"]:
new_file_index = index_file(file_path)
index[file_path] = new_file_index
# ¿Y los archivos eliminados? ¿Y los archivos nuevos?Tres escenarios no manejados:
- 📁 Archivo nuevo creado → no se indexaba hasta el próximo reinicio
- ✏️ Archivo modificado → se re-indexaba, pero el índice no se guardaba
- 🗑️ Archivo eliminado → seguía en el índice como un fantasma
La solución: index_directory() con lógica incremental
Shiven refactorizó completamente index_directory():
def index_directory(path, existingIndex={}, indexPath=""):
index = existingIndex
deletedFiles = []
modifiedFiles = []
writeToIndex = False
# PASO 1: Detectar archivos eliminados y modificados
for file_path, file_index in index.items():
if not os.path.isfile(file_path):
deletedFiles.append(file_path)
writeToIndex = True
continue
if os.path.getmtime(file_path) != file_index["last_modified"]:
modifiedFiles.append(file_path)
new_file_index = index_file(file_path)
index[file_path] = new_file_index
writeToIndex = True
# PASO 2: Eliminar los borrados del índice
for file_path in deletedFiles:
index.pop(file_path, None)
# PASO 3: Agregar archivos nuevos
for root, _, files in os.walk(path):
for file in files:
file_path = os.path.join(root, file)
if file_path not in index or file_path in modifiedFiles:
writeToIndex = True
file_index = index_file(file_path)
index[file_path] = file_index
# PASO 4: Solo guardar si hubo cambios
if writeToIndex:
with open(indexPath, 'w') as f:
json.dump(index, f)
return indexEl detalle de writeToIndex
writeToIndex = False
# ... detectar cambios ...
if writeToIndex:
with open(indexPath, 'w') as f:
json.dump(index, f)Este flag evita escribir el archivo _.aifs si no hubo cambios. Escribir JSON al disco es costoso (sobre todo en proyectos grandes) y afecta el timestamp del directorio. Solo se escribe cuando es necesario.
last_modified como firma de cambios
last_modified = os.path.getmtime(file_path)os.path.getmtime() retorna el timestamp Unix de la última modificación. Comparar timestamps es mucho más rápido que comparar hashes de contenido.
La contrapartida: si haces touch archivo.py (cambias el timestamp sin cambiar el contenido), el archivo se re-indexa innecesariamente. Para búsqueda semántica, ese trade-off está bien.
Lección del capítulo
El caché es fácil de implementar. El caché correcto — que se invalida cuando debe — es lo difícil. Los tres casos (nuevo, modificado, eliminado) deben manejarse explícitamente.
Siguiente: Cap 07 — La opción python_docstrings_only