or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

import-export.mdindex.mdnode-construction.mdpath-resolution.mdsearch.mdtree-iteration.mdtree-rendering.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

Helper functions for common tree operations including finding common ancestors, accessing sibling nodes, and performance-optimized cached search operations. These utilities provide convenient shortcuts for frequent tree manipulation tasks.

3

4

## Capabilities

5

6

### Ancestor and Relationship Utilities

7

8

#### commonancestors - Find Common Ancestors

9

10

Determine common ancestors of multiple nodes, useful for finding shared parent nodes and calculating relationships between tree branches.

11

12

```python { .api }

13

def commonancestors(*nodes):

14

"""

15

Determine common ancestors of nodes.

16

17

Args:

18

*nodes: Variable number of nodes to find common ancestors for

19

20

Returns:

21

Tuple of common ancestor nodes from closest to root

22

"""

23

```

24

25

**Usage Example:**

26

27

```python

28

from anytree import Node, util

29

30

# Create tree structure

31

root = Node("Company")

32

engineering = Node("Engineering", parent=root)

33

marketing = Node("Marketing", parent=root)

34

backend = Node("Backend", parent=engineering)

35

frontend = Node("Frontend", parent=engineering)

36

content = Node("Content", parent=marketing)

37

social = Node("Social", parent=marketing)

38

39

# Find common ancestors of nodes in same subtree

40

backend_frontend_ancestors = util.commonancestors(backend, frontend)

41

print(f"Backend & Frontend ancestors: {[n.name for n in backend_frontend_ancestors]}")

42

# Output: ['Company', 'Engineering']

43

44

# Find common ancestors of nodes in different subtrees

45

backend_content_ancestors = util.commonancestors(backend, content)

46

print(f"Backend & Content ancestors: {[n.name for n in backend_content_ancestors]}")

47

# Output: ['Company']

48

49

# Common ancestors of multiple nodes

50

all_teams_ancestors = util.commonancestors(backend, frontend, content, social)

51

print(f"All teams ancestors: {[n.name for n in all_teams_ancestors]}")

52

# Output: ['Company']

53

54

# Single node (returns all ancestors)

55

single_ancestors = util.commonancestors(backend)

56

print(f"Backend ancestors: {[n.name for n in single_ancestors]}")

57

# Output: ['Company', 'Engineering']

58

59

# No nodes

60

no_ancestors = util.commonancestors()

61

print(f"No nodes ancestors: {no_ancestors}")

62

# Output: ()

63

```

64

65

### Sibling Navigation

66

67

#### leftsibling - Get Left Sibling

68

69

Return the left (previous) sibling of a node, useful for navigation and ordering operations.

70

71

```python { .api }

72

def leftsibling(node):

73

"""

74

Return Left Sibling of node.

75

76

Args:

77

node: Node to find left sibling for

78

79

Returns:

80

Left sibling node or None if node is first child or root

81

"""

82

```

83

84

**Usage Example:**

85

86

```python

87

from anytree import Node, util

88

89

# Create ordered children

90

parent = Node("Parent")

91

first = Node("First", parent=parent)

92

second = Node("Second", parent=parent)

93

third = Node("Third", parent=parent)

94

fourth = Node("Fourth", parent=parent)

95

96

# Get left siblings

97

print(f"First left sibling: {util.leftsibling(first)}") # None

98

print(f"Second left sibling: {util.leftsibling(second)}") # Node('/Parent/First')

99

print(f"Third left sibling: {util.leftsibling(third)}") # Node('/Parent/Second')

100

print(f"Fourth left sibling: {util.leftsibling(fourth)}") # Node('/Parent/Third')

101

102

# Root node has no sibling

103

print(f"Root left sibling: {util.leftsibling(parent)}") # None

104

```

105

106

#### rightsibling - Get Right Sibling

107

108

Return the right (next) sibling of a node, useful for navigation and ordering operations.

109

110

```python { .api }

111

def rightsibling(node):

112

"""

113

Return Right Sibling of node.

114

115

Args:

116

node: Node to find right sibling for

117

118

Returns:

119

Right sibling node or None if node is last child or root

120

"""

121

```

122

123

**Usage Example:**

124

125

```python

126

from anytree import Node, util

127

128

# Create ordered children

129

parent = Node("Parent")

130

first = Node("First", parent=parent)

131

second = Node("Second", parent=parent)

132

third = Node("Third", parent=parent)

133

fourth = Node("Fourth", parent=parent)

134

135

# Get right siblings

136

print(f"First right sibling: {util.rightsibling(first)}") # Node('/Parent/Second')

137

print(f"Second right sibling: {util.rightsibling(second)}") # Node('/Parent/Third')

138

print(f"Third right sibling: {util.rightsibling(third)}") # Node('/Parent/Fourth')

139

print(f"Fourth right sibling: {util.rightsibling(fourth)}") # None

140

141

# Root node has no sibling

142

print(f"Root right sibling: {util.rightsibling(parent)}") # None

143

```

