diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ea17bdb..508c221 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -147,18 +147,53 @@ jobs:
- uses: actions/setup-python@v7
with:
python-version: '3.14'
- - name: Install diskcache
+ - name: Install diskcache-community
run: pip install .
- - name: Import diskcache without Django installed
+ - name: Import diskcache_community without Django installed
working-directory: ${{ runner.temp }}
run: |
python -c "import importlib.util, sys; sys.exit(importlib.util.find_spec('django') is not None)"
- python -W error -c "import diskcache; assert 'DjangoCache' not in diskcache.__all__, diskcache.__all__; print('diskcache', diskcache.__version__, 'imported without Django')"
+ python -W error -c "import diskcache_community as dc; assert 'DjangoCache' not in dc.__all__, dc.__all__; print('diskcache_community', dc.__version__, 'imported without Django')"
+
+ coexist:
+ name: next to upstream diskcache 5.6.3
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ env:
+ DJANGO_SETTINGS_MODULE: tests.settings
+ PYTHONPATH: ${{ github.workspace }}
+ steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-python@v7
+ with:
+ python-version: '3.12'
+ - name: Install diskcache-community and upstream diskcache 5.6.3 together
+ run: pip install . "diskcache==5.6.3" "django==5.2.*" pytest pytest-cov pytest-django pytest-xdist
+ - name: Create a separate environment with only diskcache 5.6.3
+ run: |
+ python -m venv "$RUNNER_TEMP/venv-563"
+ "$RUNNER_TEMP/venv-563/bin/pip" install "diskcache==5.6.3"
+ - name: Run the coexistence and golden tests in both directions
+ env:
+ DISKCACHE_GOLDEN_563_PYTHON: ${{ runner.temp }}/venv-563/bin/python
+ run: pytest --no-cov -p no:cacheprovider tests/test_init.py tests/test_golden.py
+ - name: Uninstall either distribution and check the other still imports
+ working-directory: ${{ runner.temp }}
+ env:
+ PYTHONPATH: ''
+ run: |
+ pip check
+ pip uninstall -y diskcache
+ python -c "import diskcache_community as dc; print('diskcache_community', dc.__version__)"
+ pip install "diskcache==5.6.3"
+ pip uninstall -y diskcache-community
+ python -c "import diskcache; assert diskcache.__version__ == '5.6.3'; print('diskcache', diskcache.__version__)"
+ python -c "import importlib.util, sys; sys.exit(importlib.util.find_spec('diskcache_community') is not None)"
ci-ok:
name: ci-ok
if: always()
- needs: [lint, test, django, sqlite-dqs, no-django]
+ needs: [lint, test, django, sqlite-dqs, no-django, coexist]
runs-on: ubuntu-latest
steps:
- name: Fail if any job did not succeed
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4402c8b..77165d4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- - name: Check that the tag matches diskcache.__version__
+ - name: Check that the tag matches diskcache_community.__version__
if: github.ref_type == 'tag'
run: |
python - <<'PY'
@@ -27,7 +27,7 @@ jobs:
import re
import sys
- text = open('diskcache/__init__.py', encoding='utf-8').read()
+ text = open('diskcache_community/__init__.py', encoding='utf-8').read()
version = re.search(r"^__version__ = '([^']+)'", text, re.M).group(1)
tag = os.environ['GITHUB_REF_NAME']
if tag != 'v' + version:
@@ -45,7 +45,7 @@ jobs:
run: |
python -m venv check
check/bin/pip install -q "${{ github.workspace }}"/dist/*.whl
- check/bin/python -W error -c "import diskcache; print(diskcache.__title__, diskcache.__version__)"
+ check/bin/python -W error -c "import diskcache_community as diskcache; print(diskcache.__title__, diskcache.__version__)"
- uses: actions/upload-artifact@v7
with:
name: dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09a1ccb..ad7183e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 20
### Packaging
+- **Breaking:** the package is imported as `diskcache_community` instead of `diskcache`, so it installs next to the upstream `diskcache` distribution without overwriting it, and a project can move to the fork while other dependencies still require `diskcache`. The public API, the `cache.db` schema, the file layout and the serialization are unchanged, so cache directories are shared with 5.6.3 in both directions. Django settings name `diskcache_community.DjangoCache` and `diskcache_community.SafeDisk`. The import-time warning about an upstream `diskcache` installed in the same environment is gone, and a CI job installs both distributions together, runs the golden tests in both directions and checks that uninstalling either leaves the other working (PR #22).
- `LICENSE` now holds the full Apache License 2.0 text instead of the short boilerplate notice, so GitHub and package indexes detect the license; the copyright and fork notice stay in `NOTICE` (PR #21).
## 5.7.0 (2026-10-11)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 618dbaf..e6c2134 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -39,9 +39,9 @@ Fixes imported from upstream pull requests keep their original commits and autho
## Compatibility contract
-This fork publishes the same library under a new distribution name. The following stay as upstream left them, and a change to any of them is a compatibility break that needs a major version:
+This fork publishes the same library under a new distribution name, `diskcache-community`, and a new import name, `diskcache_community`, so it installs next to the upstream `diskcache` package. The following stay as upstream left them, and a change to any of them is a compatibility break that needs a major version:
-- the import name, `import diskcache`, and the public API exported by `diskcache/__init__.py`;
+- the import name, `import diskcache_community` (it replaced `import diskcache` in 5.8.0, before any dependent used the fork), and the public API exported by `diskcache_community/__init__.py`;
- the `cache.db` schema: the `Settings` and `Cache` tables, the value modes 1 to 4, and the index and trigger names;
- the directory layout: `
/ab/cd/<28 hex>.val` for values and `%03d` for `FanoutCache` shards;
- the `RAW`, `BINARY`, `TEXT` and `PICKLE` serialization and the bytes `JSONDisk` writes;
diff --git a/README.md b/README.md
index 61542d6..6e2ecbd 100644
--- a/README.md
+++ b/README.md
@@ -29,9 +29,10 @@
> [!IMPORTANT]
> This is a community fork of [python-diskcache](https://github.com/grantjenks/python-diskcache),
> whose last release was 5.6.3 in August 2023. It is published on PyPI as
-> `diskcache-community` and still imported as `diskcache`. It carries the
-> fix path for [CVE-2025-69872](https://github.com/advisories/GHSA-w8v5-vhqr-4h9v)
-> and stays compatible with existing 5.6.3 cache directories.
+> `diskcache-community` and imported as `diskcache_community`, so it installs
+> next to the upstream `diskcache` package. It carries the fix path for
+> [CVE-2025-69872](https://github.com/advisories/GHSA-w8v5-vhqr-4h9v) and
+> reads and writes the same cache directories as 5.6.3.
> See [Why this fork](#why-this-fork) and [Migrating from diskcache](#migrating-from-diskcache).
## Install
@@ -40,14 +41,14 @@
pip install diskcache-community
```
-If the upstream `diskcache` package is already installed, remove it first.
-Both distributions write to the same `diskcache/` package directory and
-cannot coexist; see [Migrating from diskcache](#migrating-from-diskcache).
+The package is imported as `diskcache_community` and never touches the
+upstream `diskcache` package, so both can be installed in the same environment;
+see [Migrating from diskcache](#migrating-from-diskcache).
## Usage
```python
-from diskcache import Cache
+from diskcache_community import Cache
cache = Cache('cache') # the directory is created with mode 0700
cache.set('answer', 42, expire=3600, tag='math')
@@ -69,7 +70,7 @@ Shard across several SQLite files for higher write throughput, and plug the
same cache into Django:
```python
-from diskcache import FanoutCache
+from diskcache_community import FanoutCache
cache = FanoutCache('cache', shards=8, timeout=1)
```
@@ -78,10 +79,10 @@ cache = FanoutCache('cache', shards=8, timeout=1)
# settings.py
CACHES = {
'default': {
- 'BACKEND': 'diskcache.DjangoCache',
+ 'BACKEND': 'diskcache_community.DjangoCache',
'LOCATION': '/var/tmp/django_cache',
'SHARDS': 8,
- 'DISK': 'diskcache.SafeDisk', # restricted unpickling, see below
+ 'DISK': 'diskcache_community.SafeDisk', # restricted unpickling, see below
}
}
```
@@ -91,7 +92,7 @@ only a fixed set of built-in types and keeps file reads inside the cache
directory:
```python
-from diskcache import Cache, SafeDisk
+from diskcache_community import Cache, SafeDisk
cache = Cache('cache', disk=SafeDisk)
```
@@ -120,13 +121,14 @@ anyone who can write into the cache directory can run code in the process
that reads from it. The package still works, but scanners flag it and there
is no fixed upstream version.
-This fork publishes the same library as `diskcache-community` and fixes it in
-two steps. Version 5.7.0 keeps the `cache.db` schema, the file layout, the
-serialization modes and the memoize key format unchanged, so a 5.6.3 process
-opens a directory written by this fork and the reverse. It adds the opt-in
-`SafeDisk`, creates cache directories with mode `0700`, opens read-only
-directories read-only, closes the per-thread SQLite connections it used to
-leak, and survives `fork`. Version 6.0.0 will make safe reading the default.
+This fork publishes the same library as `diskcache-community`. It keeps the
+`cache.db` schema, the file layout, the serialization modes and the memoize
+key format unchanged, so a 5.6.3 process opens a directory written by this
+fork and the reverse. It adds the opt-in `SafeDisk`, creates cache directories
+with mode `0700`, opens read-only directories read-only, closes the per-thread
+SQLite connections it used to leak, and survives `fork`. The package is
+imported as `diskcache_community`, so a project can move to it while other
+dependencies in the same environment still use the upstream `diskcache`.
The [changelog](CHANGELOG.md) lists every change and
[SECURITY.md](SECURITY.md) describes what is and is not fixed.
@@ -135,25 +137,32 @@ fork keeps his license, copyright and author credit; see [NOTICE](NOTICE).
## Migrating from diskcache
-The import name and the public API are unchanged, so no code changes are
-needed. Only the distribution changes:
+The public API and the files on disk are unchanged; only the import name
+differs. Install the fork and change the imports:
```sh
-pip uninstall diskcache
pip install diskcache-community
```
-Never keep both installed. Uninstalling either one deletes the shared
-package directory, and the fork warns at import time when both are present.
-If `diskcache` arrives through a dependency, exclude it. With uv:
-
-```toml
-[tool.uv]
-exclude-dependencies = ["diskcache"]
+```python
+import diskcache_community as diskcache # was: import diskcache
+from diskcache_community import Cache # was: from diskcache import Cache
```
-With pip, install the dependent with `--no-deps` or exclude `diskcache` in a
-constraints file.
+In Django settings, use `'diskcache_community.DjangoCache'` and
+`'diskcache_community.SafeDisk'`. Existing cache directories open as they
+are, and a 5.6.3 process still opens directories written by the fork.
+
+The upstream `diskcache` package can stay installed, for example when another
+dependency requires it. Each library uses the package it imports. A library
+that wants to support both can import whichever is present:
+
+```python
+try:
+ import diskcache_community as diskcache
+except ImportError:
+ import diskcache
+```
## Documentation
diff --git a/SECURITY.md b/SECURITY.md
index d705d1c..8c7cd09 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -17,9 +17,9 @@ You will hear back within a week. A confirmed problem is fixed in the next relea
Where this fork stands:
- 5.7.0, the first release of this fork, reads pickles with the default `Disk` exactly as 5.6.3 does, so it opens every 5.6.3 cache. It adds an opt-in `SafeDisk` that unpickles only an allowlist of standard-library types, rejects anything outside it at write time and confines value paths to the cache directory; `Cache`, `FanoutCache`, `Deque` and `Index` take it through `disk=SafeDisk` and the Django backend through the `DISK` option. New cache directories are created with mode `0700`.
-- 6.0.0 makes safe reading the default and turns the trusted-pickle behaviour into an explicit choice, with a migration guide.
+- The default stays as it is. Existing caches hold objects outside any allowlist, such as instances of your own classes, Django responses and the keys of `memoize(typed=True)`, so `SafeDisk` remains an explicit choice.
-Until you run a version where safe reading is the default or you have opted into it, treat the cache directory as trusted input: keep it on local storage that only the user running the process can write to, and do not share it with other accounts.
+Unless you have opted into `SafeDisk`, treat the cache directory as trusted input: keep it on local storage that only the user running the process can write to, and do not share it with other accounts.
A note on scanners: vulnerability scanners match on the distribution name, so `pip-audit`, Dependabot and similar tools report nothing for `diskcache-community` even though the default `Disk` in 5.7.0 behaves as it did in 5.6.3. A clean scan is not evidence that this issue is fixed. Use the list above instead, and the release notes of the version you run.
diff --git a/diskcache/__init__.py b/diskcache_community/__init__.py
similarity index 53%
rename from diskcache/__init__.py
rename to diskcache_community/__init__.py
index 8b6d11c..a9d7c7d 100644
--- a/diskcache/__init__.py
+++ b/diskcache_community/__init__.py
@@ -5,9 +5,6 @@
The :doc:`tutorial` provides a helpful walkthrough of most methods.
"""
-import os
-import warnings
-
from .core import (
DEFAULT_SETTINGS,
ENOVAL,
@@ -68,43 +65,6 @@
pass
-def _warn_if_upstream_installed(site_dir=None):
- """Warn when the upstream ``diskcache`` distribution is installed too.
-
- Both distributions install into the same ``diskcache`` package directory,
- so the files on disk belong to whichever was installed last and
- uninstalling either one removes them for both. Return True when the
- warning was issued.
-
- """
- if site_dir is None:
- site_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
- try:
- names = os.listdir(site_dir)
- except OSError:
- return False
-
- for name in names:
- if name.startswith('diskcache-') and name.endswith(
- ('.dist-info', '.egg-info')
- ):
- warnings.warn(
- 'the diskcache distribution is installed next to'
- ' diskcache-community; both provide the diskcache package'
- ' directory, so uninstall diskcache (pip uninstall diskcache)'
- ' and then reinstall diskcache-community',
- RuntimeWarning,
- stacklevel=2,
- )
- return True
-
- return False
-
-
-_warn_if_upstream_installed()
-
-
__title__ = 'diskcache-community'
__version__ = '5.7.0'
__build__ = 0x050700
diff --git a/diskcache/cli.py b/diskcache_community/cli.py
similarity index 100%
rename from diskcache/cli.py
rename to diskcache_community/cli.py
diff --git a/diskcache/core.py b/diskcache_community/core.py
similarity index 99%
rename from diskcache/core.py
rename to diskcache_community/core.py
index df90353..47d54fa 100644
--- a/diskcache/core.py
+++ b/diskcache_community/core.py
@@ -390,7 +390,7 @@ def filename(self, key=UNKNOWN, value=UNKNOWN):
The default implementation ignores the `key` and `value` parameters.
In some scenarios, for example :meth:`Cache.push
- `, the `key` or `value` may not be known when the
+ `, the `key` or `value` may not be known when the
item is stored in the cache.
:param key: key for item (default UNKNOWN)
@@ -1081,7 +1081,9 @@ def __init__(
try:
assert issubclass(disk, Disk)
except (TypeError, AssertionError):
- raise ValueError('disk must subclass diskcache.Disk') from None
+ raise ValueError(
+ 'disk must subclass diskcache_community.Disk'
+ ) from None
if directory is None:
directory = tempfile.mkdtemp(prefix='diskcache-')
@@ -2645,7 +2647,7 @@ def memoize(
attribute. This is useful for introspection, for bypassing the cache,
or for rewrapping the function with a different cache.
- >>> from diskcache import Cache
+ >>> from diskcache_community import Cache
>>> cache = Cache()
>>> @cache.memoize(expire=1, tag='fib')
... def fibonacci(number):
diff --git a/diskcache/djangocache.py b/diskcache_community/djangocache.py
similarity index 98%
rename from diskcache/djangocache.py
rename to diskcache_community/djangocache.py
index 857bfbe..6364476 100644
--- a/diskcache/djangocache.py
+++ b/diskcache_community/djangocache.py
@@ -23,9 +23,9 @@ def __init__(self, directory, params):
The `params` mapping holds the keys of the Django ``CACHES`` setting.
``SHARDS`` and ``DATABASE_TIMEOUT`` configure the underlying
- :class:`FanoutCache `, ``DISK`` selects the
- :class:`Disk ` type or subclass used for serialization
- as a class or a dotted import path such as ``'diskcache.SafeDisk'``,
+ :class:`FanoutCache `, ``DISK`` selects the
+ :class:`Disk ` type or subclass used for serialization
+ as a class or a dotted import path such as ``'diskcache_community.SafeDisk'``,
and ``OPTIONS`` passes further settings through.
:param str directory: cache directory
diff --git a/diskcache/fanout.py b/diskcache_community/fanout.py
similarity index 100%
rename from diskcache/fanout.py
rename to diskcache_community/fanout.py
diff --git a/diskcache/persistent.py b/diskcache_community/persistent.py
similarity index 99%
rename from diskcache/persistent.py
rename to diskcache_community/persistent.py
index 9591be9..eda2740 100644
--- a/diskcache/persistent.py
+++ b/diskcache_community/persistent.py
@@ -85,7 +85,7 @@ def __init__(self, iterable=(), directory=None, maxlen=None, disk=Disk):
:param directory: deque directory (default None)
:param maxlen: max length (default None, no max)
:param disk: `Disk` type or subclass for serialization (default
- :class:`Disk `)
+ :class:`Disk `)
"""
self._cache = Cache(directory, disk=disk, eviction_policy='none')
@@ -659,7 +659,7 @@ def transact(self):
Transactions may be nested and may not be shared between threads.
- >>> from diskcache import Deque
+ >>> from diskcache_community import Deque
>>> deque = Deque()
>>> deque += range(5)
>>> with deque.transact(): # Atomically rotate elements.
@@ -704,7 +704,7 @@ def __init__(self, *args, disk=Disk, **kwargs):
Optional first argument may be string specifying directory where items
are stored. When None or not given, temporary directory is created.
- The keyword argument `disk` selects the :class:`Disk `
+ The keyword argument `disk` selects the :class:`Disk `
type or subclass used for serialization. Every other keyword argument
is stored as an item, so an item named ``disk`` has to be set after
initialization.
@@ -1177,7 +1177,7 @@ def memoize(self, name=None, typed=False, ignore=()):
attribute. This is useful for introspection, for bypassing the cache,
or for rewrapping the function with a different cache.
- >>> from diskcache import Index
+ >>> from diskcache_community import Index
>>> mapping = Index()
>>> @mapping.memoize()
... def fibonacci(number):
@@ -1227,7 +1227,7 @@ def transact(self):
Transactions may be nested and may not be shared between threads.
- >>> from diskcache import Index
+ >>> from diskcache_community import Index
>>> mapping = Index()
>>> with mapping.transact(): # Atomically increment two keys.
... mapping['total'] = mapping.get('total', 0) + 123.4
diff --git a/diskcache/recipes.py b/diskcache_community/recipes.py
similarity index 97%
rename from diskcache/recipes.py
rename to diskcache_community/recipes.py
index 48bae68..d1ce965 100644
--- a/diskcache/recipes.py
+++ b/diskcache_community/recipes.py
@@ -20,7 +20,7 @@ class Averager:
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
- >>> import diskcache
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.FanoutCache()
>>> ave = Averager(cache, 'latency')
>>> ave.add(0.080)
@@ -71,7 +71,7 @@ class Lock:
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
- >>> import diskcache
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.Cache()
>>> lock = Lock(cache, 'report-123')
>>> lock.acquire()
@@ -122,7 +122,7 @@ class RLock:
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
- >>> import diskcache
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.Cache()
>>> rlock = RLock(cache, 'user-123')
>>> rlock.acquire()
@@ -193,7 +193,7 @@ class BoundedSemaphore:
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
- >>> import diskcache
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.Cache()
>>> semaphore = BoundedSemaphore(cache, 'max-cons', value=2)
>>> semaphore.acquire()
@@ -266,7 +266,9 @@ def throttle(
Assumes keys will not be evicted. Set the eviction policy to 'none' on the
cache to guarantee the keys are not evicted.
- >>> import diskcache, time
+ >>> import time
+
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.Cache()
>>> count = 0
>>> @throttle(cache, 2, 1) # 2 calls per 1 second
@@ -323,7 +325,9 @@ def barrier(cache, lock_factory, name=None, expire=None, tag=None):
Assumes keys will not be evicted. Set the eviction policy to 'none' on the
cache to guarantee the keys are not evicted.
- >>> import diskcache, time
+ >>> import time
+
+ >>> import diskcache_community as diskcache
>>> cache = diskcache.Cache()
>>> @barrier(cache, Lock)
... def work(num):
@@ -383,7 +387,7 @@ def memoize_stampede(
attribute. This is useful for introspection, for bypassing the cache, or
for rewrapping the function with a different cache.
- >>> from diskcache import Cache
+ >>> from diskcache_community import Cache
>>> cache = Cache()
>>> @memoize_stampede(cache, expire=1)
... def fib(number):
diff --git a/docs/api.rst b/docs/api.rst
index a1fb6ed..eaf70c7 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -13,7 +13,7 @@ Cache
Read the :ref:`Cache tutorial ` for example usage.
-.. autoclass:: diskcache.Cache
+.. autoclass:: diskcache_community.Cache
:members:
:special-members:
:exclude-members: __weakref__
@@ -23,7 +23,7 @@ FanoutCache
Read the :ref:`FanoutCache tutorial ` for example usage.
-.. autoclass:: diskcache.FanoutCache
+.. autoclass:: diskcache_community.FanoutCache
:members:
:special-members:
:exclude-members: __weakref__
@@ -33,14 +33,14 @@ DjangoCache
Read the :ref:`DjangoCache tutorial ` for example usage.
-.. autoclass:: diskcache.DjangoCache
+.. autoclass:: diskcache_community.DjangoCache
:members:
:special-members:
Deque
-----
-.. autoclass:: diskcache.Deque
+.. autoclass:: diskcache_community.Deque
:members:
:special-members:
:exclude-members: __weakref__
@@ -48,7 +48,7 @@ Deque
Index
-----
-.. autoclass:: diskcache.Index
+.. autoclass:: diskcache_community.Index
:members:
:special-members:
:exclude-members: __weakref__
@@ -56,23 +56,23 @@ Index
Recipes
-------
-.. autoclass:: diskcache.Averager
+.. autoclass:: diskcache_community.Averager
:members:
-.. autoclass:: diskcache.Lock
+.. autoclass:: diskcache_community.Lock
:members:
-.. autoclass:: diskcache.RLock
+.. autoclass:: diskcache_community.RLock
:members:
-.. autoclass:: diskcache.BoundedSemaphore
+.. autoclass:: diskcache_community.BoundedSemaphore
:members:
-.. autodecorator:: diskcache.throttle
+.. autodecorator:: diskcache_community.throttle
-.. autodecorator:: diskcache.barrier
+.. autodecorator:: diskcache_community.barrier
-.. autodecorator:: diskcache.memoize_stampede
+.. autodecorator:: diskcache_community.memoize_stampede
.. _constants:
@@ -81,7 +81,7 @@ Constants
Read the :ref:`Settings tutorial ` for details.
-.. data:: diskcache.DEFAULT_SETTINGS
+.. data:: diskcache_community.DEFAULT_SETTINGS
* `statistics` (int) default 0 - disabled when 0, enabled when 1.
* `tag_index` (int) default 0 - disabled when 0, enabled when 1.
@@ -104,7 +104,7 @@ Read the :ref:`Settings tutorial ` for details.
* `disk_pickle_protocol` (int) default highest Pickle protocol - the Pickle
protocol to use for data types that are not natively supported.
-.. data:: diskcache.EVICTION_POLICY
+.. data:: diskcache_community.EVICTION_POLICY
* `least-recently-stored` (default) - evict least recently stored keys first.
* `least-recently-used` - evict least recently used keys first.
@@ -116,7 +116,7 @@ Disk
Read the :ref:`Disk tutorial ` for details.
-.. autoclass:: diskcache.Disk
+.. autoclass:: diskcache_community.Disk
:members:
:special-members:
:exclude-members: __weakref__
@@ -126,7 +126,7 @@ JSONDisk
Read the :ref:`Disk tutorial ` for details.
-.. autoclass:: diskcache.JSONDisk
+.. autoclass:: diskcache_community.JSONDisk
:members:
:special-members:
:exclude-members: __weakref__
@@ -136,29 +136,29 @@ SafeDisk
Read the :ref:`SafeDisk tutorial ` for details.
-.. autoclass:: diskcache.SafeDisk
+.. autoclass:: diskcache_community.SafeDisk
:members:
:special-members:
:exclude-members: __weakref__
-.. data:: diskcache.core.SAFE_PICKLE_NAMES
+.. data:: diskcache_community.core.SAFE_PICKLE_NAMES
Mapping of module name to the names that :class:`SafeDisk
- ` lets pickle resolve while reading.
+ ` lets pickle resolve while reading.
-.. data:: diskcache.core.SAFE_TYPES
+.. data:: diskcache_community.core.SAFE_TYPES
- The Python types that :class:`SafeDisk ` accepts
+ The Python types that :class:`SafeDisk ` accepts
for writing; exactly the types the names above read back.
Timeout
-------
-.. autoexception:: diskcache.Timeout
+.. autoexception:: diskcache_community.Timeout
ReadOnlyError
-------------
Read the :ref:`read-only tutorial ` for details.
-.. autoexception:: diskcache.ReadOnlyError
+.. autoexception:: diskcache_community.ReadOnlyError
diff --git a/docs/cache-benchmarks.rst b/docs/cache-benchmarks.rst
index 5b125e8..f55f5b9 100644
--- a/docs/cache-benchmarks.rst
+++ b/docs/cache-benchmarks.rst
@@ -102,8 +102,8 @@ Timings for diskcache.FanoutCache(shards=8, timeout=0.010)
========= ========= ========= ========= ========= ========= ========= =========
Notice the low overhead of the :class:`FanoutCache
-`. Increasing the number of shards from four to eight
-has a negligible impact on performance.
+`. Increasing the number of shards from four
+to eight has a negligible impact on performance.
========= ========= ========= ========= ========= ========= ========= =========
Timings for pylibmc.Client
@@ -163,7 +163,8 @@ Delete
.. image:: _static/core-p8-delete.png
Again deletes require writes to disk. Only the :class:`FanoutCache
-` performs well with one shard allocated per worker.
+` performs well with one shard allocated per
+worker.
Timing Data
...........
@@ -184,7 +185,7 @@ Timings for diskcache.Cache
Notice the unacceptably high maximum store and delete latency. Without
sharding, cache writers block each other. By default :class:`Cache
-` objects raise a timeout error after sixty seconds.
+` objects raise a timeout error after sixty seconds.
========= ========= ========= ========= ========= ========= ========= =========
Timings for diskcache.FanoutCache(shards=4, timeout=1.0)
@@ -197,7 +198,7 @@ Timings for diskcache.FanoutCache(shards=4, timeout=1.0)
Total 791992 72.634s
========= ========= ========= ========= ========= ========= ========= =========
-Here :class:`FanoutCache ` uses four shards to
+Here :class:`FanoutCache ` uses four shards to
distribute writes. That reduces the maximum latency by a factor of ten. Note
the miss rate is variable due to the interleaved operations of concurrent
workers.
@@ -232,9 +233,9 @@ Timings for pylibmc.Client
Memcached performance is low latency and stable even under heavy load. Notice
that cache gets are three times slower in total as compared with
-:class:`FanoutCache `. The superior performance of get
-operations put the overall performance of :doc:`DiskCache ` ahead of
-Memcached.
+:class:`FanoutCache `. The superior
+performance of get operations put the overall performance of :doc:`DiskCache
+` ahead of Memcached.
========= ========= ========= ========= ========= ========= ========= =========
Timings for redis.StrictRedis
diff --git a/docs/case-study-landing-page-caching.rst b/docs/case-study-landing-page-caching.rst
index 677186e..27216fb 100644
--- a/docs/case-study-landing-page-caching.rst
+++ b/docs/case-study-landing-page-caching.rst
@@ -30,7 +30,7 @@ regenerates the page with a consistently slow latency.
.. code-block:: python
:emphasize-lines: 5
- import diskcache as dc
+ import diskcache_community as dc
cache = dc.Cache()
diff --git a/docs/case-study-web-crawler.rst b/docs/case-study-web-crawler.rst
index c37e2a5..e5c5a01 100644
--- a/docs/case-study-web-crawler.rst
+++ b/docs/case-study-web-crawler.rst
@@ -73,12 +73,12 @@ dictionary.
The results of our current code are ephemeral. All results are lost once the
program terminates. To make the results persistent, we can use :doc:`DiskCache
-` data structures and store the results in the local file
-system. :doc:`DiskCache ` provides both :class:`Deque `
-and :class:`Index ` data structures which can replace our urls
-and results variables.
+` data structures and store the results in the local file system.
+:doc:`DiskCache ` provides both :class:`Deque
+` and :class:`Index `
+data structures which can replace our urls and results variables.
- >>> from diskcache import Deque, Index
+ >>> from diskcache_community import Deque, Index
>>> def crawl():
... urls = Deque([0], 'data/urls')
... results = Index('data/results')
diff --git a/docs/conf.py b/docs/conf.py
index a789a68..b0901be 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -14,7 +14,7 @@
import sys
sys.path.insert(0, os.path.abspath('..'))
-import diskcache
+import diskcache_community as diskcache
# -- Project information -----------------------------------------------------
diff --git a/docs/djangocache-benchmarks.rst b/docs/djangocache-benchmarks.rst
index 88d1fec..6319e31 100644
--- a/docs/djangocache-benchmarks.rst
+++ b/docs/djangocache-benchmarks.rst
@@ -2,9 +2,9 @@ DiskCache DjangoCache Benchmarks
================================
:doc:`DiskCache ` provides a Django-compatible cache API in
-:class:`diskcache.DjangoCache`. A discussion of its options and abilities are
-described in the :doc:`tutorial `. Here we try to assess its
-performance compared to other Django cache backends.
+:class:`diskcache_community.DjangoCache`. A discussion of its options and
+abilities are described in the :doc:`tutorial `. Here we try to
+assess its performance compared to other Django cache backends.
Keys and Values
---------------
@@ -102,9 +102,9 @@ Get
.. image:: _static/djangocache-get.png
-Under heavy load, :class:`DjangoCache ` gets are low
-latency. At the 99th percentile they are on par with the Memcached cache
-backend.
+Under heavy load, :class:`DjangoCache ` gets
+are low latency. At the 99th percentile they are on par with the Memcached
+cache backend.
Set
...
@@ -121,9 +121,9 @@ Delete
.. image:: _static/djangocache-delete.png
Like sets, deletes require writes to disk. Though :class:`DjangoCache
-` is the slowest, it remains competitive with latency
-less than five milliseconds. Remember that unlike Local-memory, Memached, and
-Redis, it persists all cached data.
+` is the slowest, it remains competitive with
+latency less than five milliseconds. Remember that unlike Local-memory,
+Memached, and Redis, it persists all cached data.
Timing Data
...........
@@ -185,12 +185,12 @@ Timings for diskcache
Total 791992 67.842s
========= ========= ========= ========= ========= ========= ========= =========
-:class:`DjangoCache ` defaults to using eight shards
-with a 10 millisecond timeout. Notice that cache get operations are in
+:class:`DjangoCache ` defaults to using eight
+shards with a 10 millisecond timeout. Notice that cache get operations are in
aggregate more than twice as fast as Memcached. And total cache time for all
operations is comparable. The higher set and delete latencies are due to the
-retry behavior of :class:`DjangoCache ` objects. If
-lower latency is required then the retry behavior can be disabled.
+retry behavior of :class:`DjangoCache `
+objects. If lower latency is required then the retry behavior can be disabled.
========= ========= ========= ========= ========= ========= ========= =========
Timings for filebased
@@ -205,4 +205,5 @@ Timings for filebased
Notice the higher cache miss rate. That's a result of the cache's random
culling strategy. Get and set operations also take three to twenty times longer
-in aggregate as compared with :class:`DjangoCache `.
+in aggregate as compared with :class:`DjangoCache
+`.
diff --git a/docs/index.rst b/docs/index.rst
index 763a474..c2fc223 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -9,16 +9,16 @@ processes, evicts by LRU, LFU and other policies, and ships ``Deque`` and
This documentation describes `diskcache-community`_, a community fork of
`python-diskcache`_. The fork is published on PyPI as ``diskcache-community``
-and imported as ``diskcache``. It keeps the public API, the ``cache.db``
-schema and the file layout of the last upstream release, 5.6.3, so existing
-cache directories keep working, and it carries the fix path for
-CVE-2025-69872 through the opt-in :class:`SafeDisk `.
-The README in the repository explains why the fork exists and how to migrate.
+and imported as ``diskcache_community``, so it installs next to the upstream
+``diskcache`` package. It keeps the public API, the ``cache.db`` schema and
+the file layout of the last upstream release, 5.6.3, so existing cache
+directories keep working, and it carries the fix path for CVE-2025-69872
+through the opt-in :class:`SafeDisk `. The README
+in the repository explains why the fork exists and how to migrate.
-Install it with pip, after removing the upstream ``diskcache`` package if it
-is present::
+Install it with pip and import ``diskcache_community`` where you imported
+``diskcache``::
- $ pip uninstall diskcache
$ pip install diskcache-community
Start with the :doc:`tutorial` and keep the :doc:`api` at hand.
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index 1f7cedf..fbf3262 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -63,11 +63,12 @@ can help, please open an issue in the `DiskCache Issue Tracker`_.
Cache
-----
-The core of :doc:`DiskCache ` is :class:`diskcache.Cache` which
-represents a disk and file backed cache. As a Cache, it supports a familiar
-Python mapping interface with additional cache and performance parameters.
+The core of :doc:`DiskCache ` is :class:`diskcache_community.Cache`
+which represents a disk and file backed cache. As a Cache, it supports a
+familiar Python mapping interface with additional cache and performance
+parameters.
- >>> from diskcache import Cache
+ >>> from diskcache_community import Cache
>>> cache = Cache()
Initialization expects a directory path reference. If the directory path does
@@ -88,7 +89,7 @@ Calling :meth:`close <.Cache.close>` closes the calling thread's connection
and the connections of threads that have exited; connections that other
running threads use stay open until those threads call ``close`` or the Cache
object is garbage collected. Cache objects can be used in a `with` statement
-to safeguard calling :meth:`close `.
+to safeguard calling :meth:`close `.
Cache objects also support process forking. Before a fork, the forking
thread's connections are closed in the parent, where they reopen on the next
@@ -122,8 +123,8 @@ Set an item, get a value, and delete a key using the usual operators:
True
>>> del cache['key']
-There's also a :meth:`set ` method with additional keyword
-parameters: `expire`, `read`, and `tag`.
+There's also a :meth:`set ` method with
+additional keyword parameters: `expire`, `read`, and `tag`.
>>> from io import BytesIO
>>> cache.set('key', BytesIO(b'value'), expire=5, read=True, tag='data')
@@ -131,8 +132,8 @@ parameters: `expire`, `read`, and `tag`.
In the example above: the key expires in 5 seconds, the value is read as a
file-like object, and tag metadata is stored with the key. Another method,
-:meth:`get ` supports querying extra information with
-`default`, `read`, `expire_time`, and `tag` keyword parameters.
+:meth:`get ` supports querying extra information
+with `default`, `read`, `expire_time`, and `tag` keyword parameters.
>>> result = cache.get('key', read=True, expire_time=True, tag=True)
>>> reader, timestamp, tag = result
@@ -161,9 +162,9 @@ the cache.
>>> cache.touch('does-not-exist', expire=1)
False
-Like :meth:`set `, the method :meth:`add
-` can be used to insert an item in the cache. The item is
-inserted only if the key is not already present.
+Like :meth:`set `, the method :meth:`add
+` can be used to insert an item in the cache.
+The item is inserted only if the key is not already present.
>>> cache.add(b'test', 123)
True
@@ -175,7 +176,8 @@ inserted only if the key is not already present.
123
Item values can also be incremented and decremented using :meth:`incr
-` and :meth:`decr ` methods.
+` and :meth:`decr
+` methods.
>>> cache.incr(b'test')
124
@@ -198,9 +200,10 @@ decrementing a missing key will raise a :exc:`KeyError`.
Increment and decrement operations are atomic and assume the value may be
stored in a SQLite integer column. SQLite supports 64-bit signed integers.
-Like :meth:`delete ` and :meth:`get
-`, the method :meth:`pop ` can be
-used to delete an item in the cache and return its value.
+Like :meth:`delete ` and :meth:`get
+`, the method :meth:`pop
+` can be used to delete an item in the cache and
+return its value.
>>> cache.pop('alice')
1
@@ -217,10 +220,10 @@ used to delete an item in the cache and return its value.
>>> print(tag)
admin
-The :meth:`pop ` operation is atomic and using :meth:`incr
-` together is an accurate method for counting and dumping
-statistics in long-running systems. Unlike :meth:`get `
-the `read` argument is not supported.
+The :meth:`pop ` operation is atomic and using
+:meth:`incr ` together is an accurate method
+for counting and dumping statistics in long-running systems. Unlike :meth:`get
+` the `read` argument is not supported.
.. _tutorial-culling:
@@ -241,14 +244,15 @@ Another four methods remove items from the cache::
>>> cache.expire()
10
-:meth:`Expire ` removes all expired keys from the
-cache. Resetting the :ref:`cull_limit ` to zero will disable
-culling during :meth:`set ` and :meth:`add
-` operations. Because culling is performed lazily, the
-reported length of the cache includes expired items. Iteration likewise
-includes expired items because it is a read-only operation. To exclude expired
-items you must explicitly call :meth:`expire ` which
-works regardless of the :ref:`cull_limit `.
+:meth:`Expire ` removes all expired keys from
+the cache. Resetting the :ref:`cull_limit ` to zero will
+disable culling during :meth:`set ` and
+:meth:`add ` operations. Because culling is
+performed lazily, the reported length of the cache includes expired items.
+Iteration likewise includes expired items because it is a read-only operation.
+To exclude expired items you must explicitly call :meth:`expire
+` which works regardless of the
+:ref:`cull_limit `.
>>> for num in range(100):
... _ = cache.set(num, num, tag='odd' if num % 2 else 'even')
@@ -257,10 +261,10 @@ works regardless of the :ref:`cull_limit `.
.. _tutorial-tag-index:
-:meth:`Evict ` removes all the keys with a matching
-tag. The default tag is ``None``. Tag values may be any of integer, float,
-string, bytes and None. To accelerate the eviction of items by tag, an index
-can be created. To do so, initialize the cache with ``tag_index=True``.
+:meth:`Evict ` removes all the keys with a
+matching tag. The default tag is ``None``. Tag values may be any of integer,
+float, string, bytes and None. To accelerate the eviction of items by tag, an
+index can be created. To do so, initialize the cache with ``tag_index=True``.
>>> cache.clear()
50
@@ -282,9 +286,10 @@ But prefer initializing the cache with a tag index rather than explicitly
creating or dropping the tag index.
To manually enforce the cache's size limit, use the :meth:`cull
-` method. :meth:`Cull ` begins by
-removing expired items from the cache and then uses the eviction policy to
-remove items until the cache volume is less than the size limit.
+` method. :meth:`Cull
+` begins by removing expired items from the
+cache and then uses the eviction policy to remove items until the cache volume
+is less than the size limit.
>>> cache.clear()
50
@@ -303,12 +308,14 @@ remove items until the cache volume is less than the size limit.
Some users may defer all culling to a cron-like process by setting the
:ref:`cull_limit ` to zero and manually calling :meth:`cull
-` to remove items. Like :meth:`evict
-` and :meth:`expire `, calls to
-:meth:`cull ` will work regardless of the :ref:`cull_limit
+` to remove items. Like :meth:`evict
+` and :meth:`expire
+`, calls to :meth:`cull
+` will work regardless of the :ref:`cull_limit
`.
-:meth:`Clear ` simply removes all items from the cache.
+:meth:`Clear ` simply removes all items from
+the cache.
>>> cache.clear() > 0
True
@@ -381,16 +388,16 @@ and :meth:`get <.Cache.get>`, methods :meth:`push <.Cache.push>`, :meth:`pull
expiration time and tag.
Lastly, three methods support metadata about the cache. The first is
-:meth:`volume ` which returns the estimated total size
-in bytes of the cache directory on disk.
+:meth:`volume ` which returns the estimated
+total size in bytes of the cache directory on disk.
>>> cache.volume() < int(1e5)
True
.. _tutorial-statistics:
-The second is :meth:`stats ` which returns cache hits
-and misses. Cache statistics must first be enabled.
+The second is :meth:`stats ` which returns
+cache hits and misses. Cache statistics must first be enabled.
>>> cache.stats(enable=True)
(0, 0)
@@ -407,9 +414,9 @@ Cache statistics are useful when evaluating different :ref:`eviction policies
incur an extra overhead on cache lookups. Increment and decrement operations
are not counted in cache statistics.
-The third is :meth:`check ` which verifies cache
-consistency. It can also fix inconsistencies and reclaim unused space. The
-return value is a list of warnings.
+The third is :meth:`check ` which verifies
+cache consistency. It can also fix inconsistencies and reclaim unused space.
+The return value is a list of warnings.
>>> warnings = cache.check()
@@ -431,41 +438,46 @@ To permanently delete the cache, recursively remove the cache's directory.
FanoutCache
-----------
-Built atop :class:`Cache ` is :class:`diskcache.FanoutCache`
-which automatically `shards` the underlying database. `Sharding`_ is the
-practice of horizontally partitioning data. Here it is used to decrease
-blocking writes. While readers and writers do not block each other, writers
-block other writers. Therefore a shard for every concurrent writer is
-suggested. This will depend on your scenario. The default value is 8.
+Built atop :class:`Cache ` is
+:class:`diskcache_community.FanoutCache` which automatically `shards` the
+underlying database. `Sharding`_ is the practice of horizontally partitioning
+data. Here it is used to decrease blocking writes. While readers and writers do
+not block each other, writers block other writers. Therefore a shard for every
+concurrent writer is suggested. This will depend on your scenario. The default
+value is 8.
Another parameter, `timeout`, sets a limit on how long to wait for database
transactions. Transactions are used for every operation that writes to the
-database. When the timeout expires, a :exc:`diskcache.Timeout` error is raised
-internally. This `timeout` parameter is also present on
-:class:`diskcache.Cache`. When a :exc:`Timeout ` error
-occurs in :class:`Cache ` methods, the exception may be raised
-to the caller. In contrast, :class:`FanoutCache `
+database. When the timeout expires, a :exc:`diskcache_community.Timeout` error
+is raised internally. This `timeout` parameter is also present on
+:class:`diskcache_community.Cache`. When a :exc:`Timeout
+` error occurs in :class:`Cache
+` methods, the exception may be raised to the
+caller. In contrast, :class:`FanoutCache `
catches all timeout errors and aborts the operation. As a result, :meth:`set
-` and :meth:`delete `
-methods may silently fail.
-
-Most methods that handle :exc:`Timeout ` exceptions also
-include a `retry` keyword parameter (default ``False``) to automatically repeat
-attempts that timeout. The mapping interface operators: :meth:`cache[key]
-`, :meth:`cache[key] = value
-`, and :meth:`del cache[key]
-` automatically retry operations when
-:exc:`Timeout ` errors occur. :class:`FanoutCache
-` will never raise a :exc:`Timeout `
-exception. The default `timeout` is 0.010 (10 milliseconds).
-
- >>> from diskcache import FanoutCache
+` and :meth:`delete
+` methods may silently fail.
+
+Most methods that handle :exc:`Timeout `
+exceptions also include a `retry` keyword parameter (default ``False``) to
+automatically repeat attempts that timeout. The mapping interface operators:
+:meth:`cache[key] `,
+:meth:`cache[key] = value `, and
+:meth:`del cache[key] `
+automatically retry operations when :exc:`Timeout
+` errors occur. :class:`FanoutCache
+` will never raise a :exc:`Timeout
+` exception. The default `timeout` is 0.010 (10
+milliseconds).
+
+ >>> from diskcache_community import FanoutCache
>>> cache = FanoutCache(shards=4, timeout=1)
The example above creates a cache in a temporary directory with four shards and
a one second timeout. Operations will attempt to abort if they take longer than
-one second. The remaining API of :class:`FanoutCache `
-matches :class:`Cache ` as described above.
+one second. The remaining API of :class:`FanoutCache
+` matches :class:`Cache
+` as described above.
The :class:`.FanoutCache` :ref:`size_limit ` is used as the total
size of the cache. The size limit of individual cache shards is the total size
@@ -475,10 +487,10 @@ of 256 megabytes. Items that are larger than the size limit are immediately
culled.
Caches have an additional feature: :meth:`memoizing
-` decorator. The decorator wraps a callable and
-caches arguments and return values.
+` decorator. The decorator wraps a
+callable and caches arguments and return values.
- >>> from diskcache import FanoutCache
+ >>> from diskcache_community import FanoutCache
>>> cache = FanoutCache()
>>> @cache.memoize(typed=True, expire=1, tag='fib')
... def fibonacci(number):
@@ -505,7 +517,7 @@ TypeError will occur::
TypeError: name cannot be callable
Observe the lack of parenthenses after :meth:`memoize
-` above.
+` above.
The ``ignore`` argument leaves arguments out of the cache key. An integer
ignores the argument at that position and a string ignores the parameter with
@@ -525,23 +537,23 @@ arguments that do not affect the result, like a database session:
DjangoCache
-----------
-:class:`diskcache.DjangoCache` uses :class:`FanoutCache
-` to provide a Django-compatible cache interface. With
-:doc:`DiskCache ` installed, you can use :class:`DjangoCache
-` in your settings file.
+:class:`diskcache_community.DjangoCache` uses :class:`FanoutCache
+` to provide a Django-compatible cache
+interface. With :doc:`DiskCache ` installed, you can use
+:class:`DjangoCache ` in your settings file.
.. code-block:: python
CACHES = {
'default': {
- 'BACKEND': 'diskcache.DjangoCache',
+ 'BACKEND': 'diskcache_community.DjangoCache',
'LOCATION': '/path/to/cache/directory',
'TIMEOUT': 300,
# ^-- Django setting for default timeout of each key.
'SHARDS': 8,
'DATABASE_TIMEOUT': 0.010, # 10 milliseconds
# ^-- Timeout for each DjangoCache database transaction.
- 'DISK': 'diskcache.Disk',
+ 'DISK': 'diskcache_community.Disk',
# ^-- Disk type for serialization, see SafeDisk below.
'OPTIONS': {
'size_limit': 2 ** 30 # 1 gigabyte
@@ -549,24 +561,26 @@ DjangoCache
},
}
-As with :class:`FanoutCache ` above, these settings
-create a Django-compatible cache with eight shards and a 10ms timeout. You can
-pass further settings via the ``OPTIONS`` mapping as shown in the Django
-documentation. ``DISK`` names the :class:`Disk ` type or
-subclass used for serialization, as a class or a dotted import path; see
-:ref:`SafeDisk ` for the restricted alternative. Only the
-``BACKEND`` and ``LOCATION`` keys are necessary in the above example. The other
-keys simply display their default value. :class:`DjangoCache ` will never raise a
-:exc:`Timeout ` exception. But unlike :class:`FanoutCache
-`, the keyword parameter `retry` defaults to ``True``
-for :class:`DjangoCache ` methods.
-
-The API of :class:`DjangoCache ` is a superset of the
-functionality described in the `Django documentation on caching`_ and includes
-many :class:`FanoutCache ` features.
-
-:class:`DjangoCache ` also works well with `X-Sendfile`
-and `X-Accel-Redirect` headers.
+As with :class:`FanoutCache ` above, these
+settings create a Django-compatible cache with eight shards and a 10ms timeout.
+You can pass further settings via the ``OPTIONS`` mapping as shown in the
+Django documentation. ``DISK`` names the :class:`Disk
+` type or subclass used for serialization, as a class
+or a dotted import path; see :ref:`SafeDisk ` for the
+restricted alternative. Only the ``BACKEND`` and ``LOCATION`` keys are
+necessary in the above example. The other keys simply display their default
+value. :class:`DjangoCache ` will never raise
+a :exc:`Timeout ` exception. But unlike
+:class:`FanoutCache `, the keyword parameter
+`retry` defaults to ``True`` for :class:`DjangoCache
+` methods.
+
+The API of :class:`DjangoCache ` is a superset
+of the functionality described in the `Django documentation on caching`_ and
+includes many :class:`FanoutCache ` features.
+
+:class:`DjangoCache ` also works well with
+`X-Sendfile` and `X-Accel-Redirect` headers.
.. code-block:: python
@@ -581,26 +595,26 @@ and `X-Accel-Redirect` headers.
except KeyError:
# Handle cache miss.
-When values are :meth:`set ` using ``read=True``
-they are guaranteed to be stored in files. The full path is available on the
-file handle in the `name` attribute. Remember to also include the
-`Content-Type` header if known.
+When values are :meth:`set ` using
+``read=True`` they are guaranteed to be stored in files. The full path is
+available on the file handle in the `name` attribute. Remember to also include
+the `Content-Type` header if known.
.. _`Django documentation on caching`: https://docs.djangoproject.com/en/3.2/topics/cache/#the-low-level-cache-api
Deque
-----
-:class:`diskcache.Deque` (pronounced "deck") uses a :class:`Cache
-` to provide a `collections.deque
+:class:`diskcache_community.Deque` (pronounced "deck") uses a :class:`Cache
+` to provide a `collections.deque
`_-compatible
double-ended queue. Deques are a generalization of stacks and queues with fast
access and editing at both front and back sides. :class:`Deque
-` objects use the :meth:`push <.Cache.push>`, :meth:`pull
-<.Cache.pull>`, and :meth:`peek <.Cache.peek>` methods of :class:`Cache
-<.Cache>` objects but never evict or expire items.
+` objects use the :meth:`push <.Cache.push>`,
+:meth:`pull <.Cache.pull>`, and :meth:`peek <.Cache.peek>` methods of
+:class:`Cache <.Cache>` objects but never evict or expire items.
- >>> from diskcache import Deque
+ >>> from diskcache_community import Deque
>>> deque = Deque(range(5, 10))
>>> deque.pop()
9
@@ -620,25 +634,26 @@ access and editing at both front and back sides. :class:`Deque
>>> list(thing)
['c', 'd', 'e']
-:class:`Deque ` objects provide an efficient and safe means of
-cross-thread and cross-process communication. :class:`Deque `
-objects are also useful in scenarios where contents should remain persistent or
-limitations prohibit holding all items in memory at the same time. The deque
-uses a fixed amount of memory regardless of the size or number of items stored
-inside it.
+:class:`Deque ` objects provide an efficient and
+safe means of cross-thread and cross-process communication. :class:`Deque
+` objects are also useful in scenarios where
+contents should remain persistent or limitations prohibit holding all items in
+memory at the same time. The deque uses a fixed amount of memory regardless of
+the size or number of items stored inside it.
Index
-----
-:class:`diskcache.Index` uses a :class:`Cache ` to provide a
-`mutable mapping
+:class:`diskcache_community.Index` uses a :class:`Cache
+` to provide a `mutable mapping
`_
and `ordered dictionary
`_
-interface. :class:`Index ` objects inherit all the benefits of
-:class:`Cache ` objects but never evict or expire items.
+interface. :class:`Index ` objects inherit all the
+benefits of :class:`Cache ` objects but never evict
+or expire items.
- >>> from diskcache import Index
+ >>> from diskcache_community import Index
>>> index = Index([('a', 1), ('b', 2), ('c', 3)])
>>> 'b' in index
True
@@ -653,12 +668,12 @@ interface. :class:`Index ` objects inherit all the benefits of
>>> other.popitem(last=False)
('b', 2)
-:class:`Index ` objects provide an efficient and safe means of
-cross-thread and cross-process communication. :class:`Index `
-objects are also useful in scenarios where contents should remain persistent or
-limitations prohibit holding all items in memory at the same time. The index
-uses a fixed amount of memory regardless of the size or number of items stored
-inside it.
+:class:`Index ` objects provide an efficient and
+safe means of cross-thread and cross-process communication. :class:`Index
+` objects are also useful in scenarios where
+contents should remain persistent or limitations prohibit holding all items in
+memory at the same time. The index uses a fixed amount of memory regardless of
+the size or number of items stored inside it.
.. _tutorial-transactions:
@@ -743,32 +758,32 @@ Settings
--------
A variety of settings are available to improve performance. These values are
-stored in the database for durability and to communicate between
-processes. Each value is cached in an attribute with matching name. Attributes
-are updated using :meth:`reset `. Attributes are set
+stored in the database for durability and to communicate between processes.
+Each value is cached in an attribute with matching name. Attributes are updated
+using :meth:`reset `. Attributes are set
during initialization when passed as keyword arguments.
* `size_limit`, default one gigabyte. The maximum on-disk size of the cache.
* `cull_limit`, default ten. The maximum number of keys to cull when adding a
new item. Set to zero to disable automatic culling. Some systems may disable
automatic culling in exchange for a cron-like job that regularly calls
- :meth:`cull ` in a separate process.
+ :meth:`cull ` in a separate process.
* `statistics`, default False, disabled. The setting to collect :ref:`cache
statistics `.
* `tag_index`, default False, disabled. The setting to create a database
:ref:`tag index ` for :meth:`evict
- `.
+ `.
* `eviction_policy`, default "least-recently-stored". The setting to determine
:ref:`eviction policy `.
-The :meth:`reset ` method accepts an optional
-second argument that updates the corresponding value in the database. The
-return value is the latest retrieved from the database. Notice that attributes
-are updated lazily. Prefer idioms like :meth:`len
-`, :meth:`volume
-`, and :meth:`keyword arguments
-` rather than using :meth:`reset
-` directly.
+The :meth:`reset ` method accepts an
+optional second argument that updates the corresponding value in the database.
+The return value is the latest retrieved from the database. Notice that
+attributes are updated lazily. Prefer idioms like :meth:`len
+`, :meth:`volume
+`, and :meth:`keyword arguments
+` rather than using :meth:`reset
+` directly.
>>> cache = Cache(size_limit=int(4e9))
>>> print(cache.size_limit)
@@ -787,7 +802,7 @@ are updated lazily. Prefer idioms like :meth:`len
More settings correspond to :ref:`Disk ` attributes. Each of
these may be specified when initializing the :ref:`Cache
`. Changing these values will update the unprefixed attribute
-on the :class:`Disk ` object.
+on the :class:`Disk ` object.
* `disk_min_file_size`, default 32 kilobytes. The minimum size to store a value
in a file.
@@ -805,9 +820,9 @@ pragma documentation`_ for more details.
* `sqlite_synchronous`, default 1, "NORMAL".
Each of these settings can passed to :class:`DjangoCache
-` via the ``OPTIONS`` key mapping. Always measure before
-and after changing the default values. Default settings are programmatically
-accessible at :data:`diskcache.DEFAULT_SETTINGS`.
+` via the ``OPTIONS`` key mapping. Always
+measure before and after changing the default values. Default settings are
+programmatically accessible at :data:`diskcache_community.DEFAULT_SETTINGS`.
.. _`SQLite pragma documentation`: https://www.sqlite.org/pragma.html
@@ -859,16 +874,16 @@ to initialize the cache.
Disk
----
-:class:`diskcache.Disk` objects are responsible for serializing and
+:class:`diskcache_community.Disk` objects are responsible for serializing and
deserializing data stored in the cache. Serialization behavior differs between
keys and values. In particular, keys are always stored in the cache metadata
database while values are sometimes stored separately in files.
-To customize serialization, you may pass in a :class:`Disk `
-subclass to initialize the cache. All clients accessing the cache are expected
-to use the same serialization. The default implementation uses Pickle and the
-example below uses compressed JSON, available for convenience as
-:class:`JSONDisk `.
+To customize serialization, you may pass in a :class:`Disk
+` subclass to initialize the cache. All clients
+accessing the cache are expected to use the same serialization. The default
+implementation uses Pickle and the example below uses compressed JSON,
+available for convenience as :class:`JSONDisk `.
.. code-block:: python
@@ -912,11 +927,12 @@ will require identical types.
SafeDisk
........
-The default :class:`Disk ` reads pickled keys and values back
-with :func:`pickle.load`, which runs whatever code a pickle asks for. Anyone
-who can write into the cache directory can therefore run code in every
-process that reads from it (CVE-2025-69872). :class:`SafeDisk
-` keeps the same on-disk format but closes that path:
+The default :class:`Disk ` reads pickled keys and
+values back with :func:`pickle.load`, which runs whatever code a pickle asks
+for. Anyone who can write into the cache directory can therefore run code in
+every process that reads from it (CVE-2025-69872). :class:`SafeDisk
+` keeps the same on-disk format but closes that
+path:
* Pickles are read with an unpickler that resolves only an allowlist of
standard-library constructors: ``None``, ``bool``, ``int``, ``float``,
@@ -935,30 +951,32 @@ that opens the directory chooses for itself:
.. code-block:: python
- from diskcache import Cache, Deque, FanoutCache, Index, SafeDisk
+ from diskcache_community import Cache, Deque, FanoutCache, Index, SafeDisk
cache = Cache('/tmp/mycache', disk=SafeDisk)
fanout = FanoutCache('/tmp/myfanout', disk=SafeDisk)
deque = Deque(directory='/tmp/mydeque', disk=SafeDisk)
index = Index('/tmp/myindex', disk=SafeDisk)
-For :class:`DjangoCache `, set ``'DISK':
-'diskcache.SafeDisk'`` next to ``SHARDS`` and ``DATABASE_TIMEOUT`` in the
-``CACHES`` setting. Sub-containers created through :meth:`FanoutCache.cache
-`, :meth:`deque `
-and :meth:`index ` use the fanout cache's disk
-unless given their own.
-
-A directory written with :class:`SafeDisk ` opens with the
-default :class:`Disk ` and the reverse, as long as the values
-are within the allowlist. Some uses need the default :class:`Disk
-` because their objects are outside it: instances of your own
-classes, Django model instances and ``HttpResponse`` objects, datetimes whose
-``tzinfo`` is a ``zoneinfo.ZoneInfo`` rather than a ``datetime.timezone``, and
-:meth:`memoize ` with ``typed=True``, which puts
-``type`` objects into the cache key. Every process that writes to a shared
-directory with the default :class:`Disk ` must be trusted, and
-a reader using :class:`SafeDisk ` raises
+For :class:`DjangoCache `, set ``'DISK':
+'diskcache_community.SafeDisk'`` next to ``SHARDS`` and ``DATABASE_TIMEOUT`` in
+the ``CACHES`` setting. Sub-containers created through :meth:`FanoutCache.cache
+