Merge "Fix wrap_exception to get all arguments for payload"

This commit is contained in:
Jenkins 2015-12-14 19:44:21 +00:00 committed by Gerrit Code Review
commit cbfae225d9
8 changed files with 90 additions and 84 deletions

View File

@ -287,7 +287,7 @@ def errors_out_migration(function):
return function(self, context, *args, **kwargs)
except Exception as ex:
with excutils.save_and_reraise_exception():
wrapped_func = utils.get_wrapped_function(function)
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = safe_utils.getcallargs(wrapped_func, self,
context, *args, **kwargs)
migration = keyed_args['migration']
@ -329,7 +329,7 @@ def reverts_task_state(function):
e.format_message())
except Exception:
with excutils.save_and_reraise_exception():
wrapped_func = utils.get_wrapped_function(function)
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = safe_utils.getcallargs(wrapped_func, self,
context, *args, **kwargs)
# NOTE(mriedem): 'instance' must be in keyed_args because we
@ -389,7 +389,7 @@ def wrap_instance_event(function):
@functools.wraps(function)
def decorated_function(self, context, *args, **kwargs):
wrapped_func = utils.get_wrapped_function(function)
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = safe_utils.getcallargs(wrapped_func, self, context, *args,
**kwargs)
instance_uuid = keyed_args['instance']['uuid']

View File

@ -90,8 +90,10 @@ def wrap_exception(notifier=None, get_notifier=None):
with excutils.save_and_reraise_exception():
if notifier or get_notifier:
payload = dict(exception=e)
call_dict = safe_utils.getcallargs(f, self, context,
*args, **kw)
wrapped_func = safe_utils.get_wrapped_function(f)
call_dict = safe_utils.getcallargs(wrapped_func, self,
context, *args,
**kw)
# self can't be serialized and shouldn't be in the
# payload
call_dict.pop('self', None)

View File

@ -49,3 +49,24 @@ def getcallargs(function, *args, **kwargs):
keyed_args[argname] = value
return keyed_args
def get_wrapped_function(function):
"""Get the method at the bottom of a stack of decorators."""
if not hasattr(function, '__closure__') or not function.__closure__:
return function
def _get_wrapped_function(function):
if not hasattr(function, '__closure__') or not function.__closure__:
return None
for closure in function.__closure__:
func = closure.cell_contents
deeper_func = _get_wrapped_function(func)
if deeper_func:
return deeper_func
elif hasattr(closure.cell_contents, '__call__'):
return closure.cell_contents
return _get_wrapped_function(function)

View File

@ -9458,6 +9458,7 @@ class ComputeAPITestCase(BaseTestCase):
def test_attach_interface_failed(self):
new_type = flavors.get_flavor_by_flavor_id('4')
instance = objects.Instance(
id=42,
uuid='f0000000-0000-0000-0000-000000000000',
image_ref='foo',
system_metadata={},
@ -9500,7 +9501,7 @@ class ComputeAPITestCase(BaseTestCase):
def test_detach_interface_failed(self):
nwinfo, port_id = self.test_attach_interface()
instance = objects.Instance()
instance = objects.Instance(id=42)
instance['uuid'] = 'f6000000-0000-0000-0000-000000000000'
instance.info_cache = objects.InstanceInfoCache.new(
self.context, 'f6000000-0000-0000-0000-000000000000')
@ -9524,7 +9525,7 @@ class ComputeAPITestCase(BaseTestCase):
# Tests that when deallocate_port_for_instance fails we log the failure
# before exiting compute.detach_interface.
nwinfo, port_id = self.test_attach_interface()
instance = objects.Instance(uuid=uuidutils.generate_uuid())
instance = objects.Instance(id=42, uuid=uuidutils.generate_uuid())
instance.info_cache = objects.InstanceInfoCache.new(
self.context, 'f6000000-0000-0000-0000-000000000000')
instance.info_cache.network_info = network_model.NetworkInfo.hydrate(
@ -9558,6 +9559,7 @@ class ComputeAPITestCase(BaseTestCase):
block_device_obj.BlockDeviceMapping(),
fake_bdm)
instance = self._create_fake_instance_obj()
instance.id = 42
fake_volume = {'id': 'fake-volume-id'}
with test.nested(

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
from nova import safe_utils
from nova import test
@ -108,3 +110,55 @@ class GetCallArgsTestCase(test.NoDBTestCase):
self.assertEqual(1, len(callargs))
self.assertIn('instance', callargs)
self.assertEqual({'uuid': 1}, callargs['instance'])
class WrappedCodeTestCase(test.NoDBTestCase):
"""Test the get_wrapped_function utility method."""
def _wrapper(self, function):
@functools.wraps(function)
def decorated_function(self, *args, **kwargs):
function(self, *args, **kwargs)
return decorated_function
def test_single_wrapped(self):
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = safe_utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)
def test_double_wrapped(self):
@self._wrapper
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = safe_utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)
def test_triple_wrapped(self):
@self._wrapper
@self._wrapper
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = safe_utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)