144

145

## Cached Search Module

146

147

High-performance cached versions of search functions for repeated queries on the same tree structure. These functions provide significant performance improvements when performing multiple searches on static or slowly-changing trees.

148

149

### Performance-Optimized Search

150

151

```python { .api }

152

# Import cached search functions

153

from anytree import cachedsearch

154

155

# Cached versions of search functions with same signatures

156

def cachedsearch.find(node, filter_=None, stop=None, maxlevel=None): ...

157

def cachedsearch.find_by_attr(node, value, name="name", maxlevel=None): ...

158

def cachedsearch.findall(node, filter_=None, stop=None, maxlevel=None, mincount=None, maxcount=None): ...

159

def cachedsearch.findall_by_attr(node, value, name="name", maxlevel=None, mincount=None, maxcount=None): ...

160

```

161

162

**Usage Example:**

163

164

```python

165

from anytree import Node, cachedsearch

166

import time

167

168

# Create large tree for performance testing

169

def create_large_tree(depth=5, breadth=10):

170

root = Node("root")

171

172

def add_children(parent, current_depth):

173

if current_depth >= depth:

174

return

175

176

for i in range(breadth):

177

child = Node(f"{parent.name}_child_{i}",

178

parent=parent,

179

level=current_depth,

180

category=f"cat_{i % 3}")

181

add_children(child, current_depth + 1)

182

183

add_children(root, 0)

184

return root

185

186

large_tree = create_large_tree(depth=4, breadth=8)

187

print(f"Created tree with {len(list(large_tree.descendants)) + 1} nodes")

188

189

# Compare performance: regular vs cached search

190

def time_search(search_func, *args, **kwargs):

191

start = time.time()

192

result = search_func(*args, **kwargs)

193

end = time.time()

194

return result, end - start

195

196

# Regular search

197

from anytree import findall_by_attr

198

regular_results, regular_time = time_search(

199

findall_by_attr, large_tree, "cat_1", name="category"

200

)

201

202

# First cached search (builds cache)

203

cached_results1, cached_time1 = time_search(

204

cachedsearch.findall_by_attr, large_tree, "cat_1", name="category"

205

)

206

207

# Second cached search (uses cache)

208

cached_results2, cached_time2 = time_search(

209

cachedsearch.findall_by_attr, large_tree, "cat_1", name="category"

210

)

211

212

print(f"Regular search: {regular_time:.4f}s")

213

print(f"First cached search: {cached_time1:.4f}s")

214

print(f"Second cached search: {cached_time2:.4f}s")

215

print(f"Cache speedup: {regular_time/cached_time2:.1f}x")

216

print(f"Results identical: {len(regular_results) == len(cached_results2)}")

217

```

218

219

## Common Utility Patterns

220

221

### Tree Navigation Helpers

222

223

Build navigation utilities using sibling functions:

224

225

```python

226

from anytree import Node, util

227

228

class TreeNavigator:

229

@staticmethod

230

def get_siblings(node):

231

"""Get all siblings of a node (excluding the node itself)"""

232

if not node.parent:

233

return ()

234

return tuple(child for child in node.parent.children if child != node)

235

236

@staticmethod

237

def get_sibling_by_offset(node, offset):

238

"""Get sibling at specified offset (negative for left, positive for right)"""

239

if not node.parent:

240

return None

241

242

siblings = node.parent.children

243

current_index = siblings.index(node)

244

target_index = current_index + offset

245

246

if 0 <= target_index < len(siblings):

247

return siblings[target_index]

248

return None

249

250

@staticmethod

251

def move_node(node, direction):

252

"""Move node left or right among siblings"""

253

if direction == "left":

254

left = util.leftsibling(node)

255

if left:

256

# Swap positions by manipulating parent's children

257

parent = node.parent

258

children = list(parent.children)

259

node_idx = children.index(node)

260

left_idx = children.index(left)

261

children[node_idx], children[left_idx] = children[left_idx], children[node_idx]

262

parent.children = children

263

return True

264

elif direction == "right":

265

right = util.rightsibling(node)

266

if right:

267

# Similar swap logic

268

parent = node.parent

269

children = list(parent.children)

270

node_idx = children.index(node)

271

right_idx = children.index(right)

272

children[node_idx], children[right_idx] = children[right_idx], children[node_idx]

273

parent.children = children

274

return True

275

return False

276

277

# Example usage

278

parent = Node("Tasks")

279

task1 = Node("Task 1", parent=parent, priority=1)

280

task2 = Node("Task 2", parent=parent, priority=2)

281

task3 = Node("Task 3", parent=parent, priority=3)

282

283

navigator = TreeNavigator()

284

285

# Get all siblings of task2

286

siblings = navigator.get_siblings(task2)

287

print(f"Task 2 siblings: {[s.name for s in siblings]}") # ['Task 1', 'Task 3']

288

289

# Get sibling by offset

290

prev_task = navigator.get_sibling_by_offset(task2, -1) # Task 1

291

next_task = navigator.get_sibling_by_offset(task2, 1) # Task 3

292

print(f"Previous: {prev_task.name}, Next: {next_task.name}")

293

```

