1+ # ########################################################################
2+ # A wrapper around an error that happened in a Julia callback
3+
4+ struct PyJlError <: Exception
5+ err
6+ trace
7+ end
8+
9+ function show (io:: IO , e:: PyJlError )
10+ println (io, " An error occured in a Julia function called from Python:" )
11+ _showerror_string (io, e. err, e. trace)
12+ end
13+
14+
115# ########################################################################
216# Wrapper around Python exceptions
317
@@ -12,15 +26,6 @@ struct PyError <: Exception
1226 # generate a PyError object. Should normally only be called when
1327 # PyErr_Occurred returns non-NULL, and clears the Python error
1428 # indicator.
15- function PyError (msg:: AbstractString )
16- ptype, pvalue, ptraceback = Ref {PyPtr} (), Ref {PyPtr} (), Ref {PyPtr} ()
17- # equivalent of passing C pointers &exc[1], &exc[2], &exc[3]:
18- ccall ((@pysym :PyErr_Fetch ), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
19- ccall ((@pysym :PyErr_NormalizeException ), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
20- new (msg, PyObject (ptype[]), PyObject (pvalue[]), PyObject (ptraceback[]))
21- end
22-
23- PyError (msg:: AbstractString , e:: PyError ) = new (msg, e. T, e. val, e. traceback)
2429end
2530
2631function show (io:: IO , e:: PyError )
@@ -54,7 +59,7 @@ pyerr_occurred() = ccall((@pysym :PyErr_Occurred), PyPtr, ()) != C_NULL
5459pyerr_clear () = ccall ((@pysym :PyErr_Clear ), Cvoid, ())
5560
5661function pyerr_check (msg:: AbstractString , val:: Any )
57- pyerr_occurred () && throw (PyError (msg))
62+ pyerr_occurred () && throw (pyerror (msg))
5863 val # the val argument is there just to pass through to the return value
5964end
6065
@@ -101,6 +106,37 @@ macro pycheckz(ex)
101106 :(@pycheckv $ (esc (ex)) - 1 )
102107end
103108
109+ function pyerror (msg:: AbstractString )
110+ ptype, pvalue, ptraceback = Ref {PyPtr} (), Ref {PyPtr} (), Ref {PyPtr} ()
111+ # equivalent of passing C pointers &exc[1], &exc[2], &exc[3]:
112+ ccall ((@pysym :PyErr_Fetch ), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
113+ ccall ((@pysym :PyErr_NormalizeException ), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
114+ pyerror (msg, PyObject (ptype[]), PyObject (pvalue[]), PyObject (ptraceback[]))
115+ end
116+
117+ function pyerror (msg:: AbstractString , e:: PyError )
118+ pyerror (msg, e. T, e. val, e. traceback)
119+ end
120+
121+ function pyerror (msg:: AbstractString , ptype:: PyObject , pvalue:: PyObject , ptraceback:: PyObject )
122+ pargs = _getproperty (pvalue, " args" )
123+
124+ # If the value of the error is a PyJlError, it was generated in a pyjlwrap callback, and
125+ # we forward it.
126+ if pargs != C_NULL
127+ args = PyObject (pargs)
128+ if length (args) > 0
129+ arg = PyObject (@pycheckn ccall ((@pysym :PySequence_GetItem ), PyPtr, (PyPtr,Int), args, 0 ))
130+ if is_pyjlwrap (arg)
131+ jarg = unsafe_pyjlwrap_to_objref (arg)
132+ jarg isa PyJlError && return jarg
133+ end
134+ end
135+ end
136+
137+ return PyError (msg, ptype, pvalue, ptraceback)
138+ end
139+
104140# ########################################################################
105141# Mapping of Julia Exception types to Python exceptions
106142
178214function pyraise (e, bt = nothing )
179215 eT = typeof (e)
180216 pyeT = haskey (pyexc:: Dict , eT) ? pyexc[eT] : pyexc[Exception]
181- ccall ((@pysym :PyErr_SetString ), Cvoid, (PyPtr, Cstring),
182- pyeT, string (" Julia exception: " , showerror_string (e, bt)))
217+ err = PyJlError (e, bt)
218+ ccall ((@pysym :PyErr_SetObject ), Cvoid, (PyPtr, PyPtr),
219+ pyeT, PyObject (err))
183220end
184221
185222# Second argument allows for backtraces passed to `pyraise` to be ignored.
0 commit comments