Skip to content

Capítulo 10 — Soporte Python 3.12 y dependencias opcionales

Commits: acd6ca6, 4626351, 6fbd832, aa4e5db, 0d1360b, 3f74c6a (Killian, abril-junio 2024)

¿Qué pasó?

Abril de 2024 fue un período turbulento para aifs. Python 3.12 fue lanzado y unstructured no lo soportaba todavía. Killian tomó una decisión radical: hacer todas las dependencias pesadas opcionales.

El problema: Python 3.12 + unstructured

ERROR: unstructured requires Python >=3.8, <3.12

Los usuarios que instalaban aifs con Python 3.12 obtenían un error críptico al importar. Peor: el error no decía nada sobre la versión de Python.

Intento 1: Mensaje de error específico para 3.12

python
# Commit acd6ca6
import sys

try:
    from unstructured.chunking.title import chunk_by_title
    from unstructured.partition.auto import partition
except ImportError:
    if sys.version_info.major == 3 and sys.version_info.minor == 12:
        raise ImportError(
            "The 'unstructured' package required by this project does not support Python 3.12. "
            "Please downgrade to Python 3.11 to continue."
        )
    raise ImportError("Please run 'pip install \"unstructured[all-docs]\"' to continue.")

Al menos ahora el error era útil. Pero no resolvía el problema.

Intento 2: Ampliar rango de Python en pyproject.toml

toml
# Antes:
python = ">=3.9,<3.12"

# Después (3.12 Support):
python = ">=3.8,<4"

Declarar <4 en vez de <3.12 permite que el paquete se instale en Python 3.12, pero si unstructured falla, el error sigue ahí.

Solución final: unstructured como dependencia opcional

python
# Commit 6fbd832 — La solución elegante
try:
    from unstructured.chunking.title import chunk_by_title
    from unstructured.partition.auto import partition

    def chunk_file(file_path):
        elements = partition(filename=file_path)
        chunks = chunk_by_title(elements, max_characters=MAX_CHARS_PER_CHUNK)
        return [c.text for c in chunks]

except ImportError:
    # Fallback: chunking simple por caracteres
    def chunk_file(file_path):
        with open(file_path, 'r', encoding='utf-8') as file:
            file_text = file.read()
        chunks = [file_text[i:i+MAX_CHARS_PER_CHUNK] 
                  for i in range(0, len(file_text), MAX_CHARS_PER_CHUNK)]
        return chunks

Si unstructured no está instalado (o falla porque es Python 3.12), el código usa un chunking simple: divide el texto cada 500 caracteres. Menos inteligente, pero funciona en cualquier versión.

ChromaDB también se vuelve opcional

python
# Commit aa4e5db — "Vlite?"
try:
    import chromadb
    from chromadb.utils.embedding_functions import DefaultEmbeddingFunction as setup_embed
    os.environ["TOKENIZERS_PARALLELISM"] = "false"
    embed = setup_embed()
except:
    pass  # shoulda used vlite

El comentario # shoulda used vlite es interesante — Killian estaba considerando migrar el backend de embeddings a vlite, una alternativa más ligera.

La migración nunca se completó, pero el try/except quedó como preparación.

El mensaje silenciado

python
# Commit 3f74c6a — "Random message"
# ANTES:
print("Please run 'pip install \"unstructured[all-docs]\"' to improve document-reading performance.")

# DESPUÉS:
# print("Please run 'pip install \"unstructured[all-docs]\"' to improve document-reading performance.")

Con unstructured ahora siendo opcional y teniendo un fallback silencioso, imprimir un mensaje de "falta instalar algo" cada vez que se importa aifs sería molesto. Se comenta.

La evolución de las dependencias

VersiónPythonunstructuredchromadb
0.0.1>=3.9,❤️.12requeridarequerida
0.0.12>=3.8,<4requerida (error útil)requerida
final>=3.8,<4opcional (fallback)opcional (try/except)

Lección del capítulo

Las dependencias pesadas deben ser opcionales cuando hay un fallback razonable. Un sistema que funciona al 80% es infinitamente mejor que uno que no funciona por falta de una librería.


Siguiente: Cap 11 — Estado final del proyecto

Libro generado por El Profe 🧑‍🏫