# How graphviz2drawio Differentiates Between `graph` and `digraph`

> Learn how graphviz2drawio distinguishes between DOT graphs and digraphs using pygraphviz's AGraph directed attribute. Ensure correct arrowhead rendering in your conversions.

- Repository: [Harold Martin/graphviz2drawio](https://github.com/hbmartin/graphviz2drawio)
- Tags: internals
- Published: 2026-03-03

---

**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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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:

```python
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:

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

```

This call in [`graphviz2drawio/graphviz2drawio.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/models/SvgParser.py) (lines 35-36), the parser instantiates an `EdgeFactory` with the direction flag:

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

```

The factory subsequently creates `Edge` objects in [`graphviz2drawio/mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Edge.py) (lines 31-33), where the direction attribute is set based on the propagated flag:

```python
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`)**

```python
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`)**

```python
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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Edge.py).

### What happens if the DOT file uses the `strict` keyword?

The regex pattern in [`graphviz2drawio/graphviz2drawio.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/graphviz2drawio.py), the code accesses `graph.directed` immediately after instantiation, using this boolean to initialize the conversion pipeline's direction state.