Skip to content

Commit b9656ce

Browse files
author
Christopher Doris
committed
loadsa dev
1 parent 20186f4 commit b9656ce

39 files changed

+2693
-1323
lines changed

src/PyCode.jl

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
11
mutable struct PyCode
2-
ptr :: CPyPtr
2+
ref :: PyRef
33
code :: String
44
filename :: String
55
mode :: Symbol
6-
function PyCode(code::String, filename::String, mode::Symbol)
6+
PyCode(code::String, filename::String, mode::Symbol) = begin
77
mode in (:exec, :eval) || error("invalid mode $(repr(mode))")
8-
o = new(CPyPtr(), code, filename, mode)
9-
finalizer(o) do o
10-
if CONFIG.isinitialized
11-
ptr = getfield(o, :ptr)
12-
if !isnull(ptr)
13-
with_gil(false) do
14-
C.Py_DecRef(ptr)
15-
end
16-
end
17-
end
18-
end
19-
return o
8+
new(PyRef(), code, filename, mode)
209
end
2110
end
2211
export PyCode
2312

24-
function pyptr(co::PyCode)
25-
ptr = getfield(co, :ptr)
13+
ispyreftype(::Type{PyCode}) = true
14+
pyptr(co::PyCode) = begin
15+
ptr = co.ref.ptr
2616
if isnull(ptr)
27-
ptr = C.Py_CompileString(co.code, co.filename, co.mode == :exec ? C.Py_file_input : co.mode == :eval ? C.Py_eval_input : error("invalid mode $(repr(co.mode))"))
28-
if isnull(ptr)
29-
pythrow()
30-
else
31-
setfield!(co, :ptr, ptr)
32-
ptr
33-
end
34-
else
35-
ptr
17+
ptr = co.ref.ptr = C.Py_CompileString(co.code, co.filename, co.mode == :exec ? C.Py_file_input : co.mode == :eval ? C.Py_eval_input : error("invalid mode $(repr(co.mode))"))
3618
end
19+
ptr
3720
end
21+
Base.unsafe_convert(::Type{CPyPtr}, x::PyCode) = checknull(pyptr(x))

src/PyDict.jl

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,104 @@
1-
mutable struct PyDict
2-
ptr :: CPyPtr
1+
"""
2+
PyDict{K=PyObject, V=PyObject}([o])
3+
4+
Wrap the Python dictionary `o` (or anything satisfying the mapping interface) as a Julia dictionary with keys of type `K` and values of type `V`.
5+
6+
If `o` is not given, an empty dict is created.
7+
"""
8+
mutable struct PyDict{K,V} <: AbstractDict{K,V}
9+
ref :: PyRef
310
hasbuiltins :: Bool
4-
function PyDict()
5-
o = new(CPyPtr(), false)
6-
finalizer(o) do o
7-
if CONFIG.isinitialized
8-
ptr = getfield(o, :ptr)
9-
if !isnull(ptr)
10-
with_gil(false) do
11-
C.Py_DecRef(ptr)
12-
end
13-
end
14-
end
15-
end
16-
return o
17-
end
11+
PyDict{K,V}(o) where {K,V} = new(PyRef(o), false)
12+
PyDict{K,V}() where {K,V} = new(PyRef(), false)
1813
end
14+
PyDict{K}(args...) where {K} = PyDict{K,PyObject}(args...)
15+
PyDict(args...) = PyDict{PyObject,PyObject}(args...)
1916
export PyDict
2017

21-
const pyglobals = PyDict()
18+
const pyglobals = PyDict{String}()
2219
export pyglobals
2320

24-
function pyptr(x::PyDict)
25-
ptr = getfield(x, :ptr)
21+
ispyreftype(::Type{<:PyDict}) = true
22+
pyptr(x::PyDict) = begin
23+
ptr = x.ref.ptr
2624
if isnull(ptr)
27-
ptr = C.PyDict_New()
28-
if isnull(ptr)
29-
pythrow()
30-
else
31-
setfield!(x, :ptr, ptr)
32-
ptr
33-
end
25+
ptr = x.ref.ptr = C.PyDict_New()
26+
end
27+
ptr
28+
end
29+
Base.unsafe_convert(::Type{CPyPtr}, x::PyDict) = checknull(pyptr(x))
30+
31+
Base.iterate(x::PyDict{K,V}, it::PyRef) where {K,V} = begin
32+
ptr = C.PyIter_Next(it)
33+
if !isnull(ptr)
34+
ko = C.PySequence_GetItem(ptr, 0)
35+
isnull(ko) && pythrow()
36+
r = C.PyObject_Convert(ko, K)
37+
C.Py_DecRef(ko)
38+
ism1(r) && pythrow()
39+
k = C.takeresult(K)
40+
vo = C.PySequence_GetItem(ptr, 1)
41+
isnull(vo) && pythrow()
42+
r = C.PyObject_Convert(vo, V)
43+
C.Py_DecRef(vo)
44+
ism1(r) && pythrow()
45+
v = C.takeresult(V)
46+
(k => v, it)
47+
elseif C.PyErr_IsSet()
48+
pythrow()
3449
else
35-
ptr
50+
nothing
3651
end
3752
end
53+
Base.iterate(x::PyDict) = begin
54+
a = C.PyObject_GetAttrString(x, "items")
55+
isnull(a) && pythrow()
56+
b = C.PyObject_CallNice(a)
57+
C.Py_DecRef(a)
58+
isnull(b) && pythrow()
59+
it = C.PyObject_GetIter(b)
60+
C.Py_DecRef(b)
61+
isnull(it) && pythrow()
62+
iterate(x, pynewref(it))
63+
end
64+
65+
Base.setindex!(x::PyDict{K,V}, v, k) where {K,V} = pysetitem(x, convertref(K, k), convertref(V, v))
66+
67+
Base.getindex(x::PyDict{K,V}, k) where {K,V} = pygetitem(V, x, convertref(K, k))
68+
69+
Base.delete!(x::PyDict{K}, k) where {K} = pydelitem(x, convertref(K, k))
70+
71+
Base.length(x::PyDict) = Int(pylen(x))
72+
73+
Base.empty!(x::PyDict) = (@py `$x.clear()`; x)
74+
75+
Base.copy(x::PyDict) = @pyv typeof(x) `$x.copy()`
76+
77+
Base.haskey(x::PyDict{K}, _k) where {K} = begin
78+
k = tryconvertref(K, _k)
79+
k === PYERR() && pythrow()
80+
k === NOTIMPLEMENTED() && return false
81+
pycontains(x, k)
82+
end
83+
84+
Base.get(x::PyDict{K}, _k, d) where {K} = begin
85+
k = tryconvertref(K, _k)
86+
k === PYERR() && pythrow()
87+
(k !== NOTIMPLEMENTED() && pycontains(x, k)) ? x[k] : d
88+
end
89+
90+
Base.get(d::Function, x::PyDict{K}, _k) where {K} = begin
91+
k = tryconvertref(K, _k)
92+
k === PYERR() && pythrow()
93+
(k !== NOTIMPLEMENTED() && pycontains(x, k)) ? x[k] : d()
94+
end
95+
96+
Base.get!(x::PyDict{K,V}, _k, d) where {K,V} = begin
97+
k = convertref(K, _k)
98+
pycontains(x, k) ? x[k] : (x[k] = convert(V, d))
99+
end
100+
101+
Base.get!(d::Function, x::PyDict{K,V}, _k) where {K,V} = begin
102+
k = convertref(K, _k)
103+
pycontains(x, k) ? x[k] : (x[k] = convert(V, d()))
104+
end

src/PyException.jl

Lines changed: 91 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,77 @@
11
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))
287
end
298
export PyException
309

3110
pythrow() = throw(PyException(Val(:new), C.PyErr_FetchTuple()...))
3211

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+
3362
function Base.showerror(io::IO, e::PyException)
34-
if isnull(e.tptr)
63+
if isnull(e.tref)
3564
print(io, "Python: mysterious error (no error was actually set)")
3665
return
3766
end
3867

3968
if CONFIG.sysautolasttraceback
4069
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)
4271
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)
4473
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)
4675
ism1(err) && return PYERR()
4776
nothing
4877
end
@@ -85,68 +114,53 @@ function Base.showerror(io::IO, e::PyException)
85114
print(io, "Python: ")
86115

87116
# 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__`
95119
print(io, tname)
120+
catch
121+
print(io, "<error while printing type>")
96122
end
97123

98124
# print the error message
99-
if !isnull(e.vptr)
125+
if !isnull(e.vref)
100126
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))`
106129
print(io, vstr)
130+
catch
131+
print(io, "<error while printing value>")
107132
end
108133
end
109134

110135
# print the stacktrace
111-
if !isnull(e.bptr)
136+
if !isnull(e.bref)
112137
@label pystacktrace
113138
println(io)
114139
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)
145159
end
160+
finally
161+
C.Py_DecRef(fs)
146162
end
147-
end
148-
if err === PYERR()
149-
C.PyErr_Clear()
163+
catch
150164
print(io, "<error while printing stacktrace>")
151165
end
152166
end

0 commit comments

Comments
 (0)