CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-syrupy

Pytest snapshot testing utility that enables developers to write tests asserting immutability of computed results.

Overall
score

80%

Overview
Eval results
Files

extensions.mddocs/

Extensions

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.

Capabilities

Base Extension Classes

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

class AbstractSyrupyExtension:
    """
    Base class combining serialization, storage, and reporting.
    
    All extension classes should inherit from this base.
    """
    
    def get_snapshot_name(self, *, index: SnapshotIndex = 0, **kwargs) -> str:
        """
        Generate snapshot name for given index.
        
        Parameters:
        - index: Snapshot index (int or str)
        - **kwargs: Additional naming options
        
        Returns:
        str: Generated snapshot name
        """
    
    def get_location(self, *, test_location: "PyTestLocation") -> str:
        """
        Get storage location for snapshots.
        
        Parameters:
        - test_location: Test file location info
        
        Returns:
        str: Storage location path
        """
    
    def discover_snapshots(self, *, location: str) -> "SnapshotCollections":
        """
        Discover existing snapshots at location.
        
        Parameters:
        - location: Location to search
        
        Returns:
        SnapshotCollections: Found snapshot collections
        """

class SnapshotSerializer:
    def serialize(self, data: SerializableData, **kwargs) -> SerializedData:
        """
        Serialize data for snapshot storage.
        
        Parameters:
        - data: Data to serialize
        - **kwargs: Additional serialization options
        
        Returns:
        SerializedData: Serialized representation (str or bytes)
        """

class SnapshotCollectionStorage:
    def read_snapshot(self, snapshot_location: str, snapshot_name: str) -> SerializedData:
        """
        Read snapshot data from storage.
        
        Parameters:
        - snapshot_location: Location identifier for snapshot
        - snapshot_name: Name identifier for specific snapshot
        
        Returns:
        SerializedData: Stored snapshot data
        """
    
    def write_snapshot(self, snapshot_data: SerializedData, snapshot_location: str, snapshot_name: str) -> None:
        """
        Write snapshot data to storage.
        
        Parameters:
        - snapshot_data: Serialized data to store
        - snapshot_location: Location identifier for snapshot
        - snapshot_name: Name identifier for specific snapshot
        """

Amber Extension (Default)

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

class AmberSnapshotExtension(AbstractSyrupyExtension):
    """
    Default extension creating .ambr files with multiple snapshots per file.
    
    Features:
    - Multi-snapshot storage in single file
    - Deep object introspection and serialization
    - Support for most Python data types
    - Human-readable text format
    """

class AmberDataSerializer(SnapshotSerializer):
    """
    Serializes Python objects to amber format with comprehensive type handling.
    
    Supports:
    - Built-in types (dict, list, tuple, set, etc.)
    - Custom objects with __repr__ or __dict__
    - Named tuples and dataclasses
    - Complex nested structures
    """
    
    @staticmethod
    def object_as_named_tuple(obj: Any) -> Any:
        """
        Convert object to named tuple representation bypassing custom __repr__.
        
        Parameters:
        - obj: Object to convert
        
        Returns:
        Any: Named tuple representation of object
        """

Usage examples:

def test_amber_default(snapshot):
    # Uses AmberSnapshotExtension by default
    data = {
        "users": [
            {"name": "Alice", "scores": [85, 92]},
            {"name": "Bob", "scores": [78, 91]}
        ],
        "metadata": {"version": "1.0", "timestamp": "2023-12-01"}
    }
    assert data == snapshot

def test_custom_repr_bypass(snapshot):
    from syrupy.extensions.amber.serializer import AmberDataSerializer
    
    class CustomClass:
        def __init__(self, value):
            self.value = value
            
        def __repr__(self):
            return "CustomClass(...)"  # Hides internal state
    
    obj = CustomClass("important_data")
    
    # Bypass custom __repr__ to show actual structure
    assert AmberDataSerializer.object_as_named_tuple(obj) == snapshot

Single File Extensions

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

class SingleFileSnapshotExtension(AbstractSyrupyExtension):
    """
    Base class for single-file extensions creating one .raw file per test.
    
    Features:
    - One file per test case
    - Binary and text mode support
    - Raw data storage without additional formatting
    """

class WriteMode(Enum):
    BINARY = "b"
    TEXT = "t"

class SingleFileAmberSnapshotExtension(SingleFileSnapshotExtension):
    """
    Amber extension variant creating one file per snapshot.
    
    Combines amber serialization with single-file storage.
    """

Usage examples:

def test_single_file_raw(snapshot):
    from syrupy.extensions.single_file import SingleFileSnapshotExtension
    
    # Binary data stored as-is in .raw file
    binary_data = b"\\x00\\x01\\x02\\x03"
    assert binary_data == snapshot.use_extension(SingleFileSnapshotExtension)

def test_single_file_amber(snapshot):
    from syrupy.extensions.single_file import SingleFileAmberSnapshotExtension
    
    # Amber serialization but one file per test
    data = {"config": {"debug": True, "port": 8080}}
    assert data == snapshot.use_extension(SingleFileAmberSnapshotExtension)

Image Extensions

Specialized extensions for image data supporting PNG and SVG formats.

class PNGImageSnapshotExtension(SingleFileSnapshotExtension):
    """
    Extension for PNG image snapshots creating .png files.
    
    Expects byte string input representing PNG image data.
    """

class SVGImageSnapshotExtension(SingleFileSnapshotExtension):
    """
    Extension for SVG image snapshots creating .svg files.
    
    Expects string input containing SVG markup.
    """

Usage examples:

def test_png_image(snapshot):
    from syrupy.extensions.image import PNGImageSnapshotExtension
    
    # PNG image as bytes (from PIL, matplotlib, etc.)
    png_bytes = create_chart_image()  # Returns bytes
    assert png_bytes == snapshot.use_extension(PNGImageSnapshotExtension)

def test_svg_image(snapshot):
    from syrupy.extensions.image import SVGImageSnapshotExtension
    
    # SVG as string markup
    svg_content = '''
    <svg width="100" height="100">
        <circle cx="50" cy="50" r="25" fill="blue"/>
    </svg>
    '''
    assert svg_content == snapshot.use_extension(SVGImageSnapshotExtension)

JSON Extension

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

class JSONSnapshotExtension(AbstractSyrupyExtension):
    """
    Extension creating .json files with proper JSON formatting.
    
    Features:
    - Clean, properly indented JSON output
    - Support for dictionaries and lists
    - Custom serialization for non-JSON types
    - Human-readable formatting
    """

Usage examples:

def test_api_response(snapshot):
    from syrupy.extensions.json import JSONSnapshotExtension
    
    # API response data
    response = {
        "status": "success",
        "data": {
            "user": {"id": 123, "name": "Alice"},
            "permissions": ["read", "write"]
        },
        "meta": {"timestamp": "2023-12-01T10:00:00Z"}
    }
    
    assert response == snapshot.use_extension(JSONSnapshotExtension)

def test_list_data(snapshot):
    from syrupy.extensions.json import JSONSnapshotExtension
    
    # List of structured data
    users = [
        {"id": 1, "name": "Alice", "active": True},
        {"id": 2, "name": "Bob", "active": False}
    ]
    
    assert users == snapshot.use_extension(JSONSnapshotExtension)

Default Extension Configuration

Global configuration for default extension class used across all snapshots.

DEFAULT_EXTENSION = AmberSnapshotExtension

Usage with CLI:

# Change default extension class
pytest --snapshot-default-extension=syrupy.extensions.json.JSONSnapshotExtension

# Update snapshots with custom extension
pytest --snapshot-update --snapshot-default-extension=syrupy.extensions.json.JSONSnapshotExtension

Custom Extension Development

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

from syrupy.extensions.base import AbstractSyrupyExtension

class MyCustomExtension(AbstractSyrupyExtension):
    def serialize(self, data, **kwargs):
        # Custom serialization logic
        return str(data)
    
    def get_file_extension(self):
        return "custom"
    
    def read_snapshot(self, snapshot_location, snapshot_name):
        # Custom read logic
        pass
    
    def write_snapshot(self, snapshot_data, snapshot_location, snapshot_name):
        # Custom write logic
        pass

# Usage in tests
def test_with_custom_extension(snapshot):
    assert my_data == snapshot.use_extension(MyCustomExtension)

Install with Tessl CLI

npx tessl i tessl/pypi-syrupy

docs

cli-integration.md

core-assertions.md

extensions.md

filters.md

index.md

matchers.md

tile.json