Skip to content

Commit c4fc8ba

Browse files
Use base.ensure_key() in _build_key() to ensure consistency of enum keys between different Python versions (#635)
1 parent 2cc74f7 commit c4fc8ba

File tree

7 files changed

+30
-30
lines changed

7 files changed

+30
-30
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* Add ``async with`` support to ``BaseCache``.
77
* Remove deprecated ``loop`` parameters.
88
* Remove deprecated ``cache`` parameter from ``create()``.
9-
* Use ``str()`` in ``_build_key()`` to ensure consistency of enum keys between different Python versions (if using enum keys, upgrading to 0.12 may invalidate existing caches due to key values changing).
9+
* Use ``base._ensure_key()`` in ``_build_key()`` to ensure consistency of enum
10+
keys between different Python versions. [#633](https://github.com/aio-libs/aiocache/issues/633) -- Padraic Shafer
1011
* Improved support for ``build_key(key, namespace)`` [#569](https://github.com/aio-libs/aiocache/issues/569) - Padraic Shafer
1112
* `BaseCache.build_key` uses `namespace` argument if provided,
1213
otherwise it uses `self.namespace`.

aiocache/backends/redis.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import redis.asyncio as redis
55
from redis.exceptions import ResponseError as IncrbyException
66

7-
from aiocache.base import BaseCache
7+
from aiocache.base import BaseCache, _ensure_key
88
from aiocache.serializers import JsonSerializer
99

1010

@@ -219,11 +219,12 @@ def parse_uri_path(cls, path):
219219
return options
220220

221221
def _build_key(self, key, namespace=None):
222-
# TODO(PY311): Remove str()
223222
if namespace is not None:
224-
return "{}{}{}".format(namespace, ":" if namespace else "", str(key))
223+
return "{}{}{}".format(
224+
namespace, ":" if namespace else "", _ensure_key(key))
225225
if self.namespace is not None:
226-
return "{}{}{}".format(self.namespace, ":" if self.namespace else "", str(key))
226+
return "{}{}{}".format(
227+
self.namespace, ":" if self.namespace else "", _ensure_key(key))
227228
return key
228229

229230
def __repr__(self): # pragma: no cover

aiocache/base.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import os
55
import time
6+
from enum import Enum
67
from types import TracebackType
78
from typing import Callable, Optional, Set, Type
89

@@ -498,14 +499,10 @@ async def _close(self, *args, **kwargs):
498499
pass
499500

500501
def _build_key(self, key, namespace=None):
501-
# TODO(PY311): Remove str() calls.
502-
# str() is needed to ensure consistent results when using enums between
503-
# Python 3.11+ and older releases due to changed __format__() method:
504-
# https://docs.python.org/3/whatsnew/3.11.html#enum
505502
if namespace is not None:
506-
return "{}{}".format(namespace, str(key))
503+
return "{}{}".format(namespace, _ensure_key(key))
507504
if self.namespace is not None:
508-
return "{}{}".format(self.namespace, str(key))
505+
return "{}{}".format(self.namespace, _ensure_key(key))
509506
return key
510507

511508
def _get_ttl(self, ttl):
@@ -553,5 +550,12 @@ async def _do_inject_conn(self, *args, **kwargs):
553550
return _do_inject_conn
554551

555552

553+
def _ensure_key(key):
554+
if isinstance(key, Enum):
555+
return key.value
556+
else:
557+
return key
558+
559+
556560
for cmd in API.CMDS:
557561
setattr(_Conn, cmd.__name__, _Conn._inject_conn(cmd.__name__))

tests/acceptance/test_decorators.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66

77
from aiocache import cached, cached_stampede, multi_cached
8+
from aiocache.base import _ensure_key
89
from ..utils import Keys
910

1011

@@ -137,17 +138,16 @@ async def fn(keys):
137138
assert await cache.exists(Keys.KEY) is True
138139

139140
async def test_multi_cached_key_builder(self, cache):
140-
# TODO(PY311): Remove str() calls
141141
def build_key(key, f, self, keys, market="ES"):
142-
return "{}_{}_{}".format(f.__name__, str(key), market)
142+
return "{}_{}_{}".format(f.__name__, _ensure_key(key), market)
143143

144144
@multi_cached(keys_from_attr="keys", key_builder=build_key)
145145
async def fn(self, keys, market="ES"):
146146
return {Keys.KEY: 1, Keys.KEY_1: 2}
147147

148148
await fn("self", keys=[Keys.KEY, Keys.KEY_1])
149-
assert await cache.exists("fn_" + str(Keys.KEY) + "_ES") is True
150-
assert await cache.exists("fn_" + str(Keys.KEY_1) + "_ES") is True
149+
assert await cache.exists("fn_" + _ensure_key(Keys.KEY) + "_ES") is True
150+
assert await cache.exists("fn_" + _ensure_key(Keys.KEY_1) + "_ES") is True
151151

152152
async def test_fn_with_args(self, cache):
153153
@multi_cached("keys")

tests/ut/backends/test_memcached.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55

66
from aiocache.backends.memcached import MemcachedBackend, MemcachedCache
7-
from aiocache.base import BaseCache
7+
from aiocache.base import BaseCache, _ensure_key
88
from aiocache.serializers import JsonSerializer
99
from ...utils import Keys
1010

@@ -249,8 +249,7 @@ def test_parse_uri_path(self):
249249

250250
@pytest.mark.parametrize(
251251
"namespace, expected",
252-
# TODO(PY311): Remove str()
253-
([None, "test" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns" + str(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
252+
([None, "test" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns" + _ensure_key(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
254253
)
255254
def test_build_key_bytes(self, set_test_namespace, memcached_cache, namespace, expected):
256255
assert memcached_cache.build_key(Keys.KEY, namespace=namespace) == expected.encode()

tests/ut/backends/test_redis.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from redis.exceptions import ResponseError
66

77
from aiocache.backends.redis import RedisBackend, RedisCache
8-
from aiocache.base import BaseCache
8+
from aiocache.base import BaseCache, _ensure_key
99
from aiocache.serializers import JsonSerializer
1010
from ...utils import Keys
1111

@@ -253,8 +253,7 @@ def test_parse_uri_path(self, path, expected):
253253

254254
@pytest.mark.parametrize(
255255
"namespace, expected",
256-
# TODO(PY311): Remove str()
257-
([None, "test:" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns:" + str(Keys.KEY)]), # noqa: B950
256+
([None, "test:" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns:" + _ensure_key(Keys.KEY)]), # noqa: B950
258257
)
259258
def test_build_key_double_dot(self, set_test_namespace, redis_cache, namespace, expected):
260259
assert redis_cache.build_key(Keys.KEY, namespace=namespace) == expected

tests/ut/test_base.py

+5-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from aiocache.base import API, BaseCache, _Conn
7+
from aiocache.base import API, BaseCache, _Conn, _ensure_key
88
from ..utils import Keys
99

1010

@@ -205,8 +205,7 @@ def set_test_namespace(self, base_cache):
205205

206206
@pytest.mark.parametrize(
207207
"namespace, expected",
208-
# TODO(PY311): Remove str()
209-
([None, "test" + str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns" + str(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
208+
([None, "test" + _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns" + _ensure_key(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
210209
)
211210
def test_build_key(self, set_test_namespace, base_cache, namespace, expected):
212211
assert base_cache.build_key(Keys.KEY, namespace=namespace) == expected
@@ -219,18 +218,16 @@ def test_alt_build_key(self):
219218
def alt_base_cache(self, init_namespace="test"):
220219
"""Custom key_builder for cache"""
221220
def build_key(key, namespace=None):
222-
# TODO(PY311): Remove str()
223221
ns = namespace if namespace is not None else ""
224222
sep = ":" if namespace else ""
225-
return f"{ns}{sep}{str(key)}"
223+
return f"{ns}{sep}{_ensure_key(key)}"
226224

227225
cache = BaseCache(key_builder=build_key, namespace=init_namespace)
228226
return cache
229227

230228
@pytest.mark.parametrize(
231229
"namespace, expected",
232-
# TODO(PY311): Remove str()
233-
([None, str(Keys.KEY)], ["", str(Keys.KEY)], ["my_ns", "my_ns:" + str(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
230+
([None, _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["my_ns", "my_ns:" + _ensure_key(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
234231
)
235232
def test_alt_build_key_override_namespace(self, alt_base_cache, namespace, expected):
236233
"""Custom key_builder overrides namespace of cache"""
@@ -239,8 +236,7 @@ def test_alt_build_key_override_namespace(self, alt_base_cache, namespace, expec
239236

240237
@pytest.mark.parametrize(
241238
"init_namespace, expected",
242-
# TODO(PY311): Remove str()
243-
([None, str(Keys.KEY)], ["", str(Keys.KEY)], ["test", "test:" + str(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
239+
([None, _ensure_key(Keys.KEY)], ["", _ensure_key(Keys.KEY)], ["test", "test:" + _ensure_key(Keys.KEY)]), # type: ignore[attr-defined] # noqa: B950
244240
)
245241
async def test_alt_build_key_default_namespace(
246242
self, init_namespace, alt_base_cache, expected):

0 commit comments

Comments
 (0)