How to pass Mojo function to Python in Python interop?

The question is how to pass Mojo function to Python in Python interop? For example,
# This is main.mojo
from python.python import Python

def callback():
return 5

def main():
Python.add_to_path(".")
let test_module = Python.import_module("lib")
print(test_module.test_interop(callback))
# This is main.mojo
from python.python import Python

def callback():
return 5

def main():
Python.add_to_path(".")
let test_module = Python.import_module("lib")
print(test_module.test_interop(callback))
# This is lib.py
def test_interop(func):
return func()
# This is lib.py
def test_interop(func):
return func()
If I run this, it will show the following message:
$ main.mojo
main.mojo:9:33: error: invalid call to '__call__': argument #1 cannot be converted from 'fn() raises -> object' to 'PythonObject'
print(test_module.test_interop(callback))
~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
main.mojo:1:1: note: function declared here
from python.python import Python
^
mojo: error: failed to parse the provided Mojo
$ main.mojo
main.mojo:9:33: error: invalid call to '__call__': argument #1 cannot be converted from 'fn() raises -> object' to 'PythonObject'
print(test_module.test_interop(callback))
~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
main.mojo:1:1: note: function declared here
from python.python import Python
^
mojo: error: failed to parse the provided Mojo
Question originally posted by HKTonyLee on SO: https://stackoverflow.com/questions/77063677/how-to-pass-mojo-function-to-python-in-python-interop
Stack Overflow
How to pass Mojo function to Python in Python interop?
The question is how to pass Mojo function to Python in Python interop? For example, This is main.mojo from python.python import Python def callback(): return 5 def main(): Python.add_to_p...
8 Replies
Business
Business13mo ago
Not tested so sorry if it’s wrong, but there may be a way to turn the mojo ‘object’ (5) into a PythonObject (5)
Jack Clayton
Jack Clayton13mo ago
Hi @Mahmoud Abduljawad you need to pass a concrete type like Int, String etc so it knows how to convert it. Also you need to use a function that actually exists e.g:
from python import Python


def callback() -> Int:
return 5


def main():
Python.add_to_path(".")
let pprint = Python.import_module("pprint")
pprint.pprint(callback())
from python import Python


def callback() -> Int:
return 5


def main():
Python.add_to_path(".")
let pprint = Python.import_module("pprint")
pprint.pprint(callback())
Mahmoud Abduljawad
The function returns an object created by python lib, effectively, PythonObject. If I set the return type of function as such, marshalling fails. This is another example where this fails:
# hello.py
from typing import Callable

class MyClass:
def __init__(self, arg1: str):
self.arg1 = arg1

def print_arg1(self) -> None:
print(self.arg1)

def do(func: Callable) -> None:
func()

if __name__ == "__main__":
MyClass("Hello, world!").print_arg1()
# hello.py
from typing import Callable

class MyClass:
def __init__(self, arg1: str):
self.arg1 = arg1

def print_arg1(self) -> None:
print(self.arg1)

def do(func: Callable) -> None:
func()

if __name__ == "__main__":
MyClass("Hello, world!").print_arg1()
# hello.mojo
from python.python import (
Python,
_destroy_python,
_init_python,
)
from python.object import PythonObject

def print_arg1() -> None:
Python.add_to_path(".")
let hello = Python.import_module("hello")
let my_class = hello.MyClass("Hello, world from Mojo!")
print(my_class.arg1)

def main():
Python.add_to_path(".")
let hello = Python.import_module("hello")
let my_class = hello.MyClass("Hello, world from Python!")
print(my_class)
hello.do(my_class.print_arg1)
hello.do(print_arg1)
# hello.mojo
from python.python import (
Python,
_destroy_python,
_init_python,
)
from python.object import PythonObject

def print_arg1() -> None:
Python.add_to_path(".")
let hello = Python.import_module("hello")
let my_class = hello.MyClass("Hello, world from Mojo!")
print(my_class.arg1)

def main():
Python.add_to_path(".")
let hello = Python.import_module("hello")
let my_class = hello.MyClass("Hello, world from Python!")
print(my_class)
hello.do(my_class.print_arg1)
hello.do(print_arg1)
Error:
hello.mojo:37:11: error: invalid call to '__call__': argument #1 cannot be converted from 'fn() raises -> None' to 'PythonObject'
hello.do(print_arg1)
~~~~~~~~^~~~~~~~~~~~
hello.mojo:1:1: note: function declared here
# ===----------------------------------------------------------------------=== #
^
mojo: error: failed to parse the provided Mojo
hello.mojo:37:11: error: invalid call to '__call__': argument #1 cannot be converted from 'fn() raises -> None' to 'PythonObject'
hello.do(print_arg1)
~~~~~~~~^~~~~~~~~~~~
hello.mojo:1:1: note: function declared here
# ===----------------------------------------------------------------------=== #
^
mojo: error: failed to parse the provided Mojo
Jack Clayton
Jack Clayton13mo ago
Oh you want to pass a callback in Mojo to Python class? You can't do that at the moment sorry, interesting concept though
Mahmoud Abduljawad
I see, @Jack Clayton. I'm trying to offload some of the logic from Python to Mojo to compare performance gains in such cases. Of course this is the simplest explanation, however, I was hoping to start building a sample web application with Mojo built on top of AIOHTTP which won't be possible at the moment due to this limitation. Do I need to open an issue to track this?
ModularBot
ModularBot13mo ago
Congrats @Mahmoud Abduljawad, you just advanced to level 1!
Jack Clayton
Jack Clayton13mo ago
Yeah I think it's a good one for https://github.com/modularml/mojo/discussions/new?category=ideas I'm sure of the difficulty level of implementing something like that but I'd be interested to see what the engineers have to say
Mahmoud Abduljawad
GitHub
Allow passing Mojo function as argument to Python function · modula...
Currently, Mojo doesn't support passing Mojo functions as arguments to Python functions. Certain Python libs accept a callable as an argument which runs side effects, and Python interop in Mojo...
Want results from more Discord servers?
Add your server