diff options
| author | Jordan Cook <jordan.cook@pioneer.com> | 2022-04-11 21:03:50 -0500 |
|---|---|---|
| committer | Jordan Cook <jordan.cook@pioneer.com> | 2022-04-16 21:08:42 -0500 |
| commit | d6ee9143965d53dae44ca3a98802b2cc7ad6eeb7 (patch) | |
| tree | 96e38c3f7289a0ff3a46c23df3f1e5b3b7c1a940 /requests_cache | |
| parent | 166f5690fb8d5b067f839fa8ffb9421cf1b8a7e7 (diff) | |
| download | requests-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.py | 34 | ||||
| -rw-r--r-- | requests_cache/backends/dynamodb.py | 51 | ||||
| -rw-r--r-- | requests_cache/backends/filesystem.py | 56 | ||||
| -rw-r--r-- | requests_cache/backends/gridfs.py | 18 | ||||
| -rw-r--r-- | requests_cache/backends/mongodb.py | 97 | ||||
| -rw-r--r-- | requests_cache/backends/redis.py | 61 | ||||
| -rw-r--r-- | requests_cache/backends/sqlite.py | 87 | ||||
| -rw-r--r-- | requests_cache/session.py | 16 |
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 |
