Skip to content

Commit

Permalink
add more specific ExecutableNotFound exception
Browse files Browse the repository at this point in the history
  • Loading branch information
xflr6 committed May 11, 2017
1 parent 4663c43 commit b1edc12
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -7,6 +7,9 @@ Version 0.7.1 (in development)

Fix graphivz.view exception on unsupported platform.

Raise a dedicated RuntimeError subclass graphviz.ExecutableNotFound when the
Graphviz executables are not found.


Version 0.7
-----------
Expand Down
3 changes: 3 additions & 0 deletions docs/api.rst
Expand Up @@ -61,3 +61,6 @@ Other

.. autodata:: graphviz.FORMATS
:annotation:

.. autodata:: graphviz.ExecutableNotFound
:annotation:
5 changes: 4 additions & 1 deletion graphviz/__init__.py
Expand Up @@ -26,13 +26,14 @@

from .dot import Graph, Digraph
from .files import Source
from .backend import render, pipe, view, ENGINES, FORMATS
from .backend import render, pipe, view, ENGINES, FORMATS, ExecutableNotFound

__all__ = [
'Graph', 'Digraph',
'Source',
'render', 'pipe', 'view',
'ENGINES', 'FORMATS',
'ExecutableNotFound',
]

__title__ = 'graphviz'
Expand All @@ -46,3 +47,5 @@

#: Set of known output formats for rendering ('pdf', 'png', ...)
FORMATS = FORMATS

ExecutableNotFound = ExecutableNotFound
22 changes: 14 additions & 8 deletions graphviz/backend.py
Expand Up @@ -64,6 +64,16 @@
STARTUPINFO.wShowWindow = subprocess.SW_HIDE


class ExecutableNotFound(RuntimeError):
"""Exception raised if the Graphviz executable is not found."""

_msg = ('failed to execute %r, '
'make sure the Graphviz executables are on your systems\' PATH')

def __init__(self, args):
super(ExecutableNotFound, self).__init__(self._msg % args)


def command(engine, format, filepath=None):
"""Return args list for subprocess.Popen and name of the rendered file."""
if engine not in ENGINES:
Expand All @@ -90,7 +100,7 @@ def render(engine, format, filepath):
The (possibly relative) path of the rendered file.
Raises:
ValueError: If engine or format are not known.
RuntimeError: If the Graphviz executable is not found.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
"""
args, rendered = command(engine, format, filepath)
Expand All @@ -99,9 +109,7 @@ def render(engine, format, filepath):
subprocess.check_call(args, startupinfo=STARTUPINFO)
except OSError as e:
if e.errno == errno.ENOENT:
raise RuntimeError('failed to execute %r, '
'make sure the Graphviz executables '
'are on your systems\' path' % args)
raise ExecutableNotFound(args)
else: # pragma: no cover
raise

Expand All @@ -120,7 +128,7 @@ def pipe(engine, format, data, quiet=False):
Binary (encoded) stdout of the layout command.
Raises:
ValueError: If engine or format are not known.
RuntimeError: If the Graphviz executable is not found.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
"""
args, _ = command(engine, format)
Expand All @@ -131,9 +139,7 @@ def pipe(engine, format, data, quiet=False):
startupinfo=STARTUPINFO)
except OSError as e:
if e.errno == errno.ENOENT:
raise RuntimeError('failed to execute %r, '
'make sure the Graphviz executables '
'are on your systems\' path' % args)
raise ExecutableNotFound(args)
else: # pragma: no cover
raise

Expand Down
6 changes: 3 additions & 3 deletions graphviz/files.py
Expand Up @@ -92,7 +92,7 @@ def pipe(self, format=None):
Binary (encoded) stdout of the layout command.
Raises:
ValueError: If `format` is not known.
RuntimeError: If the Graphviz executable is not found.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
"""
if format is None:
Expand Down Expand Up @@ -145,7 +145,7 @@ def render(self, filename=None, directory=None, view=False, cleanup=False):
Returns:
The (possibly relative) path of the rendered file.
Raises:
RuntimeError: If the Graphviz executable is not found.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
RuntimeError: If viewer opening is requested but not supported.
"""
Expand All @@ -171,7 +171,7 @@ def view(self, filename=None, directory=None, cleanup=False):
Returns:
The (possibly relative) path of the rendered file.
Raises:
RuntimeError: If the Graphviz executable is not found.
graphviz.ExecutableNotFound: If the Graphviz executable is not found.
subprocess.CalledProcessError: If the exit status is non-zero.
RuntimeError: If opening the viewer is not supported.
Expand Down
15 changes: 10 additions & 5 deletions tests/test_backend.py
Expand Up @@ -4,7 +4,8 @@

import pytest

from graphviz.backend import render, pipe, view, STARTUPINFO

from graphviz.backend import render, pipe, view, ExecutableNotFound, STARTUPINFO


def test_render_engine_unknown():
Expand All @@ -20,7 +21,7 @@ def test_render_format_unknown():


def test_render_missingdot(empty_path):
with pytest.raises(RuntimeError) as e:
with pytest.raises(ExecutableNotFound) as e:
render('dot', 'pdf', '')
e.match(r'execute')

Expand All @@ -31,14 +32,18 @@ def test_render_missingfile():
assert e.value.returncode == 2


def test_render_mock(check_call):
def test_render_mocked(check_call):
assert render('dot', 'pdf', '') == '.pdf'
check_call.assert_called_once_with(['dot', '-Tpdf', '-O', ''],
startupinfo=STARTUPINFO)


def test_render(check_call):
pass # TODO


def test_pipe_missingdot(empty_path):
with pytest.raises(RuntimeError) as e:
with pytest.raises(ExecutableNotFound) as e:
pipe('dot', 'pdf', b'')
e.match(r'execute')

Expand All @@ -49,7 +54,7 @@ def test_pipe_invalid_data():
assert e.value.returncode == 1


def test_pipe_mock(Popen):
def test_pipe_mocked(Popen):
pass # TODO


Expand Down

0 comments on commit b1edc12

Please sign in to comment.