or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-models.mddataclasses-adapters.mderror-handling.mdindex.mdjson-schema.mdplugins.mdserialization-config.mdtype-system.mdvalidation-system.md

validation-system.mddocs/

0

# Validation System

1

2

Decorators and functions for custom validation logic, including field validators, model validators, and functional validation utilities.

3

4

## Capabilities

5

6

### Field Validators

7

8

Decorators for creating custom field validation logic that runs during model validation.

9

10

```python { .api }

11

def field_validator(*fields, mode='before', check_fields=None):

12

"""

13

Decorator for field validation methods.

14

15

Args:

16

*fields (str): Field names to validate

17

mode (str): Validation mode ('before', 'after', 'wrap', 'plain')

18

check_fields (bool, optional): Whether to check if fields exist

19

20

Returns:

21

Decorator function

22

"""

23

24

@field_validator('field_name')

25

@classmethod

26

def validate_field(cls, v):

27

"""

28

Template for field validator method.

29

30

Args:

31

v: Field value to validate

32

33

Returns:

34

Validated value

35

36

Raises:

37

ValueError: If validation fails

38

"""

39

```

40

41

### Model Validators

42

43

Decorators for creating validation logic that operates on the entire model or multiple fields.

44

45

```python { .api }

46

def model_validator(*, mode):

47

"""

48

Decorator for model validation methods.

49

50

Args:

51

mode (str): Validation mode ('before', 'after', 'wrap')

52

53

Returns:

54

Decorator function

55

"""

56

57

@model_validator(mode='after')

58

@classmethod

59

def validate_model(cls, values):

60

"""

61

Template for model validator method.

62

63

Args:

64

values: Model values (dict for 'before', model instance for 'after')

65

66

Returns:

67

Validated values or model instance

68

69

Raises:

70

ValueError: If validation fails

71

"""

72

```

73

74

### Function Validation

75

76

Decorator to add pydantic validation to regular functions.

77

78

```python { .api }

79

def validate_call(*, config=None, validate_return=False):

80

"""

81

Decorator to validate function arguments and optionally return values.

82

83

Args:

84

config: Validation configuration

85

validate_return (bool): Whether to validate return values

86

87

Returns:

88

Decorated function with validation

89

"""

90

```

91

92

### BeforeValidator and AfterValidator

93

94

Functional validators that can be used with Annotated types.

95

96

```python { .api }

97

class BeforeValidator:

98

"""

99

Validator that runs before pydantic's internal validation.

100

"""

101

102

def __init__(self, func):

103

"""

104

Initialize validator.

105

106

Args:

107

func: Validation function

108

"""

109

110

class AfterValidator:

111

"""

112

Validator that runs after pydantic's internal validation.

113

"""

114

115

def __init__(self, func):

116

"""

117

Initialize validator.

118

119

Args:

120

func: Validation function

121

"""

122

123

class WrapValidator:

124

"""

125

Validator that wraps pydantic's internal validation.

126

"""

127

128

def __init__(self, func):

129

"""

130

Initialize validator.

131

132

Args:

133

func: Validation function

134

"""

135

136

class PlainValidator:

137

"""

138

Validator that completely replaces pydantic's internal validation.

139

"""

140

141

def __init__(self, func):

142

"""

143

Initialize validator.

144

145

Args:

146

func: Validation function

147

"""

148

```

149

150

### Core Schema Classes

151

152

Core classes from pydantic-core that are part of the validation API.

153

154

```python { .api }

155

class ValidationInfo:

156

"""

157

Information available during validation.

158

"""

159

160

@property

161

def config(self):

162

"""ConfigDict: Current model configuration"""

163

164

@property

165

def context(self):

166

"""dict | None: Validation context"""

167

168

@property

169

def data(self):

170

"""dict: Raw input data"""

171

172

@property

173

def field_name(self):

174

"""str | None: Current field name"""

175

176

@property

177

def mode(self):

178

"""str: Validation mode ('python' or 'json')"""

179

180

class ValidatorFunctionWrapHandler:

181

"""

182

Handler for wrap validators.

183

"""

184

185

def __call__(self, value):

186

"""

187

Call the wrapped validator.

188

189

Args:

190

value: Value to validate

191

192

Returns:

193

Validated value

194

"""

195

```

196

197

## Usage Examples

198

199

### Field Validators

200

201

```python

202

from pydantic import BaseModel, field_validator

203

import re

204

205

class User(BaseModel):

206

name: str

207

email: str

208

age: int

209

210

@field_validator('name')

211

@classmethod

212

def validate_name(cls, v):

213

if not v.strip():

214

raise ValueError('Name cannot be empty')

215

return v.title()

216

217

@field_validator('email')

218

@classmethod

219

def validate_email(cls, v):

220

pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'

221

if not re.match(pattern, v):

222

raise ValueError('Invalid email format')

223

return v.lower()

224

225

@field_validator('age')

226

@classmethod

227

def validate_age(cls, v):

228

if v < 0:

229

raise ValueError('Age cannot be negative')

230

if v > 150:

231

raise ValueError('Age seems unrealistic')

232

return v

233

234

# Usage

235

user = User(name="john doe", email="JOHN@EXAMPLE.COM", age=30)

236

print(user.name) # "John Doe"

237

print(user.email) # "john@example.com"

238

```

239

240

### Model Validators

241

242

```python

243

from pydantic import BaseModel, model_validator

244

from typing import Optional

245

246

class DateRange(BaseModel):

247

start_date: str

248

end_date: str

249

duration_days: Optional[int] = None

250

251

@model_validator(mode='after')

252

def validate_date_range(self):

253

from datetime import datetime

254

255

start = datetime.fromisoformat(self.start_date)

256

end = datetime.fromisoformat(self.end_date)

257

258

if start >= end:

259

raise ValueError('start_date must be before end_date')

260

261

# Calculate duration if not provided

262

if self.duration_days is None:

263

self.duration_days = (end - start).days

264

265

return self

266

267

# Usage

268

date_range = DateRange(

269

start_date="2023-01-01",

270

end_date="2023-01-10"

271

)

272

print(date_range.duration_days) # 9

273

```

274

275

### Function Validation

276

277

```python

278

from pydantic import validate_call

279

from typing import List

280

281

@validate_call

282

def process_data(

283

data: List[int],

284

multiplier: float = 1.0,

285

max_value: int = 100

286

) -> List[int]:

287

"""

288

Process list of integers with validation.

289

"""

290

result = []

291

for item in data:

292

processed = int(item * multiplier)

293

if processed > max_value:

294

processed = max_value

295

result.append(processed)

296

return result

297

298

# Usage - arguments are validated automatically

299

result = process_data([1, 2, 3], multiplier=2.5, max_value=50)

300

print(result) # [2, 5, 7]

301

302

# This would raise ValidationError

303

# process_data("not a list", multiplier=2.5)

304

```

305

306

### Functional Validators with Annotated

307

308

```python

309

from pydantic import BaseModel, BeforeValidator, AfterValidator

310

from typing import Annotated

311

312

def normalize_string(v):

313

"""Normalize string by stripping and converting to lowercase."""

314

if isinstance(v, str):

315

return v.strip().lower()

316

return v

317

318

def validate_positive(v):

319

"""Ensure value is positive."""

320

if v <= 0:

321

raise ValueError('Value must be positive')

322

return v

323

324

class Product(BaseModel):

325

name: Annotated[str, BeforeValidator(normalize_string)]

326

price: Annotated[float, AfterValidator(validate_positive)]

327

328

# Usage

329

product = Product(name=" LAPTOP ", price=999.99)

330

print(product.name) # "laptop"

331

print(product.price) # 999.99

332

```

333

334

### Validation with Context

335

336

```python

337

from pydantic import BaseModel, field_validator, ValidationInfo

338

339

class SecurityModel(BaseModel):

340

username: str

341

role: str

342

343

@field_validator('role')

344

@classmethod

345

def validate_role(cls, v, info: ValidationInfo):

346

# Access validation context

347

if info.context and 'allowed_roles' in info.context:

348

allowed = info.context['allowed_roles']

349

if v not in allowed:

350

raise ValueError(f'Role must be one of: {allowed}')

351

return v

352

353

# Usage with context

354

context = {'allowed_roles': ['admin', 'user', 'guest']}

355

user = SecurityModel.model_validate(

356

{'username': 'john', 'role': 'admin'},

357

context=context

358

)

359

```