diff options
author | Jordan Cook <jordan.cook@pioneer.com> | 2022-06-10 16:00:10 -0500 |
---|---|---|
committer | Jordan Cook <jordan.cook@pioneer.com> | 2022-06-10 16:31:52 -0500 |
commit | 5acc07aef936708198acedcb4eee12ba7ca726cb (patch) | |
tree | 86897a8a4e5f648fcf036f261ea929158c43d1f6 /requests_cache | |
parent | f24b5fe56e0c939dcdb6afa694c0a07b15ad5fdd (diff) | |
download | requests-cache-5acc07aef936708198acedcb4eee12ba7ca726cb.tar.gz |
Add decode_content option for storage classes, and use as the default behavior for Filesystem, DynamoDB, and MongoDB backends
Diffstat (limited to 'requests_cache')
-rw-r--r-- | requests_cache/backends/base.py | 32 | ||||
-rw-r--r-- | requests_cache/backends/dynamodb.py | 2 | ||||
-rw-r--r-- | requests_cache/backends/filesystem.py | 13 | ||||
-rw-r--r-- | requests_cache/backends/gridfs.py | 4 | ||||
-rw-r--r-- | requests_cache/backends/mongodb.py | 9 | ||||
-rw-r--r-- | requests_cache/serializers/__init__.py | 8 | ||||
-rw-r--r-- | requests_cache/serializers/cattrs.py | 3 | ||||
-rw-r--r-- | requests_cache/serializers/pipeline.py | 10 |
8 files changed, 58 insertions, 23 deletions
diff --git a/requests_cache/backends/base.py b/requests_cache/backends/base.py index 4b21833..7698af5 100644 --- a/requests_cache/backends/base.py +++ b/requests_cache/backends/base.py @@ -16,10 +16,13 @@ from typing import Iterable, Iterator, Optional, Tuple from requests import PreparedRequest, Response +from requests_cache.serializers.cattrs import CattrStage +from requests_cache.serializers.pipeline import SerializerPipeline + from ..cache_keys import create_key, redact_response from ..models import CachedResponse from ..policy import DEFAULT_CACHE_NAME, CacheSettings, ExpirationTime -from ..serializers import SerializerType, init_serializer, pickle_serializer +from ..serializers import SERIALIZERS, SerializerType, pickle_serializer # Specific exceptions that may be raised during deserialization DESERIALIZE_ERRORS = (AttributeError, ImportError, PickleError, TypeError, ValueError) @@ -269,23 +272,34 @@ class BaseStorage(MutableMapping, ABC): serializer: Custom serializer that provides ``loads`` and ``dumps`` methods no_serializer: Explicitly disable serialization, and write values as-is; this is to avoid ambiguity with ``serializer=None`` + decode_content: Decode JSON or text response body into a human-readable format kwargs: Additional backend-specific keyword arguments """ - # Default serializer to use for responses, if one isn't specified; may be overridden + # Default serializer to use for responses, if one isn't specified; may be overridden by subclass default_serializer: SerializerType = pickle_serializer - def __init__(self, serializer: SerializerType = None, no_serializer: bool = False, **kwargs): - if no_serializer: - self.serializer = None - else: - self.serializer = init_serializer(serializer or self.default_serializer) + def __init__( + self, + serializer: SerializerType = None, + no_serializer: bool = False, + decode_content: bool = False, + **kwargs, + ): + # Set a default serializer, unless explicitly disabled + self.serializer = None if no_serializer else (serializer or self.default_serializer) + + # Look up a serializer by name, if needed + if isinstance(self.serializer, str): + self.serializer = SERIALIZERS[self.serializer] + if isinstance(self.serializer, (SerializerPipeline, CattrStage)): + self.serializer.decode_content = decode_content logger.debug(f'Initialized {type(self).__name__} with serializer: {self.serializer}') def bulk_delete(self, keys: Iterable[str]): """Delete multiple keys from the cache, without raising errors for missing keys. This is a - naive implementation that subclasses should override with a more efficient backend-specific - implementation, if possible. + naive, generic implementation that subclasses should override with a more efficient + backend-specific implementation, if possible. """ for k in keys: try: diff --git a/requests_cache/backends/dynamodb.py b/requests_cache/backends/dynamodb.py index 11776d1..e5f0c4b 100644 --- a/requests_cache/backends/dynamodb.py +++ b/requests_cache/backends/dynamodb.py @@ -34,6 +34,7 @@ class DynamoDbCache(BaseCache): table_name: str = 'http_cache', ttl: bool = True, connection: ServiceResource = None, + decode_content: bool = True, **kwargs, ): super().__init__(cache_name=table_name, **kwargs) @@ -42,6 +43,7 @@ class DynamoDbCache(BaseCache): namespace='responses', ttl=ttl, connection=connection, + decode_content=decode_content, **kwargs, ) self.redirects = DynamoDbDict( diff --git a/requests_cache/backends/filesystem.py b/requests_cache/backends/filesystem.py index ece7042..39c0c46 100644 --- a/requests_cache/backends/filesystem.py +++ b/requests_cache/backends/filesystem.py @@ -25,13 +25,22 @@ class FileCache(BaseCache): use_cache_dir: Store datebase in a user cache directory (e.g., `~/.cache/`) use_temp: Store cache files in a temp directory (e.g., ``/tmp/http_cache/``). Note: if ``cache_name`` is an absolute path, this option will be ignored. + decode_content: Decode JSON or text response body into a human-readable format extension: Extension for cache files. If not specified, the serializer default extension will be used. """ - def __init__(self, cache_name: AnyPath = 'http_cache', use_temp: bool = False, **kwargs): + def __init__( + self, + cache_name: AnyPath = 'http_cache', + use_temp: bool = False, + decode_content: bool = True, + **kwargs, + ): super().__init__(cache_name=str(cache_name), **kwargs) - self.responses: FileDict = FileDict(cache_name, use_temp=use_temp, **kwargs) + self.responses: FileDict = FileDict( + cache_name, use_temp=use_temp, decode_content=decode_content, **kwargs + ) self.redirects: SQLiteDict = SQLiteDict( self.cache_dir / 'redirects.sqlite', 'redirects', no_serializer=True, **kwargs ) diff --git a/requests_cache/backends/gridfs.py b/requests_cache/backends/gridfs.py index 0e01ad3..7d48e6d 100644 --- a/requests_cache/backends/gridfs.py +++ b/requests_cache/backends/gridfs.py @@ -27,9 +27,9 @@ class GridFSCache(BaseCache): kwargs: Additional keyword arguments for :py:class:`pymongo.MongoClient` """ - def __init__(self, db_name: str, **kwargs): + def __init__(self, db_name: str, decode_content: bool = False, **kwargs): super().__init__(cache_name=db_name, **kwargs) - self.responses = GridFSDict(db_name, **kwargs) + self.responses = GridFSDict(db_name, decode_content=decode_content, **kwargs) self.redirects = MongoDict( db_name, collection_name='redirects', diff --git a/requests_cache/backends/mongodb.py b/requests_cache/backends/mongodb.py index eb1cc22..c719aa7 100644 --- a/requests_cache/backends/mongodb.py +++ b/requests_cache/backends/mongodb.py @@ -29,12 +29,19 @@ class MongoCache(BaseCache): kwargs: Additional keyword arguments for :py:class:`pymongo.mongo_client.MongoClient` """ - def __init__(self, db_name: str = 'http_cache', connection: MongoClient = None, **kwargs): + def __init__( + self, + db_name: str = 'http_cache', + connection: MongoClient = None, + decode_content: bool = True, + **kwargs, + ): super().__init__(cache_name=db_name, **kwargs) self.responses: MongoDict = MongoDict( db_name, collection_name='responses', connection=connection, + decode_content=decode_content, **kwargs, ) self.redirects: MongoDict = MongoDict( diff --git a/requests_cache/serializers/__init__.py b/requests_cache/serializers/__init__.py index 6328ea7..085dfc2 100644 --- a/requests_cache/serializers/__init__.py +++ b/requests_cache/serializers/__init__.py @@ -48,7 +48,6 @@ __all__ = [ 'pickle_serializer', 'safe_pickle_serializer', 'yaml_serializer', - 'init_serializer', 'utf8_encoder', ] @@ -60,10 +59,3 @@ SERIALIZERS = { } SerializerType = Union[str, SerializerPipeline, Stage] - - -def init_serializer(serializer: SerializerType = None): - """Initialize a serializer from a name or instance""" - if isinstance(serializer, str): - serializer = SERIALIZERS[serializer] - return serializer diff --git a/requests_cache/serializers/cattrs.py b/requests_cache/serializers/cattrs.py index c124830..befc4b5 100644 --- a/requests_cache/serializers/cattrs.py +++ b/requests_cache/serializers/cattrs.py @@ -46,10 +46,11 @@ class CattrStage(Stage): * Supported Content-Types are ``application/json`` and ``text/*``. All other types will be saved as-is. * Decoded responses are saved in a separate ``_decoded_content`` attribute, to ensure that ``_content`` is always binary. + * This is the default behavior for Filesystem, DynamoDB, and MongoDB backends. """ def __init__( - self, factory: Callable[..., GenConverter] = None, decode_content: bool = True, **kwargs + self, factory: Callable[..., GenConverter] = None, decode_content: bool = False, **kwargs ): self.converter = init_converter(factory, **kwargs) self.decode_content = decode_content diff --git a/requests_cache/serializers/pipeline.py b/requests_cache/serializers/pipeline.py index 589a36c..25da5b0 100644 --- a/requests_cache/serializers/pipeline.py +++ b/requests_cache/serializers/pipeline.py @@ -60,5 +60,15 @@ class SerializerPipeline: value = step(value) return value + @property + def decode_content(self) -> bool: + return getattr(self.stages[0], 'decode_content', False) if self.stages else False + + @decode_content.setter + def decode_content(self, value: bool): + """Set decode_content, if the pipeline is based on CattrStage""" + if self.stages and hasattr(self.stages[0], 'decode_content'): + self.stages[0].decode_content = value + def __str__(self) -> str: return f'SerializerPipeline(name={self.name}, n_stages={len(self.dump_stages)})' |