Draws Python object reference graphs with graphviz
—
Generate visual representations of object reference graphs using graphviz. These functions create diagrams showing how objects reference each other, essential for understanding memory layouts, debugging circular references, and visualizing object relationships.
Generate graphs showing what objects refer to the target objects.
def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10, highlight=None, filename=None, extra_info=None, refcounts=False, shortnames=True, output=None, extra_node_attrs=None):
"""
Generate an object reference graph ending at objs.
Args:
objs: Single object or list of objects to analyze
max_depth (int): Maximum graph depth to traverse
extra_ignore (tuple): Object IDs to ignore in the graph
filter (callable, optional): Function taking object and returning bool; objects returning False are excluded
too_many (int): Limit for references per object before truncating
highlight (callable, optional): Function taking object and returning bool; matching objects highlighted in blue
filename (str, optional): Output file name (.dot, .png, .svg, etc.)
extra_info (callable, optional): Function taking object and returning string for extra node information
refcounts (bool): Show reference counts in node labels
shortnames (bool): Use short class names vs fully qualified names
output (file-like, optional): File object to write GraphViz output
extra_node_attrs (callable, optional): Function taking object and returning dict of GraphViz node attributes
Returns:
None or graphviz.Source: None for file output, Source object for interactive display
Note:
Shows what refers to the objects. Automatically stops at proper modules to avoid clutter.
Cannot specify both filename and output parameters (raises ValueError).
"""Usage examples:
import objgraph
# Basic backref visualization
my_object = [1, 2, 3]
objgraph.show_backrefs([my_object]) # Interactive display or xdot
# Save to file
objgraph.show_backrefs([my_object], filename='my_backrefs.png')
# Multiple objects
obj1 = {'key': 'value'}
obj2 = [1, 2, 3]
objgraph.show_backrefs([obj1, obj2], filename='multi_backrefs.png')
# Advanced filtering
def is_interesting(obj):
return not isinstance(obj, (str, int, float, bool, type(None)))
objgraph.show_backrefs([my_object],
filter=is_interesting,
max_depth=5,
filename='filtered_backrefs.png')
# Highlight specific objects
import types
objgraph.show_backrefs([my_object],
highlight=lambda x: isinstance(x, types.ModuleType),
filename='highlighted_backrefs.png')
# Show reference counts
objgraph.show_backrefs([my_object],
refcounts=True,
filename='refcount_backrefs.png')
# Fully qualified names
objgraph.show_backrefs([my_object],
shortnames=False,
filename='qualified_backrefs.png')
# Custom extra info
def show_size(obj):
if hasattr(obj, '__len__'):
return f"size: {len(obj)}"
return ""
objgraph.show_backrefs([my_object],
extra_info=show_size,
filename='size_info_backrefs.png')
# Output to file object
import io
output_buffer = io.StringIO()
objgraph.show_backrefs([my_object], output=output_buffer)
dot_content = output_buffer.getvalue()
# Custom node attributes
def node_attrs(obj):
if isinstance(obj, list):
return {'color': 'red', 'style': 'bold'}
return {}
objgraph.show_backrefs([my_object],
extra_node_attrs=node_attrs,
filename='custom_attrs_backrefs.png')Generate graphs showing what objects the target objects refer to.
def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10, highlight=None, filename=None, extra_info=None, refcounts=False, shortnames=True, output=None, extra_node_attrs=None):
"""
Generate an object reference graph starting at objs.
Args:
objs: Single object or list of objects to analyze
max_depth (int): Maximum graph depth to traverse
extra_ignore (tuple): Object IDs to ignore in the graph
filter (callable, optional): Function taking object and returning bool; objects returning False are excluded
too_many (int): Limit for references per object before truncating
highlight (callable, optional): Function taking object and returning bool; matching objects highlighted in blue
filename (str, optional): Output file name (.dot, .png, .svg, etc.)
extra_info (callable, optional): Function taking object and returning string for extra node information
refcounts (bool): Show reference counts in node labels
shortnames (bool): Use short class names vs fully qualified names
output (file-like, optional): File object to write GraphViz output
extra_node_attrs (callable, optional): Function taking object and returning dict of GraphViz node attributes
Returns:
None or graphviz.Source: None for file output, Source object for interactive display
Note:
Shows what the objects refer to. Follows references from modules (unlike show_backrefs).
Cannot specify both filename and output parameters (raises ValueError).
"""Usage examples:
import objgraph
# Basic forward reference visualization
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
self.info = {'status': 'active'}
obj = MyClass()
objgraph.show_refs([obj], filename='forward_refs.png')
# Show what a complex object contains
complex_structure = {
'lists': [[1, 2], [3, 4], [5, 6]],
'nested': {'inner': {'deep': 'value'}},
'function': lambda x: x * 2
}
objgraph.show_refs([complex_structure],
max_depth=4,
filename='complex_refs.png')
# Filter out built-in types
def is_custom(obj):
return not isinstance(obj, (str, int, float, bool, type(None)))
objgraph.show_refs([obj],
filter=is_custom,
filename='custom_only_refs.png')
# Highlight containers
objgraph.show_refs([obj],
highlight=lambda x: hasattr(x, '__len__'),
filename='highlighted_containers.png')
# Limit references to avoid cluttered graphs
large_object = {'key_' + str(i): f'value_{i}' for i in range(100)}
objgraph.show_refs([large_object],
too_many=5, # Only show first 5 references
filename='limited_refs.png')Display specific reference chains found with find_ref_chain or find_backref_chain.
def show_chain(*chains, **kw):
"""
Show a chain (or several chains) of object references.
Args:
*chains: One or more reference chains (lists of objects)
backrefs (bool, keyword): If True, trace backwards; if False, trace forwards (default: True)
highlight (callable, keyword): Function taking object and returning bool for highlighting
extra_info (callable, keyword): Function taking object and returning string for extra info
refcounts (bool, keyword): Show reference counts in node labels
shortnames (bool, keyword): Use short class names vs fully qualified names
filename (str, keyword): Output file name (.dot, .png, .svg, etc.)
output (file-like, keyword): File object to write GraphViz output to
Returns:
None or graphviz.Source: None for file output, Source object for interactive display
Note:
Useful with find_ref_chain() or find_backref_chain().
Filters the graph to show only objects in the provided chains.
"""Usage examples:
import objgraph
# Find and visualize a chain
my_object = [1, 2, 3]
globals()['my_global'] = my_object # Ensure it's reachable from a module
chain = objgraph.find_backref_chain(my_object, objgraph.is_proper_module)
objgraph.show_chain(chain, filename='backref_chain.png')
# Multiple chains
obj1 = [1, 2, 3]
obj2 = {'key': 'value'}
globals()['obj1'] = obj1
globals()['obj2'] = obj2
chain1 = objgraph.find_backref_chain(obj1, objgraph.is_proper_module)
chain2 = objgraph.find_backref_chain(obj2, objgraph.is_proper_module)
objgraph.show_chain(chain1, chain2, filename='multiple_chains.png')
# Forward chain visualization
target_obj = [1, 2, 3]
container = {'target': target_obj}
forward_chain = objgraph.find_ref_chain(container, lambda x: isinstance(x, list))
objgraph.show_chain(forward_chain, backrefs=False, filename='forward_chain.png')
# Chain with highlighting
objgraph.show_chain(chain,
highlight=lambda x: isinstance(x, dict),
filename='highlighted_chain.png')
# Chain with extra info
def show_type_and_size(obj):
type_name = type(obj).__name__
if hasattr(obj, '__len__'):
return f"{type_name} (size: {len(obj)})"
return type_name
objgraph.show_chain(chain,
extra_info=show_type_and_size,
filename='detailed_chain.png')objgraph supports multiple output formats and viewing methods:
# PNG images (most common)
objgraph.show_backrefs([obj], filename='graph.png')
# SVG for scalable graphics
objgraph.show_backrefs([obj], filename='graph.svg')
# PDF for high-quality printing
objgraph.show_backrefs([obj], filename='graph.pdf')
# Raw GraphViz DOT format
objgraph.show_backrefs([obj], filename='graph.dot')
# Format determined by extension
objgraph.show_backrefs([obj], filename='graph.jpg') # JPEG# Automatic viewing (tries xdot, then generates PNG)
objgraph.show_backrefs([obj])
# IPython/Jupyter inline display
# (returns graphviz.Source object when IS_INTERACTIVE=True)
result = objgraph.show_backrefs([obj])
# result can be displayed inline in notebooksimport objgraph
import gc
# Find objects that might be leaking
gc.collect()
before_count = objgraph.count('MyClass')
# Run code that might create objects
for i in range(100):
# some_operation_that_might_leak()
pass
gc.collect()
after_count = objgraph.count('MyClass')
if after_count > before_count:
print(f"Potential leak: {after_count - before_count} new MyClass objects")
# Get some instances to investigate
instances = objgraph.by_type('MyClass')
recent_instances = instances[before_count:before_count + 5] # First 5 new ones
# Show what keeps them alive
objgraph.show_backrefs(recent_instances,
max_depth=5,
filename='potential_leak.png')import objgraph
# Analyze a complex data structure
class Node:
def __init__(self, value):
self.value = value
self.children = []
self.metadata = {}
def add_child(self, child):
self.children.append(child)
child.metadata['parent'] = self
# Create complex structure
root = Node("root")
for i in range(3):
child = Node(f"child_{i}")
root.add_child(child)
for j in range(2):
grandchild = Node(f"grandchild_{i}_{j}")
child.add_child(grandchild)
# Visualize the structure
objgraph.show_refs([root],
max_depth=4,
highlight=lambda x: isinstance(x, Node),
extra_info=lambda x: getattr(x, 'value', ''),
filename='tree_structure.png')
# Show what keeps leaf nodes alive
leaf_nodes = [node for node in objgraph.by_type('Node')
if not node.children]
objgraph.show_backrefs(leaf_nodes[:2], # First 2 leaf nodes
filename='leaf_backrefs.png')import objgraph
# Create circular references
class CircularA:
def __init__(self):
self.ref_to_b = None
class CircularB:
def __init__(self):
self.ref_to_a = None
# Create the cycle
a = CircularA()
b = CircularB()
a.ref_to_b = b
b.ref_to_a = a
# Visualize the circular reference
objgraph.show_refs([a],
max_depth=3,
highlight=lambda x: isinstance(x, (CircularA, CircularB)),
filename='circular_refs.png')
# Also show backrefs to see the complete cycle
objgraph.show_backrefs([a],
max_depth=3,
highlight=lambda x: isinstance(x, (CircularA, CircularB)),
filename='circular_backrefs.png')Install with Tessl CLI
npx tessl i tessl/pypi-objgraph