# Initial support for calling Mojo from Python

**URL:** <https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514>\
**Category:** Python Interop\
**Created:** [May 25, 2025, 3:28pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514 "2025-05-25T15:28:42Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![BradLarson](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/bradlarson/32/62_2.png) [@BradLarson](https://forum.modular.com/u/BradLarson)\
**Post date:** [May 25, 2025, 3:28pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/1 "2025-05-25T15:28:42Z")

</div>

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](https://docs.modular.com/mojo/manual/python/) has been expanded and now includes a dedicated document on [calling Mojo from Python](https://docs.modular.com/mojo/manual/python/mojo-from-python). We’ve also added [a couple of new examples](https://github.com/modular/modular/tree/main/mojo/examples/python-interop) 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`](https://forum.modular.com/t/pip-install-modular-now-available/1382), 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**

```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**

```python
import 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 `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](https://docs.modular.com/mojo/manual/python/mojo-from-python#known-limitations) 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

```sh
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](https://docs.modular.com/mojo/manual/python/mojo-from-python#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.

---

<div class="post-metadata">

**Author:** ![mseritan](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/mseritan/32/130_2.png) [@mseritan](https://forum.modular.com/u/mseritan)\
**Post date:** [May 27, 2025, 12:33am UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/2 "2025-05-27T00:33:15Z")

</div>

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

```auto
❯ 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)
❯

```

---

<div class="post-metadata">

**Author:** ![BradLarson](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/bradlarson/32/62_2.png) [@BradLarson](https://forum.modular.com/u/BradLarson)\
**Post date:** [May 27, 2025, 3:31pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/3 "2025-05-27T15:31:51Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![mseritan](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/mseritan/32/130_2.png) [@mseritan](https://forum.modular.com/u/mseritan)\
**Post date:** [May 27, 2025, 4:15pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/4 "2025-05-27T16:15:15Z")

</div>

I used magic to create a mojoproject.

---

<div class="post-metadata">

**Author:** ![timdavis](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/timdavis/32/52_2.png) [@timdavis](https://forum.modular.com/u/timdavis)\
**Post date:** [May 27, 2025, 7:52pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/5 "2025-05-27T19:52:04Z")

</div>

Pinned globally on May 27, 2025, 7:52pm UTC



---

<div class="post-metadata">

**Author:** ![msdiallo](https://avatars.discourse-cdn.com/v4/letter/m/f07891/32.png) [@msdiallo](https://forum.modular.com/u/msdiallo)\
**Post date:** [June 4, 2025, 5:24pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/6 "2025-06-04T17:24:50Z")

</div>

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,

---

<div class="post-metadata">

**Author:** ![koaning](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/koaning/32/572_2.png) [@koaning](https://forum.modular.com/u/koaning)\
**Post date:** [June 17, 2025, 8:42am UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/7 "2025-06-17T08:42:45Z")

</div>

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

> **[Python can run Mojo now](https://koaning.io/posts/giving-mojo-a-spin/)**
>
> Chris Latner mentioned that Python can actually call Mojo code now. I love this idea (!) as I'm definitely in the market for a simple compiled language that can offer Python some really fast functions.

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

---

<div class="post-metadata">

**Author:** ![Olobor](https://avatars.discourse-cdn.com/v4/letter/o/67e7ee/32.png) [@Olobor](https://forum.modular.com/u/Olobor)\
**Post date:** [July 9, 2025, 5:34pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/8 "2025-07-09T17:34:01Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![BradLarson](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/bradlarson/32/62_2.png) [@BradLarson](https://forum.modular.com/u/BradLarson)\
**Post date:** [July 9, 2025, 5:54pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/9 "2025-07-09T17:54:34Z")

</div>

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:

```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](https://github.com/atooln/modular-hacks-physics-sim/blob/main/mojo/maxwell_equation.mojo#L125) that modeled heat transfer on the GPU in Mojo and then provided the results back in a NumPy array for visualization.

---

<div class="post-metadata">

**Author:** ![Olobor](https://avatars.discourse-cdn.com/v4/letter/o/67e7ee/32.png) [@Olobor](https://forum.modular.com/u/Olobor)\
**Post date:** [July 12, 2025, 11:33pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/10 "2025-07-12T23:33:46Z")

</div>

Thanks @BradLarson,

Will it be possible (or is it already be possible) to pass a Numpy array argument from the Python side and receive a memory view on the Mojo side. Below is an example:

```auto
from python import Python, PythonObject
from sys.intrinsics import _unsafe_aliasing_address_to_pointer

# Numpy array as argument
fn add_one(np_arr: PythonObject) raises:
    var n = Int(np_arr.shape[0])
    var address = Scalar[DType.index](Int(np_arr. __array_interface__ ["data"][0]))
    var arr_view = _unsafe_aliasing_address_to_pointer[DType.int64](address)
    for i in range(n):
        arr_view[i] += 1

# Memmory view as argument (implicit conversion from Numpy array into a view)
# fn add_one(arr_view: NDARRAY_VIEW[DType.int64, ndim=1]):
    # ...

def main():
    var np = Python.import_module("numpy")
    var np_arr = np.array(Python.list(1, 2, 3), dtype=np.int64)
    add_one(np_arr)
    print(np_arr)

```

Edit: I assume memoryview support for python buffer protocol is still a work in progress.  
Here is a related feature request:

> <https://github.com/modular/modular/issues/1515>
>
> \### Review Mojo's priorities
> 
> \- \[X\] I have read the \[roadmap and priorities\](h…ttps://docs.modular.com/mojo/roadmap.html#overall-priorities) and I believe this request falls within the priorities.
> 
> \### What is your request?
> 
> This enhancement request is to add support for Python's \`memoryview\` builtin and support for python buffer protocol. Here are some ideas about what kind of tasks and level of effort might be involved:
> 
> \- Add a new Mojo trait (\`Bufferable\`?) which has dunder methods: \` \_\_buffer\_\_\` and \` \_\_release\_buffer\_\_\`.
> \- Add support for python's builtin \`memoryview()\` on Mojo structs. \`\_\_buffer\_\_\` returns \`memoryview\` so this has to be builtin to Mojo (not a python module import).
> \- Add support for the C data interface defined here \[python buffer protocol\](https://docs.python.org/3.8/c-api/buffer.html#bufferobjects). This would allow Mojo structs implementing the C data interface as \`Py\_buffer\` https://docs.python.org/3/c-api/buffer.html to be called from Python. Or maybe they could be wrapped in a PythonObject and returned as a \`memoryview\`?
> 
> 
> \### What is your motivation for this change?
> 
> Currently Mojo 0.6 has poor (nonexistent?) support for zero-copy shared memory buffers with Python. 
> 
> For example in Mojo's documentation the \[Ray Tracing notebook\](https://docs.modular.com/mojo/notebooks/RayTracing.html) has an example of raster imagery being copied into a numpy array, using MLIR ops. Not only is this an unnecessary memory copy, it's also too verbose, undocumented, and not pythonic. See \`def to\_numpy\_image(self) -\> PythonObject:\` in \[source notebook\](https://docs.modular.com/mojo/notebooks/RayTracing.html).
> 
> Mojo should enable and encourage interop with existing scientific computing packages in the most efficient manner. For example the \[Apache Arrow format\](https://arrow.apache.org/). 
> 
> \> The Arrow C data interface is inspired by the \[Python buffer protocol\](https://www.python.org/dev/peps/pep-3118/), which has proven immensely successful in allowing various Python libraries exchange numerical data with no knowledge of each other and near-zero adaptation cost. \[Arrow Spec\](https://arrow.apache.org/docs/format/CDataInterface.html#inspiration)
> 
> This enhancement would also lay the groundwork for supporting the \[Python array API standard\](https://data-apis.org/array-api/latest/). 
> 
> \### Any other details?
> 
> \### Related Discussions/Issues:
> 
> \- https://github.com/modularml/mojo/issues/1210
> \- https://github.com/modularml/mojo/discussions/1281
> 
> \### Reference PEPs:
> 
> \- \[PEP 3118 – Revising the buffer protocol (Python 3.0)\](https://peps.python.org/pep-3118/)
> \- \[PEP 688 – Making the buffer protocol accessible in Python (Python 3.12)\](https://peps.python.org/pep-0688/)

---

<div class="post-metadata">

**Author:** ![BradLarson](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/bradlarson/32/62_2.png) [@BradLarson](https://forum.modular.com/u/BradLarson)\
**Post date:** [August 13, 2025, 10:21pm UTC](https://forum.modular.com/t/initial-support-for-calling-mojo-from-python/1514/11 "2025-08-13T22:21:21Z")

</div>

As a small update for this feature: as of the latest nightly, Python interoperability only requires the `mojo` or `mojo-compiler` Conda packages, not the full `max`. The invocation for the importer has been lifted out of the `max` module and is now:

```python
import mojo.importer

```

This should make it easier to incorporate Mojo into a Python project.
