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
```