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

context-management.mddocs/

0

# Context and Symbol Resolution

1

2

Context management system for controlling how symbols are resolved, type checking is performed, and default values are handled. The context system enables custom symbol resolution, type safety, and flexible data access patterns.

3

4

## Capabilities

5

6

### Context Creation

7

8

Create context objects that define how symbols are resolved and type checking is performed.

9

10

```python { .api }

11

class Context:

12

def __init__(self, *, regex_flags=0, resolver=None, type_resolver=None,

13

default_timezone='local', default_value=UNDEFINED, decimal_context=None):

14

"""

15

Create a context for rule evaluation.

16

17

Args:

18

regex_flags (int): Flags for regex operations (re module flags)

19

resolver: Function for symbol value resolution

20

type_resolver: Function or mapping for symbol type resolution

21

default_timezone: Default timezone ('local', 'utc', or tzinfo instance)

22

default_value: Default value for unresolved symbols

23

decimal_context: Decimal context for float operations

24

"""

25

```

26

27

**Usage Example:**

28

29

```python

30

import rule_engine

31

32

# Basic context with default value

33

context = rule_engine.Context(default_value="N/A")

34

rule = rule_engine.Rule('missing_field', context=context)

35

result = rule.evaluate({}) # Returns "N/A"

36

37

# Context with custom resolver

38

def custom_resolver(symbol_name, obj):

39

if symbol_name == 'full_name':

40

return f"{obj.get('first', '')} {obj.get('last', '')}"

41

return obj.get(symbol_name)

42

43

context = rule_engine.Context(resolver=custom_resolver)

44

rule = rule_engine.Rule('full_name', context=context)

45

person = {'first': 'John', 'last': 'Doe'}

46

print(rule.evaluate(person)) # "John Doe"

47

```

48

49

### Symbol Resolution

50

51

Resolve symbols to values using context-specific resolution functions.

52

53

```python { .api }

54

def resolve(self, thing, name: str, scope: str = None):

55

"""

56

Resolve a symbol name to its value.

57

58

Args:

59

thing: The object to resolve the symbol against

60

name (str): The symbol name to resolve

61

scope (str, optional): The scope for symbol resolution

62

63

Returns:

64

The resolved value for the symbol

65

66

Raises:

67

SymbolResolutionError: If the symbol cannot be resolved

68

"""

69

```

70

71

### Attribute Resolution

72

73

Resolve attributes from object values within the context.

74

75

```python { .api }

76

def resolve_attribute(self, thing, object_, name: str):

77

"""

78

Resolve an attribute from an object value.

79

80

Args:

81

thing: The root object for resolution

82

object_: The object to access the attribute on

83

name (str): The attribute name to resolve

84

85

Returns:

86

The resolved attribute value

87

88

Raises:

89

AttributeResolutionError: If the attribute cannot be resolved

90

"""

91

```

92

93

### Type Resolution

94

95

Resolve type information for symbols during rule parsing.

96

97

```python { .api }

98

def resolve_type(self, name: str, scope: str = None):

99

"""

100

Resolve type information for a symbol.

101

102

Args:

103

name (str): The symbol name to get type information for

104

scope (str, optional): The scope for type resolution

105

106

Returns:

107

DataType: The data type for the symbol

108

109

Raises:

110

SymbolResolutionError: If type cannot be resolved

111

"""

112

```

113

114

### Assignment Management

115

116

Manage temporary symbol assignments within expression contexts.

117

118

```python { .api }

119

@contextlib.contextmanager

120

def assignments(self, *assignments):

121

"""

122

Add assignments to a thread-specific scope.

123

124

Args:

125

*assignments: Assignment objects to define in the scope

126

127

Yields:

128

Context manager for assignment scope

129

"""

130

```

131

132

### Standalone Attribute Resolution

133

134

Standalone function for resolving symbols as object attributes with error handling and suggestions.

135

136

```python { .api }

137

def resolve_attribute(thing, name: str):

138

"""

139

Resolve a symbol as an attribute of the provided object.

140

141

Args:

142

thing: The object to access the attribute on

143

name (str): The attribute name to resolve

144

145

Returns:

146

The value of the specified attribute

147

148

Raises:

149

SymbolResolutionError: If the attribute doesn't exist

150

151

Warning:

152

This exposes all attributes of the object. Use custom resolvers

153

for security-sensitive applications.

154

"""

155

```

156

157

**Usage Example:**

158

159

```python

160

import rule_engine

161

162

class Person:

163

def __init__(self, name, age):

164

self.name = name

165

self.age = age

166

self._private = "secret"

167

168

# Using attribute resolution

169

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

170

rule = rule_engine.Rule('name + " is " + str(age)', context=context)

171

172

person = Person("Alice", 30)

173

result = rule.evaluate(person)

174

print(result) # "Alice is 30"

175

176

# Accessing private attributes (be careful!)

177

private_rule = rule_engine.Rule('_private', context=context)

178

print(private_rule.evaluate(person)) # "secret"

179

```

180

181

### Item Resolution

182

183

Resolve symbols as dictionary/mapping keys with error handling and suggestions.

184

185

```python { .api }

186

def resolve_item(thing, name: str):

187

"""

188

Resolve a symbol as a key in a mapping object.

189

190

Args:

191

thing: The mapping object to access

192

name (str): The key name to resolve

193

194

Returns:

195

The value for the specified key

196

197

Raises:

198

SymbolResolutionError: If the key doesn't exist or object isn't a mapping

199

"""

200

```

201

202

**Usage Example:**

203

204

```python

205

import rule_engine

206

207

# Using item resolution for dictionaries

208

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

209

rule = rule_engine.Rule('user_id > 1000 and status == "active"', context=context)

210

211

user_data = {

212

'user_id': 1234,

213

'status': 'active',

214

'email': 'user@example.com'

215

}

216

217

print(rule.matches(user_data)) # True

218

219

# Works with any mapping-like object

220

from collections import OrderedDict

221

ordered_data = OrderedDict([('priority', 1), ('task', 'important')])

222

task_rule = rule_engine.Rule('priority == 1', context=context)

223

print(task_rule.matches(ordered_data)) # True

224

```

225

226

### Type Resolution

227

228

Create type resolvers from dictionaries to enable type checking and validation.

229

230

```python { .api }

231

def type_resolver_from_dict(dictionary: dict):

232

"""

233

Create a type resolver function from a dictionary mapping.

234

235

Args:

236

dictionary (dict): Mapping of symbol names to DataType constants

237

238

Returns:

239

A type resolver function for use with Context

240

241

Raises:

242

SymbolResolutionError: If a symbol is not defined in the dictionary

243

"""

244

```

245

246

**Usage Example:**

247

248

```python

249

import rule_engine

250

251

# Define types for symbols

252

type_map = {

253

'name': rule_engine.DataType.STRING,

254

'age': rule_engine.DataType.FLOAT,

255

'active': rule_engine.DataType.BOOLEAN,

256

'tags': rule_engine.DataType.ARRAY,

257

'metadata': rule_engine.DataType.MAPPING

258

}

259

260

type_resolver = rule_engine.type_resolver_from_dict(type_map)

261

context = rule_engine.Context(type_resolver=type_resolver)

262

263

# This will work - types match

264

rule = rule_engine.Rule('name + " is " + str(age)', context=context)

265

266

# This will fail - type mismatch

267

try:

268

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

269

except rule_engine.EvaluationError as e:

270

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

271

272

# This will fail - undefined symbol

273

try:

274

unknown_rule = rule_engine.Rule('unknown_field == "value"', context=context)

275

except rule_engine.SymbolResolutionError as e:

276

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

277

```

278

279

## Advanced Context Patterns

280

281

### Combined Resolvers

282

283

Use both type and value resolvers together for comprehensive symbol management.

284

285

```python

286

import rule_engine

287

288

# Custom resolver that handles special cases

289

def smart_resolver(symbol_name, obj):

290

if symbol_name == 'full_name':

291

return f"{obj.get('first_name', '')} {obj.get('last_name', '')}"

292

elif symbol_name == 'is_adult':

293

return obj.get('age', 0) >= 18

294

return rule_engine.resolve_item(obj, symbol_name)

295

296

# Type definitions including computed fields

297

type_map = {

298

'first_name': rule_engine.DataType.STRING,

299

'last_name': rule_engine.DataType.STRING,

300

'age': rule_engine.DataType.FLOAT,

301

'full_name': rule_engine.DataType.STRING, # computed field

302

'is_adult': rule_engine.DataType.BOOLEAN # computed field

303

}

304

305

context = rule_engine.Context(

306

type_resolver=rule_engine.type_resolver_from_dict(type_map),

307

resolver=smart_resolver

308

)

309

310

rule = rule_engine.Rule('is_adult and full_name =~ "John.*"', context=context)

311

person = {'first_name': 'John', 'last_name': 'Doe', 'age': 25}

312

print(rule.matches(person)) # True

313

```

314

315

### Security-Conscious Resolution

316

317

Create secure resolvers that limit access to specific attributes or keys.

318

319

```python

320

import rule_engine

321

322

def secure_resolver(symbol_name, obj):

323

# Whitelist of allowed attributes

324

allowed_fields = {'name', 'email', 'department', 'role'}

325

326

if symbol_name not in allowed_fields:

327

raise rule_engine.SymbolResolutionError(symbol_name)

328

329

return getattr(obj, symbol_name, None)

330

331

context = rule_engine.Context(resolver=secure_resolver)

332

rule = rule_engine.Rule('department == "engineering"', context=context)

333

334

class Employee:

335

def __init__(self):

336

self.name = "John"

337

self.department = "engineering"

338

self.salary = 100000 # This won't be accessible

339

340

employee = Employee()

341

print(rule.matches(employee)) # True

342

343

# This will fail - salary is not in the whitelist

344

try:

345

salary_rule = rule_engine.Rule('salary > 50000', context=context)

346

salary_rule.matches(employee)

347

except rule_engine.SymbolResolutionError as e:

348

print(f"Access denied to: {e.symbol_name}")

349

```

350

351

### Context Properties

352

353

Access context configuration and metadata.

354

355

```python

356

context = rule_engine.Context(

357

type_resolver=rule_engine.type_resolver_from_dict({'name': rule_engine.DataType.STRING}),

358

resolver=rule_engine.resolve_item,

359

default_value="unknown"

360

)

361

362

print(f"Has type resolver: {context.type_resolver is not None}")

363

print(f"Has custom resolver: {context.resolver is not None}")

364

print(f"Default value: {context.default_value}")

365

```

366

367

## Error Handling and Debugging

368

369

Context operations can raise various exceptions that provide detailed error information:

370

371

```python

372

import rule_engine

373

374

try:

375

context = rule_engine.Context()

376

rule = rule_engine.Rule('undefined_symbol', context=context)

377

rule.evaluate({})

378

except rule_engine.SymbolResolutionError as e:

379

print(f"Symbol '{e.symbol_name}' not found")

380

if e.thing is not rule_engine.UNDEFINED:

381

print(f"Available attributes: {dir(e.thing)}")

382

if e.suggestion:

383

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

384

385

# Type resolution errors

386

try:

387

type_resolver = rule_engine.type_resolver_from_dict({'age': rule_engine.DataType.FLOAT})

388

context = rule_engine.Context(type_resolver=type_resolver)

389

rule = rule_engine.Rule('name == "John"', context=context) # 'name' not defined

390

except rule_engine.SymbolResolutionError as e:

391

print(f"Type not defined for symbol: {e.symbol_name}")

392

```