New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python callback to a Julia function? #474
Comments
I don't think you can do this with the You'll have to do something like outer_vector = np.array([0])
def callback(inner_vector):
inner_vector[0] = 1
jl.MyModule.caller(callback, outer_vector)
assert outer_vector[0] == 1 |
First, Second and more importantly, even putting this aside, a plain callback doesn't work. For example:
Gives the error message:
It seems that a Python function object is not converted to a Julia function, instead it is wrapped into a generic |
Julia can call Python and Pythin can call Julia so this is possible. The workaround is somewhat convoluted:
Running this prints So, back to the feature request: Can we have a built-in conversion rule that takes plain-old Python functions and lambdas and wraps them as Julia functions. This would allow passing functions as arguments without having to use the above workaround (that is, remove the need for defining and using |
Well no, they are wrapped as a generic Your issue is simply that you've got a
|
I'm pretty sure we were talking at cross purposes about the |
Yes, there are two issues - My later post showed a workaround around both issues which requires writing manual wrappers. So it is possible to do achieve what I want (given writing the manual wrappers), which is great! That said, ideally one should not have to write such wrappers:
Makes sense? |
I'm happy to consider the PyFunction idea - feel free to make a separate issue about that. |
I don't understand what you want |
Something along the lines of the following (up to bikeshedding on the names and exact syntax): from contextlib import contextmanager
from juliacall import Main as jl # type: ignore
from typing import Any
from typing import Callable
from typing import Iterator
#: This would not be needed if/when issue #477 is resolved.
jl.seval("""
function py_function_to_fulia_function(py_object::Py)::Function
return (args...; kwargs...) -> pycall(py_object, args...; kwargs...)
end
""")
# Example Julia caller function.
jl.seval("""
function jl_caller(callback::Function, positional:: AbstractString; named:: AbstractString)::Any
extra = 1
return callback(positional, named, extra) # All must be positional.
end
""")
# Example Python callback function.
def py_callback(first: str, second: str, third: int) -> Any:
print(f"first: {first}")
print(f"second: {second}")
print(f"third: {third}")
return 7
# Pass a callback as an explicit Function parameter. Return value is available.
returned = jl.jl_caller(jl.py_function_to_fulia_function(py_callback), "positional", named ="named")
print(f"returned: {returned}")
# Proposed addition to `juliacall`, converts Python `with` to work similarly to Julia's `do`.
@contextmanager
def jl_do(jl_caller: Callable, *args: Any, **kwargs: Any) -> Iterator[Any]:
def capture(*args: Any) -> Iterator[Any]:
if len(args) == 1:
yield args[0]
else:
yield args
yield from jl_caller(jl.py_function_to_fulia_function(capture), *args, **kwargs)
# Use in `with` statement. No return value.
with jl_do(jl.jl_caller, "positional", named = "named") as args:
print(f"args: {args}") |
Could you explain some more how this is useful? I don't understand the utility of |
Consider Julia jl_caller("positional", named="named") do first, second, third
println("first: $(first)")
println("second: $(second)")
println("third: $(third)")
end Compared to Python with jl_do(jl.jl_caller, "positional", named="named") as (first, second, third):
print(f"first: {first}")
print(f"second: {second}")
print(f"third: {third}") Looks mighty similar to me. |
If I have a Julia function which takes a callback (artificial example here):
And I'd like to call it from Python - it seems not possible to do so? Ideally:
I have a Julia package that uses callbacks for various functions (for example, initializing arrays), and I'm trying to wrap it with a Python interface. Being able to zero-copy pass around numpy arrays is a godsend, but it seems that callbacks of the above type are not supported. Looking at the code I see the tests for "callback" are empty...
Is there some manual workaround I could use in my code instead of direct support for the above? Any way at all, as long as I can bury the boilerplate code in my Python wrappers so the end user can use the
with
statement.The text was updated successfully, but these errors were encountered: