Draws Python object reference graphs with graphviz
—
Tools for tracing reference chains between objects to understand how objects are connected, what keeps them alive, and how to reach specific objects. These functions help diagnose memory leaks and understand object relationships.
Find a path from a starting object to any object matching a predicate by following object references.
def find_ref_chain(obj, predicate, max_depth=20, extra_ignore=()):
"""
Find a shortest chain of references leading from obj.
Args:
obj: Starting object for the search
predicate (callable): Function taking object and returning bool; target match condition
max_depth (int): Maximum search depth to prevent infinite recursion
extra_ignore (tuple): Object IDs to exclude from the search
Returns:
list: Reference chain from obj to matching object, or [obj] if no chain found
Note:
The end of the chain will be some object that matches your predicate.
Returns the chain in forward direction (from source to target).
"""Usage examples:
import objgraph
import sys
# Find path from an object to any module
my_object = [1, 2, 3]
chain = objgraph.find_ref_chain(my_object, lambda x: objgraph.is_proper_module(x))
print(f"Chain length: {len(chain)}")
for i, obj in enumerate(chain):
print(f" {i}: {type(obj).__name__} - {repr(obj)[:50]}")
# Find path to a specific class
class MyTarget:
pass
target = MyTarget()
some_container = {'target': target, 'other': 'data'}
chain = objgraph.find_ref_chain(
some_container,
lambda x: isinstance(x, MyTarget)
)
if len(chain) > 1:
print("Found path to MyTarget instance")
# Find path to any function
def sample_function():
pass
namespace = {'func': sample_function, 'data': [1, 2, 3]}
chain = objgraph.find_ref_chain(
namespace,
lambda x: callable(x) and hasattr(x, '__name__')
)
# Ignore specific objects in the search
frame = sys._getframe()
chain = objgraph.find_ref_chain(
my_object,
lambda x: objgraph.is_proper_module(x),
extra_ignore=(id(frame), id(locals()))
)Find a path from any object matching a predicate to a target object by following back-references.
def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
"""
Find a shortest chain of references leading to obj.
Args:
obj: Target object for the search
predicate (callable): Function taking object and returning bool; source match condition
max_depth (int): Maximum search depth to prevent infinite recursion
extra_ignore (tuple): Object IDs to exclude from the search
Returns:
list: Reference chain from matching object to obj, or [obj] if no chain found
Note:
The start of the chain will be some object that matches your predicate.
Useful for finding what keeps an object alive.
"""Usage examples:
import objgraph
# Find what module keeps an object alive
my_object = [1, 2, 3]
# Store it in a global for this example
globals()['my_global_list'] = my_object
chain = objgraph.find_backref_chain(my_object, objgraph.is_proper_module)
if len(chain) > 1:
print(f"Object is reachable from module: {chain[0]}")
print(f"Chain length: {len(chain)}")
for i, obj in enumerate(chain):
print(f" {i}: {type(obj).__name__}")
# Find what keeps a custom object alive
class MyClass:
def __init__(self, name):
self.name = name
obj = MyClass("example")
container = {'my_obj': obj}
root_dict = {'container': container}
# Find path from any dict to our object
chain = objgraph.find_backref_chain(
obj,
lambda x: isinstance(x, dict) and len(x) > 0
)
# Find path from module to object (common for debugging)
import types
chain = objgraph.find_backref_chain(
obj,
lambda x: isinstance(x, types.ModuleType)
)
# Ignore current frame to avoid finding temporary references
import sys
chain = objgraph.find_backref_chain(
obj,
objgraph.is_proper_module,
extra_ignore=(id(sys._getframe()), id(locals()))
)Use reference chains to understand what keeps objects alive when they should be garbage collected.
import objgraph
# Suppose you have objects that should be garbage collected but aren't
class PotentiallyLeakedClass:
def __init__(self, data):
self.data = data
# Create some objects
leaked_candidates = [PotentiallyLeakedClass(f"data_{i}") for i in range(5)]
# Clear obvious references
leaked_candidates.clear()
# But suppose objects are still alive - find what keeps them alive
remaining_objects = objgraph.by_type('PotentiallyLeakedClass')
if remaining_objects:
print(f"Found {len(remaining_objects)} objects that should be gone")
# For each remaining object, find what root keeps it alive
for obj in remaining_objects[:3]: # Check first 3
chain = objgraph.find_backref_chain(obj, objgraph.is_proper_module)
if len(chain) > 1:
print(f"Object kept alive by: {chain[0]}")
print(f"Path length: {len(chain)}")
# Visualize the chain
objgraph.show_chain(chain)Understand how objects are connected and reachable from different roots.
import objgraph
# Create a complex object structure
class Node:
def __init__(self, value):
self.value = value
self.children = []
self.parent = None
def add_child(self, child):
child.parent = self
self.children.append(child)
# Create a tree structure
root = Node("root")
child1 = Node("child1")
child2 = Node("child2")
grandchild = Node("grandchild")
root.add_child(child1)
root.add_child(child2)
child1.add_child(grandchild)
# Find path from grandchild to root
chain = objgraph.find_ref_chain(
grandchild,
lambda x: isinstance(x, Node) and x.value == "root"
)
print(f"Path from grandchild to root: {len(chain)} steps")
for i, node in enumerate(chain):
if isinstance(node, Node):
print(f" {i}: Node({node.value})")
else:
print(f" {i}: {type(node).__name__}")
# Find what ultimately keeps grandchild alive
backchain = objgraph.find_backref_chain(grandchild, objgraph.is_proper_module)
print(f"Grandchild kept alive by chain of length: {len(backchain)}")import objgraph
import types
# Find path to any function
chain = objgraph.find_ref_chain(
some_object,
lambda x: isinstance(x, types.FunctionType)
)
# Find path to large containers
chain = objgraph.find_ref_chain(
some_object,
lambda x: hasattr(x, '__len__') and len(x) > 1000
)
# Find path to objects with specific attributes
chain = objgraph.find_ref_chain(
some_object,
lambda x: hasattr(x, 'connection') and hasattr(x, 'execute')
)
# Find path from specific module
import json
chain = objgraph.find_backref_chain(
target_object,
lambda x: x is json # Specifically the json module
)
# Find path from any class definition
chain = objgraph.find_backref_chain(
instance_object,
lambda x: isinstance(x, type) # Any class/type object
)Reference chains work well with objgraph's visualization functions:
import objgraph
# Find a chain and visualize it
my_object = [1, 2, 3]
chain = objgraph.find_backref_chain(my_object, objgraph.is_proper_module)
if len(chain) > 1:
# Show the entire chain
objgraph.show_chain(chain, filename='chain.png')
# Show backrefs from the object with chain context
objgraph.show_backrefs([my_object],
filter=lambda x: id(x) in {id(obj) for obj in chain},
filename='chain_context.png')Install with Tessl CLI
npx tessl i tessl/pypi-objgraph