-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathTry.jl
197 lines (167 loc) · 5.49 KB
/
Try.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
"""
Try{T} = Union{Const{<:Exception}, Identity{T}}
@Try(error("something happend")) isa Const(<:Thrown{ErrorException})
@Try(:successfull) == Identity(:successfull)
A specific case of [`Either`](@ref), where the Failure is always an Exception.
This can be used as an alternative to using try-catch syntax and allows for very flexible
error handling, as the error is now captured in a proper defined type.
Often it is really handy to treat errors like other values (without the need of extra try-catch syntax which only
applies to exceptions).
We reuse [`Identity`](@ref) for representing the single-element-container and `Const(<:Exception)` as the Exception
thrown.
"""
const Try{T} = Union{Const{<:Exception}, Identity{T}}
Try(t) = Identity(t)
Try(t::Exception) = Const(t)
"""
Thrown(exception, stacktrace)
Thrown is like Exception, however can also cary stacktraces
"""
struct Thrown{E, S} <: Exception
exception::E
stacktrace::S
end
# == controversy https://github.com/JuliaLang/julia/issues/4648
function Base.:(==)(a::Thrown, b::Thrown)
a.exception == b.exception && a.stacktrace == b.stacktrace
end
function Base.show(io::IO, x::Thrown)
print(io, "Thrown($(repr(x.exception)))")
end
# Multiline version, following https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing-1
function Base.show(io::IO, ::MIME"text/plain", exc::Thrown)
println(io, "Thrown($(repr(exc.exception)))")
for (exc′, bt′) in exc.stacktrace
showerror(io, exc′, bt′)
println(io)
end
end
function Base.showerror(io::IO, ::MIME"text/plain", exc::Thrown)
Base.show(io, MIME"text/plain"(), exc)
end
"""
MultipleExceptions(exception1, exception2, ...)
MultipleExceptions(tuple_or_vector_of_exceptions)
Little helper type which combines several Exceptions into one new Exception.
In the several arguments version, and only there, if an MultipleExceptions is given, it will be flattened directly for convenience.
"""
struct MultipleExceptions{Es<:Tuple} <: Exception
exceptions::Es
function MultipleExceptions(exceptions::Tuple)
@assert(isempty(exceptions) || eltype(exceptions) <: Exception,
"expecting tuple of exception types, however got `eltype(exceptions) = $(eltype(exceptions))`")
new{typeof(exceptions)}(exceptions)
end
end
MultipleExceptions(exceptions::Vector) = MultipleExceptions(tuple(exceptions...))
function MultipleExceptions(args...)
exceptions = ()
for a in args
exceptions = a isa MultipleExceptions ? tuple(exceptions..., a.exceptions...) : tuple(exceptions..., a)
end
MultipleExceptions(exceptions)
end
# == controversy https://github.com/JuliaLang/julia/issues/4648
Base.:(==)(a::MultipleExceptions, b::MultipleExceptions) = a.exceptions == b.exceptions
# Multiline version, following https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing-1
function Base.show(io::IO, ::MIME"text/plain", exc::MultipleExceptions)
println(io, "MultipleExceptions:")
for exception in exc.exceptions
println(repr(exception))
end
end
function Base.showerror(io::IO, ::MIME"text/plain", exc::MultipleExceptions)
Base.show(io, MIME"text/plain"(), exc)
end
Base.merge(e1::Exception, e2::Exception) = MultipleExceptions((e1, e2))
Base.merge(es::MultipleExceptions, e::Exception) = MultipleExceptions(tuple(es.exceptions..., e))
Base.merge(e::Exception, es::MultipleExceptions) = MultipleExceptions(tuple(e, es.exceptions...))
Base.merge(es1::MultipleExceptions, es2::MultipleExceptions) = MultipleExceptions(tuple(es1.exceptions..., es2.exceptions...))
# we use a macro instead of dispatching on Try(f::Function) as this interferes e.g. with mapn
# (in mapn anonymous functions are passed through, which should not get executed automatically)
"""
@Try begin
your_code
end
Macro which directly captures an Excpetion into a proper `Try`representation.
It translates to
```julia
try
r = your_code
Identity(r)
catch exc
Const(Thrown(exc, Base.catch_stack()))
end
```
"""
macro Try(expr)
quote
try
r = $(esc(expr))
Identity(r)
catch exc
Const(Thrown(exc, Base.catch_stack()))
end
end
end
"""
@TryCatch YourException begin
your_code
end
A version of [`@Try`](@ref) which catches only specific errors.
Every other orrer will be `rethrown`.
It translates to
```julia
try
r = your_code
Identity(r)
catch exc
if exc isa YourException
Const(Thrown(exc, Base.catch_stack()))
else
rethrow()
end
end
```
"""
macro TryCatch(exception, expr)
quote
try
r = $(esc(expr))
Identity(r)
catch exc
if exc isa $(esc(exception))
Const(Thrown(exc, Base.catch_stack()))
else
rethrow()
end
end
end
end
"""
isoption(::Const{Nothing}) = true
isoption(::Identity) = true
isoption(other) = false
check whether something is a [`Try`](@ref)
"""
istry(::Identity) = true
istry(::Const{<:Exception}) = true
istry(other) = false
"""
issuccess(::Identity) = true
issuccess(::Const{<:Exception}) = false
Similar to [`isright`](@ref), but only defined for `Const{<:Exception}`. Will
throw MethodError when applied on other `Const`.
"""
issuccess(::Identity) = true
issuccess(::Const{<:Exception}) = false
"""
isfailure(::Identity) = false
isfailure(::Const{<:Exception}) = true
Similar to [`isleft`](@ref), but only defined for `Const{<:Exception}`. Will
throw MethodError when applied on other `Const`.
"""
isfailure(::Identity) = false
isfailure(::Const{<:Exception}) = true
Base.eltype(::Type{Try{T}}) where T = T
Base.eltype(::Type{Try}) = Any