or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

builtin-libraries.mdconfiguration-variables.mdcore-execution.mdindex.mdlibrary-development.mdparsing-model.md

parsing-model.mddocs/

0

# Parsing and Model

1

2

Robot Framework provides comprehensive APIs for parsing test data, manipulating AST models, and programmatically working with test structure. These APIs enable custom parsers, test data transformation, and deep integration with Robot Framework's internal data structures.

3

4

## Capabilities

5

6

### Test Data Parsing

7

8

Parse Robot Framework test files into tokens and abstract syntax tree (AST) models.

9

10

```python { .api }

11

def get_model(source, **options):

12

"""

13

Parse test data into AST model.

14

15

Args:

16

source: Path to test file or string content

17

**options: Parsing options (language, etc.)

18

19

Returns:

20

File model object representing parsed content

21

"""

22

23

def get_resource_model(source, **options):

24

"""

25

Parse resource file into AST model.

26

27

Args:

28

source: Path to resource file or string content

29

**options: Parsing options

30

31

Returns:

32

File model object for resource file

33

"""

34

35

def get_init_model(source, **options):

36

"""

37

Parse suite initialization file into AST model.

38

39

Args:

40

source: Path to __init__.robot file or string content

41

**options: Parsing options

42

43

Returns:

44

File model object for initialization file

45

"""

46

47

def get_tokens(source, **options):

48

"""

49

Parse test data into tokens.

50

51

Args:

52

source: Path to test file or string content

53

**options: Parsing options

54

55

Returns:

56

Generator yielding Token objects

57

"""

58

59

def get_resource_tokens(source, **options):

60

"""Parse resource file into tokens."""

61

62

def get_init_tokens(source, **options):

63

"""Parse initialization file into tokens."""

64

```

65

66

**Usage Examples:**

67

68

```python

69

from robot.api.parsing import get_model, get_tokens

70

71

# Parse test file into AST model

72

model = get_model('tests/example.robot')

73

print(f"Sections: {len(model.sections)}")

74

75

for section in model.sections:

76

print(f"Section type: {type(section).__name__}")

77

if hasattr(section, 'body'):

78

print(f" Items: {len(section.body)}")

79

80

# Parse into tokens for low-level processing

81

tokens = list(get_tokens('tests/example.robot'))

82

for token in tokens[:10]: # First 10 tokens

83

print(f"Token: {token.type} = '{token.value}' at {token.lineno}:{token.col_offset}")

84

85

# Parse resource file

86

resource_model = get_resource_model('resources/common.resource')

87

print(f"Resource keywords: {len(resource_model.keyword_section.body)}")

88

```

89

90

### Token Representation

91

92

Low-level token representation for detailed parsing control.

93

94

```python { .api }

95

class Token:

96

"""

97

Represents a single token in Robot Framework syntax.

98

99

Attributes:

100

type: Token type (string constant)

101

value: Token value/content

102

lineno: Line number (1-based)

103

col_offset: Column offset (0-based)

104

error: Error message if token represents an error

105

"""

106

107

# Token types as class attributes

108

SETTING_HEADER = 'SETTING HEADER'

109

VARIABLE_HEADER = 'VARIABLE HEADER'

110

TESTCASE_HEADER = 'TESTCASE HEADER'

111

KEYWORD_HEADER = 'KEYWORD HEADER'

112

COMMENT_HEADER = 'COMMENT HEADER'

113

114

TESTCASE_NAME = 'TESTCASE NAME'

115

KEYWORD_NAME = 'KEYWORD NAME'

116

117

DOCUMENTATION = 'DOCUMENTATION'

118

TAGS = 'TAGS'

119

SETUP = 'SETUP'

120

TEARDOWN = 'TEARDOWN'

121

TIMEOUT = 'TIMEOUT'

122

TEMPLATE = 'TEMPLATE'

123

ARGUMENTS = 'ARGUMENTS'

124

RETURN = 'RETURN'

125

126

VARIABLE = 'VARIABLE'

127

KEYWORD = 'KEYWORD'

128

ASSIGN = 'ASSIGN'

129

ARGUMENT = 'ARGUMENT'

130

131

FOR = 'FOR'

132

FOR_SEPARATOR = 'FOR SEPARATOR'

133

END = 'END'

134

IF = 'IF'

135

INLINE_IF = 'INLINE IF'

136

ELSE_IF = 'ELSE IF'

137

ELSE = 'ELSE'

138

TRY = 'TRY'

139

EXCEPT = 'EXCEPT'

140

FINALLY = 'FINALLY'

141

WHILE = 'WHILE'

142

143

COMMENT = 'COMMENT'

144

CONTINUATION = 'CONTINUATION'

145

EOL = 'EOL'

146

EOS = 'EOS' # End of statement

147

EOF = 'EOF' # End of file

148

```

149

150

**Usage Examples:**

151

152

```python

153

from robot.api.parsing import get_tokens, Token

154

155

def analyze_test_structure(source):

156

"""Analyze test file structure using tokens."""

157

158

stats = {

159

'test_cases': 0,

160

'keywords': 0,

161

'variables': 0,

162

'comments': 0,

163

'for_loops': 0,

164

'if_statements': 0

165

}

166

167

for token in get_tokens(source):

168

if token.type == Token.TESTCASE_NAME:

169

stats['test_cases'] += 1

170

elif token.type == Token.KEYWORD_NAME:

171

stats['keywords'] += 1

172

elif token.type == Token.VARIABLE:

173

stats['variables'] += 1

174

elif token.type == Token.COMMENT:

175

stats['comments'] += 1

176

elif token.type == Token.FOR:

177

stats['for_loops'] += 1

178

elif token.type == Token.IF:

179

stats['if_statements'] += 1

180

181

return stats

182

183

# Analyze test file

184

stats = analyze_test_structure('tests/complex.robot')

185

print(f"Test file contains: {stats}")

186

```

187

188

### AST Model Classes

189

190

Abstract syntax tree model classes representing different parts of Robot Framework test data.

191

192

```python { .api }

193

class File:

194

"""

195

Root model representing a complete Robot Framework file.

196

197

Attributes:

198

source: Source file path

199

sections: List of section objects

200

"""

201

202

# Section classes

203

class SettingSection:

204

"""Settings section (*** Settings ***)."""

205

body: List # Setting statements

206

207

class VariableSection:

208

"""Variables section (*** Variables ***)."""

209

body: List # Variable statements

210

211

class TestCaseSection:

212

"""Test Cases section (*** Test Cases ***)."""

213

body: List # TestCase objects

214

215

class KeywordSection:

216

"""Keywords section (*** Keywords ***)."""

217

body: List # Keyword objects

218

219

class CommentSection:

220

"""Comments section (*** Comments ***)."""

221

body: List # Comment statements

222

223

# Block classes (can contain other blocks and statements)

224

class TestCase:

225

"""Individual test case definition."""

226

header: TestCaseName

227

body: List # Keyword calls and control structures

228

229

class Keyword:

230

"""Individual keyword definition."""

231

header: KeywordName

232

body: List # Keyword calls and control structures

233

234

# Control structure classes

235

class If:

236

"""IF/ELSE control structure."""

237

header: IfHeader

238

body: List

239

orelse: List # ELSE IF and ELSE branches

240

241

class For:

242

"""FOR loop control structure."""

243

header: ForHeader

244

body: List

245

246

class While:

247

"""WHILE loop control structure."""

248

header: WhileHeader

249

body: List

250

251

class Try:

252

"""TRY/EXCEPT control structure."""

253

header: TryHeader

254

body: List

255

next: List # EXCEPT and FINALLY branches

256

```

257

258

**Usage Examples:**

259

260

```python

261

from robot.api.parsing import get_model

262

263

def extract_test_keywords(source):

264

"""Extract all keywords used in test cases."""

265

266

model = get_model(source)

267

keywords_used = set()

268

269

for section in model.sections:

270

if hasattr(section, 'body'):

271

for item in section.body:

272

if hasattr(item, 'body'): # Test case or keyword

273

for statement in item.body:

274

if hasattr(statement, 'keyword'):

275

keywords_used.add(statement.keyword)

276

277

return sorted(keywords_used)

278

279

# Extract keywords from test file

280

keywords = extract_test_keywords('tests/suite.robot')

281

print(f"Keywords used: {keywords}")

282

```

283

284

### Model Visitor Pattern

285

286

Process and traverse AST models using the visitor pattern.

287

288

```python { .api }

289

class ModelVisitor:

290

"""

291

Base class for inspecting model objects.

292

Override visit_* methods to process specific node types.

293

"""

294

295

def visit(self, node): ...

296

def generic_visit(self, node): ...

297

298

# Visit methods for different node types

299

def visit_File(self, node): ...

300

def visit_Section(self, node): ...

301

def visit_TestCase(self, node): ...

302

def visit_Keyword(self, node): ...

303

def visit_For(self, node): ...

304

def visit_If(self, node): ...

305

def visit_Statement(self, node): ...

306

307

class ModelTransformer:

308

"""

309

Base class for modifying model objects.

310

Override visit_* methods to transform specific node types.

311

"""

312

313

def visit(self, node): ...

314

def generic_visit(self, node): ...

315

316

# Transform methods return modified nodes

317

def visit_File(self, node): ...

318

def visit_TestCase(self, node): ...

319

def visit_Keyword(self, node): ...

320

```

321

322

**Usage Examples:**

323

324

```python

325

from robot.api.parsing import get_model, ModelVisitor, ModelTransformer

326

327

class TestCaseAnalyzer(ModelVisitor):

328

"""Analyze test cases for complexity metrics."""

329

330

def __init__(self):

331

self.test_stats = {}

332

self.current_test = None

333

334

def visit_TestCase(self, node):

335

self.current_test = node.header.data_tokens[0].value

336

self.test_stats[self.current_test] = {

337

'keywords': 0,

338

'for_loops': 0,

339

'if_statements': 0,

340

'assignments': 0

341

}

342

self.generic_visit(node)

343

344

def visit_KeywordCall(self, node):

345

if self.current_test:

346

self.test_stats[self.current_test]['keywords'] += 1

347

348

def visit_For(self, node):

349

if self.current_test:

350

self.test_stats[self.current_test]['for_loops'] += 1

351

self.generic_visit(node)

352

353

def visit_If(self, node):

354

if self.current_test:

355

self.test_stats[self.current_test]['if_statements'] += 1

356

self.generic_visit(node)

357

358

class TagAdder(ModelTransformer):

359

"""Add tags to test cases based on content analysis."""

360

361

def visit_TestCase(self, node):

362

# Analyze test content to determine appropriate tags

363

tags_to_add = []

364

365

# Check if test uses database keywords

366

for statement in node.body:

367

if hasattr(statement, 'keyword'):

368

keyword = statement.keyword.lower()

369

if 'database' in keyword or 'sql' in keyword:

370

tags_to_add.append('database')

371

elif 'web' in keyword or 'browser' in keyword:

372

tags_to_add.append('web')

373

374

# Add tags statement if new tags found

375

if tags_to_add:

376

# Create new tags statement

377

tags_statement = Tags([Token(Token.TAGS, 'Tags')] +

378

[Token(Token.ARGUMENT, tag) for tag in tags_to_add])

379

node.body.insert(0, tags_statement)

380

381

return node

382

383

# Use visitors to analyze and transform

384

model = get_model('tests/suite.robot')

385

386

# Analyze complexity

387

analyzer = TestCaseAnalyzer()

388

analyzer.visit(model)

389

print("Test complexity metrics:", analyzer.test_stats)

390

391

# Transform by adding tags

392

transformer = TagAdder()

393

modified_model = transformer.visit(model)

394

395

# Convert back to text

396

print(str(modified_model))

397

```

398

399

### Suite Structure Analysis

400

401

Analyze and work with test suite directory structures.

402

403

```python { .api }

404

class SuiteStructure:

405

"""

406

Represents the structure of a test suite directory.

407

408

Attributes:

409

source: Root directory path

410

init_file: Initialization file path (if exists)

411

children: Child suite structures and test files

412

"""

413

414

class SuiteStructureBuilder:

415

"""Build suite structures from file system."""

416

417

def build(self, source): ...

418

419

class SuiteStructureVisitor:

420

"""Process suite structures using visitor pattern."""

421

422

def visit_directory(self, directory): ...

423

def visit_file(self, file): ...

424

```

425

426

**Usage Examples:**

427

428

```python

429

from robot.parsing.suitestructure import SuiteStructureBuilder

430

431

# Analyze test suite directory structure

432

builder = SuiteStructureBuilder()

433

structure = builder.build('tests/')

434

435

def print_structure(structure, indent=0):

436

"""Print suite structure recursively."""

437

prefix = " " * indent

438

print(f"{prefix}{structure.source}")

439

440

if structure.init_file:

441

print(f"{prefix} __init__.robot")

442

443

for child in structure.children:

444

if hasattr(child, 'children'): # Directory

445

print_structure(child, indent + 1)

446

else: # Test file

447

print(f"{prefix} {child.source}")

448

449

print_structure(structure)

450

```

451

452

### Suite Processing

453

454

Process executable test suites before execution.

455

456

```python { .api }

457

class SuiteVisitor:

458

"""

459

Abstract base class for processing test suites.

460

Can be used as pre-run modifier (--prerunmodifier option).

461

"""

462

463

def start_suite(self, suite): ...

464

def end_suite(self, suite): ...

465

def start_test(self, test): ...

466

def end_test(self, test): ...

467

def start_keyword(self, keyword): ...

468

def end_keyword(self, keyword): ...

469

def visit_suite(self, suite): ...

470

def visit_test(self, test): ...

471

def visit_keyword(self, keyword): ...

472

```

473

474

**Usage Examples:**

475

476

```python

477

from robot.api import SuiteVisitor

478

479

class TestFilter(SuiteVisitor):

480

"""Filter tests based on custom criteria."""

481

482

def __init__(self, min_priority=1):

483

self.min_priority = min_priority

484

485

def start_suite(self, suite):

486

# Filter tests based on priority tag

487

original_tests = list(suite.tests)

488

suite.tests.clear()

489

490

for test in original_tests:

491

priority = self._get_priority(test)

492

if priority >= self.min_priority:

493

suite.tests.append(test)

494

495

def _get_priority(self, test):

496

"""Extract priority from test tags."""

497

for tag in test.tags:

498

if tag.startswith('priority:'):

499

try:

500

return int(tag.split(':')[1])

501

except ValueError:

502

pass

503

return 0 # Default priority

504

505

# Use as pre-run modifier

506

class TestEnhancer(SuiteVisitor):

507

"""Enhance tests with additional setup/teardown."""

508

509

def start_test(self, test):

510

# Add timestamp variable to each test

511

test.keywords.create(

512

'Set Test Variable',

513

args=['${TEST_START_TIME}', '${CURTIME}'],

514

assign=['${START_TIME}']

515

).insert(0) # Insert at beginning

516

517

def end_test(self, test):

518

# Add cleanup keyword to each test

519

test.teardown.config(

520

name='Log Test Duration',

521

args=['Test completed in ${CURTIME - START_TIME}']

522

)

523

```

524

525

## Types

526

527

```python { .api }

528

# Token position information

529

LineNumber = int # 1-based line number

530

ColumnOffset = int # 0-based column offset

531

532

# Model node types

533

NodeType = Union[

534

File, Section, TestCase, Keyword,

535

For, If, While, Try, Statement

536

]

537

538

# Parsing options

539

ParsingOptions = Dict[str, Any] # language, etc.

540

541

# Statement types

542

StatementType = Union[

543

# Settings

544

Documentation, Tags, Setup, Teardown, Timeout, Template, Arguments, Return,

545

# Variables

546

Variable,

547

# Execution

548

KeywordCall, TemplateArguments,

549

# Control structures

550

IfHeader, ElseIfHeader, ElseHeader, ForHeader, WhileHeader, TryHeader,

551

ExceptHeader, FinallyHeader, End,

552

# Other

553

Comment, EmptyLine, Error

554

]

555

```