Skip to content

Commit 87ca101

Browse files
Add initial redis-py asyncio client (#5)
* Add initial redis-py asyncio client * Add additional compressors from django-redis * Use GitHub Actions instead of Travis * Upgrade dependencies * Update tox dependencies * Set publishing pipeline using GitHub Actions * Bump version to 0.2.0
1 parent 7aae00e commit 87ca101

29 files changed

+1153
-757
lines changed

.github/workflows/ci.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
16+
django-version: ['3.2', '4.1']
17+
exclude:
18+
- python-version: '3.10'
19+
django-version: '3.2'
20+
- python-version: '3.11'
21+
django-version: '3.2'
22+
- python-version: '3.7'
23+
django-version: '4.1'
24+
services:
25+
redis:
26+
image: redis:6
27+
ports:
28+
- 6379:6379
29+
30+
steps:
31+
- uses: actions/checkout@v2
32+
- name: Set up Python ${{ matrix.python-version }}
33+
uses: actions/setup-python@v2
34+
with:
35+
python-version: ${{ matrix.python-version }}
36+
- name: Get pip cache dir
37+
id: pip-cache
38+
run: |
39+
echo "::set-output name=dir::$(pip cache dir)"
40+
- name: Cache
41+
uses: actions/cache@v2
42+
with:
43+
path: ${{ steps.pip-cache.outputs.dir }}
44+
key:
45+
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}
46+
restore-keys: |
47+
${{ matrix.python-version }}-v1-
48+
- name: Install dependencies
49+
run: |
50+
sudo apt-get install -y zlib1g-dev
51+
python -m pip install --upgrade pip wheel
52+
python -m pip install tox tox-gh-actions coveralls
53+
- name: Tox Test
54+
run: tox
55+
env:
56+
PYTHON_VER: ${{ matrix.python-version }}
57+
DJANGO: ${{ matrix.django-version }}

