Repository navigation
v5.8.2
Adds SafeJSONDisk and SafeDisk.with_numpy(), keeps float NaN values, and makes get and set faster. The cache.db schema and the file layout are unchanged.
Security
-
SafeJSONDiskstores JSON likeJSONDisk, reads and writes the same files, and never reads a pickle.JSONDiskwrites only JSON but inherits the pickle reads ofDisk, so a crafted row still ran code and a crafted path could still delete a file outside the cache.SafeJSONDiskrefuses pickled keys and values without parsing them and confines paths asSafeDiskdoes (PR #33). -
SafeDisk.with_numpy()returns aSafeDisksubclass that stores numpy arrays in the.npyformat instead of pickling them, through pickle's persistent IDs, anywhere in a value including inside classes allowed withwith_types, which combines with it in either order, aswith_optionsdoes. 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 raisingMemoryError, and every forged entry raisesUnpicklingErroror reads as a miss withon_error='miss'. Object arrays,ndarraysubclasses and arrays the reader would refuse raiseTypeErrorat write time. Entries holding numpy data can't be read by other disks, including a plainSafeDisk, or by 5.6.3, which raiseUnpicklingError. numpy stays optional, andSafeDiskwithoutwith_numpy()still refuses it; itswith_typeserror now points towith_numpy()(PR #34). -
DISKCACHE_SAFE_TYPESandDISKCACHE_SAFE_MODULESnow apply whenDISKCACHE_DISKnames aSafeDisksubclass, not only withsafe(PR #34).
Fixes
Cache.getis as fast on a hit as in 5.6.3 again; since 5.7.0 it was about 5% slower (PR #35).- 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_stampedeworks withSafeDisk: the key of its early recomputation marker held the internalENOVALconstant, whichSafeDiskrefuses, so a call raisedTypeErroronce a cached value neared expiry (PR #32).
Performance
Cache.getis about 15% faster on a hit and 12% on a miss, andDjangoCache.getabout 16%: a plain lookup reads only the value columns, on a cursor reused per thread (PR #38).Cache.setis about 15% faster, andDjangoCache.setabout 17%: each write runs fewer SQL statements and less Python (PR #37).
Packaging
- The package installs on Python 3.8 and later (PR #36).