or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-management.mdcore-operations.mderror-handling.mdindex.mdtype-system.md

error-handling.mddocs/

0

# Error Handling

1

2

Hierarchical exception system covering syntax errors, evaluation errors, type mismatches, and symbol resolution failures. The error handling system provides detailed error information, suggestions for fixes, and proper exception hierarchies for comprehensive error management.

3

4

## Capabilities

5

6

### Base Exception Classes

7

8

The foundation exception classes that all rule engine errors inherit from.

9

10

```python { .api }

11

class EngineError(Exception):

12

"""Base exception class for all rule engine errors."""

13

14

def __init__(self, message: str = ''):

15

"""

16

Initialize the engine error.

17

18

Args:

19

message (str): Description of the error

20

"""

21

22

@property

23

def message(self) -> str:

24

"""The error message description."""

25

```

26

27

**Usage Example:**

28

29

```python

30

import rule_engine

31

32

try:

33

# Any rule engine operation

34

rule = rule_engine.Rule('invalid syntax ===')

35

except rule_engine.EngineError as e:

36

print(f"Rule engine error: {e.message}")

37

print(f"Error type: {type(e).__name__}")

38

```

39

40

### Evaluation Errors

41

42

Errors that occur during rule evaluation and expression processing.

43

44

```python { .api }

45

class EvaluationError(EngineError):

46

"""

47

Errors that occur during rule evaluation, including type mismatches,

48

function call failures, and expression evaluation issues.

49

"""

50

```

51

52

**Usage Example:**

53

54

```python

55

import rule_engine

56

57

try:

58

rule = rule_engine.Rule('name + age') # Can't add string + number

59

rule.evaluate({'name': 'John', 'age': 25})

60

except rule_engine.EvaluationError as e:

61

print(f"Evaluation failed: {e.message}")

62

```

63

64

### Syntax Errors

65

66

Errors related to rule expression syntax and parsing.

67

68

```python { .api }

69

class RuleSyntaxError(EngineError):

70

"""Errors in rule expression grammar and syntax."""

71

72

def __init__(self, message: str, token=None):

73

"""

74

Initialize syntax error.

75

76

Args:

77

message (str): Description of the syntax error

78

token: PLY token related to the error (if available)

79

"""

80

81

@property

82

def token(self):

83

"""The PLY token related to the syntax error."""

84

```

85

86

**Usage Example:**

87

88

```python

89

import rule_engine

90

91

try:

92

rule = rule_engine.Rule('name == "John" and and age > 18') # Double 'and'

93

except rule_engine.RuleSyntaxError as e:

94

print(f"Syntax error: {e.message}")

95

if e.token:

96

print(f"Error at line {e.token.lineno}, position {e.token.lexpos}")

97

98

# Common syntax errors

99

syntax_errors = [

100

'name === "value"', # Invalid operator

101

'if (condition', # Missing closing parenthesis

102

'name == "unclosed', # Unclosed string

103

'3.14.15', # Invalid float format

104

'regex =~ "[unclosed', # Invalid regex

105

]

106

107

for expr in syntax_errors:

108

try:

109

rule_engine.Rule(expr)

110

except rule_engine.RuleSyntaxError as e:

111

print(f"'{expr}' -> {e.message}")

112

```

113

114

## Symbol Resolution Errors

115

116

Errors related to resolving symbols and accessing object members.

117

118

### Symbol Resolution Error

119

120

Raised when a symbol cannot be resolved to a value.

121

122

```python { .api }

123

class SymbolResolutionError(EvaluationError):

124

"""Error when a symbol name cannot be resolved."""

125

126

def __init__(self, symbol_name: str, symbol_scope: str = None,

127

thing=UNDEFINED, suggestion: str = None):

128

"""

129

Initialize symbol resolution error.

130

131

Args:

132

symbol_name (str): Name of the unresolved symbol

133

symbol_scope (str): Scope where symbol should be valid

134

thing: Root object used for resolution

135

suggestion (str): Optional suggestion for correct symbol name

136

"""

137

138

@property

139

def symbol_name(self) -> str:

140

"""The name of the symbol that couldn't be resolved."""

141

142

@property

143

def symbol_scope(self) -> str:

144

"""The scope where the symbol should be valid."""

145

146

@property

147

def thing(self):

148

"""The root object used for symbol resolution."""

149

150

@property

151

def suggestion(self) -> str:

152

"""Optional suggestion for a correct symbol name."""

153

```

154

155

**Usage Example:**

156

157

```python

158

import rule_engine

159

160

try:

161

rule = rule_engine.Rule('unknown_field == "value"')

162

rule.matches({'known_field': 'value'})

163

except rule_engine.SymbolResolutionError as e:

164

print(f"Unknown symbol: '{e.symbol_name}'")

165

if e.suggestion:

166

print(f"Did you mean: '{e.suggestion}'")

167

if e.thing is not rule_engine.UNDEFINED:

168

available = list(e.thing.keys()) if hasattr(e.thing, 'keys') else dir(e.thing)

169

print(f"Available symbols: {available}")

170

```

171

172

### Attribute Resolution Error

173

174

Raised when an object attribute cannot be accessed.

175

176

```python { .api }

177

class AttributeResolutionError(EvaluationError):

178

"""Error when an attribute cannot be resolved on an object."""

179

180

def __init__(self, attribute_name: str, object_, thing=UNDEFINED, suggestion: str = None):

181

"""

182

Initialize attribute resolution error.

183

184

Args:

185

attribute_name (str): Name of the unresolved attribute

186

object_: Object where attribute access was attempted

187

thing: Root object used for resolution

188

suggestion (str): Optional suggestion for correct attribute name

189

"""

190

191

@property

192

def attribute_name(self) -> str:

193

"""The name of the attribute that couldn't be resolved."""

194

195

@property

196

def object(self):

197

"""The object where attribute access was attempted."""

198

199

@property

200

def thing(self):

201

"""The root object used for resolution."""

202

203

@property

204

def suggestion(self) -> str:

205

"""Optional suggestion for a correct attribute name."""

206

```

207

208

**Usage Example:**

209

210

```python

211

import rule_engine

212

213

class Person:

214

def __init__(self, name):

215

self.name = name

216

self.first_name = name.split()[0]

217

218

try:

219

context = rule_engine.Context(resolver=rule_engine.resolve_attribute)

220

rule = rule_engine.Rule('firstname', context=context) # Typo: should be 'first_name'

221

rule.evaluate(Person('John Doe'))

222

except rule_engine.AttributeResolutionError as e:

223

print(f"Attribute '{e.attribute_name}' not found")

224

if e.suggestion:

225

print(f"Did you mean: '{e.suggestion}'")

226

print(f"Available attributes: {[attr for attr in dir(e.object) if not attr.startswith('_')]}")

227

```

228

229

## Type-Related Errors

230

231

Errors related to type mismatches and type validation.

232

233

### Symbol Type Error

234

235

Raised when a symbol resolves to a value of the wrong type.

236

237

```python { .api }

238

class SymbolTypeError(EvaluationError):

239

"""Error when a symbol resolves to an incompatible type."""

240

241

def __init__(self, symbol_name: str, is_value, is_type, expected_type):

242

"""

243

Initialize symbol type error.

244

245

Args:

246

symbol_name (str): Name of the symbol with wrong type

247

is_value: The actual Python value

248

is_type: The actual DataType

249

expected_type: The expected DataType

250

"""

251

252

@property

253

def symbol_name(self) -> str:

254

"""The name of the symbol with incorrect type."""

255

256

@property

257

def is_value(self):

258

"""The actual Python value of the symbol."""

259

260

@property

261

def is_type(self):

262

"""The actual DataType of the symbol."""

263

264

@property

265

def expected_type(self):

266

"""The expected DataType for the symbol."""

267

```

268

269

### Attribute Type Error

270

271

Raised when an attribute resolves to a value of the wrong type.

272

273

```python { .api }

274

class AttributeTypeError(EvaluationError):

275

"""Error when an attribute resolves to an incompatible type."""

276

277

def __init__(self, attribute_name: str, object_type, is_value, is_type, expected_type):

278

"""

279

Initialize attribute type error.

280

281

Args:

282

attribute_name (str): Name of the attribute with wrong type

283

object_type: The object type where attribute was resolved

284

is_value: The actual Python value

285

is_type: The actual DataType

286

expected_type: The expected DataType

287

"""

288

289

@property

290

def attribute_name(self) -> str:

291

"""The name of the attribute with incorrect type."""

292

293

@property

294

def object_type(self):

295

"""The object on which the attribute was resolved."""

296

297

@property

298

def is_value(self):

299

"""The actual Python value of the attribute."""

300

301

@property

302

def is_type(self):

303

"""The actual DataType of the attribute."""

304

305

@property

306

def expected_type(self):

307

"""The expected DataType for the attribute."""

308

```

309

310

**Usage Example:**

311

312