294

295

### Common Ancestor Analysis

296

297

Use common ancestors for tree analysis and relationship detection:

298

299

```python

300

from anytree import Node, util

301

302

def analyze_relationships(nodes):

303

"""Analyze relationships between multiple nodes"""

304

if len(nodes) < 2:

305

return "Insufficient nodes for analysis"

306

307

# Find common ancestors

308

ancestors = util.commonancestors(*nodes)

309

310

if not ancestors:

311

return "Nodes are in separate trees"

312

313

closest_ancestor = ancestors[-1] # Last in tuple is closest to nodes

314

315

# Calculate relationship metrics

316

depths_from_ancestor = []

317

for node in nodes:

318

depth = 0

319

current = node

320

while current != closest_ancestor:

321

current = current.parent

322

depth += 1

323

depths_from_ancestor.append(depth)

324

325

analysis = {

326

"common_ancestors": [a.name for a in ancestors],

327

"closest_common_ancestor": closest_ancestor.name,

328

"relationship_distances": dict(zip([n.name for n in nodes], depths_from_ancestor)),

329

"max_distance": max(depths_from_ancestor),

330

"are_siblings": len(set(n.parent for n in nodes if n.parent)) == 1,

331

"are_cousins": len(ancestors) > 1 and all(d == 2 for d in depths_from_ancestor)

332

}

333

334

return analysis

335

336

# Example usage

337

company = Node("Company")

338

eng = Node("Engineering", parent=company)

339

mkt = Node("Marketing", parent=company)

340

backend = Node("Backend", parent=eng)

341

frontend = Node("Frontend", parent=eng)

342

content = Node("Content", parent=mkt)

343

344

# Analyze siblings

345

sibling_analysis = analyze_relationships([backend, frontend])

346

print("Sibling analysis:", sibling_analysis)

347

348

# Analyze cousins

349

cousin_analysis = analyze_relationships([backend, content])

350

print("Cousin analysis:", cousin_analysis)

351

```

352

353

### Tree Manipulation Utilities

354

355

Combine utilities for advanced tree manipulation:

356

357

```python

358

from anytree import Node, util

359

360

class TreeManipulator:

361

@staticmethod

362

def swap_siblings(node1, node2):

363

"""Swap two sibling nodes"""

364

if node1.parent != node2.parent:

365

raise ValueError("Nodes must be siblings")

366

367

parent = node1.parent

368

children = list(parent.children)

369

idx1 = children.index(node1)

370

idx2 = children.index(node2)

371

children[idx1], children[idx2] = children[idx2], children[idx1]

372

parent.children = children

373

374

@staticmethod

375

def sort_children(parent, key_func=None, reverse=False):

376

"""Sort children of a node"""

377

if key_func is None:

378

key_func = lambda n: n.name

379

380

sorted_children = sorted(parent.children, key=key_func, reverse=reverse)

381

parent.children = sorted_children

382

383

@staticmethod

384

def insert_between_siblings(new_node, left_sibling, right_sibling):

385

"""Insert new node between two siblings"""

386

if left_sibling.parent != right_sibling.parent:

387

raise ValueError("Siblings must have same parent")

388

389

if util.rightsibling(left_sibling) != right_sibling:

390

raise ValueError("Nodes must be adjacent siblings")

391

392

parent = left_sibling.parent

393

children = list(parent.children)

394

right_idx = children.index(right_sibling)

395

396

# Set parent and insert at correct position

397

new_node.parent = parent

398

children.insert(right_idx, new_node)

399

parent.children = children

400

401

# Example usage

402

project = Node("Project")

403

phase1 = Node("Phase 1", parent=project)

404

phase3 = Node("Phase 3", parent=project)

405

406

manipulator = TreeManipulator()

407

408

# Insert phase 2 between phase 1 and 3

409

phase2 = Node("Phase 2")

410

manipulator.insert_between_siblings(phase2, phase1, phase3)

411

412

print(f"Project phases: {[child.name for child in project.children]}")

413

# Output: ['Phase 1', 'Phase 2', 'Phase 3']

414

415

# Sort children alphabetically

416

team = Node("Team")

417

Node("Charlie", parent=team, role="developer")

418

Node("Alice", parent=team, role="manager")

419

Node("Bob", parent=team, role="designer")

420

421

print(f"Before sort: {[child.name for child in team.children]}")

422

manipulator.sort_children(team)

423

print(f"After sort: {[child.name for child in team.children]}")

424

# Output: ['Alice', 'Bob', 'Charlie']

425

```

