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

metadata.mddocs/

0

# Metadata Analysis

1

2

LibCST's metadata system attaches semantic information to CST nodes, enabling advanced static analysis capabilities including scope analysis, position tracking, qualified name resolution, and type inference. The system uses a provider architecture for extensible metadata computation.

3

4

## Capabilities

5

6

### Metadata Wrapper

7

8

Central coordinator for metadata computation and resolution.

9

10

```python { .api }

11

class MetadataWrapper:

12

"""Main interface for metadata-enabled CST analysis."""

13

14

def __init__(self, module: Module, cache: Optional[Dict] = None) -> None:

15

"""

16

Initialize metadata wrapper for a module.

17

18

Parameters:

19

- module: CST module to analyze

20

- cache: Optional cache for metadata providers

21

"""

22

23

def resolve(self, provider: Type[BaseMetadataProvider]) -> Mapping[CSTNode, Any]:

24

"""

25

Resolve metadata for all nodes using the specified provider.

26

27

Parameters:

28

- provider: Metadata provider class

29

30

Returns:

31

Mapping[CSTNode, Any]: Metadata values by node

32

"""

33

34

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

35

"""

36

Resolve multiple metadata providers efficiently.

37

38

Parameters:

39

- providers: Sequence of provider classes

40

41

Returns:

42

Dict[Type[BaseMetadataProvider], Mapping[CSTNode, Any]]: Results by provider

43

"""

44

45

def visit(self, visitor: MetadataDependent) -> CSTNode:

46

"""

47

Visit module with metadata-dependent visitor.

48

49

Parameters:

50

- visitor: Visitor that requires metadata

51

52

Returns:

53

CSTNode: Potentially transformed module

54

"""

55

```

56

57

### Position Tracking

58

59

Track source code positions for CST nodes.

60

61

```python { .api }

62

class PositionProvider(BaseMetadataProvider):

63

"""Provides line/column positions for CST nodes."""

64

65

class WhitespaceInclusivePositionProvider(BaseMetadataProvider):

66

"""Provides positions including leading/trailing whitespace."""

67

68

class ByteSpanPositionProvider(BaseMetadataProvider):

69

"""Provides byte-based position spans."""

70

71

class CodePosition:

72

"""Represents a line/column position in source code."""

73

line: int

74

column: int

75

76

class CodeRange:

77

"""Represents a start/end position range."""

78

start: CodePosition

79

end: CodePosition

80

81

class CodeSpan:

82

"""Represents a byte-based span."""

83

start: int

84

length: int

85

```

86

87

### Scope Analysis

88

89

Analyze variable scopes, assignments, and accesses.

90

91

```python { .api }

92

class ScopeProvider(BaseMetadataProvider):

93

"""Analyzes variable scopes and assignments."""

94

95

class Scope:

96

"""Base class for all scope types."""

97

parent: Optional["Scope"]

98

99

def __contains__(self, name: str) -> bool:

100

"""Check if name is defined in this scope."""

101

102

def __getitem__(self, name: str) -> Collection["BaseAssignment"]:

103

"""Get assignments for a name in this scope."""

104

105

class GlobalScope(Scope):

106

"""Module-level scope."""

107

108

class FunctionScope(Scope):

109

"""Function-level scope."""

110

name: str

111

112

class ClassScope(Scope):

113

"""Class-level scope."""

114

name: str

115

116

class ComprehensionScope(Scope):

117

"""Comprehension-level scope."""

118

119

class BuiltinScope(Scope):

120

"""Built-in names scope."""

121

122

class Assignment:

123

"""Tracks variable assignments."""

124

name: str

125

node: CSTNode

126

scope: Scope

127

128

class BuiltinAssignment(Assignment):

129

"""Built-in variable assignment."""

130

131

class ImportAssignment(Assignment):

132

"""Import-based assignment."""

133

134

class Access:

135

"""Tracks variable access."""

136

name: str

137

node: CSTNode

138

scope: Scope

139

140

# Type aliases

141

Accesses = Collection[Access]

142

Assignments = Collection[BaseAssignment]

143

```

144

145

### Qualified Names

146

147

Resolve qualified names for imported and defined symbols.

148

149

```python { .api }

150

class QualifiedNameProvider(BaseMetadataProvider):

151

"""Provides qualified names for nodes."""

152

153

class FullyQualifiedNameProvider(BaseMetadataProvider):

154

"""Provides fully qualified names including module paths."""

155

156

class QualifiedName:

157

"""Represents a qualified name."""

158

name: str

159

source: QualifiedNameSource

160

161

class QualifiedNameSource:

162

"""Source information for qualified names."""

163

IMPORT: ClassVar[int]

164

BUILTIN: ClassVar[int]

165

LOCAL: ClassVar[int]

166

```