```python

313

import rule_engine

314

315

# Type error example

316

type_map = {

317

'age': rule_engine.DataType.FLOAT,

318

'name': rule_engine.DataType.STRING

319

}

320

321

context = rule_engine.Context(type_resolver=rule_engine.type_resolver_from_dict(type_map))

322

323

try:

324

# This should work

325

rule = rule_engine.Rule('age > 18', context=context)

326

result = rule.matches({'age': 25, 'name': 'John'})

327

328

# This will cause a type error if age is provided as string

329

result = rule.matches({'age': "twenty-five", 'name': 'John'})

330

except rule_engine.SymbolTypeError as e:

331

print(f"Type error for symbol '{e.symbol_name}':")

332

print(f" Expected: {e.expected_type.name}")

333

print(f" Got: {e.is_type.name} ({e.is_value})")

334

```

335

336

## Specialized Syntax Errors

337

338

Specific syntax errors for different data types and constructs.

339

340

### Data Type Syntax Errors

341

342

```python { .api }

343

class BytesSyntaxError(EngineError):

344

"""Error in bytes literal syntax."""

345

346

def __init__(self, message: str, value: str):

347

"""

348

Args:

349

message (str): Error description

350

value (str): The malformed bytes value

351

"""

352

353

@property

354

def value(self) -> str:

355

"""The bytes value that caused the error."""

356

357

class StringSyntaxError(EngineError):

358

"""Error in string literal syntax."""

359

360

def __init__(self, message: str, value: str):

361

"""

362

Args:

363

message (str): Error description

364

value (str): The malformed string value

365

"""

366

367

@property

368

def value(self) -> str:

369

"""The string value that caused the error."""

370

371

class DatetimeSyntaxError(EngineError):

372

"""Error in datetime literal syntax."""

373

374

def __init__(self, message: str, value: str):

375

"""

376

Args:

377

message (str): Error description

378

value (str): The malformed datetime value

379

"""

380

381

@property

382

def value(self) -> str:

383

"""The datetime value that caused the error."""

384

385

class FloatSyntaxError(EngineError):

386

"""Error in float literal syntax."""

387

388

def __init__(self, message: str, value: str):

389

"""

390

Args:

391

message (str): Error description

392

value (str): The malformed float value

393

"""

394

395

@property

396

def value(self) -> str:

397

"""The float value that caused the error."""

398

399

class TimedeltaSyntaxError(EngineError):

400

"""Error in timedelta literal syntax."""

401

402

def __init__(self, message: str, value: str):

403

"""

404

Args:

405

message (str): Error description

406

value (str): The malformed timedelta value

407

"""

408

409

@property

410

def value(self) -> str:

411

"""The timedelta value that caused the error."""

412

413

class RegexSyntaxError(EngineError):

414

"""Error in regular expression syntax."""

415

416

def __init__(self, message: str, error, value: str):

417

"""

418

Args:

419

message (str): Error description

420

error: The original re.error exception

421

value (str): The malformed regex pattern

422

"""

423

424

@property

425

def error(self):

426

"""The original re.error exception."""

427

428

@property

429

def value(self) -> str:

430

"""The regex pattern that caused the error."""

431

```

432

433

**Usage Example:**

434

435

```python

436

import rule_engine

437

438

# Examples of syntax errors for different data types

439

syntax_test_cases = [

440

('dt"2023-13-45"', rule_engine.DatetimeSyntaxError), # Invalid date

441

('3.14.15.9', rule_engine.FloatSyntaxError), # Invalid float

442

('td"25:70:80"', rule_engine.TimedeltaSyntaxError), # Invalid timedelta

443

('b"\\xGH"', rule_engine.BytesSyntaxError), # Invalid bytes escape

444

('"unclosed string', rule_engine.StringSyntaxError), # Unclosed string

445

('name =~ "[unclosed"', rule_engine.RegexSyntaxError), # Invalid regex

446

]

447

448

for expr, expected_error in syntax_test_cases:

449

try:

450

rule_engine.Rule(expr)

451

except expected_error as e:

452

print(f"'{expr}' -> {type(e).__name__}: {e.message}")

453

if hasattr(e, 'value'):

454

print(f" Problematic value: '{e.value}'")

455

```

456

457

## Other Specialized Errors

458

459

### Function Call Error

460

461

Errors that occur when calling functions within expressions.

462

463

```python { .api }

464

class FunctionCallError(EvaluationError):

465

"""Error during function execution."""

466

467

def __init__(self, message: str, error=None, function_name: str = None):

468

"""

469

Args:

470

message (str): Error description

471

error: The original exception that caused this error

472

function_name (str): Name of the function that failed

473

"""

474

475

@property

476

def error(self):

477

"""The original exception that caused this error."""

478

479

@property

480

def function_name(self) -> str:

481

"""The name of the function that failed."""

482

```

