Package: percache
percachepercacheis a Python module to persistently cache results of functions
(or callables in general) using decorators.It is somehow similar to theMemoize
Examplefrom the Python Decorator Library but with the advantage that results
are storedpersistentlyin a cache.percacheprovides memoization
across multiple invocations of the Python interpreter.percacherequires Python 3. Install withpip install percache. It
has no dependencies outside the standard library.Example>>> import percache
>>> cache = percache.Cache("/tmp/my-cache")
>>>
>>> @cache
... def longtask(a, b):
...     print("running a long task")
...     return a + b
...
>>> longtask(1, 2)
running a long task
3
>>>
>>> longtask(1, 2)
3
>>> cache.close() # writes new cached results to diskAs you can see at the missing output after the second invocation,longtaskhas been called once only. The second time the result is
retrieved from the cache.The key feature of this module is that this
works across multiple invocations of the Python interpreter.A requirement on the results to cache is that they arepickable.Each cache file can be used for any number of differently named
callables.Alternative back-ends and live synchronizationBy defaultpercacheuses ashelveas its cache
back-end. Alternative back-ends may be used if they are given as
dictionary-like objects with aclose()andsync()method:>>>classFooCache(dict):...defsync(self):.........defclose(self):......>>>fc=FooCache()>>>cache=percache.Cache(fc,livesync=True)In this example a cache is created in live-sync mode, i.e. resultsimmediatelyare stored permanently. Normally this happens not until a
cache'sclose()method has been called or until it getsfinalized. Note that the live-sync mode may slow down your
percache-decorated functions (though it reduces the risk of "loosing"
results).Caching details (you should know)When caching the result of a callable, a SHA1 hash based on the
callable's name and arguments is used as a key to store the result in
the cache file.The hash calculation does not work directly with the arguments but with
theirrepresentations, i.e. the string returned by applyingrepr().
Argument representations are supposed to differentiate values
sufficiently for the purpose of the function but identically across
multiple invocations of the Python interpreter. By default the built-in
functionrepr()is used to get argument representations. This is just
perfect for basic types, lists, tuples and combinations of them but it
may fail on other types:>>>repr(42)42# good>>>repr(["a","b",(1,2L)])"['a', 'b', (1, 2L)]"# good>>>o=object()>>>repr(o)'<object object at 0xb769a4f8>'# bad (address is dynamic)>>>repr({"a":1,"b":2,"d":4,"c":3})"{'a': 1, 'c': 3, 'b': 2, 'd': 4}"# bad (order may change)>>>classA(object):...def__init__(self,a):...self.a=a...>>>repr(A(36))'<__main__.A object at 0xb725bb6c>'# bad (A.a not considered)>>>repr(A(35))'<__main__.A object at 0xb725bb6c>'# bad (A.a not considered)Abadrepresentation is one that is not identically across Python
invocations (allbadexamples) or one that does not differentiate
values sufficiently (last 2badexamples).To use such types anyway you can eitherimplement the type's__repr__()method accordingly orprovide a custom representation function using thereprkeyword of
theCacheconstructor.Implement the__repr__()methodTo pass dictionaries topercachedecorated functions, you could wrap
them in an own dictionary type with a suitable__repr__()method:>>>classmydict(dict):...def__repr__(self):...items=["%r:%r"%(k,self[k])forkinsorted(self)]...return"{%s}"%", ".join(items)...>>>repr(mydict({"a":1,"b":2,"d":4,"c":3}))"{'a': 1, 'b': 2, 'c': 3, 'd': 4}"# good (always same order)Provide a customrepr()functionThe following example shows how to use a custom representation function
to get a suitable argument representation offileobjects:>>>defmyrepr(arg):...ifisinstance(arg,file):...# return a string with file name and modification time...return"%s:%s"%(arg.name,os.fstat(arg.fileno())[8])...else:...returnrepr(arg)...>>>cache=percache.Cache("/some/path",repr=myrepr)HousekeepingMake sure to delete the cache file whenever the behavior of a cached
function has changed!To prevent the cache from getting larger and larger you can call theclear()method of aCacheinstance. By default it clearsallresults from the cache. The keywordmaxagemy be used to specify a
maximum number of seconds passed since a cached result has beenusedthe last time. Any result not used (written or accessed) formaxageseconds gets removed from the cache.ChangesVersion 0.4.4Project README in markdownVersion 0.4.3Pin build status badge in README to a specific version.Version 0.4.1README fixes.Use twine for PyPi upload.Use builds.sr.ht for CI.Version 0.4.0Update docs due to project migration tosourcehut.Discontinue Python 2 support.Version 0.3.0Support Python 3.3 (next to 2.6 and 2.7)Version 0.2.1Add missing README to PyPi package.Version 0.2Automatically close (i.e. sync) the cache on finalization.Optionally sync the cache on each change.Support for alternative back-ends (others thanshelve).Cache object are callable now, which makes the explicitcheck()method obsolete (though the old interface is still supported).Version 0.1.1Fix wrong usage age output of command line interface.Meet half way with pylint.Version 0.1Initial release