or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

class-definition.mdconfiguration-errors.mdfield-configuration.mdindex.mdutilities.mdvalidation-conversion.md

configuration-errors.mddocs/

0

# Configuration and Error Handling

1

2

Configuration options and comprehensive exception hierarchy for error handling in attrs applications. attrs provides global configuration settings and specific exceptions for different error conditions.

3

4

## Capabilities

5

6

### Configuration

7

8

#### Validator Configuration

9

10

Global control over validator execution.

11

12

```python { .api }

13

def set_run_validators(run):

14

"""

15

Globally enable or disable validator execution (deprecated).

16

17

This function is deprecated. Use attrs.validators.set_disabled() instead.

18

19

Parameters:

20

- run (bool): True to enable validators, False to disable

21

"""

22

23

def get_run_validators():

24

"""

25

Check if validators are globally enabled (deprecated).

26

27

This function is deprecated. Use attrs.validators.get_disabled() instead.

28

29

Returns:

30

bool: True if validators are enabled, False if disabled

31

"""

32

```

33

34

Modern validator control (preferred):

35

```python

36

# Using validators module

37

attrs.validators.set_disabled(False) # Enable validators

38

is_disabled = attrs.validators.get_disabled()

39

40

# Temporarily disable validators

41

with attrs.validators.disabled():

42

# Create instances without validation

43

user = User(name="", age=-5) # Would normally fail validation

44

```

45

46

### Exception Hierarchy

47

48

attrs provides a comprehensive exception hierarchy for different error conditions.

49

50

#### Base Frozen Errors

51

52

Exceptions related to immutable instance or attribute modification.

53

54

```python { .api }

55

class FrozenError(AttributeError):

56

"""

57

Base exception for frozen/immutable modification attempts.

58

59

Mirrors namedtuple behavior by subclassing AttributeError.

60

61

Attributes:

62

- msg (str): Error message "can't set attribute"

63

"""

64

65

class FrozenInstanceError(FrozenError):

66

"""

67

Raised when attempting to modify a frozen attrs instance.

68

69

Occurs when trying to set attributes on classes decorated with frozen=True

70

or @attrs.frozen.

71

"""

72

73

class FrozenAttributeError(FrozenError):

74

"""

75

Raised when attempting to modify a frozen attribute.

76

77

Occurs when trying to set attributes with on_setattr=attrs.setters.frozen.

78

"""

79

```

80

81

Usage examples:

82

```python

83

@attrs.frozen

84

class ImmutablePoint:

85

x: float

86

y: float

87

88

point = ImmutablePoint(1.0, 2.0)

89

try:

90

point.x = 5.0 # Raises FrozenInstanceError

91

except attrs.FrozenInstanceError as e:

92

print(f"Cannot modify frozen instance: {e}")

93

94

@attrs.define

95

class PartiallyImmutable:

96

mutable_field: str

97

immutable_field: str = attrs.field(on_setattr=attrs.setters.frozen)

98

99

obj = PartiallyImmutable("can change", "cannot change")

100

obj.mutable_field = "changed" # OK

101

try:

102

obj.immutable_field = "new value" # Raises FrozenAttributeError

103

except attrs.FrozenAttributeError as e:

104

print(f"Cannot modify frozen attribute: {e}")

105

```

106

107

#### Attrs Lookup Errors

108

109

Exceptions for attrs-specific operations and lookups.

110

111

```python { .api }

112

class AttrsAttributeNotFoundError(ValueError):

113

"""

114

Raised when an attrs function can't find a requested attribute.

115

116

Occurs in functions like evolve() when specifying non-existent field names.

117

"""

118

119

class NotAnAttrsClassError(ValueError):

120

"""

121

Raised when a non-attrs class is passed to an attrs function.

122

123

Occurs when calling attrs functions like fields(), asdict(), etc.

124

on regular classes.

125

"""

126

```

127

128

Usage examples:

129

```python

130

@attrs.define

131

class Person:

132

name: str

133

age: int

134

135

person = Person("Alice", 30)

136

137

try:

138

# Typo in field name

139

updated = attrs.evolve(person, namee="Bob")

140

except attrs.AttrsAttributeNotFoundError as e:

141

print(f"Field not found: {e}")

142

143

class RegularClass:

144

def __init__(self, value):

145

self.value = value

146

147

regular = RegularClass(42)

148

try:

149

attrs.fields(RegularClass) # Raises NotAnAttrsClassError

150

except attrs.NotAnAttrsClassError as e:

151

print(f"Not an attrs class: {e}")

152

```

