Skip to main content

Python client for Varsnap, a snapshot testing service that records and compares function inputs and outputs

Project description

Varsnap Python

Build Status Maintainability Code Coverage

Python Varsnap Client

Installation

Install from PyPI - pip install varsnap

Requirements

The client depends on four environment variables to be set:

  • VARSNAP - Should be either true or false. Varsnap will be disabled if the variable is anything other than true.
  • ENV - If set to development, the client will receive events from production. If set to production, the client will emit events.
  • VARSNAP_PRODUCER_TOKEN - Only clients with this token may emit production snapshots. Copied from https://www.varsnap.com/user/
  • VARSNAP_CONSUMER_TOKEN - Only clients with this token may consume production snapshots in development. Copied from https://www.varsnap.com/user/

Usage

Add the varsnap decorator in front of any function you'd like to make better:

from varsnap import varsnap


@varsnap
def example(args, **kwargs):
    return 'output'

Custom serialization

Varsnap serializes a function's inputs and outputs as JSON when possible and falls back to pickle otherwise. To control how a value of a particular type is serialized, give that type a pair of varsnap_serialize / varsnap_deserialize classmethods. Varsnap reads the function's type annotations and uses these classmethods for any annotated parameter or return value:

from varsnap import varsnap


class Money:
    def __init__(self, cents):
        self.cents = cents

    @classmethod
    def varsnap_serialize(cls, value):
        return str(value.cents)

    @classmethod
    def varsnap_deserialize(cls, data):
        return cls(int(data))


@varsnap
def add_tax(price: Money) -> Money:
    return Money(round(price.cents * 1.1))

If a function isn't annotated (or you want to override its annotations), pass the types explicitly to the decorator:

@varsnap(types={'price': Money}, returns=Money)
def add_tax(price):
    return Money(round(price.cents * 1.1))

The type is always taken from the decorated function, never from the serialized data, so stored snapshots can't redirect deserialization to a different type. Values whose type doesn't provide these classmethods continue to use the default JSON/pickle serialization.

Security of deserialization

Snapshots are fetched from the varsnap server and deserialized on your machine, so the wire format is treated as untrusted input. Varsnap serializes values in one of three formats:

  • json: — plain JSON. Always safe to deserialize.
  • type: — produced by a type's varsnap_serialize. Safe: the class is taken from the decorated function's annotations, never from the payload.
  • pickle: — a dill/pickle fallback for values JSON can't represent. Unpickling executes arbitrary code embedded in the payload, so anyone who can influence stored snapshot data could run code on the machine that reads it. Varsnap emits a warning whenever it falls back to this path.

Prefer the type: path for any value that would otherwise be pickled: give its type varsnap_serialize / varsnap_deserialize classmethods, which is both safe and version-stable.

Testing

With the proper environment variables set, in a test file, add:

import unittest
from varsnap import test

class TestIntegration(unittest.TestCase):
    def test_varsnap(self):
        matches, logs = test()
        if matches is None:
            raise unittest.case.SkipTest('No Snaps found')
        self.assertTrue(matches, logs)

If you're testing a Flask application, set up a test request context when testing:

# app = Flask()
with app.test_request_context():
    matches, logs = test()

Troubleshooting

Decorators changing function names

Using decorators may change the name of functions. In order to not confuse varsnap, set the decorated function's __qualname__ and __signature__ to match the original function:

import inspect


def decorator(func):
    def decorated(*args, **kwargs):
        return func(*args, **kwargs)
    decorated.__qualname__ = func.__qualname__
    decorated.__signature__ = inspect.signature(func)
    return decorated

Publishing

pip install build twine
python -m build
twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

varsnap-1.6.0.tar.gz (15.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

varsnap-1.6.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file varsnap-1.6.0.tar.gz.

File metadata

  • Download URL: varsnap-1.6.0.tar.gz
  • Upload date:
  • Size: 15.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for varsnap-1.6.0.tar.gz
Algorithm Hash digest
SHA256 15644a3b087be0bfaedae0305c75f01a355a96b49134e241c97170afcd3f4418
MD5 fc74762b27d57a3bb3b1f76c19d634b2
BLAKE2b-256 d9ee1778f88368df3a0752a5732ae40a596e8c5ec5d7f15c6d9adead61f93a5f

See more details on using hashes here.

Provenance

The following attestation bundles were made for varsnap-1.6.0.tar.gz:

Publisher: publish.yml on albertyw/varsnap-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file varsnap-1.6.0-py3-none-any.whl.

File metadata

  • Download URL: varsnap-1.6.0-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for varsnap-1.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 37599ddf3738e58d225dfcfe54491f502a043ccc4b9456ab2d543f2acadd09d5
MD5 cdabee84cbbfe1f6b89a2a4c6c22fb5c
BLAKE2b-256 11e4d8712f9ea8e445400c4d8a2926230bed60581e5c6a22452cef844962109f

See more details on using hashes here.

Provenance

The following attestation bundles were made for varsnap-1.6.0-py3-none-any.whl:

Publisher: publish.yml on albertyw/varsnap-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page