.github/workflows/publish.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This workflows will upload a Python Package using Twine when a release is created
2+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3+
4+
name: Upload Python Package
5+
6+
on:
7+
release:
8+
types: [created]
9+
10+
jobs:
11+
deploy:
12+
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v2
17+
- name: Set up Python
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: '3.x'
21+
- name: Install dependencies
22+
run: |
23+
python -m pip install --upgrade pip
24+
pip install setuptools wheel twine
25+
- name: Build and publish
26+
env:
27+
TWINE_USERNAME: __token__
28+
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
29+
run: |
30+
python setup.py sdist bdist_wheel
31+
twine upload dist/*

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ repos:
1010
- id: check-yaml
1111

1212
- repo: https://github.com/psf/black
13-
rev: 19.10b0
13+
rev: 22.12.0
1414
hooks:
1515
- id: black
1616

1717
- repo: https://github.com/timothycrosley/isort
18-
rev: 4.3.21
18+
rev: 5.11.4
1919
hooks:
2020
- id: isort
21-
args: ['--settings-path=setup.cfg', '-rc']
21+
args: ['--settings-path=setup.cfg']

.travis.yml

Lines changed: 0 additions & 70 deletions
This file was deleted.

django_async_redis/__init__.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,24 @@
22

33
__author__ = """Andrew Chen Wang"""
44
__email__ = "[email protected]"
5-
__version__ = "0.1.0"
5+
6+
VERSION = (0, 2, 0)
7+
__version__ = ".".join(map(str, VERSION))
8+
9+
10+
def get_redis_connection(alias="default", write=True):
11+
"""
12+
Helper used for obtaining a raw redis client.
13+
"""
14+
15+
from django.core.cache import caches
16+
17+
cache = caches[alias]
18+
19+
if not hasattr(cache, "client"):
20+
raise NotImplementedError("This backend does not support this feature")
21+
22+
if not hasattr(cache.client, "get_client"):
23+
raise NotImplementedError("This backend does not support this feature")
24+
25+
return cache.client.get_client(write)

django_async_redis/cache.py

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import functools
22
import logging
33
from collections import OrderedDict
4-
from typing import Any, AsyncGenerator, Optional, Union
4+
from typing import Any, AsyncIterator, Callable, Optional, Union
55

6-
from asgiref.sync import async_to_sync
76
from django.conf import settings
87
from django.core.cache.backends.base import BaseCache
98
from django.utils.module_loading import import_string
@@ -19,7 +18,9 @@
1918
CONNECTION_INTERRUPTED = object()
2019

2120

22-
def omit_exception(method=None, return_value=None):
21+
def omit_exception(
22+
method: Optional[Callable] = None, return_value: Optional[Any] = None
23+
):
2324
"""
2425
Simple decorator that intercepts connection
2526
errors and ignores these if settings specify this.
@@ -35,7 +36,7 @@ def _decorator(self, *args, **kwargs):
3536
except ConnectionInterrupted as e:
3637
if self._ignore_exceptions:
3738
if self._log_ignored_exceptions:
38-
self.logger.error(str(e))
39+
self.logger.exception("Exception ignored")
3940

4041
return return_value
4142
raise e.__cause__
@@ -70,10 +71,6 @@ def __init__(self, server, params):
7071
else None
7172
)
7273

73-
def close(self, **kwargs):
74-
# TODO Remove this once Django's close_caches implement cache.close_async
75-
async_to_sync(self.close_async)()
76-
7774
@property
7875
def client(self) -> Union[DefaultClient]:
7976
"""
@@ -84,103 +81,94 @@ def client(self) -> Union[DefaultClient]:
8481
return self._client
8582

8683
@omit_exception
87-
async def set_async(self, *args, **kwargs):
88-
"""
89-
:param key: the cache key.
90-
:param value: the cache's value.
91-
:param timeout: the timeout in seconds. Will be sent as pexpire.
92-
:param version: cache version.
93-
:param client: the client to use.
94-
:param nx: sets the nx flag. Not set if xx is True.
95-
:param xx: sets the xx flag. Takes precedence over nx if True.
96-
"""
84+
async def aset(self, *args, **kwargs):
9785
return await self.client.set(*args, **kwargs)
9886

9987
@omit_exception
100-
async def incr_version_async(self, *args, **kwargs):
88+
async def aincr_version(self, *args, **kwargs):
10189
return await self.client.incr_version(*args, **kwargs)
10290

10391
@omit_exception
104-
async def add_async(self, *args, **kwargs):
92+
async def aadd(self, *args, **kwargs):
10593
return await self.client.add(*args, **kwargs)
10694

107-
async def get_async(self, key, default=None, version=None, client=None):
108-
value = await self._get_async(key, default, version, client)
95+
async def aget(self, key, default=None, version=None, client=None):
96+
value = await self._aget(key, default, version, client)
10997
if value is CONNECTION_INTERRUPTED:
11098
value = default
11199
return value
112100

113101
@omit_exception(return_value=CONNECTION_INTERRUPTED)
114-
async def _get_async(self, key, default, version, client):
102+
async def _aget(self, key, default, version, client):
115103
return await self.client.get(
116104
key, default=default, version=version, client=client
117105
)
118106

119107
@omit_exception
120-
async def delete_async(self, *args, **kwargs):
108+
async def adelete(self, *args, **kwargs):
121109
return await self.client.delete(*args, **kwargs)
122110

123111
@omit_exception
124-
async def delete_pattern_async(self, *args, **kwargs):
112+
async def adelete_pattern(self, *args, **kwargs):
125113
kwargs["itersize"] = kwargs.get("itersize", DJANGO_ASYNC_REDIS_SCAN_ITERSIZE)
126114
return await self.client.delete_pattern(*args, **kwargs)
127115

128116
@omit_exception
129-
async def delete_many_async(self, *args, **kwargs):
117+
async def adelete_many(self, *args, **kwargs):
130118
return await self.client.delete_many(*args, **kwargs)
131119

132120
@omit_exception
133-
async def clear_async(self) -> None:
121+
async def aclear(self) -> None:
134122
await self.client.clear()
135123

136124
@omit_exception(return_value={})
137-
async def get_many_async(self, *args, **kwargs) -> OrderedDict:
125+
async def aget_many(self, *args, **kwargs) -> OrderedDict:
138126
return await self.client.get_many(*args, **kwargs)
139127

140128
@omit_exception
141-
async def set_many_async(self, *args, **kwargs):
129+
async def aset_many(self, *args, **kwargs):
142130
return await self.client.set_many(*args, **kwargs)
143131

144132
@omit_exception
145-
async def incr_async(self, *args, **kwargs):
133+
async def aincr(self, *args, **kwargs):
146134
return await self.client.incr(*args, **kwargs)
147135

148136
@omit_exception
149-
async def decr_async(self, *args, **kwargs):
137+
async def adecr(self, *args, **kwargs):
150138
return await self.client.decr(*args, **kwargs)
151139

152140
@omit_exception
153-
async def has_key_async(self, *args, **kwargs):
141+
async def ahas_key(self, *args, **kwargs):
154142
return await self.client.has_key(*args, **kwargs)
155143

156144
@omit_exception
157-
async def keys_async(self, *args, **kwargs):
145+
async def akeys(self, *args, **kwargs):
158146
return await self.client.keys(*args, **kwargs)
159147

160148
@omit_exception
161-
async def iter_keys_async(self, *args, **kwargs) -> AsyncGenerator[str, Any]:
149+
async def aiter_keys(self, *args, **kwargs) -> AsyncIterator[str]:
162150
"""
163151
Returns a coroutine that the dev will
164152
await since an async generator is returned.
165153
"""
166154
return self.client.iter_keys(*args, **kwargs)
167155

168156
@omit_exception
169-
async def ttl_async(self, *args, **kwargs) -> Optional[int]:
157+
async def attl(self, *args, **kwargs) -> Optional[int]:
170158
return await self.client.ttl(*args, **kwargs)
171159

172160
@omit_exception
173-
async def persist_async(self, *args, **kwargs):
161+
async def apersist(self, *args, **kwargs):
174162
return await self.client.persist(*args, **kwargs)
175163

176164
@omit_exception
177-
async def expire_async(self, *args, **kwargs):
165+
async def aexpire(self, *args, **kwargs):
178166
return await self.client.expire(*args, **kwargs)
179167

180168
@omit_exception
181-
async def close_async(self, **kwargs) -> None:
169+
async def aclose(self, **kwargs) -> None:
182170
await self.client.close(**kwargs)
183171

184172
@omit_exception
185-
async def touch_async(self, *args, **kwargs):
173+
async def atouch(self, *args, **kwargs):
186174
return await self.client.touch(*args, **kwargs)

0 commit comments

Comments
 (0)