Skip to main content

A lightweight, type-safe library for building, managing, and visualizing dependency graphs.

Project description

graphable

CI License: MIT

graphable is a lightweight, type-safe Python library for building, managing, and visualizing dependency graphs. It provides a simple API for defining nodes and their relationships, performing topological sorts, and exporting graphs to various formats like Mermaid, Graphviz, and ASCII text trees.

Features

  • Type-Safe: Built with modern Python generics and type hints.
  • Topological Sorting: Easily get nodes in dependency order.
  • Cycle Detection: Built-in protection against circular dependencies.
  • Filtering & Tagging: Create subgraphs based on custom predicates or tags.
  • Graph Syncing: Automatically expand graphs to include all reachable nodes with discover().
  • Transitive Reduction: Automatically remove redundant edges while preserving reachability.
  • Reachability Analysis: Easily find ancestors or descendants of any node.
  • Node Ordering: Compare nodes using standard operators (a < b means a is an ancestor of b).
  • Container Protocols: Use Pythonic idioms like len(graph), node in graph, and for node in graph.
  • Integrity: Generate and validate deterministic BLAKE2b checksums of graph structure and metadata.
  • Parallel Sorting: Group nodes into execution layers for parallel processing with parallelized_topological_order().
  • Orchestration Ready: Track node status and duration for workflow execution.
  • Edge Attributes: Support for weights, labels, and custom metadata on relationships.
  • Diffing: Compare two graphs structurally and visually with diff() and diff_graph().
  • Dependency Algorithms: Native implementations for:
    • Critical Path Method (CPM): Identify bottlenecks based on node durations.
    • BFS & DFS: Perform breadth-first or depth-first traversals with generators.
    • Longest Path: Find the deepest chain of dependencies.
    • All Paths: Explore every possible connection between two nodes.
    • Transitive Closure: Analyze the full "blast radius" of dependencies.
    • Cycle Resolution: Get suggestions for breaking circular dependencies with suggest_cycle_breaks().
  • Advanced Slicing: Extract focused subgraphs using upstream_of(), downstream_of(), or subgraph_between().
  • Unified I/O: Easy Graph.read() and graph.write() methods with automatic format detection.
  • Parsing: Reconstruct graphs from JSON, YAML, TOML, CSV, or GraphML files and strings.
  • Clustering: Group nodes into subgraphs/clusters in visualizations based on tags.
  • Visualizations:
    • Mermaid: Generate flowchart definitions or export directly to SVG. Supports clustering and color-coded diffs.
    • Graphviz: Generate DOT definitions or export to SVG with custom styling. Supports clustering.
    • D2: Generate D2 definitions or export to SVG with modern styling and layouts. Supports clustering.
    • PlantUML: Generate component or deployment diagram definitions. Supports clustering.
    • TikZ: Generate high-quality LaTeX definitions for academic documents.
    • GraphML: Industrial-standard XML export for professional analysis tools (Gephi, yEd).
    • Interactive HTML: Generate a single, portable HTML file with zooming, panning, and Live Reload when used with the CLI.
    • JSON, YAML & TOML: Export graph structure as machine-readable data.
    • CSV: Export simple edge lists for data processing.
    • NetworkX: Seamless integration with the NetworkX library for advanced analysis.
    • Text Tree & ASCII Flowchart: Generate beautiful ASCII representations.
  • Modern Tooling: Managed with uv and just.

Installation

As a Library

Use your preferred package manager to add graphable to your project:

# Using uv (recommended)
uv add graphable

# Using pip
pip install graphable

As a Command Line Tool

To use the graphable CLI globally, we recommend using pipx. You can choose between the bare-bones version or the full-featured "Rich" version:

# Full version with beautiful terminal output (recommended)
pipx install "graphable[cli]"

# Bare-bones version (standard library only)
pipx install graphable

Command Line Interface

graphable provides a powerful CLI for analyzing and transforming graph files.

Basic Usage

# Get summary information about a graph
graphable info topology.json

# Validate a graph for cycles and consistency
graphable check topology.yaml

