graphlib en Python: orden topológico

Publicado el: 27/08/2026
Tempo de leitura: 7 minutos
Close-up view of a computer screen displaying code in a software development environment.

El módulo graphlib ofrece la clase TopologicalSorter para ordenar tareas que tienen dependencias. Un orden topológico organiza los nodos de un grafo dirigido acíclico de manera que cada requisito aparezca antes del elemento que depende de él. Este patrón aparece en sistemas de build, pipelines de datos, migraciones, instalación de paquetes, generación de informes y ejecución de jobs.

El módulo no es una biblioteca general de grafos. No calcula caminos mínimos, centralidad, componentes o flujos. Su objetivo es representar dependencias y producir un orden válido, además de una API incremental que permite ejecutar en paralelo varios nodos listos.

Modelo de dependencias

TopologicalSorter recibe un mapping donde cada clave es un nodo y el valor es un iterable con sus predecesores.

from graphlib import TopologicalSorter

grafo = {
    "probar": {"instalar"},
    "empaquetar": {"probar"},
    "publicar": {"empaquetar"},
    "instalar": set(),
}

orden = tuple(TopologicalSorter(grafo).static_order())
print(orden)

La relación significa “esta tarea depende de estos predecesores”. Invertir el sentido es un error frecuente.

Nodos hashable

Los nodos deben ser hashable porque se almacenan en diccionarios y sets. Strings, números, enums y tuplas inmutables son opciones naturales.

No uses objetos mutables con hash inestable. Mantén un identificador compacto e inmutable en el grafo y guarda los metadatos en otro mapping.

Predecesores implícitos

Un predecesor citado en los valores no necesita aparecer como clave explícita. El sorter lo añade como nodo sin predecesores conocidos.

grafo = {
    "deploy": {"build"},
}

Aquí build también forma parte del grafo. La comodidad es útil, pero declarar todos los nodos mejora validación y documentación.

static_order

static_order() es la forma más simple de producir un orden completo válido.

sorter = TopologicalSorter(grafo)
for tarea in sorter.static_order():
    ejecutar(tarea)

El resultado es un iterator. Consúmelo una vez o conviértelo en tupla si necesitas reutilizar la secuencia.

Puede haber varios órdenes válidos

Cuando dos tareas son independientes, cualquiera de los dos órdenes relativos satisface el grafo. Los tests no deberían exigir una secuencia total exacta salvo que la aplicación añada otra regla determinística.

Valida la precedencia: cada predecesor debe aparecer antes que su dependiente. Ordena los nodos listos por una clave estable cuando necesites presentación reproducible.

Detección de ciclos

Un orden topológico solo existe en un grafo acíclico. Un ciclo genera CycleError.

from graphlib import CycleError, TopologicalSorter

try:
    orden = tuple(TopologicalSorter({
        "a": {"b"},
        "b": {"a"},
    }).static_order())
except CycleError as error:
    print("ciclo detectado", error)

Un ciclo significa que ninguna de sus tareas puede comenzar porque cada una espera a otra del mismo grupo.

Informe de ciclos

La excepción contiene información útil para localizar un ciclo, pero no debe tratarse como una API completa de presentación. Convierte IDs en nombres legibles y muestra una cadena clara.

Un grafo puede tener varios ciclos. Corregir el primero puede revelar otro, así que vuelve a validar.

Añade nodos incrementalmente

add(node, *predecessors) añade o amplía dependencias antes de preparar.

sorter = TopologicalSorter()
sorter.add("compilar", "generar_codigo")
sorter.add("probar", "compilar")
sorter.add("publicar", "probar")

Llamadas repetidas para el mismo nodo unen los predecesores. Es útil cuando plugins contribuyen con relaciones.

prepare

La API incremental comienza con prepare(). La llamada valida el grafo y habilita get_ready() y done().

sorter.prepare()

No añadas nuevas dependencias después de preparar. Construye y valida toda la configuración primero.

get_ready

get_ready() devuelve los nodos cuyos predecesores ya fueron marcados como terminados.

listas = sorter.get_ready()
for tarea in listas:
    iniciar(tarea)

Los nodos devueltos pasan a estar “en progreso”. No volverán a aparecer; el coordinador debe llamar done() al terminar.

done

done(*nodes) marca como completos nodos previamente listos.

sorter.done("generar_codigo")
nuevas = sorter.get_ready()

No marques un nodo que nunca fue devuelto por get_ready(). Las transiciones inválidas generan errores.

is_active

is_active() indica si todavía puede haber progreso: nodos listos no obtenidos o nodos en ejecución que pueden concluir.

while sorter.is_active():
    for tarea in sorter.get_ready():
        ejecutar(tarea)
        sorter.done(tarea)

En ejecución paralela, las tareas concluidas suelen llegar por una queue o un conjunto de futures.

Ejecución paralela

La API incremental permite enviar todos los nodos listos a workers, esperar resultados y liberar dependientes.

from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED

sorter.prepare()
activas = {}

with ThreadPoolExecutor(max_workers=4) as executor:
    while sorter.is_active():
        for tarea in sorter.get_ready():
            activas[executor.submit(ejecutar, tarea)] = tarea

        concluidas, _ = wait(activas, return_when=FIRST_COMPLETED)
        for futuro in concluidas:
            tarea = activas.pop(futuro)
            futuro.result()
            sorter.done(tarea)

Una versión de producción también necesita políticas de error, cancelación y shutdown.

Fallos de tareas

Una tarea fallida no debe marcarse automáticamente como completa cuando sus dependientes requieren un resultado válido. El coordinador decide si detiene el pipeline, bloquea descendientes o usa un fallback documentado.

