How graphviz2drawio Differentiates Between `graph` and `digraph`

graphviz2drawio uses pygraphviz's AGraph.directed boolean attribute to detect whether the input DOT defines a digraph (directed) or graph (undirected), propagating this flag through the conversion pipeline to set edge arrowheads accordingly.

Converting Graphviz DOT diagrams to Draw.io (mxGraph) format requires accurately preserving the semantic difference between directed and undirected relationships. The hbmartin/graphviz2drawio library handles this distinction by leveraging pygraphviz parsing capabilities to detect the graph type from the DOT source and apply the appropriate edge styling throughout the conversion process.

How graphviz2drawio Detects Graph Directionality

The detection begins in graphviz2drawio/graphviz2drawio.py, where the _load_pygraphviz_agraph function processes the input. This function first determines whether the supplied string is a DOT graph definition or a file path using a regular expression that explicitly matches both graph and digraph keywords:

pattern = re.compile(
    r"^(?=(\s*))\1(strict)?(?=(\s*))\3(graph|digraph)[^{]*{",
    flags=re.MULTILINE,
)

When this pattern matches, the text is passed to pygraphviz's AGraph(string=...) constructor. The resulting AGraph object exposes a directed boolean attribute that is True for digraph declarations and False for graph declarations. This attribute serves as the single source of truth for directionality throughout the library.

Propagating the Direction Flag Through the Conversion Pipeline

Once loaded, the direction flag flows through three critical stages to ensure the output Draw.io XML reflects the correct edge semantics.

From DOT Parsing to SVG Processing

After loading the DOT source, the main conversion function reads graph.directed and immediately passes it to the SVG parser:

nodes, edges, clusters = parse_nodes_edges_clusters(
    svg_data=svg_graph,
    is_directed=graph.directed,
)

This call in graphviz2drawio/graphviz2drawio.py (lines 34-38) ensures that the directionality information derived from the original DOT header is available during the SVG interpretation phase.

Edge Factory and Direction Assignment

Inside graphviz2drawio/models/SvgParser.py (lines 35-36), the parser instantiates an EdgeFactory with the direction flag:

edge_factory = EdgeFactory(coords=coords, is_directed=is_directed)

The factory subsequently creates Edge objects in graphviz2drawio/mx/Edge.py (lines 31-33), where the direction attribute is set based on the propagated flag:

self.dir = DotAttr.FORWARD if is_directed else DotAttr.NONE

This assignment determines whether the resulting Draw.io edges display directional arrowheads (forward) or terminate without arrows (none), preserving the visual semantics of the original Graphviz definition.

Practical Examples: Converting Directed and Undirected Graphs

The same convert function handles both graph types automatically. The library inspects the DOT header to determine directionality without requiring additional parameters.

Example 1: Directed Graph (digraph)

from graphviz2drawio import convert

dot = """
digraph G {
    A -> B
    B -> C
}
"""
drawio_xml = convert(dot)  # graph.directed evaluates to True

print(drawio_xml)          # Output contains edges with forward arrows

Example 2: Undirected Graph (graph)

from graphviz2drawio import convert

dot = """
graph G {
    A -- B
    B -- C
}
"""
drawio_xml = convert(dot)  # graph.directed evaluates to False

print(drawio_xml)          # Output contains edges without arrows

In both cases, graphviz2drawio automatically detects the graph type from the DOT syntax and generates the appropriate Draw.io representation.

Summary

  • graphviz2drawio relies on pygraphviz to parse DOT files and determine directionality via the AGraph.directed boolean attribute.
  • The _load_pygraphviz_agraph function in graphviz2drawio/graphviz2drawio.py uses a regex pattern to identify valid DOT strings containing either graph or digraph keywords.
  • The direction flag propagates through parse_nodes_edges_clusters to SvgParser and finally to EdgeFactory, ensuring consistent handling across the conversion stack.
  • In graphviz2drawio/mx/Edge.py, edges are assigned DotAttr.FORWARD for directed graphs and DotAttr.NONE for undirected graphs, controlling arrowhead rendering in the final Draw.io output.
  • No manual configuration is required; the library automatically differentiates between graph and digraph based solely on the DOT source declaration.

Frequently Asked Questions

Does graphviz2drawio support mixed directed and undirected graphs?

No, graphviz2drawio follows the standard Graphviz specification where a single DOT file must declare either graph (undirected) or digraph (directed) at the top level. The library uses a single is_directed flag for the entire conversion, so mixing edge types within one graph is not supported according to the source implementation in graphviz2drawio/mx/Edge.py.

What happens if the DOT file uses the strict keyword?

The regex pattern in graphviz2drawio/graphviz2drawio.py explicitly handles the optional strict modifier: r"^(?=(\s*))\1(strict)?(?=(\s*))\3(graph|digraph)[^{]*{". This ensures that both strict graph and strict digraph declarations are correctly identified and passed to pygraphviz for parsing, preserving the directionality detection regardless of strict mode.

Can I override the directionality to force arrows on an undirected graph?

The current implementation does not expose a public API to override the is_directed flag. The directionality is determined exclusively by pygraphviz's parsing of the DOT header and flows through private methods like parse_nodes_edges_clusters and EdgeFactory.__init__ without user intervention. To change edge directionality, you must modify the DOT source from graph to digraph.

Which pygraphviz attributes does graphviz2drawio use to detect graph type?

The library depends entirely on the directed attribute of the AGraph class provided by pygraphviz. As shown in graphviz2drawio/graphviz2drawio.py, the code accesses graph.directed immediately after instantiation, using this boolean to initialize the conversion pipeline's direction state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →