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

utilities.mddocs/

0

# LibCST Utilities

1

2

LibCST provides a comprehensive suite of utility functions to help developers work with Concrete Syntax Trees efficiently. These utilities cover everything from module resolution and template parsing to node introspection and testing helpers.

3

4

## Helper Functions

5

6

LibCST's helper functions provide essential utilities for working with CST nodes, module imports, templates, and more.

7

8

### Module and Package Utilities

9

10

These functions help with module resolution, import handling, and calculating module paths:

11

12

```python { .api }

13

from libcst.helpers import (

14

calculate_module_and_package,

15

get_absolute_module,

16

get_absolute_module_for_import,

17

get_absolute_module_for_import_or_raise,

18

get_absolute_module_from_package,

19

get_absolute_module_from_package_for_import,

20

get_absolute_module_from_package_for_import_or_raise,

21

insert_header_comments,

22

ModuleNameAndPackage,

23

)

24

```

25

26

**calculate_module_and_package**

27

```python

28

def calculate_module_and_package(

29

repo_root: StrPath,

30

filename: StrPath,

31

use_pyproject_toml: bool = False

32

) -> ModuleNameAndPackage

33

```

34

35

Calculates the Python module name and package for a given file path relative to a repository root.

36

37

```python

38

from libcst.helpers import calculate_module_and_package

39

from pathlib import Path

40

41

# Calculate module info for a file

42

result = calculate_module_and_package(

43

repo_root="/project/root",

44

filename="/project/root/src/mypackage/utils.py"

45

)

46

# result.name = "src.mypackage.utils"

47

# result.package = "src.mypackage"

48

```

49

50

**get_absolute_module**

51

```python

52

def get_absolute_module(

53

current_module: Optional[str],

54

module_name: Optional[str],

55

num_dots: int

56

) -> Optional[str]

57

```

58

59

Resolves relative imports to absolute module names based on the current module and number of dots.

60

61

```python

62

from libcst.helpers import get_absolute_module

63

64

# Resolve relative import

65

absolute = get_absolute_module(

66

current_module="mypackage.submodule.file",

67

module_name="utils",

68

num_dots=2 # from ..utils import something

69

)

70

# Returns: "mypackage.utils"

71

```

72

73

**get_absolute_module_for_import**

74

```python

75

def get_absolute_module_for_import(

76

current_module: Optional[str],

77

import_node: ImportFrom

78

) -> Optional[str]

79

```

80

81

Extracts and resolves the absolute module name from an ImportFrom CST node.

82

83

**insert_header_comments**

84

```python

85

def insert_header_comments(node: Module, comments: List[str]) -> Module

86

```

87

88

Inserts comments after the last non-empty line in a module header, useful for adding copyright notices or other header comments.

89

90

```python

91

import libcst as cst

92

from libcst.helpers import insert_header_comments

93

94

# Parse a module

95

module = cst.parse_module("print('hello')")

96

97

# Add header comments

98

new_module = insert_header_comments(module, [

99

"# Copyright 2024 MyCompany",

100

"# Licensed under MIT"

101

])

102

```

103

104

### Template Parsing Functions

105

106

Template functions allow you to parse Python code templates with variable substitution:

107

108

```python { .api }

109

from libcst.helpers import (

110

parse_template_expression,

111

parse_template_module,

112

parse_template_statement,

113

)

114

```

115

116

**parse_template_module**

117

```python

118

def parse_template_module(

119

template: str,

120

config: PartialParserConfig = _DEFAULT_PARTIAL_PARSER_CONFIG,

121

**template_replacements: ValidReplacementType,

122

) -> Module

123

```

124

125

Parses an entire Python module template with variable substitution.

126

127

```python

128

import libcst as cst

129

from libcst.helpers import parse_template_module

130

131

# Parse a module template with substitutions

132

module = parse_template_module(

133

"from {mod} import {name}\n\ndef {func}():\n return {value}",

134

mod=cst.Name("math"),

135

name=cst.Name("pi"),

136

func=cst.Name("get_pi"),

137

value=cst.Name("pi")

138

)

139

```

140

141

**parse_template_statement**

142

```python

143

def parse_template_statement(

144

template: str,

145

config: PartialParserConfig = _DEFAULT_PARTIAL_PARSER_CONFIG,

146

**template_replacements: ValidReplacementType,

147

) -> Union[SimpleStatementLine, BaseCompoundStatement]

148

```

149

150

Parses a statement template with variable substitution.

151

152

```python

153

from libcst.helpers import parse_template_statement

154

155

# Parse a statement template

156

stmt = parse_template_statement(

157

"assert {condition}, {message}",

158

condition=cst.parse_expression("x > 0"),

159

message=cst.SimpleString('"Value must be positive"')

160

)

161

```

162

163

**parse_template_expression**

164

```python

165

def parse_template_expression(

166

template: str,

167

config: PartialParserConfig = _DEFAULT_PARTIAL_PARSER_CONFIG,

168

**template_replacements: ValidReplacementType,

169

) -> BaseExpression

170

```

171

172

Parses an expression template with variable substitution.

173

174

```python

175

from libcst.helpers import parse_template_expression

176

177

# Parse an expression template

178

expr = parse_template_expression(

179

"{left} + {right}",

180

left=cst.Name("x"),

181

right=cst.Integer("42")

182

)

183

```

184

185

### Node Introspection Utilities

186

187

These functions help examine and analyze CST node structure:

188

189

```python { .api }

190

from libcst.helpers import (

191

get_node_fields,

192

get_field_default_value,

193

is_whitespace_node_field,

194

is_syntax_node_field,

195

is_default_node_field,

196

filter_node_fields,

197

)

198

```

199

200

**get_node_fields**

201

```python

202

def get_node_fields(node: CSTNode) -> Sequence[dataclasses.Field[CSTNode]]

203

```

204

205

Returns all dataclass fields for a CST node.

206

207

```python

208

import libcst as cst

209

from libcst.helpers import get_node_fields

210

211

# Get all fields of a Name node

212

name_node = cst.Name("variable")

213

fields = get_node_fields(name_node)

214

for field in fields:

215

print(f"Field: {field.name}, Type: {field.type}")

216

```

217

218

**filter_node_fields**

219

```python

220

def filter_node_fields(

221

node: CSTNode,

222

*,

223

show_defaults: bool,

224

show_syntax: bool,

225

show_whitespace: bool,

226

) -> Sequence[dataclasses.Field[CSTNode]]

227

```

228

229

Returns a filtered list of node fields based on various criteria.

230

231

```python

232

from libcst.helpers import filter_node_fields

233

234

# Get only non-default, non-whitespace fields

235

filtered_fields = filter_node_fields(

236

node,

237

show_defaults=False,

238

show_syntax=True,

239

show_whitespace=False

240

)

241

```

242

243

### Name Resolution Helpers

244

245

Functions for extracting qualified names from CST nodes:

246

247

```python { .api }

248

from libcst.helpers import (

249

get_full_name_for_node,

250

get_full_name_for_node_or_raise,

251

)

252

```

253

254

**get_full_name_for_node**

255

```python

256

def get_full_name_for_node(node: Union[str, CSTNode]) -> Optional[str]

257

```

258

259

Extracts the full qualified name from various CST node types (Name, Attribute, Call, etc.).

260

261

```python

262

import libcst as cst

263

from libcst.helpers import get_full_name_for_node

264

265

# Extract name from various node types

266

attr_node = cst.parse_expression("os.path.join")

267

name = get_full_name_for_node(attr_node) # Returns: "os.path.join"

268

269

call_node = cst.parse_expression("math.sqrt(4)")

270

name = get_full_name_for_node(call_node) # Returns: "math.sqrt"

271

```

272

273

### Type Checking Utilities

274

275

```python { .api }

276

from libcst.helpers import ensure_type

277

```

278

279

**ensure_type**

280

```python

281

def ensure_type(node: object, nodetype: Type[T]) -> T

282

```

283

284

Type-safe casting with runtime validation for CST nodes.

285

286

```python

287

from libcst.helpers import ensure_type

288

import libcst as cst

289

290

# Safely cast to a specific node type

291

expr = cst.parse_expression("variable_name")

292

name_node = ensure_type(expr, cst.Name) # Validates it's actually a Name node

293

```

294

295

### Path Utilities

296

297

```python { .api }

298

from libcst.helpers.paths import chdir

299

```

300

301

**chdir**

302

```python

303

@contextmanager

304

def chdir(path: StrPath) -> Generator[Path, None, None]

305

```

306

307

Context manager for temporarily changing the working directory.

308

309

```python

310

from libcst.helpers.paths import chdir

311

from pathlib import Path

312

313

# Temporarily change to another directory

314

with chdir("/some/other/path") as new_path:

315

# Work in the new directory

316

current = Path.cwd() # Points to /some/other/path

317

# Automatically returns to original directory

318

```

319

320

### Matcher Conversion Utilities

321

322

```python { .api }

323

from libcst.helpers.matchers import node_to_matcher

324

```

325

326

**node_to_matcher**

327

```python

328

def node_to_matcher(

329

node: CSTNode,

330

*,

331

match_syntactic_trivia: bool = False

332

) -> matchers.BaseMatcherNode

333

```

334

335

Converts a concrete CST node into a matcher for pattern matching.

336

337

```python

338

import libcst as cst

339

from libcst.helpers.matchers import node_to_matcher

340

341

# Convert node to matcher

342

node = cst.parse_expression("variable_name")

343

matcher = node_to_matcher(node)

344

345

# Use matcher to find similar patterns

346

# (useful in codemods and analysis tools)

347

```

348

349

## Display and Debugging Utilities

350

351

LibCST provides utilities for visualizing and debugging CST structures.

352

353

### Text Display

354

355

```python { .api }

356

from libcst.display import dump

357

```

358

359

**dump**

360

```python

361

def dump(

362

node: CSTNode,

363

*,

364

indent: str = " ",

365

show_defaults: bool = False,

366

show_syntax: bool = False,

367

show_whitespace: bool = False,

368

) -> str

369

```

370

371

Returns a string representation of a CST node with configurable detail levels.

372

373

```python

374

import libcst as cst

375

from libcst.display import dump

376

377

# Parse some code

378

node = cst.parse_expression("x + y * 2")

379

380

# Basic representation (minimal)

381

print(dump(node))

382

383

# Detailed representation showing all fields

384

print(dump(node, show_defaults=True, show_syntax=True, show_whitespace=True))

385

386

# Output example:

387

# BinaryOperation(

388

# left=Name("x"),

389

# operator=Add(),

390

# right=BinaryOperation(

391

# left=Name("y"),

392

# operator=Multiply(),

393

# right=Integer("2")

394

# )

395

# )

396

```

397

398

### GraphViz Visualization

399

400

```python { .api }

401

from libcst.display import dump_graphviz

402

```

403

404

**dump_graphviz**

405

```python

406

def dump_graphviz(

407

node: object,

408

*,

409

show_defaults: bool = False,

410

show_syntax: bool = False,

411

show_whitespace: bool = False,

412

) -> str

413

```

414

415

Generates a GraphViz .dot representation for visualizing CST structure as a graph.

416

417

```python

418

import libcst as cst

419

from libcst.display import dump_graphviz

420

421

# Parse some code

422

node = cst.parse_expression("len(items)")

423

424

# Generate GraphViz representation

425

dot_content = dump_graphviz(node)

426

427

# Save to file and render with GraphViz tools

428

with open("ast_graph.dot", "w") as f:

429

f.write(dot_content)

430

431

# Then use: dot -Tpng ast_graph.dot -o ast_graph.png

432

```

433

434

The generated GraphViz output can be rendered to various formats (PNG, SVG, PDF) using GraphViz tools, providing visual tree representations of CST structures that are invaluable for understanding complex code structures.

435

436

## Testing Utilities

437

438

LibCST provides comprehensive testing utilities for unit tests and codemod development.

439

440

### Base Testing Framework

441

442

```python { .api }

443

from libcst.testing import UnitTest

444

from libcst.testing.utils import data_provider, none_throws

445

```

446

447

**UnitTest**

448

```python

449

class UnitTest(TestCase, metaclass=BaseTestMeta)

450

```

451

452

Enhanced unittest.TestCase with data provider support and other testing conveniences.

453

454

```python

455

from libcst.testing import UnitTest, data_provider

456

457

class MyTests(UnitTest):

458

@data_provider([

459

("input1", "expected1"),

460

("input2", "expected2"),

461

])

462

def test_with_data(self, input_val: str, expected: str) -> None:

463

# Test implementation

464

pass

465

```

466

467

**data_provider**

468

```python

469

def data_provider(

470

static_data: StaticDataType,

471

*,

472

test_limit: int = DEFAULT_TEST_LIMIT

473

) -> Callable[[Callable], Callable]

474

```

475

476

Decorator that generates multiple test methods from a data set.

477

478

```python

479

from libcst.testing.utils import data_provider

480

481

@data_provider([

482

{"name": "Alice", "age": 30},

483

{"name": "Bob", "age": 25},

484

])

485

def test_person_data(self, name: str, age: int) -> None:

486

# This creates test_person_data_0 and test_person_data_1

487

pass

488

```

489

490

**none_throws**

491

```python

492

def none_throws(value: Optional[T], message: str = "Unexpected None value") -> T

493

```

494

495

Assertion helper for non-None values with better error messages.

496

497

```python

498

from libcst.testing.utils import none_throws

499

500

def test_something(self) -> None:

501

result = get_optional_value()

502

# Safely unwrap Optional value with clear error message

503

actual_value = none_throws(result, "Expected non-None result from get_optional_value")

504

```

505

506

### Codemod Testing

507

508

```python { .api }

509

from libcst.codemod import CodemodTest

510

```

511

512

**CodemodTest**

513

```python

514

class CodemodTest(_CodemodTest, UnitTest):

515

TRANSFORM: Type[Codemod] = ... # Set to your codemod class

516

```

517

518

Base class for testing codemods with convenient assertion methods.

519

520

```python

521

from libcst.codemod import CodemodTest

522

from my_codemod import MyCodemod

523

524

class TestMyCodemod(CodemodTest):

525

TRANSFORM = MyCodemod

526

527

def test_simple_transformation(self) -> None:

528

# Test a codemod transformation

529

self.assertCodemod(

530

before="""

531

def old_function():

532

pass

533

""",

534

after="""

535

def new_function():

536

pass

537

""",

538

# Any arguments to pass to the codemod constructor

539

)

540

```

541

542

**assertCodemod**

543

```python

544

def assertCodemod(

545

self,

546

before: str,

547

after: str,

548

*args: object,

549

context_override: Optional[CodemodContext] = None,

550

python_version: Optional[str] = None,

551

expected_warnings: Optional[Sequence[str]] = None,

552

expected_skip: bool = False,

553

**kwargs: object,

554

) -> None

555

```

556

557

Comprehensive codemod testing with support for warnings, skip conditions, and custom contexts.

558

559

```python

560

def test_codemod_with_warnings(self) -> None:

561

self.assertCodemod(

562

before="old_code",

563

after="new_code",

564

expected_warnings=["Deprecated function usage detected"],

565

python_version="3.8"

566

)

567

568

def test_codemod_skip(self) -> None:

569

self.assertCodemod(

570

before="code_that_should_be_skipped",

571

after="code_that_should_be_skipped", # Same as before

572

expected_skip=True

573

)

574

```

575

576

**assertCodeEqual**

577

```python

578

def assertCodeEqual(self, expected: str, actual: str) -> None

579

```

580

581

Code-aware string comparison that normalizes whitespace and handles multi-line strings.

582

583

**make_fixture_data**

584

```python

585

@staticmethod

586

def make_fixture_data(data: str) -> str

587

```

588

589

Normalizes test fixture code by removing leading/trailing whitespace and ensuring proper newlines.

590

591

```python

592

def test_fixture_normalization(self) -> None:

593

# These strings are automatically normalized

594

code = self.make_fixture_data("""

595

def example():

596

return True

597

""")

598

# Becomes: "def example():\n return True\n"

599

```

600

601

## Usage Examples

602

603

### Complete Workflow Example

604

605

Here's a comprehensive example showing how to use various LibCST utilities together:

