From 010dc8a7f0396d41bf340ef09457bb7fa1cc156e Mon Sep 17 00:00:00 2001 From: root Date: Sun, 20 Nov 2016 17:30:58 +0800 Subject: [PATCH 1/4] Display graph in IPython. --- objgraph.py | 55 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 15 deletions(-) mode change 100644 => 100755 objgraph.py diff --git a/objgraph.py b/objgraph.py old mode 100644 new mode 100755 index 6877ed1..6c1fa84 --- a/objgraph.py +++ b/objgraph.py @@ -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: @@ -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. @@ -468,7 +482,7 @@ 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, + 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, @@ -536,7 +550,7 @@ 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, + 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, @@ -654,6 +668,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: @@ -662,14 +678,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: + 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 = [] @@ -767,13 +787,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): From ced70703e55126e23d3105ad2eb4cafe8fec0b7f Mon Sep 17 00:00:00 2001 From: root Date: Sun, 20 Nov 2016 17:51:18 +0800 Subject: [PATCH 2/4] Fix make lint. --- objgraph.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/objgraph.py b/objgraph.py index 6c1fa84..01cbee5 100755 --- a/objgraph.py +++ b/objgraph.py @@ -483,11 +483,11 @@ class name part. # graph with useless clutter. That's why we're specifying # cull_func here, but not in show_graph(). 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) + 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, @@ -551,10 +551,11 @@ def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10, New parameter: ``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) + 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): @@ -794,8 +795,8 @@ def _show_graph(objs, edge_func, swap_source_target, 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. + # 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) From 6c33bfb643484baae173360072f1f93133ee6060 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Nov 2016 15:08:03 +0800 Subject: [PATCH 3/4] Detect IPython frontend. --- objgraph.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/objgraph.py b/objgraph.py index 01cbee5..61f0f67 100755 --- a/objgraph.py +++ b/objgraph.py @@ -70,13 +70,13 @@ # Python 3.x compatibility iteritems = dict.items +IS_INTERACTIVE = False try: - __IPYTHON__ import graphviz + if get_ipython().__class__.__name__ != 'TerminalInteractiveShell': + IS_INTERACTIVE = True except (NameError, ImportError): - IS_INTERACTIVE = False -else: - IS_INTERACTIVE = True + pass def _isinstance(object, classinfo): From 57719cd9cd92ec7af9adb773b59a8446a4f5fb04 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Nov 2016 18:00:23 +0800 Subject: [PATCH 4/4] Adjust else-if --- objgraph.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/objgraph.py b/objgraph.py index 61f0f67..308fd39 100755 --- a/objgraph.py +++ b/objgraph.py @@ -678,19 +678,18 @@ def _show_graph(objs, edge_func, swap_source_target, elif filename and filename.endswith('.dot'): f = codecs.open(filename, 'w', encoding='utf-8') dot_filename = filename + elif IS_INTERACTIVE: + is_interactive = True + f = StringIO() else: - if IS_INTERACTIVE: - 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') + 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 = []