or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-schema.mderror-handling.mdindex.mdrange-collection-validators.mdstring-pattern-validators.mdtype-validators.mdutility-transformers.mdvalidation-composers.md

range-collection-validators.mddocs/

0

# Range & Collection Validation

1

2

Numeric range validation, sequence length validation, membership testing, and complex sequence validation patterns. These validators handle constraints on numeric values, collection sizes, and collection contents.

3

4

## Capabilities

5

6

### Numeric Range Validation

7

8

Validate that numeric values fall within specified ranges with configurable boundary inclusion.

9

10

```python { .api }

11

class Range:

12

def __init__(self, min=None, max=None, min_included=True, max_included=True, msg=None):

13

"""

14

Validate numeric value is within range.

15

16

Parameters:

17

- min: Minimum allowed value (None for no minimum)

18

- max: Maximum allowed value (None for no maximum)

19

- min_included: Whether minimum value is included in valid range (default True)

20

- max_included: Whether maximum value is included in valid range (default True)

21

- msg: Custom error message

22

23

Returns:

24

Original numeric value if within range

25

26

Raises:

27

RangeInvalid: If value is outside specified range

28

"""

29

```

30

31

**Usage Examples:**

32

33

```python

34

from voluptuous import Schema, Range, All, Coerce

35

36

# Basic range validation

37

age_validator = Range(min=0, max=150)

38

percentage_validator = Range(min=0.0, max=100.0)

39

40

# Open ranges

41

positive_validator = Range(min=0) # No maximum

42

negative_validator = Range(max=0) # No minimum

43

44

# Exclusive ranges

45

exclusive_range = Range(min=0, max=10, min_included=False, max_included=False) # 0 < x < 10

46

47

# Combined with type coercion

48

flexible_range = Schema(All(

49

Coerce(float), # Convert to float first

50

Range(min=0.0, max=1.0), # Then validate range

51

))

52

53

# Schema usage

54

product_schema = Schema({

55

'price': All(Coerce(float), Range(min=0.01)), # Positive price

56

'discount': Range(min=0, max=100), # Percentage

57

'quantity': All(int, Range(min=1)), # At least 1

58

})

59

60

# Valid examples

61

product_schema({

62

'price': '19.99', # Coerced to 19.99

63

'discount': 25, # 25% discount

64

'quantity': 5, # 5 items

65

})

66

```

67

68

### Numeric Clamping

69

70

Automatically adjust numeric values to stay within specified bounds instead of rejecting them.

71

72

```python { .api }

73

class Clamp:

74

def __init__(self, min=None, max=None, msg=None):

75

"""

76

Clamp numeric value to specified range.

77

78

Parameters:

79

- min: Minimum allowed value (values below are set to min)

80

- max: Maximum allowed value (values above are set to max)

81

- msg: Custom error message (rarely used since values are adjusted)

82

83

Returns:

84

Numeric value clamped to specified range

85

86

Raises:

87

Invalid: If input is not numeric

88

"""

89

```

90

91

**Usage Examples:**

92

93

```python

94

from voluptuous import Schema, Clamp, All, Coerce

95

96

# Clamp values instead of rejecting them

97

volume_control = Clamp(min=0, max=100) # Volume: 0-100

98

opacity_clamp = Clamp(min=0.0, max=1.0) # Opacity: 0.0-1.0

99

100

# One-way clamping

101

non_negative = Clamp(min=0) # No upper limit

102

max_limit = Clamp(max=1000) # No lower limit

103

104

# Usage examples

105

settings_schema = Schema({

106

'volume': All(Coerce(int), volume_control),

107

'opacity': All(Coerce(float), opacity_clamp),

108

'timeout': All(Coerce(int), Clamp(min=1, max=3600)), # 1 second to 1 hour

109

})

110

111

# Clamping in action:

112

volume_control(150) # -> 100 (clamped to maximum)

113

volume_control(-10) # -> 0 (clamped to minimum)

114

opacity_clamp(1.5) # -> 1.0 (clamped to maximum)

115

```

116

117

### Length Validation

118

119

Validate the length of sequences, strings, and other objects with `len()`.

120

121

```python { .api }

122

class Length:

123

def __init__(self, min=None, max=None, msg=None):

124

"""

125

Validate length of sequences, strings, etc.

126

127

Parameters:

128

- min: Minimum allowed length (None for no minimum)

129

- max: Maximum allowed length (None for no maximum)

130

- msg: Custom error message

131

132

Returns:

133

Original object if length is within range

134

135

Raises:

136

LengthInvalid: If length is outside specified range

137

"""

138

```

