Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ loop = asyncio.get_event_loop()
loop.run_until_complete(run_example())
```

Or with Redis as cache backend:

```python
client = CachingClient(client, cache=AsyncRedisCache())
```

**Documentation:**

Expand Down
1 change: 1 addition & 0 deletions gen_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
additional_replacements={
"AsyncBaseTransport": "BaseTransport",
"AsyncHTTPTransport": "HTTPTransport",
"AsyncRedisCache": "SyncRedisCache",
"async_client": "client",
"AsyncClient": "Client",
"make_async_client": "make_client",
Expand Down
4 changes: 4 additions & 0 deletions httpx_caching/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from httpx_caching._async._cache import AsyncDictCache
from httpx_caching._async._cache_redis import AsyncRedisCache
from httpx_caching._async._transport import AsyncCachingTransport
from httpx_caching._sync._cache import SyncDictCache
from httpx_caching._sync._cache_redis import SyncRedisCache
from httpx_caching._sync._transport import SyncCachingTransport
from httpx_caching._wrapper import CachingClient

Expand All @@ -15,7 +17,9 @@
"SyncCachingTransport",
"CachingClient",
"AsyncDictCache",
"AsyncRedisCache",
"SyncDictCache",
"SyncRedisCache",
"ExpiresAfterHeuristic",
"LastModifiedHeuristic",
"OneDayCacheHeuristic",
Expand Down
34 changes: 34 additions & 0 deletions httpx_caching/_async/_cache_redis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Optional, Tuple
import redis.asyncio as redis
from httpx_caching._models import Response
from httpx_caching._serializer import Serializer
from httpx_caching._utils import AsyncLock


class AsyncRedisCache:
def __init__(self, serializer: Optional[Serializer] = None) -> None:
self.serializer = serializer if serializer else Serializer()
self.redis = redis.Redis()
self.lock = None

def get_lock(self):
if not self.lock:
self.lock = AsyncLock()
return self.lock

async def aget(self, key: str) -> Tuple[Optional[Response], Optional[dict]]:
value = await self.redis.get(key)
return self.serializer.loads(value)

async def aset(
self, key: str, response: Response, vary_header_data: dict, response_body: bytes
) -> None:
async with self.get_lock():
await self.redis.set(key, self.serializer.dumps(response, vary_header_data, response_body))

async def adelete(self, key: str) -> None:
async with self.get_lock():
await self.redis.delete(key)

async def aclose(self):
await self.redis.close()
34 changes: 34 additions & 0 deletions httpx_caching/_sync/_cache_redis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Optional, Tuple
import redis as redis
from httpx_caching._models import Response
from httpx_caching._serializer import Serializer
from httpx_caching._utils import SyncLock


class SyncRedisCache:
def __init__(self, serializer: Optional[Serializer] = None) -> None:
self.serializer = serializer if serializer else Serializer()
self.redis = redis.Redis()
self.lock = None

def get_lock(self):
if not self.lock:
self.lock = SyncLock()
return self.lock

def get(self, key: str) -> Tuple[Optional[Response], Optional[dict]]:
value = self.redis.get(key)
return self.serializer.loads(value)

def set(
self, key: str, response: Response, vary_header_data: dict, response_body: bytes
) -> None:
with self.get_lock():
self.redis.set(key, self.serializer.dumps(response, vary_header_data, response_body))

def delete(self, key: str) -> None:
with self.get_lock():
self.redis.delete(key)

def close(self):
self.redis.close()
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ def get_packages(package):
include_package_data=True,
zip_safe=False,
install_requires=[
"httpx==0.22.*",
"httpx==0.23.*",
"msgpack",
"anyio",
"multimethod",
"redis"
],
extras_require={},
classifiers=[
Expand Down
6 changes: 4 additions & 2 deletions tests/_async/test_vary.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
# SPDX-License-Identifier: Apache-2.0

from urllib.parse import urljoin
from httpx import AsyncClient

import pytest

from httpx_caching import AsyncDictCache
from httpx_caching import AsyncDictCache, CachingClient
from tests.conftest import cache_hit, make_async_client


Expand All @@ -17,7 +18,8 @@ def cache(self):

@pytest.fixture()
async def async_client(self, cache):
async_client = make_async_client(cache=cache)
async_client = AsyncClient()
async_client = CachingClient(async_client, cache=cache)
yield async_client
await async_client.aclose()

Expand Down