CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-objgraph

Draws Python object reference graphs with graphviz

Pending
Overview
Eval results
Files

graph-visualization.mddocs/

Graph Visualization

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.

Capabilities

Backward Reference Visualization

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')

Forward Reference Visualization

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')

Reference Chain Visualization

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')

Output Formats and Viewing

objgraph supports multiple output formats and viewing methods:

File Formats

# 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

Interactive Viewing

# 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 notebooks

Common Workflows

Memory Leak Visualization

import 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')

Complex Object Structure Analysis

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')

Circular Reference Detection

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

docs

graph-visualization.md

growth-analysis.md

index.md

object-discovery.md

object-statistics.md

reference-chains.md

tile.json