Skip to content

Matchers

Matchers determine how recorded interactions are matched to incoming requests during playback.

Overview

By default, grpcvcr matches requests by method name only. You can customize matching behavior using different matchers or combining them.

from grpcvcr import MethodMatcher, RequestMatcher

# Match by method name and request body
matcher = MethodMatcher() & RequestMatcher()

Built-in Matchers

Matcher Description
MethodMatcher Match by RPC method name
RequestMatcher Match by request body
MetadataMatcher Match by request metadata
AllMatcher Combine multiple matchers
CustomMatcher Custom matching function

API Reference

Bases: ABC

Base class for request matchers.

Matchers determine whether an incoming request matches a recorded interaction. They can be combined using the & operator.

Example
# Match on method and request body
matcher = MethodMatcher() & RequestMatcher()

cassette = Cassette("test.yaml", match_on=matcher)

matches abstractmethod

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Check if a request matches a recorded interaction.

Parameters:

Name Type Description Default
request InteractionRequest

The incoming request to match.

required
recorded InteractionRequest

The recorded request to compare against.

required

Returns:

Type Description
bool

True if the requests match, False otherwise.

Source code in src/grpcvcr/matchers.py
@abstractmethod
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Check if a request matches a recorded interaction.

    Args:
        request: The incoming request to match.
        recorded: The recorded request to compare against.

    Returns:
        True if the requests match, False otherwise.
    """
    ...

__and__

__and__(other: Matcher) -> AllMatcher

Combine this matcher with another using AND logic.

Parameters:

Name Type Description Default
other Matcher

Another matcher to combine with.

required

Returns:

Type Description
AllMatcher

An AllMatcher that requires both matchers to succeed.

Example
matcher = MethodMatcher() & RequestMatcher()
Source code in src/grpcvcr/matchers.py
def __and__(self, other: Matcher) -> AllMatcher:
    """Combine this matcher with another using AND logic.

    Args:
        other: Another matcher to combine with.

    Returns:
        An AllMatcher that requires both matchers to succeed.

    Example:
        ```python
        matcher = MethodMatcher() & RequestMatcher()
        ```
    """
    if isinstance(self, AllMatcher):
        return AllMatcher(matchers=[*self.matchers, other])
    return AllMatcher(matchers=[self, other])

Bases: Matcher

Matches requests by gRPC method path.

This is the default matcher. Method paths have the format: /package.ServiceName/MethodName

Example
matcher = MethodMatcher()
# Matches if request.method == recorded.method

matches

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Check if method paths match exactly.

Source code in src/grpcvcr/matchers.py
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Check if method paths match exactly."""
    return request.method == recorded.method

Bases: Matcher

Matches requests by body content.

Compares the base64-encoded protobuf bytes directly. For semantic comparison of protobuf fields, use a CustomMatcher.

Example
# Exact body match
matcher = MethodMatcher() & RequestMatcher()

matches

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Check if request bodies match exactly.

Source code in src/grpcvcr/matchers.py
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Check if request bodies match exactly."""
    return request.body == recorded.body

Bases: Matcher

Matches requests by metadata (headers).

Can be configured to match specific keys only, or to ignore certain keys (like timestamps or request IDs).

Example
# Only compare authorization header
matcher = MetadataMatcher(keys=["authorization"])

# Compare all metadata except request ID
matcher = MetadataMatcher(ignore_keys=["x-request-id"])

keys class-attribute instance-attribute

keys: list[str] | None = None

If set, only these metadata keys are compared.

ignore_keys class-attribute instance-attribute

ignore_keys: list[str] | None = None

If set, these metadata keys are ignored during comparison.

matches

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Check if metadata matches according to configured rules.

Source code in src/grpcvcr/matchers.py
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Check if metadata matches according to configured rules."""
    req_meta = request.metadata
    rec_meta = recorded.metadata

    if self.keys is not None:
        for key in self.keys:
            if req_meta.get(key) != rec_meta.get(key):
                return False
        return True

    ignore = set(self.ignore_keys or [])
    all_keys = set(req_meta.keys()) | set(rec_meta.keys())

    for key in all_keys:
        if key in ignore:
            continue
        if req_meta.get(key) != rec_meta.get(key):
            return False

    return True

Bases: Matcher

Combines multiple matchers with AND logic.

All contained matchers must return True for the request to match. Usually created implicitly via the & operator.

Example
# These are equivalent:
matcher = AllMatcher(matchers=[MethodMatcher(), RequestMatcher()])
matcher = MethodMatcher() & RequestMatcher()

matchers class-attribute instance-attribute

matchers: list[Matcher] = field(default_factory=list)

List of matchers that must all succeed.

matches

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Check if all contained matchers succeed.

Source code in src/grpcvcr/matchers.py
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Check if all contained matchers succeed."""
    return all(m.matches(request, recorded) for m in self.matchers)

Bases: Matcher

Matches requests using a custom function.

Useful for complex matching logic like comparing specific protobuf fields or implementing fuzzy matching.

Example
def match_user_id(req: InteractionRequest, rec: InteractionRequest) -> bool:
    # Deserialize and compare specific fields
    req_msg = UserRequest.FromString(req.get_body_bytes())
    rec_msg = UserRequest.FromString(rec.get_body_bytes())
    return req_msg.user_id == rec_msg.user_id

matcher = MethodMatcher() & CustomMatcher(func=match_user_id)

func instance-attribute

func: Callable[[InteractionRequest, InteractionRequest], bool]

The matching function. Takes (request, recorded) and returns bool.

name class-attribute instance-attribute

name: str | None = None

Optional name for debugging and logging.

matches

matches(request: InteractionRequest, recorded: InteractionRequest) -> bool

Delegate to the custom matching function.

Source code in src/grpcvcr/matchers.py
def matches(
    self,
    request: InteractionRequest,
    recorded: InteractionRequest,
) -> bool:
    """Delegate to the custom matching function."""
    return self.func(request, recorded)