1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
(compatibility)=
# {fas}`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 = FuturesSession(session=CachedSession())
```
In this case, `FuturesSession` must wrap `CachedSession` rather than the other way around, since
`FuturesSession` returns (as you might expect) futures rather than response objects.
See [issue #135](https://github.com/requests-cache/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 keyword 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(
... cache_name='http_cache',
... per_second=5,
... bucket_class=RedisBucket,
... backend=RedisCache(),
... )
```
```{warning}
When using mixins, use keyword arguments instead of positional arguments whenever possible.
The order of positional arguments will change based on inheritance order, but keyword arguments
can be passed in any order.
```
## 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`:
:::{dropdown} Example
:animate: fade-in-slide-down
:color: primary
:icon: file-code
```{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`:
:::{dropdown} Example
:animate: fade-in-slide-down
:color: primary
:icon: file-code
`test_requests_mock_combine_cache.py`
```{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:
:::{dropdown} Example
:animate: fade-in-slide-down
:color: primary
:icon: file-code
`test_requests_mock_load_cache.py`
```{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.
:::{dropdown} Example
:animate: fade-in-slide-down
:color: primary
:icon: file-code
`test_responses_load_cache.py`
```{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).
:::{dropdown} Example
:animate: fade-in-slide-down
:color: primary
:icon: file-code
`vcr.py`
```{literalinclude} ../../examples/vcr.py
:lines: 7-
```
:::
|