or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-type-checking.mddoor-api.mdexceptions.mdimport-hooks.mdindex.mdtyping-compatibility.mdvalidators.md

validators.mddocs/

0

# Custom Validators

1

2

Data validation beyond basic type checking using composable validator factories. The `beartype.vale` module provides a hierarchy of subscriptable classes for creating custom validation logic that integrates seamlessly with beartype's type checking system.

3

4

## Capabilities

5

6

### Generic Validator Factory

7

8

Create validators from arbitrary callable predicates.

9

10

```python { .api }

11

class Is:

12

"""

13

Generic validator factory for callable predicates.

14

15

Creates validators from functions that return boolean values.

16

Used with typing.Annotated to add validation logic to type hints.

17

"""

18

19

def __class_getitem__(cls, predicate):

20

"""

21

Create validator from predicate function.

22

23

Parameters:

24

- predicate: callable - Function that takes object and returns bool

25

26

Returns:

27

BeartypeValidator - Validator object for use with Annotated

28

"""

29

```

30

31

Usage examples:

32

33

```python

34

from beartype import beartype

35

from beartype.vale import Is

36

from typing import Annotated

37

38

# Positive integer validator

39

PositiveInt = Annotated[int, Is[lambda x: x > 0]]

40

41

@beartype

42

def deposit(amount: PositiveInt) -> str:

43

return f"Deposited ${amount}"

44

45

deposit(100) # ✓ Valid

46

# deposit(-50) # ✗ Raises validation error

47

48

# String length validator

49

NonEmptyStr = Annotated[str, Is[lambda s: len(s) > 0]]

50

51

@beartype

52

def greet(name: NonEmptyStr) -> str:

53

return f"Hello, {name}!"

54

55

greet("Alice") # ✓ Valid

56

# greet("") # ✗ Raises validation error

57

58

# Complex validation logic

59

def is_valid_email(email: str) -> bool:

60

return "@" in email and "." in email.split("@")[-1]

61

62

Email = Annotated[str, Is[is_valid_email]]

63

64

@beartype

65

def send_email(address: Email, message: str) -> bool:

66

print(f"Sending to {address}: {message}")

67

return True

68

```

69

70

### Attribute-Based Validator Factory

71

72

Create validators based on object attributes.

73

74

```python { .api }

75

class IsAttr:

76

"""

77

Attribute-based validator factory.

78

79

Creates validators that check object attributes against predicates.

80

"""

81

82

def __class_getitem__(cls, params):

83

"""

84

Create attribute validator.

85

86

Parameters:

87

- params: str or tuple - Attribute name or (attr_name, predicate)

88

89

Returns:

90

BeartypeValidator - Validator for object attributes

91

"""

92

```

93

94

Usage examples:

95

96

```python

97

from beartype.vale import IsAttr

98

from typing import Annotated

99

from dataclasses import dataclass

100

101

# Check for attribute existence

102

HasName = Annotated[object, IsAttr["name"]]

103

104

# Check attribute value

105

HasPositiveLength = Annotated[object, IsAttr[("__len__", lambda x: x > 0)]]

106

107

@dataclass

108

class Person:

109

name: str

110

age: int

111

112

@beartype

113

def process_person(person: HasName) -> str:

114

return f"Processing {person.name}"

115

116

person = Person("Alice", 30)

117

process_person(person) # ✓ Valid

118

119

# List length validation

120

@beartype

121

def process_items(items: HasPositiveLength) -> int:

122

return len(items)

123

124

process_items([1, 2, 3]) # ✓ Valid

125

# process_items([]) # ✗ Raises validation error

126

```

127

128

### Equality-Based Validator Factory

129

130

Create validators based on equality comparisons.

131

132

```python { .api }

133

class IsEqual:

134

"""

135

Equality-based validator factory.

136

137

Creates validators that check equality against specific values.

138

"""

139

140

def __class_getitem__(cls, value):

141

"""

142

Create equality validator.

143

144

Parameters:

145

- value: Any - Value to compare against

146

147

Returns:

148

BeartypeValidator - Validator for equality checking

149

"""

150

```

151

152

Usage examples:

153

154

```python

155

from beartype.vale import IsEqual

156

from typing import Annotated, Literal

157

158

# Exact value validation

159

StatusOK = Annotated[int, IsEqual[200]]

160

StatusError = Annotated[int, IsEqual[404]]

161

ConfigMode = Annotated[str, IsEqual["production"]]

162

163

@beartype

164

def handle_response(status: StatusOK) -> str:

165

return "Request successful"

166

167

@beartype

168

def set_config(mode: ConfigMode) -> None:

169

print(f"Setting config to {mode}")

170

171

handle_response(200) # ✓ Valid

172

# handle_response(404) # ✗ Raises validation error

173

174

set_config("production") # ✓ Valid

175

# set_config("development") # ✗ Raises validation error

176

```

177

178

### Instance Type Validator Factory

179

180

Create validators based on isinstance() checks.

181

182

```python { .api }

183

class IsInstance:

184

"""

185

Instance type validator factory.

186

187

Creates validators using isinstance() checks against type or tuple of types.

188

"""

189

190

def __class_getitem__(cls, types):

191

"""

192

Create isinstance validator.

193

194

Parameters:

195

- types: type or tuple - Type(s) for isinstance check

196

197

Returns:

198

BeartypeValidator - Validator using isinstance()

199

"""

200

```

201

202

Usage examples:

203

204

```python

205

from beartype.vale import IsInstance

206

from typing import Annotated, Union

207

import datetime

208

209

# Single type validation

210

NumericValue = Annotated[Union[int, float], IsInstance[int, float]]

211

212

# Multiple type validation

213

DateTimeValue = Annotated[object, IsInstance[(datetime.date, datetime.datetime)]]

214

215

@beartype

216

def calculate(value: NumericValue) -> float:

217

return float(value) * 2.0

218

219

@beartype

220

def format_date(dt: DateTimeValue) -> str:

221

return dt.strftime("%Y-%m-%d")

222

223

calculate(42) # ✓ Valid (int)

224

calculate(3.14) # ✓ Valid (float)

225

# calculate("42") # ✗ Raises validation error

226

227

format_date(datetime.date.today()) # ✓ Valid

228

format_date(datetime.datetime.now()) # ✓ Valid

229

# format_date("2023-01-01") # ✗ Raises validation error

230

```

231

232

### Subclass Validator Factory

233

234

Create validators based on issubclass() checks.

235

236

```python { .api }

237

class IsSubclass:

238

"""

239

Subclass validator factory.

240

241

Creates validators using issubclass() checks against type or tuple of types.

242

"""

243

244

def __class_getitem__(cls, types):

245

"""

246

Create issubclass validator.

247

248

Parameters:

249

- types: type or tuple - Type(s) for issubclass check

250

251

Returns:

252

BeartypeValidator - Validator using issubclass()

253

"""

254

```

255

256

Usage examples:

257

258

```python

259

from beartype.vale import IsSubclass

260

from typing import Annotated

261

from abc import ABC, abstractmethod

262

263

# Abstract base class

264

class Drawable(ABC):

265

@abstractmethod

266

def draw(self): pass

267

268

class Shape(Drawable):

269

def draw(self): return "Drawing shape"

270

271

class Circle(Shape):

272

def draw(self): return "Drawing circle"

273

274

# Validator for subclasses

275

DrawableClass = Annotated[type, IsSubclass[Drawable]]

276

ShapeClass = Annotated[type, IsSubclass[Shape]]

277

278

@beartype

279

def create_drawable(cls: DrawableClass) -> Drawable:

280

return cls()

281

282

@beartype

283

def create_shape(cls: ShapeClass) -> Shape:

284

return cls()

285

286

create_drawable(Circle) # ✓ Valid

287

create_shape(Circle) # ✓ Valid

288

# create_drawable(str) # ✗ Raises validation error

289

```

290

291

### Combining Validators

292

293

Multiple validators can be combined using multiple annotations:

294

295

```python

296

from beartype.vale import Is, IsAttr

297

from typing import Annotated

298

299

# Multiple validation constraints

300

ValidUser = Annotated[

301

object,

302

IsAttr["name"], # Must have name attribute

303

IsAttr[("age", lambda x: x >= 18)], # Age must be >= 18

304

Is[lambda obj: len(obj.name) > 0] # Name must be non-empty

305

]

306

307

@dataclass

308

class User:

309

name: str

310

age: int

311

312

@beartype

313

def register_user(user: ValidUser) -> str:

314

return f"Registered {user.name}, age {user.age}"

315

316

valid_user = User("Alice", 25)

317

register_user(valid_user) # ✓ Valid

318

319

invalid_user = User("", 16)

320

# register_user(invalid_user) # ✗ Raises validation error

321

```

322

323

### Advanced Patterns

324

325

#### Conditional Validation

326

327

```python

328

from beartype.vale import Is

329

from typing import Annotated, Union

330

331

def validate_id(obj) -> bool:

332

if isinstance(obj, str):

333

return obj.isalnum() and len(obj) >= 3

334

elif isinstance(obj, int):

335

return obj > 0

336

return False

337

338

FlexibleID = Annotated[Union[str, int], Is[validate_id]]

339

340

@beartype

341

def lookup_item(item_id: FlexibleID) -> str:

342

return f"Item {item_id}"

343

344

lookup_item("abc123") # ✓ Valid string ID

345

lookup_item(42) # ✓ Valid numeric ID

346

# lookup_item("ab") # ✗ Too short

347

# lookup_item(-1) # ✗ Negative number

348

```

349

350

#### Custom Exception Messages

351

352

```python

353

from beartype.vale import Is

354

from typing import Annotated

355

356

def positive_with_message(x: int) -> bool:

357

if x <= 0:

358

raise ValueError(f"Expected positive integer, got {x}")

359

return True

360

361

PositiveInt = Annotated[int, Is[positive_with_message]]

362

363

@beartype

364

def process_positive(value: PositiveInt) -> int:

365

return value * 2

366

367

try:

368

process_positive(-5)

369

except Exception as e:

370

print(f"Validation failed: {e}")

371

```