Source code for sympy.core.cache
""" Caching facility for SymPy """
from __future__ import print_function, division
# TODO: refactor CACHE & friends into class?
# global cache registry:
CACHE = [] # [] of
# (item, {} or tuple of {})
from sympy.core.decorators import wraps
def print_cache():
"""print cache content"""
for item, cache in CACHE:
item = str(item)
head = '='*len(item)
print(head)
print(item)
print(head)
if not isinstance(cache, tuple):
cache = (cache,)
shown = False
else:
shown = True
for i, kv in enumerate(cache):
if shown:
print('\n*** %i ***\n' % i)
for k, v in list(kv.items()):
print(' %s :\t%s' % (k, v))
def clear_cache():
"""clear cache content"""
for item, cache in CACHE:
if not isinstance(cache, tuple):
cache = (cache,)
for kv in cache:
kv.clear()
########################################
def __cacheit_nocache(func):
return func
def __cacheit(func):
"""caching decorator.
important: the result of cached function must be *immutable*
Examples
========
>>> from sympy.core.cache import cacheit
>>> @cacheit
... def f(a,b):
... return a+b
>>> @cacheit
... def f(a,b):
... return [a,b] # <-- WRONG, returns mutable object
to force cacheit to check returned results mutability and consistency,
set environment variable SYMPY_USE_CACHE to 'debug'
"""
func._cache_it_cache = func_cache_it_cache = {}
CACHE.append((func, func_cache_it_cache))
@wraps(func)
def wrapper(*args, **kw_args):
"""
Assemble the args and kw_args to compute the hash.
"""
k = [(x, type(x)) for x in args]
if kw_args:
keys = sorted(kw_args)
k.extend([(x, kw_args[x], type(kw_args[x])) for x in keys])
k = tuple(k)
try:
return func_cache_it_cache[k]
except (KeyError, TypeError):
pass
r = func(*args, **kw_args)
try:
func_cache_it_cache[k] = r
except TypeError: # k is unhashable
# Note, collections.Hashable is not smart enough to be used here.
pass
return r
return wrapper
def __cacheit_debug(func):
"""cacheit + code to check cache consistency"""
cfunc = __cacheit(func)
@wraps(func)
def wrapper(*args, **kw_args):
# always call function itself and compare it with cached version
r1 = func(*args, **kw_args)
r2 = cfunc(*args, **kw_args)
# try to see if the result is immutable
#
# this works because:
#
# hash([1,2,3]) -> raise TypeError
# hash({'a':1, 'b':2}) -> raise TypeError
# hash((1,[2,3])) -> raise TypeError
#
# hash((1,2,3)) -> just computes the hash
hash(r1), hash(r2)
# also see if returned values are the same
if r1 != r2:
raise RuntimeError("Returned values are not the same")
return r1
return wrapper
def _getenv(key, default=None):
from os import getenv
return getenv(key, default)
# SYMPY_USE_CACHE=yes/no/debug
USE_CACHE = _getenv('SYMPY_USE_CACHE', 'yes').lower()
if USE_CACHE == 'no':
cacheit = __cacheit_nocache
elif USE_CACHE == 'yes':
cacheit = __cacheit
elif USE_CACHE == 'debug':
cacheit = __cacheit_debug # a lot slower
else:
raise RuntimeError(
'unrecognized value for SYMPY_USE_CACHE: %s' % USE_CACHE)