def test_failed_request2(self): """ A request is "failed" if it throws an exception while executing. The exception should be forwarded to ALL waiting requests, which should re-raise it. """ class CustomRuntimeError(RuntimeError): pass def impossible_workload(): time.sleep(0.2) raise CustomRuntimeError("Can't service your request") impossible_req = Request(impossible_workload) def wait_for_impossible(): # This request will fail... impossible_req.wait() # Since there are some exception guards in the code we're testing, # spit something out to stderr just to be sure this error # isn't getting swallowed accidentally. sys.stderr.write("ERROR: Shouldn't get here.") assert False, "Shouldn't get here." req1 = Request(wait_for_impossible) req2 = Request(wait_for_impossible) failed_ids = [] lock = threading.Lock() def handle_failed_req(req_id, failure_exc): assert isinstance(failure_exc, CustomRuntimeError) with lock: failed_ids.append(req_id) req1.notify_failed( partial(handle_failed_req, 1) ) req2.notify_failed( partial(handle_failed_req, 2) ) req1.submit() req2.submit() try: req1.wait() except RuntimeError: pass else: assert False, "Expected an exception from that request, but didn't get it." try: req2.wait() except RuntimeError: pass else: assert False, "Expected an exception from that request, but didn't get it." assert 1 in failed_ids assert 2 in failed_ids
def testExceptionPropagation(self): """ When an exception is generated in a request, the exception should be propagated to all waiting threads. Also, the failure signal should fire. """ class SpecialException(Exception): pass def always_fails(): time.sleep(0.2) raise SpecialException() req1 = Request(always_fails) def wait_for_req1(): req1.wait() req2 = Request(wait_for_req1) req3 = Request(wait_for_req1) signaled_exceptions = [] def failure_handler(ex): signaled_exceptions.append(ex) req2.notify_failed( failure_handler ) req3.notify_failed( failure_handler ) caught_exceptions = [] def wait_for_request(req): try: req.wait() except SpecialException as ex: caught_exceptions.append(ex) except: raise # Got some other exception than the one we expected else: assert "Expected to get an exception. Didn't get one." th2 = threading.Thread( target=partial( wait_for_request, req2 ) ) th3 = threading.Thread( target=partial( wait_for_request, req3 ) ) th2.start() th3.start() th2.join() th3.join() assert len(caught_exceptions) == 2, "Expected both requests to catch exceptions." assert len(signaled_exceptions) == 2, "Expected both requests to signal failure." assert isinstance( caught_exceptions[0], SpecialException ), "Caught exception was of the wrong type." assert caught_exceptions[0] == caught_exceptions[1] == signaled_exceptions[0] == signaled_exceptions[1] # Attempting to wait for a request that has already failed will raise the exception that causes the failure wait_for_request(req2) # Subscribing to notify_failed on a request that's already failed should call the failure handler immediately. req2.notify_failed( failure_handler ) assert len(signaled_exceptions) == 3