with_retry(func, attempts) that returns a new function. When the returned function is called with some argument x, it should call func(x); if func raises a ValueError, it should retry up to attempts total tries. If all attempts raise ValueError, let the final ValueError propagate. If any attempt succeeds, return its result immediately without further retries.calls = []
def flaky(x):
calls.append(x)
if len(calls) < 3:
raise ValueError("fail")
return x * 10
safe = with_retry(flaky, 5)
safe(7)70
The first two calls raise ValueError and are retried; the third call (still within 5 attempts) succeeds and returns 7*10=70.
Use a for loop over range(attempts); inside, wrap the call in try/except ValueError.
On the last iteration, if it still fails, re-raise (let the loop exhaust and the exception propagate naturally by not catching it on the final attempt, or re-raise inside the except when it is the last attempt).