Package: grot
GrotGrotis a noun and meansarrowheadin polish language.Makes graphviz usage simplerMuch less headache. Gets you faster into the point.Hello GrotTo generate a graph you need to import Grot class, create its instance and
define nodes and edges. Whileg.edge()call, you can pass unlimited
number of nodes or plain strings (creates implicit node).If you don't connect given node (asunconnected) it's going to float somewhere around.defexample_01():importosfromgrotimportGrotthis_dir_path=os.path.dirname(__file__)# if run in console - remove 'directory' parameter belowout_dir_path=os.path.join(this_dir_path,"out")g=Grot(name="example_01",format="png",directory=out_dir_path,graph_attrs={"rankdir":"LR"})one=g.node("It is\neaiser")two=g.node("graphs",color="#8a9bac")ignored=g.node("Node floats when\nunconnected",color="#da3080")g.edge(one,"to define",two)g.render()Source of this example is inexamples/example_01.py.It generates raw dot-syntax text file in:examples/out/example_01.gv.
And the final graph file is in:examples/out/example_01.gv.png:BranchesOne call ofg.edge(node1, node2, node3, ...)creates single chain of arrows.
To make a branch, you need to callg.edgeonce again.defexample_02a():fromgrotimportGrotthis_dir_path=os.path.dirname(__file__)# if run in console - remove 'directory' parameter belowout_dir_path=os.path.join(this_dir_path,"out")g=Grot(name="example_02a",format="png",directory=out_dir_path,graph_attrs={"rankdir":"LR"})one=g.node("One")two=g.node("Two")three=g.node("Three")four=g.node("Four")five=g.node("Five")six=g.node("Six")seven=g.node("Seven")g.edge(one,two,three,four,"A")g.edge(two,five,six,"B")g.edge(two,seven,"C")g.render()Source of this example is inexamples/example_02.py.It generates raw dot-syntax text file in:examples/out/example_02a.gv.
And the final graph file is in:examples/out/example_02a.gv.png:Branches - syntax variantYou don't have to assign nodes to variables. However it's a good practice to do so.
You can define nodes whileg.edge(...)call.
Here nodetwois assigned to a local variable, because we refer it several times.defexample_02b():fromgrotimportGrotthis_dir_path=os.path.dirname(__file__)# if run in console - remove 'directory' parameter belowout_dir_path=os.path.join(this_dir_path,"out")g=Grot(name="example_02b",format="png",directory=out_dir_path,graph_attrs={"rankdir":"LR"})two=g.node("Two")g.edge(g.node("One"),two,g.node("Three"),g.node("Four"),"A")g.edge(two,g.node("Five"),g.node("Six"),"B")g.edge(two,g.node("Seven"),"C")g.render()Source of this example is inexamples/example_02.py.It generates raw dot-syntax text file in:examples/out/example_02b.gv.
And the final graph file is in:examples/out/example_02b.gv.png:BranchesOne call ofg.edge(node1, node2, node3, ...)creates single chain of arrows.
To make a branch, you need to callg.edgeonce again.defexample_tree():importosfromtypingimportList,OptionalfromairiumimportAiriumfromgrotimportGrot,NodeVisit,Nodethis_dir_path=os.path.dirname(__file__)# if run in console - remove 'directory' parameter belowout_dir_path=os.path.join(this_dir_path,"out")font_face="Monospace"# "Courier New", "Helvetica"g=Grot(name="example_tree",format="png",directory=out_dir_path,graph_attrs={"fontname":font_face,"layout":"neato","overlap":"scalexy","mode":"ipsep","sep":"+5",},node_attrs={"fontname":font_face,"penwidth":"1.1","shape":"box","width":"0","height":"0","margin":"0",},edge_attrs={"fontname":font_face,"penwidth":"0.80",},)defhtml_table_label(own_name:str,own_type:str,own_path:str,children:list[NodeVisit],fg_color:str,bg_color=str)->str:a=Airium(base_indent="")witha.table(border="0",bgcolor=bg_color):shaded_color="#757575"witha.tr():witha.td(align="left").font(color=fg_color,**{"point-size":"29"}):a(f" <b><u>{own_name}</u></b>")witha.td(align="right").font(color=fg_color):types_=" | ".join(sorted(set(c.typeforcinchildren)))types_=f" [{types_[30:]}...]"iflen(types_)>36elsef" [{types_}]"a(f"{own_type}{types_}")witha.tr():witha.td(colspan=2,align="center").font(color=shaded_color):a(f"<i>{own_path}</i>")forvisitinchildren:type_annotation=f'<font color="{shaded_color}"><b>{visit.type}</b> 'sub_types=" | ".join(sorted(NodeVisit.children_types(visit.value)))iflen(sub_types)>36:type_annotation+=f"[{sub_types[:30]}...]"elifsub_types:type_annotation+=f"[{sub_types}]"type_annotation+="</font>"right=repr(visit.value)iflen(right)<55and"<"notinright:left=f"{visit.key}:{type_annotation}"right=f" ={right}"else:left=f"{visit.key}:"right=type_annotationwitha.tr():a.td(align="left").font(_t=left,color=fg_color)a.td(align="right").font(_t=right,color=fg_color)ifnotchildren:a.tr().td(colspan=2).font(_t="No children",color=fg_color)returnstr(a)defdraw_node(node_object,path=("root",))->Optional[Node]:children:List[NodeVisit]=[]forvisitinNodeVisit.iter_tree_nodes(node_object):visit:NodeVisitvisit.graph_node_id=draw_node(visit.value,(*path,visit.key))children.append(visit)ifnotchildren:return# draw only containers not leafsfg_col="#111111"iflen(path)<2else"#112233"iflen(path)%2else"#334433"bg_col="#eeeeee"iflen(path)<2else"#ddeedd"iflen(path)%2else"#eee6dd"table_body:str=html_table_label(own_name=path[-1],own_type=type(node_object).__name__,own_path=NodeVisit.join_path_str(*path),children=children,fg_color=fg_col,bg_color=bg_col,)own_node:Node=g.html_node(table_body)forvisitinchildren:ifvisit.graph_node_id:g.edge(own_node,visit.graph_node_id)returnown_nodemy_nested_structure={"web-app":{"servlet":[{"servlet-name":"cofaxCDS","servlet-class":"org.cofax.cds.CDSServlet","init-param":{"configGlossary:installationAt":"Philadelphia, PA","configGlossary:poweredBy":"Cofax","dataStoreLogFile":"/usr/local/tomcat/logs/datastore.log","dataStoreConnUsageLimit":100,"dataStoreLogLevel":"debug","maxUrlLength":500,},},{"servlet-name":"cofaxEmail","servlet-class":"org.cofax.cds.EmailServlet","init-param":{"mailHost":"mail1","mailHostOverride":"mail2"},},{"servlet-name":"cofaxAdmin","servlet-class":"org.cofax.cds.AdminServlet"},{"servlet-name":"fileServlet","servlet-class":"org.cofax.cds.FileServlet"},{"servlet-name":"cofaxTools","servlet-class":"org.cofax.cms.CofaxToolsServlet","init-param":{"templatePath":"toolstemplates/","log":1,"logLocation":"/usr/local/tomcat/logs/CofaxTools.log","lookInContext":1,"adminGroupID":4,"betaServer":True,},},],"servlet-mapping":{"cofaxCDS":"/","cofaxEmail":"/cofaxutil/aemail/*","cofaxAdmin":"/admin/*","fileServlet":"/static/*","cofaxTools":"/tools/*",},"taglib":{"taglib-uri":"cofax.tld","taglib-location":"/WEB-INF/tlds/cofax.tld"},}}draw_node(my_nested_structure)g.render()Source of this example is inexamples/example_tree.py.It generates raw dot-syntax text file in:examples/out/example_tree.gv.
And the final graph file is in:examples/out/example_tree.gv.png: