or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

graph-visualization.mdgrowth-analysis.mdindex.mdobject-discovery.mdobject-statistics.mdreference-chains.md

reference-chains.mddocs/

0

# Reference Chain Analysis

1

2

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.

3

4

## Capabilities

5

6

### Forward Reference Chain Finding

7

8

Find a path from a starting object to any object matching a predicate by following object references.

9

10

```python { .api }

11

def find_ref_chain(obj, predicate, max_depth=20, extra_ignore=()):

12

"""

13

Find a shortest chain of references leading from obj.

14

15

Args:

16

obj: Starting object for the search

17

predicate (callable): Function taking object and returning bool; target match condition

18

max_depth (int): Maximum search depth to prevent infinite recursion

19

extra_ignore (tuple): Object IDs to exclude from the search

20

21

Returns:

22

list: Reference chain from obj to matching object, or [obj] if no chain found

23

24

Note:

25

The end of the chain will be some object that matches your predicate.

26

Returns the chain in forward direction (from source to target).

27

"""

28

```

29

30

Usage examples:

31

32

```python

33

import objgraph

34

import sys

35

36

# Find path from an object to any module

37

my_object = [1, 2, 3]

38

chain = objgraph.find_ref_chain(my_object, lambda x: objgraph.is_proper_module(x))

39

print(f"Chain length: {len(chain)}")

40

for i, obj in enumerate(chain):

41

print(f" {i}: {type(obj).__name__} - {repr(obj)[:50]}")

42

43

# Find path to a specific class

44

class MyTarget:

45

pass

46

47

target = MyTarget()

48

some_container = {'target': target, 'other': 'data'}

49

50

chain = objgraph.find_ref_chain(

51

some_container,

52

lambda x: isinstance(x, MyTarget)

53

)

54

if len(chain) > 1:

55

print("Found path to MyTarget instance")

56

57

# Find path to any function

58

def sample_function():

59

pass

60

61

namespace = {'func': sample_function, 'data': [1, 2, 3]}

62

chain = objgraph.find_ref_chain(

63

namespace,

64

lambda x: callable(x) and hasattr(x, '__name__')

65

)

66

67

# Ignore specific objects in the search

68

frame = sys._getframe()

69

chain = objgraph.find_ref_chain(

70

my_object,

71

lambda x: objgraph.is_proper_module(x),

72

extra_ignore=(id(frame), id(locals()))

73

)

74

```

75

76

### Backward Reference Chain Finding

77

78

Find a path from any object matching a predicate to a target object by following back-references.

79

80

```python { .api }

81

def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):

82

"""

83

Find a shortest chain of references leading to obj.

84

85

Args:

86

obj: Target object for the search

87

predicate (callable): Function taking object and returning bool; source match condition

88

max_depth (int): Maximum search depth to prevent infinite recursion

89

extra_ignore (tuple): Object IDs to exclude from the search

90

91

Returns:

92

list: Reference chain from matching object to obj, or [obj] if no chain found

93

94

Note:

95

The start of the chain will be some object that matches your predicate.

96

Useful for finding what keeps an object alive.

97

"""

98

```

99

100

Usage examples:

101

102

```python

103

import objgraph

104

105

# Find what module keeps an object alive

106

my_object = [1, 2, 3]

107

# Store it in a global for this example

108

globals()['my_global_list'] = my_object

109

110

chain = objgraph.find_backref_chain(my_object, objgraph.is_proper_module)

111

if len(chain) > 1:

112

print(f"Object is reachable from module: {chain[0]}")

113

print(f"Chain length: {len(chain)}")

114

for i, obj in enumerate(chain):

115

print(f" {i}: {type(obj).__name__}")

116

117

# Find what keeps a custom object alive

118

class MyClass:

119

def __init__(self, name):

120

self.name = name

121

122

obj = MyClass("example")

123

container = {'my_obj': obj}

124

root_dict = {'container': container}

125

126

# Find path from any dict to our object

127

chain = objgraph.find_backref_chain(

128

obj,

129

lambda x: isinstance(x, dict) and len(x) > 0

130

)

131

132

# Find path from module to object (common for debugging)

133

import types

134

chain = objgraph.find_backref_chain(

135

obj,

136

lambda x: isinstance(x, types.ModuleType)

137

)

138

139

# Ignore current frame to avoid finding temporary references

140

import sys

141

chain = objgraph.find_backref_chain(

142

obj,

143

objgraph.is_proper_module,

144

extra_ignore=(id(sys._getframe()), id(locals()))

145

)

146

```

147

148

## Common Workflows

149

150

### Memory Leak Investigation

151

152

Use reference chains to understand what keeps objects alive when they should be garbage collected.

153

154

```python

155

import objgraph

156

157

# Suppose you have objects that should be garbage collected but aren't

158

class PotentiallyLeakedClass:

159

def __init__(self, data):

160

self.data = data

161

162

# Create some objects

163

leaked_candidates = [PotentiallyLeakedClass(f"data_{i}") for i in range(5)]

164

165

# Clear obvious references

166

leaked_candidates.clear()

167

168

# But suppose objects are still alive - find what keeps them alive

169

remaining_objects = objgraph.by_type('PotentiallyLeakedClass')

170

if remaining_objects:

171

print(f"Found {len(remaining_objects)} objects that should be gone")

172

173

# For each remaining object, find what root keeps it alive

174

for obj in remaining_objects[:3]: # Check first 3

175

chain = objgraph.find_backref_chain(obj, objgraph.is_proper_module)

176

if len(chain) > 1:

177

print(f"Object kept alive by: {chain[0]}")

178

print(f"Path length: {len(chain)}")

179

# Visualize the chain

180

objgraph.show_chain(chain)

181

```

182

183

### Object Reachability Analysis

184

185

Understand how objects are connected and reachable from different roots.

186

187

```python

188

import objgraph

189

190

# Create a complex object structure

191

class Node:

192

def __init__(self, value):

193

self.value = value

194

self.children = []

195

self.parent = None

196

197

def add_child(self, child):

198

child.parent = self

199

self.children.append(child)

200

201

# Create a tree structure

202

root = Node("root")

203

child1 = Node("child1")

204

child2 = Node("child2")

205

grandchild = Node("grandchild")

206

207

root.add_child(child1)

208

root.add_child(child2)

209

child1.add_child(grandchild)

210

211

# Find path from grandchild to root

212

chain = objgraph.find_ref_chain(

213

grandchild,

214

lambda x: isinstance(x, Node) and x.value == "root"

215

)

216

217

print(f"Path from grandchild to root: {len(chain)} steps")

218

for i, node in enumerate(chain):

219

if isinstance(node, Node):

220

print(f" {i}: Node({node.value})")

221

else:

222

print(f" {i}: {type(node).__name__}")

223

224

# Find what ultimately keeps grandchild alive

225

backchain = objgraph.find_backref_chain(grandchild, objgraph.is_proper_module)

226

print(f"Grandchild kept alive by chain of length: {len(backchain)}")

227

```

228

229

### Custom Predicate Examples

230

231

```python

232

import objgraph

233

import types

234

235

# Find path to any function

236

chain = objgraph.find_ref_chain(

237

some_object,

238

lambda x: isinstance(x, types.FunctionType)

239

)

240

241

# Find path to large containers

242

chain = objgraph.find_ref_chain(

243

some_object,

244

lambda x: hasattr(x, '__len__') and len(x) > 1000

245

)

246

247

# Find path to objects with specific attributes

248

chain = objgraph.find_ref_chain(

249

some_object,

250

lambda x: hasattr(x, 'connection') and hasattr(x, 'execute')

251

)

252

253

# Find path from specific module

254

import json

255

chain = objgraph.find_backref_chain(

256

target_object,

257

lambda x: x is json # Specifically the json module

258

)

259

260

# Find path from any class definition

261

chain = objgraph.find_backref_chain(

262

instance_object,

263

lambda x: isinstance(x, type) # Any class/type object

264

)

265

```

266

267

## Integration with Visualization

268

269

Reference chains work well with objgraph's visualization functions:

270

271

```python

272

import objgraph

273

274

# Find a chain and visualize it

275

my_object = [1, 2, 3]

276

chain = objgraph.find_backref_chain(my_object, objgraph.is_proper_module)

277

278

if len(chain) > 1:

279

# Show the entire chain

280

objgraph.show_chain(chain, filename='chain.png')

281

282

# Show backrefs from the object with chain context

283

objgraph.show_backrefs([my_object],

284

filter=lambda x: id(x) in {id(obj) for obj in chain},

285

filename='chain_context.png')

286

```