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.mojo_importer
import os
import sys

# These two lines are temporary workarounds.
sys.path.insert(0, "")
os.environ["MOJO_PYTHON_LIBRARY"] = ""

import hello_mojo

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

Importing the max._mojo.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.

14 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.