summaryrefslogtreecommitdiff
path: root/requests_cache
diff options
context:
space:
mode:
authorJordan Cook <jordan.cook@pioneer.com>2022-06-10 16:00:10 -0500
committerJordan Cook <jordan.cook@pioneer.com>2022-06-10 16:31:52 -0500
commit5acc07aef936708198acedcb4eee12ba7ca726cb (patch)
tree86897a8a4e5f648fcf036f261ea929158c43d1f6 /requests_cache
parentf24b5fe56e0c939dcdb6afa694c0a07b15ad5fdd (diff)
downloadrequests-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.py32
-rw-r--r--requests_cache/backends/dynamodb.py2
-rw-r--r--requests_cache/backends/filesystem.py13
-rw-r--r--requests_cache/backends/gridfs.py4
-rw-r--r--requests_cache/backends/mongodb.py9
-rw-r--r--requests_cache/serializers/__init__.py8
-rw-r--r--requests_cache/serializers/cattrs.py3
-rw-r--r--requests_cache/serializers/pipeline.py10
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)})'