Skip to main content

Protocol-agnostic interaction interposition with lifecycle hooks for record, replay, and control.

Project description

interposition

Protocol-agnostic interaction interposition with lifecycle hooks for record, replay, and control.

Overview

Interposition is a Python library for replaying recorded interactions. Unlike VCRpy or other HTTP-specific tools, Interposition does not automatically hook into network libraries.

Instead, it provides a pure logic engine for storage, matching, and replay. You write the adapter for your specific target (HTTP client, database driver, IoT message handler), and Interposition handles the rest.

Key Features:

  • Protocol-agnostic: Works with any protocol (HTTP, gRPC, SQL, Pub/Sub, etc.)
  • Type-safe: Full mypy strict mode support with Pydantic v2
  • Immutable: All data structures are frozen Pydantic models
  • Serializable: Built-in JSON/YAML serialization for cassette persistence
  • Memory-efficient: O(1) lookup with fingerprint indexing
  • Streaming: Generator-based response delivery

Architecture

Interposition sits behind your application's data access layer. You provide the "Adapter" that captures live traffic or requests replay from the Broker.

+-------------+      +------------------+      +---------------+
| Application | <--> | Your Adapter     | <--> | Interposition |
+-------------+      +------------------+      +---------------+
                            |                          |
                       (Traps calls)              (Manages)
                                                       |
                                                  [Cassette]

Installation

pip install interposition

Practical Integration (Pytest Recipe)

The most common use case is using Interposition as a test fixture. Here is a production-ready recipe for pytest:

import pytest
from interposition import Broker, Cassette, InteractionRequest

@pytest.fixture
def cassette_broker():
    # Load cassette from a JSON file (or create one programmatically)
    with open("tests/fixtures/my_cassette.json", "rb") as f:
        cassette = Cassette.model_validate_json(f.read())
    return Broker(cassette)

def test_user_service(cassette_broker, monkeypatch):
    # 1. Create your adapter (mocking your actual client)
    def mock_fetch(url):
        request = InteractionRequest(
            protocol="http",
            action="GET",
            target=url,
            headers=(),
            body=b"",
        )
        # Delegate to Interposition
        chunks = list(cassette_broker.replay(request))
        return chunks[0].data

    # 2. Inject the adapter
    monkeypatch.setattr("my_app.client.fetch", mock_fetch)

    # 3. Run your test
    from my_app import get_user_name
    assert get_user_name(42) == "Alice"

Protocol-Agnostic Examples

Interposition shines where HTTP-only tools fail.

SQL Database Query

request = InteractionRequest(
    protocol="postgres",
    action="SELECT",
    target="users_table",
    headers=(),
    body=b"SELECT id, name FROM users WHERE id = 42",
)
# Replay returns: b'[(42, "Alice")]'

MQTT / PubSub Message

request = InteractionRequest(
    protocol="mqtt",
    action="subscribe",
    target="sensors/temp/room1",
    headers=(("qos", "1"),),
    body=b"",
)
# Replay returns stream of messages: b'24.5', b'24.6', ...

Usage Guide

Manual Construction (Quick Start)

If you need to build interactions programmatically (e.g., for seeding tests):

from interposition import (
    Broker,
    Cassette,
    Interaction,
    InteractionRequest,
    ResponseChunk,
)

# 1. Define the Request
request = InteractionRequest(
    protocol="api",
    action="query",
    target="users/42",
    headers=(),
    body=b"",
)

# 2. Define the Response
chunks = (
    ResponseChunk(data=b'{"id": 42, "name": "Alice"}', sequence=0),
)

# 3. Create Interaction & Cassette
interaction = Interaction(
    request=request,
    fingerprint=request.fingerprint(),
    response_chunks=chunks,
)
cassette = Cassette(interactions=(interaction,))

# 4. Replay
broker = Broker(cassette=cassette)
response = list(broker.replay(request))

Persistence & Serialization

Interposition models are Pydantic v2 models, making serialization trivial.

# Save to JSON
with open("cassette.json", "w") as f:
    f.write(cassette.model_dump_json(indent=2))

# Load from JSON
with open("cassette.json") as f:
    cassette = Cassette.model_validate_json(f.read())

# Generate JSON Schema
schema = Cassette.model_json_schema()

Streaming Responses

For large files or streaming protocols, responses are yielded lazily:

# The broker returns a generator
for chunk in broker.replay(request):
    print(f"Received chunk: {len(chunk.data)} bytes")

Error Handling

If a matching interaction is not found, the broker raises InteractionNotFoundError:

from interposition import InteractionNotFoundError

try:
    broker.replay(unknown_request)
except InteractionNotFoundError as e:
    print(f"Not recorded: {e.request.target}")

Development

Prerequisites

  • Python 3.10+
  • uv (recommended)

Setup & Testing

# Clone and install
git clone https://github.com/osoekawaitlab/interposition.git
cd interposition
uv pip install -e . --group=dev

# Run tests
nox -s tests

License

MIT

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

interposition-0.2.0.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

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

interposition-0.2.0-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file interposition-0.2.0.tar.gz.

File metadata

  • Download URL: interposition-0.2.0.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for interposition-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3eff5d267d8fdb7c5bb6c1ceb50f13c4498612cd7827b52c57d6105bff8d6d0f
MD5 2e74a20d6513a7ca473e5b0301bce91b
BLAKE2b-256 d87b0374f3a18aad471731dd7e0410efee11a1cd3f01e024d50267cf759d7817

See more details on using hashes here.

Provenance

The following attestation bundles were made for interposition-0.2.0.tar.gz:

Publisher: release.yml on osoekawaitlab/interposition

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

File details

Details for the file interposition-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: interposition-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for interposition-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7c960fe23abc02fb1b165fe344119a2637fa438a6abd663468b5ac124f6fb17
MD5 8ca6b15e086e7049828e18b68110d20f
BLAKE2b-256 e486cac47db550b0ee88e483f5804a39f0ba29e74e6c25350b934de14b236f0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for interposition-0.2.0-py3-none-any.whl:

Publisher: release.yml on osoekawaitlab/interposition

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