3
3
import logging
4
4
import os
5
5
import time
6
+ from abc import abstractmethod
6
7
from enum import Enum
7
8
from types import TracebackType
8
- from typing import Callable , Optional , Set , Type
9
+ from typing import Callable , Generic , List , Optional , Set , TYPE_CHECKING , Type , TypeVar
9
10
10
- from aiocache import serializers
11
+ from aiocache .serializers import StringSerializer
12
+
13
+ if TYPE_CHECKING : # pragma: no cover
14
+ from aiocache .plugins import BasePlugin
15
+ from aiocache .serializers import BaseSerializer
11
16
12
17
13
18
logger = logging .getLogger (__name__ )
14
19
15
20
SENTINEL = object ()
21
+ CacheKeyType = TypeVar ("CacheKeyType" )
16
22
17
23
18
24
class API :
@@ -87,7 +93,7 @@ async def _plugins(self, *args, **kwargs):
87
93
return _plugins
88
94
89
95
90
- class BaseCache :
96
+ class BaseCache ( Generic [ CacheKeyType ]) :
91
97
"""
92
98
Base class that agregates the common logic for the different caches that may exist. Cache
93
99
related available options are:
@@ -97,9 +103,9 @@ class BaseCache:
97
103
:param plugins: list of :class:`aiocache.plugins.BasePlugin` derived classes. Default is empty
98
104
list.
99
105
:param namespace: string to use as default prefix for the key used in all operations of
100
- the backend. Default is None
106
+ the backend. Default is an empty string, "".
101
107
:param key_builder: alternative callable to build the key. Receives the key and the namespace
102
- as params and should return something that can be used as key by the underlying backend.
108
+ as params and should return a string that can be used as a key by the underlying backend.
103
109
:param timeout: int or float in seconds specifying maximum timeout for the operations to last.
104
110
By default its 5. Use 0 or None if you want to disable it.
105
111
:param ttl: int the expiration time in seconds to use as a default in all operations of
@@ -109,18 +115,22 @@ class BaseCache:
109
115
NAME : str
110
116
111
117
def __init__ (
112
- self , serializer = None , plugins = None , namespace = None , key_builder = None , timeout = 5 , ttl = None
118
+ self ,
119
+ serializer : Optional ["BaseSerializer" ] = None ,
120
+ plugins : Optional [List ["BasePlugin" ]] = None ,
121
+ namespace : str = "" ,
122
+ key_builder : Callable [[str , str ], str ] = lambda key , namespace : f"{ namespace } { key } " ,
123
+ timeout : Optional [float ] = 5 ,
124
+ ttl : Optional [float ] = None ,
113
125
):
114
- self .timeout = float (timeout ) if timeout is not None else timeout
115
- self .namespace = namespace
116
- self .ttl = float (ttl ) if ttl is not None else ttl
117
- self .build_key = key_builder or self ._build_key
126
+ self .timeout = float (timeout ) if timeout is not None else None
127
+ self .ttl = float (ttl ) if ttl is not None else None
118
128
119
- self ._serializer = None
120
- self .serializer = serializer or serializers . StringSerializer ()
129
+ self .namespace = namespace
130
+ self ._build_key = key_builder
121
131
122
- self ._plugins = None
123
- self .plugins = plugins or []
132
+ self ._serializer = serializer or StringSerializer ()
133
+ self ._plugins = plugins or []
124
134
125
135
@property
126
136
def serializer (self ):
@@ -162,9 +172,8 @@ async def add(self, key, value, ttl=SENTINEL, dumps_fn=None, namespace=None, _co
162
172
- :class:`asyncio.TimeoutError` if it lasts more than self.timeout
163
173
"""
164
174
start = time .monotonic ()
165
- dumps = dumps_fn or self ._serializer .dumps
166
- ns = namespace if namespace is not None else self .namespace
167
- ns_key = self .build_key (key , namespace = ns )
175
+ dumps = dumps_fn or self .serializer .dumps
176
+ ns_key = self .build_key (key , namespace )
168
177
169
178
await self ._add (ns_key , dumps (value ), ttl = self ._get_ttl (ttl ), _conn = _conn )
170
179
@@ -192,9 +201,8 @@ async def get(self, key, default=None, loads_fn=None, namespace=None, _conn=None
192
201
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
193
202
"""
194
203
start = time .monotonic ()
195
- loads = loads_fn or self ._serializer .loads
196
- ns = namespace if namespace is not None else self .namespace
197
- ns_key = self .build_key (key , namespace = ns )
204
+ loads = loads_fn or self .serializer .loads
205
+ ns_key = self .build_key (key , namespace )
198
206
199
207
value = loads (await self ._get (ns_key , encoding = self .serializer .encoding , _conn = _conn ))
200
208
@@ -224,10 +232,9 @@ async def multi_get(self, keys, loads_fn=None, namespace=None, _conn=None):
224
232
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
225
233
"""
226
234
start = time .monotonic ()
227
- loads = loads_fn or self ._serializer .loads
228
- ns = namespace if namespace is not None else self .namespace
235
+ loads = loads_fn or self .serializer .loads
229
236
230
- ns_keys = [self .build_key (key , namespace = ns ) for key in keys ]
237
+ ns_keys = [self .build_key (key , namespace ) for key in keys ]
231
238
values = [
232
239
loads (value )
233
240
for value in await self ._multi_get (
@@ -269,9 +276,8 @@ async def set(
269
276
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
270
277
"""
271
278
start = time .monotonic ()
272
- dumps = dumps_fn or self ._serializer .dumps
273
- ns = namespace if namespace is not None else self .namespace
274
- ns_key = self .build_key (key , namespace = ns )
279
+ dumps = dumps_fn or self .serializer .dumps
280
+ ns_key = self .build_key (key , namespace )
275
281
276
282
res = await self ._set (
277
283
ns_key , dumps (value ), ttl = self ._get_ttl (ttl ), _cas_token = _cas_token , _conn = _conn
@@ -303,12 +309,11 @@ async def multi_set(self, pairs, ttl=SENTINEL, dumps_fn=None, namespace=None, _c
303
309
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
304
310
"""
305
311
start = time .monotonic ()
306
- dumps = dumps_fn or self ._serializer .dumps
307
- ns = namespace if namespace is not None else self .namespace
312
+ dumps = dumps_fn or self .serializer .dumps
308
313
309
314
tmp_pairs = []
310
315
for key , value in pairs :
311
- tmp_pairs .append ((self .build_key (key , namespace = ns ), dumps (value )))
316
+ tmp_pairs .append ((self .build_key (key , namespace ), dumps (value )))
312
317
313
318
await self ._multi_set (tmp_pairs , ttl = self ._get_ttl (ttl ), _conn = _conn )
314
319
@@ -339,8 +344,7 @@ async def delete(self, key, namespace=None, _conn=None):
339
344
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
340
345
"""
341
346
start = time .monotonic ()
342
- ns = namespace if namespace is not None else self .namespace
343
- ns_key = self .build_key (key , namespace = ns )
347
+ ns_key = self .build_key (key , namespace )
344
348
ret = await self ._delete (ns_key , _conn = _conn )
345
349
logger .debug ("DELETE %s %d (%.4f)s" , ns_key , ret , time .monotonic () - start )
346
350
return ret
@@ -364,8 +368,7 @@ async def exists(self, key, namespace=None, _conn=None):
364
368
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
365
369
"""
366
370
start = time .monotonic ()
367
- ns = namespace if namespace is not None else self .namespace
368
- ns_key = self .build_key (key , namespace = ns )
371
+ ns_key = self .build_key (key , namespace )
369
372
ret = await self ._exists (ns_key , _conn = _conn )
370
373
logger .debug ("EXISTS %s %d (%.4f)s" , ns_key , ret , time .monotonic () - start )
371
374
return ret
@@ -392,8 +395,7 @@ async def increment(self, key, delta=1, namespace=None, _conn=None):
392
395
:raises: :class:`TypeError` if value is not incrementable
393
396
"""
394
397
start = time .monotonic ()
395
- ns = namespace if namespace is not None else self .namespace
396
- ns_key = self .build_key (key , namespace = ns )
398
+ ns_key = self .build_key (key , namespace )
397
399
ret = await self ._increment (ns_key , delta , _conn = _conn )
398
400
logger .debug ("INCREMENT %s %d (%.4f)s" , ns_key , ret , time .monotonic () - start )
399
401
return ret
@@ -418,8 +420,7 @@ async def expire(self, key, ttl, namespace=None, _conn=None):
418
420
:raises: :class:`asyncio.TimeoutError` if it lasts more than self.timeout
419
421
"""
420
422
start = time .monotonic ()
421
- ns = namespace if namespace is not None else self .namespace
422
- ns_key = self .build_key (key , namespace = ns )
423
+ ns_key = self .build_key (key , namespace )
423
424
ret = await self ._expire (ns_key , ttl , _conn = _conn )
424
425
logger .debug ("EXPIRE %s %d (%.4f)s" , ns_key , ret , time .monotonic () - start )
425
426
return ret
@@ -498,12 +499,15 @@ async def close(self, *args, _conn=None, **kwargs):
498
499
async def _close (self , * args , ** kwargs ):
499
500
pass
500
501
501
- def _build_key (self , key , namespace = None ):
502
- if namespace is not None :
503
- return "{}{}" .format (namespace , _ensure_key (key ))
504
- if self .namespace is not None :
505
- return "{}{}" .format (self .namespace , _ensure_key (key ))
506
- return key
502
+ @abstractmethod
503
+ def build_key (self , key : str , namespace : Optional [str ] = None ) -> CacheKeyType :
504
+ raise NotImplementedError ()
505
+
506
+ def _str_build_key (self , key : str , namespace : Optional [str ] = None ) -> str :
507
+ """Simple key builder that can be used in subclasses for build_key()."""
508
+ key_name = key .value if isinstance (key , Enum ) else key
509
+ ns = self .namespace if namespace is None else namespace
510
+ return self ._build_key (key_name , ns )
507
511
508
512
def _get_ttl (self , ttl ):
509
513
return ttl if ttl is not SENTINEL else self .ttl
@@ -550,12 +554,5 @@ async def _do_inject_conn(self, *args, **kwargs):
550
554
return _do_inject_conn
551
555
552
556
553
- def _ensure_key (key ):
554
- if isinstance (key , Enum ):
555
- return key .value
556
- else :
557
- return key
558
-
559
-
560
557
for cmd in API .CMDS :
561
558
setattr (_Conn , cmd .__name__ , _Conn ._inject_conn (cmd .__name__ ))
0 commit comments