Skip to content

Commit 07251d8

Browse files
committed
ENH(plot): return SVG rendered in JUPYTER, ...
+ doc: rename in sample code: netop --> pipeline. + enh(build): add `ipython` in test dependencies. + include it in the plot TC.
1 parent 7d389c3 commit 07251d8

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed

graphkit/base.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,19 @@ def set_execution_method(self, method):
171171
assert method in options
172172
self._execution_method = method
173173

174-
def plot(self, filename=None, show=False,
174+
def plot(self, filename=None, show=False, jupyter=None,
175175
inputs=None, outputs=None, solution=None):
176176
"""
177177
:param str filename:
178178
Write diagram into a file.
179179
Common extensions are ``.png .dot .jpg .jpeg .pdf .svg``
180180
call :func:`network.supported_plot_formats()` for more.
181-
:param boolean show:
181+
:param show:
182182
If it evaluates to true, opens the diagram in a matplotlib window.
183+
If it equals `-1`, it plots but does not open the Window.
184+
:param jupyter:
185+
If it evaluates to true, return an SVG suitable to render
186+
in *jupyter notebook cells* (`ipython` must be installed).
183187
:param inputs:
184188
an optional name list, any nodes in there are plotted
185189
as a "house"
@@ -195,7 +199,7 @@ def plot(self, filename=None, show=False,
195199
196200
See :func:`network.plot_graph()` for the plot legend and example code.
197201
"""
198-
return self.net.plot(filename, show, inputs, outputs, solution)
202+
return self.net.plot(filename, show, jupyter, inputs, outputs, solution)
199203

200204
def __getstate__(self):
201205
state = Operation.__getstate__(self)

graphkit/network.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ def _compute_sequential_method(self, named_inputs, outputs):
376376
return {k: cache[k] for k in iter(cache) if k in outputs}
377377

378378

379-
def plot(self, filename=None, show=False,
379+
def plot(self, filename=None, show=False, jupyter=None,
380380
inputs=None, outputs=None, solution=None):
381381
"""
382382
Plot a *Graphviz* graph and return it, if no other argument provided.
@@ -385,8 +385,12 @@ def plot(self, filename=None, show=False,
385385
Write diagram into a file.
386386
Common extensions are ``.png .dot .jpg .jpeg .pdf .svg``
387387
call :func:`network.supported_plot_formats()` for more.
388-
:param boolean show:
388+
:param show:
389389
If it evaluates to true, opens the diagram in a matplotlib window.
390+
If it equals `-1``, it plots but does not open the Window.
391+
:param jupyter:
392+
If it evaluates to true, return an SVG suitable to render
393+
in *jupyter notebook cells* (`ipython` must be installed).
390394
:param inputs:
391395
an optional name list, any nodes in there are plotted
392396
as a "house"
@@ -402,8 +406,8 @@ def plot(self, filename=None, show=False,
402406
403407
See :func:`network.plot_graph` for the plot legend and example code.
404408
"""
405-
return plot_graph(self.graph, filename, show, self.steps,
406-
inputs, outputs, solution)
409+
return plot_graph(self.graph, filename, show, jupyter,
410+
self.steps, inputs, outputs, solution)
407411

408412

409413
def ready_to_schedule_operation(op, has_executed, graph):
@@ -461,8 +465,8 @@ def supported_plot_formats():
461465
return [".%s" % f for f in pydot.Dot().formats]
462466

463467

464-
def plot_graph(graph, filename=None, show=False, steps=None,
465-
inputs=None, outputs=None, solution=None):
468+
def plot_graph(graph, filename=None, show=False, jupyter=False,
469+
steps=None, inputs=None, outputs=None, solution=None):
466470
"""
467471
Plot a *Graphviz* graph/steps and return it, if no other argument provided.
468472
@@ -494,9 +498,12 @@ def plot_graph(graph, filename=None, show=False, steps=None,
494498
Write diagram into a file.
495499
Common extensions are ``.png .dot .jpg .jpeg .pdf .svg``
496500
call :func:`network.supported_plot_formats()` for more.
497-
:param boolean show:
501+
:param show:
498502
If it evaluates to true, opens the diagram in a matplotlib window.
499-
If it equals ``-1``, it plots but does not open the Window.
503+
If it equals `-1``, it plots but does not open the Window.
504+
:param jupyter:
505+
If it evaluates to true, return an SVG suitable to render
506+
in *jupyter notebook cells* (`ipython` must be installed).
500507
:param steps:
501508
a list of nodes & instructions to overlay on the diagram
502509
:param inputs:
@@ -514,15 +521,18 @@ def plot_graph(graph, filename=None, show=False, steps=None,
514521
515522
**Example:**
516523
517-
>>> netop = compose(name="netop")(
524+
>>> from graphkit import compose, operation
525+
>>> from graphkit.modifiers import optional
526+
527+
>>> pipeline = compose(name="pipeline")(
518528
... operation(name="add", needs=["a", "b1"], provides=["ab1"])(add),
519529
... operation(name="sub", needs=["a", optional("b2")], provides=["ab2"])(lambda a, b=1: a-b),
520530
... operation(name="abb", needs=["ab1", "ab2"], provides=["asked"])(add),
521531
... )
522532
523533
>>> inputs = {'a': 1, 'b1': 2}
524-
>>> solution=netop(inputs)
525-
>>> netop.plot('plot.svg', inputs=inputs, solution=solution, outputs=['asked', 'b1']);
534+
>>> solution=pipeline(inputs)
535+
>>> pipeline.plot('plot.svg', inputs=inputs, solution=solution, outputs=['asked', 'b1']);
526536
527537
"""
528538
import pydot
@@ -596,7 +606,8 @@ def get_node_name(a):
596606
penwidth=3, arrowhead="vee")
597607
g.add_edge(edge)
598608

599-
# save plot
609+
# Save plot
610+
#
600611
if filename:
601612
formats = supported_plot_formats()
602613
_basename, ext = os.path.splitext(filename)
@@ -608,7 +619,14 @@ def get_node_name(a):
608619

609620
g.write(filename, format=ext.lower()[1:])
610621

611-
# display graph via matplotlib
622+
## Return an SVG renderable in jupyter.
623+
#
624+
if jupyter:
625+
from IPython.display import SVG
626+
g = SVG(data=g.create_svg())
627+
628+
## Display graph via matplotlib
629+
#
612630
if show:
613631
import matplotlib.pyplot as plt
614632
import matplotlib.image as mpimg

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
},
3838
tests_require=[
3939
"numpy",
40+
"ipython", # to test jupyter plot.
4041
"pydot", # to test plot
4142
"matplotlib" # to test plot
4243
],

test/test_graphkit.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ def test_plotting():
139139
finally:
140140
shutil.rmtree(tdir, ignore_errors=True)
141141

142-
## Don't open matplotlib window.
142+
## Try matplotlib Window, but
143+
# without opening a Window.
143144
#
144145
if sys.version_info < (3, 5):
145146
# On PY< 3.5 it fails with:
@@ -150,6 +151,12 @@ def test_plotting():
150151
# do not open window in headless travis
151152
assert pipeline.plot(show=-1)
152153

154+
## Try Jupyter SVG.
155+
#
156+
# but latest ipython-7+ dropped < PY3.4
157+
if sys.version_info >= (3, 5):
158+
assert "display.SVG" in str(type(pipeline.plot(jupyter=True)))
159+
153160
try:
154161
pipeline.plot('bad.format')
155162
assert False, "Should had failed writting arbitrary file format!"

0 commit comments

Comments
 (0)