or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-tool.mdcodemods.mdindex.mdmatchers.mdmetadata.mdnodes.mdparsing.mdutilities.mdvisitors.md

visitors.mddocs/

0

# Visitor Framework

1

2

LibCST's visitor framework provides a powerful pattern for traversing and transforming concrete syntax trees. The framework supports read-only analysis, tree transformation, metadata-dependent processing, and batched operations for performance optimization.

3

4

## Capabilities

5

6

### Basic Visitor

7

8

Read-only traversal of CST trees for analysis and data collection.

9

10

```python { .api }

11

class CSTVisitor:

12

"""Base class for read-only CST traversal."""

13

14

def visit(self, node: CSTNode) -> None:

15

"""Called when entering a node during traversal."""

16

17

def leave(self, node: CSTNode) -> None:

18

"""Called when leaving a node during traversal."""

19

20

# Node-specific visit methods (examples)

21

def visit_Module(self, node: Module) -> None: ...

22

def visit_FunctionDef(self, node: FunctionDef) -> None: ...

23

def visit_Name(self, node: Name) -> None: ...

24

def visit_Call(self, node: Call) -> None: ...

25

26

# Node-specific leave methods (examples)

27

def leave_Module(self, node: Module) -> None: ...

28

def leave_FunctionDef(self, node: FunctionDef) -> None: ...

29

def leave_Name(self, node: Name) -> None: ...

30

def leave_Call(self, node: Call) -> None: ...

31

```

32

33

### Transformer

34

35

Transform CST trees by replacing nodes during traversal.

36

37

```python { .api }

38

class CSTTransformer(CSTVisitor):

39

"""Base class for CST transformation."""

40

41

def leave(self, original_node: CSTNode, updated_node: CSTNode) -> CSTNode:

42

"""

43

Transform node during traversal.

44

45

Parameters:

46

- original_node: Original node before any child transformations

47

- updated_node: Node with transformed children

48

49

Returns:

50

CSTNode: Replacement node or updated_node to keep unchanged

51

"""

52

53

# Node-specific transformation methods (examples)

54

def leave_Module(self, original_node: Module, updated_node: Module) -> Module: ...

55

def leave_FunctionDef(self, original_node: FunctionDef, updated_node: FunctionDef) -> Union[FunctionDef, RemovalSentinel]: ...

56

def leave_Name(self, original_node: Name, updated_node: Name) -> Name: ...

57

def leave_Call(self, original_node: Call, updated_node: Call) -> Union[Call, BaseExpression]: ...

58

```

59

60

### Metadata-Dependent Analysis

61

62

Access semantic information during traversal using metadata providers.

63

64

```python { .api }

65

class MetadataDependent:

66

"""Base class for visitors that depend on metadata."""

67

68

METADATA_DEPENDENCIES: Sequence[Type[BaseMetadataProvider]] = ()

69

70

def resolve(self, node: CSTNode) -> Any:

71

"""

72

Resolve metadata for a node.

73

74

Parameters:

75

- node: CST node to resolve metadata for

76

77

Returns:

78

Any: Metadata value for the node

79

"""

80

81

def resolve_many(self, providers: Sequence[Type[BaseMetadataProvider]]) -> Dict[Type[BaseMetadataProvider], Mapping[CSTNode, Any]]:

82

"""Resolve multiple metadata providers at once."""

83

```

84

85

### Batched Visitor

86

87

Process multiple nodes efficiently with batched operations.

88

89

```python { .api }

90

class BatchableCSTVisitor(CSTVisitor):

91

"""Base class for visitors that support batched processing."""

92

93

def on_visit(self, node: CSTNode) -> bool:

94

"""

95

Determine if this visitor should process the node.

96

97

Parameters:

98

- node: Node being visited

99

100

Returns:

101

bool: True if visitor should process this node type

102

"""

103

104

def on_leave(self, original_node: CSTNode) -> None:

105

"""Process node when leaving during batched traversal."""

106

107

def visit_batched(

108

nodes: Sequence[CSTNode],

109

visitors: Sequence[BatchableCSTVisitor]

110

) -> None:

111

"""

112

Visit multiple nodes with multiple visitors efficiently.

113

114

Parameters:

115

- nodes: Sequence of CST nodes to visit

116

- visitors: Sequence of visitors to apply

117

"""

118

```

119

120

### Node Removal

121

122

Remove nodes from the tree during transformation.

123

124

```python { .api }

125

class RemovalSentinel:

126

"""Sentinel type for indicating node removal."""

127

128

# Global instance for node removal

129

RemoveFromParent: RemovalSentinel

130

131

class FlattenSentinel:

132

"""Sentinel type for flattening sequences."""

133

134

class MaybeSentinel:

135

"""Sentinel type for optional values."""

136

```

137

138

## Usage Examples

139

140

### Basic Analysis Visitor

141

142