139

140

**Usage Examples:**

141

142

```python

143

from voluptuous import Schema, Length, All

144

145

# String length validation

146

username_length = Length(min=3, max=20)

147

password_length = Length(min=8) # At least 8 characters

148

description_length = Length(max=500) # At most 500 characters

149

150

# List length validation

151

tags_length = Length(min=1, max=10) # 1-10 tags

152

items_length = Length(min=0, max=100) # 0-100 items

153

154

# Combined validation

155

user_schema = Schema({

156

'username': All(str, username_length),

157

'password': All(str, password_length),

158

'bio': All(str, description_length),

159

'tags': All([str], tags_length),

160

})

161

162

# Dictionary length (number of keys)

163

config_schema = Schema(All(

164

dict,

165

Length(min=1), # At least one configuration key

166

))

167

168

# Valid examples

169

user_schema({

170

'username': 'john_doe', # 8 characters (valid)

171

'password': 'secretpassword', # 14 characters (valid)

172

'bio': 'Software developer', # 18 characters (valid)

173

'tags': ['python', 'web'], # 2 tags (valid)

174

})

175

```

176

177

### Numeric Precision Validation

178

179

Validate numeric precision and scale for high-precision financial or scientific calculations.

180

181

```python { .api }

182

class Number:

183

def __init__(self, precision=None, scale=None, msg=None, yield_decimal=False):

184

"""

185

Validate numeric precision and scale.

186

187

Parameters:

188

- precision: Maximum total number of digits (None for no limit)

189

- scale: Maximum number of digits after decimal point (None for no limit)

190

- msg: Custom error message

191

- yield_decimal: Return Decimal object instead of original number

192

193

Returns:

194

Original number or Decimal object if yield_decimal=True

195

196

Raises:

197

Invalid: If number exceeds precision or scale limits

198

"""

199

```

200

201

**Usage Examples:**

202

203

```python

204

from voluptuous import Schema, Number, All, Coerce

205

from decimal import Decimal

206

207

# Financial precision validation

208

price_validator = Number(precision=10, scale=2) # Max 10 digits, 2 decimal places

209

currency_validator = Number(precision=15, scale=4) # High precision currency

210

211

# Scientific precision

212

measurement_validator = Number(precision=8, scale=6) # Scientific measurements

213

214

# With Decimal conversion

215

financial_schema = Schema({

216

'amount': All(

217

Coerce(Decimal), # Convert to Decimal first

218

Number(precision=12, scale=2, yield_decimal=True) # Validate and return Decimal

219

),

220

'rate': Number(precision=6, scale=4), # Interest rate with 4 decimal places

221

})

222

223

# Usage examples:

224

price_validator(123.45) # Valid: 5 digits total, 2 decimal places

225

price_validator(12345678.99) # Valid: 10 digits total, 2 decimal places

226

# price_validator(123.456) # Invalid: too many decimal places

227

228

currency_validator(1234567890.1234) # Valid: 14 digits total, 4 decimal places

229

230

# Financial calculations

231

financial_schema({

232

'amount': '1234.56', # Converted to Decimal(1234.56)

233

'rate': 0.0525, # 5.25% interest rate

234

})

235

```

236

237

### Membership Testing

238

239

Validate that values are present or absent in specified containers.

240

241

```python { .api }

242

class In:

243

def __init__(self, container, msg=None):

244

"""

245

Validate value is in specified container.

246

247

Parameters:

248

- container: Container to check membership in (list, set, dict keys, etc.)

249

- msg: Custom error message

250

251

Returns:

252

Original value if found in container

253

254

Raises:

255

InInvalid: If value not found in container

256

"""

257

258

class NotIn:

259

def __init__(self, container, msg=None):

260

"""

261

Validate value is not in specified container.

262

263

Parameters:

264

- container: Container to check membership against

265

- msg: Custom error message

266

267

Returns:

268

Original value if not found in container

269

270

Raises:

271

NotInInvalid: If value found in container

272

"""

273

```

274

275

**Usage Examples:**

276

277

```python

278

from voluptuous import Schema, In, NotIn, All

279

280

# Whitelist validation

281

status_validator = In(['active', 'inactive', 'pending', 'suspended'])

282

priority_validator = In([1, 2, 3, 4, 5])

283

country_validator = In(['US', 'CA', 'UK', 'DE', 'FR', 'JP'])

284

285

# Blacklist validation

286

forbidden_usernames = NotIn(['admin', 'root', 'system', 'administrator'])

287

forbidden_ports = NotIn([22, 23, 25, 53, 80, 110, 443])

288

289

# Combined validation

290

user_schema = Schema({

291

'username': All(str, forbidden_usernames, Length(min=3)),

292

'status': status_validator,

293

'priority': priority_validator,

294

'country': country_validator,

295

})

296

297

# Server configuration

298

server_schema = Schema({

299

'port': All(int, Range(min=1024, max=65535), forbidden_ports),

300

'protocol': In(['http', 'https', 'tcp', 'udp']),

301

})

302

303

# Dynamic containers

304

def get_valid_roles():

305

return ['user', 'moderator', 'admin'] # Could be from database

306

307

role_validator = lambda value: In(get_valid_roles())(value)

308

```

309

310

### Collection Content Validation

311

312

Validate that collections contain specific items or match expected patterns.

313

314

```python { .api }

315

class Contains:

316

def __init__(self, item, msg=None):

317

"""

318

Validate sequence contains specific item.

319

320

Parameters:

321

- item: Item that must be present in sequence

322

- msg: Custom error message

323

324

Returns:

325

Original sequence if item is found

326

327

Raises:

328

ContainsInvalid: If item not found in sequence

329

"""

330

```

331

332

**Usage Examples:**

333

334

```python

335

from voluptuous import Schema, Contains, All, Length

336

337

# Required items validation

338

required_permissions = Contains('read') # Must have 'read' permission

339

required_features = Contains('authentication') # Must include auth feature

340

341

# Multiple required items

342

permissions_schema = All(

343

[str], # List of strings

344

Contains('read'), # Must contain 'read'

345

Contains('write'), # Must contain 'write'

346

Length(min=2), # At least 2 permissions

347

)

348

349

# Configuration validation

350

app_config_schema = Schema({

351

'features': All([str], Contains('core')), # Core feature is required

352

'plugins': All([str], Length(min=0)), # Optional plugins

353

})

354

355

# Email list validation

356

email_list_schema = All(

357

[str],

358

Contains(lambda emails: any('@company.com' in email for email in emails)), # At least one company email

359

)

360

```

361

362

### Sequence Structure Validation

363

364

Validate sequences against specific structural patterns.

365

366

```python { .api }

367

class ExactSequence:

368

def __init__(self, validators, msg=None):

369

"""

370

Validate sequence elements against validators in exact order.

371

372

Parameters:

373

- validators: List of validators, one per sequence element

374

- msg: Custom error message

375

376

Returns:

377

Validated sequence with each element validated by corresponding validator

378

379

Raises:

380

ExactSequenceInvalid: If sequence length doesn't match validators or validation fails

381

"""

382

383

class Unordered:

384

def __init__(self, validators, msg=None):

385

"""

386

Validate sequence contains elements matching validators in any order.

387

388

Parameters:

389

- validators: List of validators that must all match some element

390

- msg: Custom error message

391

392

Returns:

393

Original sequence if all validators match some element

394

395

Raises:

396

Invalid: If any validator doesn't match any element

397

"""

398

399

class Unique:

400

def __init__(self, msg=None):

401

"""

402

Ensure sequence contains no duplicate elements.

403

404

Parameters:

405

- msg: Custom error message

406

407

Returns:

408

Original sequence if all elements are unique

409

410

Raises:

411

Invalid: If duplicate elements are found

412

"""

413

```

414

415

**Usage Examples:**

416

417

```python

418

from voluptuous import Schema, ExactSequence, Unordered, Unique, All, Range

419

420

# Exact sequence validation (fixed structure)

421

coordinates_validator = ExactSequence([

422

All(float, Range(min=-90, max=90)), # Latitude

423

All(float, Range(min=-180, max=180)), # Longitude

424

All(float, Range(min=0)), # Altitude (optional, positive)

425

])

426

427

rgb_color_validator = ExactSequence([

428

All(int, Range(min=0, max=255)), # Red

429

All(int, Range(min=0, max=255)), # Green

430

All(int, Range(min=0, max=255)), # Blue

431

])

432

433

# Unordered validation (flexible structure)

434

required_fields_validator = Unordered([

435

'name', # Must contain 'name'

436

'email', # Must contain 'email'

437

'age', # Must contain 'age'

438

])

439

440

# Unique elements validation

441

unique_tags = All([str], Unique())

442

unique_ids = All([int], Unique())

443

444

# Combined sequence validation

445

data_schema = Schema({

446

'coordinates': coordinates_validator, # [lat, lon, alt]

447

'color': rgb_color_validator, # [r, g, b]

448

'required_fields': required_fields_validator,

449

'tags': unique_tags, # Unique string tags

450

})

451

452

# Usage examples:

453

coordinates_validator([40.7128, -74.0060, 10.0]) # Valid coordinates

454

rgb_color_validator([255, 128, 0]) # Valid orange color

455

unique_tags(['python', 'web', 'api']) # Valid unique tags

456

```

457

458

### Exact Value Matching

459

460

Ensure value exactly matches a target value (no coercion or transformation).

461

462

```python { .api }

463

class Equal:

464

def __init__(self, target, msg=None):

465

"""

466

Ensure value exactly matches target value.

467

468

Parameters:

469

- target: Exact value that input must match

470

- msg: Custom error message

471

472

Returns:

473

Original value if it exactly matches target

474

475

Raises:

476

Invalid: If value doesn't exactly match target

477

"""

478

```

479

480

**Usage Examples:**

481

482

```python

483

from voluptuous import Schema, Equal, Any, Required

484

485

# API status validation

486

status_schema = Schema({

487

'status': Equal('OK'), # Must be exactly 'OK'

488

'version': Equal(1), # Must be exactly 1 (integer)

489

'enabled': Equal(True), # Must be exactly True (boolean)

490

})

491

492

# Configuration validation

493

config_schema = Schema({

494

'debug': Equal(False), # Must be exactly False

495

'max_retries': Equal(3), # Must be exactly 3

496

'api_version': Any(Equal('v1'), Equal('v2')), # Must be 'v1' or 'v2'

497

})

498

499

# Validate exact matches

500

status_schema({'status': 'OK', 'version': 1, 'enabled': True}) # Valid

501

config_schema({'debug': False, 'max_retries': 3, 'api_version': 'v1'}) # Valid

502

503

# These would fail:

504

# status_schema({'status': 'ok'}) # Fails: 'ok' != 'OK' (case sensitive)

505

# config_schema({'debug': 0}) # Fails: 0 != False (no coercion)

506

```

507

508

### Advanced Collection Patterns

509

510

Complex validation patterns for sophisticated collection requirements.

511

512

**Conditional Collection Validation:**

513

514

```python

515

from voluptuous import Schema, All, Any, Length, Contains

516

517

def conditional_length(data):

518

"""Different length requirements based on data type."""

519

if data.get('type') == 'premium':

520

return All([str], Length(min=5, max=20)) # Premium users get more tags

521

else:

522

return All([str], Length(min=1, max=10)) # Regular users get fewer tags

523

524

user_schema = Schema({

525

'type': In(['basic', 'premium']),

526

'tags': conditional_length,

527

})

528

```

529

530

**Nested Collection Validation:**

531

532

```python

533

from voluptuous import Schema, All, Length, Range

534

535

# Validate list of lists (matrix)

536

matrix_validator = All(

537

[[int]], # List of lists of integers

538

Length(min=1), # At least one row

539

lambda matrix: all(len(row) == len(matrix[0]) for row in matrix), # Rectangular

540

)

541

542

# Validate hierarchical data

543

tree_node = Schema({

544

'value': int,

545

'children': All([lambda x: tree_node(x)], Length(max=10)), # Recursive validation

546

})

547

```

548

549

**Collection Aggregation Validation:**

550

551

```python

552

from voluptuous import Schema, All

553

554

def sum_constraint(target_sum):

555

"""Validate that numeric list sums to target value."""

556

def validator(values):

557

if sum(values) != target_sum:

558

raise ValueError(f"Sum must equal {target_sum}, got {sum(values)}")

559

return values

560

return validator

561

562

def average_constraint(min_avg, max_avg):

563

"""Validate that numeric list average is within range."""

564

def validator(values):

565

if not values:

566

raise ValueError("Cannot calculate average of empty list")

567

avg = sum(values) / len(values)

568

if not (min_avg <= avg <= max_avg):

569

raise ValueError(f"Average {avg} not in range [{min_avg}, {max_avg}]")

570

return values

571

return validator

572

573

# Budget allocation (percentages must sum to 100)

574

budget_schema = Schema(All(

575

[All(float, Range(min=0, max=100))], # Each percentage 0-100

576

sum_constraint(100.0), # Must sum to 100%

577

Length(min=1), # At least one category

578

))

579

580

# Performance scores (average must be acceptable)

581

scores_schema = Schema(All(

582

[All(int, Range(min=0, max=100))], # Each score 0-100

583

average_constraint(70, 100), # Average must be 70-100

584

Length(min=3), # At least 3 scores

585

))

586

```