def test_wait_until_true_with_timeout(mock_time_sleep): """wait_until_true with dynamic invocation, exhaust wait attempts.""" mock = MagicMock() mock.side_effect = [ 'test string 1', 'test string 2', 'test string 3', 'test string 4', 'test string 5', 'test string 6', 'test string 7', 'test string 8', 'test string 9', 'test string 10', 'test string 11', ] def decorate_me(arg1, arg2): """Test static decorator syntax""" assert arg1 == 'v1' assert arg2 == 'v2' if mock(arg1) == 'expected value': return True else: return False assert not poll.wait_until_true(interval=0.01, max_attempts=10)(decorate_me)('v1', 'v2') assert mock.call_count == 10 mock.assert_called_with('v1') assert mock_time_sleep.call_count == 9 mock_time_sleep.assert_called_with(0.01)
def test_wait_until_true_once_not_found(mock_time_sleep): """wait_until_true max_attempts 1.""" mock = MagicMock() mock.side_effect = [ 'test string 1', 'test string 2', ] def decorate_me(arg1, arg2): """Test static decorator syntax.""" assert arg1 == 'v1' assert arg2 == 'v2' return mock(arg1) == 'expected value' assert not poll.wait_until_true(interval=0.01, max_attempts=1)( decorate_me)('v1', 'v2') mock.assert_called_once_with('v1') mock_time_sleep.assert_not_called()
def test_wait_until_true_invoke_inline(mock_time_sleep): """wait_until_true with dynamic invocation.""" mock = MagicMock() mock.side_effect = [ 'test string 1', 'test string 2', 'test string 3', 'expected value', 'test string 5' ] def decorate_me(arg1, arg2): """Test static decorator syntax""" assert arg1 == 'v1' assert arg2 == 'v2' if mock(arg1) == 'expected value': return True else: return False assert poll.wait_until_true(interval=0.01, max_attempts=10)(decorate_me)('v1', 'v2') assert mock.call_count == 4 mock.assert_called_with('v1') assert mock_time_sleep.call_count == 3 mock_time_sleep.assert_called_with(0.01)
def run_step(context): """Custom waiter for any aws client operation. All of the awsWaitFor descendant values support {key} string interpolation, except waitForField. Args: context: Dictionary. Mandatory. Requires the following context keys in context: - awsWaitFor. dict. mandatory. Contains keys: - awsClientIn. dict. mandatory. This is the same as for the pypyraws.steps.client in step. Contains keys: - serviceName: mandatory. String for service name. Available services here: http://boto3.readthedocs.io/en/latest/reference/services/ - methodName: mandatory. String. Name of method to execute. - clientArgs: optional. Dict. kwargs for the boto client ctor. - methodArgs: optional. Dict. kwargs for the client method call - waitForField: mandatory. string. format expression for field name to check in awsClient response. - toBe: mandatory. string. string. Stop waiting when waitForField equals this value. - pollInterval: optional. int. In seconds. Default to 30. - maxAttempts: optional. int. Default 10. - errorOnWaitTimeout: optional. Default True. Throws error if maxAttempts exhausted without reaching toBe value. If false, step completes without raising error. Returns: None Adds key to context: - awsWaitForTimedOut: bool. Adds key with value True if errorOnWaitTimeout=False and max_attempts exhausted without reaching toBe. If steps completes successfully and waitForField's value becomes toBe, awsWaitForTimedOut == False. Raises: pypyr.errors.KeyNotInContextError: awsWaitFor missing in context. pypyr.errors.KeyInContextHasNoValueError: awsWaitFor exists but is None. pypyraws.errors.WaitTimeOut: maxAttempts exceeded without waitForField changing to toBe. """ logger.debug("started") context.assert_key_has_value('awsWaitFor', __name__) wait_for = context['awsWaitFor'] client_in, service_name, method_name = contextargs.get_awsclient_args( wait_for, __name__) service_name = context.get_formatted_string(service_name) method_name = context.get_formatted_string(method_name) client_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='clientArgs', context=context) method_args = contextargs.get_formatted_iterable(input_dict=client_in, field_name='methodArgs', context=context) (wait_for_field, to_be, poll_interval, max_attempts, error_on_wait_timeout) = get_poll_args(wait_for, context) wait_response = wait_until_true( interval=poll_interval, max_attempts=max_attempts)(execute_aws_client_method)( service_name=service_name, method_name=method_name, client_args=client_args, method_args=method_args, wait_for_field=wait_for_field, to_be=to_be) if wait_response: context['awsWaitForTimedOut'] = False logger.info(f"aws {service_name} {method_name} returned {to_be}. " "Pipeline will now continue.") else: if error_on_wait_timeout: context['awsWaitForTimedOut'] = True logger.error(f"aws {service_name} {method_name} did not return " f"{to_be} within {max_attempts}. errorOnWaitTimeout " "is True, throwing error") raise WaitTimeOut(f"aws {service_name} {method_name} did not " f"return {to_be} within {max_attempts} retries.") else: context['awsWaitForTimedOut'] = True logger.warn(f"aws {service_name} {method_name} did NOT return " f" {to_be}. errorOnWaitTimeout is False, so pipeline " "will proceed to the next step anyway.") logger.debug("done")