Skip to content

Commit 41a317d

Browse files
committed
add retry and @catch per
JuliaLang#14843
1 parent c96f322 commit 41a317d

File tree

5 files changed

+132
-1
lines changed

5 files changed

+132
-1
lines changed

base/error.jl

+41
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,44 @@ macro assert(ex, msgs...)
4949
end
5050
:($(esc(ex)) ? $(nothing) : throw(Main.Base.AssertionError($msg)))
5151
end
52+
53+
54+
"""
55+
retry(f, [condition], n=3)
56+
57+
Returns a lambda that retries function `f` up to `n` times in the
58+
event of an exception. If `condition` is a `Type` then retry only
59+
for exceptions of that type. If `condition` is a function
60+
`cond(::Exception) -> Bool` then retry only if it is true.
61+
62+
e.g. `retry(http_get, e->e.status == "503")(url)` or `retry(read, UVError)(io)`.
63+
"""
64+
function retry(f::Function, condition::Function=e->true, n::Int=3)
65+
(args...) -> begin
66+
for i = 1:n
67+
try
68+
return f(args...)
69+
catch e
70+
if i == n || try condition(e) end != true
71+
rethrow(e)
72+
end
73+
end
74+
end
75+
end
76+
end
77+
78+
retry(f::Function, condition::Type, n::Int=3) = retry(f, e->isa(e, condition), n)
79+
80+
81+
"""
82+
@catch(f)
83+
84+
Returns a lambda that executes `f` and returns either the result of `f` or
85+
an `Exception` thrown by `f`.
86+
87+
e.g. `@catch(length)() == MethodError(length,())`
88+
`@catch(length)([1,2,3]) == 3`
89+
"""
90+
macro catch(f)
91+
:((args...) -> try $(esc(f))(args...) catch ex; ex end)
92+
end

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,11 @@ export
10441044
# errors
10451045
assert,
10461046
backtrace,
1047+
@catch,
10471048
catch_backtrace,
10481049
error,
10491050
rethrow,
1051+
retry,
10501052
systemerror,
10511053

10521054
# stack traces

doc/stdlib/base.rst

+16
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,22 @@ Errors
12181218
12191219
An error occurred when running a module's ``__init__`` function. The actual error thrown is available in the ``.error`` field.
12201220

1221+
.. function:: retry(f, [condition], n=3)
1222+
1223+
.. Docstring generated from Julia source
1224+
1225+
Returns a lambda that retries function ``f`` up to ``n`` times in the event of an exception. If ``condition`` is a ``Type`` then retry only for exceptions of that type. If ``condition`` is a function ``cond(::Exception) -> Bool`` then retry only if it is true.
1226+
1227+
e.g. ``retry(http_get, e->e.status == "503")(url)`` or ``retry(read, UVError)(io)``\ .
1228+
1229+
.. function:: @catch(f)
1230+
1231+
.. Docstring generated from Julia source
1232+
1233+
Returns a lambda that executes ``f`` and returns either the result of ``f`` or an ``Exception`` thrown by ``f``\ .
1234+
1235+
e.g. ``@catch(length)() == MethodError(length,())`` ``@catch(length)([1,2,3]) == 3``
1236+
12211237
Events
12221238
------
12231239

test/choosetests.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function choosetests(choices = [])
3333
"markdown", "base64", "serialize", "functors", "misc", "threads",
3434
"enums", "cmdlineargs", "i18n", "workspace", "libdl", "int",
3535
"checked", "intset", "floatfuncs", "compile", "parallel", "inline",
36-
"boundscheck"
36+
"boundscheck", "error"
3737
]
3838

3939
if Base.USE_GPL_LIBS

test/error.jl

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# This file is a part of Julia. License is MIT: http://julialang.org/license
2+
3+
4+
@test map(typeof, map(@catch(i->[1,2,3][i]), 1:6)) ==
5+
[Int, Int, Int, BoundsError, BoundsError, BoundsError]
6+
7+
@test typeof(@catch(open)("/no/file/with/this/name")) == SystemError
8+
9+
10+
let
11+
12+
function f(c, n)
13+
c[1] += 1
14+
if c[1] <= n
15+
error("foo")
16+
end
17+
return 7
18+
end
19+
20+
# Success on first attempt...
21+
c = [0]
22+
@test retry(f)(c,0) == 7
23+
@test c[1] == 1
24+
25+
# Success on second attempt...
26+
c = [0]
27+
@test retry(f)(c,1) == 7
28+
@test c[1] == 2
29+
30+
# Success on third attempt...
31+
c = [0]
32+
@test retry(f)(c,2) == 7
33+
@test c[1] == 3
34+
35+
# 3 failed attempts, so exception is raised...
36+
c = [0]
37+
ex = @catch(retry(f))(c,3)
38+
@test ex.msg == "foo"
39+
@test c[1] == 3
40+
41+
c = [0]
42+
ex = @catch(retry(f, ErrorException))(c,3)
43+
@test typeof(ex) == ErrorException
44+
@test ex.msg == "foo"
45+
@test c[1] == 3
46+
47+
c = [0]
48+
ex = @catch(retry(f, e->e.msg == "foo"))(c,3)
49+
@test typeof(ex) == ErrorException
50+
@test ex.msg == "foo"
51+
@test c[1] == 3
52+
53+
# No retry if condition does not match...
54+
c = [0]
55+
ex = @catch(retry(f, e->e.msg == "bar"))(c,3)
56+
@test typeof(ex) == ErrorException
57+
@test ex.msg == "foo"
58+
@test c[1] == 1
59+
60+
c = [0]
61+
ex = @catch(retry(f, e->e.http_status_code == "503"))(c,3)
62+
@test typeof(ex) == ErrorException
63+
@test ex.msg == "foo"
64+
@test c[1] == 1
65+
66+
c = [0]
67+
ex = @catch(retry(f, SystemError))(c,3)
68+
@test typeof(ex) == ErrorException
69+
@test ex.msg == "foo"
70+
@test c[1] == 1
71+
72+
end

0 commit comments

Comments
 (0)