Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display graph in IPython. #28

Merged
merged 4 commits into from
Nov 21, 2016
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 50 additions & 24 deletions objgraph.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
import sys
import itertools

try:
# Python 2.x compatibility
from StringIO import StringIO
except ImportError:
from io import StringIO

try:
from types import InstanceType
except ImportError:
Expand All @@ -64,6 +70,14 @@
# Python 3.x compatibility
iteritems = dict.items

try:
__IPYTHON__
import graphviz
except (NameError, ImportError):
IS_INTERACTIVE = False
else:
IS_INTERACTIVE = True


def _isinstance(object, classinfo):
"""Return whether an object is an instance of a class or its subclass.
Expand Down Expand Up @@ -468,12 +482,12 @@ class name part.
# module because you'll end up in sys.modules and explode the
# graph with useless clutter. That's why we're specifying
# cull_func here, but not in show_graph().
_show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
filter=filter, too_many=too_many, highlight=highlight,
edge_func=gc.get_referrers, swap_source_target=False,
filename=filename, output=output, extra_info=extra_info,
refcounts=refcounts, shortnames=shortnames,
cull_func=is_proper_module)
return _show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
filter=filter, too_many=too_many, highlight=highlight,
edge_func=gc.get_referrers, swap_source_target=False,
filename=filename, output=output, extra_info=extra_info,
refcounts=refcounts, shortnames=shortnames,
cull_func=is_proper_module)


def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
Expand Down Expand Up @@ -536,11 +550,12 @@ def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
.. versionchanged:: 2.0
New parameter: ``output``.
"""
_show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
filter=filter, too_many=too_many, highlight=highlight,
edge_func=gc.get_referents, swap_source_target=True,
filename=filename, extra_info=extra_info, refcounts=refcounts,
shortnames=shortnames, output=output)
return _show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
filter=filter, too_many=too_many, highlight=highlight,
edge_func=gc.get_referents, swap_source_target=True,
filename=filename, extra_info=extra_info,
refcounts=refcounts, shortnames=shortnames,
output=output)


def show_chain(*chains, **kw):
Expand Down Expand Up @@ -654,6 +669,8 @@ def _show_graph(objs, edge_func, swap_source_target,
cull_func=None):
if not _isinstance(objs, (list, tuple)):
objs = [objs]

is_interactive = False
if filename and output:
raise ValueError('Cannot specify both output and filename.')
elif output:
Expand All @@ -662,14 +679,18 @@ def _show_graph(objs, edge_func, swap_source_target,
f = codecs.open(filename, 'w', encoding='utf-8')
dot_filename = filename
else:
fd, dot_filename = tempfile.mkstemp(prefix='objgraph-',
suffix='.dot', text=True)
f = os.fdopen(fd, "w")
if getattr(f, 'encoding', None):
# Python 3 will wrap the file in the user's preferred encoding
# Re-wrap it for utf-8
import io
f = io.TextIOWrapper(f.detach(), 'utf-8')
if IS_INTERACTIVE:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use elif here instead of nesting an if inside the else? I think it'll also make the diff smaller (no indentation changes for the write-to-file branch).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be tested by injecting get_ipython() to __builtin__ .

is_interactive = True
f = StringIO()
else:
fd, dot_filename = tempfile.mkstemp(prefix='objgraph-',
suffix='.dot', text=True)
f = os.fdopen(fd, "w")
if getattr(f, 'encoding', None):
# Python 3 will wrap the file in the user's preferred encoding
# Re-wrap it for utf-8
import io
f = io.TextIOWrapper(f.detach(), 'utf-8')
f.write('digraph ObjectGraph {\n'
' node[shape=box, style=filled, fillcolor=white];\n')
queue = []
Expand Down Expand Up @@ -767,13 +788,18 @@ def _show_graph(objs, edge_func, swap_source_target,
f.write(' too_many_%s[fontcolor=white];\n'
% (_obj_node_id(target)))
f.write("}\n")

if output:
return
# The file should only be closed if this function was in charge of opening
# the file.
f.close()
print("Graph written to %s (%d nodes)" % (dot_filename, nodes))
_present_graph(dot_filename, filename)

if is_interactive:
return graphviz.Source(f.getvalue())
else:
# The file should only be closed if this function was in charge of
# opening the file.
f.close()
print("Graph written to %s (%d nodes)" % (dot_filename, nodes))
_present_graph(dot_filename, filename)


def _present_graph(dot_filename, filename=None):
Expand Down