def test_deep_recursion(): def fact(n, result=1): if n <= 1: returnValue(result) else: noreturn(fact(n - 1, n * result)) yield assert deferred_result(inlineCallbacks(fact)(1)) == 1 assert deferred_result(inlineCallbacks(fact)(10)) == safe_fact(10) with recursion_limit(100): with assert_not_raises(RuntimeError): # +10 is actually too high here as we probably already have some stuff on the stack, but just to be sure assert deferred_result(inlineCallbacks(fact)(110)) == safe_fact(110) # ...and now let's prove that the same (tail call optimizable) algorithm without noreturn will eat up the stack def normal_fact(n, result=1): if n <= 1: returnValue(result) else: return normal_fact(n - 1, n * result) with recursion_limit(100): with assert_raises(RuntimeError): normal_fact(110)
def test_noreturn_with_regular_function(): after_noreturn_reached = [False] @inlineCallbacks def fn(): yield noreturn(fn2()) after_noreturn_reached[0] = True def fn2(): return "someretval" retval = deferred_result(fn()) assert not after_noreturn_reached[0] assert retval == "someretval"
def test_noreturn_of_other_inlineCallbacks_wrapped_callable(): after_noreturn_reached = [False] @inlineCallbacks def fn(): yield noreturn(fn2()) after_noreturn_reached[0] = True fn2_called = [False] @inlineCallbacks def fn2(): fn2_called[0] = True yield returnValue("someretval") retval = deferred_result(fn()) assert fn2_called[0] assert not after_noreturn_reached[0] assert retval == "someretval"