**** CubicPower OpenStack Study ****
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Nebula, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import functools
import warnings
import weakref
**** CubicPower OpenStack Study ****
class UnhashableKeyWarning(RuntimeWarning):
"""Raised when trying to memoize a function with an unhashable argument."""
**** CubicPower OpenStack Study ****
def wrapped(*args, **kwargs):
# We need to have defined key early, to be able to use it in the
# remove() function, but we calculate the actual value of the key
# later on, because we need the remove() function for that.
key = None
def remove(ref):
"""A callback to remove outdated items from cache."""
try:
# The key here is from closure, and is calculated later.
del cache[key]
except KeyError:
# Some other weak reference might have already removed that
# key -- in that case we don't need to do anything.
pass
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)
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.
warnings.warn(
"The key %r is not hashable and cannot be memoized." % key,
UnhashableKeyWarning, 2)
value = func(*args, **kwargs)
return value
return wrapped
# We can use @memoized for methods now too, because it uses weakref and so
# it doesn't keep the instances in memory forever. We might want to separate
# them in the future, however.
memoized_method = memoized
**** CubicPower OpenStack Study ****
def remove(ref):
"""A callback to remove outdated items from cache."""
try:
# The key here is from closure, and is calculated later.
del cache[key]
except KeyError:
# Some other weak reference might have already removed that
# key -- in that case we don't need to do anything.
pass
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)
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.
warnings.warn(
"The key %r is not hashable and cannot be memoized." % key,
UnhashableKeyWarning, 2)
value = func(*args, **kwargs)
return value
return wrapped
# We can use @memoized for methods now too, because it uses weakref and so
# it doesn't keep the instances in memory forever. We might want to separate
# them in the future, however.
memoized_method = memoized