426

427

### Performance Optimization Helpers

428

429

Use cached search efficiently in applications:

430

431

```python

432

from anytree import Node, cachedsearch

433

from functools import lru_cache

434

435

class OptimizedTreeService:

436

def __init__(self, root):

437

self.root = root

438

self._cache_warmed = False

439

440

def warm_cache(self):

441

"""Pre-warm search cache with common queries"""

442

if self._cache_warmed:

443

return

444

445

# Common searches to pre-cache

446

cachedsearch.findall(self.root, filter_=lambda n: True) # Cache all nodes

447

448

# Cache searches by common attributes

449

for attr_name in ['name', 'type', 'category', 'status']:

450

try:

451

cachedsearch.findall_by_attr(self.root, "", name=attr_name)

452

except:

453

pass # Attribute might not exist

454

455

self._cache_warmed = True

456

457

@lru_cache(maxsize=128)

458

def find_by_path(self, path_parts):

459

"""Cached path-based node finding"""

460

current = self.root

461

for part in path_parts:

462

found = cachedsearch.find_by_attr(current, part, name="name")

463

if not found:

464

return None

465

current = found

466

return current

467

468

def bulk_find(self, search_criteria):

469

"""Efficiently perform multiple searches"""

470

self.warm_cache()

471

472

results = {}

473

for criterion_name, (filter_func, max_results) in search_criteria.items():

474

matches = cachedsearch.findall(

475

self.root,

476

filter_=filter_func,

477

maxcount=max_results

478

)

479

results[criterion_name] = matches

480

481

return results

482

483

# Example usage

484

# Create service tree

485

service_root = Node("Services")

486

web_service = Node("WebService", parent=service_root, type="web", status="active")

487

db_service = Node("DatabaseService", parent=service_root, type="database", status="active")

488

cache_service = Node("CacheService", parent=service_root, type="cache", status="inactive")

489

490

# Create optimized service

491

tree_service = OptimizedTreeService(service_root)

492

493

# Perform bulk searches

494

search_criteria = {

495

"active_services": (lambda n: getattr(n, 'status', '') == 'active', 10),

496

"web_services": (lambda n: getattr(n, 'type', '') == 'web', 5),

497

"all_services": (lambda n: hasattr(n, 'type'), 100)

498

}

499

500

results = tree_service.bulk_find(search_criteria)

501

for category, nodes in results.items():

502

print(f"{category}: {[n.name for n in nodes]}")

503

```

504

505

## Module Access Patterns

506

507

### Direct Utility Import

508

509

```python

510

# Import individual utility functions

511

from anytree.util import commonancestors, leftsibling, rightsibling

512

513

# Import utility module

514

from anytree import util

515

516

# Use utilities

517

ancestors = util.commonancestors(node1, node2)

518

left = util.leftsibling(node)

519

right = util.rightsibling(node)

520

```

521

522

### Cached Search Import

523

524

```python

525

# Import cached search module

526

from anytree import cachedsearch

527

528

# Use cached search functions

529

results = cachedsearch.findall_by_attr(root, "target", name="category")

530

node = cachedsearch.find(root, filter_=lambda n: n.value > 100)

531

```

532

533

## Best Practices

534

535

### When to Use Cached Search

536

537

- **Static or slowly-changing trees**: Maximum benefit from caching

538

- **Repeated similar queries**: Cache pays off with multiple searches

539

- **Large trees (1000+ nodes)**: Performance gains are significant

540

- **Interactive applications**: Better user experience with faster responses

541

542

### When to Use Regular Search

543

544

- **One-time queries**: Cache overhead not worth it

545

- **Frequently changing trees**: Cache invalidation overhead

546

- **Small trees (<100 nodes)**: Performance difference negligible

547

- **Memory-constrained environments**: Cache uses additional memory

548

549

### Utility Function Guidelines

550

551

- **Use `commonancestors`** for: relationship analysis, finding merge points, calculating distances

552

- **Use `leftsibling`/`rightsibling`** for: navigation UI, ordering operations, sequential processing

553

- **Combine utilities** for: complex tree manipulation, custom navigation logic, specialized algorithms