Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Julia exceptions wrapped in Python Exceptions more informative #608

Merged
merged 4 commits into from
Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/callback.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function _pyjlwrap_call(f, args_::PyPtr, kw_::PyPtr)

return pyreturn(ret)
catch e
pyraise(e)
pyraise(e, catch_backtrace())
Copy link
Member

@stevengj stevengj Nov 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be cleaner to have a @pyraise e macro that inserts the catch_backtrace() call:

"""
    @pyraise e

Throw the exception `e` to Python, attaching a backtrace.  This macro should only be
used in a `catch` block so that `catch_backtrace()` is valid.
"""
macro pyraise(e)
    :(pyraise($(esc(e)), catch_backtrace())
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

finally
args.o = PyPtr_NULL # don't decref
end
Expand Down
16 changes: 11 additions & 5 deletions src/exception.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,21 @@ function pyexc_initialize()
pyexc[PyIOError] = @pyglobalobjptr :PyExc_IOError
end

_showerror_string(io::IO, e, ::Nothing) = showerror(io, e)
_showerror_string(io::IO, e, bt) = showerror(io, e, bt)

# bt argument defaults to nothing, to delay dispatching on the presence of a
# backtrace until after the try-catch block
"""
showerror_string(e) :: String

Convert output of `showerror` to a `String`. Since this function may
be called via Python C-API, it tries to not throw at all cost.
"""
function showerror_string(e::T) where {T}
function showerror_string(e::T, bt = nothing) where {T}
try
io = IOBuffer()
showerror(io, e)
_showerror_string(io, e, bt)
return String(take!(io))
catch
try
Expand Down Expand Up @@ -163,14 +168,15 @@ function showerror_string(e::T) where {T}
end
end

function pyraise(e)
function pyraise(e, bt = nothing)
eT = typeof(e)
pyeT = haskey(pyexc::Dict, eT) ? pyexc[eT] : pyexc[Exception]
ccall((@pysym :PyErr_SetString), Cvoid, (PyPtr, Cstring),
pyeT, string("Julia exception: ", showerror_string(e)))
pyeT, string("Julia exception: ", showerror_string(e, bt)))
end

function pyraise(e::PyError)
# Second argument allows for backtraces passed to `pyraise` to be ignored.
function pyraise(e::PyError, ::Vector = [])
ccall((@pysym :PyErr_Restore), Cvoid, (PyPtr, PyPtr, PyPtr),
e.T, e.val, e.traceback)
e.T.o = e.val.o = e.traceback.o = C_NULL # refs were stolen
Expand Down
6 changes: 3 additions & 3 deletions src/pyiterator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const jlWrapIteratorType = PyTypeObject()
return pyreturn(item)
end
catch e
pyraise(e)
pyraise(e, catch_backtrace())
end
return PyPtr_NULL
end
Expand All @@ -149,7 +149,7 @@ else
return pyreturn(item)
end
catch e
pyraise(e)
pyraise(e, catch_backtrace())
end
return PyPtr_NULL
end
Expand All @@ -162,7 +162,7 @@ function pyjlwrap_getiter(self_::PyPtr)
self = unsafe_pyjlwrap_to_objref(self_)
return pystealref!(jlwrap_iterator(self))
catch e
pyraise(e)
pyraise(e, catch_backtrace())
end
return PyPtr_NULL
end
Expand Down
4 changes: 2 additions & 2 deletions src/pytype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ function pyjlwrap_repr(o::PyPtr)
return pyreturn(o != C_NULL ? string("<PyCall.jlwrap ",unsafe_pyjlwrap_to_objref(o),">")
: "<PyCall.jlwrap NULL>")
catch e
pyraise(e)
pyraise(e, catch_backtrace())
return PyPtr_NULL
end
end
Expand Down Expand Up @@ -395,7 +395,7 @@ function pyjlwrap_getattr(self_::PyPtr, attr__::PyPtr)
end
end
catch e
pyraise(e)
pyraise(e, catch_backtrace())
finally
attr_.o = PyPtr_NULL # don't decref
end
Expand Down