or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-integration.mdcore-assertions.mdextensions.mdfilters.mdindex.mdmatchers.md

extensions.mddocs/

0

# Extensions

1

2

Syrupy's extensible serialization and storage system supporting multiple output formats. Extensions determine how data is serialized and where snapshots are stored, allowing customization for different data types and use cases.

3

4

## Capabilities

5

6

### Base Extension Classes

7

8

Abstract base classes for creating custom extensions with serialization, storage, and comparison capabilities.

9

10

```python { .api }

11

class AbstractSyrupyExtension:

12

"""

13

Base class combining serialization, storage, and reporting.

14

15

All extension classes should inherit from this base.

16

"""

17

18

def get_snapshot_name(self, *, index: SnapshotIndex = 0, **kwargs) -> str:

19

"""

20

Generate snapshot name for given index.

21

22

Parameters:

23

- index: Snapshot index (int or str)

24

- **kwargs: Additional naming options

25

26

Returns:

27

str: Generated snapshot name

28

"""

29

30

def get_location(self, *, test_location: "PyTestLocation") -> str:

31

"""

32

Get storage location for snapshots.

33

34

Parameters:

35

- test_location: Test file location info

36

37

Returns:

38

str: Storage location path

39

"""

40

41

def discover_snapshots(self, *, location: str) -> "SnapshotCollections":

42

"""

43

Discover existing snapshots at location.

44

45

Parameters:

46

- location: Location to search

47

48

Returns:

49

SnapshotCollections: Found snapshot collections

50

"""

51

52

class SnapshotSerializer:

53

def serialize(self, data: SerializableData, **kwargs) -> SerializedData:

54

"""

55

Serialize data for snapshot storage.

56

57

Parameters:

58

- data: Data to serialize

59

- **kwargs: Additional serialization options

60

61

Returns:

62

SerializedData: Serialized representation (str or bytes)

63

"""

64

65

class SnapshotCollectionStorage:

66

def read_snapshot(self, snapshot_location: str, snapshot_name: str) -> SerializedData:

67

"""

68

Read snapshot data from storage.

69

70

Parameters:

71

- snapshot_location: Location identifier for snapshot

72

- snapshot_name: Name identifier for specific snapshot

73

74

Returns:

75

SerializedData: Stored snapshot data

76

"""

77

78

def write_snapshot(self, snapshot_data: SerializedData, snapshot_location: str, snapshot_name: str) -> None:

79

"""

80

Write snapshot data to storage.

81

82

Parameters:

83

- snapshot_data: Serialized data to store

84

- snapshot_location: Location identifier for snapshot

85

- snapshot_name: Name identifier for specific snapshot

86

"""

87

```

88

89

### Amber Extension (Default)

90

91

Default multi-snapshot extension creating .ambr files with comprehensive Python object serialization.

92

93

```python { .api }

94

class AmberSnapshotExtension(AbstractSyrupyExtension):

95

"""

96

Default extension creating .ambr files with multiple snapshots per file.

97

98

Features:

99

- Multi-snapshot storage in single file

100

- Deep object introspection and serialization

101

- Support for most Python data types

102

- Human-readable text format

103

"""

104

105

class AmberDataSerializer(SnapshotSerializer):

106

"""

107

Serializes Python objects to amber format with comprehensive type handling.

108

109

Supports:

110

- Built-in types (dict, list, tuple, set, etc.)

111

- Custom objects with __repr__ or __dict__

112

- Named tuples and dataclasses

113

- Complex nested structures

114

"""

115

116

@staticmethod

117

def object_as_named_tuple(obj: Any) -> Any:

118

"""

119

Convert object to named tuple representation bypassing custom __repr__.

120

121

Parameters:

122

- obj: Object to convert

123

124

Returns:

125

Any: Named tuple representation of object

126

"""

127

```

128

129

Usage examples:

130

131

```python

132

def test_amber_default(snapshot):

133

# Uses AmberSnapshotExtension by default

134

data = {

135

"users": [

136

{"name": "Alice", "scores": [85, 92]},

137

{"name": "Bob", "scores": [78, 91]}

138

],

139

"metadata": {"version": "1.0", "timestamp": "2023-12-01"}

140

}

141

assert data == snapshot

142

143

def test_custom_repr_bypass(snapshot):

144

from syrupy.extensions.amber.serializer import AmberDataSerializer

145

146

class CustomClass:

147

def __init__(self, value):

148

self.value = value

149

150

def __repr__(self):

151

return "CustomClass(...)" # Hides internal state

152

153

obj = CustomClass("important_data")

154

155

# Bypass custom __repr__ to show actual structure

156

assert AmberDataSerializer.object_as_named_tuple(obj) == snapshot

157

```

158

159

### Single File Extensions

160

161

Extensions that create one file per test case, suitable for binary data and individual snapshots.

162

163

```python { .api }

164

class SingleFileSnapshotExtension(AbstractSyrupyExtension):

165

"""

166

Base class for single-file extensions creating one .raw file per test.

167

168

Features:

169

- One file per test case

170

- Binary and text mode support

171

- Raw data storage without additional formatting

172

"""

173

174

class WriteMode(Enum):

175

BINARY = "b"

176

TEXT = "t"

177

178

class SingleFileAmberSnapshotExtension(SingleFileSnapshotExtension):

179

"""

180

Amber extension variant creating one file per snapshot.

181

182

Combines amber serialization with single-file storage.

183

"""

184

```

185

186

Usage examples:

187

188

```python

189

def test_single_file_raw(snapshot):

190

from syrupy.extensions.single_file import SingleFileSnapshotExtension

191

192

# Binary data stored as-is in .raw file

193

binary_data = b"\\x00\\x01\\x02\\x03"

194

assert binary_data == snapshot.use_extension(SingleFileSnapshotExtension)

195

196

def test_single_file_amber(snapshot):

197

from syrupy.extensions.single_file import SingleFileAmberSnapshotExtension

198

199

# Amber serialization but one file per test

200

data = {"config": {"debug": True, "port": 8080}}

201

assert data == snapshot.use_extension(SingleFileAmberSnapshotExtension)

202

```

203

204

### Image Extensions

205

206

Specialized extensions for image data supporting PNG and SVG formats.

207

208

```python { .api }

209

class PNGImageSnapshotExtension(SingleFileSnapshotExtension):

210

"""

211

Extension for PNG image snapshots creating .png files.

212

213

Expects byte string input representing PNG image data.

214

"""

215

216

class SVGImageSnapshotExtension(SingleFileSnapshotExtension):

217

"""

218

Extension for SVG image snapshots creating .svg files.

219

220

Expects string input containing SVG markup.

221

"""

222

```

223

224

Usage examples:

225

226

```python

227

def test_png_image(snapshot):

228

from syrupy.extensions.image import PNGImageSnapshotExtension

229

230

# PNG image as bytes (from PIL, matplotlib, etc.)

231

png_bytes = create_chart_image() # Returns bytes

232

assert png_bytes == snapshot.use_extension(PNGImageSnapshotExtension)

233

234

def test_svg_image(snapshot):

235

from syrupy.extensions.image import SVGImageSnapshotExtension

236

237

# SVG as string markup

238

svg_content = '''

239

<svg width="100" height="100">

240

<circle cx="50" cy="50" r="25" fill="blue"/>

241

</svg>

242

'''

243

assert svg_content == snapshot.use_extension(SVGImageSnapshotExtension)

244

```

245

246

### JSON Extension

247

248

Extension for clean JSON output ideal for API responses and structured data.

249

250

```python { .api }

251

class JSONSnapshotExtension(AbstractSyrupyExtension):

252

"""

253

Extension creating .json files with proper JSON formatting.

254

255

Features:

256

- Clean, properly indented JSON output

257

- Support for dictionaries and lists

258

- Custom serialization for non-JSON types

259

- Human-readable formatting

260

"""

261

```

262

263

Usage examples:

264

265

```python

266

def test_api_response(snapshot):

267

from syrupy.extensions.json import JSONSnapshotExtension

268

269

# API response data

270

response = {

271

"status": "success",

272

"data": {

273

"user": {"id": 123, "name": "Alice"},

274

"permissions": ["read", "write"]

275

},

276

"meta": {"timestamp": "2023-12-01T10:00:00Z"}

277

}

278

279

assert response == snapshot.use_extension(JSONSnapshotExtension)

280

281

def test_list_data(snapshot):

282

from syrupy.extensions.json import JSONSnapshotExtension

283

284

# List of structured data

285

users = [

286

{"id": 1, "name": "Alice", "active": True},

287

{"id": 2, "name": "Bob", "active": False}

288

]

289

290

assert users == snapshot.use_extension(JSONSnapshotExtension)

291

```

292

293

### Default Extension Configuration

294

295

Global configuration for default extension class used across all snapshots.

296

297

```python { .api }

298

DEFAULT_EXTENSION = AmberSnapshotExtension

299

```

300

301

Usage with CLI:

302

303

```bash

304

# Change default extension class

305

pytest --snapshot-default-extension=syrupy.extensions.json.JSONSnapshotExtension

306

307

# Update snapshots with custom extension

308

pytest --snapshot-update --snapshot-default-extension=syrupy.extensions.json.JSONSnapshotExtension

309

```

310

311

## Custom Extension Development

312

313

To create custom extensions, inherit from AbstractSyrupyExtension and implement required methods:

314

315

```python

316

from syrupy.extensions.base import AbstractSyrupyExtension

317

318

class MyCustomExtension(AbstractSyrupyExtension):

319

def serialize(self, data, **kwargs):

320

# Custom serialization logic

321

return str(data)

322

323

def get_file_extension(self):

324

return "custom"

325

326

def read_snapshot(self, snapshot_location, snapshot_name):

327

# Custom read logic

328

pass

329

330

def write_snapshot(self, snapshot_data, snapshot_location, snapshot_name):

331

# Custom write logic

332

pass

333

334

# Usage in tests

335

def test_with_custom_extension(snapshot):

336

assert my_data == snapshot.use_extension(MyCustomExtension)

337

```