diff options
| author | Jordan Cook <jordan.cook@pioneer.com> | 2021-08-20 23:53:27 -0500 |
|---|---|---|
| committer | Jordan Cook <jordan.cook@pioneer.com> | 2021-08-21 14:13:28 -0500 |
| commit | ca2ed80089f9f3ec73aa4953d1ae95153229669f (patch) | |
| tree | 0399d474f71f1d9b67aeb4e753c3cfa10c28adbb | |
| parent | 901a2b997ecc15500c1874123a399a1464342e53 (diff) | |
| download | requests-cache-ca2ed80089f9f3ec73aa4953d1ae95153229669f.tar.gz | |
Add appdirs as a dependency, and add 'use_cache_dir' option to SQLite and Filesystem backends
| -rw-r--r-- | poetry.lock | 14 | ||||
| -rw-r--r-- | pyproject.toml | 1 | ||||
| -rw-r--r-- | requests_cache/backends/filesystem.py | 28 | ||||
| -rw-r--r-- | requests_cache/backends/sqlite.py | 54 | ||||
| -rw-r--r-- | tests/integration/test_filesystem.py | 7 | ||||
| -rw-r--r-- | tests/integration/test_sqlite.py | 8 |
6 files changed, 78 insertions, 34 deletions
diff --git a/poetry.lock b/poetry.lock index 3dfba55..29ca8c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7,6 +7,14 @@ optional = false python-versions = "*" [[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = "*" + +[[package]] name = "argcomplete" version = "1.12.3" description = "Bash tab completion for argparse" @@ -1181,13 +1189,17 @@ yaml = [] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "d73d313f19bec94728639a91b739ae34bd04d1f975716df3ae938db343328562" +content-hash = "01c4c5d4ac701b6499db508b2334b5d51fbdc773e56193ced940e73c989edc17" [metadata.files] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] argcomplete = [ {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, diff --git a/pyproject.toml b/pyproject.toml index a4291c3..c7b76ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ include = [ [tool.poetry.dependencies] python = "^3.7" +appdirs = "^1.4.4" attrs = "^21.2" cattrs = "^1.8" itsdangerous = "^2.0" diff --git a/requests_cache/backends/filesystem.py b/requests_cache/backends/filesystem.py index 63d83ba..a70a6f7 100644 --- a/requests_cache/backends/filesystem.py +++ b/requests_cache/backends/filesystem.py @@ -38,16 +38,15 @@ API Reference from contextlib import contextmanager from glob import glob from os import listdir, makedirs, unlink -from os.path import abspath, basename, dirname, expanduser, isabs, join, splitext +from os.path import basename, dirname, join, splitext from pathlib import Path from pickle import PickleError from shutil import rmtree -from tempfile import gettempdir from typing import List, Union from ..serializers import SERIALIZERS from . import BaseCache, BaseStorage -from .sqlite import SQLiteDict +from .sqlite import SQLiteDict, get_cache_path class FileCache(BaseCache): @@ -55,6 +54,7 @@ class FileCache(BaseCache): Args: cache_name: Base directory for cache files + 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. extension: Extension for cache files. If not specified, the serializer default extension @@ -75,9 +75,16 @@ class FileCache(BaseCache): class FileDict(BaseStorage): """A dictionary-like interface to files on the local filesystem""" - def __init__(self, cache_name, use_temp: bool = False, extension: str = None, **kwargs): + def __init__( + self, + cache_name, + use_temp: bool = False, + use_cache_dir: bool = False, + extension: str = None, + **kwargs, + ): super().__init__(**kwargs) - self.cache_dir = _get_cache_dir(cache_name, use_temp) + self.cache_dir = get_cache_path(cache_name, use_cache_dir=use_cache_dir, use_temp=use_temp) self.extension = extension if extension is not None else _get_default_ext(self.serializer) self.is_binary = False makedirs(self.cache_dir, exist_ok=True) @@ -135,17 +142,6 @@ class FileDict(BaseStorage): return glob(self._path('*')) -def _get_cache_dir(cache_dir: Union[Path, str], use_temp: bool) -> str: - # Save to a temp directory, if specified - if use_temp and not isabs(cache_dir): - cache_dir = join(gettempdir(), cache_dir, 'responses') - - # Expand relative and user paths (~/*), and make sure parent dirs exist - cache_dir = abspath(expanduser(str(cache_dir))) - makedirs(cache_dir, exist_ok=True) - return cache_dir - - def _get_default_ext(serializer) -> str: for k, v in SERIALIZERS.items(): if serializer is v: diff --git a/requests_cache/backends/sqlite.py b/requests_cache/backends/sqlite.py index a66e228..ed671f5 100644 --- a/requests_cache/backends/sqlite.py +++ b/requests_cache/backends/sqlite.py @@ -54,7 +54,7 @@ Special System Paths If you don't know exactly where you want to put your cache file, your **system's default temp directory** or **cache directory** is a good choice. -Use a temp directory with the ``use_temp`` option: +Use your system's default temp directory with the ``use_temp`` option: >>> session = CachedSession('http_cache', use_temp=True) >>> print(session.cache.db_path) @@ -64,12 +64,14 @@ Use a temp directory with the ``use_temp`` option: If the cache name is an absolute path, the ``use_temp`` option will be ignored. If it's a relative path, it will be relative to the temp directory. +Use your system's default cache directory with the ``use_cache_dir`` option: + If you want an easy cross-platform way to get the system cache directory, use the `appdirs <https://github.com/ActiveState/appdirs>`_ library: - >>> from appdirs import user_cache_dir - >>> db_path = join(user_cache_dir('requests_cache'), 'http_cache') - >>> session = CachedSession(db_path, use_temp=True) + >>> session = CachedSession('http_cache', use_cache_dir=True) + >>> print(session.cache.db_path) + '/home/user/.cache/http_cache.sqlite' In-Memory Caching ~~~~~~~~~~~~~~~~~ @@ -132,6 +134,8 @@ from pathlib import Path from tempfile import gettempdir from typing import Collection, Iterable, Iterator, List, Tuple, Type, Union +from appdirs import user_cache_dir + from . import BaseCache, BaseStorage, get_valid_kwargs MEMORY_URI = 'file::memory:?cache=shared' @@ -144,6 +148,7 @@ class SQLiteCache(BaseCache): Args: db_path: Database file path (expands user paths and creates parent dirs) + use_cache_dir: Store datebase in a user cache directory (e.g., `~/.cache/http_cache.sqlite`) use_temp: Store database in a temp directory (e.g., ``/tmp/http_cache.sqlite``) use_memory: Store database in memory instead of in a file fast_save: Significantly increases cache write performance, but with the possibility of data @@ -191,13 +196,16 @@ class SQLiteDict(BaseStorage): db_path, table_name='http_cache', fast_save=False, - use_temp: bool = False, + use_cache_dir: bool = False, use_memory: bool = False, + use_temp: bool = False, **kwargs, ): super().__init__(**kwargs) self.connection_kwargs = get_valid_kwargs(sqlite_template, kwargs) - self.db_path = _get_db_path(db_path, use_temp, use_memory) + self.db_path = _get_sqlite_cache_path( + db_path, use_cache_dir=use_cache_dir, use_temp=use_temp, use_memory=use_memory + ) self.fast_save = fast_save self.table_name = table_name @@ -336,27 +344,39 @@ def _format_sequence(values: Collection) -> Tuple[str, List]: return ','.join(['?'] * len(values)), list(values) -def _get_db_path(db_path: Union[Path, str], use_temp: bool, use_memory: bool) -> str: - """Get resolved path for database file""" - db_path = str(db_path) +def _get_sqlite_cache_path( + db_path: Union[Path, str], use_cache_dir: bool, use_temp: bool, use_memory: bool = False +) -> str: + """Get a resolved path for a SQLite database file (or memory URI(""" # Use an in-memory database, if specified + db_path = str(db_path) if use_memory: return MEMORY_URI elif ':memory:' in db_path or 'mode=memory' in db_path: return db_path - # Save to a temp directory, if specified - if use_temp and not isabs(db_path): - db_path = join(gettempdir(), db_path) - - # Expand relative and user paths (~/*), and add file extension if not specified - db_path = abspath(expanduser(db_path)) + # Add file extension if not specified if '.' not in basename(db_path): db_path += '.sqlite' + return get_cache_path(db_path, use_cache_dir, use_temp) + + +def get_cache_path( + db_path: Union[Path, str], use_cache_dir: bool = False, use_temp: bool = False +) -> str: + """Get a resolved cache path""" + db_path = str(db_path) + + # Save to platform-specific temp or user cache directory, if specified + if use_cache_dir and not isabs(db_path): + db_path = join(user_cache_dir(), db_path) + elif use_temp and not isabs(db_path): + db_path = join(gettempdir(), db_path) - # Make sure parent dirs exist + # Expand relative and user paths (~/*), and make sure parent dirs exist + db_path = abspath(expanduser(db_path)) makedirs(dirname(db_path), exist_ok=True) - return db_path + return str(db_path) def sqlite_template( diff --git a/tests/integration/test_filesystem.py b/tests/integration/test_filesystem.py index c1061c1..3a60190 100644 --- a/tests/integration/test_filesystem.py +++ b/tests/integration/test_filesystem.py @@ -4,6 +4,7 @@ from shutil import rmtree from tempfile import gettempdir import pytest +from appdirs import user_cache_dir from requests_cache.backends import FileCache, FileDict from requests_cache.serializers import SERIALIZERS, SerializerPipeline @@ -24,6 +25,12 @@ class TestFileDict(BaseStorageTest): cache.clear() return cache + def test_use_cache_dir(self): + relative_path = self.storage_class(CACHE_NAME).cache_dir + cache_dir_path = self.storage_class(CACHE_NAME, use_cache_dir=True).cache_dir + assert not relative_path.startswith(user_cache_dir()) + assert cache_dir_path.startswith(user_cache_dir()) + def test_use_temp(self): relative_path = self.storage_class(CACHE_NAME).cache_dir temp_path = self.storage_class(CACHE_NAME, use_temp=True).cache_dir diff --git a/tests/integration/test_sqlite.py b/tests/integration/test_sqlite.py index c0f83e3..c7107e8 100644 --- a/tests/integration/test_sqlite.py +++ b/tests/integration/test_sqlite.py @@ -3,6 +3,8 @@ from tempfile import gettempdir from threading import Thread from unittest.mock import patch +from appdirs import user_cache_dir + from requests_cache.backends.base import BaseCache from requests_cache.backends.sqlite import MEMORY_URI, SQLiteCache, SQLiteDict, SQLitePickleDict from tests.integration.base_cache_test import BaseCacheTest @@ -19,6 +21,12 @@ class SQLiteTestCase(BaseStorageTest): except Exception: pass + def test_use_cache_dir(self): + relative_path = self.storage_class(CACHE_NAME).db_path + cache_dir_path = self.storage_class(CACHE_NAME, use_cache_dir=True).db_path + assert not relative_path.startswith(user_cache_dir()) + assert cache_dir_path.startswith(user_cache_dir()) + def test_use_temp(self): relative_path = self.storage_class(CACHE_NAME).db_path temp_path = self.storage_class(CACHE_NAME, use_temp=True).db_path |
