From 9561962765315c83a8be6d47ac2a141061a2e6ad Mon Sep 17 00:00:00 2001 From: Rory Finnegan Date: Tue, 12 Sep 2017 12:27:06 -0500 Subject: [PATCH] Updates for retry changes in 0.6. (#368) * Updates for retry changes in 0.6. * Added retry info to README. --- README.md | 2 ++ src/Compat.jl | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 47 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/README.md b/README.md index 8a60d8803..702c0fdbd 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,8 @@ Currently, the `@compat` macro supports the following syntaxes: * There are versions of `InexactError`, `DomainError`, and `OverflowError` that take the same arguments as introduced in Julia 0.7-DEV ([#20005], [#22751], [#22761]). +* `retry` for the more flexible `retry` method introduced in 0.6 which includes support for kwargs ([#19331], [#21419]). + ## Renamed functions diff --git a/src/Compat.jl b/src/Compat.jl index 4e406b4f5..7661ac3df 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -140,8 +140,65 @@ macro compat(ex...) esc(_compat(ex[1])) end + export @compat +if VERSION < v"0.6.0-dev.2042" + immutable ExponentialBackOff + n::Int + first_delay::Float64 + max_delay::Float64 + factor::Float64 + jitter::Float64 + + function ExponentialBackOff(n, first_delay, max_delay, factor, jitter) + all(x->x>=0, (n, first_delay, max_delay, factor, jitter)) || error("all inputs must be non-negative") + new(n, first_delay, max_delay, factor, jitter) + end + end + + """ + ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) + + A [`Float64`](@ref) iterator of length `n` whose elements exponentially increase at a + rate in the interval `factor` * (1 ± `jitter`). The first element is + `first_delay` and all elements are clamped to `max_delay`. + """ + ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) = + ExponentialBackOff(n, first_delay, max_delay, factor, jitter) + Base.start(ebo::ExponentialBackOff) = (ebo.n, min(ebo.first_delay, ebo.max_delay)) + function Base.next(ebo::ExponentialBackOff, state) + next_n = state[1]-1 + curr_delay = state[2] + next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (rand() * 2.0 * ebo.jitter))) + (curr_delay, (next_n, next_delay)) + end + Base.done(ebo::ExponentialBackOff, state) = state[1]<1 + Base.length(ebo::ExponentialBackOff) = ebo.n + + function retry(f::Function; delays=ExponentialBackOff(), check=nothing) + (args...; kwargs...) -> begin + state = start(delays) + while true + try + return f(args...; kwargs...) + catch e + done(delays, state) && rethrow(e) + if check !== nothing + state, retry_or_not = check(state, e) + retry_or_not || rethrow(e) + end + end + (delay, state) = next(delays, state) + sleep(delay) + end + end + end +else + import Base.ExponentialBackOff + import Base.retry +end + import Base: redirect_stdin, redirect_stdout, redirect_stderr if VERSION < v"0.6.0-dev.374" for (F,S) in ((:redirect_stdin, :STDIN), (:redirect_stdout, :STDOUT), (:redirect_stderr, :STDERR)) diff --git a/test/runtests.jl b/test/runtests.jl index cefaa0263..7fa9a0f84 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -105,6 +105,53 @@ cd(dirwalk) do end rm(dirwalk, recursive=true) +let + # Subset of tests copied from base test/error.jl + function foo_error(c, n) + c[1] += 1 + if c[1] <= n + error("foo") + end + return 7 + end + + # Success on first attempt + c = [0] + @test Compat.retry(foo_error)(c, 0) == 7 + @test c[1] == 1 + + # Success on second attempt + c = [0] + @test Compat.retry(foo_error)(c,1) == 7 + @test c[1] == 2 + + # 2 failed retry attempts, so exception is raised + c = [0] + ex = try + Compat.retry(foo_error, delays=Compat.ExponentialBackOff(n=2))(c, 3) + catch e + e + end + + c = [0] + ex = try + Compat.retry( + foo_error, + check=(s,e)->(s, try e.http_status_code == "503" end != true) + )(c, 2) + catch e + e + end + @test typeof(ex) == ErrorException + @test ex.msg == "foo" + @test c[1] == 2 + + # Functions with keyword arguments + foo_kwargs(x; y=5) = x + y + @test Compat.retry(foo_kwargs)(3) == 8 + @test Compat.retry(foo_kwargs)(3; y=4) == 7 +end + for os in [:apple, :bsd, :linux, :unix, :windows] from_base = if VERSION >= v"0.7.0-DEV.914" Expr(:., Expr(:., :Base, Base.Meta.quot(:Sys)), Base.Meta.quot(Symbol("is", os)))