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 Carlos Jenkins <carlos@jenkins.co.cr>
|
||||||
Copyright (c) 2014 Lance Hepler
|
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>
|
Copyright (c) 2004-2007 Michael Krause <michael@krause-software.de>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
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
|
About
|
||||||
=====
|
=====
|
||||||
|
|
||||||
PyDotPlus is a Python 2.7 - Python 3, documented and CI Tested version of the
|
PyDotPlus is an improved version of the old pydot project that provides a
|
||||||
old pydot project that provides a Python Interface to Graphviz's Dot language.
|
Python Interface to Graphviz's Dot language.
|
||||||
|
|
||||||
http://pydotplus.readthedocs.org/
|
http://pydotplus.readthedocs.org/
|
||||||
|
|
||||||
|
Differences with pydot:
|
||||||
|
|
||||||
|
- Compatible with PyParsing 2.0+.
|
||||||
|
- Python 2.7 - Python 3 compatible.
|
||||||
|
- Well documented.
|
||||||
|
- CI Tested.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
35
doc/conf.py
35
doc/conf.py
@ -18,7 +18,7 @@ import os
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# 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
|
# 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.
|
# 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 ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
@ -186,22 +186,24 @@ htmlhelp_basename = 'PyDotPlusdoc'
|
|||||||
# -- Options for LaTeX output ---------------------------------------------
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#'pointsize': '10pt',
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#'preamble': '',
|
#'preamble': '',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
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
|
# 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
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
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.
|
# If true, show URL addresses after external links.
|
||||||
@ -244,9 +248,12 @@ man_pages = [
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'PyDotPlus', u'PyDotPlus Documentation',
|
(
|
||||||
u'PyDotPlus Developers', 'PyDotPlus', 'One line description of project.',
|
'index', 'PyDotPlus', u'PyDotPlus Documentation',
|
||||||
'Miscellaneous'),
|
u'PyDotPlus Developers', 'PyDotPlus',
|
||||||
|
'One line description of project.',
|
||||||
|
'Miscellaneous'
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# 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 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Graphviz's dot language Python interface.
|
#
|
||||||
|
# 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
|
This module provides with a full interface to create handle modify
|
||||||
and process graphs in Graphviz's dot language.
|
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
|
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>
|
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
|
from __future__ import division, print_function
|
||||||
|
|
||||||
__author__ = 'Ero Carrera'
|
|
||||||
__version__ = '1.0.29'
|
|
||||||
__license__ = 'MIT'
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -63,7 +82,7 @@ GRAPH_ATTRIBUTES = set([
|
|||||||
'stylesheet', 'target', 'truecolor', 'viewport', 'voro_margin',
|
'stylesheet', 'target', 'truecolor', 'viewport', 'voro_margin',
|
||||||
# for subgraphs
|
# for subgraphs
|
||||||
'rank'
|
'rank'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
EDGE_ATTRIBUTES = set([
|
EDGE_ATTRIBUTES = set([
|
||||||
@ -79,7 +98,7 @@ EDGE_ATTRIBUTES = set([
|
|||||||
'style', 'tailURL', 'tailclip', 'tailhref', 'taillabel', 'tailport',
|
'style', 'tailURL', 'tailclip', 'tailhref', 'taillabel', 'tailport',
|
||||||
'tailtarget', 'tailtooltip', 'target', 'tooltip', 'weight',
|
'tailtarget', 'tailtooltip', 'target', 'tooltip', 'weight',
|
||||||
'rank'
|
'rank'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
NODE_ATTRIBUTES = set([
|
NODE_ATTRIBUTES = set([
|
||||||
@ -92,7 +111,7 @@ NODE_ATTRIBUTES = set([
|
|||||||
'target', 'tooltip', 'vertices', 'width', 'z',
|
'target', 'tooltip', 'vertices', 'width', 'z',
|
||||||
# The following are attributes dot2tex
|
# The following are attributes dot2tex
|
||||||
'texlbl', 'texmode'
|
'texlbl', 'texmode'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
CLUSTER_ATTRIBUTES = set([
|
CLUSTER_ATTRIBUTES = set([
|
||||||
@ -100,10 +119,10 @@ CLUSTER_ATTRIBUTES = set([
|
|||||||
'fillcolor', 'fontcolor', 'fontname', 'fontsize', 'label', 'labeljust',
|
'fillcolor', 'fontcolor', 'fontname', 'fontsize', 'label', 'labeljust',
|
||||||
'labelloc', 'lheight', 'lp', 'lwidth', 'nojustify', 'pencolor',
|
'labelloc', 'lheight', 'lp', 'lwidth', 'nojustify', 'pencolor',
|
||||||
'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'
|
'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def is_string_like(obj): # from John Hunter, types-free version
|
def is_string_like(obj): # from John Hunter, types-free version
|
||||||
"""Check if obj is string."""
|
"""Check if obj is string."""
|
||||||
try:
|
try:
|
||||||
obj + ''
|
obj + ''
|
||||||
@ -111,13 +130,15 @@ def is_string_like(obj): # from John Hunter, types-free version
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_fobj(fname, mode='w+'):
|
def get_fobj(fname, mode='w+'):
|
||||||
"""Obtain a proper file object.
|
"""Obtain a proper file object.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : string, file object, file descriptor
|
fname : string, file object, file descriptor
|
||||||
If a string or file descriptor, then we create a file object. If *fname*
|
If a string or file descriptor, then we create a file object.
|
||||||
|
If *fname*
|
||||||
is a file object, then we do nothing and ignore the specified *mode*
|
is a file object, then we do nothing and ignore the specified *mode*
|
||||||
parameter.
|
parameter.
|
||||||
mode : str
|
mode : str
|
||||||
@ -129,9 +150,10 @@ def get_fobj(fname, mode='w+'):
|
|||||||
The file object.
|
The file object.
|
||||||
close : bool
|
close : bool
|
||||||
If *fname* was a string, then *close* will be *True* to signify that
|
If *fname* was a string, then *close* will be *True* to signify that
|
||||||
the file object should be closed after writing to it. Otherwise, *close*
|
the file object should be closed after writing to it. Otherwise,
|
||||||
will be *False* signifying that the user, in essence, created the file
|
*close* will be *False* signifying that the user, in essence, created
|
||||||
object already and that subsequent operations should not close it.
|
the file object already and that subsequent operations should not
|
||||||
|
close it.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if is_string_like(fname):
|
if is_string_like(fname):
|
||||||
@ -209,7 +231,7 @@ dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict']
|
|||||||
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
|
id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
|
||||||
id_re_alpha_nums_with_ports = re.compile(
|
id_re_alpha_nums_with_ports = re.compile(
|
||||||
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE
|
'^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE
|
||||||
)
|
)
|
||||||
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
|
id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
|
||||||
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
|
id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
|
||||||
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
|
id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
|
||||||
@ -241,8 +263,7 @@ def needs_quotes(s):
|
|||||||
|
|
||||||
for test_re in [
|
for test_re in [
|
||||||
id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
|
id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
|
||||||
id_re_html, id_re_alpha_nums_with_ports
|
id_re_html, id_re_alpha_nums_with_ports]:
|
||||||
]:
|
|
||||||
if test_re.match(s):
|
if test_re.match(s):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -372,8 +393,8 @@ def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False):
|
|||||||
Edge(
|
Edge(
|
||||||
node_prefix + node_orig,
|
node_prefix + node_orig,
|
||||||
node_prefix + node_dest
|
node_prefix + node_dest
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
node_dest += 1
|
node_dest += 1
|
||||||
node_orig += 1
|
node_orig += 1
|
||||||
|
|
||||||
@ -410,8 +431,8 @@ def graph_from_incidence_matrix(matrix, node_prefix='', directed=False):
|
|||||||
Edge(
|
Edge(
|
||||||
node_prefix + abs(nodes[0]),
|
node_prefix + abs(nodes[0]),
|
||||||
node_prefix + nodes[1]
|
node_prefix + nodes[1]
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not directed:
|
if not directed:
|
||||||
graph.set_simplify(True)
|
graph.set_simplify(True)
|
||||||
@ -431,7 +452,14 @@ def __find_executables(path):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
success = False
|
success = False
|
||||||
progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': '', 'sfdp': ''}
|
progs = {
|
||||||
|
'dot': '',
|
||||||
|
'twopi': '',
|
||||||
|
'neato': '',
|
||||||
|
'circo': '',
|
||||||
|
'fdp': '',
|
||||||
|
'sfdp': ''
|
||||||
|
}
|
||||||
|
|
||||||
was_quoted = False
|
was_quoted = False
|
||||||
path = path.strip()
|
path = path.strip()
|
||||||
@ -515,7 +543,9 @@ def find_graphviz():
|
|||||||
|
|
||||||
def RegOpenKeyEx(key, subkey, opt, sam):
|
def RegOpenKeyEx(key, subkey, opt, sam):
|
||||||
result = ctypes.c_uint(0)
|
result = ctypes.c_uint(0)
|
||||||
ctypes.windll.advapi32.RegOpenKeyExA(key, subkey, opt, sam, ctypes.byref(result))
|
ctypes.windll.advapi32.RegOpenKeyExA(
|
||||||
|
key, subkey, opt, sam, ctypes.byref(result)
|
||||||
|
)
|
||||||
return result.value
|
return result.value
|
||||||
|
|
||||||
def RegQueryValueEx(hkey, valuename):
|
def RegQueryValueEx(hkey, valuename):
|
||||||
@ -527,7 +557,7 @@ def find_graphviz():
|
|||||||
ctypes.windll.advapi32.RegQueryValueExA(
|
ctypes.windll.advapi32.RegQueryValueExA(
|
||||||
hkey, valuename, 0, ctypes.byref(data_type),
|
hkey, valuename, 0, ctypes.byref(data_type),
|
||||||
data, ctypes.byref(data_len)
|
data, ctypes.byref(data_len)
|
||||||
)
|
)
|
||||||
|
|
||||||
return data.value
|
return data.value
|
||||||
|
|
||||||
@ -543,21 +573,22 @@ def find_graphviz():
|
|||||||
potentialKeys = [
|
potentialKeys = [
|
||||||
"SOFTWARE\\ATT\\Graphviz",
|
"SOFTWARE\\ATT\\Graphviz",
|
||||||
"SOFTWARE\\AT&T Research Labs\\Graphviz"
|
"SOFTWARE\\AT&T Research Labs\\Graphviz"
|
||||||
]
|
]
|
||||||
for potentialKey in potentialKeys:
|
for potentialKey in potentialKeys:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
hkey = RegOpenKeyEx(
|
hkey = RegOpenKeyEx(
|
||||||
HKEY_LOCAL_MACHINE,
|
HKEY_LOCAL_MACHINE,
|
||||||
potentialKey, 0, KEY_QUERY_VALUE
|
potentialKey, 0, KEY_QUERY_VALUE
|
||||||
)
|
)
|
||||||
|
|
||||||
if hkey is not None:
|
if hkey is not None:
|
||||||
path = RegQueryValueEx(hkey, "InstallPath")
|
path = RegQueryValueEx(hkey, "InstallPath")
|
||||||
RegCloseKey(hkey)
|
RegCloseKey(hkey)
|
||||||
|
|
||||||
# The regitry variable might exist, left by old installations
|
# The regitry variable might exist, left by old
|
||||||
# but with no value, in those cases we keep searching...
|
# installations but with no value, in those cases we
|
||||||
|
# keep searching...
|
||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -590,7 +621,9 @@ def find_graphviz():
|
|||||||
if 'PROGRAMFILES' in os.environ:
|
if 'PROGRAMFILES' in os.environ:
|
||||||
# Note, we could also use the win32api to get this
|
# Note, we could also use the win32api to get this
|
||||||
# information, but win32api may not be installed.
|
# information, but win32api may not be installed.
|
||||||
path = os.path.join(os.environ['PROGRAMFILES'], 'ATT', 'GraphViz', 'bin')
|
path = os.path.join(
|
||||||
|
os.environ['PROGRAMFILES'], 'ATT', 'GraphViz', 'bin'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
#Just in case, try the default...
|
#Just in case, try the default...
|
||||||
path = r"C:\Program Files\att\Graphviz\bin"
|
path = r"C:\Program Files\att\Graphviz\bin"
|
||||||
@ -606,8 +639,7 @@ def find_graphviz():
|
|||||||
'/usr/bin', '/usr/local/bin',
|
'/usr/bin', '/usr/local/bin',
|
||||||
'/opt/local/bin',
|
'/opt/local/bin',
|
||||||
'/opt/bin', '/sw/bin', '/usr/share',
|
'/opt/bin', '/sw/bin', '/usr/share',
|
||||||
'/Applications/Graphviz.app/Contents/MacOS/'
|
'/Applications/Graphviz.app/Contents/MacOS/'):
|
||||||
):
|
|
||||||
|
|
||||||
progs = __find_executables(path)
|
progs = __find_executables(path)
|
||||||
if progs is not None:
|
if progs is not None:
|
||||||
@ -732,11 +764,14 @@ class Common(object):
|
|||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'set_' + attr,
|
'set_' + attr,
|
||||||
lambda x, a=attr: self.obj_dict['attributes'].__setitem__(a, x)
|
lambda x, a=attr: self.obj_dict['attributes'].__setitem__(a, x)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate all the Getter methods.
|
# Generate all the Getter methods.
|
||||||
#
|
#
|
||||||
self.__setattr__('get_' + attr, lambda a=attr: self.__get_attribute__(a))
|
self.__setattr__(
|
||||||
|
'get_' + attr,
|
||||||
|
lambda a=attr: self.__get_attribute__(a)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
@ -750,7 +785,9 @@ class Error(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class InvocationException(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):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
@ -776,7 +813,8 @@ class Node(Common):
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Nodes will take attributes of all other types because the defaults
|
# Nodes will take attributes of all other types because the defaults
|
||||||
# for any GraphViz object are dealt with as if they were Node definitions
|
# for any GraphViz object are dealt with as if they were Node
|
||||||
|
# definitions
|
||||||
#
|
#
|
||||||
|
|
||||||
if obj_dict is not None:
|
if obj_dict is not None:
|
||||||
@ -844,7 +882,9 @@ class Node(Common):
|
|||||||
|
|
||||||
node_attr = list()
|
node_attr = list()
|
||||||
|
|
||||||
for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
|
for attr, value in sorted(
|
||||||
|
self.obj_dict['attributes'].items(),
|
||||||
|
key=itemgetter(0)):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
node_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
node_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -1026,7 +1066,9 @@ class Edge(Common):
|
|||||||
|
|
||||||
edge_attr = list()
|
edge_attr = list()
|
||||||
|
|
||||||
for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
|
for attr, value in sorted(
|
||||||
|
self.obj_dict['attributes'].items(),
|
||||||
|
key=itemgetter(0)):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
edge_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
edge_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -1077,8 +1119,9 @@ class Graph(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, graph_name='G', obj_dict=None, graph_type='digraph', strict=False,
|
self, graph_name='G', obj_dict=None, graph_type='digraph',
|
||||||
suppress_disconnected=False, simplify=False, **attrs):
|
strict=False, suppress_disconnected=False, simplify=False,
|
||||||
|
**attrs):
|
||||||
|
|
||||||
if obj_dict is not None:
|
if obj_dict is not None:
|
||||||
self.obj_dict = obj_dict
|
self.obj_dict = obj_dict
|
||||||
@ -1091,7 +1134,7 @@ class Graph(Common):
|
|||||||
raise Error((
|
raise Error((
|
||||||
'Invalid type "%s". Accepted graph types are: '
|
'Invalid type "%s". Accepted graph types are: '
|
||||||
'graph, digraph, subgraph' % graph_type
|
'graph, digraph, subgraph' % graph_type
|
||||||
))
|
))
|
||||||
|
|
||||||
self.obj_dict['name'] = quote_if_necessary(graph_name)
|
self.obj_dict['name'] = quote_if_necessary(graph_name)
|
||||||
self.obj_dict['type'] = graph_type
|
self.obj_dict['type'] = graph_type
|
||||||
@ -1241,17 +1284,24 @@ class Graph(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(graph_node, Node):
|
if not isinstance(graph_node, Node):
|
||||||
raise TypeError('add_node() received a non node class object: ' + str(graph_node))
|
raise TypeError(
|
||||||
|
'add_node() received a non node '
|
||||||
|
'class object: {}'.format(str(graph_node))
|
||||||
|
)
|
||||||
|
|
||||||
node = self.get_node(graph_node.get_name())
|
node = self.get_node(graph_node.get_name())
|
||||||
|
|
||||||
if not node:
|
if not node:
|
||||||
self.obj_dict['nodes'][graph_node.get_name()] = [graph_node.obj_dict]
|
self.obj_dict['nodes'][graph_node.get_name()] = [
|
||||||
|
graph_node.obj_dict
|
||||||
|
]
|
||||||
|
|
||||||
#self.node_dict[graph_node.get_name()] = graph_node.attributes
|
#self.node_dict[graph_node.get_name()] = graph_node.attributes
|
||||||
graph_node.set_parent_graph(self.get_parent_graph())
|
graph_node.set_parent_graph(self.get_parent_graph())
|
||||||
else:
|
else:
|
||||||
self.obj_dict['nodes'][graph_node.get_name()].append(graph_node.obj_dict)
|
self.obj_dict['nodes'][graph_node.get_name()].append(
|
||||||
|
graph_node.obj_dict
|
||||||
|
)
|
||||||
|
|
||||||
graph_node.set_sequence(self.get_next_sequence_number())
|
graph_node.set_sequence(self.get_next_sequence_number())
|
||||||
|
|
||||||
@ -1304,7 +1354,7 @@ class Graph(Common):
|
|||||||
Node(obj_dict=obj_dict)
|
Node(obj_dict=obj_dict)
|
||||||
for obj_dict
|
for obj_dict
|
||||||
in self.obj_dict['nodes'][name]
|
in self.obj_dict['nodes'][name]
|
||||||
])
|
])
|
||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
@ -1327,7 +1377,7 @@ class Graph(Common):
|
|||||||
Node(obj_dict=obj_d)
|
Node(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list
|
||||||
])
|
])
|
||||||
|
|
||||||
return node_objs
|
return node_objs
|
||||||
|
|
||||||
@ -1339,7 +1389,10 @@ class Graph(Common):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(graph_edge, Edge):
|
if not isinstance(graph_edge, Edge):
|
||||||
raise TypeError('add_edge() received a non edge class object: ' + str(graph_edge))
|
raise TypeError(
|
||||||
|
'add_edge() received a non '
|
||||||
|
'edge class object: {}'.format(str(graph_edge))
|
||||||
|
)
|
||||||
|
|
||||||
edge_points = (graph_edge.get_source(), graph_edge.get_destination())
|
edge_points = (graph_edge.get_source(), graph_edge.get_destination())
|
||||||
|
|
||||||
@ -1384,7 +1437,8 @@ class Graph(Common):
|
|||||||
dst = dst.get_name()
|
dst = dst.get_name()
|
||||||
|
|
||||||
if (src, dst) in self.obj_dict['edges']:
|
if (src, dst) in self.obj_dict['edges']:
|
||||||
if index is not None and index < len(self.obj_dict['edges'][(src, dst)]):
|
if index is not None and index < len(
|
||||||
|
self.obj_dict['edges'][(src, dst)]):
|
||||||
del self.obj_dict['edges'][(src, dst)][index]
|
del self.obj_dict['edges'][(src, dst)][index]
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -1415,17 +1469,18 @@ class Graph(Common):
|
|||||||
|
|
||||||
if edge_points in self.obj_dict['edges'] or (
|
if edge_points in self.obj_dict['edges'] or (
|
||||||
self.get_top_graph_type() == 'graph' and
|
self.get_top_graph_type() == 'graph' and
|
||||||
edge_points_reverse in self.obj_dict['edges']
|
edge_points_reverse in self.obj_dict['edges']):
|
||||||
):
|
|
||||||
|
|
||||||
edges_obj_dict = self.obj_dict['edges'].get(
|
edges_obj_dict = self.obj_dict['edges'].get(
|
||||||
edge_points,
|
edge_points,
|
||||||
self.obj_dict['edges'].get(edge_points_reverse, None))
|
self.obj_dict['edges'].get(edge_points_reverse, None))
|
||||||
|
|
||||||
for edge_obj_dict in edges_obj_dict:
|
for edge_obj_dict in edges_obj_dict:
|
||||||
match.append(
|
match.append(Edge(
|
||||||
Edge(edge_points[0], edge_points[1], obj_dict=edge_obj_dict)
|
edge_points[0],
|
||||||
)
|
edge_points[1],
|
||||||
|
obj_dict=edge_obj_dict
|
||||||
|
))
|
||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
@ -1446,7 +1501,7 @@ class Graph(Common):
|
|||||||
Edge(obj_dict=obj_d)
|
Edge(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list
|
||||||
])
|
])
|
||||||
|
|
||||||
return edge_objs
|
return edge_objs
|
||||||
|
|
||||||
@ -1457,8 +1512,12 @@ class Graph(Common):
|
|||||||
None.
|
None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster):
|
if not isinstance(sgraph, Subgraph) and \
|
||||||
raise TypeError('add_subgraph() received a non subgraph class object:' + str(sgraph))
|
not isinstance(sgraph, Cluster):
|
||||||
|
raise TypeError(
|
||||||
|
'add_subgraph() received a non '
|
||||||
|
'subgraph class object:'.format(str(sgraph))
|
||||||
|
)
|
||||||
|
|
||||||
if sgraph.get_name() in self.obj_dict['subgraphs']:
|
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)
|
sgraphs_obj_dict = self.obj_dict['subgraphs'].get(name)
|
||||||
|
|
||||||
for obj_dict_list in sgraphs_obj_dict:
|
for obj_dict_list in sgraphs_obj_dict:
|
||||||
#match.extend(Subgraph(obj_dict = obj_d) for obj_d in obj_dict_list)
|
|
||||||
match.append(Subgraph(obj_dict=obj_dict_list))
|
match.append(Subgraph(obj_dict=obj_dict_list))
|
||||||
|
|
||||||
return match
|
return match
|
||||||
@ -1511,7 +1569,7 @@ class Graph(Common):
|
|||||||
Subgraph(obj_dict=obj_d)
|
Subgraph(obj_dict=obj_d)
|
||||||
for obj_d
|
for obj_d
|
||||||
in obj_dict_list
|
in obj_dict_list
|
||||||
])
|
])
|
||||||
|
|
||||||
return sgraph_objs
|
return sgraph_objs
|
||||||
|
|
||||||
@ -1544,14 +1602,22 @@ class Graph(Common):
|
|||||||
graph.append('strict ')
|
graph.append('strict ')
|
||||||
|
|
||||||
if self.obj_dict['name'] == '':
|
if self.obj_dict['name'] == '':
|
||||||
if 'show_keyword' in self.obj_dict and self.obj_dict['show_keyword']:
|
if 'show_keyword' in self.obj_dict and \
|
||||||
|
self.obj_dict['show_keyword']:
|
||||||
graph.append('subgraph {\n')
|
graph.append('subgraph {\n')
|
||||||
else:
|
else:
|
||||||
graph.append('{\n')
|
graph.append('{\n')
|
||||||
else:
|
else:
|
||||||
graph.append('%s %s {\n' % (self.obj_dict['type'], self.obj_dict['name']))
|
graph.append(
|
||||||
|
'{} {} {{\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:
|
if value is not None:
|
||||||
graph.append('%s=%s' % (attr, quote_if_necessary(value)))
|
graph.append('%s=%s' % (attr, quote_if_necessary(value)))
|
||||||
else:
|
else:
|
||||||
@ -1566,7 +1632,9 @@ class Graph(Common):
|
|||||||
edge_obj_dicts.extend(e)
|
edge_obj_dicts.extend(e)
|
||||||
|
|
||||||
if edge_obj_dicts:
|
if edge_obj_dicts:
|
||||||
edge_src_set, edge_dst_set = list(zip(*[obj['points'] for obj in edge_obj_dicts]))
|
edge_src_set, edge_dst_set = list(
|
||||||
|
zip(*[obj['points'] for obj in edge_obj_dicts])
|
||||||
|
)
|
||||||
edge_src_set, edge_dst_set = set(edge_src_set), set(edge_dst_set)
|
edge_src_set, edge_dst_set = set(edge_src_set), set(edge_dst_set)
|
||||||
else:
|
else:
|
||||||
edge_src_set, edge_dst_set = set(), set()
|
edge_src_set, edge_dst_set = set(), set()
|
||||||
@ -1583,7 +1651,7 @@ class Graph(Common):
|
|||||||
(obj['sequence'], obj)
|
(obj['sequence'], obj)
|
||||||
for obj
|
for obj
|
||||||
in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts)
|
in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts)
|
||||||
])
|
])
|
||||||
|
|
||||||
for idx, obj in obj_list:
|
for idx, obj in obj_list:
|
||||||
if obj['type'] == 'node':
|
if obj['type'] == 'node':
|
||||||
@ -1620,7 +1688,9 @@ class Subgraph(Graph):
|
|||||||
This class implements the methods to work on a representation
|
This class implements the methods to work on a representation
|
||||||
of a subgraph in Graphviz's dot language.
|
of a subgraph in Graphviz's dot language.
|
||||||
|
|
||||||
subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
|
subgraph(
|
||||||
|
graph_name='subG', suppress_disconnected=False, attribute=value, ...
|
||||||
|
)
|
||||||
|
|
||||||
graph_name:
|
graph_name:
|
||||||
the subgraph's name
|
the subgraph's name
|
||||||
@ -1651,7 +1721,8 @@ class Subgraph(Graph):
|
|||||||
|
|
||||||
Graph.__init__(
|
Graph.__init__(
|
||||||
self, graph_name=graph_name, obj_dict=obj_dict,
|
self, graph_name=graph_name, obj_dict=obj_dict,
|
||||||
suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs)
|
suppress_disconnected=suppress_disconnected,
|
||||||
|
simplify=simplify, **attrs)
|
||||||
|
|
||||||
if obj_dict is None:
|
if obj_dict is None:
|
||||||
self.obj_dict['type'] = 'subgraph'
|
self.obj_dict['type'] = 'subgraph'
|
||||||
@ -1664,7 +1735,9 @@ class Cluster(Graph):
|
|||||||
This class implements the methods to work on a representation
|
This class implements the methods to work on a representation
|
||||||
of a cluster in Graphviz's dot language.
|
of a cluster in Graphviz's dot language.
|
||||||
|
|
||||||
cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
|
cluster(
|
||||||
|
graph_name='subG', suppress_disconnected=False, attribute=value, ...
|
||||||
|
)
|
||||||
|
|
||||||
graph_name:
|
graph_name:
|
||||||
the cluster's name (the string 'cluster' will be always prepended)
|
the cluster's name (the string 'cluster' will be always prepended)
|
||||||
@ -1687,13 +1760,15 @@ class Cluster(Graph):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
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):
|
simplify=False, **attrs):
|
||||||
|
|
||||||
Graph.__init__(
|
Graph.__init__(
|
||||||
self, graph_name=graph_name, obj_dict=obj_dict,
|
self, graph_name=graph_name, obj_dict=obj_dict,
|
||||||
suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs
|
suppress_disconnected=suppress_disconnected, simplify=simplify,
|
||||||
)
|
**attrs
|
||||||
|
)
|
||||||
|
|
||||||
if obj_dict is None:
|
if obj_dict is None:
|
||||||
self.obj_dict['type'] = 'subgraph'
|
self.obj_dict['type'] = 'subgraph'
|
||||||
@ -1721,7 +1796,7 @@ class Dot(Graph):
|
|||||||
'jpe', 'jpeg', 'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain',
|
'jpe', 'jpeg', 'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain',
|
||||||
'plain-ext', 'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz',
|
'plain-ext', 'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz',
|
||||||
'vrml', 'vtx', 'wbmp', 'xdot', 'xlib'
|
'vrml', 'vtx', 'wbmp', 'xdot', 'xlib'
|
||||||
]
|
]
|
||||||
self.prog = 'dot'
|
self.prog = 'dot'
|
||||||
|
|
||||||
# Automatically creates all the methods enabling the creation
|
# Automatically creates all the methods enabling the creation
|
||||||
@ -1730,24 +1805,26 @@ class Dot(Graph):
|
|||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'create_' + frmt,
|
'create_' + frmt,
|
||||||
lambda f=frmt, prog=self.prog: self.create(format=f, prog=prog)
|
lambda f=frmt, prog=self.prog: self.create(format=f, prog=prog)
|
||||||
)
|
)
|
||||||
f = self.__dict__['create_' + frmt]
|
f = self.__dict__['create_' + frmt]
|
||||||
f.__doc__ = (
|
f.__doc__ = (
|
||||||
'''Refer to the docstring accompanying the'''
|
'''Refer to the docstring accompanying the'''
|
||||||
''''create' method for more information.'''
|
''''create' method for more information.'''
|
||||||
)
|
)
|
||||||
|
|
||||||
for frmt in self.formats + ['raw']:
|
for frmt in self.formats + ['raw']:
|
||||||
self.__setattr__(
|
self.__setattr__(
|
||||||
'write_' + frmt,
|
'write_' + frmt,
|
||||||
lambda path, f=frmt, prog=self.prog: self.write(path, format=f, prog=prog)
|
lambda path,
|
||||||
)
|
f=frmt,
|
||||||
|
prog=self.prog: self.write(path, format=f, prog=prog)
|
||||||
|
)
|
||||||
|
|
||||||
f = self.__dict__['write_' + frmt]
|
f = self.__dict__['write_' + frmt]
|
||||||
f.__doc__ = (
|
f.__doc__ = (
|
||||||
'''Refer to the docstring accompanying the'''
|
'''Refer to the docstring accompanying the'''
|
||||||
''''write' method for more information.'''
|
''''write' method for more information.'''
|
||||||
)
|
)
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return copy.copy(self.obj_dict)
|
return copy.copy(self.obj_dict)
|
||||||
@ -1759,13 +1836,13 @@ class Dot(Graph):
|
|||||||
"""Add the paths of the required image files.
|
"""Add the paths of the required image files.
|
||||||
|
|
||||||
If the graph needs graphic objects to be used as shapes or otherwise
|
If the graph needs graphic objects to be used as shapes or otherwise
|
||||||
those need to be in the same folder as the graph is going to be rendered
|
those need to be in the same folder as the graph is going to be
|
||||||
from. Alternatively the absolute path to the files can be specified when
|
rendered from. Alternatively the absolute path to the files can be
|
||||||
including the graphics in the graph.
|
specified when including the graphics in the graph.
|
||||||
|
|
||||||
The files in the location pointed to by the path(s) specified as arguments
|
The files in the location pointed to by the path(s) specified as
|
||||||
to this method will be copied to the same temporary location where the
|
arguments to this method will be copied to the same temporary location
|
||||||
graph is going to be rendered.
|
where the graph is going to be rendered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(file_paths, basestring):
|
if isinstance(file_paths, basestring):
|
||||||
@ -1783,14 +1860,17 @@ class Dot(Graph):
|
|||||||
self.prog = prog
|
self.prog = prog
|
||||||
|
|
||||||
def set_graphviz_executables(self, paths):
|
def set_graphviz_executables(self, paths):
|
||||||
"""This method allows to manually specify the location of the GraphViz executables.
|
"""
|
||||||
|
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': ''}
|
{'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''}
|
||||||
|
|
||||||
and the values are the paths to the corresponding executable, including the name
|
and the values are the paths to the corresponding executable,
|
||||||
of the executable itself.
|
including the name of the executable itself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.progs = paths
|
self.progs = paths
|
||||||
@ -1893,9 +1973,12 @@ class Dot(Graph):
|
|||||||
raise InvocationException(
|
raise InvocationException(
|
||||||
'GraphViz\'s executable "%s" not found' % prog)
|
'GraphViz\'s executable "%s" not found' % prog)
|
||||||
|
|
||||||
if not os.path.exists(self.progs[prog]) or not os.path.isfile(self.progs[prog]):
|
if not os.path.exists(self.progs[prog]) or \
|
||||||
|
not os.path.isfile(self.progs[prog]):
|
||||||
raise InvocationException(
|
raise InvocationException(
|
||||||
'GraphViz\'s executable "%s" is not a file or doesn\'t exist' % self.progs[prog])
|
'GraphViz\'s executable "{}" is not'
|
||||||
|
' a file or doesn\'t exist'.format(self.progs[prog])
|
||||||
|
)
|
||||||
|
|
||||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||||
os.close(tmp_fd)
|
os.close(tmp_fd)
|
||||||
@ -1910,7 +1993,8 @@ class Dot(Graph):
|
|||||||
f_data = f.read()
|
f_data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# And copy it under a file with the same name in the temporary directory
|
# And copy it under a file with the same name in the temporary
|
||||||
|
# directory
|
||||||
f = open(os.path.join(tmp_dir, os.path.basename(img)), 'wb')
|
f = open(os.path.join(tmp_dir, os.path.basename(img)), 'wb')
|
||||||
f.write(f_data)
|
f.write(f_data)
|
||||||
f.close()
|
f.close()
|
@ -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
|
The dotparser parses graphviz files in dot and dot files and transforms them
|
||||||
into a class representation defined by pydot.
|
into a class representation defined by pydot.
|
||||||
|
|
||||||
The module needs pyparsing (tested with version 1.2.2) and pydot
|
|
||||||
|
|
||||||
Author: Michael Krause <michael@krause-software.de>
|
|
||||||
Fixes by: Ero Carrera <ero@dkbza.org>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import division, print_function
|
from __future__ import division, print_function
|
||||||
|
|
||||||
__author__ = ['Michael Krause', 'Ero Carrera']
|
|
||||||
__license__ = 'MIT'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pydot
|
import pydot
|
||||||
import codecs
|
import codecs
|
||||||
@ -25,7 +43,7 @@ from pyparsing import (
|
|||||||
Forward, Group, Optional, Combine, nums, restOfLine,
|
Forward, Group, Optional, Combine, nums, restOfLine,
|
||||||
cStyleComment, alphanums, printables, ParseException,
|
cStyleComment, alphanums, printables, ParseException,
|
||||||
ParseResults, CharsNotIn, QuotedString
|
ParseResults, CharsNotIn, QuotedString
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
PY3 = not sys.version_info < (3, 0, 0)
|
PY3 = not sys.version_info < (3, 0, 0)
|
||||||
@ -65,7 +83,7 @@ class DefaultStatement(P_AttrList):
|
|||||||
return "%s(%s, %r)" % (
|
return "%s(%s, %r)" % (
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
self.default_type, self.attrs
|
self.default_type, self.attrs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
top_graphs = list()
|
top_graphs = list()
|
||||||
@ -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 key, objs in item_dict[key_name].items():
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if 'parent_graph' in obj and obj['parent_graph'].get_parent_graph() == g:
|
if 'parent_graph' in obj and \
|
||||||
|
obj['parent_graph'].get_parent_graph() == g:
|
||||||
if obj['parent_graph'] is g:
|
if obj['parent_graph'] is g:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -142,13 +161,18 @@ def update_parent_graph_hierarchy(g, parent_graph=None, level=0):
|
|||||||
|
|
||||||
if key_name == 'edges' and len(key) == 2:
|
if key_name == 'edges' and len(key) == 2:
|
||||||
for idx, vertex in enumerate(obj['points']):
|
for idx, vertex in enumerate(obj['points']):
|
||||||
if isinstance(vertex, (pydot.Graph, pydot.Subgraph, pydot.Cluster)):
|
if isinstance(
|
||||||
|
vertex,
|
||||||
|
(pydot.Graph, pydot.Subgraph, pydot.Cluster)
|
||||||
|
):
|
||||||
vertex.set_parent_graph(parent_graph)
|
vertex.set_parent_graph(parent_graph)
|
||||||
if isinstance(vertex, pydot.frozendict):
|
if isinstance(vertex, pydot.frozendict):
|
||||||
if vertex['parent_graph'] is g:
|
if vertex['parent_graph'] is g:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
vertex['parent_graph'].set_parent_graph(parent_graph)
|
vertex['parent_graph'].set_parent_graph(
|
||||||
|
parent_graph
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_defaults(element, defaults):
|
def add_defaults(element, defaults):
|
||||||
@ -158,7 +182,10 @@ def add_defaults(element, defaults):
|
|||||||
d[key] = value
|
d[key] = value
|
||||||
|
|
||||||
|
|
||||||
def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None):
|
def add_elements(g, toks,
|
||||||
|
defaults_graph=None,
|
||||||
|
defaults_node=None,
|
||||||
|
defaults_edge=None):
|
||||||
if defaults_graph is None:
|
if defaults_graph is None:
|
||||||
defaults_graph = {}
|
defaults_graph = {}
|
||||||
if defaults_node is None:
|
if defaults_node is None:
|
||||||
@ -181,7 +208,9 @@ def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge
|
|||||||
|
|
||||||
elif isinstance(element, ParseResults):
|
elif isinstance(element, ParseResults):
|
||||||
for e in element:
|
for e in element:
|
||||||
add_elements(g, [e], defaults_graph, defaults_node, defaults_edge)
|
add_elements(
|
||||||
|
g, [e], defaults_graph, defaults_node, defaults_edge
|
||||||
|
)
|
||||||
|
|
||||||
elif isinstance(element, DefaultStatement):
|
elif isinstance(element, DefaultStatement):
|
||||||
if element.default_type == 'graph':
|
if element.default_type == 'graph':
|
||||||
@ -198,7 +227,9 @@ def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge
|
|||||||
defaults_edge.update(element.attrs)
|
defaults_edge.update(element.attrs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown DefaultStatement: %s " % element.default_type)
|
raise ValueError(
|
||||||
|
"Unknown DefaultStatement: %s " % element.default_type
|
||||||
|
)
|
||||||
|
|
||||||
elif isinstance(element, P_AttrList):
|
elif isinstance(element, P_AttrList):
|
||||||
g.obj_dict['attributes'].update(element.attrs)
|
g.obj_dict['attributes'].update(element.attrs)
|
||||||
@ -290,7 +321,9 @@ def push_edge_stmt(str, loc, toks):
|
|||||||
e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
|
e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
|
||||||
|
|
||||||
elif isinstance(toks[2][0], pydot.Graph):
|
elif isinstance(toks[2][0], pydot.Graph):
|
||||||
e.append(pydot.Edge(n_prev, pydot.frozendict(toks[2][0].obj_dict), **attrs))
|
e.append(
|
||||||
|
pydot.Edge(n_prev, pydot.frozendict(toks[2][0].obj_dict), **attrs)
|
||||||
|
)
|
||||||
|
|
||||||
elif isinstance(toks[2][0], pydot.Node):
|
elif isinstance(toks[2][0], pydot.Node):
|
||||||
node = toks[2][0]
|
node = toks[2][0]
|
||||||
@ -304,7 +337,8 @@ def push_edge_stmt(str, loc, toks):
|
|||||||
|
|
||||||
elif isinstance(toks[2][0], type('')):
|
elif isinstance(toks[2][0], type('')):
|
||||||
for n_next in [n for n in tuple(toks)[2::2]]:
|
for n_next in [n for n in tuple(toks)[2::2]]:
|
||||||
if isinstance(n_next, P_AttrList) or not isinstance(n_next[0], type('')):
|
if isinstance(n_next, P_AttrList) or \
|
||||||
|
not isinstance(n_next[0], type('')):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
n_next_port = do_node_ports(n_next)
|
n_next_port = do_node_ports(n_next)
|
||||||
@ -372,7 +406,9 @@ def graph_definition():
|
|||||||
identifier = Word(alphanums + "_.").setName("identifier")
|
identifier = Word(alphanums + "_.").setName("identifier")
|
||||||
|
|
||||||
# dblQuotedString
|
# 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 != ","])
|
noncomma_ = "".join([c for c in printables if c != ","])
|
||||||
alphastring_ = OneOrMore(CharsNotIn(noncomma_ + ' '))
|
alphastring_ = OneOrMore(CharsNotIn(noncomma_ + ' '))
|
||||||
@ -385,18 +421,18 @@ def graph_definition():
|
|||||||
html_text = nestedExpr(
|
html_text = nestedExpr(
|
||||||
opener, closer,
|
opener, closer,
|
||||||
(CharsNotIn(opener + closer))
|
(CharsNotIn(opener + closer))
|
||||||
).setParseAction(parse_html).leaveWhitespace()
|
).setParseAction(parse_html).leaveWhitespace()
|
||||||
|
|
||||||
ID = (
|
ID = (
|
||||||
identifier | html_text |
|
identifier | html_text |
|
||||||
double_quoted_string | # .setParseAction(strip_quotes) |
|
double_quoted_string | # .setParseAction(strip_quotes) |
|
||||||
alphastring_
|
alphastring_
|
||||||
).setName("ID")
|
).setName("ID")
|
||||||
|
|
||||||
float_number = Combine(
|
float_number = Combine(
|
||||||
Optional(minus) +
|
Optional(minus) +
|
||||||
OneOrMore(Word(nums + "."))
|
OneOrMore(Word(nums + "."))
|
||||||
).setName("float_number")
|
).setName("float_number")
|
||||||
|
|
||||||
righthand_id = (float_number | ID).setName("righthand_id")
|
righthand_id = (float_number | ID).setName("righthand_id")
|
||||||
|
|
||||||
@ -405,23 +441,25 @@ def graph_definition():
|
|||||||
port_location = (
|
port_location = (
|
||||||
OneOrMore(Group(colon + ID)) |
|
OneOrMore(Group(colon + ID)) |
|
||||||
Group(colon + lparen + ID + comma + ID + rparen)
|
Group(colon + lparen + ID + comma + ID + rparen)
|
||||||
).setName("port_location")
|
).setName("port_location")
|
||||||
|
|
||||||
port = (
|
port = (
|
||||||
Group(port_location + Optional(port_angle)) |
|
Group(port_location + Optional(port_angle)) |
|
||||||
Group(port_angle + Optional(port_location))
|
Group(port_angle + Optional(port_location))
|
||||||
).setName("port")
|
).setName("port")
|
||||||
|
|
||||||
node_id = (ID + Optional(port))
|
node_id = (ID + Optional(port))
|
||||||
a_list = OneOrMore(
|
a_list = OneOrMore(
|
||||||
ID + Optional(equals + righthand_id) + Optional(comma.suppress())
|
ID + Optional(equals + righthand_id) + Optional(comma.suppress())
|
||||||
).setName("a_list")
|
).setName("a_list")
|
||||||
|
|
||||||
attr_list = OneOrMore(
|
attr_list = OneOrMore(
|
||||||
lbrack.suppress() + Optional(a_list) + rbrack.suppress()
|
lbrack.suppress() + Optional(a_list) + rbrack.suppress()
|
||||||
).setName("attr_list")
|
).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")
|
edgeop = (Literal("--") | Literal("->")).setName("edgeop")
|
||||||
|
|
||||||
@ -429,32 +467,36 @@ def graph_definition():
|
|||||||
graph_stmt = Group(
|
graph_stmt = Group(
|
||||||
lbrace.suppress() + Optional(stmt_list) +
|
lbrace.suppress() + Optional(stmt_list) +
|
||||||
rbrace.suppress() + Optional(semi.suppress())
|
rbrace.suppress() + Optional(semi.suppress())
|
||||||
).setName("graph_stmt")
|
).setName("graph_stmt")
|
||||||
|
|
||||||
edge_point = Forward()
|
edge_point = Forward()
|
||||||
|
|
||||||
edgeRHS = OneOrMore(edgeop + edge_point)
|
edgeRHS = OneOrMore(edgeop + edge_point)
|
||||||
edge_stmt = edge_point + edgeRHS + Optional(attr_list)
|
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_stmt = (
|
||||||
node_id + Optional(attr_list) + Optional(semi.suppress())
|
node_id + Optional(attr_list) + Optional(semi.suppress())
|
||||||
).setName("node_stmt")
|
).setName("node_stmt")
|
||||||
|
|
||||||
assignment = (ID + equals + righthand_id).setName("assignment")
|
assignment = (ID + equals + righthand_id).setName("assignment")
|
||||||
stmt = (
|
stmt = (
|
||||||
assignment | edge_stmt | attr_stmt |
|
assignment | edge_stmt | attr_stmt |
|
||||||
subgraph | graph_stmt | node_stmt
|
subgraph | graph_stmt | node_stmt
|
||||||
).setName("stmt")
|
).setName("stmt")
|
||||||
stmt_list << OneOrMore(stmt + Optional(semi.suppress()))
|
stmt_list << OneOrMore(stmt + Optional(semi.suppress()))
|
||||||
|
|
||||||
graphparser = OneOrMore((
|
graphparser = OneOrMore((
|
||||||
Optional(strict_) + Group((graph_ | digraph_)) +
|
Optional(strict_) + Group((graph_ | digraph_)) +
|
||||||
Optional(ID) + graph_stmt
|
Optional(ID) + graph_stmt
|
||||||
).setResultsName("graph"))
|
).setResultsName("graph"))
|
||||||
|
|
||||||
singleLineComment = Group("//" + restOfLine) | Group("#" + restOfLine)
|
singleLineComment = Group("//" + restOfLine) | Group("#" + restOfLine)
|
||||||
|
|
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__']
|
78
setup.py
78
setup.py
@ -1,39 +1,69 @@
|
|||||||
#!/usr/bin/env python
|
#!/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 setuptools import setup, find_packages
|
||||||
from distutils.core import setup
|
|
||||||
except ImportError:
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
import pydot
|
|
||||||
import os
|
|
||||||
|
|
||||||
os.environ['COPY_EXTENDED_ATTRIBUTES_DISABLE'] = 'true'
|
def find_version(filename):
|
||||||
os.environ['COPYFILE_DISABLE'] = 'true'
|
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.')
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='pydot',
|
name='pydotplus',
|
||||||
version=pydot.__version__,
|
version=find_version('lib/pydotplus/version.py'),
|
||||||
description='Python interface to Graphviz\'s Dot',
|
package_dir={'' : 'lib'},
|
||||||
author='Ero Carrera',
|
packages=find_packages('lib'),
|
||||||
author_email='ero@dkbza.org',
|
|
||||||
url='http://code.google.com/p/pydot/',
|
# Metadata
|
||||||
download_url='http://pydot.googlecode.com/files/pydot-%s.tar.gz' % pydot.__version__,
|
author='PyDotPlus Developers',
|
||||||
license='MIT',
|
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',
|
keywords='graphviz dot graphs visualization',
|
||||||
platforms=['any'],
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Intended Audience :: Science/Research',
|
'Intended Audience :: Science/Research',
|
||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
'Natural Language :: English',
|
'Natural Language :: English',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
'Topic :: Scientific/Engineering :: Visualization',
|
'Topic :: Scientific/Engineering :: Visualization',
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
'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