|
1 | 1 | mutable struct PyException <: Exception
|
2 |
| - tptr :: CPyPtr |
3 |
| - vptr :: CPyPtr |
4 |
| - bptr :: CPyPtr |
5 |
| - function PyException(::Val{:new}, t::Ptr=C_NULL, v::Ptr=C_NULL, b::Ptr=C_NULL, borrowed::Bool=false) |
6 |
| - o = new(CPyPtr(t), CPyPtr(v), CPyPtr(b)) |
7 |
| - if borrowed |
8 |
| - C.Py_IncRef(t) |
9 |
| - C.Py_IncRef(v) |
10 |
| - C.Py_IncRef(b) |
11 |
| - end |
12 |
| - finalizer(o) do o |
13 |
| - if CONFIG.isinitialized |
14 |
| - tptr = getfield(o, :tptr) |
15 |
| - vptr = getfield(o, :vptr) |
16 |
| - bptr = getfield(o, :bptr) |
17 |
| - if !isnull(tptr) || !isnull(vptr) || !isnull(bptr) |
18 |
| - with_gil(false) do |
19 |
| - C.Py_DecRef(tptr) |
20 |
| - C.Py_DecRef(vptr) |
21 |
| - C.Py_DecRef(bptr) |
22 |
| - end |
23 |
| - end |
24 |
| - end |
25 |
| - end |
26 |
| - return o |
27 |
| - end |
| 2 | + tref :: PyRef |
| 3 | + vref :: PyRef |
| 4 | + bref :: PyRef |
| 5 | + PyException(::Val{:new}, t::Ptr=C_NULL, v::Ptr=C_NULL, b::Ptr=C_NULL, borrowed::Bool=false) = |
| 6 | + new(PyRef(Val(:new), t, borrowed), PyRef(Val(:new), v, borrowed), PyRef(Val(:new), b, borrowed)) |
28 | 7 | end
|
29 | 8 | export PyException
|
30 | 9 |
|
31 | 10 | pythrow() = throw(PyException(Val(:new), C.PyErr_FetchTuple()...))
|
32 | 11 |
|
| 12 | +""" |
| 13 | + check(x) |
| 14 | +
|
| 15 | +Checks if the Python error indicator is set. If so, throws the current Python exception. Otherwise returns `x`. |
| 16 | +""" |
| 17 | +check(x) = C.PyErr_IsSet() ? pythrow() : x |
| 18 | + |
| 19 | +""" |
| 20 | + checkm1(x, ambig=false) |
| 21 | +
|
| 22 | +Same as `check(x)` but errors are indicated by `x == -1` instead. |
| 23 | +
|
| 24 | +If `ambig` is true the error indicator is also checked. |
| 25 | +""" |
| 26 | +checkm1(x::Number, ambig::Bool=false) = ism1(x) ? ambig ? check(x) : pythrow() : x |
| 27 | + |
| 28 | +""" |
| 29 | + checknull(x, ambig=false) |
| 30 | +
|
| 31 | +Same as `check(x)` but errors are indicated by `x == C_NULL` instead. |
| 32 | +
|
| 33 | +If `ambig` is true the error indicator is also checked. |
| 34 | +""" |
| 35 | +checknull(x::Ptr, ambig::Bool=false) = isnull(x) ? ambig ? check(x) : pythrow() : x |
| 36 | + |
| 37 | +""" |
| 38 | + checkerr(x, ambig=false) |
| 39 | +
|
| 40 | +Same as `check(x)` but errors are indicated by `x == PYERR()` instead. |
| 41 | +
|
| 42 | +If `ambig` is true the error indicator is also checked. |
| 43 | +""" |
| 44 | +checkerr(x, ambig::Bool=false) = x===PYERR() ? ambig ? check(x) : pythrow() : x |
| 45 | + |
| 46 | +""" |
| 47 | + checknullconvert(T, x, ambig=false) :: T |
| 48 | +
|
| 49 | +Same as `checknull(x, ambig)` but steals a reference to `x` and converts the result to a `T`. |
| 50 | +""" |
| 51 | +checknullconvert(::Type{T}, x::Ptr, ambig::Bool=false) where {T} = begin |
| 52 | + if isnull(x) && (!ambig || C.PyErr_IsSet()) |
| 53 | + C.Py_DecRef(x) |
| 54 | + pythrow() |
| 55 | + end |
| 56 | + r = C.PyObject_Convert(x, T) |
| 57 | + C.Py_DecRef(x) |
| 58 | + checkm1(r) |
| 59 | + C.takeresult(T) |
| 60 | +end |
| 61 | + |
33 | 62 | function Base.showerror(io::IO, e::PyException)
|
34 |
| - if isnull(e.tptr) |
| 63 | + if isnull(e.tref) |
35 | 64 | print(io, "Python: mysterious error (no error was actually set)")
|
36 | 65 | return
|
37 | 66 | end
|
38 | 67 |
|
39 | 68 | if CONFIG.sysautolasttraceback
|
40 | 69 | err = C.Py_DecRef(C.PyImport_ImportModule("sys"), PYERR()) do sys
|
41 |
| - err = C.PyObject_SetAttrString(sys, "last_type", isnull(e.tptr) ? C.Py_None() : e.tptr) |
| 70 | + err = C.PyObject_SetAttrString(sys, "last_type", isnull(e.tref) ? C.Py_None() : e.tref.ptr) |
42 | 71 | ism1(err) && return PYERR()
|
43 |
| - err = C.PyObject_SetAttrString(sys, "last_value", isnull(e.vptr) ? C.Py_None() : e.vptr) |
| 72 | + err = C.PyObject_SetAttrString(sys, "last_value", isnull(e.vref) ? C.Py_None() : e.vref.ptr) |
44 | 73 | ism1(err) && return PYERR()
|
45 |
| - err = C.PyObject_SetAttrString(sys, "last_traceback", isnull(e.bptr) ? C.Py_None() : e.bptr) |
| 74 | + err = C.PyObject_SetAttrString(sys, "last_traceback", isnull(e.bref) ? C.Py_None() : e.bref.ptr) |
46 | 75 | ism1(err) && return PYERR()
|
47 | 76 | nothing
|
48 | 77 | end
|
@@ -85,68 +114,53 @@ function Base.showerror(io::IO, e::PyException)
|
85 | 114 | print(io, "Python: ")
|
86 | 115 |
|
87 | 116 | # print the type name
|
88 |
| - tname = C.Py_DecRef(C.PyObject_GetAttrString(e.tptr, "__name__")) do tnameo |
89 |
| - C.PyUnicode_As(tnameo, String) |
90 |
| - end |
91 |
| - if tname === PYERR() |
92 |
| - C.PyErr_Clear() |
93 |
| - print(io, "<error while printing type>") |
94 |
| - else |
| 117 | + try |
| 118 | + tname = @pyv String `$(e.tref).__name__` |
95 | 119 | print(io, tname)
|
| 120 | + catch |
| 121 | + print(io, "<error while printing type>") |
96 | 122 | end
|
97 | 123 |
|
98 | 124 | # print the error message
|
99 |
| - if !isnull(e.vptr) |
| 125 | + if !isnull(e.vref) |
100 | 126 | print(io, ": ")
|
101 |
| - vstr = C.PyObject_StrAs(e.vptr, String) |
102 |
| - if vstr === PYERR() |
103 |
| - C.PyErr_Clear() |
104 |
| - print(io, "<error while printing value>") |
105 |
| - else |
| 127 | + try |
| 128 | + vstr = @pyv String `str($(e.vref))` |
106 | 129 | print(io, vstr)
|
| 130 | + catch |
| 131 | + print(io, "<error while printing value>") |
107 | 132 | end
|
108 | 133 | end
|
109 | 134 |
|
110 | 135 | # print the stacktrace
|
111 |
| - if !isnull(e.bptr) |
| 136 | + if !isnull(e.bref) |
112 | 137 | @label pystacktrace
|
113 | 138 | println(io)
|
114 | 139 | printstyled(io, "Python stacktrace:")
|
115 |
| - err = C.Py_DecRef(C.PyImport_ImportModule("traceback")) do tb |
116 |
| - C.Py_DecRef(C.PyObject_GetAttrString(tb, "extract_tb")) do extr |
117 |
| - C.Py_DecRef(C.PyObject_CallNice(extr, C.PyObjectRef(e.bptr))) do fs |
118 |
| - nfs = C.PySequence_Length(fs) |
119 |
| - ism1(nfs) && return PYERR() |
120 |
| - for i in 1:nfs |
121 |
| - println(io) |
122 |
| - printstyled(io, " [", i, "] ") |
123 |
| - # name |
124 |
| - err = C.Py_DecRef(C.PySequence_GetItem(fs, i-1)) do f |
125 |
| - name = C.Py_DecRef(C.PyObject_GetAttrString(f, "name")) do nameo |
126 |
| - C.PyObject_StrAs(nameo, String) |
127 |
| - end |
128 |
| - name === PYERR() && return PYERR() |
129 |
| - printstyled(io, name, bold=true) |
130 |
| - printstyled(io, " at ") |
131 |
| - fname = C.Py_DecRef(C.PyObject_GetAttrString(f, "filename")) do fnameo |
132 |
| - C.PyObject_StrAs(fnameo, String) |
133 |
| - end |
134 |
| - fname === PYERR() && return PYERR() |
135 |
| - printstyled(io, fname, ":", bold=true) |
136 |
| - lineno = C.Py_DecRef(C.PyObject_GetAttrString(f, "lineno")) do linenoo |
137 |
| - C.PyObject_StrAs(linenoo, String) |
138 |
| - end |
139 |
| - lineno === PYERR() && return PYERR() |
140 |
| - printstyled(io, lineno, bold=true) |
141 |
| - nothing |
142 |
| - end |
143 |
| - err === PYERR() && return PYERR() |
144 |
| - end |
| 140 | + try |
| 141 | + @py ``` |
| 142 | + import traceback |
| 143 | + $(fs :: C.PyObjectRef) = fs = traceback.extract_tb($(e.bref)) |
| 144 | + $(nfs :: Int) = len(fs) |
| 145 | + ``` |
| 146 | + try |
| 147 | + for i in 1:nfs |
| 148 | + @py ``` |
| 149 | + f = $(fs)[$(i-1)] |
| 150 | + $(name::String) = f.name |
| 151 | + $(fname::String) = f.filename |
| 152 | + $(lineno::Int) = f.lineno |
| 153 | + ``` |
| 154 | + println(io) |
| 155 | + printstyled(io, " [", i, "] ") |
| 156 | + printstyled(io, name, bold=true) |
| 157 | + printstyled(io, " at ") |
| 158 | + printstyled(io, fname, ":", lineno, bold=true) |
145 | 159 | end
|
| 160 | + finally |
| 161 | + C.Py_DecRef(fs) |
146 | 162 | end
|
147 |
| - end |
148 |
| - if err === PYERR() |
149 |
| - C.PyErr_Clear() |
| 163 | + catch |
150 | 164 | print(io, "<error while printing stacktrace>")
|
151 | 165 | end
|
152 | 166 | end
|
|
0 commit comments