Merge "Avoid UnhashableKeyWarning in api.nova.novaclient"

This commit is contained in:
Zuul 2018-09-12 08:28:14 +00:00 committed by Gerrit Code Review
commit 610eab4ba4
3 changed files with 76 additions and 9 deletions

View File

@ -81,3 +81,36 @@ class MemoizedTests(test.TestCase):
self.assertEqual(output2[position], leader)
# check that some_other_func returned a memoized list.
self.assertIs(output1, output2)
def test_memoized_with_argcnv(self):
value_list = []
def converter(*args, **kwargs):
new_args = tuple(reversed(args))
new_kwargs = dict((k, v + 1) for k, v in kwargs.items())
return new_args, new_kwargs
@memoized.memoized_with_argconv(converter)
def target_func(*args, **kwargs):
value_list.append(1)
return args, kwargs
for i in range(3):
ret_args, ret_kwargs = target_func(1, 2, 3)
self.assertEqual((3, 2, 1), ret_args)
self.assertEqual({}, ret_kwargs)
self.assertEqual(1, len(value_list))
value_list = []
for i in range(3):
ret_args, ret_kwargs = target_func(a=1, b=2, c=3)
self.assertEqual(tuple(), ret_args)
self.assertEqual({'a': 2, 'b': 3, 'c': 4}, ret_kwargs)
self.assertEqual(1, len(value_list))
value_list = []
for i in range(3):
ret_args, ret_kwargs = target_func(1, 2, a=3, b=4)
self.assertEqual((2, 1), ret_args)
self.assertEqual({'a': 4, 'b': 5}, ret_kwargs)
self.assertEqual(1, len(value_list))

View File

@ -178,3 +178,31 @@ def memoized_with_request(request_func, request_index=0):
return wrapped
return wrapper
def memoized_with_argconv(convert_func):
"""Decorator for caching functions which receive unhashable arguments
This decorator is a generalized version of memoized_with_request.
There are cases where argument(s) other than 'request' are also unhashable.
For such cases, such arguments also need to be converted into hashable
variables.
'convert_func' is responsible for replacing unhashable arguments
into corresponding hashable variables.
'convert_func' receives original arguments as its arguments and
it must return a full arguments including converted arguments.
See openstack_dashboard.api.nova as an example.
"""
def wrapper(func):
memoized_func = memoized(func)
@functools.wraps(func)
def wrapped(*args, **kwargs):
args, kwargs = convert_func(*args, **kwargs)
return memoized_func(*args, **kwargs)
return wrapped
return wrapper

View File

@ -36,8 +36,7 @@ from novaclient.v2 import servers as nova_servers
from horizon import exceptions as horizon_exceptions
from horizon.utils import functions as utils
from horizon.utils.memoized import memoized
from horizon.utils.memoized import memoized_with_request
from horizon.utils import memoized
from openstack_dashboard.api import base
from openstack_dashboard.api import microversions
@ -58,7 +57,7 @@ INSECURE = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
CACERT = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
@memoized
@memoized.memoized
def get_microversion(request, features):
client = novaclient(request)
min_ver, max_ver = api_versions._get_server_version_range(client)
@ -267,7 +266,14 @@ def get_auth_params_from_request(request):
)
@memoized_with_request(get_auth_params_from_request)
def _argconv_for_novaclient(request, version=None):
req_param = get_auth_params_from_request(request)
if isinstance(version, api_versions.APIVersion):
version = version.get_string()
return (req_param, version), {}
@memoized.memoized_with_argconv(_argconv_for_novaclient)
def novaclient(request_auth_params, version=None):
(
username,
@ -361,7 +367,7 @@ def flavor_get(request, flavor_id, get_extras=False):
@profiler.trace
@memoized
@memoized.memoized
def flavor_list(request, is_public=True, get_extras=False):
"""Get the list of available instance sizes (flavors)."""
flavors = novaclient(request).flavors.list(is_public=is_public)
@ -397,7 +403,7 @@ def update_pagination(entities, page_size, marker, sort_dir, sort_key,
@profiler.trace
@memoized
@memoized.memoized
def flavor_list_paged(request, is_public=True, get_extras=False, marker=None,
paginate=False, sort_key="name", sort_dir="desc",
reversed_order=False):
@ -427,7 +433,7 @@ def flavor_list_paged(request, is_public=True, get_extras=False, marker=None,
@profiler.trace
@memoized
@memoized.memoized
def flavor_access_list(request, flavor=None):
"""Get the list of access instance sizes (flavors)."""
return novaclient(request).flavor_access.list(flavor=flavor)
@ -1060,7 +1066,7 @@ def interface_detach(request, server, port_id):
@profiler.trace
@memoized_with_request(novaclient)
@memoized.memoized_with_request(novaclient)
def list_extensions(nova_api):
"""List all nova extensions, except the ones in the blacklist."""
blacklist = set(getattr(settings,
@ -1080,7 +1086,7 @@ def _list_extensions_wrap(request):
@profiler.trace
@memoized_with_request(_list_extensions_wrap, 1)
@memoized.memoized_with_request(_list_extensions_wrap, 1)
def extension_supported(extension_name, supported_ext_names):
"""Determine if nova supports a given extension name.