diff --git a/CHANGELOG.md b/CHANGELOG.md index e48ee51..0d48e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added - +- Added `MemcacheCacheHandler`, a cache handler that stores the token info using pymemcache. - Added support for audiobook endpoints: get_audiobook, get_audiobooks, and get_audiobook_chapters. - Added integration tests for audiobook endpoints. - Removed `python 2.7` from GitHub Actions CI workflow. Python v2.7 reached end of life support and is no longer supported by Ubuntu 20.04. diff --git a/docs/index.rst b/docs/index.rst index 3b73452..3534bfa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -185,14 +185,17 @@ cache handler ``CacheHandler``. The default cache handler ``CacheFileHandler`` i An instance of that new class can then be passed as a parameter when creating ``SpotifyOAuth``, ``SpotifyPKCE`` or ``SpotifyImplicitGrant``. The following handlers are available and defined in the URL above. + - ``CacheFileHandler`` - ``MemoryCacheHandler`` - ``DjangoSessionCacheHandler`` - ``FlaskSessionCacheHandler`` - ``RedisCacheHandler`` + - ``MemcacheCacheHandler``: install with dependency using ``pip install "spotipy[pymemcache]"`` Feel free to contribute new cache handlers to the repo. + Examples ======================= diff --git a/setup.py b/setup.py index c7d1752..e1ad46e 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,13 @@ test_reqs = [ 'mock==2.0.0' ] +memcache_cache_reqs = [ + 'pymemcache>=3.5.2' +] + extra_reqs = { - 'test': test_reqs + 'test': test_reqs, + 'memcache': memcache_cache_reqs } setup( @@ -25,7 +30,7 @@ setup( }, python_requires='>3.8', install_requires=[ - "redis>=3.5.3", + "redis>=3.5.3", # TODO: Move to extras_require in v3 "requests>=2.25.0", "urllib3>=1.26.0" ], diff --git a/spotipy/cache_handler.py b/spotipy/cache_handler.py index 0ab98e9..7ae94a2 100644 --- a/spotipy/cache_handler.py +++ b/spotipy/cache_handler.py @@ -4,7 +4,8 @@ __all__ = [ 'DjangoSessionCacheHandler', 'FlaskSessionCacheHandler', 'MemoryCacheHandler', - 'RedisCacheHandler'] + 'RedisCacheHandler', + 'MemcacheCacheHandler'] import errno import json @@ -208,3 +209,34 @@ class RedisCacheHandler(CacheHandler): self.redis.set(self.key, json.dumps(token_info)) except RedisError as e: logger.warning('Error saving token to cache: ' + str(e)) + + +class MemcacheCacheHandler(CacheHandler): + """A Cache handler that stores the token info in Memcache using the pymemcache client + """ + def __init__(self, memcache, key=None) -> None: + """ + Parameters: + * memcache: memcache client object provided by pymemcache + (https://pymemcache.readthedocs.io/en/latest/getting_started.html) + * key: May be supplied, will otherwise be generated + (takes precedence over `token_info`) + """ + self.memcache = memcache + self.key = key if key else 'token_info' + + def get_cached_token(self): + from pymemcache import MemcacheError + try: + token_info = self.memcache.get(self.key) + if token_info: + return json.loads(token_info.decode()) + except MemcacheError as e: + logger.warning('Error getting token from cache' + str(e)) + + def save_token_to_cache(self, token_info): + from pymemcache import MemcacheError + try: + self.memcache.set(self.key, json.dumps(token_info)) + except MemcacheError as e: + logger.warning('Error saving token to cache' + str(e))