From d7c49b13897a45f7da891ce98090b64c18812fbe Mon Sep 17 00:00:00 2001 From: Kamil Sambor Date: Sun, 8 Mar 2015 20:00:54 +0100 Subject: [PATCH 1/6] Fix flakes8 and improve readability --- pydot_ng/__init__.py | 257 +++++++++++++++++------------------------ pydot_ng/_dotparser.py | 61 +++++----- test/test_pydot.py | 85 +++----------- 3 files changed, 154 insertions(+), 249 deletions(-) diff --git a/pydot_ng/__init__.py b/pydot_ng/__init__.py index dd8855b..ae4c5b4 100644 --- a/pydot_ng/__init__.py +++ b/pydot_ng/__init__.py @@ -1,25 +1,17 @@ # -*- coding: Latin-1 -*- -"""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 - -Distributed under MIT license [http://opensource.org/licenses/mit-license.html]. -""" +# 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 +# Distributed under MIT license +# [http://opensource.org/licenses/mit-license.html]. from __future__ import division, print_function -__author__ = 'Ero Carrera' -__license__ = 'MIT' - import os import re import subprocess @@ -32,8 +24,11 @@ from operator import itemgetter try: from . 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) @@ -103,23 +98,25 @@ CLUSTER_ATTRIBUTES = set([ ]) -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 +126,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): @@ -431,7 +429,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 +514,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): @@ -556,8 +556,9 @@ def find_graphviz(): 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 +566,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 +578,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,16 +588,15 @@ 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 ( @@ -611,7 +608,6 @@ def find_graphviz(): progs = __find_executables(path) if progs is not None: - #print("Used path") return progs # Failed to find GraphViz @@ -626,23 +622,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 +667,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 +682,6 @@ class Common(object): which are defined for all the existing attributes. """ - self.obj_dict['attributes'][name] = value def get(self, name): @@ -706,37 +694,28 @@ 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) ) # 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): @@ -750,7 +729,8 @@ class Error(Exception): class InvocationException(Exception): - """To indicate that a ploblem occurred while running any of the GraphViz executables. + """To indicate that a ploblem occurred while running any of the + GraphViz executables. """ def __init__(self, value): self.value = value @@ -773,11 +753,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 +763,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 +770,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 +786,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): @@ -837,14 +810,13 @@ class Node(Common): def to_string(self): """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 +863,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 +892,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 +919,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 +929,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,11 +947,8 @@ 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 @@ -997,7 +956,6 @@ class Edge(Common): def to_string(self): """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 +984,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 +1036,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 @@ -1163,7 +1123,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 +1130,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 +1153,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 +1160,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 +1169,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 +1176,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 +1189,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,7 +1248,6 @@ class Graph(Common): Node instances is returned. An empty list is returned otherwise. """ - match = list() if name in self.obj_dict['nodes']: @@ -1310,7 +1261,6 @@ class Graph(Common): def get_nodes(self): """Get the list of Node instances.""" - return self.get_node_list() def get_node_list(self): @@ -1319,7 +1269,6 @@ 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(): @@ -1334,12 +1283,11 @@ class Graph(Common): 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 +1332,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: @@ -1424,8 +1373,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,7 +1387,6 @@ 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(): @@ -1457,8 +1405,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 +1440,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 @@ -1516,7 +1466,6 @@ class Graph(Common): 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 +1493,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 +1518,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() @@ -1620,7 +1573,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 +1595,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 +1618,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 +1641,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' @@ -1740,7 +1693,8 @@ class Dot(Graph): 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] @@ -1759,13 +1713,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,14 +1737,16 @@ class Dot(Graph): self.prog = prog def set_graphviz_executables(self, paths): - """This method allows to manually specify the location of the GraphViz executables. + """This method allows to 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 @@ -1893,9 +1849,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 +1868,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 +1908,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 +1919,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))) diff --git a/pydot_ng/_dotparser.py b/pydot_ng/_dotparser.py index df191fd..ebb2aab 100644 --- a/pydot_ng/_dotparser.py +++ b/pydot_ng/_dotparser.py @@ -1,19 +1,15 @@ -"""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 -Fixes by: Ero Carrera -""" +# Author: Michael Krause +# Fixes by: Ero Carrera from __future__ import division, print_function -__author__ = ['Michael Krause', 'Ero Carrera'] -__license__ = 'MIT' - import sys import pydot_ng as pydot import codecs @@ -27,6 +23,9 @@ from pyparsing import ( ParseResults, CharsNotIn, QuotedString ) +__author__ = ['Michael Krause', 'Ero Carrera'] +__license__ = 'MIT' + PY3 = not sys.version_info < (3, 0, 0) @@ -134,7 +133,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 +142,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 +160,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: @@ -181,7 +184,8 @@ def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge elif isinstance(element, 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 +202,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) @@ -290,7 +295,8 @@ def push_edge_stmt(str, loc, toks): 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 +310,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) @@ -352,10 +359,6 @@ def graph_definition(): rparen = Literal(")") equals = Literal("=") comma = Literal(",") - # dot = Literal(".") - # slash = Literal("/") - # bslash = Literal("\\") - # star = Literal("*") semi = Literal(";") at = Literal("@") minus = Literal("-") @@ -372,7 +375,8 @@ def graph_definition(): identifier = Word(alphanums + "_.").setName("identifier") # dblQuotedString - double_quoted_string = QuotedString('"', multiline=True, unquoteResults=False) + double_quoted_string = QuotedString('"', multiline=True, + unquoteResults=False) noncomma_ = "".join([c for c in printables if c != ","]) alphastring_ = OneOrMore(CharsNotIn(noncomma_ + ' ')) @@ -389,7 +393,7 @@ def graph_definition(): ID = ( identifier | html_text | - double_quoted_string | # .setParseAction(strip_quotes) | + double_quoted_string | alphastring_ ).setName("ID") @@ -421,7 +425,8 @@ def graph_definition(): lbrack.suppress() + Optional(a_list) + rbrack.suppress() ).setName("attr_list") - attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt") + attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).\ + setName("attr_stmt") edgeop = (Literal("--") | Literal("->")).setName("edgeop") @@ -436,9 +441,11 @@ def graph_definition(): edgeRHS = OneOrMore(edgeop + edge_point) edge_stmt = edge_point + edgeRHS + Optional(attr_list) - subgraph = Group(subgraph_ + Optional(ID) + graph_stmt).setName("subgraph") + subgraph = Group(subgraph_ + Optional(ID) + graph_stmt).\ + setName("subgraph") - edge_point << Group(subgraph | graph_stmt | node_id).setName('edge_point') + edge_point << Group(subgraph | graph_stmt | node_id).\ + setName('edge_point') node_stmt = ( node_id + Optional(attr_list) + Optional(semi.suppress()) diff --git a/test/test_pydot.py b/test/test_pydot.py index d8ff895..e006216 100644 --- a/test/test_pydot.py +++ b/test/test_pydot.py @@ -39,25 +39,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 +60,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 +67,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 +78,6 @@ class TestGraphAPI(unittest.TestCase): self.assertEqual('decorate' in attrs, True) def test_subgraphs(self): - g = pydot.Graph() s = pydot.Subgraph("foo") @@ -99,7 +90,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 +103,6 @@ class TestGraphAPI(unittest.TestCase): self.assertEqual(type(pickle.dumps(g)), bytes) def test_unicode_ids(self): - node1 = '"aánñoöüé€"' node2 = '"îôø®çßΩ"' @@ -139,7 +128,6 @@ 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') @@ -154,29 +142,19 @@ class TestGraphAPI(unittest.TestCase): 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), @@ -197,25 +175,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): @@ -230,10 +199,9 @@ class TestGraphAPI(unittest.TestCase): dot_files = [ fname for fname in os.listdir(directory) if fname.endswith('.dot') - ] # and fname.startswith('')] + ] for dot in dot_files: - #print 'Processing: %s' % dot os.sys.stdout.write('#') os.sys.stdout.flush() @@ -244,7 +212,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 +220,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 +288,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) From 1e9274785f6e7090d0784b758d01990d1ba74e6d Mon Sep 17 00:00:00 2001 From: Sebastian Kalinowski Date: Sun, 8 Mar 2015 23:05:02 +0100 Subject: [PATCH 2/6] Enable flake8 checks on CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 09fc611..5158e9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,3 +11,4 @@ env: - TOXENV=py27 - TOXENV=py33 - TOXENV=py34 + - TOXENV=pep8 From d70e9762ce3b80da0c0c6743d852f3bf7782832d Mon Sep 17 00:00:00 2001 From: Kamil Sambor Date: Sun, 8 Mar 2015 20:00:54 +0100 Subject: [PATCH 3/6] Fix flakes8 and improve readability --- pydot_ng/__init__.py | 293 +++++++++++++++++------------------------ pydot_ng/_dotparser.py | 214 +++++++++++++++--------------- test/test_pydot.py | 96 +++----------- 3 files changed, 248 insertions(+), 355 deletions(-) diff --git a/pydot_ng/__init__.py b/pydot_ng/__init__.py index dd8855b..2fa1014 100644 --- a/pydot_ng/__init__.py +++ b/pydot_ng/__init__.py @@ -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 +# 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 - -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): @@ -241,8 +235,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 @@ -431,7 +424,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 +509,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): @@ -556,8 +551,9 @@ def find_graphviz(): 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 +561,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 +573,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,16 +583,15 @@ 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 ( @@ -611,7 +603,6 @@ def find_graphviz(): progs = __find_executables(path) if progs is not None: - #print("Used path") return progs # Failed to find GraphViz @@ -626,23 +617,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 +662,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 +677,6 @@ class Common(object): which are defined for all the existing attributes. """ - self.obj_dict['attributes'][name] = value def get(self, name): @@ -706,42 +689,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) ) # 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 +723,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 +745,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 +755,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 +762,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 +778,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 +800,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 +854,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 +883,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 +910,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 +920,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 +938,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 +974,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 +1026,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 @@ -1163,7 +1113,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 +1120,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 +1143,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 +1150,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 +1159,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 +1166,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 +1179,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,7 +1238,6 @@ class Graph(Common): Node instances is returned. An empty list is returned otherwise. """ - match = list() if name in self.obj_dict['nodes']: @@ -1310,7 +1251,6 @@ class Graph(Common): def get_nodes(self): """Get the list of Node instances.""" - return self.get_node_list() def get_node_list(self): @@ -1319,7 +1259,6 @@ 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(): @@ -1334,12 +1273,11 @@ class Graph(Common): 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 +1322,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: @@ -1424,8 +1363,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,7 +1377,6 @@ 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(): @@ -1457,8 +1395,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 +1430,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 @@ -1516,7 +1456,6 @@ class Graph(Common): 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 +1483,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 +1508,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() @@ -1620,7 +1563,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 +1585,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 +1608,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 +1631,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' @@ -1740,7 +1683,8 @@ class Dot(Graph): 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] @@ -1759,13 +1703,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 +1727,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 +1775,7 @@ class Dot(Graph): if not isinstance(data, unicode): try: data = unicode(data, 'utf-8') - except: + except Exception: pass try: @@ -1837,7 +1783,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 +1839,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 +1858,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 +1898,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 +1909,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))) diff --git a/pydot_ng/_dotparser.py b/pydot_ng/_dotparser.py index df191fd..5915bba 100644 --- a/pydot_ng/_dotparser.py +++ b/pydot_ng/_dotparser.py @@ -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 -Fixes by: Ero Carrera -""" +# Author: Michael Krause +# Fixes by: Ero Carrera + +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 = {} @@ -76,7 +69,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 +97,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 +127,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 +136,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 +154,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 +176,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 +196,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 +219,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 +252,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 +282,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 +304,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,59 +344,58 @@ 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.alphapyparsing.nums + "_.").\ + 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)) + (pyparsing.CharsNotIn(opener + closer)) ).setParseAction(parse_html).leaveWhitespace() ID = ( identifier | html_text | - double_quoted_string | # .setParseAction(strip_quotes) | + double_quoted_string | alphastring_ ).setName("ID") - float_number = Combine( - Optional(minus) + - OneOrMore(Word(nums + ".")) + float_number = pyparsing.Combine( + pyparsing.Optional(minus) + + pyparsing.OneOrMore(pyparsing.Word(pyparsing.nums + ".")) ).setName("float_number") righthand_id = (float_number | ID).setName("righthand_id") @@ -403,64 +403,72 @@ def graph_definition(): port_angle = (at + ID).setName("port_angle") port_location = ( - OneOrMore(Group(colon + ID)) | - Group(colon + lparen + ID + comma + ID + rparen) + 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)) + 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()) + 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() + 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()) + 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())) + stmt_list << pyparsing.OneOrMore(stmt + pyparsing.Optional( + semi.suppress())) - graphparser = OneOrMore(( - Optional(strict_) + Group((graph_ | digraph_)) + - Optional(ID) + graph_stmt + 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 +500,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 +510,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 +520,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) + "^") diff --git a/test/test_pydot.py b/test/test_pydot.py index d8ff895..efd2e8d 100644 --- a/test/test_pydot.py +++ b/test/test_pydot.py @@ -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,7 +125,6 @@ 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') @@ -154,29 +139,19 @@ class TestGraphAPI(unittest.TestCase): 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), @@ -197,25 +172,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): @@ -230,10 +196,9 @@ class TestGraphAPI(unittest.TestCase): dot_files = [ fname for fname in os.listdir(directory) if fname.endswith('.dot') - ] # and fname.startswith('')] + ] for dot in dot_files: - #print 'Processing: %s' % dot os.sys.stdout.write('#') os.sys.stdout.flush() @@ -244,7 +209,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 +217,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 +285,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) From acec98fe2b356e914e268586eb29224706629ad1 Mon Sep 17 00:00:00 2001 From: Kamil Sambor Date: Sat, 14 Mar 2015 13:25:16 +0100 Subject: [PATCH 4/6] Fix wrong import --- pydot_ng/_dotparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydot_ng/_dotparser.py b/pydot_ng/_dotparser.py index 5915bba..81b9047 100644 --- a/pydot_ng/_dotparser.py +++ b/pydot_ng/_dotparser.py @@ -366,7 +366,7 @@ def graph_definition(): edge_ = pyparsing.CaselessLiteral("edge") # token definitions - identifier = pyparsing.Word(pyparsing.alphapyparsing.nums + "_.").\ + identifier = pyparsing.Word(pyparsing.alphanums + "_.").\ setName("identifier") # dblpyparsing.QuotedString From e263d22247906ea0ff9e448ed533d5572f09ceda Mon Sep 17 00:00:00 2001 From: Sebastian Kalinowski Date: Sat, 14 Mar 2015 16:59:02 +0100 Subject: [PATCH 5/6] Ignore H105 H105 checks for "Authors" tags in files. We want (and need) to keep such checks. --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index d57a861..537e76f 100644 --- a/tox.ini +++ b/tox.ini @@ -16,3 +16,5 @@ commands = flake8 [flake8] exclude = .venv,.tox,dist,doc,*.egg,build show-source = true +# H105 - author tags in files +ignore = H105 From f794a853001c3ce0a13ba4e3bcd300b0c64a7187 Mon Sep 17 00:00:00 2001 From: Kamil Sambor Date: Sun, 15 Mar 2015 20:41:38 +0100 Subject: [PATCH 6/6] Fix rest of e123 errors --- pydot_ng/__init__.py | 64 +++++++++++++++--------------------------- pydot_ng/_dotparser.py | 42 +++++++++++++-------------- setup.py | 9 ++---- test/test_pydot.py | 9 ++---- 4 files changed, 48 insertions(+), 76 deletions(-) diff --git a/pydot_ng/__init__.py b/pydot_ng/__init__.py index 2fa1014..1c4e4d1 100644 --- a/pydot_ng/__init__.py +++ b/pydot_ng/__init__.py @@ -202,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) @@ -364,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 @@ -402,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) @@ -521,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 @@ -537,15 +531,13 @@ 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") @@ -598,8 +590,7 @@ def find_graphviz(): '/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: @@ -705,8 +696,8 @@ class Common(object): # 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, @@ -1040,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 @@ -1244,8 +1234,7 @@ class Graph(Common): match.extend([ Node(obj_dict=obj_dict) for obj_dict - in self.obj_dict['nodes'][name] - ]) + in self.obj_dict['nodes'][name]]) return match @@ -1265,8 +1254,7 @@ class Graph(Common): node_objs.extend([ Node(obj_dict=obj_d) for obj_d - in obj_dict_list - ]) + in obj_dict_list]) return node_objs @@ -1354,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, @@ -1383,8 +1370,7 @@ class Graph(Common): edge_objs.extend([ Edge(obj_dict=obj_d) for obj_d - in obj_dict_list - ]) + in obj_dict_list]) return edge_objs @@ -1450,8 +1436,7 @@ 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 @@ -1525,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': @@ -1663,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 @@ -1672,26 +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) - ) + 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) diff --git a/pydot_ng/_dotparser.py b/pydot_ng/_dotparser.py index 81b9047..f917a9c 100644 --- a/pydot_ng/_dotparser.py +++ b/pydot_ng/_dotparser.py @@ -57,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() @@ -384,19 +383,18 @@ def graph_definition(): closer = '>' html_text = pyparsing.nestedExpr( opener, closer, - (pyparsing.CharsNotIn(opener + closer)) - ).setParseAction(parse_html).leaveWhitespace() + (pyparsing.CharsNotIn(opener + closer))).\ + setParseAction(parse_html).leaveWhitespace() ID = ( identifier | html_text | double_quoted_string | - alphastring_ - ).setName("ID") + alphastring_).setName("ID") float_number = pyparsing.Combine( pyparsing.Optional(minus) + - pyparsing.OneOrMore(pyparsing.Word(pyparsing.nums + ".")) - ).setName("float_number") + pyparsing.OneOrMore(pyparsing.Word(pyparsing.nums + "."))).\ + setName("float_number") righthand_id = (float_number | ID).setName("righthand_id") @@ -404,23 +402,23 @@ def graph_definition(): port_location = ( pyparsing.OneOrMore(pyparsing.Group(colon + ID)) | - pyparsing.Group(colon + lparen + ID + comma + ID + rparen) - ).setName("port_location") + pyparsing.Group(colon + lparen + ID + comma + ID + rparen)).\ + setName("port_location") port = ( pyparsing.Group(port_location + pyparsing.Optional(port_angle)) | - pyparsing.Group(port_angle + pyparsing.Optional(port_location)) - ).setName("port") + pyparsing.Group(port_angle + pyparsing.Optional(port_location))).\ + setName("port") node_id = (ID + pyparsing.Optional(port)) a_list = pyparsing.OneOrMore( ID + pyparsing.Optional(equals + righthand_id) + - pyparsing.Optional(comma.suppress()) - ).setName("a_list") + pyparsing.Optional(comma.suppress())).\ + setName("a_list") attr_list = pyparsing.OneOrMore( - lbrack.suppress() + pyparsing.Optional(a_list) + rbrack.suppress() - ).setName("attr_list") + lbrack.suppress() + pyparsing.Optional(a_list) + + rbrack.suppress()).setName("attr_list") attr_stmt = (pyparsing.Group(graph_ | node_ | edge_) + attr_list).\ setName("attr_stmt") @@ -431,8 +429,8 @@ def graph_definition(): stmt_list = pyparsing.Forward() graph_stmt = pyparsing.Group( lbrace.suppress() + pyparsing.Optional(stmt_list) + - rbrace.suppress() + pyparsing.Optional(semi.suppress()) - ).setName("graph_stmt") + rbrace.suppress() + pyparsing.Optional(semi.suppress())).\ + setName("graph_stmt") edge_point = pyparsing.Forward() @@ -452,16 +450,16 @@ def graph_definition(): assignment = (ID + equals + righthand_id).setName("assignment") stmt = ( assignment | edge_stmt | attr_stmt | - subgraph | graph_stmt | node_stmt - ).setName("stmt") + subgraph | graph_stmt | node_stmt).\ + setName("stmt") stmt_list << pyparsing.OneOrMore(stmt + pyparsing.Optional( semi.suppress())) graphparser = pyparsing.OneOrMore(( pyparsing.Optional(strict_) + pyparsing.Group((graph_ | digraph_)) + - pyparsing.Optional(ID) + graph_stmt - ).setResultsName("graph")) + pyparsing.Optional(ID) + graph_stmt). + setResultsName("graph")) singleLineComment = (pyparsing.Group("//" + pyparsing.restOfLine) | pyparsing.Group("#" + pyparsing.restOfLine)) diff --git a/setup.py b/setup.py index 6671ef4..55e0441 100644 --- a/setup.py +++ b/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'], +) diff --git a/test/test_pydot.py b/test/test_pydot.py index efd2e8d..409d6dc 100644 --- a/test/test_pydot.py +++ b/test/test_pydot.py @@ -131,8 +131,7 @@ class TestGraphAPI(unittest.TestCase): 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() @@ -156,8 +155,7 @@ class TestGraphAPI(unittest.TestCase): (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 @@ -195,8 +193,7 @@ class TestGraphAPI(unittest.TestCase): dot_files = [ fname for fname in os.listdir(directory) - if fname.endswith('.dot') - ] + if fname.endswith('.dot')] for dot in dot_files: os.sys.stdout.write('#')