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_global→True): Graphs → Project Graphs → [name]. - Entity-level (
test_entity→True): select the entity, then Graphs → Graphs for [entity] → [name], or right-click it → Graphical Views. - Architecture-level (
test_architecture→True): 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.