This is really a PyCall question instead of a pure Julia question but maybe others will find it useful. Basically, I’m interested in the following steps:
- Define a one-argument Julia function.
- Obtain a
Ptr{Void}
to it using cfunction
.
- Store the function pointer in a python class (using PyCall).
- Sometime later, call it from the python class with an argument (
Ptr{Void}
again) sent from Julia.
Is this doable and/or coherent? Keywords and links to resources that could help me better understand this type of problem would be welcome too.
PyCall already does this. If you have a Julia function f(x)
, just pass it to a Python function (or, more explicitly, call PyObject(f)
) to convert it to a callable Python object. Internally, the Python object is a C struct with a tp_call
member that is a pointer to a cfunction
pointer, which calls back to the Julia function f
(which is compiled like any other Julia function).
Thanks for the quick reply. The issue for me with the current PyCall setup is that the callback is wrapped in a different Julia function. I’m trying to do a inter-thread notification system using uv_async_send
and so I can’t run Julia functions from the python-invoked callback.
Would it be feasible to transfer the callback wrapping you did in Julia to the python side? Any pointers/references for going that direction?
Here’s a trivial example of this setup. Save both files in the same directory and just run julia PyCB.jl
. Note that this doesn’t work on julia master, I assume because of PyCall#343.
I assume this could work if I could somehow run cfunction
on cb
and pass it to be called directly by python.
PyCB.jl:
module PyCB
using PyCall
function cb(handle)
ccall(:uv_async_send, Cint, (Ptr{Void},), handle)
end
function go()
pycb_obj = pyimport("pycb")["CB"](cb)
c = Base.AsyncCondition()
@async begin
wait(c)
println("Notified!")
end
pycb_obj[:call](c.handle)
sleep(1)
end
function __init__()
pypath = PyVector(pyimport("sys")["path"])
if ! (dirname(@__FILE__) in pypath)
unshift!(pypath, dirname(@__FILE__))
end
end
end
if ! isinteractive()
PyCB.go()
end
pycb.py:
import threading
import time
class CB(object):
def __init__(self, cb):
self._cb = cb
def call(self, arg):
print("Starting thread...")
t = threading.Thread(target=self._cb, args=(arg,))
t.start()
time.sleep(1)
print("Exiting callback")
Doesn’t the python ctypes module have code to invoke a C function pointer? This should work fine with a cfunction.
Thanks, that pointer was what I needed. After a bit of googling I’ve got it working nicely.