Initial support for calling Mojo from Python

A new language feature has started to appear in the latest nightlies: the ability to call Mojo code from Python!

The Python interoperability section of the Mojo manual has been expanded and now includes a dedicated document on calling Mojo from Python. We’ve also added a couple of new examples to the modular GitHub repository: a “hello world” that shows how to round-trip from Python to Mojo and back, and one that shows how even Mojo code that uses the GPU can be called from Python. This is usable through any of the ways of installing MAX and the Mojo compiler: via pip install modular / pip install max, or with Conda via Magic / Pixi.

One of our goals has been the progressive introduction of MAX and Mojo into the massive Python codebases out in the world today. We feel that enabling selective migration of performance bottlenecks in Python code to fast Mojo (especially Mojo running on accelerators) will unlock entirely new applications. I’m really excited for how this will expand the reach of the Mojo code many of you have been writing.

Let’s look at a simple example to see how this works today. Once you have one of the latest nightly packages installed, you can create a Mojo file that will be referenced from Python which looks something like this:

hello_mojo.mojo

from python import PythonObject, PythonModule
from python.bindings import PythonModuleBuilder
from os import abort

@export
fn PyInit_hello_mojo() -> PythonObject:
    try:
        var module = PythonModuleBuilder("hello_mojo")
        module.def_function[passthrough]("passthrough")
        return module.finalize()
    except e:
        return abort[PythonObject](
            String("failed to create Python module: ", e)
        )

fn passthrough(value: PythonObject) raises -> PythonObject:
    return value + " world from Mojo"

The new elements in this Mojo file are at the top, where we configure and publish an interface for this Mojo module that describes how this Mojo code will appear to Python. The Mojo function itself uses existing Python - Mojo interoperability types and functions.

Using this Mojo module from Python is simple:

hello.py

import max.mojo.importer
import os
import sys

# These two lines are temporary workarounds.
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, current_dir)

import hello_mojo

if __name__ == "__main__":
    result = hello_mojo.passthrough("Hello")
    print(result)

Importing the max.mojo.importer will let the Mojo compiler automatically compile and cache any Mojo modules that are imported by the Python script. There are some temporary workarounds in this Python script at top that we will remove over time (these are known issues that are being worked on), but the usage of the Mojo code is otherwise straightforward. The Mojo looks to Python just like any other Python module and function, and all it takes to run this script is

python hello.py

It has taken months of deep technical work to get to this point, and this is just the first step in the roll-out of this new language feature. I strongly recommend reading the list of current known limitations to understand what may not work just yet, both to avoid potential frustration and to prevent the filing of duplicate issues for known areas that we’re working on.

We are really interested in what you’ll build with this new functionality, as well as hearing your feedback about how this could be made even better. Feel free to leave comments in this thread, or ask any questions you might have.

18 Likes

This is great! I had to manually compile the shared library

❯ python3 hello.py
Traceback (most recent call last):
  File "/Users/mseritan/dev/py2mojo/hello.py", line 9, in <module>
    import hello_mojo
ModuleNotFoundError: No module named 'hello_mojo'

❯ mojo --version
Mojo 25.4.0.dev2025052605 (5b562fcd)

❯ mojo build hello.mojo --emit shared-lib -o hello_mojo.so

❯ python3 hello.py
Hello world from Mojo
(py2mojo)
~/dev/py2mojo with 🔥 25.4.0.dev2025052605 via 🐍 v3.13.3 via 🧚 (default) on ☁️  (us-east-1)
❯

What’s your system and installation method (Conda. pip, etc.)? That should handle compilation for you, so maybe something’s getting missed on a particular combination there.

I used magic to create a mojoproject.

Hi @BradLarson, this is a fantastic feature. I have been waiting for it for a long time.

I ran the factorial example in the docs. It was significantly slower (~10x) than the Python built-in factorial function (from the math library). I think the Python version is implemented in C, so it should be fast. But I was expecting similar performance from the Mojo bindings.

Am I doing something wrong? Do I need to run the build with optimizations on? I did not see a way to do it in the example. Maybe I need to do it manually.

Best,

I gave this a spin today and was super excited! Immediately wrote a blogpost about it too:

May look into writing another one soon on how to build Python packages with it. Props!

7 Likes

Hi @BradLarson,

It’s fantastic to see Mojo’s interoperability with Python improving!

My primary interest lies in using Mojo functions for memory-efficient computations on NumPy ND-arrays and Polars or Pandas DataFrames for row, column or element-wise operations where Python can be notoriously slow.

Is it currently possible to pass views of ND-arrays or DataFrames to Mojo functions without causing a data copy, perform computations, and then return the modified views directly back to Python? If so, are examples available?

I’m a Mojo starter. Is this the right place to ask such questions?

A better native interface in Mojo to NumPy arrays is on our roadmap, but there are hacky ways right now to work on the underlying pointer without incurring a copy. One snippet I’ve seen to do this would be something like the following in Mojo:

my_array = np.zeros(result_length, dtype=np.intp)
my_array_data = my_array_argmax.ctypes.data.unsafe_get_as_pointer[DType.uint64]()

(The array initialization is just there as an example, you’d get the array from the Python side.)

Here’s a recent hackathon project that modeled heat transfer on the GPU in Mojo and then provided the results back in a NumPy array for visualization.

1 Like