diff options
author | robot-piglet <[email protected]> | 2024-09-02 00:01:09 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2024-09-02 00:09:17 +0300 |
commit | b5c4ec42ac2cc59dc3b104277ce2e85f5f77c88e (patch) | |
tree | 9b37605b78a3d2398da4addca3ee37899621c38e /contrib/python/Automat/py3/automat/_visualize.py | |
parent | 88fb7b5c334c3afdffacd104ee20efda4400d1bc (diff) |
Intermediate changes
Diffstat (limited to 'contrib/python/Automat/py3/automat/_visualize.py')
-rw-r--r-- | contrib/python/Automat/py3/automat/_visualize.py | 234 |
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("<", "<").replace(">", ">") + + 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: |