| (compatibility)= |
| # {fa}`puzzle-piece` Compatibility with other libraries |
| This library works by patching and/or extending {py:class}`requests.Session`. Many other libraries |
| out there do the same thing, making it potentially difficult to combine them. |
| |
| For that scenario, a mixin class is provided, so you can create a custom class with behavior from |
| multiple Session-modifying libraries: |
| ```python |
| >>> from requests import Session |
| >>> from requests_cache import CacheMixin |
| >>> from some_other_lib import SomeOtherMixin |
| |
| >>> class CustomSession(CacheMixin, SomeOtherMixin, Session): |
| ... """Session class with features from both some_other_lib and requests-cache""" |
| ``` |
| |
| ## Requests-HTML |
| [requests-html](https://github.com/psf/requests-html) is one library that works with this method: |
| ```python |
| >>> import requests |
| >>> from requests_cache import CacheMixin, install_cache |
| >>> from requests_html import HTMLSession |
| |
| >>> class CachedHTMLSession(CacheMixin, HTMLSession): |
| ... """Session with features from both CachedSession and HTMLSession""" |
| |
| >>> session = CachedHTMLSession() |
| >>> response = session.get('https://github.com/') |
| >>> print(response.from_cache, response.html.links) |
| ``` |
| |
| |
| Or if you are using {py:func}`.install_cache`, you can use the `session_factory` argument: |
| ```python |
| >>> install_cache(session_factory=CachedHTMLSession) |
| >>> response = requests.get('https://github.com/') |
| >>> print(response.from_cache, response.html.links) |
| ``` |
| |
| The same approach can be used with other libraries that subclass {py:class}`requests.Session`. |
| |
| ## Requests-Futures |
| Some libraries, including [requests-futures](https://github.com/ross/requests-futures), |
| support wrapping an existing session object: |
| ```python |
| >>> from requests_cache import CachedSession |
| >>> from requests_futures.sessions import FuturesSession |
| |
| >>> session = FutureSession(session=CachedSession()) |
| ``` |
| |
| In this case, `FutureSession` must wrap `CachedSession` rather than the other way around, since |
| `FutureSession` returns (as you might expect) futures rather than response objects. |
| See [issue #135](https://github.com/reclosedev/requests-cache/issues/135) for more notes on this. |
| |
| ## Requests-OAuthlib |
| Usage with [requests-oauthlib](https://github.com/requests/requests-oauthlib) is the same as other |
| libraries that subclass `requests.Session`: |
| ```python |
| >>> from requests_cache import CacheMixin |
| >>> from requests_oauthlib import OAuth2Session |
| |
| >>> class CachedOAuth2Session(CacheMixin, OAuth2Session): |
| ... """Session with features from both CachedSession and OAuth2Session""" |
| |
| >>> session = CachedOAuth2Session('my_client_id') |
| ``` |
| |
| ## Requests-Ratelimiter |
| [requests-ratelimiter](https://github.com/JWCook/requests-ratelimiter) adds rate-limiting to |
| requests via the [pyrate-limiter](https://github.com/vutran1710/PyrateLimiter) library. It also |
| provides a mixin, but note that the inheritance order is important: If rate-limiting is applied |
| _after_ caching, you get the added benefit of not counting cache hits against your rate limit. |
| ```python |
| >>> from pyrate_limiter import RedisBucket, RequestRate, Duration |
| >>> from requests import Session |
| >>> from requests_cache import CacheMixin, RedisCache |
| >>> from requests_ratelimiter import LimiterMixin |
| |
| >>> class CachedLimiterSession(CacheMixin, LimiterMixin, Session): |
| ... """Session class with caching and rate-limiting behavior. Accepts arguments for both |
| ... LimiterSession and CachedSession. |
| ... """ |
| |
| >>> # Limit non-cached requests to 5 requests per second, with unlimited cached requests |
| >>> # Optionally use Redis as both the bucket backend and the cache backend |
| >>> session = CachedLimiterSession( |
| ... rates=RequestRate(5, Duration.SECOND), |
| ... bucket_class=RedisBucket, |
| ... backend=RedisCache(), |
| ... ) |
| ``` |
| |
| ## Internet Archive |
| Usage with [internetarchive](https://github.com/jjjake/internetarchive) is the same as other libraries |
| that subclass `requests.Session`: |
| ```python |
| >>> from requests_cache import CacheMixin |
| >>> from internetarchive.session import ArchiveSession |
| |
| >>> class CachedArchiveSession(CacheMixin, ArchiveSession): |
| ... """Session with features from both CachedSession and ArchiveSession""" |
| |
| >>> session = CachedArchiveSession() |
| ``` |
| |
| ## Requests-Mock |
| [requests-mock](https://github.com/jamielennox/requests-mock) has multiple methods for mocking |
| requests, including a contextmanager, decorator, fixture, and adapter. There are a few different |
| options for using it with requests-cache, depending on how you want your tests to work. |
| |
| ### Disabling requests-cache |
| If you have an application that uses requests-cache and you just want to use requests-mock in |
| your tests, the easiest thing to do is to disable requests-cache. |
| |
| For example, if you are using {py:func}`.install_cache` in your application and the |
| requests-mock [pytest fixture](https://requests-mock.readthedocs.io/en/latest/pytest.html) in your |
| tests, you could wrap it in another fixture that uses {py:func}`.uninstall_cache` or |
| {py:func}`.disabled`: |
| :::{admonition} Example: test_requests_mock_disable_cache.py |
| :class: toggle |
| ```{literalinclude} ../../tests/compat/test_requests_mock_disable_cache.py |
| ``` |
| ::: |
| |
| |
| Or if you use a `CachedSession` object, you could replace it with a regular `Session`, for example: |
| ```python |
| >>> import unittest |
| >>> import pytest |
| >>> import requests |
| |
| >>> @pytest.fixure(scope='function', autouse=True) |
| >>> def disable_requests_cache(): |
| ... """Replace CachedSession with a regular Session for all test functions""" |
| ... with unittest.mock.patch('requests_cache.CachedSession', requests.Session): |
| ... yield |
| ``` |
| |
| ### Combining requests-cache with requests-mock |
| If you want both caching and mocking features at the same time, you can attach requests-mock's |
| [adapter](https://requests-mock.readthedocs.io/en/latest/adapter.html) to a `CachedSession`: |
| |
| :::{admonition} Example: `test_requests_mock_combine_cache.py` |
| :class: toggle |
| ```{literalinclude} ../../tests/compat/test_requests_mock_combine_cache.py |
| ``` |
| ::: |
| |
| ### Building a mocker using requests-cache data |
| Another approach is to use cached data to dynamically define mock requests + responses. |
| This has the advantage of only using request-mock's behavior for |
| [request matching](https://requests-mock.readthedocs.io/en/latest/matching.html). |
| |
| ```{literalinclude} ../../tests/compat/test_requests_mock_load_cache.py |
| :lines: 21-40 |
| ``` |
| |
| To turn that into a complete example: |
| :::{admonition} Example: `test_requests_mock_load_cache.py` |
| :class: toggle |
| ```{literalinclude} ../../tests/compat/test_requests_mock_load_cache.py |
| ``` |
| ::: |
| |
| ## Responses |
| Usage with the [responses](https://github.com/getsentry/responses) library is similar to the |
| requests-mock examples above. |
| |
| :::{admonition} Example: `test_responses_load_cache.py` |
| :class: toggle |
| ```{literalinclude} ../../tests/compat/test_responses_load_cache.py |
| ``` |
| ::: |
| |
| ## VCR |
| If you would like to reuse your cached response data for unit tests, one option is to convert your |
| cache into a format compatible with VCR-vased libraries like |
| [vcrpy](https://github.com/kevin1024/vcrpy) and [betamax](https://github.com/betamaxpy/betamax). |
| :::{admonition} Example: `vcr.py` |
| :class: toggle |
| ```{literalinclude} ../../examples/vcr.py |
| :lines: 7- |
| ``` |
| ::: |