Skip to content
This repository

Class `%hierarchy` and graphiz `%%dot` magics #1854

Closed
wants to merge 2 commits into from

3 participants

Takafumi Arakaki Thomas Kluyver Brian E. Granger
Takafumi Arakaki
tkf commented June 04, 2012

This PR introduces two magics.

First magic is %hierarchy. This magic command draws hierarchy of given class or the class of given instance. For example, the following shows class hierarchy of currently running IPython shell.

%hierarchy get_ipython()

Second magic is %%dot. You can write graphiz dot language in a cell using this magic. Example:

%%dot -- -Kfdp
digraph G {
    a->b; b->c; c->d; d->b; d->a;
}

I have two concerns:

  • The function run_dot, which runs the dot command to produce the figure, is heavily borrowed from Sphinx extension. I am not sure if there are any copyright problem.

  • I used self.shell.display_formatter.format to get "full path" (something like IPython.zmq.zmqshell.ZMQInteractiveShell) of a class. Maybe there is a better way to do that.

added some commits June 03, 2012
Takafumi Arakaki Add %hierarchy magic
%hierarchy magic draws class hierarchy of a given class or class of a
given instance.  It relies on Sphinx's inheritance_diagram extension.
98a48e8
Takafumi Arakaki Add %%dot Graphviz magic 58353a2
Thomas Kluyver
Collaborator

It's great to see people making creative use of the magic system, but this does raise a question about how much we want to ship as part of IPython. We do have an extension system, and my inclination is that this belongs as a separate extension. Others may disagree, though.

Your concerns:

  • Sphinx is BSD licensed, and so long as that covers whichever extension it's from, we can borrow their code. We should include a copyright notice from Sphinx, though, and a note that we're using it under a BSD license.
  • klass.__name__ and klass.__module__ seem to cover it.
Takafumi Arakaki
tkf commented June 06, 2012

Can these magic commands go into IPython? I will fix what you suggested if it is likely. I think drawing class hierarchy is pretty general so having it in IPython is good. %%dot command is kind of a side effect of implementing the %hierarchy command and I guess it won't hurt having it as a extension.

Thomas Kluyver
Collaborator

I've posted on the mailing list about it, and how we can make it easier to find third party extensions. We'll see if anyone else weighs in.

My own feeling is that it would be best to do this as a separate extension for now, so that you can easily work on it and make more frequent updates than IPython's release schedule. Installing an extension is very simple with the %install_ext command. If %hierarchy becomes widely used, we could look at including it to save people that installation step.

Takafumi Arakaki
tkf commented June 07, 2012

Thanks. Yea, maybe it should be a separate extension.

Brian E. Granger
Owner

I am probably +1 on these being separate extensions. But we are discussing on the list where such extensions should be hosted.

Thomas Kluyver
Collaborator

The feeling on the list is that you can use gist.github.com to host extensions (or, of course, set up a regular repository on Github/Bitbucket/etc.). Then we can list them in an index - I've set up a draft here: http://wiki.ipython.org/Extensions_Index

The conversation's still going on on the mailing list, so feel free to join in. http://mail.scipy.org/mailman/listinfo/ipython-dev

Takafumi Arakaki
tkf commented June 08, 2012

I changed them into a separate extension.
https://github.com/tkf/ipython-hierarchymagic

Takafumi Arakaki tkf closed this June 08, 2012
Thomas Kluyver
Collaborator

Great, thanks. We'll keep you updated on anything we decide about extensions in the future.

Would you like to add the extension to the index? It's a wiki, so just create an account and edit the page, adding a section like that for physics.
http://wiki.ipython.org/Extensions_Index

Takafumi Arakaki
tkf commented June 09, 2012

Thanks for the mention. I edited the page.

Thomas Kluyver
Collaborator

Thanks. Can I suggest that you manipulate the screenshot in an image editor to put the two cells side by side? That would make better use of the space on the page.

Takafumi Arakaki
tkf commented June 10, 2012

I just removed the screenshot. I thought it was small enough. Maybe I will put the side-by-side one sometime later.

Thomas Kluyver
Collaborator

I think it's OK to include screenshots, I was just thinking that there's width to spare, while we don't want to make people scroll too much.

Thomas Kluyver
Collaborator

(Not that there's much need to scroll, but I hope one day we'll have 100 extensions on that page ;-) )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Jun 03, 2012
Takafumi Arakaki Add %hierarchy magic
%hierarchy magic draws class hierarchy of a given class or class of a
given instance.  It relies on Sphinx's inheritance_diagram extension.
98a48e8
Jun 05, 2012
Takafumi Arakaki Add %%dot Graphviz magic 58353a2
This page is out of date. Refresh to see the latest.
70  IPython/extensions/graphvizmagic.py
... ...
@@ -0,0 +1,70 @@
  1
+from IPython.core.magic import Magics, magics_class, cell_magic
  2
+from IPython.core.magic_arguments import (argument, magic_arguments,
  3
+                                          parse_argstring)
  4
+from IPython.core.display import display_png, display_svg
  5