# Convert between any supported formats
graphable convert input.json output.mmd

# Simplify a graph using transitive reduction
graphable reduce complex.graphml simple.svg

# Compare two versions of a graph
graphable diff old.json new.json

# Generate a visual diff (added elements in green, removed in red)
graphable diff old.json new.json -o diff.svg

# Serve a live-reloading interactive visualization
graphable serve architecture.yaml

Automation & CI/CD

If you have the cli extra installed but need plain-text output for logging or automation, use the --bare flag:

graphable --bare info topology.json

Supported Formats

The CLI automatically detects formats based on file extensions:

  • Parsers: .json, .yaml/.yml, .toml, .csv, .graphml
  • Exporters: .json, .yaml, .toml, .csv, .graphml, .dot, .mmd, .d2, .puml, .html, .tex, .txt, .ascii, .svg

Quick Start

from graphable.graph import Graph
from graphable.graphable import Graphable
from graphable.views.texttree import create_topology_tree_txt

# 1. Define your nodes
a = Graphable("Database")
b = Graphable("API Service")
c = Graphable("Web Frontend")

# 2. Build the graph
g = Graph()
g.add_edge(a, b)  # API Service depends on Database
g.add_edge(b, c)  # Web Frontend depends on API Service

# 3. Get topological order
for node in g.topological_order():
    print(node.reference)
# Output: Database, API Service, Web Frontend

# 4. Visualize as a text tree
print(create_topology_tree_txt(g))
# Output:
# Web Frontend
# └─ API Service
#    └─ Database

Visualizing with ASCII Flowchart

from graphable.views.asciiflow import create_topology_ascii_flow

print(create_topology_ascii_flow(g))
# Output:
# +----------+
# | Database |
# +----------+
#   v
#   +--> API Service
#
# +-------------+
# | API Service |
# +-------------+
#   v
#   +--> Web Frontend

Visualizing with Mermaid

from graphable.views.mermaid import create_topology_mermaid_mmd

mmd = create_topology_mermaid_mmd(g)
print(mmd)
# Output:
# flowchart TD
# Database --> API Service
# API Service --> Web Frontend

Visualizing with Graphviz

from graphable.views.graphviz import create_topology_graphviz_dot

dot = create_topology_graphviz_dot(g)
print(dot)
# Output:
# digraph G {
#     "Database" [label="Database"];
#     "Database" -> "API Service";
#     "API Service" [label="API Service"];
#     "API Service" -> "Web Frontend";
#     "Web Frontend" [label="Web Frontend"];
# }

Advanced Analysis

Edge Attributes & Orchestration

Track durations and weights for workflow optimization:

# Add weighted edges
g.add_edge(task_a, task_b, weight=10, label="critical path")

# Set task metadata
task_a.duration = 5.0
task_a.status = "completed"

Critical Path Method (CPM)

Identify the longest chain of dependencies:

analysis = g.cpm_analysis()
print(f"Project duration: {max(n['EF'] for n in analysis.values())}")

critical_nodes = g.critical_path()
print(f"Critical Path: {[n.reference for n in critical_nodes]}")

Graph Diffing

Compare two versions of a graph:

diff_data = g_v1.diff(g_v2)
print(f"Added nodes: {diff_data['added_nodes']}")

# Generate a visual diff graph for rendering
dg = g_v1.diff_graph(g_v2)
print(dg.render(create_topology_mermaid_mmd))

Advanced Slicing

Focus on specific parts of the graph:

# All nodes required by 'Web UI'
sub = g.upstream_of(ui)

# All nodes impacted by a change in 'Database'
sub = g.downstream_of(db)

# Everything between 'Source' and 'Sink'
sub = g.subgraph_between(source, sink)

Native Traversals (BFS & DFS)

Perform idiomatic iterations over your graph:

from graphable.enums import Direction

# Visit every node reachable from 'Task A' breadth-first
for node in g.bfs(task_a):
    print(f"Visiting: {node.reference}")

# Explore dependencies (upstream) depth-first
for node in g.dfs(task_a, direction=Direction.UP):
    print(f"Required by: {node.reference}")

