Matchers¶
You can specify how you would like Betamax to match requests you are making with the recorded requests. You have the following options for default (built-in) matchers:
Matcher |
Behaviour |
---|---|
body |
This matches by checking the equality of the request bodies. |
headers |
This matches by checking the equality of all of the request headers |
host |
This matches based on the host of the URI |
method |
This matches based on the method, e.g., |
path |
This matches on the path of the URI |
query |
This matches on the query part of the URI |
uri |
This matches on the entirety of the URI |
Default Matchers¶
By default, Betamax matches on uri
and method
.
Specifying Matchers¶
You can specify the matchers to be used in the entire library by configuring Betamax like so:
import betamax
with betamax.Betamax.configure() as config:
config.default_cassette_options['match_requests_on'].extend([
'headers', 'body'
])
Instead of configuring global state, though, you can set it per cassette. For example:
import betamax
import requests
session = requests.Session()
recorder = betamax.Betamax(session)
match_on = ['uri', 'method', 'headers', 'body']
with recorder.use_cassette('example', match_requests_on=match_on):
# ...
Making Your Own Matcher¶
So long as you are matching requests, you can define your own way of matching.
Each request matcher has to inherit from betamax.BaseMatcher
and implement
match
.
- class betamax.BaseMatcher¶
Base class that ensures sub-classes that implement custom matchers can be registered and have the only method that is required.
Usage:
from betamax import Betamax, BaseMatcher class MyMatcher(BaseMatcher): name = 'my' def match(self, request, recorded_request): # My fancy matching algorithm Betamax.register_request_matcher(MyMatcher)
The last line is absolutely necessary.
The match method will be given a requests.PreparedRequest object and a dictionary. The dictionary always has the following keys:
url
method
body
headers
- match(request, recorded_request)¶
A method that must be implemented by the user.
- Parameters
request (PreparedRequest) – A requests PreparedRequest object
recorded_request (dict) – A dictionary containing the serialized request in the cassette
- Returns bool
True if they match else False
- on_init()¶
Method to implement if you wish something to happen in
__init__
.The return value is not checked and this is called at the end of
__init__
. It is meant to provide the matcher author a way to perform things during initialization of the instance that would otherwise require them to overrideBaseMatcher.__init__
.
Some examples of matchers are in the source reproduced here:
# -*- coding: utf-8 -*-
from .base import BaseMatcher
class HeadersMatcher(BaseMatcher):
# Matches based on the headers of the request
name = 'headers'
def match(self, request, recorded_request):
return dict(request.headers) == self.flatten_headers(recorded_request)
def flatten_headers(self, request):
from betamax.util import from_list
headers = request['headers'].items()
return dict((k, from_list(v)) for (k, v) in headers)
# -*- coding: utf-8 -*-
from .base import BaseMatcher
from requests.compat import urlparse
class HostMatcher(BaseMatcher):
# Matches based on the host of the request
name = 'host'
def match(self, request, recorded_request):
request_host = urlparse(request.url).netloc
recorded_host = urlparse(recorded_request['uri']).netloc
return request_host == recorded_host
# -*- coding: utf-8 -*-
from .base import BaseMatcher
class MethodMatcher(BaseMatcher):
# Matches based on the method of the request
name = 'method'
def match(self, request, recorded_request):
return request.method == recorded_request['method']
# -*- coding: utf-8 -*-
from .base import BaseMatcher
from requests.compat import urlparse
class PathMatcher(BaseMatcher):
# Matches based on the path of the request
name = 'path'
def match(self, request, recorded_request):
request_path = urlparse(request.url).path
recorded_path = urlparse(recorded_request['uri']).path
return request_path == recorded_path
# -*- coding: utf-8 -*-
from .base import BaseMatcher
from requests.compat import urlparse
class PathMatcher(BaseMatcher):
# Matches based on the path of the request
name = 'path'
def match(self, request, recorded_request):
request_path = urlparse(request.url).path
recorded_path = urlparse(recorded_request['uri']).path
return request_path == recorded_path
# -*- coding: utf-8 -*-
from .base import BaseMatcher
from .query import QueryMatcher
from requests.compat import urlparse
class URIMatcher(BaseMatcher):
# Matches based on the uri of the request
name = 'uri'
def on_init(self):
# Get something we can use to match query strings with
self.query_matcher = QueryMatcher().match
def match(self, request, recorded_request):
queries_match = self.query_matcher(request, recorded_request)
request_url, recorded_url = request.url, recorded_request['uri']
return self.all_equal(request_url, recorded_url) and queries_match
def parse(self, uri):
parsed = urlparse(uri)
return {
'scheme': parsed.scheme,
'netloc': parsed.netloc,
'path': parsed.path,
'fragment': parsed.fragment
}
def all_equal(self, new_uri, recorded_uri):
new_parsed = self.parse(new_uri)
recorded_parsed = self.parse(recorded_uri)
return (new_parsed == recorded_parsed)
When you have finished writing your own matcher, you can instruct betamax to use it like so:
import betamax
class MyMatcher(betamax.BaseMatcher):
name = 'my'
def match(self, request, recorded_request):
return True
betamax.Betamax.register_request_matcher(MyMatcher)
To use it, you simply use the name you set like you use the name of the default matchers, e.g.:
with Betamax(s).use_cassette('example', match_requests_on=['uri', 'my']):
# ...
on_init
¶
As you can see in the code for URIMatcher
, we use on_init
to
initialize an attribute on the URIMatcher
instance. This method serves to
provide the matcher author with a different way of initializing the object
outside of the match
method. This also means that the author does not have
to override the base class’ __init__
method.