Merge pull request #28 from pydot/fix-flakes8
Fixes #2 - fix flakes8 and improve readability
This commit is contained in:
commit
a142f0ab8e
@ -11,3 +11,4 @@ env:
|
|||||||
- TOXENV=py27
|
- TOXENV=py27
|
||||||
- TOXENV=py33
|
- TOXENV=py33
|
||||||
- TOXENV=py34
|
- TOXENV=py34
|
||||||
|
- TOXENV=pep8
|
||||||
|
@ -1,39 +1,35 @@
|
|||||||
# -*- coding: Latin-1 -*-
|
# -*- coding: Latin-1 -*-
|
||||||
"""Graphviz's dot language Python interface.
|
# Graphviz's dot language Python interface.
|
||||||
|
# This module provides with a full interface to create handle modify
|
||||||
|
# and process graphs in Graphviz's dot language.
|
||||||
|
# References:
|
||||||
|
# pydot Homepage: http://code.google.com/p/pydot/
|
||||||
|
# Graphviz: http://www.graphviz.org/
|
||||||
|
# DOT Language: http://www.graphviz.org/doc/info/lang.html
|
||||||
|
# Copyright (c) 2005-2011 Ero Carrera <ero.carrera@gmail.com>
|
||||||
|
# Distributed under MIT license
|
||||||
|
# [http://opensource.org/licenses/mit-license.html].
|
||||||
|
|
||||||
This module provides with a full interface to create handle modify
|
from __future__ import division
|
||||||
and process graphs in Graphviz's dot language.
|
from __future__ import print_function
|
||||||
|
|
||||||
References:
|
|
||||||
|
|
||||||
pydot Homepage: http://code.google.com/p/pydot/
|
|
||||||
Graphviz: http://www.graphviz.org/
|
|
||||||
DOT Language: http://www.graphviz.org/doc/info/lang.html
|
|
||||||
|
|
||||||
Copyright (c) 2005-2011 Ero Carrera <ero.carrera@gmail.com>
|
|
||||||
|
|
||||||
Distributed under MIT license [http://opensource.org/licenses/mit-license.html].
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division, print_function
|
|
||||||
|
|
||||||
__author__ = 'Ero Carrera'
|
|
||||||
__license__ = 'MIT'
|
|
||||||
|
|
||||||
|
import copy
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import copy
|
|
||||||
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from . import _dotparser as dot_parser
|
from pydot_ng import _dotparser as dot_parser
|
||||||
except Exception:
|
except Exception:
|
||||||
print("Couldn't import _dotparser, loading of dot files will not be possible.")
|
print("Couldn't import _dotparser, "
|
||||||
|
"loading of dot files will not be possible.")
|
||||||
|
|
||||||
|
__author__ = 'Ero Carrera'
|
||||||
|
__license__ = 'MIT'
|
||||||
|
|
||||||
PY3 = not sys.version_info < (3, 0, 0)
|
PY3 = not sys.version_info < (3, 0, 0)
|
||||||
|
|
||||||
@ -62,8 +58,7 @@ GRAPH_ATTRIBUTES = set([
|
|||||||
'showboxes', 'size', 'smoothing', 'sortv', 'splines', 'start',
|
'showboxes', 'size', 'smoothing', 'sortv', 'splines', 'start',
|
||||||
'stylesheet', 'target', 'truecolor', 'viewport', 'voro_margin',
|
'stylesheet', 'target', 'truecolor', 'viewport', 'voro_margin',
|
||||||
# for subgraphs
|
# for subgraphs
|
||||||
'rank'
|
'rank'])
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
EDGE_ATTRIBUTES = set([
|
EDGE_ATTRIBUTES = set([
|
||||||
@ -78,8 +73,7 @@ EDGE_ATTRIBUTES = set([
|
|||||||
'nojustify', 'penwidth', 'pos', 'samehead', 'sametail', 'showboxes',
|
'nojustify', 'penwidth', 'pos', 'samehead', 'sametail', 'showboxes',
|
||||||
'style', 'tailURL', 'tailclip', 'tailhref', 'taillabel', 'tailport',
|
'style', 'tailURL', 'tailclip', 'tailhref', 'taillabel', 'tailport',
|
||||||
'tailtarget', 'tailtooltip', 'target', 'tooltip', 'weight',
|
'tailtarget', 'tailtooltip', 'target', 'tooltip', 'weight',
|
||||||
'rank'
|
'rank'])
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
NODE_ATTRIBUTES = set([
|
NODE_ATTRIBUTES = set([
|
||||||
@ -91,35 +85,34 @@ NODE_ATTRIBUTES = set([
|
|||||||
'shape', 'shapefile', 'showboxes', 'sides', 'skew', 'sortv', 'style',
|
'shape', 'shapefile', 'showboxes', 'sides', 'skew', 'sortv', 'style',
|
||||||
'target', 'tooltip', 'vertices', 'width', 'z',
|
'target', 'tooltip', 'vertices', 'width', 'z',
|
||||||
# The following are attributes dot2tex
|
# The following are attributes dot2tex
|
||||||
'texlbl', 'texmode'
|
'texlbl', 'texmode'])
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
CLUSTER_ATTRIBUTES = set([
|
CLUSTER_ATTRIBUTES = set([
|
||||||
'K', 'URL', 'bgcolor', 'color', 'colorscheme',
|
'K', 'URL', 'bgcolor', 'color', 'colorscheme',
|
||||||
'fillcolor', 'fontcolor', 'fontname', 'fontsize', 'label', 'labeljust',
|
'fillcolor', 'fontcolor', 'fontname', 'fontsize', 'label', 'labeljust',
|
||||||
'labelloc', 'lheight', 'lp', 'lwidth', 'nojustify', 'pencolor',
|
'labelloc', 'lheight', 'lp', 'lwidth', 'nojustify', 'pencolor',
|
||||||
'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'
|
'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'])
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def is_string_like(obj): # from John Hunter, types-free version
|
def is_string_like(obj):
|
||||||
"""Check if obj is string."""
|
"""Check if obj is string. from John Hunter, types-free version"""
|
||||||
try:
|
try:
|
||||||
obj + ''
|
obj + ''
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_fobj(fname, mode='w+'):
|
def get_fobj(fname, mode='w+'):
|
||||||
"""Obtain a proper file object.
|
"""Obtain a proper file object.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : string, file object, file descriptor
|
fname : string, file object, file descriptor
|
||||||
If a string or file descriptor, then we create a file object. If *fname*
|
If a string or file descriptor, then we create a file object. If
|
||||||
is a file object, then we do nothing and ignore the specified *mode*
|
*fname* is a file object, then we do nothing and ignore the specified
|
||||||
parameter.
|
*mode* parameter.
|
||||||
mode : str
|
mode : str
|
||||||
The mode of the file to be opened.
|
The mode of the file to be opened.
|
||||||
|
|
||||||
@ -129,9 +122,10 @@ def get_fobj(fname, mode='w+'):
|
|||||||
The file object.
|
The file object.
|
||||||
close : bool
|
close : bool
|
||||||
If *fname* was a string, then *close* will be *True* to signify that
|
If *fname* was a string, then *close* will be *True* to signify that
|
||||||
the file object should be closed after writing to it. Otherwise, *close*
|
the file object should be closed after writing to it. Otherwise,
|
||||||
will be *False* signifying that the user, in essence, created the file
|
*close* will be *False* signifying that the user, in essence,
|
||||||
object already and that subsequent operations should not close it.
|
created the file object already and that subsequent operations
|
||||||
|
should not close it.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if is_string_like(fname):
|
if is_string_like(fname):
|
||||||
@ -208,8 +202,7 @@ dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict']
|
|||||||
|
|
||||||
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
|
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
|
||||||
id_re_alpha_nums_with_ports = re.compile(
|
id_re_alpha_nums_with_ports = re.compile(
|
||||||
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE
|
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE)
|
||||||
)
|
|
||||||
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
|
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
|
||||||
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
|
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
|
||||||
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
|
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
|
||||||
@ -241,8 +234,7 @@ def needs_quotes(s):
|
|||||||
|
|
||||||
for test_re in [
|
for test_re in [
|
||||||
id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
|
id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
|
||||||
id_re_html, id_re_alpha_nums_with_ports
|
id_re_html, id_re_alpha_nums_with_ports]:
|
||||||
]:
|
|
||||||
if test_re.match(s):
|
if test_re.match(s):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -371,9 +363,7 @@ def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False):
|
|||||||
graph.add_edge(
|
graph.add_edge(
|
||||||
Edge(
|
Edge(
|
||||||
node_prefix + node_orig,
|
node_prefix + node_orig,
|
||||||
node_prefix + node_dest
|
node_prefix + node_dest))
|
||||||
)
|
|
||||||
)
|
|
||||||
node_dest += 1
|
node_dest += 1
|
||||||
node_orig += 1
|
node_orig += 1
|
||||||
|
|
||||||
@ -409,9 +399,7 @@ def graph_from_incidence_matrix(matrix, node_prefix='', directed=False):
|
|||||||
graph.add_edge(
|
graph.add_edge(
|
||||||
Edge(
|
Edge(
|
||||||
node_prefix + abs(nodes[0]),
|
node_prefix + abs(nodes[0]),
|
||||||
node_prefix + nodes[1]
|
node_prefix + nodes[1]))
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not directed:
|
if not directed:
|
||||||
graph.set_simplify(True)
|
graph.set_simplify(True)
|
||||||
@ -431,7 +419,8 @@ def __find_executables(path):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
success = False
|
success = False
|
||||||
progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': '', 'sfdp': ''}
|
progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': '',
|
||||||
|
'sfdp': ''}
|
||||||
|
|
||||||
was_quoted = False
|
was_quoted = False
|
||||||
path = path.strip()
|
path = path.strip()
|
||||||
@ -515,7 +504,8 @@ def find_graphviz():
|
|||||||
|
|
||||||
def RegOpenKeyEx(key, subkey, opt, sam):
|
def RegOpenKeyEx(key, subkey, opt, sam):
|
||||||
result = ctypes.c_uint(0)
|
result = ctypes.c_uint(0)
|
||||||
ctypes.windll.advapi32.RegOpenKeyExA(key, subkey, opt, sam, ctypes.byref(result))
|
ctypes.windll.advapi32.RegOpenKeyExA(key, subkey, opt, sam,
|
||||||
|
ctypes.byref(result))
|
||||||
return result.value
|
return result.value
|
||||||
|
|
||||||
def RegQueryValueEx(hkey, valuename):
|
def RegQueryValueEx(hkey, valuename):
|
||||||
@ -526,8 +516,7 @@ def find_graphviz():
|
|||||||
# this has a return value, which we should probably check
|
# this has a return value, which we should probably check
|
||||||
ctypes.windll.advapi32.RegQueryValueExA(
|
ctypes.windll.advapi32.RegQueryValueExA(
|
||||||
hkey, valuename, 0, ctypes.byref(data_type),
|
hkey, valuename, 0, ctypes.byref(data_type),
|
||||||
data, ctypes.byref(data_len)
|
data, ctypes.byref(data_len))
|
||||||
)
|
|
||||||
|
|
||||||
return data.value
|
return data.value
|
||||||
|
|
||||||
@ -542,22 +531,21 @@ def find_graphviz():
|
|||||||
hkey = None
|
hkey = None
|
||||||
potentialKeys = [
|
potentialKeys = [
|
||||||
"SOFTWARE\\ATT\\Graphviz",
|
"SOFTWARE\\ATT\\Graphviz",
|
||||||
"SOFTWARE\\AT&T Research Labs\\Graphviz"
|
"SOFTWARE\\AT&T Research Labs\\Graphviz"]
|
||||||
]
|
|
||||||
for potentialKey in potentialKeys:
|
for potentialKey in potentialKeys:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hkey = RegOpenKeyEx(
|
hkey = RegOpenKeyEx(
|
||||||
HKEY_LOCAL_MACHINE,
|
HKEY_LOCAL_MACHINE,
|
||||||
potentialKey, 0, KEY_QUERY_VALUE
|
potentialKey, 0, KEY_QUERY_VALUE)
|
||||||
)
|
|
||||||
|
|
||||||
if hkey is not None:
|
if hkey is not None:
|
||||||
path = RegQueryValueEx(hkey, "InstallPath")
|
path = RegQueryValueEx(hkey, "InstallPath")
|
||||||
RegCloseKey(hkey)
|
RegCloseKey(hkey)
|
||||||
|
|
||||||
# The regitry variable might exist, left by old installations
|
# The regitry variable might exist, left by
|
||||||
# but with no value, in those cases we keep searching...
|
# old installations but with no value, in those cases
|
||||||
|
# we keep searching...
|
||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -565,11 +553,9 @@ def find_graphviz():
|
|||||||
path = os.path.join(path, "bin")
|
path = os.path.join(path, "bin")
|
||||||
progs = __find_executables(path)
|
progs = __find_executables(path)
|
||||||
if progs is not None:
|
if progs is not None:
|
||||||
#print("Used Windows registry")
|
|
||||||
return progs
|
return progs
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
#raise
|
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
@ -579,7 +565,6 @@ def find_graphviz():
|
|||||||
for path in os.environ['PATH'].split(os.pathsep):
|
for path in os.environ['PATH'].split(os.pathsep):
|
||||||
progs = __find_executables(path)
|
progs = __find_executables(path)
|
||||||
if progs is not None:
|
if progs is not None:
|
||||||
#print("Used path")
|
|
||||||
return progs
|
return progs
|
||||||
|
|
||||||
# Method 3 (Windows only)
|
# Method 3 (Windows only)
|
||||||
@ -590,28 +575,25 @@ def find_graphviz():
|
|||||||
if 'PROGRAMFILES' in os.environ:
|
if 'PROGRAMFILES' in os.environ:
|
||||||
# Note, we could also use the win32api to get this
|
# Note, we could also use the win32api to get this
|
||||||
# information, but win32api may not be installed.
|
# information, but win32api may not be installed.
|
||||||
path = os.path.join(os.environ['PROGRAMFILES'], 'ATT', 'GraphViz', 'bin')
|
path = os.path.join(os.environ['PROGRAMFILES'], 'ATT',
|
||||||
|
'GraphViz', 'bin')
|
||||||
else:
|
else:
|
||||||
#Just in case, try the default...
|
# Just in case, try the default...
|
||||||
path = r"C:\Program Files\att\Graphviz\bin"
|
path = r"C:\Program Files\att\Graphviz\bin"
|
||||||
|
|
||||||
progs = __find_executables(path)
|
progs = __find_executables(path)
|
||||||
|
|
||||||
if progs is not None:
|
if progs is not None:
|
||||||
|
|
||||||
#print("Used default install location")
|
|
||||||
return progs
|
return progs
|
||||||
|
|
||||||
for path in (
|
for path in (
|
||||||
'/usr/bin', '/usr/local/bin',
|
'/usr/bin', '/usr/local/bin',
|
||||||
'/opt/local/bin',
|
'/opt/local/bin',
|
||||||
'/opt/bin', '/sw/bin', '/usr/share',
|
'/opt/bin', '/sw/bin', '/usr/share',
|
||||||
'/Applications/Graphviz.app/Contents/MacOS/'
|
'/Applications/Graphviz.app/Contents/MacOS/'):
|
||||||
):
|
|
||||||
|
|
||||||
progs = __find_executables(path)
|
progs = __find_executables(path)
|
||||||
if progs is not None:
|
if progs is not None:
|
||||||
#print("Used path")
|
|
||||||
return progs
|
return progs
|
||||||
|
|
||||||
# Failed to find GraphViz
|
# Failed to find GraphViz
|
||||||
@ -626,23 +608,18 @@ class Common(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
|
|
||||||
dict = copy.copy(self.obj_dict)
|
dict = copy.copy(self.obj_dict)
|
||||||
|
|
||||||
return dict
|
return dict
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
|
|
||||||
self.obj_dict = state
|
self.obj_dict = state
|
||||||
|
|
||||||
def __get_attribute__(self, attr):
|
def __get_attribute__(self, attr):
|
||||||
"""Look for default attributes for this node"""
|
"""Look for default attributes for this node"""
|
||||||
|
|
||||||
attr_val = self.obj_dict['attributes'].get(attr, None)
|
attr_val = self.obj_dict['attributes'].get(attr, None)
|
||||||
|
|
||||||
if attr_val is None:
|
if attr_val is None:
|
||||||
# get the defaults for nodes/edges
|
# get the defaults for nodes/edges
|
||||||
|
|
||||||
default_node_name = self.obj_dict['type']
|
default_node_name = self.obj_dict['type']
|
||||||
|
|
||||||
# The defaults for graphs are set on a node named 'graph'
|
# The defaults for graphs are set on a node named 'graph'
|
||||||
@ -676,11 +653,9 @@ class Common(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def set_parent_graph(self, parent_graph):
|
def set_parent_graph(self, parent_graph):
|
||||||
|
|
||||||
self.obj_dict['parent_graph'] = parent_graph
|
self.obj_dict['parent_graph'] = parent_graph
|
||||||
|
|
||||||
def get_parent_graph(self):
|
def get_parent_graph(self):
|
||||||
|
|
||||||
return self.obj_dict.get('parent_graph', None)
|
return self.obj_dict.get('parent_graph', None)
|
||||||
|
|
||||||
def set(self, name, value):
|
def set(self, name, value):
|
||||||
@ -693,7 +668,6 @@ class Common(object):
|
|||||||
|
|
||||||
which are defined for all the existing attributes.
|
which are defined for all the existing attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.obj_dict['attributes'][name] = value
|
self.obj_dict['attributes'][name] = value
|
||||||
|
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
@ -706,42 +680,32 @@ class Common(object):
|
|||||||
|
|
||||||
which are defined for all the existing attributes.
|
which are defined for all the existing attributes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.obj_dict['attributes'].get(name, None)
|
return self.obj_dict['attributes'].get(name, None)
|
||||||
|
|
||||||
def get_attributes(self):
|
def get_attributes(self):
|
||||||
""""""
|
|
||||||
|
|
||||||
return self.obj_dict['attributes']
|
return self.obj_dict['attributes']
|
||||||
|
|
||||||
def set_sequence(self, seq):
|
def set_sequence(self, seq):
|
||||||
|
|
||||||
self.obj_dict['sequence'] = seq
|
self.obj_dict['sequence'] = seq
|
||||||
|
|
||||||
def get_sequence(self):
|
def get_sequence(self):
|
||||||
|
|
||||||
return self.obj_dict['sequence']
|
return self.obj_dict['sequence']
|
||||||
|
|
||||||
def create_attribute_methods(self, obj_attributes):
|
def create_attribute_methods(self, obj_attributes):
|
||||||
|
|
||||||
#for attr in self.obj_dict['attributes']:
|
|
||||||
for attr in obj_attributes:
|
for attr in obj_attributes:
|
||||||
|
|
||||||
# Generate all the Setter methods.
|
# Generate all the Setter methods.
|
||||||
#
|
|
||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'set_' + attr,
|
'set_' + attr,
|
||||||
lambda x, a=attr: self.obj_dict['attributes'].__setitem__(a, x)
|
lambda x, a=attr: self.obj_dict['attributes'].
|
||||||
)
|
__setitem__(a, x))
|
||||||
|
|
||||||
# Generate all the Getter methods.
|
# Generate all the Getter methods.
|
||||||
#
|
self.__setattr__('get_' + attr,
|
||||||
self.__setattr__('get_' + attr, lambda a=attr: self.__get_attribute__(a))
|
lambda a=attr: self.__get_attribute__(a))
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
"""General error handling class.
|
"""General error handling class."""
|
||||||
"""
|
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
@ -750,8 +714,7 @@ class Error(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class InvocationException(Exception):
|
class InvocationException(Exception):
|
||||||
"""To indicate that a ploblem occurred while running any of the GraphViz executables.
|
"""Indicate a ploblem occurred running any of the GraphViz executables."""
|
||||||
"""
|
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
@ -773,11 +736,9 @@ class Node(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name='', obj_dict=None, **attrs):
|
def __init__(self, name='', obj_dict=None, **attrs):
|
||||||
|
|
||||||
#
|
|
||||||
# Nodes will take attributes of all other types because the defaults
|
# Nodes will take attributes of all other types because the defaults
|
||||||
# for any GraphViz object are dealt with as if they were Node definitions
|
# for any GraphViz object are dealt with as if they were
|
||||||
#
|
# Node definitions
|
||||||
|
|
||||||
if obj_dict is not None:
|
if obj_dict is not None:
|
||||||
self.obj_dict = obj_dict
|
self.obj_dict = obj_dict
|
||||||
@ -785,7 +746,6 @@ class Node(Common):
|
|||||||
self.obj_dict = dict()
|
self.obj_dict = dict()
|
||||||
|
|
||||||
# Copy the attributes
|
# Copy the attributes
|
||||||
#
|
|
||||||
self.obj_dict['attributes'] = dict(attrs)
|
self.obj_dict['attributes'] = dict(attrs)
|
||||||
self.obj_dict['type'] = 'node'
|
self.obj_dict['type'] = 'node'
|
||||||
self.obj_dict['parent_graph'] = None
|
self.obj_dict['parent_graph'] = None
|
||||||
@ -793,7 +753,6 @@ class Node(Common):
|
|||||||
self.obj_dict['sequence'] = None
|
self.obj_dict['sequence'] = None
|
||||||
|
|
||||||
# Remove the compass point
|
# Remove the compass point
|
||||||
#
|
|
||||||
port = None
|
port = None
|
||||||
if isinstance(name, basestring) and not name.startswith('"'):
|
if isinstance(name, basestring) and not name.startswith('"'):
|
||||||
idx = name.find(':')
|
idx = name.find(':')
|
||||||
@ -810,17 +769,14 @@ class Node(Common):
|
|||||||
|
|
||||||
def set_name(self, node_name):
|
def set_name(self, node_name):
|
||||||
"""Set the node's name."""
|
"""Set the node's name."""
|
||||||
|
|
||||||
self.obj_dict['name'] = node_name
|
self.obj_dict['name'] = node_name
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""Get the node's name."""
|
"""Get the node's name."""
|
||||||
|
|
||||||
return self.obj_dict['name']
|
return self.obj_dict['name']
|
||||||
|
|
||||||
def get_port(self):
|
def get_port(self):
|
||||||
"""Get the node's port."""
|
"""Get the node's port."""
|
||||||
|
|
||||||
return self.obj_dict['port']
|
return self.obj_dict['port']
|
||||||
|
|
||||||
def add_style(self, style):
|
def add_style(self, style):
|
||||||
@ -835,16 +791,14 @@ class Node(Common):
|
|||||||
self.obj_dict['attributes']['style'] = ','.join(styles)
|
self.obj_dict['attributes']['style'] = ','.join(styles)
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
"""Returns a string representation of the node in dot language.
|
"""Returns a string representation of the node in dot language."""
|
||||||
"""
|
|
||||||
|
|
||||||
# RMF: special case defaults for node, edge and graph properties.
|
# RMF: special case defaults for node, edge and graph properties.
|
||||||
#
|
|
||||||
node = quote_if_necessary(self.obj_dict['name'])
|
node = quote_if_necessary(self.obj_dict['name'])
|
||||||
|
|
||||||
node_attr = list()
|
node_attr = list()
|
||||||
|
|
||||||
for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
|
for attr, value in sorted(self.obj_dict['attributes'].items(),
|
||||||
|
key=itemgetter(0)):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
node_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
node_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -891,20 +845,15 @@ class Edge(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, src='', dst='', obj_dict=None, **attrs):
|
def __init__(self, src='', dst='', obj_dict=None, **attrs):
|
||||||
|
|
||||||
if isinstance(src, (list, tuple)) and dst == '':
|
if isinstance(src, (list, tuple)) and dst == '':
|
||||||
src, dst = src
|
src, dst = src
|
||||||
|
|
||||||
if obj_dict is not None:
|
if obj_dict is not None:
|
||||||
|
|
||||||
self.obj_dict = obj_dict
|
self.obj_dict = obj_dict
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
self.obj_dict = dict()
|
self.obj_dict = dict()
|
||||||
|
|
||||||
# Copy the attributes
|
# Copy the attributes
|
||||||
#
|
|
||||||
self.obj_dict['attributes'] = dict(attrs)
|
self.obj_dict['attributes'] = dict(attrs)
|
||||||
self.obj_dict['type'] = 'edge'
|
self.obj_dict['type'] = 'edge'
|
||||||
self.obj_dict['parent_graph'] = None
|
self.obj_dict['parent_graph'] = None
|
||||||
@ -925,12 +874,10 @@ class Edge(Common):
|
|||||||
|
|
||||||
def get_source(self):
|
def get_source(self):
|
||||||
"""Get the edges source node name."""
|
"""Get the edges source node name."""
|
||||||
|
|
||||||
return self.obj_dict['points'][0]
|
return self.obj_dict['points'][0]
|
||||||
|
|
||||||
def get_destination(self):
|
def get_destination(self):
|
||||||
"""Get the edge's destination node name."""
|
"""Get the edge's destination node name."""
|
||||||
|
|
||||||
return self.obj_dict['points'][1]
|
return self.obj_dict['points'][1]
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@ -954,7 +901,6 @@ class Edge(Common):
|
|||||||
|
|
||||||
# If the graph is undirected, the edge has neither
|
# If the graph is undirected, the edge has neither
|
||||||
# source nor destination.
|
# source nor destination.
|
||||||
#
|
|
||||||
if ((self.get_source() == edge.get_source() and
|
if ((self.get_source() == edge.get_source() and
|
||||||
self.get_destination() == edge.get_destination()) or
|
self.get_destination() == edge.get_destination()) or
|
||||||
(edge.get_source() == self.get_destination() and
|
(edge.get_source() == self.get_destination() and
|
||||||
@ -965,11 +911,9 @@ class Edge(Common):
|
|||||||
if (self.get_source() == edge.get_source() and
|
if (self.get_source() == edge.get_source() and
|
||||||
self.get_destination() == edge.get_destination()):
|
self.get_destination() == edge.get_destination()):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def parse_node_ref(self, node_str):
|
def parse_node_ref(self, node_str):
|
||||||
|
|
||||||
if not isinstance(node_str, str):
|
if not isinstance(node_str, str):
|
||||||
return node_str
|
return node_str
|
||||||
|
|
||||||
@ -985,19 +929,14 @@ class Edge(Common):
|
|||||||
if node_port_idx > 0:
|
if node_port_idx > 0:
|
||||||
a = node_str[:node_port_idx]
|
a = node_str[:node_port_idx]
|
||||||
b = node_str[node_port_idx + 1:]
|
b = node_str[node_port_idx + 1:]
|
||||||
|
|
||||||
node = quote_if_necessary(a)
|
node = quote_if_necessary(a)
|
||||||
|
|
||||||
node += ':' + quote_if_necessary(b)
|
node += ':' + quote_if_necessary(b)
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
return node_str
|
return node_str
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
"""Returns a string representation of the edge in dot language.
|
"""Returns a string representation of the edge in dot language."""
|
||||||
"""
|
|
||||||
|
|
||||||
src = self.parse_node_ref(self.get_source())
|
src = self.parse_node_ref(self.get_source())
|
||||||
dst = self.parse_node_ref(self.get_destination())
|
dst = self.parse_node_ref(self.get_destination())
|
||||||
|
|
||||||
@ -1026,7 +965,8 @@ class Edge(Common):
|
|||||||
|
|
||||||
edge_attr = list()
|
edge_attr = list()
|
||||||
|
|
||||||
for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
|
for attr, value in sorted(self.obj_dict['attributes'].items(),
|
||||||
|
key=itemgetter(0)):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
edge_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
edge_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -1077,8 +1017,9 @@ class Graph(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, graph_name='G', obj_dict=None, graph_type='digraph', strict=False,
|
self, graph_name='G', obj_dict=None, graph_type='digraph',
|
||||||
suppress_disconnected=False, simplify=False, **attrs):
|
strict=False, suppress_disconnected=False, simplify=False,
|
||||||
|
**attrs):
|
||||||
|
|
||||||
if obj_dict is not None:
|
if obj_dict is not None:
|
||||||
self.obj_dict = obj_dict
|
self.obj_dict = obj_dict
|
||||||
@ -1090,8 +1031,7 @@ class Graph(Common):
|
|||||||
if graph_type not in ['graph', 'digraph']:
|
if graph_type not in ['graph', 'digraph']:
|
||||||
raise Error((
|
raise Error((
|
||||||
'Invalid type "%s". Accepted graph types are: '
|
'Invalid type "%s". Accepted graph types are: '
|
||||||
'graph, digraph, subgraph' % graph_type
|
'graph, digraph, subgraph' % graph_type))
|
||||||
))
|
|
||||||
|
|
||||||
self.obj_dict['name'] = quote_if_necessary(graph_name)
|
self.obj_dict['name'] = quote_if_necessary(graph_name)
|
||||||
self.obj_dict['type'] = graph_type
|
self.obj_dict['type'] = graph_type
|
||||||
@ -1163,7 +1103,6 @@ class Graph(Common):
|
|||||||
only one edge between two nodes. removing the
|
only one edge between two nodes. removing the
|
||||||
duplicated ones.
|
duplicated ones.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.obj_dict['simplify'] = simplify
|
self.obj_dict['simplify'] = simplify
|
||||||
|
|
||||||
def get_simplify(self):
|
def get_simplify(self):
|
||||||
@ -1171,27 +1110,22 @@ class Graph(Common):
|
|||||||
|
|
||||||
Refer to set_simplify for more information.
|
Refer to set_simplify for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.obj_dict['simplify']
|
return self.obj_dict['simplify']
|
||||||
|
|
||||||
def set_type(self, graph_type):
|
def set_type(self, graph_type):
|
||||||
"""Set the graph's type, 'graph' or 'digraph'."""
|
"""Set the graph's type, 'graph' or 'digraph'."""
|
||||||
|
|
||||||
self.obj_dict['type'] = graph_type
|
self.obj_dict['type'] = graph_type
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
"""Get the graph's type, 'graph' or 'digraph'."""
|
"""Get the graph's type, 'graph' or 'digraph'."""
|
||||||
|
|
||||||
return self.obj_dict['type']
|
return self.obj_dict['type']
|
||||||
|
|
||||||
def set_name(self, graph_name):
|
def set_name(self, graph_name):
|
||||||
"""Set the graph's name."""
|
"""Set the graph's name."""
|
||||||
|
|
||||||
self.obj_dict['name'] = graph_name
|
self.obj_dict['name'] = graph_name
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""Get the graph's name."""
|
"""Get the graph's name."""
|
||||||
|
|
||||||
return self.obj_dict['name']
|
return self.obj_dict['name']
|
||||||
|
|
||||||
def set_strict(self, val):
|
def set_strict(self, val):
|
||||||
@ -1199,7 +1133,6 @@ class Graph(Common):
|
|||||||
|
|
||||||
This option is only valid for top level graphs.
|
This option is only valid for top level graphs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.obj_dict['strict'] = val
|
self.obj_dict['strict'] = val
|
||||||
|
|
||||||
def get_strict(self, val):
|
def get_strict(self, val):
|
||||||
@ -1207,7 +1140,6 @@ class Graph(Common):
|
|||||||
|
|
||||||
This option is only valid for top level graphs.
|
This option is only valid for top level graphs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.obj_dict['strict']
|
return self.obj_dict['strict']
|
||||||
|
|
||||||
def set_suppress_disconnected(self, val):
|
def set_suppress_disconnected(self, val):
|
||||||
@ -1217,7 +1149,6 @@ class Graph(Common):
|
|||||||
edges. This option works also for subgraphs and has effect only in the
|
edges. This option works also for subgraphs and has effect only in the
|
||||||
current graph/subgraph.
|
current graph/subgraph.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.obj_dict['suppress_disconnected'] = val
|
self.obj_dict['suppress_disconnected'] = val
|
||||||
|
|
||||||
def get_suppress_disconnected(self, val):
|
def get_suppress_disconnected(self, val):
|
||||||
@ -1225,7 +1156,6 @@ class Graph(Common):
|
|||||||
|
|
||||||
Refer to set_suppress_disconnected for more information.
|
Refer to set_suppress_disconnected for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.obj_dict['suppress_disconnected']
|
return self.obj_dict['suppress_disconnected']
|
||||||
|
|
||||||
def get_next_sequence_number(self):
|
def get_next_sequence_number(self):
|
||||||
@ -1239,19 +1169,21 @@ class Graph(Common):
|
|||||||
It takes a node object as its only argument and returns
|
It takes a node object as its only argument and returns
|
||||||
None.
|
None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(graph_node, Node):
|
if not isinstance(graph_node, Node):
|
||||||
raise TypeError('add_node() received a non node class object: ' + str(graph_node))
|
raise TypeError(''.join([
|
||||||
|
'add_node() received a non node class object: ',
|
||||||
|
str(graph_node)]))
|
||||||
|
|
||||||
node = self.get_node(graph_node.get_name())
|
node = self.get_node(graph_node.get_name())
|
||||||
|
|
||||||
if not node:
|
if not node:
|
||||||
self.obj_dict['nodes'][graph_node.get_name()] = [graph_node.obj_dict]
|
self.obj_dict['nodes'][graph_node.get_name()] =\
|
||||||
|
[graph_node.obj_dict]
|
||||||
|
|
||||||
#self.node_dict[graph_node.get_name()] = graph_node.attributes
|
|
||||||
graph_node.set_parent_graph(self.get_parent_graph())
|
graph_node.set_parent_graph(self.get_parent_graph())
|
||||||
else:
|
else:
|
||||||
self.obj_dict['nodes'][graph_node.get_name()].append(graph_node.obj_dict)
|
self.obj_dict['nodes'][graph_node.get_name()].\
|
||||||
|
append(graph_node.obj_dict)
|
||||||
|
|
||||||
graph_node.set_sequence(self.get_next_sequence_number())
|
graph_node.set_sequence(self.get_next_sequence_number())
|
||||||
|
|
||||||
@ -1296,21 +1228,18 @@ class Graph(Common):
|
|||||||
Node instances is returned.
|
Node instances is returned.
|
||||||
An empty list is returned otherwise.
|
An empty list is returned otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
match = list()
|
match = list()
|
||||||
|
|
||||||
if name in self.obj_dict['nodes']:
|
if name in self.obj_dict['nodes']:
|
||||||
match.extend([
|
match.extend([
|
||||||
Node(obj_dict=obj_dict)
|
Node(obj_dict=obj_dict)
|
||||||
for obj_dict
|
for obj_dict
|
||||||
in self.obj_dict['nodes'][name]
|
in self.obj_dict['nodes'][name]])
|
||||||
])
|
|
||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
def get_nodes(self):
|
def get_nodes(self):
|
||||||
"""Get the list of Node instances."""
|
"""Get the list of Node instances."""
|
||||||
|
|
||||||
return self.get_node_list()
|
return self.get_node_list()
|
||||||
|
|
||||||
def get_node_list(self):
|
def get_node_list(self):
|
||||||
@ -1319,27 +1248,24 @@ class Graph(Common):
|
|||||||
This method returns the list of Node instances
|
This method returns the list of Node instances
|
||||||
composing the graph.
|
composing the graph.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
node_objs = list()
|
node_objs = list()
|
||||||
|
|
||||||
for node, obj_dict_list in self.obj_dict['nodes'].items():
|
for node, obj_dict_list in self.obj_dict['nodes'].items():
|
||||||
node_objs.extend([
|
node_objs.extend([
|
||||||
Node(obj_dict=obj_d)
|
Node(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list])
|
||||||
])
|
|
||||||
|
|
||||||
return node_objs
|
return node_objs
|
||||||
|
|
||||||
def add_edge(self, graph_edge):
|
def add_edge(self, graph_edge):
|
||||||
"""Adds an edge object to the graph.
|
"""Adds an edge object to the graph.
|
||||||
|
|
||||||
It takes a edge object as its only argument and returns
|
It takes a edge object as its only argument and returns None.
|
||||||
None.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(graph_edge, Edge):
|
if not isinstance(graph_edge, Edge):
|
||||||
raise TypeError('add_edge() received a non edge class object: ' + str(graph_edge))
|
raise TypeError(''.join(['add_edge() received a non edge class '
|
||||||
|
'object: ', str(graph_edge)]))
|
||||||
|
|
||||||
edge_points = (graph_edge.get_source(), graph_edge.get_destination())
|
edge_points = (graph_edge.get_source(), graph_edge.get_destination())
|
||||||
|
|
||||||
@ -1384,7 +1310,8 @@ class Graph(Common):
|
|||||||
dst = dst.get_name()
|
dst = dst.get_name()
|
||||||
|
|
||||||
if (src, dst) in self.obj_dict['edges']:
|
if (src, dst) in self.obj_dict['edges']:
|
||||||
if index is not None and index < len(self.obj_dict['edges'][(src, dst)]):
|
if (index is not None and index <
|
||||||
|
len(self.obj_dict['edges'][(src, dst)])):
|
||||||
del self.obj_dict['edges'][(src, dst)][index]
|
del self.obj_dict['edges'][(src, dst)][index]
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -1415,8 +1342,7 @@ class Graph(Common):
|
|||||||
|
|
||||||
if edge_points in self.obj_dict['edges'] or (
|
if edge_points in self.obj_dict['edges'] or (
|
||||||
self.get_top_graph_type() == 'graph' and
|
self.get_top_graph_type() == 'graph' and
|
||||||
edge_points_reverse in self.obj_dict['edges']
|
edge_points_reverse in self.obj_dict['edges']):
|
||||||
):
|
|
||||||
|
|
||||||
edges_obj_dict = self.obj_dict['edges'].get(
|
edges_obj_dict = self.obj_dict['edges'].get(
|
||||||
edge_points,
|
edge_points,
|
||||||
@ -1424,8 +1350,8 @@ class Graph(Common):
|
|||||||
|
|
||||||
for edge_obj_dict in edges_obj_dict:
|
for edge_obj_dict in edges_obj_dict:
|
||||||
match.append(
|
match.append(
|
||||||
Edge(edge_points[0], edge_points[1], obj_dict=edge_obj_dict)
|
Edge(edge_points[0], edge_points[1],
|
||||||
)
|
obj_dict=edge_obj_dict))
|
||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
@ -1438,15 +1364,13 @@ class Graph(Common):
|
|||||||
This method returns the list of Edge instances
|
This method returns the list of Edge instances
|
||||||
composing the graph.
|
composing the graph.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
edge_objs = list()
|
edge_objs = list()
|
||||||
|
|
||||||
for edge, obj_dict_list in self.obj_dict['edges'].items():
|
for edge, obj_dict_list in self.obj_dict['edges'].items():
|
||||||
edge_objs.extend([
|
edge_objs.extend([
|
||||||
Edge(obj_dict=obj_d)
|
Edge(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list])
|
||||||
])
|
|
||||||
|
|
||||||
return edge_objs
|
return edge_objs
|
||||||
|
|
||||||
@ -1457,8 +1381,11 @@ class Graph(Common):
|
|||||||
None.
|
None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster):
|
if (not isinstance(sgraph, Subgraph) and
|
||||||
raise TypeError('add_subgraph() received a non subgraph class object:' + str(sgraph))
|
not isinstance(sgraph, Cluster)):
|
||||||
|
raise TypeError(''.join([
|
||||||
|
'add_subgraph() received a non subgraph class object:',
|
||||||
|
str(sgraph)]))
|
||||||
|
|
||||||
if sgraph.get_name() in self.obj_dict['subgraphs']:
|
if sgraph.get_name() in self.obj_dict['subgraphs']:
|
||||||
|
|
||||||
@ -1489,7 +1416,6 @@ class Graph(Common):
|
|||||||
sgraphs_obj_dict = self.obj_dict['subgraphs'].get(name)
|
sgraphs_obj_dict = self.obj_dict['subgraphs'].get(name)
|
||||||
|
|
||||||
for obj_dict_list in sgraphs_obj_dict:
|
for obj_dict_list in sgraphs_obj_dict:
|
||||||
#match.extend(Subgraph(obj_dict = obj_d) for obj_d in obj_dict_list)
|
|
||||||
match.append(Subgraph(obj_dict=obj_dict_list))
|
match.append(Subgraph(obj_dict=obj_dict_list))
|
||||||
|
|
||||||
return match
|
return match
|
||||||
@ -1510,13 +1436,11 @@ class Graph(Common):
|
|||||||
sgraph_objs.extend([
|
sgraph_objs.extend([
|
||||||
Subgraph(obj_dict=obj_d)
|
Subgraph(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list])
|
||||||
])
|
|
||||||
|
|
||||||
return sgraph_objs
|
return sgraph_objs
|
||||||
|
|
||||||
def set_parent_graph(self, parent_graph):
|
def set_parent_graph(self, parent_graph):
|
||||||
|
|
||||||
self.obj_dict['parent_graph'] = parent_graph
|
self.obj_dict['parent_graph'] = parent_graph
|
||||||
|
|
||||||
for obj_list in self.obj_dict['nodes'].values():
|
for obj_list in self.obj_dict['nodes'].values():
|
||||||
@ -1544,14 +1468,17 @@ class Graph(Common):
|
|||||||
graph.append('strict ')
|
graph.append('strict ')
|
||||||
|
|
||||||
if self.obj_dict['name'] == '':
|
if self.obj_dict['name'] == '':
|
||||||
if 'show_keyword' in self.obj_dict and self.obj_dict['show_keyword']:
|
if ('show_keyword' in self.obj_dict and
|
||||||
|
self.obj_dict['show_keyword']):
|
||||||
graph.append('subgraph {\n')
|
graph.append('subgraph {\n')
|
||||||
else:
|
else:
|
||||||
graph.append('{\n')
|
graph.append('{\n')
|
||||||
else:
|
else:
|
||||||
graph.append('%s %s {\n' % (self.obj_dict['type'], self.obj_dict['name']))
|
graph.append('%s %s {\n' % (self.obj_dict['type'],
|
||||||
|
self.obj_dict['name']))
|
||||||
|
|
||||||
for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
|
for attr, value in sorted(self.obj_dict['attributes'].items(),
|
||||||
|
key=itemgetter(0)):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
graph.append('%s=%s' % (attr, quote_if_necessary(value)))
|
graph.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -1566,7 +1493,8 @@ class Graph(Common):
|
|||||||
edge_obj_dicts.extend(e)
|
edge_obj_dicts.extend(e)
|
||||||
|
|
||||||
if edge_obj_dicts:
|
if edge_obj_dicts:
|
||||||
edge_src_set, edge_dst_set = list(zip(*[obj['points'] for obj in edge_obj_dicts]))
|
edge_src_set, edge_dst_set = list(
|
||||||
|
zip(*[obj['points'] for obj in edge_obj_dicts]))
|
||||||
edge_src_set, edge_dst_set = set(edge_src_set), set(edge_dst_set)
|
edge_src_set, edge_dst_set = set(edge_src_set), set(edge_dst_set)
|
||||||
else:
|
else:
|
||||||
edge_src_set, edge_dst_set = set(), set()
|
edge_src_set, edge_dst_set = set(), set()
|
||||||
@ -1582,8 +1510,7 @@ class Graph(Common):
|
|||||||
obj_list = sorted([
|
obj_list = sorted([
|
||||||
(obj['sequence'], obj)
|
(obj['sequence'], obj)
|
||||||
for obj
|
for obj
|
||||||
in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts)
|
in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts)])
|
||||||
])
|
|
||||||
|
|
||||||
for idx, obj in obj_list:
|
for idx, obj in obj_list:
|
||||||
if obj['type'] == 'node':
|
if obj['type'] == 'node':
|
||||||
@ -1620,7 +1547,8 @@ class Subgraph(Graph):
|
|||||||
This class implements the methods to work on a representation
|
This class implements the methods to work on a representation
|
||||||
of a subgraph in Graphviz's dot language.
|
of a subgraph in Graphviz's dot language.
|
||||||
|
|
||||||
subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
|
subgraph(graph_name='subG', suppress_disconnected=False, attribute=value,
|
||||||
|
...)
|
||||||
|
|
||||||
graph_name:
|
graph_name:
|
||||||
the subgraph's name
|
the subgraph's name
|
||||||
@ -1641,17 +1569,17 @@ class Subgraph(Graph):
|
|||||||
subgraph_instance.obj_dict['attributes']['label']
|
subgraph_instance.obj_dict['attributes']['label']
|
||||||
subgraph_instance.obj_dict['attributes']['fontname']
|
subgraph_instance.obj_dict['attributes']['fontname']
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# RMF: subgraph should have all the attributes of graph so it can be passed
|
# RMF: subgraph should have all the attributes of graph so it can be passed
|
||||||
# as a graph to all methods
|
# as a graph to all methods
|
||||||
#
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, graph_name='', obj_dict=None, suppress_disconnected=False,
|
self, graph_name='', obj_dict=None, suppress_disconnected=False,
|
||||||
simplify=False, **attrs):
|
simplify=False, **attrs):
|
||||||
|
|
||||||
Graph.__init__(
|
Graph.__init__(
|
||||||
self, graph_name=graph_name, obj_dict=obj_dict,
|
self, graph_name=graph_name, obj_dict=obj_dict,
|
||||||
suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs)
|
suppress_disconnected=suppress_disconnected, simplify=simplify,
|
||||||
|
**attrs)
|
||||||
|
|
||||||
if obj_dict is None:
|
if obj_dict is None:
|
||||||
self.obj_dict['type'] = 'subgraph'
|
self.obj_dict['type'] = 'subgraph'
|
||||||
@ -1664,7 +1592,8 @@ class Cluster(Graph):
|
|||||||
This class implements the methods to work on a representation
|
This class implements the methods to work on a representation
|
||||||
of a cluster in Graphviz's dot language.
|
of a cluster in Graphviz's dot language.
|
||||||
|
|
||||||
cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
|
cluster(graph_name='subG', suppress_disconnected=False, attribute=value,
|
||||||
|
...)
|
||||||
|
|
||||||
graph_name:
|
graph_name:
|
||||||
the cluster's name (the string 'cluster' will be always prepended)
|
the cluster's name (the string 'cluster' will be always prepended)
|
||||||
@ -1686,14 +1615,12 @@ class Cluster(Graph):
|
|||||||
cluster_instance.obj_dict['attributes']['fontname']
|
cluster_instance.obj_dict['attributes']['fontname']
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, graph_name='subG', obj_dict=None,
|
||||||
self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
|
suppress_disconnected=False, simplify=False, **attrs):
|
||||||
simplify=False, **attrs):
|
|
||||||
|
|
||||||
Graph.__init__(
|
Graph.__init__(self, graph_name=graph_name, obj_dict=obj_dict,
|
||||||
self, graph_name=graph_name, obj_dict=obj_dict,
|
suppress_disconnected=suppress_disconnected,
|
||||||
suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs
|
simplify=simplify, **attrs)
|
||||||
)
|
|
||||||
|
|
||||||
if obj_dict is None:
|
if obj_dict is None:
|
||||||
self.obj_dict['type'] = 'subgraph'
|
self.obj_dict['type'] = 'subgraph'
|
||||||
@ -1720,8 +1647,7 @@ class Dot(Graph):
|
|||||||
'fig', 'gd', 'gd2', 'gif', 'hpgl', 'imap', 'imap_np', 'ismap',
|
'fig', 'gd', 'gd2', 'gif', 'hpgl', 'imap', 'imap_np', 'ismap',
|
||||||
'jpe', 'jpeg', 'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain',
|
'jpe', 'jpeg', 'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain',
|
||||||
'plain-ext', 'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz',
|
'plain-ext', 'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz',
|
||||||
'vrml', 'vtx', 'wbmp', 'xdot', 'xlib'
|
'vrml', 'vtx', 'wbmp', 'xdot', 'xlib']
|
||||||
]
|
|
||||||
self.prog = 'dot'
|
self.prog = 'dot'
|
||||||
|
|
||||||
# Automatically creates all the methods enabling the creation
|
# Automatically creates all the methods enabling the creation
|
||||||
@ -1729,25 +1655,23 @@ class Dot(Graph):
|
|||||||
for frmt in self.formats:
|
for frmt in self.formats:
|
||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'create_' + frmt,
|
'create_' + frmt,
|
||||||
lambda f=frmt, prog=self.prog: self.create(format=f, prog=prog)
|
lambda f=frmt, prog=self.prog:
|
||||||
)
|
self.create(format=f, prog=prog))
|
||||||
f = self.__dict__['create_' + frmt]
|
f = self.__dict__['create_' + frmt]
|
||||||
f.__doc__ = (
|
f.__doc__ = (
|
||||||
'''Refer to the docstring accompanying the'''
|
'''Refer to the docstring accompanying the'''
|
||||||
''''create' method for more information.'''
|
''''create' method for more information.''')
|
||||||
)
|
|
||||||
|
|
||||||
for frmt in self.formats + ['raw']:
|
for frmt in self.formats + ['raw']:
|
||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'write_' + frmt,
|
'write_' + frmt,
|
||||||
lambda path, f=frmt, prog=self.prog: self.write(path, format=f, prog=prog)
|
lambda path, f=frmt, prog=self.prog: self.write(path, format=f,
|
||||||
)
|
prog=prog))
|
||||||
|
|
||||||
f = self.__dict__['write_' + frmt]
|
f = self.__dict__['write_' + frmt]
|
||||||
f.__doc__ = (
|
f.__doc__ = (
|
||||||
'''Refer to the docstring accompanying the'''
|
'''Refer to the docstring accompanying the'''
|
||||||
''''write' method for more information.'''
|
''''write' method for more information.''')
|
||||||
)
|
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return copy.copy(self.obj_dict)
|
return copy.copy(self.obj_dict)
|
||||||
@ -1759,13 +1683,13 @@ class Dot(Graph):
|
|||||||
"""Add the paths of the required image files.
|
"""Add the paths of the required image files.
|
||||||
|
|
||||||
If the graph needs graphic objects to be used as shapes or otherwise
|
If the graph needs graphic objects to be used as shapes or otherwise
|
||||||
those need to be in the same folder as the graph is going to be rendered
|
those need to be in the same folder as the graph is going to be
|
||||||
from. Alternatively the absolute path to the files can be specified when
|
rendered from. Alternatively the absolute path to the files can be
|
||||||
including the graphics in the graph.
|
specified when including the graphics in the graph.
|
||||||
|
|
||||||
The files in the location pointed to by the path(s) specified as arguments
|
The files in the location pointed to by the path(s) specified as
|
||||||
to this method will be copied to the same temporary location where the
|
arguments to this method will be copied to the same temporary location
|
||||||
graph is going to be rendered.
|
where the graph is going to be rendered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(file_paths, basestring):
|
if isinstance(file_paths, basestring):
|
||||||
@ -1783,20 +1707,22 @@ class Dot(Graph):
|
|||||||
self.prog = prog
|
self.prog = prog
|
||||||
|
|
||||||
def set_graphviz_executables(self, paths):
|
def set_graphviz_executables(self, paths):
|
||||||
"""This method allows to manually specify the location of the GraphViz executables.
|
"""Manually specify the location of the GraphViz executables.
|
||||||
|
|
||||||
The argument to this method should be a dictionary where the keys are as follows:
|
The argument to this method should be a dictionary where the keys
|
||||||
|
are as follows:
|
||||||
|
|
||||||
{'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''}
|
{'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''}
|
||||||
|
|
||||||
and the values are the paths to the corresponding executable, including the name
|
and the values are the paths to the corresponding executable,
|
||||||
of the executable itself.
|
including the name of the executable itself
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.progs = paths
|
self.progs = paths
|
||||||
|
|
||||||
def write(self, path, prog=None, format='raw'):
|
def write(self, path, prog=None, format='raw'):
|
||||||
"""
|
"""Write graph to file in selected format.
|
||||||
|
|
||||||
Given a filename 'path' it will open/create and truncate
|
Given a filename 'path' it will open/create and truncate
|
||||||
such file and write on it a representation of the graph
|
such file and write on it a representation of the graph
|
||||||
defined by the dot object and in the format specified by
|
defined by the dot object and in the format specified by
|
||||||
@ -1829,7 +1755,7 @@ class Dot(Graph):
|
|||||||
if not isinstance(data, unicode):
|
if not isinstance(data, unicode):
|
||||||
try:
|
try:
|
||||||
data = unicode(data, 'utf-8')
|
data = unicode(data, 'utf-8')
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1837,7 +1763,7 @@ class Dot(Graph):
|
|||||||
if not PY3 or not charset:
|
if not PY3 or not charset:
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
data = data.encode(charset)
|
data = data.encode(charset)
|
||||||
except:
|
except Exception:
|
||||||
if PY3:
|
if PY3:
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
pass
|
pass
|
||||||
@ -1893,9 +1819,11 @@ class Dot(Graph):
|
|||||||
raise InvocationException(
|
raise InvocationException(
|
||||||
'GraphViz\'s executable "%s" not found' % prog)
|
'GraphViz\'s executable "%s" not found' % prog)
|
||||||
|
|
||||||
if not os.path.exists(self.progs[prog]) or not os.path.isfile(self.progs[prog]):
|
if (not os.path.exists(self.progs[prog]) or
|
||||||
|
not os.path.isfile(self.progs[prog])):
|
||||||
raise InvocationException(
|
raise InvocationException(
|
||||||
'GraphViz\'s executable "%s" is not a file or doesn\'t exist' % self.progs[prog])
|
'GraphViz\'s executable "%s" is not a file or doesn\'t exist'
|
||||||
|
% self.progs[prog])
|
||||||
|
|
||||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||||
os.close(tmp_fd)
|
os.close(tmp_fd)
|
||||||
@ -1910,7 +1838,8 @@ class Dot(Graph):
|
|||||||
f_data = f.read()
|
f_data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# And copy it under a file with the same name in the temporary directory
|
# And copy it under a file with the same name in the
|
||||||
|
# temporary directory
|
||||||
f = open(os.path.join(tmp_dir, os.path.basename(img)), 'wb')
|
f = open(os.path.join(tmp_dir, os.path.basename(img)), 'wb')
|
||||||
f.write(f_data)
|
f.write(f_data)
|
||||||
f.close()
|
f.close()
|
||||||
@ -1949,7 +1878,6 @@ class Dot(Graph):
|
|||||||
if PY3:
|
if PY3:
|
||||||
stderr_output = stderr_output.decode(sys.stderr.encoding)
|
stderr_output = stderr_output.decode(sys.stderr.encoding)
|
||||||
|
|
||||||
#pid, status = os.waitpid(p.pid, 0)
|
|
||||||
status = p.wait()
|
status = p.wait()
|
||||||
|
|
||||||
if status != 0:
|
if status != 0:
|
||||||
@ -1961,7 +1889,6 @@ class Dot(Graph):
|
|||||||
|
|
||||||
# For each of the image files...
|
# For each of the image files...
|
||||||
for img in self.shape_files:
|
for img in self.shape_files:
|
||||||
|
|
||||||
# remove it
|
# remove it
|
||||||
os.unlink(os.path.join(tmp_dir, os.path.basename(img)))
|
os.unlink(os.path.join(tmp_dir, os.path.basename(img)))
|
||||||
|
|
||||||
|
@ -1,32 +1,25 @@
|
|||||||
"""Graphviz's dot language parser.
|
# Graphviz's dot language parser.
|
||||||
|
|
||||||
The dotparser parses graphviz files in dot and dot files and transforms them
|
# The dotparser parses graphviz files in dot and dot files and transforms them
|
||||||
into a class representation defined by pydot.
|
# into a class representation defined by pydot.
|
||||||
|
|
||||||
The module needs pyparsing (tested with version 1.2.2) and pydot
|
# The module needs pyparsing (tested with version 1.2.2) and pydot
|
||||||
|
|
||||||
Author: Michael Krause <michael@krause-software.de>
|
# Author: Michael Krause <michael@krause-software.de>
|
||||||
Fixes by: Ero Carrera <ero@dkbza.org>
|
# Fixes by: Ero Carrera <ero@dkbza.org>
|
||||||
"""
|
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
import pydot_ng as pydot
|
||||||
|
import pyparsing
|
||||||
|
import sys
|
||||||
|
|
||||||
from __future__ import division, print_function
|
|
||||||
|
|
||||||
__author__ = ['Michael Krause', 'Ero Carrera']
|
__author__ = ['Michael Krause', 'Ero Carrera']
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
|
|
||||||
import sys
|
|
||||||
import pydot_ng as pydot
|
|
||||||
import codecs
|
|
||||||
|
|
||||||
from pyparsing import __version__ as pyparsing_version
|
|
||||||
|
|
||||||
from pyparsing import (
|
|
||||||
nestedExpr, Literal, CaselessLiteral, Word, OneOrMore,
|
|
||||||
Forward, Group, Optional, Combine, nums, restOfLine,
|
|
||||||
cStyleComment, alphanums, printables, ParseException,
|
|
||||||
ParseResults, CharsNotIn, QuotedString
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
PY3 = not sys.version_info < (3, 0, 0)
|
PY3 = not sys.version_info < (3, 0, 0)
|
||||||
|
|
||||||
@ -34,7 +27,7 @@ if PY3:
|
|||||||
basestring = str
|
basestring = str
|
||||||
|
|
||||||
|
|
||||||
class P_AttrList:
|
class P_AttrList(object):
|
||||||
|
|
||||||
def __init__(self, toks):
|
def __init__(self, toks):
|
||||||
self.attrs = {}
|
self.attrs = {}
|
||||||
@ -64,8 +57,7 @@ class DefaultStatement(P_AttrList):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s, %r)" % (
|
return "%s(%s, %r)" % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
self.default_type, self.attrs
|
self.default_type, self.attrs)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
top_graphs = list()
|
top_graphs = list()
|
||||||
@ -76,7 +68,7 @@ def push_top_graph_stmt(str, loc, toks):
|
|||||||
g = None
|
g = None
|
||||||
|
|
||||||
for element in toks:
|
for element in toks:
|
||||||
if (isinstance(element, (ParseResults, tuple, list)) and
|
if (isinstance(element, (pyparsing.ParseResults, tuple, list)) and
|
||||||
len(element) == 1 and isinstance(element[0], basestring)):
|
len(element) == 1 and isinstance(element[0], basestring)):
|
||||||
element = element[0]
|
element = element[0]
|
||||||
|
|
||||||
@ -104,7 +96,7 @@ def push_top_graph_stmt(str, loc, toks):
|
|||||||
elif isinstance(element, P_AttrList):
|
elif isinstance(element, P_AttrList):
|
||||||
attrs.update(element.attrs)
|
attrs.update(element.attrs)
|
||||||
|
|
||||||
elif isinstance(element, (ParseResults, list)):
|
elif isinstance(element, (pyparsing.ParseResults, list)):
|
||||||
add_elements(g, element)
|
add_elements(g, element)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -134,7 +126,8 @@ def update_parent_graph_hierarchy(g, parent_graph=None, level=0):
|
|||||||
|
|
||||||
for key, objs in item_dict[key_name].items():
|
for key, objs in item_dict[key_name].items():
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if 'parent_graph' in obj and obj['parent_graph'].get_parent_graph() == g:
|
if ('parent_graph' in obj and
|
||||||
|
obj['parent_graph'].get_parent_graph() == g):
|
||||||
if obj['parent_graph'] is g:
|
if obj['parent_graph'] is g:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -142,13 +135,15 @@ def update_parent_graph_hierarchy(g, parent_graph=None, level=0):
|
|||||||
|
|
||||||
if key_name == 'edges' and len(key) == 2:
|
if key_name == 'edges' and len(key) == 2:
|
||||||
for idx, vertex in enumerate(obj['points']):
|
for idx, vertex in enumerate(obj['points']):
|
||||||
if isinstance(vertex, (pydot.Graph, pydot.Subgraph, pydot.Cluster)):
|
if isinstance(vertex, (pydot.Graph, pydot.Subgraph,
|
||||||
|
pydot.Cluster)):
|
||||||
vertex.set_parent_graph(parent_graph)
|
vertex.set_parent_graph(parent_graph)
|
||||||
if isinstance(vertex, pydot.frozendict):
|
if isinstance(vertex, pydot.frozendict):
|
||||||
if vertex['parent_graph'] is g:
|
if vertex['parent_graph'] is g:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
vertex['parent_graph'].set_parent_graph(parent_graph)
|
vertex['parent_graph'].\
|
||||||
|
set_parent_graph(parent_graph)
|
||||||
|
|
||||||
|
|
||||||
def add_defaults(element, defaults):
|
def add_defaults(element, defaults):
|
||||||
@ -158,7 +153,8 @@ def add_defaults(element, defaults):
|
|||||||
d[key] = value
|
d[key] = value
|
||||||
|
|
||||||
|
|
||||||
def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None):
|
def add_elements(g, toks, defaults_graph=None, defaults_node=None,
|
||||||
|
defaults_edge=None):
|
||||||
if defaults_graph is None:
|
if defaults_graph is None:
|
||||||
defaults_graph = {}
|
defaults_graph = {}
|
||||||
if defaults_node is None:
|
if defaults_node is None:
|
||||||
@ -179,9 +175,10 @@ def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge
|
|||||||
add_defaults(element, defaults_edge)
|
add_defaults(element, defaults_edge)
|
||||||
g.add_edge(element)
|
g.add_edge(element)
|
||||||
|
|
||||||
elif isinstance(element, ParseResults):
|
elif isinstance(element, pyparsing.ParseResults):
|
||||||
for e in element:
|
for e in element:
|
||||||
add_elements(g, [e], defaults_graph, defaults_node, defaults_edge)
|
add_elements(g, [e], defaults_graph, defaults_node,
|
||||||
|
defaults_edge)
|
||||||
|
|
||||||
elif isinstance(element, DefaultStatement):
|
elif isinstance(element, DefaultStatement):
|
||||||
if element.default_type == 'graph':
|
if element.default_type == 'graph':
|
||||||
@ -198,7 +195,8 @@ def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge
|
|||||||
defaults_edge.update(element.attrs)
|
defaults_edge.update(element.attrs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown DefaultStatement: %s " % element.default_type)
|
raise ValueError("Unknown DefaultStatement: {0} ".
|
||||||
|
format(element.default_type))
|
||||||
|
|
||||||
elif isinstance(element, P_AttrList):
|
elif isinstance(element, P_AttrList):
|
||||||
g.obj_dict['attributes'].update(element.attrs)
|
g.obj_dict['attributes'].update(element.attrs)
|
||||||
@ -220,11 +218,11 @@ def push_subgraph_stmt(str, loc, toks):
|
|||||||
if len(e) == 3:
|
if len(e) == 3:
|
||||||
e[2].set_name(e[1])
|
e[2].set_name(e[1])
|
||||||
if e[0] == 'subgraph':
|
if e[0] == 'subgraph':
|
||||||
e[2].obj_dict['show_keyword'] = True
|
e[2].obj_dict['show_keypyparsing.Word'] = True
|
||||||
return e[2]
|
return e[2]
|
||||||
else:
|
else:
|
||||||
if e[0] == 'subgraph':
|
if e[0] == 'subgraph':
|
||||||
e[1].obj_dict['show_keyword'] = True
|
e[1].obj_dict['show_keypyparsing.Word'] = True
|
||||||
return e[1]
|
return e[1]
|
||||||
|
|
||||||
return g
|
return g
|
||||||
@ -253,7 +251,7 @@ def push_attr_list(str, loc, toks):
|
|||||||
|
|
||||||
def get_port(node):
|
def get_port(node):
|
||||||
if len(node) > 1:
|
if len(node) > 1:
|
||||||
if isinstance(node[1], ParseResults):
|
if isinstance(node[1], pyparsing.ParseResults):
|
||||||
if len(node[1][0]) == 2:
|
if len(node[1][0]) == 2:
|
||||||
if node[1][0][0] == ':':
|
if node[1][0][0] == ':':
|
||||||
return node[1][0][1]
|
return node[1][0][1]
|
||||||
@ -283,14 +281,15 @@ def push_edge_stmt(str, loc, toks):
|
|||||||
else:
|
else:
|
||||||
n_prev = toks[0][0] + do_node_ports(toks[0])
|
n_prev = toks[0][0] + do_node_ports(toks[0])
|
||||||
|
|
||||||
if isinstance(toks[2][0], ParseResults):
|
if isinstance(toks[2][0], pyparsing.ParseResults):
|
||||||
n_next_list = [[n.get_name()] for n in toks[2][0]]
|
n_next_list = [[n.get_name()] for n in toks[2][0]]
|
||||||
for n_next in [n for n in n_next_list]:
|
for n_next in [n for n in n_next_list]:
|
||||||
n_next_port = do_node_ports(n_next)
|
n_next_port = do_node_ports(n_next)
|
||||||
e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
|
e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
|
||||||
|
|
||||||
elif isinstance(toks[2][0], pydot.Graph):
|
elif isinstance(toks[2][0], pydot.Graph):
|
||||||
e.append(pydot.Edge(n_prev, pydot.frozendict(toks[2][0].obj_dict), **attrs))
|
e.append(pydot.Edge(n_prev, pydot.frozendict(toks[2][0].obj_dict),
|
||||||
|
**attrs))
|
||||||
|
|
||||||
elif isinstance(toks[2][0], pydot.Node):
|
elif isinstance(toks[2][0], pydot.Node):
|
||||||
node = toks[2][0]
|
node = toks[2][0]
|
||||||
@ -304,7 +303,8 @@ def push_edge_stmt(str, loc, toks):
|
|||||||
|
|
||||||
elif isinstance(toks[2][0], type('')):
|
elif isinstance(toks[2][0], type('')):
|
||||||
for n_next in [n for n in tuple(toks)[2::2]]:
|
for n_next in [n for n in tuple(toks)[2::2]]:
|
||||||
if isinstance(n_next, P_AttrList) or not isinstance(n_next[0], type('')):
|
if isinstance(n_next, P_AttrList) or not isinstance(n_next[0],
|
||||||
|
type('')):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
n_next_port = do_node_ports(n_next)
|
n_next_port = do_node_ports(n_next)
|
||||||
@ -343,124 +343,130 @@ def graph_definition():
|
|||||||
|
|
||||||
if not graphparser:
|
if not graphparser:
|
||||||
# punctuation
|
# punctuation
|
||||||
colon = Literal(":")
|
colon = pyparsing.Literal(":")
|
||||||
lbrace = Literal("{")
|
lbrace = pyparsing.Literal("{")
|
||||||
rbrace = Literal("}")
|
rbrace = pyparsing.Literal("}")
|
||||||
lbrack = Literal("[")
|
lbrack = pyparsing.Literal("[")
|
||||||
rbrack = Literal("]")
|
rbrack = pyparsing.Literal("]")
|
||||||
lparen = Literal("(")
|
lparen = pyparsing.Literal("(")
|
||||||
rparen = Literal(")")
|
rparen = pyparsing.Literal(")")
|
||||||
equals = Literal("=")
|
equals = pyparsing.Literal("=")
|
||||||
comma = Literal(",")
|
comma = pyparsing.Literal(",")
|
||||||
# dot = Literal(".")
|
semi = pyparsing.Literal(";")
|
||||||
# slash = Literal("/")
|
at = pyparsing.Literal("@")
|
||||||
# bslash = Literal("\\")
|
minus = pyparsing.Literal("-")
|
||||||
# star = Literal("*")
|
|
||||||
semi = Literal(";")
|
|
||||||
at = Literal("@")
|
|
||||||
minus = Literal("-")
|
|
||||||
|
|
||||||
# keywords
|
# keypyparsing.Words
|
||||||
strict_ = CaselessLiteral("strict")
|
strict_ = pyparsing.CaselessLiteral("strict")
|
||||||
graph_ = CaselessLiteral("graph")
|
graph_ = pyparsing.CaselessLiteral("graph")
|
||||||
digraph_ = CaselessLiteral("digraph")
|
digraph_ = pyparsing.CaselessLiteral("digraph")
|
||||||
subgraph_ = CaselessLiteral("subgraph")
|
subgraph_ = pyparsing.CaselessLiteral("subgraph")
|
||||||
node_ = CaselessLiteral("node")
|
node_ = pyparsing.CaselessLiteral("node")
|
||||||
edge_ = CaselessLiteral("edge")
|
edge_ = pyparsing.CaselessLiteral("edge")
|
||||||
|
|
||||||
# token definitions
|
# token definitions
|
||||||
identifier = Word(alphanums + "_.").setName("identifier")
|
identifier = pyparsing.Word(pyparsing.alphanums + "_.").\
|
||||||
|
setName("identifier")
|
||||||
|
|
||||||
# dblQuotedString
|
# dblpyparsing.QuotedString
|
||||||
double_quoted_string = QuotedString('"', multiline=True, unquoteResults=False)
|
double_quoted_string = pyparsing.QuotedString('"', multiline=True,
|
||||||
|
unquoteResults=False)
|
||||||
|
|
||||||
noncomma_ = "".join([c for c in printables if c != ","])
|
noncomma_ = "".join([c for c in pyparsing.printables if c != ","])
|
||||||
alphastring_ = OneOrMore(CharsNotIn(noncomma_ + ' '))
|
alphastring_ = pyparsing.OneOrMore(pyparsing.CharsNotIn(noncomma_ +
|
||||||
|
' '))
|
||||||
|
|
||||||
def parse_html(s, loc, toks):
|
def parse_html(s, loc, toks):
|
||||||
return '<%s>' % ''.join(toks[0])
|
return '<%s>' % ''.join(toks[0])
|
||||||
|
|
||||||
opener = '<'
|
opener = '<'
|
||||||
closer = '>'
|
closer = '>'
|
||||||
html_text = nestedExpr(
|
html_text = pyparsing.nestedExpr(
|
||||||
opener, closer,
|
opener, closer,
|
||||||
(CharsNotIn(opener + closer))
|
(pyparsing.CharsNotIn(opener + closer))).\
|
||||||
).setParseAction(parse_html).leaveWhitespace()
|
setParseAction(parse_html).leaveWhitespace()
|
||||||
|
|
||||||
ID = (
|
ID = (
|
||||||
identifier | html_text |
|
identifier | html_text |
|
||||||
double_quoted_string | # .setParseAction(strip_quotes) |
|
double_quoted_string |
|
||||||
alphastring_
|
alphastring_).setName("ID")
|
||||||
).setName("ID")
|
|
||||||
|
|
||||||
float_number = Combine(
|
float_number = pyparsing.Combine(
|
||||||
Optional(minus) +
|
pyparsing.Optional(minus) +
|
||||||
OneOrMore(Word(nums + "."))
|
pyparsing.OneOrMore(pyparsing.Word(pyparsing.nums + "."))).\
|
||||||
).setName("float_number")
|
setName("float_number")
|
||||||
|
|
||||||
righthand_id = (float_number | ID).setName("righthand_id")
|
righthand_id = (float_number | ID).setName("righthand_id")
|
||||||
|
|
||||||
port_angle = (at + ID).setName("port_angle")
|
port_angle = (at + ID).setName("port_angle")
|
||||||
|
|
||||||
port_location = (
|
port_location = (
|
||||||
OneOrMore(Group(colon + ID)) |
|
pyparsing.OneOrMore(pyparsing.Group(colon + ID)) |
|
||||||
Group(colon + lparen + ID + comma + ID + rparen)
|
pyparsing.Group(colon + lparen + ID + comma + ID + rparen)).\
|
||||||
).setName("port_location")
|
setName("port_location")
|
||||||
|
|
||||||
port = (
|
port = (
|
||||||
Group(port_location + Optional(port_angle)) |
|
pyparsing.Group(port_location + pyparsing.Optional(port_angle)) |
|
||||||
Group(port_angle + Optional(port_location))
|
pyparsing.Group(port_angle + pyparsing.Optional(port_location))).\
|
||||||
).setName("port")
|
setName("port")
|
||||||
|
|
||||||
node_id = (ID + Optional(port))
|
node_id = (ID + pyparsing.Optional(port))
|
||||||
a_list = OneOrMore(
|
a_list = pyparsing.OneOrMore(
|
||||||
ID + Optional(equals + righthand_id) + Optional(comma.suppress())
|
ID + pyparsing.Optional(equals + righthand_id) +
|
||||||
).setName("a_list")
|
pyparsing.Optional(comma.suppress())).\
|
||||||
|
setName("a_list")
|
||||||
|
|
||||||
attr_list = OneOrMore(
|
attr_list = pyparsing.OneOrMore(
|
||||||
lbrack.suppress() + Optional(a_list) + rbrack.suppress()
|
lbrack.suppress() + pyparsing.Optional(a_list) +
|
||||||
).setName("attr_list")
|
rbrack.suppress()).setName("attr_list")
|
||||||
|
|
||||||
attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt")
|
attr_stmt = (pyparsing.Group(graph_ | node_ | edge_) + attr_list).\
|
||||||
|
setName("attr_stmt")
|
||||||
|
|
||||||
edgeop = (Literal("--") | Literal("->")).setName("edgeop")
|
edgeop = (pyparsing.Literal("--") | pyparsing.Literal("->")).\
|
||||||
|
setName("edgeop")
|
||||||
|
|
||||||
stmt_list = Forward()
|
stmt_list = pyparsing.Forward()
|
||||||
graph_stmt = Group(
|
graph_stmt = pyparsing.Group(
|
||||||
lbrace.suppress() + Optional(stmt_list) +
|
lbrace.suppress() + pyparsing.Optional(stmt_list) +
|
||||||
rbrace.suppress() + Optional(semi.suppress())
|
rbrace.suppress() + pyparsing.Optional(semi.suppress())).\
|
||||||
).setName("graph_stmt")
|
setName("graph_stmt")
|
||||||
|
|
||||||
edge_point = Forward()
|
edge_point = pyparsing.Forward()
|
||||||
|
|
||||||
edgeRHS = OneOrMore(edgeop + edge_point)
|
edgeRHS = pyparsing.OneOrMore(edgeop + edge_point)
|
||||||
edge_stmt = edge_point + edgeRHS + Optional(attr_list)
|
edge_stmt = edge_point + edgeRHS + pyparsing.Optional(attr_list)
|
||||||
|
|
||||||
subgraph = Group(subgraph_ + Optional(ID) + graph_stmt).setName("subgraph")
|
subgraph = pyparsing.Group(subgraph_ +
|
||||||
|
pyparsing.Optional(ID) + graph_stmt).\
|
||||||
|
setName("subgraph")
|
||||||
|
|
||||||
edge_point << Group(subgraph | graph_stmt | node_id).setName('edge_point')
|
edge_point << pyparsing.Group(subgraph | graph_stmt | node_id).\
|
||||||
|
setName('edge_point')
|
||||||
|
|
||||||
node_stmt = (
|
node_stmt = (node_id + pyparsing.Optional(attr_list) +
|
||||||
node_id + Optional(attr_list) + Optional(semi.suppress())
|
pyparsing.Optional(semi.suppress())).setName("node_stmt")
|
||||||
).setName("node_stmt")
|
|
||||||
|
|
||||||
assignment = (ID + equals + righthand_id).setName("assignment")
|
assignment = (ID + equals + righthand_id).setName("assignment")
|
||||||
stmt = (
|
stmt = (
|
||||||
assignment | edge_stmt | attr_stmt |
|
assignment | edge_stmt | attr_stmt |
|
||||||
subgraph | graph_stmt | node_stmt
|
subgraph | graph_stmt | node_stmt).\
|
||||||
).setName("stmt")
|
setName("stmt")
|
||||||
stmt_list << OneOrMore(stmt + Optional(semi.suppress()))
|
stmt_list << pyparsing.OneOrMore(stmt + pyparsing.Optional(
|
||||||
|
semi.suppress()))
|
||||||
|
|
||||||
graphparser = OneOrMore((
|
graphparser = pyparsing.OneOrMore((
|
||||||
Optional(strict_) + Group((graph_ | digraph_)) +
|
pyparsing.Optional(strict_) +
|
||||||
Optional(ID) + graph_stmt
|
pyparsing.Group((graph_ | digraph_)) +
|
||||||
).setResultsName("graph"))
|
pyparsing.Optional(ID) + graph_stmt).
|
||||||
|
setResultsName("graph"))
|
||||||
|
|
||||||
singleLineComment = Group("//" + restOfLine) | Group("#" + restOfLine)
|
singleLineComment = (pyparsing.Group("//" + pyparsing.restOfLine) |
|
||||||
|
pyparsing.Group("#" + pyparsing.restOfLine))
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
graphparser.ignore(singleLineComment)
|
graphparser.ignore(singleLineComment)
|
||||||
graphparser.ignore(cStyleComment)
|
graphparser.ignore(pyparsing.cStyleComment)
|
||||||
|
|
||||||
assignment.setParseAction(push_attr_list)
|
assignment.setParseAction(push_attr_list)
|
||||||
a_list.setParseAction(push_attr_list)
|
a_list.setParseAction(push_attr_list)
|
||||||
@ -492,7 +498,7 @@ def parse_dot_data(data):
|
|||||||
idx += 1
|
idx += 1
|
||||||
charset = data[fst:idx].strip(b'"\'').decode('ascii')
|
charset = data[fst:idx].strip(b'"\'').decode('ascii')
|
||||||
data = data.decode(charset)
|
data = data.decode(charset)
|
||||||
except:
|
except Exception:
|
||||||
data = data.decode('utf-8')
|
data = data.decode('utf-8')
|
||||||
else:
|
else:
|
||||||
if data.startswith(codecs.BOM_UTF8):
|
if data.startswith(codecs.BOM_UTF8):
|
||||||
@ -502,7 +508,7 @@ def parse_dot_data(data):
|
|||||||
|
|
||||||
graphparser = graph_definition()
|
graphparser = graph_definition()
|
||||||
|
|
||||||
if pyparsing_version >= '1.2':
|
if pyparsing.__version__ >= '1.2':
|
||||||
graphparser.parseWithTabs()
|
graphparser.parseWithTabs()
|
||||||
|
|
||||||
tokens = graphparser.parseString(data)
|
tokens = graphparser.parseString(data)
|
||||||
@ -512,7 +518,7 @@ def parse_dot_data(data):
|
|||||||
else:
|
else:
|
||||||
return [g for g in tokens]
|
return [g for g in tokens]
|
||||||
|
|
||||||
except ParseException:
|
except pyparsing.ParseException:
|
||||||
err = sys.exc_info()[1]
|
err = sys.exc_info()[1]
|
||||||
print(err.line)
|
print(err.line)
|
||||||
print(" " * (err.column - 1) + "^")
|
print(" " * (err.column - 1) + "^")
|
||||||
|
9
setup.py
9
setup.py
@ -30,12 +30,9 @@ setup(
|
|||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Topic :: Scientific/Engineering :: Visualization',
|
'Topic :: Scientific/Engineering :: Visualization',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
'Topic :: Software Development :: Libraries :: Python Modules'],
|
||||||
],
|
|
||||||
long_description=readme,
|
long_description=readme,
|
||||||
packages=['pydot_ng'],
|
packages=['pydot_ng'],
|
||||||
package_dir={'pydot_ng': 'pydot_ng'},
|
package_dir={'pydot_ng': 'pydot_ng'},
|
||||||
install_requires=[
|
install_requires=['pyparsing>=2.0.1'],
|
||||||
'pyparsing>=2.0.1',
|
)
|
||||||
],
|
|
||||||
)
|
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
# coding=iso-8859-1
|
# coding=iso-8859-1
|
||||||
|
|
||||||
# TODO:
|
# TODO(ksambor)
|
||||||
# -test graph generation APIs (from adjacency, etc..)
|
# -test graph generation APIs (from adjacency, etc..)
|
||||||
# -test del_node, del_edge methods
|
# -test del_node, del_edge methods
|
||||||
# -test Common.set method
|
# -test Common.set method
|
||||||
|
|
||||||
from __future__ import division, print_function
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from hashlib import sha256
|
||||||
import os
|
import os
|
||||||
try:
|
|
||||||
from hashlib import sha256
|
|
||||||
except ImportError:
|
|
||||||
import sha
|
|
||||||
sha256 = sha.new
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -39,25 +36,18 @@ MY_REGRESSION_TESTS_DIR = os.path.join(TEST_DIR, 'my_tests')
|
|||||||
class TestGraphAPI(unittest.TestCase):
|
class TestGraphAPI(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
def _reset_graphs(self):
|
def _reset_graphs(self):
|
||||||
|
|
||||||
self.graph_directed = pydot.Graph('testgraph', graph_type='digraph')
|
self.graph_directed = pydot.Graph('testgraph', graph_type='digraph')
|
||||||
|
|
||||||
def test_keep_graph_type(self):
|
def test_keep_graph_type(self):
|
||||||
|
|
||||||
g = pydot.Dot(graph_name='Test', graph_type='graph')
|
g = pydot.Dot(graph_name='Test', graph_type='graph')
|
||||||
|
|
||||||
self.assertEqual(g.get_type(), 'graph')
|
self.assertEqual(g.get_type(), 'graph')
|
||||||
|
|
||||||
g = pydot.Dot(graph_name='Test', graph_type='digraph')
|
g = pydot.Dot(graph_name='Test', graph_type='digraph')
|
||||||
|
|
||||||
self.assertEqual(g.get_type(), 'digraph')
|
self.assertEqual(g.get_type(), 'digraph')
|
||||||
|
|
||||||
def test_add_style(self):
|
def test_add_style(self):
|
||||||
|
|
||||||
node = pydot.Node('mynode')
|
node = pydot.Node('mynode')
|
||||||
node.add_style('abc')
|
node.add_style('abc')
|
||||||
self.assertEqual(node.get_style(), 'abc')
|
self.assertEqual(node.get_style(), 'abc')
|
||||||
@ -67,7 +57,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
self.assertEqual(node.get_style(), 'abc,def,ghi')
|
self.assertEqual(node.get_style(), 'abc,def,ghi')
|
||||||
|
|
||||||
def test_create_simple_graph_with_node(self):
|
def test_create_simple_graph_with_node(self):
|
||||||
|
|
||||||
g = pydot.Dot()
|
g = pydot.Dot()
|
||||||
g.set_type('digraph')
|
g.set_type('digraph')
|
||||||
node = pydot.Node('legend')
|
node = pydot.Node('legend')
|
||||||
@ -75,10 +64,10 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
g.add_node(node)
|
g.add_node(node)
|
||||||
node.set('label', 'mine')
|
node.set('label', 'mine')
|
||||||
|
|
||||||
self.assertEqual(g.to_string(), 'digraph G {\nlegend [label=mine, shape=box];\n}\n')
|
self.assertEqual(g.to_string(),
|
||||||
|
'digraph G {\nlegend [label=mine, shape=box];\n}\n')
|
||||||
|
|
||||||
def test_attribute_with_implicit_value(self):
|
def test_attribute_with_implicit_value(self):
|
||||||
|
|
||||||
d = 'digraph {\na -> b[label="hi", decorate];\n}'
|
d = 'digraph {\na -> b[label="hi", decorate];\n}'
|
||||||
g = pydot.graph_from_dot_data(d)
|
g = pydot.graph_from_dot_data(d)
|
||||||
attrs = g.get_edges()[0].get_attributes()
|
attrs = g.get_edges()[0].get_attributes()
|
||||||
@ -86,7 +75,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
self.assertEqual('decorate' in attrs, True)
|
self.assertEqual('decorate' in attrs, True)
|
||||||
|
|
||||||
def test_subgraphs(self):
|
def test_subgraphs(self):
|
||||||
|
|
||||||
g = pydot.Graph()
|
g = pydot.Graph()
|
||||||
s = pydot.Subgraph("foo")
|
s = pydot.Subgraph("foo")
|
||||||
|
|
||||||
@ -99,7 +87,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
self.assertEqual(g.get_subgraph_list()[0].get_name(), s.get_name())
|
self.assertEqual(g.get_subgraph_list()[0].get_name(), s.get_name())
|
||||||
|
|
||||||
def test_graph_pickling(self):
|
def test_graph_pickling(self):
|
||||||
|
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
g = pydot.Graph()
|
g = pydot.Graph()
|
||||||
@ -113,7 +100,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
self.assertEqual(type(pickle.dumps(g)), bytes)
|
self.assertEqual(type(pickle.dumps(g)), bytes)
|
||||||
|
|
||||||
def test_unicode_ids(self):
|
def test_unicode_ids(self):
|
||||||
|
|
||||||
node1 = '"aánñoöüé€"'
|
node1 = '"aánñoöüé€"'
|
||||||
node2 = '"îôø®çßΩ"'
|
node2 = '"îôø®çßΩ"'
|
||||||
|
|
||||||
@ -139,50 +125,37 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
|
|
||||||
@unittest.skip("failing checksum")
|
@unittest.skip("failing checksum")
|
||||||
def test_graph_with_shapefiles(self):
|
def test_graph_with_shapefiles(self):
|
||||||
|
|
||||||
shapefile_dir = os.path.join(TEST_DIR, 'from-past-to-future')
|
shapefile_dir = os.path.join(TEST_DIR, 'from-past-to-future')
|
||||||
dot_file = os.path.join(shapefile_dir, 'from-past-to-future.dot')
|
dot_file = os.path.join(shapefile_dir, 'from-past-to-future.dot')
|
||||||
|
|
||||||
pngs = [
|
pngs = [
|
||||||
os.path.join(shapefile_dir, fname)
|
os.path.join(shapefile_dir, fname)
|
||||||
for fname in os.listdir(shapefile_dir)
|
for fname in os.listdir(shapefile_dir)
|
||||||
if fname.endswith('.png')
|
if fname.endswith('.png')]
|
||||||
]
|
|
||||||
|
|
||||||
f = open(dot_file, 'rt')
|
f = open(dot_file, 'rt')
|
||||||
graph_data = f.read()
|
graph_data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
g = pydot.graph_from_dot_data(graph_data)
|
g = pydot.graph_from_dot_data(graph_data)
|
||||||
|
|
||||||
g.set_shape_files(pngs)
|
g.set_shape_files(pngs)
|
||||||
|
|
||||||
jpe_data = g.create(format='jpe')
|
jpe_data = g.create(format='jpe')
|
||||||
|
|
||||||
hexdigest = sha256(jpe_data).hexdigest()
|
hexdigest = sha256(jpe_data).hexdigest()
|
||||||
|
|
||||||
hexdigest_original = self._render_with_graphviz(dot_file)
|
hexdigest_original = self._render_with_graphviz(dot_file)
|
||||||
|
|
||||||
self.assertEqual(hexdigest, hexdigest_original)
|
self.assertEqual(hexdigest, hexdigest_original)
|
||||||
|
|
||||||
def test_multiple_graphs(self):
|
def test_multiple_graphs(self):
|
||||||
|
|
||||||
graph_data = 'graph A { a->b };\ngraph B {c->d}'
|
graph_data = 'graph A { a->b };\ngraph B {c->d}'
|
||||||
|
|
||||||
graphs = pydot.graph_from_dot_data(graph_data)
|
graphs = pydot.graph_from_dot_data(graph_data)
|
||||||
|
|
||||||
self.assertEqual(len(graphs), 2)
|
self.assertEqual(len(graphs), 2)
|
||||||
|
|
||||||
self.assertEqual([g.get_name() for g in graphs], ['A', 'B'])
|
self.assertEqual([g.get_name() for g in graphs], ['A', 'B'])
|
||||||
|
|
||||||
def _render_with_graphviz(self, filename):
|
def _render_with_graphviz(self, filename):
|
||||||
|
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
(DOT_BINARY_PATH, '-Tjpe'),
|
(DOT_BINARY_PATH, '-Tjpe'),
|
||||||
cwd=os.path.dirname(filename),
|
cwd=os.path.dirname(filename),
|
||||||
stdin=open(filename, 'rt'),
|
stdin=open(filename, 'rt'),
|
||||||
stderr=subprocess.PIPE, stdout=subprocess.PIPE
|
stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
)
|
|
||||||
|
|
||||||
stdout = p.stdout
|
stdout = p.stdout
|
||||||
|
|
||||||
@ -197,25 +170,16 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
if stdout_output:
|
if stdout_output:
|
||||||
stdout_output = NULL_SEP.join(stdout_output)
|
stdout_output = NULL_SEP.join(stdout_output)
|
||||||
|
|
||||||
#pid, status = os.waitpid(p.pid, 0)
|
|
||||||
# this returns a status code we should check
|
# this returns a status code we should check
|
||||||
p.wait()
|
p.wait()
|
||||||
|
|
||||||
return sha256(stdout_output).hexdigest()
|
return sha256(stdout_output).hexdigest()
|
||||||
|
|
||||||
def _render_with_pydot(self, filename):
|
def _render_with_pydot(self, filename):
|
||||||
#f = open(filename, 'rt')
|
|
||||||
#graph_data = f.read()
|
|
||||||
#f.close()
|
|
||||||
|
|
||||||
#g = pydot.parse_from_dot_data(graph_data)
|
|
||||||
g = pydot.graph_from_dot_file(filename)
|
g = pydot.graph_from_dot_file(filename)
|
||||||
|
|
||||||
if not isinstance(g, list):
|
if not isinstance(g, list):
|
||||||
g = [g]
|
g = [g]
|
||||||
|
|
||||||
jpe_data = NULL_SEP.join([_g.create(format='jpe') for _g in g])
|
jpe_data = NULL_SEP.join([_g.create(format='jpe') for _g in g])
|
||||||
|
|
||||||
return sha256(jpe_data).hexdigest()
|
return sha256(jpe_data).hexdigest()
|
||||||
|
|
||||||
def test_my_regression_tests(self):
|
def test_my_regression_tests(self):
|
||||||
@ -229,11 +193,9 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
|
|
||||||
dot_files = [
|
dot_files = [
|
||||||
fname for fname in os.listdir(directory)
|
fname for fname in os.listdir(directory)
|
||||||
if fname.endswith('.dot')
|
if fname.endswith('.dot')]
|
||||||
] # and fname.startswith('')]
|
|
||||||
|
|
||||||
for dot in dot_files:
|
for dot in dot_files:
|
||||||
#print 'Processing: %s' % dot
|
|
||||||
os.sys.stdout.write('#')
|
os.sys.stdout.write('#')
|
||||||
os.sys.stdout.flush()
|
os.sys.stdout.flush()
|
||||||
|
|
||||||
@ -244,7 +206,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
original_data_hexdigest = self._render_with_graphviz(fname)
|
original_data_hexdigest = self._render_with_graphviz(fname)
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Failed rendering BAD(%s)' % dot)
|
print('Failed rendering BAD(%s)' % dot)
|
||||||
#print 'Error:', str(excp)
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if parsed_data_hexdigest != original_data_hexdigest:
|
if parsed_data_hexdigest != original_data_hexdigest:
|
||||||
@ -253,92 +214,67 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
self.assertEqual(parsed_data_hexdigest, original_data_hexdigest)
|
self.assertEqual(parsed_data_hexdigest, original_data_hexdigest)
|
||||||
|
|
||||||
def test_numeric_node_id(self):
|
def test_numeric_node_id(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node(1))
|
self.graph_directed.add_node(pydot.Node(1))
|
||||||
|
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(), '1')
|
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(), '1')
|
||||||
|
|
||||||
def test_quoted_node_id(self):
|
def test_quoted_node_id(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node('"node"'))
|
self.graph_directed.add_node(pydot.Node('"node"'))
|
||||||
|
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(),
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(), '"node"')
|
'"node"')
|
||||||
|
|
||||||
def test_quoted_node_id_to_string_no_attributes(self):
|
def test_quoted_node_id_to_string_no_attributes(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node('"node"'))
|
self.graph_directed.add_node(pydot.Node('"node"'))
|
||||||
|
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(),
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(), '"node";')
|
'"node";')
|
||||||
|
|
||||||
def test_keyword_node_id(self):
|
def test_keyword_node_id(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node('node'))
|
self.graph_directed.add_node(pydot.Node('node'))
|
||||||
|
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(),
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].get_name(), 'node')
|
'node')
|
||||||
|
|
||||||
def test_keyword_node_id_to_string_no_attributes(self):
|
def test_keyword_node_id_to_string_no_attributes(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node('node'))
|
self.graph_directed.add_node(pydot.Node('node'))
|
||||||
|
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(), '')
|
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(), '')
|
||||||
|
|
||||||
def test_keyword_node_id_to_string_with_attributes(self):
|
def test_keyword_node_id_to_string_with_attributes(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node('node', shape='box'))
|
self.graph_directed.add_node(pydot.Node('node', shape='box'))
|
||||||
|
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(),
|
||||||
self.assertEqual(self.graph_directed.get_nodes()[0].to_string(), 'node [shape=box];')
|
'node [shape=box];')
|
||||||
|
|
||||||
def test_names_of_a_thousand_nodes(self):
|
def test_names_of_a_thousand_nodes(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
names = set(['node_%05d' % i for i in xrange(10 ** 4)])
|
names = set(['node_%05d' % i for i in xrange(10 ** 4)])
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
|
|
||||||
self.graph_directed.add_node(pydot.Node(name, label=name))
|
self.graph_directed.add_node(pydot.Node(name, label=name))
|
||||||
|
|
||||||
self.assertEqual(set([n.get_name() for n in self.graph_directed.get_nodes()]), names)
|
self.assertEqual(set([n.get_name() for n in
|
||||||
|
self.graph_directed.get_nodes()]), names)
|
||||||
|
|
||||||
def test_executable_not_found_exception(self):
|
def test_executable_not_found_exception(self):
|
||||||
paths = {'dot': 'invalid_executable_path'}
|
paths = {'dot': 'invalid_executable_path'}
|
||||||
|
|
||||||
graph = pydot.Dot('graphname', graph_type='digraph')
|
graph = pydot.Dot('graphname', graph_type='digraph')
|
||||||
|
|
||||||
graph.set_graphviz_executables(paths)
|
graph.set_graphviz_executables(paths)
|
||||||
|
|
||||||
self.assertRaises(pydot.InvocationException, graph.create)
|
self.assertRaises(pydot.InvocationException, graph.create)
|
||||||
|
|
||||||
def test_graph_add_node_argument_type(self):
|
def test_graph_add_node_argument_type(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_node, 1)
|
self.assertRaises(TypeError, self.graph_directed.add_node, 1)
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_node, 'a')
|
self.assertRaises(TypeError, self.graph_directed.add_node, 'a')
|
||||||
|
|
||||||
def test_graph_add_edge_argument_type(self):
|
def test_graph_add_edge_argument_type(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_edge, 1)
|
self.assertRaises(TypeError, self.graph_directed.add_edge, 1)
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_edge, 'a')
|
self.assertRaises(TypeError, self.graph_directed.add_edge, 'a')
|
||||||
|
|
||||||
def test_graph_add_subgraph_argument_type(self):
|
def test_graph_add_subgraph_argument_type(self):
|
||||||
|
|
||||||
self._reset_graphs()
|
self._reset_graphs()
|
||||||
|
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_subgraph, 1)
|
self.assertRaises(TypeError, self.graph_directed.add_subgraph, 1)
|
||||||
self.assertRaises(TypeError, self.graph_directed.add_subgraph, 'a')
|
self.assertRaises(TypeError, self.graph_directed.add_subgraph, 'a')
|
||||||
|
|
||||||
@ -346,7 +282,6 @@ class TestGraphAPI(unittest.TestCase):
|
|||||||
import string
|
import string
|
||||||
g = pydot.Dot()
|
g = pydot.Dot()
|
||||||
g.add_node(pydot.Node("test", label=string.printable))
|
g.add_node(pydot.Node("test", label=string.printable))
|
||||||
#print g.to_string()
|
|
||||||
data = g.create(format='jpe')
|
data = g.create(format='jpe')
|
||||||
self.assertEqual(len(data) > 0, True)
|
self.assertEqual(len(data) > 0, True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user