Advanced Analysis with NetworkX

If you have networkx installed, you can convert your graph for advanced analysis:

import networkx as nx

# Convert to NetworkX DiGraph
dg = g.to_networkx()

# Use NetworkX algorithms
print(nx.dag_longest_path(dg))
# Output: ['Database', 'API Service', 'Web Frontend']

Visualizing with D2

from graphable.views.d2 import create_topology_d2

d2 = create_topology_d2(g)
print(d2)
# Output:
# Database: Database
# Database -> API Service
# API Service: API Service
# API Service -> Web Frontend
# Web Frontend: Web Frontend

Advanced Usage

Pythonic Protocols

print(f"Nodes: {len(g)}")
if "Database" in g:
    node = g["Database"]
    
for node in g:  # Iterates in topological order
    print(node.reference)

Transitive Reduction

Clean up your graphs by removing redundant edges:

reduced_g = g.transitive_reduction()
# Or render directly
print(g.render(create_topology_mermaid_mmd, transitive_reduction=True))

Clustering by Tags

Group nodes in visualizations:

from graphable.views.mermaid import MermaidStylingConfig

a.add_tag("backend")
b.add_tag("backend")
config = MermaidStylingConfig(cluster_by_tag=True)

print(g.render(create_topology_mermaid_mmd, config=config))

Parsing Graphs

Reconstruct graphs from exported data:

# From a file
g = Graph.from_json("graph.json")

# From a string (YAML)
yaml_data = """
nodes:
  - id: Database
  - id: API
edges:
  - source: Database
    target: API
"""
g = Graph.from_yaml(yaml_data)

Equality Comparison

Compare graphs easily:

g1 = Graph.from_json("topology.json")
g2 = Graph.from_yaml("topology.yaml")

if g1 == g2:
    print("Graphs are identical in structure and tags.")

Node Ordering

Nodes support comparison based on reachability:

if db < api:
    print("Database is an ancestor of API")
    
if ui > api:
    print("UI is a descendant of API")

Checksums & Integrity

Verify your graph hasn't changed:

digest = g.checksum()
# ... later ...
if g.validate_checksum(digest):
    print("Graph integrity verified.")

Unified I/O

Read and write any format without thinking about parsers:

# Automatically detects JSON
g = Graph.read("topology.json")

# Automatically detects YAML
g.write("topology.yaml")

Documentation

Full documentation is available in the docs/ directory. You can build it locally:

just docs-view

Development

This project uses uv for dependency management and just as a command runner.

just install    # Install dependencies
just check      # Run linting, type checking, and tests
just coverage   # Run tests with coverage report

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graphable-0.5.0.tar.gz (38.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

graphable-0.5.0-py3-none-any.whl (58.4 kB view details)

Uploaded Python 3

File details

Details for the file graphable-0.5.0.tar.gz.

File metadata

  • Download URL: graphable-0.5.0.tar.gz
  • Upload date:
  • Size: 38.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for graphable-0.5.0.tar.gz
Algorithm Hash digest
SHA256 8f77cb413c4b39af0ef006868ae09ab90342562429003e9bad7390f1613a0642
MD5 fd6162b8bda8ad2b7aae335f672e7647
BLAKE2b-256 e9e0ae0f8a59becdac12d95789a2496f288ad6e2c30e6287d83469620eb7bfad

See more details on using hashes here.

Provenance

The following attestation bundles were made for graphable-0.5.0.tar.gz:

Publisher: publish.yml on TheTrueSCU/graphable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file graphable-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: graphable-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 58.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for graphable-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 51f19720313796bcd6d137cd33e44b0e187262e5a5d469fbcb8608f3502a2c23
MD5 d49cbe90e708e39e95916488ca2cacc5
BLAKE2b-256 b52498e3ebc328da5c8bf6816e7e26aedbf7ec33609e82a42e62edb748b5b4bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for graphable-0.5.0-py3-none-any.whl:

Publisher: publish.yml on TheTrueSCU/graphable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page