153

154

#### Definition Errors

155

156

Exceptions related to incorrect attrs class or field definitions.

157

158

```python { .api }

159

class DefaultAlreadySetError(RuntimeError):

160

"""

161

Raised when attempting to set a default value multiple times.

162

163

Occurs when both default and factory are specified for the same field,

164

or when default is set in conflicting ways.

165

"""

166

167

class UnannotatedAttributeError(RuntimeError):

168

"""

169

Raised when type annotation is missing with auto_attribs=True.

170

171

Occurs when using @attrs.define or auto_attribs=True without proper

172

type annotations on all fields.

173

"""

174

175

class PythonTooOldError(RuntimeError):

176

"""

177

Raised when a feature requires a newer Python version.

178

179

Occurs when using attrs features that require Python versions

180

newer than the current runtime.

181

"""

182

```

183

184

Usage examples:

185

```python

186

try:

187

@attrs.define

188

class BadClass:

189

# Missing type annotation with auto_attribs (implicit with @define)

190

name = "default" # Raises UnannotatedAttributeError

191

except attrs.UnannotatedAttributeError as e:

192

print(f"Missing type annotation: {e}")

193

194

try:

195

@attrs.define

196

class ConflictingDefaults:

197

# Cannot specify both default and factory

198

value: int = attrs.field(default=0, factory=int) # Raises DefaultAlreadySetError

199

except attrs.DefaultAlreadySetError as e:

200

print(f"Conflicting defaults: {e}")

201

```

202

203

#### Validation Errors

204

205

Exceptions related to validation failures.

206

207

```python { .api }

208

class NotCallableError(TypeError):

209

"""

210

Raised when a non-callable is passed where callable is required.

211

212

Occurs when using attrs.validators.is_callable() validator

213

or when passing non-callable validators or converters.

214

"""

215

```

216

217

Usage example:

218

```python

219

@attrs.define

220

class Config:

221

callback: callable = attrs.field(validator=attrs.validators.is_callable())

222

223

try:

224

config = Config(callback="not a function") # Raises NotCallableError

225

except attrs.NotCallableError as e:

226

print(f"Not callable: {e}")

227

```

228

229

### Error Handling Patterns

230

231

#### Graceful Validation Handling

232

233

Handle validation errors gracefully in applications.

234

235

```python

236

def create_user_safely(name, age, email):

237

"""Create user with error handling."""

238

try:

239

return User(name=name, age=age, email=email)

240

except (TypeError, ValueError) as e:

241

# Handle validation errors

242

print(f"Invalid user data: {e}")

243

return None

244

245

def validate_data_batch(data_list):

246

"""Validate batch of data with individual error handling."""

247

results = []

248

errors = []

249

250

for i, data in enumerate(data_list):

251

try:

252

user = User(**data)

253

attrs.validate(user) # Explicit validation

254

results.append(user)

255

except Exception as e:

256

errors.append((i, str(e)))

257

258

return results, errors

259

```

260

261

#### Configuration Validation

262

263

Validate attrs configuration at class definition time.

264

265

```python

266

def validate_attrs_class(cls):

267

"""Validate attrs class configuration."""

268

if not attrs.has(cls):

269

raise attrs.NotAnAttrsClassError(f"{cls} is not an attrs class")

270

271

field_names = set()

272

for field in attrs.fields(cls):

273

if field.name in field_names:

274

raise ValueError(f"Duplicate field name: {field.name}")

275

field_names.add(field.name)

276

277

# Check for conflicting configurations

278

if field.default is not attrs.NOTHING and field.factory is not None:

279

raise attrs.DefaultAlreadySetError(f"Field {field.name} has both default and factory")

280

281

return True

282

```

283

284

#### Migration and Compatibility

285

286

Handle attrs version compatibility and migration issues.

287

288

```python

289

def safe_evolve(instance, **changes):

290

"""Safely evolve instance with error handling."""

291

try:

292

return attrs.evolve(instance, **changes)

293

except attrs.AttrsAttributeNotFoundError as e:

294

# Handle field name changes or typos

295

available_fields = [f.name for f in attrs.fields(instance.__class__)]

296

print(f"Available fields: {available_fields}")

297

raise ValueError(f"Cannot evolve: {e}") from e

298

299

def check_attrs_compatibility(cls):

300

"""Check if class is compatible with current attrs version."""

301

try:

302

# Try to access modern features

303

attrs.fields_dict(cls)

304

return True

305

except AttributeError:

306

# Older attrs version

307

return False

308

```

309

310

## Common Patterns

311

312

### Defensive Programming

313

```python

314

def safe_attrs_operation(obj, operation):

315

"""Safely perform attrs operations with comprehensive error handling."""

316

if not attrs.has(obj.__class__):

317

raise ValueError("Object is not an attrs instance")

318

319

try:

320

return operation(obj)

321

except attrs.FrozenInstanceError:

322

print("Cannot modify frozen instance")

323

return obj # Return unchanged

324

except attrs.AttrsAttributeNotFoundError as e:

325

print(f"Attribute not found: {e}")

326

raise

327

except Exception as e:

328

print(f"Unexpected error: {e}")

329

raise

330

```

331

332

### Configuration Management

333

```python

334

class AttrsConfig:

335

"""Centralized attrs configuration management."""

336

337

@staticmethod

338

def disable_validators():

339

"""Disable validators for performance."""

340

attrs.validators.set_disabled(True)

341

342

@staticmethod

343

def enable_validators():

344

"""Enable validators for safety."""

345

attrs.validators.set_disabled(False)

346

347

@contextmanager

348

def temporary_config(self, disable_validators=False):

349

"""Temporarily change attrs configuration."""

350

old_disabled = attrs.validators.get_disabled()

351

352

try:

353

if disable_validators:

354

attrs.validators.set_disabled(True)

355

yield

356

finally:

357

attrs.validators.set_disabled(old_disabled)

358

```

359

360

### Error Context Enhancement

361

```python

362

def enhanced_evolve(instance, **changes):

363

"""Evolve with enhanced error messages."""

364

try:

365

return attrs.evolve(instance, **changes)

366

except attrs.AttrsAttributeNotFoundError as e:

367

# Provide helpful suggestions

368

available = [f.name for f in attrs.fields(instance.__class__)]

369

suggestions = []

370

371

for change_name in changes:

372

# Simple fuzzy matching for typos

373

for field_name in available:

374

if abs(len(change_name) - len(field_name)) <= 2:

375

# Simple edit distance check

376

if sum(c1 != c2 for c1, c2 in zip(change_name, field_name)) <= 2:

377

suggestions.append(field_name)

378

379

error_msg = str(e)

380

if suggestions:

381

error_msg += f". Did you mean: {', '.join(suggestions)}?"

382

383

raise attrs.AttrsAttributeNotFoundError(error_msg) from e

384

```

385

386

### Testing Utilities

387

```python

388

def assert_attrs_equal(obj1, obj2, ignore_fields=None):

389

"""Assert two attrs objects are equal, optionally ignoring fields."""

390

if not attrs.has(obj1.__class__) or not attrs.has(obj2.__class__):

391

raise ValueError("Both objects must be attrs instances")

392

393

if obj1.__class__ != obj2.__class__:

394

raise ValueError("Objects must be of the same class")

395

396

ignore_fields = ignore_fields or []

397

398

for field in attrs.fields(obj1.__class__):

399

if field.name in ignore_fields:

400

continue

401

402

val1 = getattr(obj1, field.name)

403

val2 = getattr(obj2, field.name)

404

405

if val1 != val2:

406

raise AssertionError(f"Field {field.name} differs: {val1} != {val2}")

407

408

def create_test_instance(cls, **overrides):

409

"""Create test instance with safe defaults."""

410

try:

411

# Try to create with minimal required fields

412

required_fields = {}

413

for field in attrs.fields(cls):

414

if field.default is attrs.NOTHING and field.factory is None:

415

# Required field, provide a test default

416

if field.type == str:

417

required_fields[field.name] = "test"

418

elif field.type == int:

419

required_fields[field.name] = 0

420

# Add more type defaults as needed

421

422

required_fields.update(overrides)

423

return cls(**required_fields)

424

425

except Exception as e:

426

raise ValueError(f"Could not create test instance of {cls}: {e}")

427

```