fix: dev: Improved project layout.
This commit is contained in:
parent
3dc0e6e666
commit
fa561ada17
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
Copyright (c) 2014 Lance Hepler
|
||||
Copyright (c) 2004-2007 Ero Carrera <ero@dkbza.org>
|
||||
Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
11
README.rst
11
README.rst
@ -34,11 +34,18 @@ PyDotplus - Python interface to Graphviz's Dot language
|
||||
About
|
||||
=====
|
||||
|
||||
PyDotPlus is a Python 2.7 - Python 3, documented and CI Tested version of the
|
||||
old pydot project that provides a Python Interface to Graphviz's Dot language.
|
||||
PyDotPlus is an improved version of the old pydot project that provides a
|
||||
Python Interface to Graphviz's Dot language.
|
||||
|
||||
http://pydotplus.readthedocs.org/
|
||||
|
||||
Differences with pydot:
|
||||
|
||||
- Compatible with PyParsing 2.0+.
|
||||
- Python 2.7 - Python 3 compatible.
|
||||
- Well documented.
|
||||
- CI Tested.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
23
doc/conf.py
23
doc/conf.py
@ -18,7 +18,7 @@ import os
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.insert(0, os.path.abspath('../lib/'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
@ -200,8 +200,10 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'PyDotPlus.tex', u'PyDotPlus Documentation',
|
||||
u'PyDotPlus Developers', 'manual'),
|
||||
(
|
||||
'index', 'PyDotPlus.tex', u'PyDotPlus Documentation',
|
||||
u'PyDotPlus Developers', 'manual'
|
||||
),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@ -230,8 +232,10 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'pydotplus', u'PyDotPlus Documentation',
|
||||
[u'PyDotPlus Developers'], 1)
|
||||
(
|
||||
'index', 'pydotplus', u'PyDotPlus Documentation',
|
||||
[u'PyDotPlus Developers'], 1
|
||||
)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@ -244,9 +248,12 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'PyDotPlus', u'PyDotPlus Documentation',
|
||||
u'PyDotPlus Developers', 'PyDotPlus', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
'index', 'PyDotPlus', u'PyDotPlus Documentation',
|
||||
u'PyDotPlus Developers', 'PyDotPlus',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
31
lib/pydotplus/__init__.py
Normal file
31
lib/pydotplus/__init__.py
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (c) 2014 Lance Hepler
|
||||
# Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
# Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
PyDotPlus module entry.
|
||||
"""
|
||||
|
||||
from .parser import * # noqa
|
||||
from .graph import * # noqa
|
@ -1,5 +1,30 @@
|
||||
# -*- coding: Latin-1 -*-
|
||||
"""Graphviz's dot language Python interface.
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (c) 2014 Lance Hepler
|
||||
# Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
# Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
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.
|
||||
@ -13,16 +38,10 @@ DOT Language: http://www.graphviz.org/doc/info/lang.html
|
||||
Programmed and tested with Graphviz 2.26.3 and Python 2.6 on OSX 10.6.4
|
||||
|
||||
Copyright (c) 2005-2011 Ero Carrera <ero.carrera@gmail.com>
|
||||
|
||||
Distributed under MIT license [http://opensource.org/licenses/mit-license.html].
|
||||
"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
__author__ = 'Ero Carrera'
|
||||
__version__ = '1.0.29'
|
||||
__license__ = 'MIT'
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
@ -111,13 +130,15 @@ def is_string_like(obj): # from John Hunter, types-free version
|
||||
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*
|
||||
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
|
||||
@ -129,9 +150,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 +263,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 +452,14 @@ 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 +543,9 @@ 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 +586,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
|
||||
|
||||
@ -590,7 +621,9 @@ 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...
|
||||
path = r"C:\Program Files\att\Graphviz\bin"
|
||||
@ -606,8 +639,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:
|
||||
@ -736,7 +768,10 @@ class Common(object):
|
||||
|
||||
# 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 +785,9 @@ 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
|
||||
@ -776,7 +813,8 @@ class Node(Common):
|
||||
|
||||
#
|
||||
# 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:
|
||||
@ -844,7 +882,9 @@ class Node(Common):
|
||||
|
||||
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:
|
||||
@ -1026,7 +1066,9 @@ 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 +1119,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
|
||||
@ -1241,17 +1284,24 @@ class Graph(Common):
|
||||
"""
|
||||
|
||||
if not isinstance(graph_node, Node):
|
||||
raise TypeError('add_node() received a non node class object: ' + str(graph_node))
|
||||
raise TypeError(
|
||||
'add_node() received a non node '
|
||||
'class object: {}'.format(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())
|
||||
|
||||
@ -1339,7 +1389,10 @@ class Graph(Common):
|
||||
"""
|
||||
|
||||
if not isinstance(graph_edge, Edge):
|
||||
raise TypeError('add_edge() received a non edge class object: ' + str(graph_edge))
|
||||
raise TypeError(
|
||||
'add_edge() received a non '
|
||||
'edge class object: {}'.format(str(graph_edge))
|
||||
)
|
||||
|
||||
edge_points = (graph_edge.get_source(), graph_edge.get_destination())
|
||||
|
||||
@ -1384,7 +1437,8 @@ class Graph(Common):
|
||||
dst = dst.get_name()
|
||||
|
||||
if (src, dst) in self.obj_dict['edges']:
|
||||
if index is not None and index < len(self.obj_dict['edges'][(src, dst)]):
|
||||
if index is not None and index < len(
|
||||
self.obj_dict['edges'][(src, dst)]):
|
||||
del self.obj_dict['edges'][(src, dst)][index]
|
||||
return True
|
||||
else:
|
||||
@ -1415,17 +1469,18 @@ 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,
|
||||
self.obj_dict['edges'].get(edge_points_reverse, None))
|
||||
|
||||
for edge_obj_dict in edges_obj_dict:
|
||||
match.append(
|
||||
Edge(edge_points[0], edge_points[1], obj_dict=edge_obj_dict)
|
||||
)
|
||||
match.append(Edge(
|
||||
edge_points[0],
|
||||
edge_points[1],
|
||||
obj_dict=edge_obj_dict
|
||||
))
|
||||
|
||||
return match
|
||||
|
||||
@ -1457,8 +1512,12 @@ 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(
|
||||
'add_subgraph() received a non '
|
||||
'subgraph class object:'.format(str(sgraph))
|
||||
)
|
||||
|
||||
if sgraph.get_name() in self.obj_dict['subgraphs']:
|
||||
|
||||
@ -1489,7 +1548,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
|
||||
@ -1544,14 +1602,22 @@ 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(
|
||||
'{} {} {{\n'.format(
|
||||
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 +1632,9 @@ 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 +1688,9 @@ 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
|
||||
@ -1651,7 +1721,8 @@ class Subgraph(Graph):
|
||||
|
||||
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 +1735,9 @@ 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)
|
||||
@ -1687,12 +1760,14 @@ class Cluster(Graph):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
|
||||
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
|
||||
suppress_disconnected=suppress_disconnected, simplify=simplify,
|
||||
**attrs
|
||||
)
|
||||
|
||||
if obj_dict is None:
|
||||
@ -1740,7 +1815,9 @@ 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 +1836,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 +1860,17 @@ 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 +1973,12 @@ 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 "{}" is not'
|
||||
' a file or doesn\'t exist'.format(self.progs[prog])
|
||||
)
|
||||
|
||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||
os.close(tmp_fd)
|
||||
@ -1910,7 +1993,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()
|
@ -1,19 +1,37 @@
|
||||
"""Graphviz's dot language parser.
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (c) 2014 Lance Hepler
|
||||
# Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
# Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
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 module needs pyparsing (tested with version 1.2.2) and pydot
|
||||
|
||||
Author: Michael Krause <michael@krause-software.de>
|
||||
Fixes by: Ero Carrera <ero@dkbza.org>
|
||||
"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
__author__ = ['Michael Krause', 'Ero Carrera']
|
||||
__license__ = 'MIT'
|
||||
|
||||
import sys
|
||||
import pydot
|
||||
import codecs
|
||||
@ -134,7 +152,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 +161,18 @@ 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 +182,10 @@ 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 +208,9 @@ 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 +227,9 @@ 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: %s " % element.default_type
|
||||
)
|
||||
|
||||
elif isinstance(element, P_AttrList):
|
||||
g.obj_dict['attributes'].update(element.attrs)
|
||||
@ -290,7 +321,9 @@ 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 +337,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)
|
||||
@ -372,7 +406,9 @@ 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_ + ' '))
|
||||
@ -421,7 +457,9 @@ 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 +474,13 @@ 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())
|
35
lib/pydotplus/version.py
Normal file
35
lib/pydotplus/version.py
Normal file
@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (c) 2014 Lance Hepler
|
||||
# Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
# Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
PyDotPlus version module.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import print_function
|
||||
|
||||
__version__ = '2.0.0'
|
||||
|
||||
__all__ = ['__version__']
|
72
setup.py
72
setup.py
@ -1,39 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||
# Copyright (c) 2014 Lance Hepler
|
||||
# Copyright (c) 2004-2011 Ero Carrera <ero@dkbza.org>
|
||||
# Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
try:
|
||||
from distutils.core import setup
|
||||
except ImportError:
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
import pydot
|
||||
|
||||
def find_version(filename):
|
||||
import os
|
||||
import re
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
with open(os.path.join(here, filename)) as fd:
|
||||
version_match = re.search(
|
||||
r"^__version__ = ['\"]([^'\"]*)['\"]", fd.read(), re.M
|
||||
)
|
||||
if version_match:
|
||||
return version_match.group(1)
|
||||
raise RuntimeError('Unable to find version string.')
|
||||
|
||||
os.environ['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
|
||||
os.environ['COPYFILE_DISABLE'] = 'true'
|
||||
|
||||
setup(
|
||||
name='pydot',
|
||||
version=pydot.__version__,
|
||||
description='Python interface to Graphviz\'s Dot',
|
||||
author='Ero Carrera',
|
||||
author_email='ero@dkbza.org',
|
||||
url='http://code.google.com/p/pydot/',
|
||||
download_url='http://pydot.googlecode.com/files/pydot-%s.tar.gz' % pydot.__version__,
|
||||
license='MIT',
|
||||
name='pydotplus',
|
||||
version=find_version('lib/pydotplus/version.py'),
|
||||
package_dir={'' : 'lib'},
|
||||
packages=find_packages('lib'),
|
||||
|
||||
# Metadata
|
||||
author='PyDotPlus Developers',
|
||||
author_email='carlos@jenkins.co.cr',
|
||||
description='Python interface to Graphviz\'s Dot Language',
|
||||
long_description=open('README.rst', 'r').read(),
|
||||
url='http://pydotplus.readthedocs.org/',
|
||||
|
||||
keywords='graphviz dot graphs visualization',
|
||||
platforms=['any'],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Science/Research',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Scientific/Engineering :: Visualization',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
],
|
||||
long_description="\n".join(pydot.__doc__.split('\n')),
|
||||
packages=['pydot'],
|
||||
package_dir={'pydot': 'pydot'},
|
||||
install_requires=['pyparsing>=2.0.1',],
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user