167

168

### Expression Context

169

170

Determine Load/Store/Del context for expressions.

171

172

```python { .api }

173

class ExpressionContextProvider(BaseMetadataProvider):

174

"""Determines expression context (Load/Store/Del)."""

175

176

class ExpressionContext:

177

"""Expression context enumeration."""

178

LOAD: ClassVar[int]

179

STORE: ClassVar[int]

180

DEL: ClassVar[int]

181

```

182

183

### Parent Node Relationships

184

185

Track parent-child relationships in the CST.

186

187

```python { .api }

188

class ParentNodeProvider(BaseMetadataProvider):

189

"""Provides parent node relationships."""

190

```

191

192

### Advanced Providers

193

194

Additional metadata providers for specialized analysis.

195

196

```python { .api }

197

class TypeInferenceProvider(BaseMetadataProvider):

198

"""Experimental type inference provider."""

199

200

class FullRepoManager:

201

"""Manager for cross-file analysis."""

202

203

def __init__(self, repo_root: str, paths: Sequence[str], providers: Sequence[Type[BaseMetadataProvider]]) -> None:

204

"""

205

Initialize repository-wide analysis.

206

207

Parameters:

208

- repo_root: Root directory of repository

209

- paths: Python files to analyze

210

- providers: Metadata providers to use

211

"""

212

213

class AccessorProvider(BaseMetadataProvider):

214

"""Provides accessor metadata."""

215

216

class FilePathProvider(BaseMetadataProvider):

217

"""Provides file path information."""

218

```

219

220

### Provider Base Classes

221

222

Foundation for implementing custom metadata providers.

223

224

```python { .api }

225

class BaseMetadataProvider:

226

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

227

228

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

229

"""Visit module node."""

230

231

class BatchableMetadataProvider(BaseMetadataProvider):

232

"""Base class for batchable providers."""

233

234

class VisitorMetadataProvider(BaseMetadataProvider):

235

"""Base class for visitor-based providers."""

236

237

ProviderT = TypeVar("ProviderT", bound=BaseMetadataProvider)

238

```

239

240

## Usage Examples

241

242

### Basic Position Tracking

243

244

```python

245

import libcst as cst

246

from libcst.metadata import MetadataWrapper, PositionProvider

247

248

source = '''

249

def foo():

250

x = 42

251

return x

252

'''

253

254

module = cst.parse_module(source)

255

wrapper = MetadataWrapper(module)

256

positions = wrapper.resolve(PositionProvider)

257

258

# Find positions of all Name nodes

259

for node in cst.metadata.findall(module, cst.Name):

260

if node in positions:

261

pos = positions[node]

262

print(f"Name '{node.value}' at line {pos.start.line}, column {pos.start.column}")

263

```

264

265

### Scope Analysis

266

267

```python

268

import libcst as cst

269

from libcst.metadata import MetadataWrapper, ScopeProvider

270

271

source = '''

272

x = "global"

273

274

def outer():

275

y = "outer"

276

277

def inner():

278

z = "inner"

279

print(x, y, z) # Access variables from different scopes

280

281

inner()

282

283

outer()

284

'''

285

286

module = cst.parse_module(source)

287

wrapper = MetadataWrapper(module)

288

scopes = wrapper.resolve(ScopeProvider)

289

290

# Analyze variable assignments and accesses

291

for node, scope in scopes.items():

292

if isinstance(node, cst.Name):

293

scope_type = type(scope).__name__

294

print(f"Name '{node.value}' in {scope_type}")

295

296

# Check if this is an assignment or access

297

if node.value in scope:

298

assignments = scope[node.value]

299

print(f" Has {len(assignments)} assignments in this scope")

300

```

301

302

### Qualified Name Resolution

303

304

```python

305

import libcst as cst

306

from libcst.metadata import MetadataWrapper, QualifiedNameProvider

307

308

source = '''

309

import os.path

310

from collections import Counter

311

from .local import helper

312

313

def process():

314

return os.path.join("a", "b")

315

316

def analyze(data):

317

return Counter(data)

318

319

def work():

320

return helper.process()

321

'''

322

323

module = cst.parse_module(source)

324

wrapper = MetadataWrapper(module)

325

qualified_names = wrapper.resolve(QualifiedNameProvider)

326

327

# Find qualified names for all function calls

328

for node in cst.matchers.findall(module, cst.Call()):

329

if node.func in qualified_names:

330

qnames = qualified_names[node.func]

331

for qname in qnames:

332

print(f"Call to: {qname.name}")

333

```

