Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/dot_parser.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
| author | shellac | 
|---|---|
| date | Mon, 22 Mar 2021 18:12:50 +0000 | 
| parents | |
| children | 
   comparison
  equal
  deleted
  inserted
  replaced
| -1:000000000000 | 0:4f3585e2f14b | 
|---|---|
| 1 """Graphviz's dot language parser. | |
| 2 | |
| 3 The dotparser parses GraphViz files in | |
| 4 dot and dot files and transforms them | |
| 5 into a class representation defined by `pydot`. | |
| 6 | |
| 7 Author: Michael Krause <michael@krause-software.de> | |
| 8 Fixes by: Ero Carrera <ero.carrera@gmail.com> | |
| 9 """ | |
| 10 from __future__ import division | |
| 11 from __future__ import print_function | |
| 12 import sys | |
| 13 | |
| 14 from pyparsing import ( | |
| 15 nestedExpr, Literal, CaselessLiteral, | |
| 16 Word, OneOrMore, | |
| 17 Forward, | |
| 18 Group, Optional, Combine, | |
| 19 restOfLine, cStyleComment, nums, alphanums, | |
| 20 printables, | |
| 21 ParseException, ParseResults, CharsNotIn, | |
| 22 QuotedString) | |
| 23 | |
| 24 import pydot | |
| 25 | |
| 26 __author__ = ['Michael Krause', 'Ero Carrera'] | |
| 27 __license__ = 'MIT' | |
| 28 | |
| 29 | |
| 30 PY3 = sys.version_info >= (3, 0, 0) | |
| 31 if PY3: | |
| 32 str_type = str | |
| 33 else: | |
| 34 str_type = basestring | |
| 35 | |
| 36 | |
| 37 class P_AttrList(object): | |
| 38 | |
| 39 def __init__(self, toks): | |
| 40 | |
| 41 self.attrs = {} | |
| 42 i = 0 | |
| 43 | |
| 44 while i < len(toks): | |
| 45 attrname = toks[i] | |
| 46 if i+2 < len(toks) and toks[i+1] == '=': | |
| 47 attrvalue = toks[i+2] | |
| 48 i += 3 | |
| 49 else: | |
| 50 attrvalue = None | |
| 51 i += 1 | |
| 52 | |
| 53 self.attrs[attrname] = attrvalue | |
| 54 | |
| 55 | |
| 56 def __repr__(self): | |
| 57 | |
| 58 return "%s(%r)" % (self.__class__.__name__, self.attrs) | |
| 59 | |
| 60 | |
| 61 | |
| 62 class DefaultStatement(P_AttrList): | |
| 63 | |
| 64 def __init__(self, default_type, attrs): | |
| 65 | |
| 66 self.default_type = default_type | |
| 67 self.attrs = attrs | |
| 68 | |
| 69 def __repr__(self): | |
| 70 | |
| 71 return "%s(%s, %r)" % (self.__class__.__name__, | |
| 72 self.default_type, self.attrs) | |
| 73 | |
| 74 | |
| 75 top_graphs = list() | |
| 76 | |
| 77 def push_top_graph_stmt(str, loc, toks): | |
| 78 | |
| 79 attrs = {} | |
| 80 g = None | |
| 81 | |
| 82 for element in toks: | |
| 83 | |
| 84 if (isinstance(element, (ParseResults, tuple, list)) and | |
| 85 len(element) == 1 and | |
| 86 isinstance(element[0], str_type)): | |
| 87 | |
| 88 element = element[0] | |
| 89 | |
| 90 if element == 'strict': | |
| 91 attrs['strict'] = True | |
| 92 | |
| 93 elif element in ['graph', 'digraph']: | |
| 94 | |
| 95 attrs = {} | |
| 96 | |
| 97 g = pydot.Dot(graph_type=element, **attrs) | |
| 98 attrs['type'] = element | |
| 99 | |
| 100 top_graphs.append( g ) | |
| 101 | |
| 102 elif isinstance( element, str_type): | |
| 103 g.set_name( element ) | |
| 104 | |
| 105 elif isinstance(element, pydot.Subgraph): | |
| 106 | |
| 107 g.obj_dict['attributes'].update( element.obj_dict['attributes'] ) | |
| 108 g.obj_dict['edges'].update( element.obj_dict['edges'] ) | |
| 109 g.obj_dict['nodes'].update( element.obj_dict['nodes'] ) | |
| 110 g.obj_dict['subgraphs'].update( element.obj_dict['subgraphs'] ) | |
| 111 | |
| 112 g.set_parent_graph(g) | |
| 113 | |
| 114 elif isinstance(element, P_AttrList): | |
| 115 attrs.update(element.attrs) | |
| 116 | |
| 117 elif isinstance(element, (ParseResults, list)): | |
| 118 add_elements(g, element) | |
| 119 | |
| 120 else: | |
| 121 raise ValueError( | |
| 122 'Unknown element statement: {s}'.format(s=element)) | |
| 123 | |
| 124 | |
| 125 for g in top_graphs: | |
| 126 update_parent_graph_hierarchy(g) | |
| 127 | |
| 128 if len( top_graphs ) == 1: | |
| 129 return top_graphs[0] | |
| 130 | |
| 131 return top_graphs | |
| 132 | |
| 133 | |
| 134 def update_parent_graph_hierarchy(g, parent_graph=None, level=0): | |
| 135 | |
| 136 | |
| 137 if parent_graph is None: | |
| 138 parent_graph = g | |
| 139 | |
| 140 for key_name in ('edges',): | |
| 141 | |
| 142 if isinstance(g, pydot.frozendict): | |
| 143 item_dict = g | |
| 144 else: | |
| 145 item_dict = g.obj_dict | |
| 146 | |
| 147 if key_name not in item_dict: | |
| 148 continue | |
| 149 | |
| 150 for key, objs in item_dict[key_name].items(): | |
| 151 for obj in objs: | |
| 152 if ('parent_graph' in obj and | |
| 153 obj['parent_graph'].get_parent_graph()==g): | |
| 154 if obj['parent_graph'] is g: | |
| 155 pass | |
| 156 else: | |
| 157 obj['parent_graph'].set_parent_graph(parent_graph) | |
| 158 | |
| 159 if key_name == 'edges' and len(key) == 2: | |
| 160 for idx, vertex in enumerate( obj['points'] ): | |
| 161 if isinstance( vertex, | |
| 162 (pydot.Graph, | |
| 163 pydot.Subgraph, pydot.Cluster)): | |
| 164 vertex.set_parent_graph(parent_graph) | |
| 165 if isinstance( vertex, pydot.frozendict): | |
| 166 if vertex['parent_graph'] is g: | |
| 167 pass | |
| 168 else: | |
| 169 vertex['parent_graph'].set_parent_graph( | |
| 170 parent_graph) | |
| 171 | |
| 172 | |
| 173 | |
| 174 def add_defaults(element, defaults): | |
| 175 | |
| 176 d = element.__dict__ | |
| 177 for key, value in defaults.items(): | |
| 178 if not d.get(key): | |
| 179 d[key] = value | |
| 180 | |
| 181 | |
| 182 | |
| 183 def add_elements(g, toks, defaults_graph=None, | |
| 184 defaults_node=None, defaults_edge=None): | |
| 185 | |
| 186 if defaults_graph is None: | |
| 187 defaults_graph = {} | |
| 188 if defaults_node is None: | |
| 189 defaults_node = {} | |
| 190 if defaults_edge is None: | |
| 191 defaults_edge = {} | |
| 192 | |
| 193 for elm_idx, element in enumerate(toks): | |
| 194 | |
| 195 if isinstance(element, (pydot.Subgraph, pydot.Cluster)): | |
| 196 | |
| 197 add_defaults(element, defaults_graph) | |
| 198 g.add_subgraph(element) | |
| 199 | |
| 200 elif isinstance(element, pydot.Node): | |
| 201 | |
| 202 add_defaults(element, defaults_node) | |
| 203 g.add_node(element) | |
| 204 | |
| 205 elif isinstance(element, pydot.Edge): | |
| 206 | |
| 207 add_defaults(element, defaults_edge) | |
| 208 g.add_edge(element) | |
| 209 | |
| 210 elif isinstance(element, ParseResults): | |
| 211 | |
| 212 for e in element: | |
| 213 add_elements(g, [e], defaults_graph, | |
| 214 defaults_node, defaults_edge) | |
| 215 | |
| 216 elif isinstance(element, DefaultStatement): | |
| 217 | |
| 218 if element.default_type == 'graph': | |
| 219 | |
| 220 default_graph_attrs = pydot.Node('graph', **element.attrs) | |
| 221 g.add_node(default_graph_attrs) | |
| 222 | |
| 223 elif element.default_type == 'node': | |
| 224 | |
| 225 default_node_attrs = pydot.Node('node', **element.attrs) | |
| 226 g.add_node(default_node_attrs) | |
| 227 | |
| 228 elif element.default_type == 'edge': | |
| 229 | |
| 230 default_edge_attrs = pydot.Node('edge', **element.attrs) | |
| 231 g.add_node(default_edge_attrs) | |
| 232 defaults_edge.update(element.attrs) | |
| 233 | |
| 234 else: | |
| 235 raise ValueError( | |
| 236 'Unknown DefaultStatement: {s}'.format( | |
| 237 s=element.default_type)) | |
| 238 | |
| 239 elif isinstance(element, P_AttrList): | |
| 240 | |
| 241 g.obj_dict['attributes'].update(element.attrs) | |
| 242 | |
| 243 else: | |
| 244 raise ValueError( | |
| 245 'Unknown element statement: {s}'.format(s=element)) | |
| 246 | |
| 247 | |
| 248 def push_graph_stmt(str, loc, toks): | |
| 249 | |
| 250 g = pydot.Subgraph('') | |
| 251 add_elements(g, toks) | |
| 252 return g | |
| 253 | |
| 254 | |
| 255 def push_subgraph_stmt(str, loc, toks): | |
| 256 | |
| 257 g = pydot.Subgraph('') | |
| 258 for e in toks: | |
| 259 if len(e)==3: | |
| 260 e[2].set_name(e[1]) | |
| 261 if e[0] == 'subgraph': | |
| 262 e[2].obj_dict['show_keyword'] = True | |
| 263 return e[2] | |
| 264 else: | |
| 265 if e[0] == 'subgraph': | |
| 266 e[1].obj_dict['show_keyword'] = True | |
| 267 return e[1] | |
| 268 | |
| 269 return g | |
| 270 | |
| 271 | |
| 272 def push_default_stmt(str, loc, toks): | |
| 273 | |
| 274 # The pydot class instances should be marked as | |
| 275 # default statements to be inherited by actual | |
| 276 # graphs, nodes and edges. | |
| 277 # | |
| 278 default_type = toks[0][0] | |
| 279 if len(toks) > 1: | |
| 280 attrs = toks[1].attrs | |
| 281 else: | |
| 282 attrs = {} | |
| 283 | |
| 284 if default_type in ['graph', 'node', 'edge']: | |
| 285 return DefaultStatement(default_type, attrs) | |
| 286 else: | |
| 287 raise ValueError( | |
| 288 'Unknown default statement: {s}'.format(s=toks)) | |
| 289 | |
| 290 | |
| 291 def push_attr_list(str, loc, toks): | |
| 292 | |
| 293 p = P_AttrList(toks) | |
| 294 return p | |
| 295 | |
| 296 | |
| 297 def get_port(node): | |
| 298 | |
| 299 if len(node)>1: | |
| 300 if isinstance(node[1], ParseResults): | |
| 301 if len(node[1][0])==2: | |
| 302 if node[1][0][0]==':': | |
| 303 return node[1][0][1] | |
| 304 | |
| 305 return None | |
| 306 | |
| 307 | |
| 308 def do_node_ports(node): | |
| 309 | |
| 310 node_port = '' | |
| 311 if len(node) > 1: | |
| 312 node_port = ''.join( [str(a)+str(b) for a,b in node[1] ] ) | |
| 313 | |
| 314 return node_port | |
| 315 | |
| 316 | |
| 317 def push_edge_stmt(str, loc, toks): | |
| 318 | |
| 319 tok_attrs = [a for a in toks if isinstance(a, P_AttrList)] | |
| 320 attrs = {} | |
| 321 for a in tok_attrs: | |
| 322 attrs.update(a.attrs) | |
| 323 | |
| 324 e = [] | |
| 325 | |
| 326 if isinstance(toks[0][0], pydot.Graph): | |
| 327 | |
| 328 n_prev = pydot.frozendict(toks[0][0].obj_dict) | |
| 329 else: | |
| 330 n_prev = toks[0][0] + do_node_ports( toks[0] ) | |
| 331 | |
| 332 if isinstance(toks[2][0], ParseResults): | |
| 333 | |
| 334 n_next_list = [[n.get_name(),] for n in toks[2][0] ] | |
| 335 for n_next in [n for n in n_next_list]: | |
| 336 n_next_port = do_node_ports(n_next) | |
| 337 e.append(pydot.Edge(n_prev, n_next[0]+n_next_port, **attrs)) | |
| 338 | |
| 339 elif isinstance(toks[2][0], pydot.Graph): | |
| 340 | |
| 341 e.append(pydot.Edge(n_prev, | |
| 342 pydot.frozendict(toks[2][0].obj_dict), | |
| 343 **attrs)) | |
| 344 | |
| 345 elif isinstance(toks[2][0], pydot.Node): | |
| 346 | |
| 347 node = toks[2][0] | |
| 348 | |
| 349 if node.get_port() is not None: | |
| 350 name_port = node.get_name() + ":" + node.get_port() | |
| 351 else: | |
| 352 name_port = node.get_name() | |
| 353 | |
| 354 e.append(pydot.Edge(n_prev, name_port, **attrs)) | |
| 355 | |
| 356 # if the target of this edge is the name of a node | |
| 357 elif isinstance(toks[2][0], str_type): | |
| 358 | |
| 359 for n_next in [n for n in tuple(toks)[2::2]]: | |
| 360 | |
| 361 if (isinstance(n_next, P_AttrList) or | |
| 362 not isinstance(n_next[0], str_type)): | |
| 363 continue | |
| 364 | |
| 365 n_next_port = do_node_ports( n_next ) | |
| 366 e.append(pydot.Edge(n_prev, n_next[0]+n_next_port, **attrs)) | |
| 367 | |
| 368 n_prev = n_next[0]+n_next_port | |
| 369 else: | |
| 370 raise Exception( | |
| 371 'Edge target {r} with type {s} unsupported.'.format( | |
| 372 r=toks[2][0], s=type(toks[2][0]))) | |
| 373 | |
| 374 return e | |
| 375 | |
| 376 | |
| 377 | |
| 378 def push_node_stmt(s, loc, toks): | |
| 379 | |
| 380 if len(toks) == 2: | |
| 381 attrs = toks[1].attrs | |
| 382 else: | |
| 383 attrs = {} | |
| 384 | |
| 385 node_name = toks[0] | |
| 386 if isinstance(node_name, list) or isinstance(node_name, tuple): | |
| 387 if len(node_name)>0: | |
| 388 node_name = node_name[0] | |
| 389 | |
| 390 n = pydot.Node(str(node_name), **attrs) | |
| 391 return n | |
| 392 | |
| 393 | |
| 394 | |
| 395 | |
| 396 | |
| 397 | |
| 398 graphparser = None | |
| 399 | |
| 400 def graph_definition(): | |
| 401 | |
| 402 global graphparser | |
| 403 | |
| 404 if not graphparser: | |
| 405 | |
| 406 # punctuation | |
| 407 colon = Literal(":") | |
| 408 lbrace = Literal("{") | |
| 409 rbrace = Literal("}") | |
| 410 lbrack = Literal("[") | |
| 411 rbrack = Literal("]") | |
| 412 lparen = Literal("(") | |
| 413 rparen = Literal(")") | |
| 414 equals = Literal("=") | |
| 415 comma = Literal(",") | |
| 416 dot = Literal(".") | |
| 417 slash = Literal("/") | |
| 418 bslash = Literal("\\") | |
| 419 star = Literal("*") | |
| 420 semi = Literal(";") | |
| 421 at = Literal("@") | |
| 422 minus = Literal("-") | |
| 423 | |
| 424 # keywords | |
| 425 strict_ = CaselessLiteral("strict") | |
| 426 graph_ = CaselessLiteral("graph") | |
| 427 digraph_ = CaselessLiteral("digraph") | |
| 428 subgraph_ = CaselessLiteral("subgraph") | |
| 429 node_ = CaselessLiteral("node") | |
| 430 edge_ = CaselessLiteral("edge") | |
| 431 | |
| 432 | |
| 433 # token definitions | |
| 434 | |
| 435 identifier = Word(alphanums + "_." ).setName("identifier") | |
| 436 | |
| 437 double_quoted_string = QuotedString( | |
| 438 '"', multiline=True, unquoteResults=False, escChar='\\') # dblQuotedString | |
| 439 | |
| 440 noncomma = "".join([c for c in printables if c != ","]) | |
| 441 alphastring_ = OneOrMore(CharsNotIn(noncomma + ' ')) | |
| 442 | |
| 443 def parse_html(s, loc, toks): | |
| 444 return '<%s>' % ''.join(toks[0]) | |
| 445 | |
| 446 | |
| 447 opener = '<' | |
| 448 closer = '>' | |
| 449 html_text = nestedExpr( opener, closer, | |
| 450 ( CharsNotIn( opener + closer ) ) | |
| 451 ).setParseAction(parse_html).leaveWhitespace() | |
| 452 | |
| 453 ID = ( identifier | html_text | | |
| 454 double_quoted_string | #.setParseAction(strip_quotes) | | |
| 455 alphastring_ ).setName("ID") | |
| 456 | |
| 457 | |
| 458 float_number = Combine(Optional(minus) + | |
| 459 OneOrMore(Word(nums + "."))).setName("float_number") | |
| 460 | |
| 461 righthand_id = (float_number | ID ).setName("righthand_id") | |
| 462 | |
| 463 port_angle = (at + ID).setName("port_angle") | |
| 464 | |
| 465 port_location = (OneOrMore(Group(colon + ID)) | | |
| 466 Group(colon + lparen + | |
| 467 ID + comma + ID + rparen)).setName("port_location") | |
| 468 | |
| 469 port = (Group(port_location + Optional(port_angle)) | | |
| 470 Group(port_angle + Optional(port_location))).setName("port") | |
| 471 | |
| 472 node_id = (ID + Optional(port)) | |
| 473 a_list = OneOrMore(ID + Optional(equals + righthand_id) + | |
| 474 Optional(comma.suppress())).setName("a_list") | |
| 475 | |
| 476 attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) + | |
| 477 rbrack.suppress()).setName("attr_list") | |
| 478 | |
| 479 attr_stmt = (Group(graph_ | node_ | edge_) + | |
| 480 attr_list).setName("attr_stmt") | |
| 481 | |
| 482 edgeop = (Literal("--") | Literal("->")).setName("edgeop") | |
| 483 | |
| 484 stmt_list = Forward() | |
| 485 graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) + | |
| 486 rbrace.suppress() + | |
| 487 Optional(semi.suppress())).setName("graph_stmt") | |
| 488 | |
| 489 | |
| 490 edge_point = Forward() | |
| 491 | |
| 492 edgeRHS = OneOrMore(edgeop + edge_point) | |
| 493 edge_stmt = edge_point + edgeRHS + Optional(attr_list) | |
| 494 | |
| 495 subgraph = Group( | |
| 496 subgraph_ + Optional(ID) + graph_stmt).setName("subgraph") | |
| 497 | |
| 498 edge_point << Group( | |
| 499 subgraph | graph_stmt | node_id).setName('edge_point') | |
| 500 | |
| 501 node_stmt = ( | |
| 502 node_id + Optional(attr_list) + | |
| 503 Optional(semi.suppress())).setName("node_stmt") | |
| 504 | |
| 505 assignment = (ID + equals + righthand_id).setName("assignment") | |
| 506 stmt = (assignment | edge_stmt | attr_stmt | | |
| 507 subgraph | graph_stmt | node_stmt).setName("stmt") | |
| 508 stmt_list << OneOrMore(stmt + Optional(semi.suppress())) | |
| 509 | |
| 510 graphparser = OneOrMore( | |
| 511 (Optional(strict_) + Group((graph_ | digraph_)) + | |
| 512 Optional(ID) + graph_stmt).setResultsName("graph")) | |
| 513 | |
| 514 singleLineComment = Group( | |
| 515 "//" + restOfLine) | Group("#" + restOfLine) | |
| 516 | |
| 517 | |
| 518 # actions | |
| 519 | |
| 520 graphparser.ignore(singleLineComment) | |
| 521 graphparser.ignore(cStyleComment) | |
| 522 | |
| 523 assignment.setParseAction(push_attr_list) | |
| 524 a_list.setParseAction(push_attr_list) | |
| 525 edge_stmt.setParseAction(push_edge_stmt) | |
| 526 node_stmt.setParseAction(push_node_stmt) | |
| 527 attr_stmt.setParseAction(push_default_stmt) | |
| 528 | |
| 529 subgraph.setParseAction(push_subgraph_stmt) | |
| 530 graph_stmt.setParseAction(push_graph_stmt) | |
| 531 graphparser.setParseAction(push_top_graph_stmt) | |
| 532 | |
| 533 | |
| 534 return graphparser | |
| 535 | |
| 536 | |
| 537 def parse_dot_data(s): | |
| 538 """Parse DOT description in (unicode) string `s`. | |
| 539 | |
| 540 @return: Graphs that result from parsing. | |
| 541 @rtype: `list` of `pydot.Dot` | |
| 542 """ | |
| 543 global top_graphs | |
| 544 top_graphs = list() | |
| 545 try: | |
| 546 graphparser = graph_definition() | |
| 547 graphparser.parseWithTabs() | |
| 548 tokens = graphparser.parseString(s) | |
| 549 return list(tokens) | |
| 550 except ParseException as err: | |
| 551 print(err.line) | |
| 552 print(" " * (err.column - 1) + "^") | |
| 553 print(err) | |
| 554 return None | 
