From fdae2be60de89cb4c07eeb46b0e392360173db5b Mon Sep 17 00:00:00 2001 From: Galen Lynch Date: Sun, 4 Nov 2018 16:28:04 -0500 Subject: [PATCH] Allow `pyraise` to include backtrace from Julia Instead of using `catch_backtrace` in `showerror_string`, which may not always be called from a catch block with a current exception set, I have added an optional argument to `pyraise` which can be used to pass backtraces to `showerror_string`. Backtraces are only used if exception is not a PyError exception. By default, no backtrace is shown. All try-catch blocks in PyCall attempt to pass the backtrace for the current exception to `pyraise`. --- src/callback.jl | 2 +- src/exception.jl | 16 +++++++++++----- src/pyiterator.jl | 6 +++--- src/pytype.jl | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/callback.jl b/src/callback.jl index 55d84936..43101edd 100644 --- a/src/callback.jl +++ b/src/callback.jl @@ -39,7 +39,7 @@ function _pyjlwrap_call(f, args_::PyPtr, kw_::PyPtr) return pyreturn(ret) catch e - pyraise(e) + pyraise(e, catch_backtrace()) finally args.o = PyPtr_NULL # don't decref end diff --git a/src/exception.jl b/src/exception.jl index 58c74fa4..84994361 100644 --- a/src/exception.jl +++ b/src/exception.jl @@ -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, catch_backtrace()) + _showerror_string(io, e, bt) return String(take!(io)) catch try @@ -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 diff --git a/src/pyiterator.jl b/src/pyiterator.jl index 5b4e44d6..80b36987 100644 --- a/src/pyiterator.jl +++ b/src/pyiterator.jl @@ -134,7 +134,7 @@ const jlWrapIteratorType = PyTypeObject() return pyreturn(item) end catch e - pyraise(e) + pyraise(e, catch_backtrace()) end return PyPtr_NULL end @@ -149,7 +149,7 @@ else return pyreturn(item) end catch e - pyraise(e) + pyraise(e, catch_backtrace()) end return PyPtr_NULL end @@ -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 diff --git a/src/pytype.jl b/src/pytype.jl index 387bc56f..e59303e2 100644 --- a/src/pytype.jl +++ b/src/pytype.jl @@ -348,7 +348,7 @@ function pyjlwrap_repr(o::PyPtr) return pyreturn(o != C_NULL ? string("") : "") catch e - pyraise(e) + pyraise(e, catch_backtrace()) return PyPtr_NULL end end @@ -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