Lab note
XML-RPC graph control
A short, practical guide to controlling a graph viewer over XML-RPC. The original Ubigraph examples are written this way and the translation to current Python is small.
Why XML-RPC
The original Ubigraph viewer exposes its API over XML-RPC. That is a defensible choice and remains a defensible choice today.
A few reasons it works for this problem:
- XML-RPC is small. The spec is short enough to read in an afternoon.
- Clients exist in every common language. Python ships one in its standard library.
- The protocol is connectionless at the application level. A client that crashes does not leave the viewer in a bad state.
- The data model is plain - strings, numbers, arrays, structs. There are no fancy serialisation tricks to debug.
The protocol is older than the design fashion of the moment, but it covers this job well.
The Python 2 to Python 3 translation
Almost all of the historical Ubigraph examples are Python 2. The translation to Python 3 is small and almost mechanical.
The module name changes. Python 2 has xmlrpclib. Python 3 has xmlrpc.client. Everything else about the call shape is the same. The Python standard library documentation for xmlrpc.client is the right reference.
A typical Python 2 client opens like this:
import xmlrpclib |
The Python 3 version is:
import xmlrpc.client |
The variable name is conventional. You can call it whatever you like.
The calls a real client uses
A working client needs surprisingly few calls. The list below is the practical subset.
v1 = server.ubigraph.new_vertex() |
That is most of what the demos use. If you have those calls working, you can build any of the demos and most real-world examples.
Styles, briefly
The original API also supports named styles. A style is a group of attributes that can be applied as a unit. Real clients use styles to keep the per-vertex calls short:
my_style = server.ubigraph.new_vertex_style(0) |
If you have more than a handful of categories of vertex, styles are worth the time. If you have only one or two, attributes are fine on their own.
Errors and how to handle them
XML-RPC reports errors as faults. The Python client raises them as exceptions. A small wrapper that logs the call and re-raises makes debugging much easier than letting raw faults reach the top of the program:
def safe_call(name, fn, *args): |
The names of the faults are usually self-explanatory once you see them. The common ones are “vertex does not exist” (you removed it earlier and forgot) and “edge already exists” (the API does not de-duplicate for you).
Two cautions
Two small things to keep in mind that catch people out.
First, vertex and edge ids are integers, but they are not consecutive. The viewer assigns them. The client must remember the id that was returned and use it for later calls. Do not assume vertex 0, 1, 2.
Second, attribute values are strings on the wire. A colour like #ff8a3d is a string. A width like 3.0 is a string. Pass numbers as strings even when they look like they should be numbers. The viewer parses them on its side.
Translating to other languages
If you would rather work in something other than Python, the call shape carries over.
- A Go client uses the
net/rpcpackage with the XML-RPC body or any third-party XML-RPC library. - A Node.js client uses one of the maintained
xmlrpcpackages. - A Rust client uses one of the XML-RPC crates.
In every case the work is the same six calls. You will spend more time on the language’s XML-RPC plumbing than on the graph API.
Where this connects in the rest of the site
- Docs index - the longer-form description of the API.
- Animation demo - the streaming model in action.
- Ubigraph server and graph streaming - why streaming matters.
- Graph visualisation alternatives - wrapping a current viewer with the same API.