Mantén estados distintos: éxito, fallo, cancelación, skip y bloqueo no son equivalentes.

Cancelación

Al cancelar, deja de enviar nuevos nodos, solicita cancelación cooperativa y espera un plazo. No llames done() sobre tareas incompletas para vaciar el sorter.

Las rutas opcionales deben modelarse explícitamente fuera del grafo obligatorio.

Dependencias opcionales

TopologicalSorter representa precedencia obligatoria. Feature flags, condiciones de entorno y plugins opcionales requieren una etapa que construya el grafo efectivo antes de prepare().

Elimina nodos desactivados y ajusta relaciones antes de ejecutar.

Valida referencias

Como los predecesores ausentes se convierten en nodos implícitos, un typo puede crear una tarea fantasma.

declaradas = set(configuracion)
referenciadas = set().union(*configuracion.values())
desconocidas = referenciadas - declaradas

Decide si permites nodos implícitos. Los pipelines configurados por usuarios suelen beneficiarse de rechazar nombres desconocidos.

Separa identidad y ejecución

Guarda IDs de tareas en el grafo y funciones en otro mapping.

tareas = {
    "extraer": lambda: extraer(datos),
    "transformar": transformar,
    "cargar": cargar,
}

Esto mantiene el grafo serializable, mejora logs y evita ejecutar código durante validación.

Side effects e idempotencia

Un orden válido no protege frente a escrituras parciales. Las tareas que modifican archivos, bases o APIs necesitan idempotencia, transacciones y rollback.

Al reanudar un pipeline, no supongas que un log de inicio significa éxito. Persiste checkpoints después de un resultado verificable.

Prioridades

El módulo no prioriza nodos listos simultáneamente. El coordinador puede ordenar la tupla devuelta por get_ready().

for tarea in sorted(sorter.get_ready(), key=prioridad):
    iniciar(tarea)

La prioridad elige entre nodos liberados, pero no puede violar dependencias.

Límites de concurrencia

No todas las tareas listas deben comenzar inmediatamente. Respeta workers, pool de base, memoria, ancho de banda y rate limits.

Mantén una queue local o envía solo hasta la capacidad disponible. Consulta concurrent.futures en Python para backpressure.

Clases de recursos

Algunas tareas consumen CPU, otras base o red. Un único límite global puede ser inadecuado. Usa executors o semáforos separados por recurso.

El grafo describe precedencia; el scheduler describe capacidad.

Estado persistente

TopologicalSorter no es un motor durable. Un reinicio pierde el progreso en memoria.

Los pipelines críticos deben persistir grafo, intentos, outputs, timestamps y estados. Reconstruye el sorter solo para calcular qué tareas pueden avanzar.

Grafos grandes

La ordenación suele escalar con nodos y aristas, pero la representación y memoria importan. Evita duplicar estructuras grandes.

Usa IDs compactos y límites antes de aceptar configuración externa.

Entrada no confiable

Un usuario puede enviar millones de aristas, nombres enormes o fan-in extremo y consumir CPU y memoria.

Limita nodos, aristas, longitud de IDs y tiempo de preparación. No conviertas nombres directamente en imports o comandos sin allowlist.

Visualización

El módulo no genera diagramas. Exporta aristas a DOT, JSON o tabla. Destaca ciclos, tareas bloqueadas y nodos finales.

La visualización es especialmente útil cuando plugins añaden dependencias automáticamente.

Pruebas

Prueba grafo vacío, un nodo, cadena lineal, branches independientes, convergencia, varios órdenes válidos, ciclos, referencias desconocidas, fallos, cancelación y finalización paralela.

Cuando el orden no sea único, verifica restricciones de precedencia en vez de una lista exacta.

Errores comunes

Los fallos frecuentes son invertir dependencias, exigir un único orden, olvidar done(), marcar fallos como completos, añadir nodos tras preparar, aceptar typos como tareas, lanzar trabajo ilimitado y tratar el sorter como sistema persistente.

Conclusión

graphlib.TopologicalSorter resuelve la ordenación de dependencias y ofrece una interfaz incremental adecuada para schedulers paralelos. Modela correctamente los predecesores, valida ciclos y nombres, limita concurrencia y separa el estado de ejecución del grafo.

Consulta la documentación oficial de graphlib y multiprocessing en Python cuando las tareas necesiten procesos.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    faulthandler en Python: diagnostica fallos

    Aprende faulthandler en Python para diagnosticar crashes, deadlocks, señales fatales, timeouts y bloqueos con dumps de todas las threads.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    A detailed image of a reticulated python showcasing its patterned scales and intricate skin texture.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    symtable en Python: analiza scopes

    Aprende symtable en Python para analizar scopes, locals, globals, parámetros, imports, nonlocals, closures y namespaces del compilador.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    dis en Python: entiende el bytecode

    Aprende dis en Python para inspeccionar bytecode, jumps, stack effects, caches adaptativos y optimizaciones sin depender de internals inestables.

    Ler mais

    Tempo de leitura: 5 minutos
    27/08/2026
    Gold Bitcoin coins displayed on a sparkling gold texture, representing digital currency and finance.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tokenize en Python: lee tokens del código

    Aprende tokenize en Python para leer tokens, comentarios, encoding, indentación y posiciones, además de transformar y reconstruir código con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Side view of contemplating female assistant in casual style standing near shelves and choosing file with documents
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipapp en Python: crea archivos .pyz

    Aprende zipapp en Python para crear archivos .pyz, definir entry points, incluir dependencias puras, usar recursos y distribuir CLIs seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sysconfig en Python: rutas y build

    Aprende sysconfig en Python para descubrir rutas, schemes, headers, flags de build, ABI, extensiones nativas y entornos virtuales.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026