Skip to content

Write a graph plugin (Python)

When no built-in graph frames your data the way you need, write a graph plugin. Once installed, it appears alongside the built-in graphs — in the Graphs menu and in the Graphical Views submenu for an entity or architecture — and can be exported or scripted like any other graph. A graph plugin is one of Understand's five plugin types; this page covers the graph-specific contract.

Python only

Graph plugins are Python scripts (.upy). You build the graph with Graphviz-style nodes, edges, and attributes through the Understand graph API.

The plugin contract

A graph plugin is a plain Python module — there is no base class. Understand classifies a .upy file as a graph plugin simply because it defines a top-level draw function. A graph plugin defines:

Function Required Purpose
name() yes The graph's name, shown in the menu.
draw(graph, target) yes Builds the graph — adds nodes and edges to graph.
style() no Label in the graph's variant dropdown (defaults to "Custom").
description() no HTML shown in the Plugin Manager.
tags() no List of tags shown in the Plugin Manager.
init(graph, target) no Called once on creation — define options and a legend here.
test_entity(ent) no Return True to offer the graph for that entity.
test_architecture(arch) no Return True to offer the graph for an architecture.
test_global(db) no Return True to list the graph as a project-level graph.

The target passed to init/draw is a understand.Ent, understand.Arch, or understand.Db, depending on which test_ function matched. If your plugin supports more than one, branch on the type with isinstance.

One graph, several contexts

Return True from more than one test_ function to make the same graph available at multiple levels (e.g. per-function and project-wide), then handle each target type inside draw.

Drawing with the graph API

Inside init and draw, the graph object (a GraphContext) is your canvas. For the full class reference and the API's own plugin-writing narrative, see the Python API graph guide:

Call Does
graph.node(label, ent) Add a node. Passing an ent auto-syncs it (double-click navigates to it).
graph.edge(tail, head) Connect two nodes.
graph.cluster(label, arch) Add a sub-graph that boxes a group of nodes; pass an arch to sync the cluster.
node.set(attr, value) / graph.set(...) Set a Graphviz attribute (shape, color, fillcolor, rankdir, …).
graph.default(attr, value, "node") Set default attributes for all nodes / edges / graphs.
edge.sync(ref) Make clicking the edge jump to a reference.
graph.options() Define / look up user options (.define(...), .lookup(...)).
graph.legend() Define / update legend entries.

Example: a simple call tree

import understand

def name():
    return "Calls"

def style():
    return "Python Sample Template"

def test_entity(ent):
    return ent.kind().check("function ~unknown ~unresolved")

def init(graph, target):
    # Options appear in the graph's sidebar; look them up in draw().
    graph.options().define("Depth", ["1", "2", "3"], "3")

def draw(graph, target):
    graph.set("rankdir", "LR")
    graph.default("shape", "box", "node")

    nodes = {}
    def grab(ent):
        if ent not in nodes:
            nodes[ent] = graph.node(ent.name(), ent)
        return nodes[ent]

    depth = int(graph.options().lookup("Depth"))
    cur = [target]
    while depth > 0:
        depth -= 1
        nxt = []
        for ent in cur:
            tail = grab(ent)
            for ref in ent.refs("call", unique=True):
                head = grab(ref.ent())
                graph.edge(tail, head).sync(ref)
                nxt.append(ref.ent())
        cur = nxt

Start from a template

The shipped plugins/Graph/ library is the easiest starting point — call trees, butterfly and comparison graphs, object-reference graphs, variable trackers, UML sequence diagrams, and architecture-dependency graphs. In the Plugin Manager, filter by the Sample Template tag, pick one, and Customize it to drop an editable copy into your plugin folder. plugins/Graph/calls.upy is the example above in full (with a legend and architecture/project support), and is the graph used in the Python API documentation.

Install & run

Install a graph plugin like any other — drag the .upy onto the main window, or add it from the Plugin Manager. Understand recognizes it as a graph (it defines draw) and files it under your per-user Graph plugin folder. See Install & run plugins for the directory locations and the Rescan Plugins step to run after editing, and Write a plugin for the plugin model and common gotchas (broken plugins, reloading, the per-interpreter GIL).

Once enabled, run it:

  • Project-level (test_globalTrue): Graphs → Project Graphs → [name].
  • Entity-level (test_entityTrue): select the entity, then Graphs → Graphs for [entity][name], or right-click it → Graphical Views.
  • Architecture-level (test_architectureTrue): select an architecture in the Architecture Browser, then right-click it → Graphical Views → [name].

You can also draw the graph headlessly from the Python API — ent.draw("*[name]*", ...), arch.draw, or db.draw — see export, print & script a graph.