+
  6
+
  7
+def run_dot(code, options=[], format='png'):
  8
+    # mostly copied from sphinx.ext.graphviz.render_dot
  9
+    from subprocess import Popen, PIPE
  10
+    from sphinx.util.osutil import EPIPE, EINVAL
  11
+
  12
+    dot_args = ['dot'] + options + ['-T', format]
  13
+    p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
  14
+    wentwrong = False
  15
+    try:
  16
+        # Graphviz may close standard input when an error occurs,
  17
+        # resulting in a broken pipe on communicate()
  18
+        stdout, stderr = p.communicate(code)
  19
+    except (OSError, IOError), err:
  20
+        if err.errno != EPIPE:
  21
+            raise
  22
+        wentwrong = True
  23
+    except IOError, err:
  24
+        if err.errno != EINVAL:
  25
+            raise
  26
+        wentwrong = True
  27
+    if wentwrong:
  28
+        # in this case, read the standard output and standard error streams
  29
+        # directly, to get the error message(s)
  30
+        stdout, stderr = p.stdout.read(), p.stderr.read()
  31
+        p.wait()
  32
+    if p.returncode != 0:
  33
+        raise RuntimeError('dot exited with error:\n[stderr]\n{0}'
  34
+                           .format(stderr))
  35
+    return stdout
  36
+
  37
+
  38
+@magics_class
  39
+class GraphvizMagic(Magics):
  40
+
  41
+    @magic_arguments()
  42
+    @argument(
  43
+        '-f', '--format', default='png', choices=('png', 'svg'),
  44
+        help='output format (png/svg)'
  45
+    )
  46
+    @argument(
  47
+        'options', default=[], nargs='*',
  48
+        help='options passed to the `dot` command'
  49
+    )
  50
+    @cell_magic
  51
+    def dot(self, line, cell):
  52
+        """Draw a figure using Graphviz dot command."""
  53
+        args = parse_argstring(self.dot, line)
  54
+
  55
+        image = run_dot(cell, args.options, format=args.format)
  56
+
  57
+        if args.format == 'png':
  58
+            display_png(image, raw=True)
  59
+        elif args.format == 'svg':
  60
+            display_svg(image, raw=True)
  61
+
  62
+
  63
+_loaded = False
  64
+
  65
+def load_ipython_extension(ip):
  66
+    """Load the extension in IPython."""
  67
+    global _loaded
  68
+    if not _loaded:
  69
+        ip.register_magics(GraphvizMagic)
  70
+        _loaded = True
56  IPython/extensions/hierarchymagic.py
... ...
@@ -0,0 +1,56 @@
  1
+from IPython.core.magic import Magics, magics_class, line_magic
  2
+from IPython.core.magic_arguments import (argument, magic_arguments,
  3
+                                          parse_argstring)
  4
+from IPython.core.display import display_png
  5
+from IPython.extensions.graphvizmagic import run_dot
  6
+
  7
+from sphinx.ext.inheritance_diagram import InheritanceGraph
  8
+
  9
+
  10
+@magics_class
  11
+class HierarchyMagic(Magics):
  12
+
  13
+    @magic_arguments()
  14
+    @argument(
  15
+        '-r', '--rankdir', default='TB',
  16
+        help='direction of the hierarchy graph (default: %(default)s)'
  17
+    )
  18
+    @argument(
  19
+        '-s', '--size', default='5.0, 12.0',
  20
+        help='size of the generated figure (default: %(default)s)',
  21
+    )
  22
+    @argument(
  23
+        'object',
  24
+        help='Class hierarchy of this class or object will be drawn',
  25
+    )
  26
+    @line_magic
  27
+    def hierarchy(self, parameter_s=''):
  28
+        """Draw hierarchy of a given class."""
  29
+        args = parse_argstring(self.hierarchy, parameter_s)
  30
+        obj = self.shell.ev(args.object)
  31
+        if isinstance(obj, type):
  32
+            objclass = obj
  33
+        elif hasattr(obj, "__class__"):
  34
+            objclass = obj.__class__
  35
+        else:
  36
+            raise ValueError(
  37
+                "Given object {0} is not a class or an instance".format(obj))
  38
+        classpath = self.shell.display_formatter.format(
  39
+            objclass, ['text/plain'])['text/plain']
  40
+        (dirpath, basepath) = classpath.rsplit('.', 1)
  41
+        ig = InheritanceGraph([basepath], dirpath)
  42
+        code = ig.generate_dot('inheritance_graph',
  43
+                               graph_attrs={'rankdir': args.rankdir,
  44
+                                            'size': '"{0}"'.format(args.size)})
  45
+        stdout = run_dot(code, format='png')
  46
+        display_png(stdout, raw=True)
  47
+
  48
+
  49
+_loaded = False
  50
+
  51
+def load_ipython_extension(ip):
  52
+    """Load the extension in IPython."""
  53
+    global _loaded
  54
+    if not _loaded:
  55
+        ip.register_magics(HierarchyMagic)
  56
+        _loaded = True
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.