334

335

### Metadata-Dependent Visitor

336

337

```python

338

import libcst as cst

339

from libcst.metadata import MetadataWrapper, ScopeProvider, PositionProvider

340

341

class VariableTracker(cst.MetadataDependent):

342

METADATA_DEPENDENCIES = (ScopeProvider, PositionProvider)

343

344

def __init__(self):

345

self.assignments = []

346

self.accesses = []

347

348

def visit_Name(self, node):

349

scope = self.resolve(ScopeProvider)[node]

350

position = self.resolve(PositionProvider)[node]

351

352

# Determine if this is assignment or access based on context

353

# (simplified - real implementation would check expression context)

354

if isinstance(node.parent, cst.Assign) and node in node.parent.targets:

355

self.assignments.append({

356

'name': node.value,

357

'line': position.start.line,

358

'scope': type(scope).__name__

359

})

360

else:

361

self.accesses.append({

362

'name': node.value,

363

'line': position.start.line,

364

'scope': type(scope).__name__

365

})

366

367

# Usage

368

source = '''

369

def example():

370

x = 1 # Assignment

371

y = x + 2 # Access to x, assignment to y

372

return y # Access to y

373

'''

374

375

module = cst.parse_module(source)

376

wrapper = MetadataWrapper(module)

377

tracker = VariableTracker()

378

wrapper.visit(tracker)

379

380

print("Assignments:", tracker.assignments)

381

print("Accesses:", tracker.accesses)

382

```

383

384

### Cross-File Analysis

385

386

```python

387

from libcst.metadata import FullRepoManager, ScopeProvider, QualifiedNameProvider

388

389

# Analyze multiple files in a repository

390

manager = FullRepoManager(

391

repo_root="/path/to/repo",

392

paths=["module1.py", "module2.py", "package/__init__.py"],

393

providers=[ScopeProvider, QualifiedNameProvider]

394

)

395

396

# Get metadata for all files

397

repo_metadata = manager.get_cache()

398

for file_path, file_metadata in repo_metadata.items():

399

print(f"Analyzing {file_path}")

400

for provider, node_metadata in file_metadata.items():

401

print(f" {provider.__name__}: {len(node_metadata)} nodes")

402

```

403

404

## Types

405

406

```python { .api }

407

# Position types

408

class CodePosition:

409

line: int

410

column: int

411

412

class CodeRange:

413

start: CodePosition

414

end: CodePosition

415

416

class CodeSpan:

417

start: int

418

length: int

419

420

# Scope types

421

class Scope:

422

parent: Optional["Scope"]

423

424

class GlobalScope(Scope): ...

425

class FunctionScope(Scope):

426

name: str

427

class ClassScope(Scope):

428

name: str

429

class ComprehensionScope(Scope): ...

430

class BuiltinScope(Scope): ...

431

432

# Assignment and access types

433

class BaseAssignment:

434

name: str

435

node: CSTNode

436

scope: Scope

437

438

class Assignment(BaseAssignment): ...

439

class BuiltinAssignment(BaseAssignment): ...

440

class ImportAssignment(BaseAssignment): ...

441

442

class Access:

443

name: str

444

node: CSTNode

445

scope: Scope

446

447

Accesses = Collection[Access]

448

Assignments = Collection[BaseAssignment]

449

450

# Qualified name types

451

class QualifiedName:

452

name: str

453

source: QualifiedNameSource

454

455

class QualifiedNameSource:

456

IMPORT: ClassVar[int]

457

BUILTIN: ClassVar[int]

458

LOCAL: ClassVar[int]

459

460

# Expression context

461

class ExpressionContext:

462

LOAD: ClassVar[int]

463

STORE: ClassVar[int]

464

DEL: ClassVar[int]

465

466

# Provider types

467

ProviderT = TypeVar("ProviderT", bound=BaseMetadataProvider)

468

469

class BaseMetadataProvider:

470

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

471

472

class BatchableMetadataProvider(BaseMetadataProvider): ...

473

class VisitorMetadataProvider(BaseMetadataProvider): ...

474

475

# Exception types

476

class MetadataException(Exception):

477

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

478

```