Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
with:
python-version: ${{ matrix.python }}
- name: Install diskcache and test dependencies
run: pip install -e . "django==${{ matrix.django }}.*" pytest pytest-cov pytest-django pytest-xdist
run: pip install -e . "django==${{ matrix.django }}.*" numpy pytest pytest-cov pytest-django pytest-xdist
- name: Run tests
run: pytest

Expand Down Expand Up @@ -134,7 +134,7 @@ jobs:
sys.exit('this SQLite accepts double-quoted strings, so the job proves nothing')
PY
- name: Install diskcache and test dependencies
run: pip install -e . "django==5.2.*" pytest pytest-cov pytest-django pytest-xdist
run: pip install -e . "django==5.2.*" numpy pytest pytest-cov pytest-django pytest-xdist
- name: Run tests
run: pytest

Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ indent-string=' '
max-line-length=100

# Maximum number of lines in a module.
max-module-lines=4000
max-module-lines=4100

# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ diskcache-community forked from DiskCache at upstream commit ebfa37c (3 March 20

- `SafeJSONDisk` stores JSON like `JSONDisk`, reads and writes the same files, and never reads a pickle. `JSONDisk` writes only JSON but inherits the pickle reads of `Disk`, so a crafted row still ran code and a crafted path could still delete a file outside the cache. `SafeJSONDisk` refuses pickled keys and values without parsing them and confines paths as `SafeDisk` does (PR #33).

- `SafeDisk.with_numpy()` returns a `SafeDisk` subclass that stores numpy arrays in the `.npy` format instead of pickling them, through pickle's persistent IDs, anywhere in a value including inside classes allowed with `with_types`, which combines with it in either order, as `with_options` does. numpy scalars read back as the same type. Reading a large array takes no second copy of it. Headers are checked against the data, so forged arrays are refused rather than raising `MemoryError`, and every forged entry raises `UnpicklingError` or reads as a miss with `on_error='miss'`. Object arrays, `ndarray` subclasses and arrays the reader would refuse raise `TypeError` at write time. Entries holding numpy data can't be read by other disks, including a plain `SafeDisk`, or by 5.6.3, which raise `UnpicklingError`. numpy stays optional, and `SafeDisk` without `with_numpy()` still refuses it; its `with_types` error now points to `with_numpy()` (PR #34).
- `DISKCACHE_SAFE_TYPES` and `DISKCACHE_SAFE_MODULES` now apply when `DISKCACHE_DISK` names a `SafeDisk` subclass, not only with `safe` (PR #34).

### Fixes

- A float NaN stored as a value is kept, on every disk. It was written to SQLite, which stores NaN as NULL, so it read back as `None`. It is now stored as a pickle, which 5.6.3 reads too (PR #34).
- `memoize_stampede` works with `SafeDisk`: the key of its early recomputation marker held the internal `ENOVAL` constant, which `SafeDisk` refuses, so a call raised `TypeError` once a cached value neared expiry (PR #32).

## 5.8.1 (2026-10-18)
Expand Down
13 changes: 0 additions & 13 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,3 @@ tox -e bluecheck,isortcheck,flake8,mypy,pylint,doc8,rstcheck,docs
Work on a branch and open a pull request against `master`. CI runs the lint checks, the test suite on Python 3.10 to 3.14 on Linux, macOS and Windows, the Django 4.2 and 6.0 jobs and an import check without Django, and a pull request merges once CI is green and the change has been reviewed. Keep each pull request to one change, and write commit subjects as one sentence that says what the change does. In the description, say what was wrong, list the concrete changes, and say what you ran to check them.

Fixes imported from upstream pull requests keep their original commits and authors; the description says which upstream pull request is imported. Entries for merged changes go into [CHANGELOG.md](CHANGELOG.md) under `Unreleased`.

## Compatibility contract

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_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: `<dir>/ab/cd/<28 hex>.val` for values and `%03d` for `FanoutCache` shards;
- the `RAW`, `BINARY`, `TEXT` and `PICKLE` serialization and the bytes `JSONDisk` writes;
- the key format `memoize` derives from a call;
- the persisted `Settings` keys; no new `disk_*` or `Settings` key before 6.0.0.

A process running 5.6.3 must be able to open a directory written by this fork, and the reverse. Golden fixtures written by the 5.6.3 wheel guard this in the test suite.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ cache.
semaphores and throttling
- Thread-safe and process-safe, with fork-safe connection handling
- Read-only mode, automatic when the directory is not writable
- Opt-in `SafeDisk` and `SafeJSONDisk` for caches whose directory other users
can write to
- Opt-in `SafeDisk` and `SafeJSONDisk` for caches whose directory other
users can write to, with `SafeDisk.with_numpy()` for numpy arrays
- Pure Python on top of the standard library `sqlite3`; no server, no compiler
- Tested on CPython 3.10 to 3.14 on Linux, macOS and Windows
- Gets in about 2.6 µs and sets in about 19 µs on an Apple M5 Pro in the
Expand Down
5 changes: 1 addition & 4 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@ 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`.
- 5.8.0 is imported as `diskcache_community`, next to the upstream `diskcache` package. `SafeDisk.with_types` allows your own classes and the classes defined in named modules while everything else stays refused; numpy classes cannot be allowed, because numpy unpickling is not memory-safe. `configure()` and `DISKCACHE_DISK=safe` select `SafeDisk` for caches created without a disk, including those that libraries create through `diskcache_community`. `Disk.with_options(on_error='miss')` deletes entries that cannot be read and treats them as missing. The steps are in [Fix CVE-2025-69872 in diskcache](https://diskcache-community.readthedocs.io/en/latest/fix-cve-2025-69872.html).
- `JSONDisk` writes no pickles but still reads one from a crafted row, as it does in 5.6.3. `SafeJSONDisk` reads and writes the same files and never reads a pickle; `SafeDisk` does not decode what `JSONDisk` wrote.
- `SafeDisk.with_numpy()` returns a `SafeDisk` that also stores numpy arrays, in the `.npy` format rather than as pickles. numpy pickles stay refused, because unpickling an array is not memory-safe.
- 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.

Unless you have opted into `SafeDisk` or `SafeJSONDisk`, 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` 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.

## Dependencies

The library has no runtime dependencies. Advisories for the development and CI dependencies are tracked through Dependabot alerts on this repository.
Loading
Loading