Merge "Make @memoize thread-aware"

This commit is contained in:
Jenkins 2017-09-11 21:30:00 +00:00 committed by Gerrit Code Review
commit 4277b078b9

View File

@ -12,7 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import functools
import threading
import warnings
import weakref
@ -58,6 +60,7 @@ def memoized(func):
# instance for every decorated function, and it's stored in a closure of
# the wrapped function.
cache = {}
locks = collections.defaultdict(threading.Lock)
@functools.wraps(func)
def wrapped(*args, **kwargs):
@ -71,6 +74,7 @@ def memoized(func):
try:
# The key here is from closure, and is calculated later.
del cache[key]
del locks[key]
except KeyError:
# Some other weak reference might have already removed that
# key -- in that case we don't need to do anything.
@ -78,22 +82,25 @@ def memoized(func):
key = _get_key(args, kwargs, remove)
try:
# We want cache hit to be as fast as possible, and don't really
# care much about the speed of a cache miss, because it will only
# happen once and likely calls some external API, database, or
# some other slow thing. That's why the hit is in straightforward
# code, and the miss is in an exception.
value = cache[key]
except KeyError:
value = cache[key] = func(*args, **kwargs)
with locks[key]:
try:
# We want cache hit to be as fast as possible, and don't
# really care much about the speed of a cache miss, because
# it will only happen once and likely calls some external
# API, database, or some other slow thing. That's why the
# hit is in straightforward code, and the miss is in an
# exception.
value = cache[key]
except KeyError:
value = cache[key] = func(*args, **kwargs)
except TypeError:
# The calculated key may be unhashable when an unhashable object,
# such as a list, is passed as one of the arguments. In that case,
# we can't cache anything and simply always call the decorated
# function.
# The calculated key may be unhashable when an unhashable
# object, such as a list, is passed as one of the arguments. In
# that case, we can't cache anything and simply always call the
# decorated function.
warnings.warn(
"The key %r is not hashable and cannot be memoized." % (key,),
UnhashableKeyWarning, 2)
"The key %r is not hashable and cannot be memoized."
% (key,), UnhashableKeyWarning, 2)
value = func(*args, **kwargs)
return value
return wrapped