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