```python

143

import libcst as cst

144

145

class FunctionAnalyzer(cst.CSTVisitor):

146

def __init__(self):

147

self.function_names = []

148

self.call_counts = {}

149

150

def visit_FunctionDef(self, node):

151

self.function_names.append(node.name.value)

152

153

def visit_Call(self, node):

154

if isinstance(node.func, cst.Name):

155

name = node.func.value

156

self.call_counts[name] = self.call_counts.get(name, 0) + 1

157

158

# Use the visitor

159

source = '''

160

def foo():

161

bar()

162

baz()

163

bar()

164

165

def qux():

166

foo()

167

'''

168

169

module = cst.parse_module(source)

170

analyzer = FunctionAnalyzer()

171

module.visit(analyzer)

172

173

print(analyzer.function_names) # ['foo', 'qux']

174

print(analyzer.call_counts) # {'bar': 2, 'baz': 1, 'foo': 1}

175

```

176

177

### Code Transformation

178

179

```python

180

import libcst as cst

181

182

class PrintReplacer(cst.CSTTransformer):

183

"""Replace print() calls with logging.info()."""

184

185

def leave_Call(self, original_node, updated_node):

186

if isinstance(updated_node.func, cst.Name) and updated_node.func.value == "print":

187

# Replace print with logging.info

188

new_func = cst.Attribute(

189

value=cst.Name("logging"),

190

attr=cst.Name("info")

191

)

192

return updated_node.with_changes(func=new_func)

193

return updated_node

194

195

# Transform code

196

source = '''

197

def greet(name):

198

print("Hello, " + name)

199

print("Welcome!")

200

'''

201

202

module = cst.parse_module(source)

203

transformer = PrintReplacer()

204

new_module = module.visit(transformer)

205

206

print(new_module.code)

207

# Output shows print() replaced with logging.info()

208

```

209

210

### Metadata-Dependent Visitor

211

212

```python

213

import libcst as cst

214

from libcst.metadata import ScopeProvider, Scope

215

216

class VariableAnalyzer(cst.CSTVisitor):

217

METADATA_DEPENDENCIES = (ScopeProvider,)

218

219

def __init__(self):

220

self.variables = {}

221

222

def visit_Name(self, node):

223

scope = self.resolve(ScopeProvider)

224

if isinstance(scope, cst.metadata.FunctionScope):

225

scope_name = scope.name

226

if scope_name not in self.variables:

227

self.variables[scope_name] = []

228

self.variables[scope_name].append(node.value)

229

230

# Use with metadata wrapper

231

from libcst.metadata import MetadataWrapper

232

233

source = '''

234

def func1():

235

x = 1

236

y = 2

237

238

def func2():

239

a = 3

240

b = 4

241

'''

242

243

module = cst.parse_module(source)

244

wrapper = MetadataWrapper(module)

245

analyzer = VariableAnalyzer()

246

wrapper.visit(analyzer)

247

```

248

249

### Node Removal

250

251

```python

252

import libcst as cst

253

254

class CommentRemover(cst.CSTTransformer):

255

"""Remove all comments from code."""

256

257

def leave_Comment(self, original_node, updated_node):

258

return cst.RemoveFromParent

259

260

# Usage

261

source = '''

262

# This is a comment

263

def foo(): # Another comment

264

return 42

265

'''

266

267

module = cst.parse_module(source)

268

transformer = CommentRemover()

269

new_module = module.visit(transformer)

270

# Comments will be removed from the output

271

```

272

273

## Types

274

275

```python { .api }

276

# Base visitor types

277

CSTNodeT = TypeVar("CSTNodeT", bound=CSTNode)

278

CSTVisitorT = TypeVar("CSTVisitorT", bound=CSTVisitor)

279

280

# Visitor base classes

281

class CSTVisitor:

282

"""Base class for read-only CST traversal."""

283

284

class CSTTransformer(CSTVisitor):

285

"""Base class for CST transformation."""

286

287

class BatchableCSTVisitor(CSTVisitor):

288

"""Base class for batched visitors."""

289

290

class MetadataDependent:

291

"""Base class for metadata-dependent operations."""

292

METADATA_DEPENDENCIES: Sequence[Type[BaseMetadataProvider]]

293

294

# Sentinel types for special operations

295

class RemovalSentinel:

296

"""Indicates a node should be removed."""

297

298

class FlattenSentinel:

299

"""Indicates a sequence should be flattened."""

300

301

class MaybeSentinel:

302

"""Represents an optional value."""

303

304

# Global sentinel instances

305

RemoveFromParent: RemovalSentinel

306

DoNotCareSentinel: Any

307

308

# Metadata provider base type

309

class BaseMetadataProvider:

310

"""Base class for all metadata providers."""

311

312

# Exception types

313

class MetadataException(Exception):

314

"""Raised for metadata-related errors."""

315

316

class CSTLogicError(Exception):

317

"""Raised for internal visitor logic errors."""

318

```