Utilities for working with TLA+ state graphs
Project description
tlaplus-state-graph-utils.py
Python utilities for working with TLA+ state graphs
Installation
pip install tlaplus-state-graph-utils
Using the CLI
Installing the package puts a CLI program named tlaplus-state-graph-utils.py
in your
PATH
. Here is its --help
text:
usage: tlaplus-state-graph-utils.py [-h] [--version] COMMAND ...
utilities for working with TLA+ state graphs
options:
-h, --help show this help message and exit
--version show program's version number and exit
subcommands:
COMMAND description
convert convert between state graph formats
It currently has only one subcommand called convert
which can be used to
convert between different representations of TLA+ state graphs. Here is its
--help
text:
usage: tlaplus-state-graph-utils.py convert [-h] [--output OUTPUT]
[--from {reasonable-json,tlaplus-dot-json}]
[--to {reasonable-json,d2}] [--pretty]
[--reasonable-json-structured-state]
[--reasonable-json-simple-structured-state]
[--reasonable-json-itf-state]
[--d2-output-state-as D2_OUTPUT_STATE_AS]
[input]
convert between TLA+ state graph formats/representations
positional arguments:
input input file path or '-' to use stdin (the default)
options:
-h, --help show this help message and exit
--output OUTPUT, -o OUTPUT
output file path or '-' to use stdout (the default)
--from {reasonable-json,tlaplus-dot-json}, -f {reasonable-json,tlaplus-dot-json}
input format (guessed from extension & content if not
given)
--to {reasonable-json,d2}, -t {reasonable-json,d2}
output format (if not given, defaults to reasonable-
json when outputting to stdout, otherwise guessed from
extension)
--pretty produce pretty-printed rather than compact output
reasonable-json options:
options relevant when outputting in reasonable-json format
--reasonable-json-structured-state
include state contents represented as structured JSON
--reasonable-json-simple-structured-state
include state contents represented as simplified
structured JSON (lossy, i.e. doesn't encode all
details of TLA+, but easy to work with)
--reasonable-json-itf-state
include state contents represented in ITF state format
(see: https://apalache-mc.org/docs/adr/015adr-
trace.html)
D2 options:
options relevant when outputting in D2 format
--d2-output-state-as D2_OUTPUT_STATE_AS
how to represent individual states in the D2 output:
label: without modification as a D2 node label; latex:
as a LaTeX equation which D2 will render; nested-
containers: as nested containers, one per variable,
record, etc.; nested-containers-simple-values-inline:
as above but with terminal/simple values not getting
their own container; nested-containers-simple-values-
inline-newline: as above but with keys and terminal
values separated by newlines
For instance, this converts a GraphViz dot file produced by TLA+'s state diagram output into a "reasonable" JSON format that's easier to work with:
# In your TLA+ Model directory:
dot -Tjson Model_1.dot | tlaplus-state-graph-utils.py convert -t reasonable-json
The following sections go into more details about the supported input and output formats.
Input formats
dot JSON
TLA+ does not currently have a "canonical" format for state graphs (see
TLA+ GitHub issue #639) and
so the only halfway convenient way to get this information out of TLA+ is to
process the GraphViz dot
files it produces.
In particular, as suggested in
a comment
on the aforementioned issue, dot
itself can convert these into a JSON
representation (dot -Tjson
), which can then by read in by this utility (it
automatically considers filenames ending in .dot.json
to be such files).
Reasonable JSON
Another possible input format is the "reasonable JSON" format produced by this tool itself - it is explained in more detail in the Output formats section below.
Output formats
"Reasonable JSON"
The aforementioned "reasonable JSON" format is meant to make it easier for other tools to work with TLA+ state graphs. It looks like this:
{
"metadata": {
"format": {
"name": "reasonable-tlaplus-state-graph-json",
"version": "0.1.1"
}
},
"states": [
{
"id": 1,
"labelTlaPlus": "/\\ a = 1 /\\ b = 2 \n ...",
// optional, if requested:
"structuredState": ...,
"simpleStructuredState": ...,
"itfState": ...
// optional, if added by user:
"styleClass": "someclass"
},
...
],
"steps": [
{
"id": 0,
"actionName": "CheckFileExists",
"fromStateId": 1,
"toStateId": 2,
"colorId": "1"
},
...
]
}
Structured state
If present, the optional fields structuredState
, simpleStructuredState
and
itfState
contain different JSON representations of each state's
"content" (i.e. the set of variables and their values for that state).
They can be switched on independently of one another via the
--reasonable-json-structured-state
,
--reasonable-json-simple-structured-state
, and
--reasonable-json-simple-structured-state
CLI flags.
The "structured" and "simple-structured" formats are ad-hoc formats defined by this tool (if you need docs for this please open an issue).
The ITF state format is the same as that used in traces output by the Apalache model checker and described in the "State object" section of its ADR-015.
Style class
The optional field styleClass
represents a "class" of styling options to
apply to the state in question and will be included in output formats that
support such a "class" concept (e.g. D2 - see below).
D2
Another supported output format is D2, but to use it,
the package's d2
extra needs to be installed:
pip install tlaplus-state-graph-utils[d2]'
Here is an example showing how to go from a dot
file produced by TLA+ to
a graph rendered using D2 in one shell command:
# In your TLA+ Model directory:
dot -Tjson Model_1.dot \
| tlaplus-state-graph-utils.py convert -t d2 \
| D2_LAYOUT=elk d2 - > Model_1.svg
There are many different ways to represent the contents of each state (i.e.
the set of variables and their values) in D2, which can be configured via
the --d2-output-state-as
option:
label
latex
nested-containers
nested-containers-simple-values-inline
nested-containers-simple-values-inline-newline
Cookbook / advanced techniques
Conditional styling
While there is no built-in support for making styleClass
dependent on the
TLA+ state contents (yet?), it's possible to hack something together using
structured/JSON state output and the jq
JSON
processing tool, e.g.:
tlaplus-state-graph-utils.py convert --reasonable-json-simple-structured-state \
| jq '.states[] |= (.styleClass =
if .someKey == "some-value" then
"class1"
elif .someKey == "some-other-value" then
"class2"
else
.styleClass
end
)'
This overwrites the styleClass
attribute of each state (adding it if it
doesn't exist) depending on the value of the someKey
variable in it.
It is recommended to use the simple-structured-state
format for such kinds of
processing because it's designed to be easy to work with in jq
.
styleClass
is included in e.g. the D2 output, making it possible to create
graphs like this with this technique (see dev/render-test-examples.sh
for the
full script to generate this one):
Use as a library
I'm too lazy to write this or set up automated API docs, especially considering nobody is going to use it. If you want to use this as a library, please just open an issue saying so and I'll get started (and maybe clean things up a little).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file tlaplus_state_graph_utils-0.2.0.tar.gz
.
File metadata
- Download URL: tlaplus_state_graph_utils-0.2.0.tar.gz
- Upload date:
- Size: 34.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.1 CPython/3.12.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 18c3260769861d30a273feadf7707973309f6cdc08f23eb273287d462ac44f02 |
|
MD5 | b16a6704fd09d5780c43fdbc64e1115a |
|
BLAKE2b-256 | 4aebf279fc482632c221f8869be4851dde3ee09ec4ebfc5fd1b820d18d467679 |
File details
Details for the file tlaplus_state_graph_utils-0.2.0-py3-none-any.whl
.
File metadata
- Download URL: tlaplus_state_graph_utils-0.2.0-py3-none-any.whl
- Upload date:
- Size: 24.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.1 CPython/3.12.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1b58250bd3fb3426dff1cc0731984eacc0ee74df7baf9e09382b42391efdfa3e |
|
MD5 | 575b7ad3c32128e0c28de59bc4d59b8e |
|
BLAKE2b-256 | 705a55013e9b3d3a9df6bf56d51f788bfa8f8bf753a33a2eb280a9c873028a3f |