606

607

```python

608

import libcst as cst

609

from libcst.helpers import (

610

parse_template_module,

611

get_full_name_for_node,

612

calculate_module_and_package,

613

insert_header_comments

614

)

615

from libcst.display import dump

616

from libcst.testing import UnitTest, data_provider

617

618

class CodeAnalysisExample(UnitTest):

619

620

@data_provider([

621

("simple_function", "def foo(): pass"),

622

("class_definition", "class Bar: pass"),

623

])

624

def test_code_analysis(self, name: str, code: str) -> None:

625

# Parse the code

626

module = cst.parse_module(code)

627

628

# Analyze and display structure

629

print(f"Analysis of {name}:")

630

print(dump(module, show_syntax=True))

631

632

# Extract function/class names

633

class NameCollector(cst.CSTVisitor):

634

def __init__(self) -> None:

635

self.names: List[str] = []

636

637

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

638

name = get_full_name_for_node(node.name)

639

if name:

640

self.names.append(f"function: {name}")

641

642

def visit_ClassDef(self, node: cst.ClassDef) -> None:

643

name = get_full_name_for_node(node.name)

644

if name:

645

self.names.append(f"class: {name}")

646

647

collector = NameCollector()

648

module.visit(collector)

649

650

# Verify we found expected definitions

651

self.assertGreater(len(collector.names), 0)

652

653

def generate_boilerplate_code():

654

"""Example of generating code with templates"""

655

656

# Create a module template

657

template = """

658

{header_comment}

659

660

from typing import {type_imports}

661

662

class {class_name}:

663

def __init__(self, {init_params}) -> None:

664

{init_body}

665

666

def {method_name}(self) -> {return_type}:

667

{method_body}

668

"""

669

670

# Generate code using template

671

module = parse_template_module(

672

template,

673

header_comment=cst.SimpleStatementLine([

674

cst.Expr(cst.SimpleString('"""Generated class module."""'))

675

]),

676

type_imports=cst.Name("Optional, List"),

677

class_name=cst.Name("DataProcessor"),

678

init_params=cst.Parameters([

679

cst.Param(cst.Name("data"), cst.Annotation(cst.Name("List[str]")))

680

]),

681

init_body=cst.SimpleStatementLine([

682

cst.Assign([cst.AssignTarget(cst.Attribute(cst.Name("self"), cst.Name("data")))], cst.Name("data"))

683

]),

684

method_name=cst.Name("process"),

685

return_type=cst.Name("Optional[str]"),

686

method_body=cst.SimpleStatementLine([

687

cst.Return(cst.Name("None"))

688

])

689

)

690

691

# Add copyright header

692

final_module = insert_header_comments(module, [

693

"# Copyright 2024 Example Corp",

694

"# Auto-generated code - do not edit manually"

695

])

696

697

return final_module.code

698

699

if __name__ == "__main__":

700

# Generate and print example code

701

generated_code = generate_boilerplate_code()

702

print("Generated Code:")

703

print(generated_code)

704

705

# Calculate module information

706

module_info = calculate_module_and_package(

707

repo_root="/project/root",

708

filename="/project/root/src/generated/processor.py"

709

)

710

print(f"\nModule: {module_info.name}")

711

print(f"Package: {module_info.package}")

712

```

713

714

### Advanced Template Usage

715

716

```python

717

from libcst.helpers import parse_template_statement, parse_template_expression

718

719

def create_error_handling_wrapper():

720

"""Create error handling code using templates"""

721

722

# Template for try-catch wrapper

723

try_template = """

724

try:

725

{original_call}

726

except {exception_type} as e:

727

{error_handler}

728

"""

729

730

# Create the wrapped statement

731

wrapped = parse_template_statement(

732

try_template,

733

original_call=parse_template_expression("process_data(input_value)"),

734

exception_type=cst.Name("ValueError"),

735

error_handler=parse_template_statement(

736

'logger.error("Processing failed: %s", str(e))'

737

)

738

)

739

740

return wrapped

741

```

742

743

This comprehensive utilities documentation provides developers with all the tools they need to work effectively with LibCST, from basic node manipulation to advanced code generation and testing workflows.