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

utilities.mddocs/

0

# Utilities and Introspection

1

2

Functions for working with attrs classes including serialization, introspection, instance manipulation, and runtime type resolution. These utilities enable dynamic interaction with attrs classes and instances.

3

4

## Capabilities

5

6

### Serialization

7

8

#### Dictionary Conversion

9

10

Convert attrs instances to dictionaries with comprehensive customization options.

11

12

```python { .api }

13

def asdict(

14

inst,

15

recurse=True,

16

filter=None,

17

dict_factory=dict,

18

retain_collection_types=False,

19

value_serializer=None,

20

):

21

"""

22

Convert attrs instance to dictionary.

23

24

Parameters:

25

- inst: Attrs instance to convert

26

- recurse (bool): Recursively convert nested attrs instances (default: True)

27

- filter (callable, optional): Function to filter attributes (attr, value) -> bool

28

- dict_factory (callable): Factory for creating dictionaries (default: dict)

29

- retain_collection_types (bool): Keep original collection types vs converting to list

30

- value_serializer (callable, optional): Hook to serialize individual values

31

32

Returns:

33

Dictionary representation of the instance

34

35

Raises:

36

NotAnAttrsClassError: If inst is not an attrs instance

37

"""

38

```

39

40

Usage examples:

41

```python

42

@attrs.define

43

class Person:

44

name: str

45

age: int

46

email: str = ""

47

48

person = Person("Alice", 30, "alice@example.com")

49

50

# Basic conversion

51

data = attrs.asdict(person)

52

# {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}

53

54

# With filter to exclude empty strings

55

data = attrs.asdict(person, filter=lambda attr, value: value != "")

56

# {'name': 'Alice', 'age': 30}

57

58

# Using OrderedDict

59

from collections import OrderedDict

60

data = attrs.asdict(person, dict_factory=OrderedDict)

61

62

# Custom value serialization

63

def serialize_dates(inst, field, value):

64

if isinstance(value, datetime):

65

return value.isoformat()

66

return value

67

68

data = attrs.asdict(person, value_serializer=serialize_dates)

69

```

70

71

#### Tuple Conversion

72

73

Convert attrs instances to tuples.

74

75

```python { .api }

76

def astuple(

77

inst,

78

recurse=True,

79

filter=None,

80

):

81

"""

82

Convert attrs instance to tuple.

83

84

Parameters:

85

- inst: Attrs instance to convert

86

- recurse (bool): Recursively convert nested attrs instances (default: True)

87

- filter (callable, optional): Function to filter attributes (attr, value) -> bool

88

89

Returns:

90

Tuple representation of the instance in field definition order

91

92

Raises:

93

NotAnAttrsClassError: If inst is not an attrs instance

94

"""

95

```

96

97

Usage example:

98

```python

99

person = Person("Alice", 30, "alice@example.com")

100

data = attrs.astuple(person)

101

# ('Alice', 30, 'alice@example.com')

102

103

# With filter

104

data = attrs.astuple(person, filter=lambda attr, value: attr.name != 'email')

105

# ('Alice', 30)

106

```

107

108

### Introspection

109

110

#### Class Inspection

111

112

Examine attrs classes and their field definitions.

113

114

```python { .api }

115

def has(cls):

116

"""

117

Check if class is an attrs class.

118

119

Parameters:

120

- cls: Class to check

121

122

Returns:

123

bool: True if class was decorated with attrs, False otherwise

124

"""

125

126

def fields(cls):

127

"""

128

Get field information for attrs class.

129

130

Parameters:

131

- cls: Attrs class to inspect

132

133

Returns:

134

tuple[Attribute, ...]: Tuple of Attribute objects for the class

135

136

Raises:

137

NotAnAttrsClassError: If cls is not an attrs class

138

"""

139

140

def fields_dict(cls):

141

"""

142

Get field information as ordered dictionary.

143

144

Parameters:

145

- cls: Attrs class to inspect

146

147

Returns:

148

dict[str, Attribute]: Ordered dict mapping field names to Attribute objects

149

150

Raises:

151

NotAnAttrsClassError: If cls is not an attrs class

152

"""

153

```

154

155

Usage examples:

156

```python

157

@attrs.define

158

class Person:

159

name: str

160

age: int = 0

161

162

# Check if attrs class

163

print(attrs.has(Person)) # True

164

print(attrs.has(dict)) # False

165

166

# Get fields

167

field_tuple = attrs.fields(Person)

168

print(field_tuple[0].name) # 'name'

169

print(field_tuple[1].default) # 0

170

171

# Get fields as dict

172

field_dict = attrs.fields_dict(Person)

173

print(field_dict['name'].type) # <class 'str'>

174

print(field_dict['age'].default) # 0

175

```

176

177

#### Attribute Information

178

179

The `Attribute` class provides detailed information about each field.

180

181

```python { .api }

182

class Attribute:

183

"""

184

Immutable representation of an attrs attribute.

185

186

Read-only properties:

187

- name (str): Name of the attribute

188

- default: Default value (NOTHING if no default)

189

- factory: Factory function for default values

190

- validator: Validation function or list of functions

191

- converter: Conversion function or list of functions

192

- type: Type annotation

193

- kw_only (bool): Whether attribute is keyword-only

194

- eq (bool): Whether included in equality comparison

195

- order (bool): Whether included in ordering comparison

196

- hash (bool): Whether included in hash calculation

197

- init (bool): Whether included in __init__

198

- repr (bool): Whether included in __repr__

199

- metadata (dict): Arbitrary metadata dictionary

200

- on_setattr: Setter functions for attribute changes

201

- alias (str): Alternative name for __init__ parameter

202

"""

203

```

204

205

Usage example:

206

```python

207

@attrs.define

208

class Config:

209

name: str = attrs.field(metadata={"required": True})

210

debug: bool = attrs.field(default=False, repr=False)

211

212

for field in attrs.fields(Config):

213

print(f"Field: {field.name}")

214

print(f" Type: {field.type}")

215

print(f" Default: {field.default}")

216

print(f" Metadata: {field.metadata}")

217

print(f" In repr: {field.repr}")

218

```

219

220

### Instance Manipulation

221

222

#### Instance Evolution

223

224

Create modified copies of attrs instances.

225

226

```python { .api }

227

def evolve(*args, **changes):

228

"""

229

Create new instance with specified changes.

230

231

Parameters:

232

- *args: Instance to evolve (can be positional or keyword)

233

- **changes: Field values to change

234

235

Returns:

236

New instance of same type with specified changes

237

238

Raises:

239

AttrsAttributeNotFoundError: If change refers to non-existent field

240

TypeError: If instance is not an attrs instance

241

"""

242

```

243

244

Usage examples:

245

```python

246

@attrs.define

247

class Person:

248

name: str

249

age: int

250

email: str = ""

251

252

person = Person("Alice", 30, "alice@example.com")

253

254

# Create older version

255

older_person = attrs.evolve(person, age=31)

256

print(older_person) # Person(name='Alice', age=31, email='alice@example.com')

257

258

# Multiple changes

259

updated_person = attrs.evolve(person, age=25, email="alice.new@example.com")

260

```

261

262

#### Legacy Association (Deprecated)

263

264

```python { .api }

265

def assoc(inst, **changes):

266

"""

267

Create new instance with changes (deprecated - use evolve instead).

268

269

Parameters:

270

- inst: Instance to modify

271

- **changes: Field values to change

272

273

Returns:

274

New instance with specified changes

275

"""

276

```

277

278

### Validation and Type Resolution

279

280

#### Instance Validation

281

282

Validate all attributes on an instance.

283

284

```python { .api }

285

def validate(inst):

286

"""

287

Validate all attributes on an attrs instance.

288

289

Runs all validators defined on the instance's fields.

290

291

Parameters:

292

- inst: Attrs instance to validate

293

294

Raises:

295

Various validation errors depending on validators

296

NotAnAttrsClassError: If inst is not an attrs instance

297

"""

298

```

299

300

Usage example:

301

```python

302

@attrs.define

303

class Person:

304

name: str = attrs.field(validator=attrs.validators.instance_of(str))

305

age: int = attrs.field(validator=attrs.validators.instance_of(int))

306

307

person = Person("Alice", 30)

308

attrs.validate(person) # Passes

309

310

# This would raise validation error:

311

# person.age = "thirty"

312

# attrs.validate(person)

313

```

314

315

#### Type Resolution

316

317

Resolve string type annotations to actual types.

318

319

```python { .api }

320

def resolve_types(cls, globalns=None, localns=None, attribs=None, include_extras=True):

321

"""

322

Resolve string type annotations to actual types.

323

324

Parameters:

325

- cls: Attrs class with string type annotations

326

- globalns (dict, optional): Global namespace for type resolution

327

- localns (dict, optional): Local namespace for type resolution

328

- attribs (list, optional): Specific attributes to resolve

329

- include_extras (bool): Include typing_extensions types (default: True)

330

331

Returns:

332

Attrs class with resolved type annotations

333

"""

334

```

335

336

Usage example:

337

```python

338

# Forward reference scenario

339

@attrs.define

340

class Node:

341

value: int

342

parent: "Optional[Node]" = None # Forward reference

343

children: "List[Node]" = attrs.field(factory=list)

344

345

# Resolve forward references

346

Node = attrs.resolve_types(Node, globalns=globals())

347

```

348

349

### Filtering

350

351

Create filters for use with `asdict` and `astuple`.

352

353

```python { .api }

354

def include(*what):

355

"""

356

Create filter that includes only specified items.

357

358

Parameters:

359

- *what: Types, attribute names, or Attribute objects to include

360

361

Returns:

362

Filter function for use with asdict/astuple

363

"""

364

365

def exclude(*what):

366

"""

367

Create filter that excludes specified items.

368

369

Parameters:

370

- *what: Types, attribute names, or Attribute objects to exclude

371

372

Returns:

373

Filter function for use with asdict/astuple

374

"""

375

```

376

377

Usage examples:

378

```python

379

@attrs.define

380

class Person:

381

name: str

382

age: int

383

email: str = ""

384

password: str = attrs.field(repr=False)

385

386

person = Person("Alice", 30, "alice@example.com", "secret123")

387

388

# Include only specific fields

389

data = attrs.asdict(person, filter=attrs.filters.include("name", "age"))

390

# {'name': 'Alice', 'age': 30}

391

392

# Exclude sensitive fields

393

data = attrs.asdict(person, filter=attrs.filters.exclude("password"))

394

# {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}

395

396

# Exclude by attribute object

397

password_field = attrs.fields_dict(Person)["password"]

398

data = attrs.asdict(person, filter=attrs.filters.exclude(password_field))

399

```

400

401

### Comparison Utilities

402

403

#### Custom Comparison

404

405

Create classes with custom comparison behavior.

406

407

```python { .api }

408

def cmp_using(

409

eq=None,

410

lt=None,

411

le=None,

412

gt=None,

413

ge=None,

414

require_same_type=True,

415

class_name="Comparable",

416

):

417

"""

418

Create class with custom comparison methods.

419

420

Parameters:

421

- eq (callable, optional): Equality comparison function

422

- lt (callable, optional): Less-than comparison function

423

- le (callable, optional): Less-equal comparison function

424

- gt (callable, optional): Greater-than comparison function

425

- ge (callable, optional): Greater-equal comparison function

426

- require_same_type (bool): Require same type for comparisons

427

- class_name (str): Name for the generated class

428

429

Returns:

430

Class with custom comparison behavior

431

"""

432

```

433

434

Usage example:

435

```python

436

@attrs.define

437

class Version:

438

major: int

439

minor: int

440

patch: int

441

442

# Custom comparison based on semantic versioning

443

def _cmp_key(self):

444

return (self.major, self.minor, self.patch)

445

446

# Create comparable version class

447

ComparableVersion = attrs.cmp_using(

448

eq=lambda self, other: self._cmp_key() == other._cmp_key(),

449

lt=lambda self, other: self._cmp_key() < other._cmp_key(),

450

class_name="ComparableVersion"

451

)

452

453

# Use in attrs class

454

@attrs.define

455

class Package(ComparableVersion):

456

name: str

457

version: Version

458

```

459

460

## Common Patterns

461

462

### Serialization with Custom Logic

463

```python

464

@attrs.define

465

class TimestampedData:

466

data: dict

467

created_at: datetime = attrs.field(factory=datetime.now)

468

469

def serialize_timestamps(inst, field, value):

470

if isinstance(value, datetime):

471

return value.isoformat()

472

return value

473

474

# Serialize with timestamp formatting

475

data = attrs.asdict(

476

instance,

477

value_serializer=serialize_timestamps

478

)

479

```

480

481

### Dynamic Field Access

482

```python

483

def get_field_info(cls, field_name):

484

"""Get information about a specific field."""

485

if not attrs.has(cls):

486

raise ValueError("Not an attrs class")

487

488

field_dict = attrs.fields_dict(cls)

489

if field_name not in field_dict:

490

raise ValueError(f"Field {field_name} not found")

491

492

field = field_dict[field_name]

493

return {

494

"name": field.name,

495

"type": field.type,

496

"default": field.default,

497

"has_validator": field.validator is not None,

498

"has_converter": field.converter is not None,

499

}

500

```

501

502

### Conditional Serialization

503

```python

504

def serialize_for_api(instance, include_private=False):

505

"""Serialize instance for API with privacy controls."""

506

def api_filter(attr, value):

507

# Exclude private fields unless requested

508

if not include_private and attr.name.startswith('_'):

509

return False

510

# Exclude None values

511

if value is None:

512

return False

513

return True

514

515

return attrs.asdict(instance, filter=api_filter)

516

```

517

518

### Bulk Operations with Evolution

519

```python

520

def update_all_ages(people, age_increment):

521

"""Update ages for multiple people efficiently."""

522

return [

523

attrs.evolve(person, age=person.age + age_increment)

524

for person in people

525

]

526

527

def merge_configs(base_config, updates):

528

"""Merge configuration updates into base config."""

529

return attrs.evolve(base_config, **updates)

530

```