def test_no_decorator(self): expected = inspect.getargspec(self.controller.index.__func__) actual = util.getargspec(self.controller.index.__func__) assert expected == actual expected = inspect.getargspec(self.controller.static_index) actual = util.getargspec(self.controller.static_index) assert expected == actual
def test_simple_decorator(self): def dec(f): return f expected = inspect.getargspec(self.controller.index.__func__) actual = util.getargspec(dec(self.controller.index.__func__)) assert expected == actual expected = inspect.getargspec(self.controller.static_index) actual = util.getargspec(dec(self.controller.static_index)) assert expected == actual
def test_simple_wrapper(self): def dec(f): @functools.wraps(f) def wrapped(*a, **kw): return f(*a, **kw) return wrapped expected = inspect.getargspec(self.controller.index.__func__) actual = util.getargspec(dec(self.controller.index.__func__)) assert expected == actual expected = inspect.getargspec(self.controller.static_index) actual = util.getargspec(dec(self.controller.static_index)) assert expected == actual
def test_decorator_with_args(self): def dec(flag): def inner(f): @functools.wraps(f) def wrapped(*a, **kw): return f(*a, **kw) return wrapped return inner expected = inspect.getargspec(self.controller.index.__func__) actual = util.getargspec(dec(True)(self.controller.index.__func__)) assert expected == actual expected = inspect.getargspec(self.controller.static_index) actual = util.getargspec(dec(True)( self.controller.static_index)) assert expected == actual
def _test_db_exceptions_handled(self, method, mock_object, receives_context, expect_retries): # NOTE(mpeterson): this test is very verbose, disabling logging logging.disable(logging.CRITICAL) self.addCleanup(logging.disable, logging.NOTSET) exceptions = RETRIABLE_EXCEPTIONS r_method = getattr(method, '__wrapped__', method) r_method_args = p_util.getargspec(r_method).args args_number = len(r_method_args) - (2 if r_method_args[0] == 'self' else 1) mock_arg = mock.MagicMock(unsafe=True) # NOTE(mpeterson): workarounds for py3 compatibility and behavior # expected by particular functions mock_arg.__name__ = 'mock_arg' mock_arg.retry_count = 1 mock_arg.__ge__.return_value = True mock_arg.__gt__.return_value = True mock_arg.__le__.return_value = True mock_arg.__lt__.return_value = True args = (mock_arg, ) * args_number def _assertRaises(exceptions, method, *args, **kwargs): try: method(*args, **kwargs) except Exception as e: if not isinstance(e, exceptions): raise e session = args[0].session if receives_context else args[0] if session.is_active and isinstance(e, _InnerException): self.assertTrue(getattr(e, '_RETRY_EXCEEDED', False)) return exc_names = (tuple(exc.__name__ for exc in exceptions) if hasattr( exceptions, '__iter__') else exceptions.__name__) self.fail('%s did not raise %s' % (method.__name__, exc_names)) try: raise _InnerException except _InnerException as e: _e = e expected_retries = RETRY_MAX if expect_retries else 0 db_object = self.db_context if receives_context else self.db_session for exception in exceptions: mock_object.side_effect = exception(_e) _assertRaises((exception, _InnerException), method, db_object, *args) self.assertEqual(expected_retries, mock_object.call_count - 1) mock_object.reset_mock()
def decorator(f): try: # NOTE(kevinbenton): we use pecan's util function here because it # deals with the horrors of finding args of already decorated # functions ctx_arg_index = p_util.getargspec(f).args.index(context_var_name) except ValueError as e: msg = _("Could not find position of var %s") % context_var_name raise RuntimeError(msg) from e f_with_retry = retry_db_errors(f) @functools.wraps(f) def wrapped(*args, **kwargs): # only use retry wrapper if we aren't nested in an active # transaction context = kwargs.get(context_var_name) if context is None: context = args[ctx_arg_index] method = f if context.session.is_active else f_with_retry return method(*args, **kwargs) return wrapped
def decorator(f): try: # NOTE(kevinbenton): we use pecan's util function here because it # deals with the horrors of finding args of already decorated # functions ctx_arg_index = p_util.getargspec(f).args.index(context_var_name) except ValueError: raise RuntimeError("Could not find position of var %s" % context_var_name) f_with_retry = retry_db_errors(f) @six.wraps(f) def wrapped(*args, **kwargs): # only use retry wrapper if we aren't nested in an active # transaction if context_var_name in kwargs: context = kwargs[context_var_name] else: context = args[ctx_arg_index] method = f if context.session.is_active else f_with_retry return method(*args, **kwargs) return wrapped
def _test_db_exceptions_handled(self, method, mock_object, expect_retries): # NOTE(mpeterson): make retries faster so it doesn't take a lot. retry_fixture = lib_fixtures.DBRetryErrorsFixture( max_retries=RETRY_MAX, retry_interval=RETRY_INTERVAL) retry_fixture.setUp() # NOTE(mpeterson): this test is very verbose, disabling logging logging.disable(logging.CRITICAL) self.addCleanup(logging.disable, logging.NOTSET) exceptions = RETRIABLE_EXCEPTIONS r_method = getattr(method, '__wrapped__', method) r_method_args = p_util.getargspec(r_method).args args_number = len(r_method_args) - (2 if r_method_args[0] == 'self' else 1) mock_arg = mock.MagicMock(unsafe=True) # NOTE(mpeterson): workarounds for py3 compatibility and behavior # expected by particular functions mock_arg.__name__ = 'mock_arg' mock_arg.retry_count = 1 mock_arg.__ge__.return_value = True mock_arg.__gt__.return_value = True mock_arg.__le__.return_value = True mock_arg.__lt__.return_value = True args = (mock_arg,) * args_number def _assertRaises(exceptions, method, context, *args, **kwargs): try: method(context, *args, **kwargs) except Exception as e: if not isinstance(e, exceptions): raise e # TODO(mpeterson): For now the check with session.is_active is # accepted, but when the enginefacade is the only accepted # pattern then it should be changed to check that a session is # attached to the context session = context.session if session.is_active and isinstance(e, _InnerException): self.assertTrue(getattr(e, '_RETRY_EXCEEDED', False)) return exc_names = (tuple(exc.__name__ for exc in exceptions) if hasattr(exceptions, '__iter__') else exceptions.__name__) self.fail('%s did not raise %s' % (method.__name__, exc_names)) try: raise _InnerException except _InnerException as e: _e = e expected_retries = RETRY_MAX if expect_retries else 0 # TODO(mpeterson): Make this an int when Py2 is no longer supported # and use the `nonlocal` directive retry_counter = [0] for exception in exceptions: def increase_retry_counter_and_except(*args, **kwargs): retry_counter[0] += 1 self.db_context.session.add(self.retry_tracker()) self.db_context.session.flush() raise exception(_e) mock_object.side_effect = increase_retry_counter_and_except _assertRaises((exception, _InnerException), method, self.db_context, *args) self.assertEqual(expected_retries, mock_object.call_count - 1) mock_object.reset_mock() retry_fixture.cleanUp() return retry_counter[0]
def _test_db_exceptions_handled(self, method, mock_object, expect_retries): # NOTE(mpeterson): make retries faster so it doesn't take a lot. retry_fixture = lib_fixtures.DBRetryErrorsFixture( max_retries=RETRY_MAX, retry_interval=RETRY_INTERVAL) retry_fixture.setUp() # NOTE(mpeterson): this test is very verbose, disabling logging logging.disable(logging.CRITICAL) self.addCleanup(logging.disable, logging.NOTSET) exceptions = RETRIABLE_EXCEPTIONS r_method = getattr(method, '__wrapped__', method) r_method_args = p_util.getargspec(r_method).args args_number = len(r_method_args) - (2 if r_method_args[0] == 'self' else 1) mock_arg = mock.MagicMock(unsafe=True) # NOTE(mpeterson): workarounds for py3 compatibility and behavior # expected by particular functions mock_arg.__name__ = 'mock_arg' mock_arg.retry_count = 1 mock_arg.__ge__.return_value = True mock_arg.__gt__.return_value = True mock_arg.__le__.return_value = True mock_arg.__lt__.return_value = True args = (mock_arg, ) * args_number def _assertRaises(exceptions, method, context, *args, **kwargs): try: method(context, *args, **kwargs) except Exception as e: if not isinstance(e, exceptions): raise e # TODO(mpeterson): For now the check with session.is_active is # accepted, but when the enginefacade is the only accepted # pattern then it should be changed to check that a session is # attached to the context session = context.session if session.is_active and isinstance(e, _InnerException): self.assertTrue(getattr(e, '_RETRY_EXCEEDED', False)) return exc_names = (tuple(exc.__name__ for exc in exceptions) if hasattr( exceptions, '__iter__') else exceptions.__name__) self.fail('%s did not raise %s' % (method.__name__, exc_names)) try: raise _InnerException except _InnerException as e: _e = e expected_retries = RETRY_MAX if expect_retries else 0 # TODO(mpeterson): Make this an int when Py2 is no longer supported # and use the `nonlocal` directive retry_counter = [0] for exception in exceptions: def increase_retry_counter_and_except(*args, **kwargs): retry_counter[0] += 1 self.db_context.session.add(self.retry_tracker()) self.db_context.session.flush() raise exception(_e) mock_object.side_effect = increase_retry_counter_and_except _assertRaises((exception, _InnerException), method, self.db_context, *args) self.assertEqual(expected_retries, mock_object.call_count - 1) mock_object.reset_mock() retry_fixture.cleanUp() return retry_counter[0]