summaryrefslogtreecommitdiff
path: root/requests_cache
diff options
context:
space:
mode:
authorJordan Cook <jordan.cook@pioneer.com>2022-04-11 21:03:50 -0500
committerJordan Cook <jordan.cook@pioneer.com>2022-04-16 21:08:42 -0500
commitd6ee9143965d53dae44ca3a98802b2cc7ad6eeb7 (patch)
tree96e38c3f7289a0ff3a46c23df3f1e5b3b7c1a940 /requests_cache
parent166f5690fb8d5b067f839fa8ffb9421cf1b8a7e7 (diff)
downloadrequests-cache-d6ee9143965d53dae44ca3a98802b2cc7ad6eeb7.tar.gz
Move detailed backend docs from rst docstings to md files
Diffstat (limited to 'requests_cache')
-rw-r--r--requests_cache/backends/base.py34
-rw-r--r--requests_cache/backends/dynamodb.py51
-rw-r--r--requests_cache/backends/filesystem.py56
-rw-r--r--requests_cache/backends/gridfs.py18
-rw-r--r--requests_cache/backends/mongodb.py97
-rw-r--r--requests_cache/backends/redis.py61
-rw-r--r--requests_cache/backends/sqlite.py87
-rw-r--r--requests_cache/session.py16
8 files changed, 18 insertions, 402 deletions
diff --git a/requests_cache/backends/base.py b/requests_cache/backends/base.py
index a8b1be3..6a0c157 100644
--- a/requests_cache/backends/base.py
+++ b/requests_cache/backends/base.py
@@ -1,4 +1,4 @@
-"""Base classes for all cache backends.
+"""Base classes for all cache backends
.. automodsumm:: requests_cache.backends.base
:classes-only:
@@ -34,8 +34,7 @@ class BaseCache:
This manages higher-level cache operations, including:
- * Cache expiration
- * Generating cache keys
+ * Saving and retrieving responses
* Managing redirect history
* Convenience methods for general cache info
@@ -242,21 +241,21 @@ class BaseCache:
class BaseStorage(MutableMapping, ABC):
- """Base class for backend storage implementations. This provides a common dictionary-like
- interface for the underlying storage operations (create, read, update, delete). One
- ``BaseStorage`` instance corresponds to a single table/hash/collection, or whatever the
- backend-specific equivalent may be.
-
- ``BaseStorage`` subclasses contain no behavior specific to ``requests`` or caching, which are
- handled by :py:class:`.BaseCache`.
-
- ``BaseStorage`` also contains a serializer module or instance (defaulting to :py:mod:`pickle`),
- which determines how :py:class:`.CachedResponse` objects are saved internally. See
- :ref:`serializers` for details.
+ """Base class for client-agnostic storage implementations. Notes:
+
+ * This provides a common dictionary-like interface for the underlying storage operations
+ (create, read, update, delete).
+ * One ``BaseStorage`` instance corresponds to a single table/hash/collection, or whatever the
+ backend-specific equivalent may be.
+ * ``BaseStorage`` subclasses contain no behavior specific to ``requests``, which are handled by
+ :py:class:`.BaseCache` subclasses.
+ * ``BaseStorage`` also contains a serializer object (defaulting to :py:mod:`pickle`), which
+ determines how :py:class:`.CachedResponse` objects are saved internally. See :ref:`serializers`
+ for details.
Args:
serializer: Custom serializer that provides ``loads`` and ``dumps`` methods
- kwargs: Additional serializer or backend-specific keyword arguments
+ kwargs: Additional backend-specific keyword arguments
"""
def __init__(self, serializer=None, **kwargs):
@@ -294,8 +293,9 @@ class DictStorage(UserDict, BaseStorage):
self.serializer = None
def __getitem__(self, key):
- """An additional step is needed here for response data. Since the original response object
- is still in memory, its content has already been read and needs to be reset.
+ """An additional step is needed here for response data. The original response object
+ is still in memory, and hasn't gone through a serialize/deserialize loop. So, the file-like
+ response body has already been read, and needs to be reset.
"""
item = super().__getitem__(key)
if getattr(item, 'raw', None):
diff --git a/requests_cache/backends/dynamodb.py b/requests_cache/backends/dynamodb.py
index 2ec971a..260a078 100644
--- a/requests_cache/backends/dynamodb.py
+++ b/requests_cache/backends/dynamodb.py
@@ -1,54 +1,3 @@
-"""
-.. image::
- ../_static/dynamodb.png
-
-`DynamoDB <https://aws.amazon.com/dynamodb>`_ is a NoSQL document database hosted on `Amazon Web
-Services <https://aws.amazon.com>`_.
-
-Use Cases
-^^^^^^^^^
-In terms of features, DynamoDB is roughly comparable to MongoDB and other NoSQL databases. It is a
-fully managed service, making it very convenient to use if you are already on AWS. It is an
-especially good fit for serverless applications running on
-`AWS Lambda <https://aws.amazon.com/lambda>`_.
-
-.. warning::
- DynamoDB binary item sizes are limited to 400KB. If you need to cache larger responses, consider
- using a different backend.
-
-
-
-Creating Tables
-^^^^^^^^^^^^^^^
-Tables will be automatically created if they don't already exist. This is convienient if you just
-want to quickly test out DynamoDB as a cache backend, but in a production environment you will
-likely want to create the tables yourself, for example with `CloudFormation
-<https://aws.amazon.com/cloudformation/>`_ or `Terraform <https://www.terraform.io/>`_. Here are the
-details you'll need:
-
-* Tables: two tables, named ``responses`` and ``redirects``
-* Partition key (aka namespace): ``namespace``
-* Range key (aka sort key): ``key``
-* Attributes: ``namespace`` (string) and ``key`` (string)
-
-Connection Options
-^^^^^^^^^^^^^^^^^^
-The DynamoDB backend accepts any keyword arguments for :py:meth:`boto3.session.Session.resource`.
-These can be passed via :py:class:`.CachedSession`:
-
- >>> session = CachedSession('http_cache', backend='dynamodb', region_name='us-west-2')
-
-Or via :py:class:`.DynamoDbCache`:
-
- >>> backend = DynamoDbCache(region_name='us-west-2')
- >>> session = CachedSession('http_cache', backend=backend)
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.dynamodb
- :classes-only:
- :nosignatures:
-"""
from typing import Dict, Iterable
import boto3
diff --git a/requests_cache/backends/filesystem.py b/requests_cache/backends/filesystem.py
index 21004ac..021204b 100644
--- a/requests_cache/backends/filesystem.py
+++ b/requests_cache/backends/filesystem.py
@@ -1,59 +1,3 @@
-"""
-.. image::
- ../_static/files-generic.png
-
-This backend stores responses in files on the local filesystem, with one file per response.
-
-Use Cases
-^^^^^^^^^
-This backend is useful if you would like to use your cached response data outside of requests-cache,
-for example:
-
-* Manually viewing cached responses without the need for extra tools (e.g., with a simple text editor)
-* Using cached responses as sample data for automated tests
-* Reading cached responses directly from another application or library, without depending on
- requests-cache
-
-File Formats
-^^^^^^^^^^^^
-By default, responses are saved as pickle files. If you want to save responses in a human-readable
-format, you can use one of the other available :ref:`serializers`. For example, to save responses as
-JSON files:
-
- >>> session = CachedSession('~/http_cache', backend='filesystem', serializer='json')
- >>> session.get('https://httpbin.org/get')
- >>> print(list(session.cache.paths()))
- ['/home/user/http_cache/4dc151d95200ec.json']
-
-Or as YAML (requires ``pyyaml``):
-
- >>> session = CachedSession('~/http_cache', backend='filesystem', serializer='yaml')
- >>> session.get('https://httpbin.org/get')
- >>> print(list(session.cache.paths()))
- ['/home/user/http_cache/4dc151d95200ec.yaml']
-
-Cache Files
-^^^^^^^^^^^
-* See :ref:`files` for general info on specifying cache paths
-* The path for a given response will be in the format ``<cache_name>/<cache_key>``
-* Redirects are stored in a separate SQLite database, located at ``<cache_name>/redirects.sqlite``
-* Use :py:meth:`.FileCache.paths` to get a list of all cached response paths
-
-Performance and Limitations
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-* Write performance will vary based on the serializer used, in the range of roughly 1-3ms per write.
-* This backend stores response files in a single directory, and does not currently implement
- fan-out. This means that on most filesystems, storing a very large number of responses will result
- in reduced performance.
-* This backend currently uses a simple threading lock rather than a file lock system, so it is not
- an ideal choice for highly parallel applications.
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.filesystem
- :classes-only:
- :nosignatures:
-"""
from contextlib import contextmanager
from os import makedirs
from pathlib import Path
diff --git a/requests_cache/backends/gridfs.py b/requests_cache/backends/gridfs.py
index 7e3051e..0e0e5ee 100644
--- a/requests_cache/backends/gridfs.py
+++ b/requests_cache/backends/gridfs.py
@@ -1,21 +1,3 @@
-"""
-.. image::
- ../_static/mongodb.png
-
-`GridFS <https://docs.mongodb.com/manual/core/gridfs/>`_ is a specification for storing large files
-in MongoDB.
-
-Use Cases
-^^^^^^^^^
-Use this backend if you are using MongoDB and expect to store responses **larger than 16MB**. See
-:py:mod:`~requests_cache.backends.mongodb` for more general info.
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.gridfs
- :classes-only:
- :nosignatures:
-"""
from logging import getLogger
from threading import RLock
diff --git a/requests_cache/backends/mongodb.py b/requests_cache/backends/mongodb.py
index 12d11d9..2e3b116 100644
--- a/requests_cache/backends/mongodb.py
+++ b/requests_cache/backends/mongodb.py
@@ -1,100 +1,3 @@
-"""
-.. image::
- ../_static/mongodb.png
-
-`MongoDB <https://www.mongodb.com>`_ is a NoSQL document database. It stores data in collections
-of documents, which are more flexible and less strictly structured than tables in a relational
-database.
-
-Use Cases
-^^^^^^^^^
-MongoDB scales well and is a good option for larger applications. For raw caching performance,
-it is not quite as fast as :py:mod:`~requests_cache.backends.redis`, but may be preferable if you
-already have an instance running, or if it has a specific feature you want to use. See below for
-some relevant examples.
-
-Viewing Responses
-^^^^^^^^^^^^^^^^^
-Unlike most of the other backends, response data can be easily viewed via the
-`MongoDB shell <https://www.mongodb.com/docs/mongodb-shell/#mongodb-binary-bin.mongosh>`_,
-`Compass <https://www.mongodb.com/products/compass>`_, or any other interface for MongoDB. This is
-possible because its internal document format (`BSON <https://www.mongodb.com/json-and-bson>`_)
-supports all the types needed to store a response as a plain document rather than a fully serialized
-blob.
-
-Here is an example response viewed in
-`MongoDB for VSCode <https://code.visualstudio.com/docs/azure/mongodb>`_:
-
-.. admonition:: Screenshot
- :class: toggle
-
- .. image:: ../_static/mongodb_vscode.png
-
-Expiration
-^^^^^^^^^^
-MongoDB `natively supports TTL <https://www.mongodb.com/docs/v4.0/core/index-ttl>`_, and can
-automatically remove expired responses from the cache.
-
-**Notes:**
-
-* TTL is set for a whole collection, and cannot be set on a per-document basis.
-* It will persist until explicitly removed or overwritten, or if the collection is deleted.
-* Expired items are
- `not guaranteed to be removed immediately <https://www.mongodb.com/docs/v4.0/core/index-ttl/#timing-of-the-delete-operation>`_.
- Typically it happens within 60 seconds.
-* If you want, you can rely entirely on MongoDB TTL instead of requests-cache
- :ref:`expiration settings <expiration>`.
-* Or you can set both values, to be certain that you don't get an expired response before MongoDB
- removes it.
-* If you intend to reuse expired responses, e.g. with :ref:`conditional-requests` or ``stale_if_error``,
- you can set TTL to a larger value than your session ``expire_after``, or disable it altogether.
-
-**Examples:**
-
-Create a TTL index:
-
->>> backend = MongoCache()
->>> backend.set_ttl(3600)
-
-Overwrite it with a new value:
-
->>> backend = MongoCache()
->>> backend.set_ttl(timedelta(days=1), overwrite=True)
-
-Remove the TTL index:
-
->>> backend = MongoCache()
->>> backend.set_ttl(None, overwrite=True)
-
-Use both MongoDB TTL and requests-cache expiration:
-
->>> ttl = timedelta(days=1)
->>> backend = MongoCache()
->>> backend.set_ttl(ttl)
->>> session = CachedSession(backend=backend, expire_after=ttl)
-
-**Recommended:** Set MongoDB TTL to a longer value than your :py:class:`.CachedSession` expiration.
-This allows expired responses to be eventually cleaned up, but still be reused for conditional
-requests for some period of time:
-
- >>> backend = MongoCache()
- >>> backend.set_ttl(timedelta(days=7))
- >>> session = CachedSession(backend=backend, expire_after=timedelta(days=1))
-
-Connection Options
-^^^^^^^^^^^^^^^^^^
-The MongoDB backend accepts any keyword arguments for :py:class:`pymongo.mongo_client.MongoClient`.
-These can be passed via :py:class:`.MongoCache`:
-
- >>> backend = MongoCache(host='192.168.1.63', port=27017)
- >>> session = CachedSession('http_cache', backend=backend)
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.mongodb
- :classes-only:
- :nosignatures:
-"""
from datetime import timedelta
from logging import getLogger
from typing import Iterable, Mapping, Optional, Union
diff --git a/requests_cache/backends/redis.py b/requests_cache/backends/redis.py
index a2430e2..a5c0675 100644
--- a/requests_cache/backends/redis.py
+++ b/requests_cache/backends/redis.py
@@ -1,64 +1,3 @@
-"""
-.. image::
- ../_static/redis.png
-
-`Redis <https://redis.io>`_ is an in-memory data store with on-disk persistence.
-
-Use Cases
-^^^^^^^^^
-Redis offers a high-performace cache that scales exceptionally well, making it an ideal choice for
-larger applications, especially those that make a large volume of concurrent requests.
-
-Persistence
-^^^^^^^^^^^
-Redis operates on data in memory, and by default also persists data to snapshots on disk. This is
-optimized for performance, with a minor risk of data loss, and is usually the best configuration
-for a cache. If you need different behavior, the frequency and type of persistence can be customized
-or disabled entirely. See `Redis Persistence <https://redis.io/topics/persistence>`_ for details.
-
-Expiration
-^^^^^^^^^^
-Redis natively supports TTL on a per-key basis, and can automatically remove expired responses from
-the cache. This will be set by by default, according to normal :ref:`expiration settings <expiration>`.
-
-If you intend to reuse expired responses, e.g. with :ref:`conditional-requests` or ``stale_if_error``,
-you can disable this behavior with the ``ttl`` argument:
-
- >>> backend = RedisCache(ttl=False)
-
-Connection Options
-^^^^^^^^^^^^^^^^^^
-The Redis backend accepts any keyword arguments for :py:class:`redis.client.Redis`. These can be
-passed via :py:class:`.RedisCache`:
-
- >>> backend = RedisCache(host='192.168.1.63', port=6379)
- >>> session = CachedSession('http_cache', backend=backend)
-
-Or you can pass an existing ``Redis`` object:
-
- >>> from redis import Redis
- >>> connection = Redis(host='192.168.1.63', port=6379)
- >>> backend = RedisCache(connection=connection))
- >>> session = CachedSession('http_cache', backend=backend)
-
-Redislite
-^^^^^^^^^
-If you can't easily set up your own Redis server, another option is
-`redislite <https://github.com/yahoo/redislite>`_. It contains its own lightweight, embedded Redis
-database, and can be used as a drop-in replacement for redis-py. Usage example:
-
- >>> from redislite import Redis
- >>> from requests_cache import CachedSession, RedisCache
- >>>
- >>> backend = RedisCache(connection=Redis())
- >>> session = CachedSession(backend=backend)
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.redis
- :classes-only:
- :nosignatures:
-"""
from logging import getLogger
from typing import Iterable
diff --git a/requests_cache/backends/sqlite.py b/requests_cache/backends/sqlite.py
index 7be83d4..baff2f8 100644
--- a/requests_cache/backends/sqlite.py
+++ b/requests_cache/backends/sqlite.py
@@ -1,90 +1,3 @@
-"""
-.. image::
- ../_static/sqlite.png
-
-`SQLite <https://www.sqlite.org/>`_ is a fast and lightweight SQL database engine that stores data
-either in memory or in a single file on disk.
-
-Use Cases
-^^^^^^^^^
-Despite its simplicity, SQLite is a powerful tool. For example, it's the primary storage system for
-a number of common applications including Dropbox, Firefox, and Chrome. It's well suited for
-caching, and requires no extra configuration or dependencies, which is why it's the default backend
-for requests-cache.
-
-Cache Files
-^^^^^^^^^^^
-* See :ref:`files` for general info on specifying cache paths
-* If you specify a name without an extension, the default extension ``.sqlite`` will be used
-
-In-Memory Caching
-~~~~~~~~~~~~~~~~~
-SQLite also supports `in-memory databases <https://www.sqlite.org/inmemorydb.html>`_.
-You can enable this (in "shared" memory mode) with the ``use_memory`` option:
-
- >>> session = CachedSession('http_cache', use_memory=True)
-
-Or specify a memory URI with additional options:
-
- >>> session = CachedSession(':file:memdb1?mode=memory')
-
-Or just ``:memory:``, if you are only using the cache from a single thread:
-
- >>> session = CachedSession(':memory:')
-
-Performance
-^^^^^^^^^^^
-When working with average-sized HTTP responses (< 1MB) and using a modern SSD for file storage, you
-can expect speeds of around:
-
-* Write: 2-8ms
-* Read: 0.2-0.6ms
-
-Of course, this will vary based on hardware specs, response size, and other factors.
-
-Concurrency
-^^^^^^^^^^^
-SQLite supports concurrent access, so it is safe to use from a multi-threaded and/or multi-process
-application. It supports unlimited concurrent reads. Writes, however, are queued and run in serial,
-so if you need to make large volumes of concurrent requests, you may want to consider a different
-backend that's specifically made for that kind of workload, like :py:class:`.RedisCache`.
-
-Hosting Services and Filesystem Compatibility
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-There are some caveats to using SQLite with some hosting services, based on what kind of storage is
-available:
-
-* NFS:
- * SQLite may be used on a NFS, but is usually only safe to use from a single process at a time.
- See the `SQLite FAQ <https://www.sqlite.org/faq.html#q5>`_ for details.
- * PythonAnywhere is one example of a host that uses NFS-backed storage. Using SQLite from a
- multiprocess application will likely result in ``sqlite3.OperationalError: database is locked``.
-* Ephemeral storage:
- * Heroku `explicitly disables SQLite <https://devcenter.heroku.com/articles/sqlite3>`_ on its dynos.
- * AWS `EC2 <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html>`_,
- `Lambda (depending on configuration) <https://aws.amazon.com/blogs/compute/choosing-between-aws-lambda-data-storage-options-in-web-apps/>`_,
- and some other AWS services use ephemeral storage that only persists for the lifetime of the
- instance. This is fine for short-term caching. For longer-term persistance, you can use an
- `attached EBS volume <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-attaching-volume.html>`_.
-
-Connection Options
-^^^^^^^^^^^^^^^^^^
-The SQLite backend accepts any keyword arguments for :py:func:`sqlite3.connect`. These can be passed
-via :py:class:`.CachedSession`:
-
- >>> session = CachedSession('http_cache', timeout=30)
-
-Or via :py:class:`.SQLiteCache`:
-
- >>> backend = SQLiteCache('http_cache', timeout=30)
- >>> session = CachedSession(backend=backend)
-
-API Reference
-^^^^^^^^^^^^^
-.. automodsumm:: requests_cache.backends.sqlite
- :classes-only:
- :nosignatures:
-"""
import sqlite3
import threading
from contextlib import contextmanager
diff --git a/requests_cache/session.py b/requests_cache/session.py
index 99d0509..a4c709f 100644
--- a/requests_cache/session.py
+++ b/requests_cache/session.py
@@ -1,18 +1,4 @@
-"""Main classes to add caching features to ``requests.Session``
-
-.. autosummary::
- :nosignatures:
-
- CachedSession
- CacheMixin
-
-.. Explicitly show inherited method docs on CachedSession instead of CachedMixin
-.. autoclass:: requests_cache.session.CachedSession
- :show-inheritance:
- :inherited-members:
-
-.. autoclass:: requests_cache.session.CacheMixin
-"""
+"""Main classes to add caching features to :py:class:`requests.Session`"""
from contextlib import contextmanager, nullcontext
from logging import getLogger
from threading import RLock