Skip to content

Commit 59c30dd

Browse files
author
Christopher Doris
committed
move some of the implementation of the @py macros to functions
1 parent 3bc3d3a commit 59c30dd

File tree

2 files changed

+122
-102
lines changed

2 files changed

+122
-102
lines changed

src/PyDict.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ pyptr(x::PyDict) = begin
2828
end
2929
Base.unsafe_convert(::Type{CPyPtr}, x::PyDict) = checknull(pyptr(x))
3030

31+
ensurehasbuiltins!(x::PyDict) = begin
32+
if !x.hasbuiltins
33+
if C.PyMapping_HasKeyString(x, "__builtins__") == 0
34+
err = C.PyMapping_SetItemString(
35+
x,
36+
"__builtins__",
37+
C.PyEval_GetBuiltins(),
38+
)
39+
ism1(err) && pythrow()
40+
end
41+
x.hasbuiltins = true
42+
end
43+
end
44+
3145
Base.iterate(x::PyDict{K,V}, it::PyRef) where {K,V} = begin
3246
ko = C.PyIter_Next(it)
3347
if !isnull(ko)

src/eval.jl

Lines changed: 108 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,58 @@
1+
eval_impl(::Type{T}, code, globals, locals=nothing, extrakeys::Tuple{Vararg{Any,N}}=(), extravals::Tuple{Vararg{Any,N}}=()) where {T,N} = GC.@preserve code globals locals begin
2+
# get pointers to inputs
3+
cptr = checknull(pyptr(code))
4+
ensurehasbuiltins!(globals)
5+
gptr = checknull(pyptr(globals))
6+
lptr = checknull(locals === nothing ? C.PyDict_New() : pyptr(locals))
7+
# insert extras into locals
8+
for (k, v) in zip(extrakeys, extravals)
9+
vo = C.PyObject_From(v)
10+
isnull(vo) && @goto error
11+
err = C.PyObject_SetItem(lptr, k, vo)
12+
C.Py_DecRef(vo)
13+
ism1(err) && @goto error
14+
end
15+
# eval
16+
rptr = C.PyEval_EvalCode(cptr, gptr, lptr)
17+
isnull(rptr) && @goto error
18+
# convert the result
19+
err = C.PyObject_Convert(rptr, T)
20+
C.Py_DecRef(rptr)
21+
ism1(err) && @goto error
22+
locals === nothing && C.Py_DecRef(lptr)
23+
return takeresult(T)
24+
25+
@label error
26+
locals === nothing && C.Py_DecRef(lptr)
27+
pythrow()
28+
end
29+
30+
exec_impl(f, code, globals, locals=nothing, extrakeys::Tuple{Vararg{Any,N}}=(), extravals::Tuple{Vararg{Any,N}}=()) where {N} = GC.@preserve code globals locals begin
31+
# get pointers to inputs
32+
cptr = checknull(pyptr(code))
33+
ensurehasbuiltins!(globals)
34+
gptr = checknull(pyptr(globals))
35+
lptr = checknull(locals === nothing ? C.PyDict_New() : pyptr(locals))
36+
# insert extras into locals
37+
for (k, v) in zip(extrakeys, extravals)
38+
vo = C.PyObject_From(v)
39+
isnull(vo) && @goto error
40+
err = C.PyObject_SetItem(lptr, k, vo)
41+
C.Py_DecRef(vo)
42+
ism1(err) && @goto error
43+
end
44+
# eval
45+
rptr = C.PyEval_EvalCode(cptr, gptr, lptr)
46+
isnull(rptr) && @goto error
47+
C.Py_DecRef(rptr)
48+
# extract the result
49+
return f(lptr, locals===nothing)
50+
51+
@label error
52+
locals === nothing && C.Py_DecRef(lptr)
53+
pythrow()
54+
end
55+
156
pyeval_filename(src) =
257
isfile(string(src.file)) ? "$(src.file):$(src.line)" : "julia:$(src.file):$(src.line)"
358

@@ -77,118 +132,69 @@ pyeval_macro(filename, mode, codearg, args...) = begin
77132
end
78133
# make the code be a function body
79134
if mode == :execr
80-
newcode = "def _jl_tmp_ans_func_($(join(intvars, ", "))):\n" * join(map(line->" "*line, split(newcode, "\n")), "\n") * "\n\nans = _jl_tmp_ans_func_($(join(intvars, ", ")))"
135+
newcode = "def _jl_tmp_ans_func_($(join(intvars, ", "))):\n" * join(map(line->" "*line, split(newcode, "\n")), "\n") * "\n\n_jl_tmp_ans_ = _jl_tmp_ans_func_($(join(intvars, ", ")))"
81136
end
82137
# make the code object
83138
co = PyCode(newcode, filename, mode == :eval ? :eval : :exec)
139+
# make the keys to interpolate
140+
extrakeys = Tuple(PyInternedString(string(k)) for (k,_) in kvs)
141+
extravals = :(($([esc(v) for (k,v) in kvs]...),))
142+
args = (co, esc(:pyglobals), esc(locals), extrakeys, extravals)
143+
mkgetvar(jv, pv, t) = quote
144+
ro = C.PyObject_GetItem(lptr, $(PyInternedString(string(pv))))
145+
isnull(ro) && (decref && C.Py_DecRef(lptr); pythrow())
146+
err = C.PyObject_Convert(ro, $t)
147+
ism1(err) && (decref && C.Py_DecRef(lptr); pythrow())
148+
$(Symbol(jv)) = takeresult($t)
149+
end
84150
# go
85-
freelocals =
86-
locals === nothing ? :(C.Py_DecRef(lptr)) : :(GC.@preserve locals nothing)
87-
ret = quote
88-
let
89-
# evaluate the inputs (so any errors are thrown before we have object references)
90-
$([:($(Symbol(:input, i)) = $(esc(v))) for (i, (k, v)) in enumerate(kvs)]...)
91-
# get the code pointer
92-
cptr = checknull(pyptr($co))
93-
# get the globals pointer
94-
globals = $(esc(:pyglobals))
95-
gptr = checknull(pyptr(globals))
96-
# ensure globals includes builtins
97-
if globals isa PyDict && !globals.hasbuiltins
98-
if C.PyMapping_HasKeyString(gptr, "__builtins__") == 0
99-
err = C.PyMapping_SetItemString(
100-
gptr,
101-
"__builtins__",
102-
C.PyEval_GetBuiltins(),
103-
)
104-
ism1(err) && pythrow()
151+
if mode == :eval
152+
:(eval_impl($(esc(rettypearg)), $(args...)))
153+
elseif mode == :exec
154+
if length(outkvts) == 0
155+
quote
156+
exec_impl($(args...),) do lptr::CPyPtr, decref::Bool
157+
decref && C.Py_DecRef(lptr)
158+
nothing
105159
end
106-
globals.hasbuiltins = true
107160
end
108-
# get locals (ALLOCATES lptr if locals===nothing)
109-
$(
110-
locals === nothing ? :(lptr = checknull(C.PyDict_New())) :
111-
:(locals = $(esc(locals)); lptr = checknull(pyptr(locals)))
112-
)
113-
# insert extra locals
114-
$(
115-
[
116-
:(
117-
let
118-
vo = C.PyObject_From($(Symbol(:input, i)))
119-
isnull(vo) && ($freelocals; pythrow())
120-
err = C.PyObject_SetItem(
121-
lptr,
122-
$(PyInternedString(string(k))),
123-
vo,
124-
)
125-
C.Py_DecRef(vo)
126-
ism1(err) && ($freelocals; pythrow())
127-
end
128-
) for (i, (k, v)) in enumerate(kvs)
129-
]...
130-
)
131-
# Call eval (ALLOCATES rptr)
132-
rptr = GC.@preserve globals C.PyEval_EvalCode(cptr, gptr, lptr)
133-
isnull(rptr) && ($freelocals; pythrow())
134-
# extract values
135-
$(
136-
if mode == :eval
137-
quote
138-
$freelocals
139-
res = C.PyObject_Convert(rptr, $(esc(rettypearg)))
140-
C.Py_DecRef(rptr)
141-
ism1(res) && pythrow()
142-
C.takeresult($(esc(rettypearg)))
143-
end
144-
elseif mode in (:execa, :execr)
145-
quote
146-
C.Py_DecRef(rptr)
147-
xo = C.PyObject_GetItem(lptr, $(PyInternedString("ans")))
148-
isnull(xo) && ($freelocals; pythrow())
149-
res = C.PyObject_Convert(xo, $(esc(rettypearg)))
150-
C.Py_DecRef(xo)
151-
$freelocals
152-
ism1(res) && pythrow()
153-
C.takeresult($(esc(rettypearg)))
154-
end
155-
elseif mode == :exec
156-
quote
157-
C.Py_DecRef(rptr)
158-
$(
159-
(
160-
quote
161-
$(Symbol(:output, i)) =
162-
let
163-
xo = C.PyObject_GetItem(
164-
lptr,
165-
$(PyInternedString(v)),
166-
)
167-
isnull(xo) && ($freelocals; pythrow())
168-
res = C.PyObject_Convert(xo, $t)
169-
C.Py_DecRef(xo)
170-
ism1(res) && ($freelocals; pythrow())
171-
C.takeresult($t)
172-
end
173-
end for (i, (_, v, t)) in enumerate(outkvts)
174-
)...
175-
)
176-
$freelocals
177-
($((Symbol(:output, i) for i = 1:length(outkvts))...),)
178-
end
179-
else
180-
error()
161+
elseif length(outkvts) == 1
162+
k, v, t = outkvts[1]
163+
quote
164+
$k = exec_impl($(args...),) do lptr::CPyPtr, decref::Bool
165+
$(mkgetvar("r", v, t))
166+
decref && C.Py_DecRef(lptr)
167+
return r
181168
end
182-
)
169+
end
170+
else
171+
quote
172+
($([k for (k,_,_) in outkvts]...),) = exec_impl($(args...),) do lptr::CPyPtr, decref::Bool
173+
$([mkgetvar("r$i", v, t) for (i,(k,v,t)) in enumerate(outkvts)]...)
174+
decref && C.Py_DecRef(lptr)
175+
return ($([Symbol("r$i") for i in 1:length(outkvts)]...),)
176+
end
177+
end
183178
end
184-
end
185-
if mode == :exec
186-
ret = quote
187-
($((k for (k, _, _) in outkvts)...),) = $ret
188-
nothing
179+
elseif mode == :execr
180+
quote
181+
exec_impl($(args...),) do lptr::CPyPtr, decref::Bool
182+
$(mkgetvar("r", "_jl_tmp_ans_", esc(rettypearg)))
183+
decref && C.Py_DecRef(lptr)
184+
return r
185+
end
186+
end
187+
elseif mode == :execa
188+
quote
189+
exec_impl($(args...),) do lptr, decref
190+
$(mkgetvar("r", "ans", esc(rettypearg)))
191+
decref && C.Py_DecRef(lptr)
192+
return r
193+
end
189194
end
195+
else
196+
@assert false
190197
end
191-
ret
192198
end
193199

194200
"""

0 commit comments

Comments
 (0)