PageRenderTime 26ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/src/callback.jl

https://gitlab.com/math4youbyusgroupillinois/PyCall.jl
Julia | 79 lines | 52 code | 10 blank | 17 comment | 7 complexity | e3b8761608706db421a4aeba3a46ebdb MD5 | raw file
  1. # Passing Julia callback functions to Python routines.
  2. #
  3. # Note that this will typically involve two functions: the
  4. # desired Julia function/closure, and a top-level C-callable
  5. # wrapper function used with PyCFunction_NewEx -- the latter
  6. # is called from Python and calls the former as needed.
  7. ################################################################
  8. # Define a Python method/function object from f(PyPtr,PyPtr)::PyPtr.
  9. # Requires f to be a top-level function.
  10. function pymethod(f::Function, name::String, flags::Integer)
  11. # Python expects the PyMethodDef structure to be a *constant*,
  12. # so we define an anonymous global to hold it.
  13. def = gensym("PyMethodDef")
  14. @eval const $def = PyMethodDef($name, $f, $flags)
  15. PyObject(@pycheckn ccall((@pysym :PyCFunction_NewEx), PyPtr,
  16. (Ptr{PyMethodDef}, Ptr{Void}, Ptr{Void}),
  17. &eval(def), C_NULL, C_NULL))
  18. end
  19. ################################################################
  20. # To pass an arbitrary Julia function to Python, we wrap it
  21. # in a jl_Function Python class, where jl_Function.__call__
  22. # executes the jl_Function_callback function in Julia, which
  23. # in turn fetches the actual Julia function from the "f" attribute
  24. # and calls it.
  25. function jl_Function_call(self_::PyPtr, args_::PyPtr, kw_::PyPtr)
  26. ret_ = convert(PyPtr, C_NULL)
  27. args = PyObject(args_)
  28. try
  29. f = unsafe_pyjlwrap_to_objref(self_)::Function
  30. if kw_ == C_NULL
  31. ret = PyObject(f(convert(PyAny, args)...))
  32. else
  33. kw = PyDict{Symbol,PyAny}(PyObject(kw_))
  34. kwargs = [ (k,v) for (k,v) in kw ]
  35. ret = PyObject(f(convert(PyAny, args)...; kwargs...))
  36. end
  37. ret_ = ret.o
  38. ret.o = convert(PyPtr, C_NULL) # don't decref
  39. catch e
  40. pyraise(e)
  41. finally
  42. args.o = convert(PyPtr, C_NULL) # don't decref
  43. end
  44. return ret_::PyPtr
  45. end
  46. const jl_Function_call_ptr = cfunction(jl_Function_call,
  47. PyPtr, (PyPtr,PyPtr,PyPtr))
  48. jl_FunctionType = PyTypeObject()
  49. function pycallback_initialize()
  50. global jl_FunctionType
  51. if (jl_FunctionType::PyTypeObject).tp_name == C_NULL
  52. jl_FunctionType::PyTypeObject =
  53. pyjlwrap_type("PyCall.jl_Function",
  54. t -> t.tp_call = jl_Function_call_ptr)
  55. end
  56. return
  57. end
  58. function pycallback_finalize()
  59. global jl_FunctionType
  60. jl_FunctionType::PyTypeObject = PyTypeObject()
  61. end
  62. function pycallback(f::Function)
  63. global jl_FunctionType
  64. if (jl_FunctionType::PyTypeObject).tp_name == C_NULL
  65. pycallback_initialize()
  66. end
  67. pyjlwrap_new(jl_FunctionType::PyTypeObject, f)
  68. end
  69. PyObject(f::Function) = pycallback(f)