483

484

### Lookup Error

485

486

Errors that occur during container lookups (similar to IndexError/KeyError).

487

488

```python { .api }

489

class LookupError(EvaluationError):

490

"""Error when lookup operations fail on containers."""

491

492

def __init__(self, container, item):

493

"""

494

Args:

495

container: The container object where lookup failed

496

item: The key/index that was used for lookup

497

"""

498

499

@property

500

def container(self):

501

"""The container where the lookup failed."""

502

503

@property

504

def item(self):

505

"""The key/index used for the failed lookup."""

506

```

507

508

**Usage Example:**

509

510

```python

511

import rule_engine

512

513

# Function call errors

514

try:

515

rule = rule_engine.Rule('unknown_function(42)')

516

rule.evaluate({})

517

except rule_engine.FunctionCallError as e:

518

print(f"Function error: {e.message}")

519

if e.function_name:

520

print(f"Failed function: {e.function_name}")

521

522

# Lookup errors

523

try:

524

rule = rule_engine.Rule('data[missing_key]')

525

rule.evaluate({'data': {'existing_key': 'value'}})

526

except rule_engine.LookupError as e:

527

print(f"Lookup failed: container={type(e.container)}, item={e.item}")

528

```

529

530

## Error Handling Best Practices

531

532

### Comprehensive Error Handling

533

534

```python

535

import rule_engine

536

537

def safe_rule_evaluation(rule_text, data, context=None):

538

"""Safely evaluate a rule with comprehensive error handling."""

539

try:

540

rule = rule_engine.Rule(rule_text, context=context)

541

return rule.matches(data), None

542

except rule_engine.RuleSyntaxError as e:

543

return False, f"Syntax error: {e.message}"

544

except rule_engine.SymbolResolutionError as e:

545

error_msg = f"Unknown symbol: {e.symbol_name}"

546

if e.suggestion:

547

error_msg += f" (did you mean: {e.suggestion}?)"

548

return False, error_msg

549

except rule_engine.EvaluationError as e:

550

return False, f"Evaluation error: {e.message}"

551

except rule_engine.EngineError as e:

552

return False, f"Rule engine error: {e.message}"

553

554

# Usage

555

result, error = safe_rule_evaluation('age > 18', {'age': 25})

556

if error:

557

print(f"Error: {error}")

558

else:

559

print(f"Result: {result}")

560

```

561

562

### Error Logging and Debugging

563

564

```python

565

import rule_engine

566

import logging

567

568

logger = logging.getLogger(__name__)

569

570

def debug_rule_execution(rule_text, data, context=None):

571

"""Execute rule with detailed error logging."""

572

try:

573

logger.info(f"Evaluating rule: {rule_text}")

574

rule = rule_engine.Rule(rule_text, context=context)

575

result = rule.matches(data)

576

logger.info(f"Rule evaluation successful: {result}")

577

return result

578

except rule_engine.RuleSyntaxError as e:

579

logger.error(f"Syntax error in rule '{rule_text}': {e.message}")

580

if e.token:

581

logger.error(f"Error location: line {e.token.lineno}, pos {e.token.lexpos}")

582

raise

583

except rule_engine.SymbolResolutionError as e:

584

logger.error(f"Symbol resolution failed: {e.symbol_name}")

585

if e.suggestion:

586

logger.info(f"Suggested correction: {e.suggestion}")

587

available_symbols = list(data.keys()) if hasattr(data, 'keys') else dir(data)

588

logger.debug(f"Available symbols: {available_symbols}")

589

raise

590

except rule_engine.EvaluationError as e:

591

logger.error(f"Evaluation failed for rule '{rule_text}': {e.message}")

592

logger.debug(f"Data: {data}")

593

raise

594

```

595

596

### Graceful Degradation

597

598

```python

599

import rule_engine

600

601

class RuleProcessor:

602

"""Process rules with fallback strategies."""

603

604

def __init__(self, fallback_result=False):

605

self.fallback_result = fallback_result

606

self.error_count = 0

607

608

def evaluate_with_fallback(self, rule_text, data, context=None):

609

"""Evaluate rule with fallback on errors."""

610

try:

611

rule = rule_engine.Rule(rule_text, context=context)

612

return rule.matches(data)

613

except rule_engine.EngineError as e:

614

self.error_count += 1

615

logging.warning(f"Rule evaluation failed, using fallback: {e.message}")

616

return self.fallback_result

617

618

def get_error_rate(self, total_evaluations):

619

"""Calculate error rate for monitoring."""

620

return self.error_count / total_evaluations if total_evaluations > 0 else 0

621

```