def entrypoint_waiter(container, entrypoint, timeout=30): """Helper to wait for entrypoints to fire (and complete) Usage:: container = ServiceContainer(ExampleService, config) with entrypoint_waiter(container, 'example_handler'): ... # e.g. rpc call that will result in handler being called """ waiter = EntrypointWaiter(entrypoint) if not get_extension(container, Entrypoint, method_name=entrypoint): raise RuntimeError("{} has no entrypoint `{}`".format( container.service_name, entrypoint)) if get_extension(container, EntrypointWaiter, entrypoint=entrypoint): raise RuntimeError( "Waiter already registered for {}".format(entrypoint)) # can't mess with dependencies while container is running wait_for_worker_idle(container) container.dependencies.add(waiter) try: yield with eventlet.Timeout(timeout): waiter.wait() finally: wait_for_worker_idle(container) container.dependencies.remove(waiter)
def entrypoint_waiter(container, entrypoint, timeout=30): """Helper to wait for entrypoints to fire (and complete) Usage:: container = ServiceContainer(ExampleService, config) with entrypoint_waiter(container, 'example_handler'): ... # e.g. rpc call that will result in handler being called """ waiter = EntrypointWaiter(entrypoint) if not get_extension(container, Entrypoint, method_name=entrypoint): raise RuntimeError("{} has no entrypoint `{}`".format( container.service_name, entrypoint)) if get_extension(container, EntrypointWaiter, entrypoint=entrypoint): raise RuntimeError("Waiter already registered for {}".format( entrypoint)) # can't mess with dependencies while container is running wait_for_worker_idle(container) container.dependencies.add(waiter) try: yield exc = waiter.Timeout( "Entrypoint {}.{} failed to complete within {} seconds".format( container.service_name, entrypoint, timeout) ) with eventlet.Timeout(timeout, exception=exc): waiter.wait() finally: wait_for_worker_idle(container) container.dependencies.remove(waiter)
def test_expected_exceptions_integration(container_factory, rabbit_config): container = container_factory(ExampleService, rabbit_config) container.start() worker_logger = get_dependency(container, WorkerErrorLogger) with entrypoint_hook(container, 'broken') as broken: with pytest.raises(ExampleError): broken() with entrypoint_hook(container, 'very_broken') as very_broken: with pytest.raises(AttributeError): very_broken() wait_for_worker_idle(container) # wait for worker lifecycle to complete assert worker_logger.expected == {'broken': ExampleError} assert worker_logger.unexpected == {'very_broken': AttributeError}
def test_expected_exceptions_integration(container_factory, rabbit_config): container = container_factory(ExampleService, rabbit_config) container.start() worker_logger = get_extension(container, WorkerErrorLogger) with entrypoint_hook(container, 'broken') as broken: with pytest.raises(ExampleError): broken() with entrypoint_hook(container, 'very_broken') as very_broken: with pytest.raises(AttributeError): very_broken() wait_for_worker_idle(container) # wait for worker lifecycle to complete assert worker_logger.expected == {'broken': ExampleError} assert worker_logger.unexpected == {'very_broken': AttributeError}
def test_handle_result(container_factory, rabbit_manager, rabbit_config): """ Verify that `handle_result` can modify the return values of the worker, such that other dependencies see the updated values. """ container = container_factory(ExampleService, rabbit_config) container.start() with RpcProxy('exampleservice', rabbit_config) as proxy: assert proxy.echo("hello") == "hello" with pytest.raises(RemoteError) as exc: proxy.unserializable() assert "is not JSON serializable" in exc.value.message wait_for_worker_idle(container) # verify CollectorInjection sees values returned from `handle_result` assert worker_result_called == [ ("hello", None), ("something went wrong", (TypeError, ANY, ANY)), ]
def test_wait_for_worker_idle(container_factory, rabbit_config): event = Event() class Service(object): name = "service" @rpc def wait_for_event(self): event.wait() container = container_factory(Service, rabbit_config) container.start() max_workers = DEFAULT_MAX_WORKERS # verify nothing running assert container._worker_pool.free() == max_workers with eventlet.Timeout(1): wait_for_worker_idle(container) # spawn a worker wait_for_event = get_extension(container, Rpc) container.spawn_worker(wait_for_event, [], {}) # verify that wait_for_worker_idle does not return while worker active assert container._worker_pool.free() == max_workers - 1 gt = eventlet.spawn(wait_for_worker_idle, container) assert not gt.dead # still waiting # verify that wait_for_worker_idle raises when it times out with pytest.raises(eventlet.Timeout): wait_for_worker_idle(container, timeout=0) # complete the worker, verify previous wait_for_worker_idle completes event.send() with eventlet.Timeout(1): gt.wait() assert container._worker_pool.free() == max_workers
def test_handle_result(container_factory, rabbit_manager, rabbit_config): """ Verify that `handle_result` can modify the return values of the worker, such that other dependencies see the updated values. """ container = container_factory(ExampleService, rabbit_config) container.start() with ServiceRpcProxy('exampleservice', rabbit_config) as proxy: assert proxy.echo("hello") == "hello" with pytest.raises(RemoteError) as exc: proxy.unserializable() assert "is not JSON serializable" in str(exc.value) wait_for_worker_idle(container) # verify ResultCollector sees values returned from `handle_result` assert worker_result_called == [ ("hello", None), ("something went wrong", (TypeError, ANY, ANY)), ]
def test_wait_for_worker_idle(container_factory, rabbit_config): event = Event() class Service(object): name = "service" @rpc def wait_for_event(self): event.wait() container = container_factory(Service, rabbit_config) container.start() max_workers = DEFAULT_MAX_WORKERS # TODO: pytest.warns is not supported until pytest >= 2.8.0, whose # `testdir` plugin is not compatible with eventlet on python3 -- # see https://github.com/mattbennett/eventlet-pytest-bug with warnings.catch_warnings(record=True) as ws: wait_for_worker_idle(container) assert len(ws) == 1 assert issubclass(ws[-1].category, DeprecationWarning) # verify nothing running assert container._worker_pool.free() == max_workers with eventlet.Timeout(1): wait_for_worker_idle(container) # spawn a worker wait_for_event = get_extension(container, Rpc) container.spawn_worker(wait_for_event, [], {}) # verify that wait_for_worker_idle does not return while worker active assert container._worker_pool.free() == max_workers - 1 gt = eventlet.spawn(wait_for_worker_idle, container) assert not gt.dead # still waiting # verify that wait_for_worker_idle raises when it times out with pytest.raises(eventlet.Timeout): wait_for_worker_idle(container, timeout=0) # complete the worker, verify previous wait_for_worker_idle completes event.send() with eventlet.Timeout(1): gt.wait() assert container._worker_pool.free() == max_workers