View File

@ -13,7 +13,6 @@
# under the License.
import datetime
import functools
import hashlib
import importlib
import logging
@ -847,58 +846,6 @@ class MetadataToDictTestCase(test.NoDBTestCase):
self.assertEqual(utils.dict_to_metadata({}), [])
class WrappedCodeTestCase(test.NoDBTestCase):
"""Test the get_wrapped_function utility method."""
def _wrapper(self, function):
@functools.wraps(function)
def decorated_function(self, *args, **kwargs):
function(self, *args, **kwargs)
return decorated_function
def test_single_wrapped(self):
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)
def test_double_wrapped(self):
@self._wrapper
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)
def test_triple_wrapped(self):
@self._wrapper
@self._wrapper
@self._wrapper
def wrapped(self, instance, red=None, blue=None):
pass
func = utils.get_wrapped_function(wrapped)
func_code = func.__code__
self.assertEqual(4, len(func_code.co_varnames))
self.assertIn('self', func_code.co_varnames)
self.assertIn('instance', func_code.co_varnames)
self.assertIn('red', func_code.co_varnames)
self.assertIn('blue', func_code.co_varnames)
class ExpectedArgsTestCase(test.NoDBTestCase):
def test_passes(self):
@utils.expects_func_args('foo', 'baz')

View File

@ -23,9 +23,9 @@ import mock
from os_win import exceptions as os_win_exc
from nova import exception
from nova import safe_utils
from nova.tests.unit import fake_instance
from nova.tests.unit.virt.hyperv import test_base
from nova import utils
from nova.virt import driver as base_driver
from nova.virt.hyperv import driver
@ -66,7 +66,7 @@ class HyperVDriverTestCase(test_base.HyperVBaseTestCase):
if callable(class_member):
mocked_method = mock.patch.object(
driver.HyperVDriver, attr,
utils.get_wrapped_function(class_member))
safe_utils.get_wrapped_function(class_member))
mocked_method.start()
self.addCleanup(mocked_method.stop)

View File

@ -57,6 +57,7 @@ from six.moves import range
from nova import exception
from nova.i18n import _, _LE, _LI, _LW
from nova import safe_utils
notify_decorator = 'nova.notifications.notify_decorator'
@ -1041,32 +1042,11 @@ def instance_sys_meta(instance):
include_deleted=True)
def get_wrapped_function(function):
"""Get the method at the bottom of a stack of decorators."""
if not hasattr(function, '__closure__') or not function.__closure__:
return function
def _get_wrapped_function(function):
if not hasattr(function, '__closure__') or not function.__closure__:
return None
for closure in function.__closure__:
func = closure.cell_contents
deeper_func = _get_wrapped_function(func)
if deeper_func:
return deeper_func
elif hasattr(closure.cell_contents, '__call__'):
return closure.cell_contents
return _get_wrapped_function(function)
def expects_func_args(*args):
def _decorator_checker(dec):
@functools.wraps(dec)
def _decorator(f):
base_f = get_wrapped_function(f)
base_f = safe_utils.get_wrapped_function(f)
arg_names, a, kw, _default = inspect.getargspec(base_f)
if a or kw or set(args) <= set(arg_names):
# NOTE (ndipanov): We can't really tell if correct stuff will