Skip to content

Commit 600a7f5

Browse files
committed
cache dlsym lookup (fixes issue #2), following suggestion by vtjnash
1 parent bc87517 commit 600a7f5

File tree

6 files changed

+107
-92
lines changed

6 files changed

+107
-92
lines changed

src/PyCall.jl

+43-30
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ libpython = C_NULL # Python shared library (from dlopen)
2424

2525
pyfunc(func::Symbol) = dlsym(libpython::Ptr{Void}, func)
2626

27+
# Macro version of pyfunc to cache dlsym lookup (thanks to vtjnash)
28+
macro pyfunc(func)
29+
z = gensym(string(func))
30+
@eval global $z = C_NULL
31+
quote begin
32+
global $z
33+
if $z::Ptr{Void} == C_NULL
34+
$z::Ptr{Void} = dlsym(libpython::Ptr{Void}, $(esc(func)))
35+
end
36+
$z::Ptr{Void}
37+
end end
38+
end
39+
2740
# Macro version of pyinitialize() to inline initialized? check
2841
macro pyinitialize()
2942
:(initialized::Bool ? nothing : pyinitialize())
@@ -44,25 +57,25 @@ end
4457

4558
function pydecref(o::PyObject)
4659
if initialized::Bool # don't decref after pyfinalize!
47-
ccall(pyfunc(:Py_DecRef), Void, (PyPtr,), o.o)
60+
ccall((@pyfunc :Py_DecRef), Void, (PyPtr,), o.o)
4861
end
4962
o.o = C_NULL
5063
o
5164
end
5265

5366
function pyincref(o::PyObject)
54-
ccall(pyfunc(:Py_IncRef), Void, (PyPtr,), o)
67+
ccall((@pyfunc :Py_IncRef), Void, (PyPtr,), o)
5568
o
5669
end
5770

5871
pyisinstance(o::PyObject, t::PyObject) =
59-
t.o != C_NULL && ccall(pyfunc(:PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t.o) == 1
72+
t.o != C_NULL && ccall((@pyfunc :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t.o) == 1
6073

61-
pyisinstance(o::PyObject, t::Symbol) =
62-
ccall(pyfunc(:PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, pyfunc(t)) == 1
74+
pyisinstance(o::PyObject, t::Ptr{Void}) =
75+
ccall((@pyfunc :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t) == 1
6376

64-
pyquery(q::Symbol, o::PyObject) =
65-
ccall(pyfunc(q), Cint, (PyPtr,), o) == 1
77+
pyquery(q::Ptr{Void}, o::PyObject) =
78+
ccall(q, Cint, (PyPtr,), o) == 1
6679

6780
pytypeof(o::PyObject) = o.o == C_NULL ? throw(ArgumentError("NULL PyObjects have no Python type")) : pycall(PyCall.TypeType, PyObject, o)
6881

@@ -123,12 +136,12 @@ function pyinitialize(libpy::Ptr{Void})
123136
error("Calling pyinitialize after pyfinalize is not supported")
124137
end
125138
libpython::Ptr{Void} = libpy == C_NULL ? ccall(:jl_load_dynamic_library, Ptr{Void}, (Ptr{Uint8},Cuint), C_NULL, 0) : libpy
126-
if 0 == ccall(pyfunc(:Py_IsInitialized), Cint, ())
139+
if 0 == ccall((@pyfunc :Py_IsInitialized), Cint, ())
127140
if !isempty(pyprogramname::ASCIIString)
128-
ccall(pyfunc(:Py_SetProgramName), Void, (Ptr{Uint8},),
141+
ccall((@pyfunc :Py_SetProgramName), Void, (Ptr{Uint8},),
129142
pyprogramname::ASCIIString)
130143
end
131-
ccall(pyfunc(:Py_InitializeEx), Void, (Cint,), 0)
144+
ccall((@pyfunc :Py_InitializeEx), Void, (Cint,), 0)
132145
end
133146
initialized::Bool = true
134147
inspect::PyObject = pyimport("inspect")
@@ -198,7 +211,7 @@ function pyfinalize()
198211
pydecref(inspect::PyObject)
199212
pygc_finalize()
200213
gc() # collect/decref any remaining PyObjects
201-
ccall(pyfunc(:Py_Finalize), Void, ())
214+
ccall((@pyfunc :Py_Finalize), Void, ())
202215
dlclose(libpython::Ptr{Void})
203216
libpython::Ptr{Void} = C_NULL
204217
initialized::Bool = false
@@ -215,7 +228,7 @@ pyversion() = VersionNumber(convert((Int,Int,Int,String,Int),
215228
# Conversion of Python exceptions into Julia exceptions
216229

217230
# call when we are throwing our own exception
218-
pyerr_clear() = ccall(pyfunc(:PyErr_Clear), Void, ())
231+
pyerr_clear() = ccall((@pyfunc :PyErr_Clear), Void, ())
219232

220233
type PyError <: Exception
221234
msg::String
@@ -225,10 +238,10 @@ end
225238
function pyerr_check(msg::String, val::Any)
226239
# note: don't call pyinitialize here since we will
227240
# only use this in contexts where initialization was already done
228-
e = ccall(pyfunc(:PyErr_Occurred), PyPtr, ())
241+
e = ccall((@pyfunc :PyErr_Occurred), PyPtr, ())
229242
if e != C_NULL
230243
# PyErr_Occurred returns borrowed ref
231-
ccall(pyfunc(:Py_IncRef), Void, (PyPtr,), e)
244+
ccall((@pyfunc :Py_IncRef), Void, (PyPtr,), e)
232245
pyerr_clear()
233246
throw(PyError(msg, PyObject(e)))
234247
end
@@ -238,7 +251,7 @@ end
238251
pyerr_check(msg::String) = pyerr_check(msg, nothing)
239252
pyerr_check() = pyerr_check("")
240253

241-
# Macros for common pyerr_check("Foo", ccall(pyfunc(:Foo), ...)) pattern.
254+
# Macros for common pyerr_check("Foo", ccall((@pyfunc :Foo), ...)) pattern.
242255
# (The "i" variant assumes Python is initialized.)
243256
macro pychecki(ex)
244257
:(pyerr_check($(string(ex.args[1].args[2].args[1])), $ex))
@@ -250,7 +263,7 @@ macro pycheck(ex)
250263
end
251264
end
252265

253-
# Macros to check that ccall(pyfunc(:Foo), ...) returns value != bad
266+
# Macros to check that ccall((@pyfunc :Foo), ...) returns value != bad
254267
# (The "i" variants assume Python is initialized.)
255268
macro pycheckvi(ex, bad)
256269
quote
@@ -311,10 +324,10 @@ function show(io::IO, o::PyObject)
311324
if o.o == C_NULL
312325
print(io, "PyObject NULL")
313326
else
314-
s = ccall(pyfunc(:PyObject_Repr), PyPtr, (PyPtr,), o)
327+
s = ccall((@pyfunc :PyObject_Repr), PyPtr, (PyPtr,), o)
315328
if (s == C_NULL)
316329
pyerr_clear()
317-
s = ccall(pyfunc(:PyObject_Str), PyPtr, (PyPtr,), o)
330+
s = ccall((@pyfunc :PyObject_Str), PyPtr, (PyPtr,), o)
318331
if (s == C_NULL)
319332
pyerr_clear()
320333
return print(io, "PyObject $(o.o)")
@@ -333,7 +346,7 @@ function ref(o::PyObject, s::String)
333346
if (o.o == C_NULL)
334347
throw(ArgumentError("ref of NULL PyObject"))
335348
end
336-
p = ccall(pyfunc(:PyObject_GetAttrString), PyPtr,
349+
p = ccall((@pyfunc :PyObject_GetAttrString), PyPtr,
337350
(PyPtr, Ptr{Uint8}), o, bytestring(s))
338351
if p == C_NULL
339352
pyerr_clear()
@@ -348,7 +361,7 @@ function assign(o::PyObject, v, s::String)
348361
if (o.o == C_NULL)
349362
throw(ArgumentError("assign of NULL PyObject"))
350363
end
351-
if -1 == ccall(pyfunc(:PyObject_SetAttrString), Cint,
364+
if -1 == ccall((@pyfunc :PyObject_SetAttrString), Cint,
352365
(PyPtr, Ptr{Uint8}, PyPtr), o, bytestring(s), PyObject(v))
353366
pyerr_clear()
354367
throw(KeyError(s))
@@ -390,7 +403,7 @@ end
390403
#########################################################################
391404

392405
pyimport(name::String) =
393-
PyObject(@pycheckn ccall(pyfunc(:PyImport_ImportModule), PyPtr,
406+
PyObject(@pycheckn ccall((@pyfunc :PyImport_ImportModule), PyPtr,
394407
(Ptr{Uint8},), bytestring(name)))
395408

396409
pyimport(name::Symbol) = pyimport(string(name))
@@ -425,10 +438,10 @@ end
425438

426439
# look up a global variable (in module __main__)
427440
function pybuiltin(name::String)
428-
main = @pycheckn ccall(pyfunc(:PyImport_AddModule),
441+
main = @pycheckn ccall((@pyfunc :PyImport_AddModule),
429442
PyPtr, (Ptr{Uint8},),
430443
bytestring("__main__"))
431-
PyObject(@pycheckni ccall(pyfunc(:PyObject_GetAttrString), PyPtr,
444+
PyObject(@pycheckni ccall((@pyfunc :PyObject_GetAttrString), PyPtr,
432445
(PyPtr, Ptr{Uint8}), main,
433446
bytestring("__builtins__")))[bytestring(name)]
434447
end
@@ -477,14 +490,14 @@ function pycall(o::PyObject, returntype::TypeTuple, args...)
477490
else
478491
kw = PyObject(C_NULL)
479492
end
480-
arg = PyObject(@pycheckn ccall(pyfunc(:PyTuple_New), PyPtr, (Int,),
493+
arg = PyObject(@pycheckn ccall((@pyfunc :PyTuple_New), PyPtr, (Int,),
481494
nargs))
482495
for i = 1:nargs
483-
@pycheckzi ccall(pyfunc(:PyTuple_SetItem), Cint, (PyPtr,Int,PyPtr),
496+
@pycheckzi ccall((@pyfunc :PyTuple_SetItem), Cint, (PyPtr,Int,PyPtr),
484497
arg, i-1, oargs[i])
485498
pyincref(oargs[i]) # PyTuple_SetItem steals the reference
486499
end
487-
ret = PyObject(@pycheckni ccall(pyfunc(:PyObject_Call), PyPtr,
500+
ret = PyObject(@pycheckni ccall((@pyfunc :PyObject_Call), PyPtr,
488501
(PyPtr,PyPtr,PyPtr), o, arg, kw))
489502
jret = convert(returntype, ret)
490503
return jret
@@ -499,15 +512,15 @@ const pyeval_fname = bytestring("PyCall.jl") # filename for pyeval
499512
# (string/symbol => value) of local variables to use in the expression
500513
function pyeval_(s::String, locals::PyDict)
501514
sb = bytestring(s) # use temp var to prevent gc before we are done with o
502-
o = PyObject(@pycheckn ccall(pyfunc(:Py_CompileString), PyPtr,
515+
o = PyObject(@pycheckn ccall((@pyfunc :Py_CompileString), PyPtr,
503516
(Ptr{Uint8}, Ptr{Uint8}, Cint),
504517
sb, pyeval_fname, Py_eval_input))
505-
main = @pycheckni ccall(pyfunc(:PyImport_AddModule),
518+
main = @pycheckni ccall((@pyfunc :PyImport_AddModule),
506519
PyPtr, (Ptr{Uint8},),
507520
bytestring("__main__"))
508-
maindict = @pycheckni ccall(pyfunc(:PyModule_GetDict), PyPtr, (PyPtr,),
521+
maindict = @pycheckni ccall((@pyfunc :PyModule_GetDict), PyPtr, (PyPtr,),
509522
main)
510-
PyObject(@pycheckni ccall(pyfunc(:PyEval_EvalCode),
523+
PyObject(@pycheckni ccall((@pyfunc :PyEval_EvalCode),
511524
PyPtr, (PyPtr, PyPtr, PyPtr),
512525
o, maindict, locals))
513526
end

src/callback.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function pymethod(f::Function, name::String, flags::Integer)
2020
$(cfunction(f, PyPtr, (PyPtr,PyPtr))),
2121
convert(Cint, $flags),
2222
convert(Ptr{Uint8}, C_NULL))
23-
PyObject(@pycheckn ccall(pyfunc(:PyCFunction_NewEx), PyPtr,
23+
PyObject(@pycheckn ccall((@pyfunc :PyCFunction_NewEx), PyPtr,
2424
(Ptr{PyMethodDef}, Ptr{Void}, Ptr{Void}),
2525
&eval(def), C_NULL, C_NULL))
2626
end
@@ -45,8 +45,8 @@ function jl_Function_call(self_::PyPtr, args_::PyPtr, kw_::PyPtr)
4545
ret_ = ret.o
4646
ret.o = C_NULL # don't decref
4747
catch e
48-
ccall(pyfunc(:PyErr_SetString), Void, (PyPtr, Ptr{Uint8}),
49-
pyfunc(:PyExc_RuntimeError),
48+
ccall((@pyfunc :PyErr_SetString), Void, (PyPtr, Ptr{Uint8}),
49+
(@pyfunc :PyExc_RuntimeError),
5050
bytestring(string("Julia exception: ", e)))
5151
finally
5252
args.o = C_NULL # don't decref

0 commit comments

Comments
 (0)