Skip to content

Capítulo 7 — La opción python_docstrings_only

Commits: 4975b00, 5560158, 98b80ce, 2691dd8 (Shiven Mian + Killian, enero-febrero 2024)

¿Qué se construyó?

Se reemplazó la variable de entorno AIFS_MINIMAL_PYTHON_INDEXING por un parámetro explícito python_docstrings_only=False en todas las funciones.

También se mejoró minimally_index_python_file() para parsear clases y métodos de forma más profunda.

El problema con las variables de entorno

python
# ANTES — estilo variable de entorno
use_minimal_python_method = os.getenv('AIFS_MINIMAL_PYTHON_INDEXING')
if use_minimal_python_method and use_minimal_python_method.lower() == 'true':
    return minimally_index_python_file(path)

Las variables de entorno son globales y persistentes — si configuras AIFS_MINIMAL_PYTHON_INDEXING=true, afecta a todas las llamadas a search() en ese proceso.

Lo que Killian quería era poder llamar:

python
# Busca en todo el proyecto con indexado mínimo de Python
search("función de pagos", path="./src", python_docstrings_only=True)

# Busca en documentación con indexado completo
search("tutorial de instalación", path="./docs", python_docstrings_only=False)

La cascada del parámetro

El parámetro tuvo que propagarse por toda la cadena de llamadas:

python
def search(query, path=None, max_results=5, verbose=False, python_docstrings_only=False):
    # ...
    index = index_directory(path, existingIndex=index, indexPath=path_to_index, 
                            python_docstrings_only=python_docstrings_only)

def index_directory(path, existingIndex={}, indexPath="", python_docstrings_only=False):
    # ...
    file_index = index_file(file_path, python_docstrings_only)

def index_file(file_path, python_docstrings_only=False):
    if python_docstrings_only and file_path.lower().endswith(".py"):
        return minimally_index_python_file(file_path)
    # ...

El bug sutil que se escapó

En el commit 2691dd8, había un typo:

python
# BUG — falta los paréntesis en .lower
if python_docstrings_only and path.lower.endswith(".py"):

path.lower es el método sin llamar, no un string. Nunca falla (porque .endswith en un método bound lanzaría AttributeError), sino que simplemente nunca entra al if.

El bug fue corregido en el siguiente commit por birbbit:

python
# CORRECTO
if python_docstrings_only and path.lower().endswith(".py"):

Este tipo de bug es invisible para los tests porque el test puede pasar aunque el feature no funcione (si no testeas explícitamente esa rama).

Mejora del traversal de clases

python
# ANTES — solo funciones top-level
for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef):
        chunks.append(node.name)

# DESPUÉS — funciones Y métodos de clase
def traverse(node):
    for child in ast.iter_child_nodes(node):
        if isinstance(child, ast.FunctionDef):
            class_name = node.name if isinstance(node, ast.ClassDef) else None
            docstring = ast.get_docstring(child)
            formatted_string = format_function_details(child, class_name)
            chunks.append(formatted_string)
            representations.append(docstring if docstring else child.name)
        traverse(child)

traverse(tree)

La diferencia es importante. Con ast.walk, obtienes todos los nodos pero pierdes el contexto de dónde está cada nodo. Con traverse recursivo, cuando encuentras una FunctionDef, sabes si su padre es una ClassDef.

Resultado: en vez de solo "calculate_total", el índice guarda "ShoppingCart.calculate_total".

El parámetro verbose

python
def search(query, path=None, max_results=5, verbose=False, python_docstrings_only=False):
    os.environ['LOG_VERBOSE'] = str(verbose)

def log(str):
    verbose = os.environ['LOG_VERBOSE']
    if verbose and verbose == 'True': print(str)

Nótese la implementación: verbose como parámetro se convierte en variable de entorno internamente. Es un patrón un poco inconsistente (¿por qué no pasar verbose directamente a las funciones internas?), pero funciona.

Lección del capítulo

Los parámetros de función son contratos explícitos; las variables de entorno son configuración implícita. Cuando algo debe variar por llamada, usa parámetros. Cuando es configuración de despliegue global, usa variables de entorno.


Siguiente: Cap 08 — Búsqueda por rutas de archivo

Libro generado por El Profe 🧑‍🏫