summaryrefslogtreecommitdiffstats
path: root/contrib/python/Automat/py3/automat/_visualize.py
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2024-09-02 00:01:09 +0300
committerrobot-piglet <[email protected]>2024-09-02 00:09:17 +0300
commitb5c4ec42ac2cc59dc3b104277ce2e85f5f77c88e (patch)
tree9b37605b78a3d2398da4addca3ee37899621c38e /contrib/python/Automat/py3/automat/_visualize.py
parent88fb7b5c334c3afdffacd104ee20efda4400d1bc (diff)
Intermediate changes
Diffstat (limited to 'contrib/python/Automat/py3/automat/_visualize.py')
-rw-r--r--contrib/python/Automat/py3/automat/_visualize.py234
1 files changed, 141 insertions, 93 deletions
diff --git a/contrib/python/Automat/py3/automat/_visualize.py b/contrib/python/Automat/py3/automat/_visualize.py
index 7a9c8c6eb55..a2b35e57267 100644
--- a/contrib/python/Automat/py3/automat/_visualize.py
+++ b/contrib/python/Automat/py3/automat/_visualize.py
@@ -1,56 +1,66 @@
-from __future__ import print_function
+from __future__ import annotations
+
import argparse
import sys
+from functools import wraps
+from typing import Callable, Iterator
import graphviz
+from ._core import Automaton, Input, Output, State
from ._discover import findMachines
+from ._methodical import MethodicalMachine
+from ._typed import TypeMachine, InputProtocol, Core
-def _gvquote(s):
- return '"{}"'.format(s.replace('"', r'\"'))
+def _gvquote(s: str) -> str:
+ return '"{}"'.format(s.replace('"', r"\""))
-def _gvhtml(s):
- return '<{}>'.format(s)
+def _gvhtml(s: str) -> str:
+ return "<{}>".format(s)
-def elementMaker(name, *children, **attrs):
+def elementMaker(name: str, *children: str, **attrs: str) -> str:
"""
Construct a string from the HTML element description.
"""
- formattedAttrs = ' '.join('{}={}'.format(key, _gvquote(str(value)))
- for key, value in sorted(attrs.items()))
- formattedChildren = ''.join(children)
- return u'<{name} {attrs}>{children}</{name}>'.format(
- name=name,
- attrs=formattedAttrs,
- children=formattedChildren)
-
-
-def tableMaker(inputLabel, outputLabels, port, _E=elementMaker):
+ formattedAttrs = " ".join(
+ "{}={}".format(key, _gvquote(str(value)))
+ for key, value in sorted(attrs.items())
+ )
+ formattedChildren = "".join(children)
+ return "<{name} {attrs}>{children}</{name}>".format(
+ name=name, attrs=formattedAttrs, children=formattedChildren
+ )
+
+
+def tableMaker(
+ inputLabel: str,
+ outputLabels: list[str],
+ port: str,
+ _E: Callable[..., str] = elementMaker,
+) -> str:
"""
Construct an HTML table to label a state transition.
"""
colspan = {}
if outputLabels:
- colspan['colspan'] = str(len(outputLabels))
+ colspan["colspan"] = str(len(outputLabels))
- inputLabelCell = _E("td",
- _E("font",
- inputLabel,
- face="menlo-italic"),
- color="purple",
- port=port,
- **colspan)
+ inputLabelCell = _E(
+ "td",
+ _E("font", inputLabel, face="menlo-italic"),
+ color="purple",
+ port=port,
+ **colspan,
+ )
pointSize = {"point-size": "9"}
- outputLabelCells = [_E("td",
- _E("font",
- outputLabel,
- **pointSize),
- color="pink")
- for outputLabel in outputLabels]
+ outputLabelCells = [
+ _E("td", _E("font", outputLabel, **pointSize), color="pink")
+ for outputLabel in outputLabels
+ ]
rows = [_E("tr", inputLabelCell)]
@@ -60,16 +70,33 @@ def tableMaker(inputLabel, outputLabels, port, _E=elementMaker):
return _E("table", *rows)
-def makeDigraph(automaton, inputAsString=repr,
- outputAsString=repr,
- stateAsString=repr):
+def escapify(x: Callable[[State], str]) -> Callable[[State], str]:
+ @wraps(x)
+ def impl(t: State) -> str:
+ return x(t).replace("<", "&lt;").replace(">", "&gt;")
+
+ return impl
+
+
+def makeDigraph(
+ automaton: Automaton[State, Input, Output],
+ inputAsString: Callable[[Input], str] = repr,
+ outputAsString: Callable[[Output], str] = repr,
+ stateAsString: Callable[[State], str] = repr,
+) -> graphviz.Digraph:
"""
Produce a L{graphviz.Digraph} object from an automaton.
"""
- digraph = graphviz.Digraph(graph_attr={'pack': 'true',
- 'dpi': '100'},
- node_attr={'fontname': 'Menlo'},
- edge_attr={'fontname': 'Menlo'})
+
+ inputAsString = escapify(inputAsString)
+ outputAsString = escapify(outputAsString)
+ stateAsString = escapify(stateAsString)
+
+ digraph = graphviz.Digraph(
+ graph_attr={"pack": "true", "dpi": "100"},
+ node_attr={"fontname": "Menlo"},
+ edge_attr={"fontname": "Menlo"},
+ )
for state in automaton.states():
if state is automaton.initialState:
@@ -78,38 +105,47 @@ def makeDigraph(automaton, inputAsString=repr,
else:
stateShape = ""
fontName = "Menlo"
- digraph.node(stateAsString(state),
- fontame=fontName,
- shape="ellipse",
- style=stateShape,
- color="blue")
+ digraph.node(
+ stateAsString(state),
+ fontame=fontName,
+ shape="ellipse",
+ style=stateShape,
+ color="blue",
+ )
for n, eachTransition in enumerate(automaton.allTransitions()):
inState, inputSymbol, outState, outputSymbols = eachTransition
thisTransition = "t{}".format(n)
inputLabel = inputAsString(inputSymbol)
port = "tableport"
- table = tableMaker(inputLabel, [outputAsString(outputSymbol)
- for outputSymbol in outputSymbols],
- port=port)
+ table = tableMaker(
+ inputLabel,
+ [outputAsString(outputSymbol) for outputSymbol in outputSymbols],
+ port=port,
+ )
- digraph.node(thisTransition,
- label=_gvhtml(table), margin="0.2", shape="none")
+ digraph.node(thisTransition, label=_gvhtml(table), margin="0.2", shape="none")
- digraph.edge(stateAsString(inState),
- '{}:{}:w'.format(thisTransition, port),
- arrowhead="none")
- digraph.edge('{}:{}:e'.format(thisTransition, port),
- stateAsString(outState))
+ digraph.edge(
+ stateAsString(inState),
+ "{}:{}:w".format(thisTransition, port),
+ arrowhead="none",
+ )
+ digraph.edge("{}:{}:e".format(thisTransition, port), stateAsString(outState))
return digraph
-def tool(_progname=sys.argv[0],
- _argv=sys.argv[1:],
- _syspath=sys.path,
- _findMachines=findMachines,
- _print=print):
+def tool(
+ _progname: str = sys.argv[0],
+ _argv: list[str] = sys.argv[1:],
+ _syspath: list[str] = sys.path,
+ _findMachines: Callable[
+ [str],
+ Iterator[tuple[str, MethodicalMachine | TypeMachine[InputProtocol, Core]]],
+ ] = findMachines,
+ _print: Callable[..., None] = print,
+) -> None:
"""
Entry point for command line utility.
"""
@@ -122,59 +158,71 @@ def tool(_progname=sys.argv[0],
http://www.graphviz.org for more information.
"""
if _syspath[0]:
- _syspath.insert(0, '')
+ _syspath.insert(0, "")
argumentParser = argparse.ArgumentParser(
- prog=_progname,
- description=DESCRIPTION,
- epilog=EPILOG)
- argumentParser.add_argument('fqpn',
- help="A Fully Qualified Path name"
- " representing where to find machines.")
- argumentParser.add_argument('--quiet', '-q',
- help="suppress output",
- default=False,
- action="store_true")
- argumentParser.add_argument('--dot-directory', '-d',
- help="Where to write out .dot files.",
- default=".automat_visualize")
- argumentParser.add_argument('--image-directory', '-i',
- help="Where to write out image files.",
- default=".automat_visualize")
- argumentParser.add_argument('--image-type', '-t',
- help="The image format.",
- choices=graphviz.FORMATS,
- default='png')
- argumentParser.add_argument('--view', '-v',
- help="View rendered graphs with"
- " default image viewer",
- default=False,
- action="store_true")
+ prog=_progname, description=DESCRIPTION, epilog=EPILOG
+ )
+ argumentParser.add_argument(
+ "fqpn",
+ help="A Fully Qualified Path name" " representing where to find machines.",
+ )
+ argumentParser.add_argument(
+ "--quiet", "-q", help="suppress output", default=False, action="store_true"
+ )
+ argumentParser.add_argument(
+ "--dot-directory",
+ "-d",
+ help="Where to write out .dot files.",
+ default=".automat_visualize",
+ )
+ argumentParser.add_argument(
+ "--image-directory",
+ "-i",
+ help="Where to write out image files.",
+ default=".automat_visualize",
+ )
+ argumentParser.add_argument(
+ "--image-type",
+ "-t",
+ help="The image format.",
+ choices=graphviz.FORMATS,
+ default="png",
+ )
+ argumentParser.add_argument(
+ "--view",
+ "-v",
+ help="View rendered graphs with" " default image viewer",
+ default=False,
+ action="store_true",
+ )
args = argumentParser.parse_args(_argv)
- explicitlySaveDot = (args.dot_directory
- and (not args.image_directory
- or args.image_directory != args.dot_directory))
+ explicitlySaveDot = args.dot_directory and (
+ not args.image_directory or args.image_directory != args.dot_directory
+ )
if args.quiet:
+
def _print(*args):
pass
for fqpn, machine in _findMachines(args.fqpn):
- _print(fqpn, '...discovered')
+ _print(fqpn, "...discovered")
digraph = machine.asDigraph()
if explicitlySaveDot:
- digraph.save(filename="{}.dot".format(fqpn),
- directory=args.dot_directory)
+ digraph.save(filename="{}.dot".format(fqpn), directory=args.dot_directory)
_print(fqpn, "...wrote dot into", args.dot_directory)
if args.image_directory:
deleteDot = not args.dot_directory or explicitlySaveDot
digraph.format = args.image_type
- digraph.render(filename="{}.dot".format(fqpn),
- directory=args.image_directory,
- view=args.view,
- cleanup=deleteDot)
+ digraph.render(
+ filename="{}.dot".format(fqpn),
+ directory=args.image_directory,
+ view=args.view,
+ cleanup=deleteDot,
+ )
if deleteDot:
msg = "...wrote image into"
else: