Skip to content

ditto

Top-level package exports.

Snapshot(group_name, module, target, _backend, recorder=_UNSET, recorder_name=_UNSET, mode=SnapshotMode.RECORD, nodeid='', target_id='', _tracker=_SessionTracker()) dataclass

Immutable configuration for a snapshot: where to store it and how to record it.

Instances are created by the snapshot fixture and hold no I/O state. All persistence is handled by the module-level free functions save_snapshot, load_snapshot, and resolve_snapshot.

Parameters:

Name Type Description Default
group_name str

Prefix used in snapshot keys. Derived from the pytest nodeid minus the file path (e.g. "test_something" or "TestClass.test_something").

required
module str

Rootdir-relative test file stem (e.g. "tests/bar/test_api"). Required for all backends. Provides namespace isolation across test files.

required
target str

URI identifying the storage location. The scheme controls key format: file:// uses flat dotted keys (module.group@key.ext); all other schemes use slash-separated keys (module/group@key.ext). Always use absolute file:// URIs (e.g. file:///home/user/proj/tests/.ditto).

required
_backend MutableMapping[str, bytes]

Resolved storage backend. Conventionally private — set by the fixture via _resolve_target. Use target= to communicate where data goes.

required
recorder Recorder

Serialisation strategy. Defaults to strict JSON.

_UNSET
recorder_name str

The recorder's registered name, which is its persisted identifier: it ends snapshot filenames and is recorded in ditto.lock. The fixture passes the name the recorder was selected by. A directly constructed Snapshot passes recorder and recorder_name together, or neither for strict JSON ("json"). The name may be omitted with the strict JSON recorder itself, whose name is "json".

_UNSET
mode SnapshotMode

Whether a snapshot is recorded, updated, or only verified. Defaults to SnapshotMode.RECORD.

RECORD
nodeid str

Full pytest node id for the owning test, e.g. tests/test_api.py::test_foo. Used to build lock-file entries. Empty when constructed outside the fixture.

''
target_id str

Portable lock-file target id (rootdir-relative for file://, URI otherwise).

''
_tracker _SessionTracker

Where snapshot activity is recorded. The fixture passes its pytest session's tracker; a directly constructed Snapshot gets a private one.

_SessionTracker()

__call__(data, key)

Save or load the snapshot for key.

Delegates to resolve_snapshot: saves data on first call and returns the stored value on subsequent calls. Either way the value returned is what the recorder reads back, not data itself.

Source code in ditto/snapshot.py
def __call__(self, data: Any, key: str) -> Any:
    """Save or load the snapshot for `key`.

    Delegates to `resolve_snapshot`: saves `data` on first call and
    returns the stored value on subsequent calls. Either way the value
    returned is what the recorder reads back, not `data` itself.
    """
    return resolve_snapshot(self, data, key)

SnapshotMode

Bases: Enum

How resolve_snapshot treats the stored value for a key.

Attributes:

Name Type Description
RECORD

Save the value when the key is absent; otherwise return the stored value.

UPDATE

Always save the value, overwriting a stored one. Set by --ditto-update.

VERIFY

Never write: return the stored value, or the given value when the key is absent. Set by --ditto-verify, so a verify run cannot recreate a deleted snapshot.

A value that is saved, or returned under VERIFY for an absent key, is
serialised and deserialised first, and the deserialised value is returned.

__getattr__(name)

Resolve plugin marks (e.g. @ditto.pandas.csv) on attribute access.

Called by Python only when normal attribute lookup fails, so built-in names defined above are served directly. Marks are derived from the names in the recorder registry, which come from entry-point metadata, so resolving a mark never imports a plugin. A bare recorder name fmt resolves to record("fmt"). For a dotted name ns.fmt, ns resolves to a namespace whose attribute fmt is record("ns.fmt").

Parameters:

Name Type Description Default
name str

The attribute name being accessed.

required

Returns:

Type Description
Any

The mark for a bare recorder name, or the namespace for a dotted one. Annotated Any so plugin marks type-check without stubs.

Raises:

Type Description
AttributeError

If name is neither a recorder name nor a recorder namespace.

Source code in ditto/__init__.py
def __getattr__(name: str) -> Any:
    """
    Resolve plugin marks (e.g. `@ditto.pandas.csv`) on attribute access.

    Called by Python only when normal attribute lookup fails, so built-in names
    defined above are served directly. Marks are derived from the names in the
    recorder registry, which come from entry-point metadata, so resolving a mark
    never imports a plugin. A bare recorder name `fmt` resolves to
    `record("fmt")`. For a dotted name `ns.fmt`, `ns` resolves to a namespace
    whose attribute `fmt` is `record("ns.fmt")`.

    Parameters
    ----------
    name : str
        The attribute name being accessed.

    Returns
    -------
    Any
        The mark for a bare recorder name, or the namespace for a dotted one.
        Annotated `Any` so plugin marks type-check without stubs.

    Raises
    ------
    AttributeError
        If `name` is neither a recorder name nor a recorder namespace.
    """
    if name in _plugins.RECORDER_REGISTRY:
        return record(name)
    if _formats_in(name):
        return _MarkNamespace(name)
    raise AttributeError(f"module 'ditto' has no attribute {name!r}")