> For the complete Mojo documentation index, see [llms.txt](/llms.txt).
> Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

# Calling Mojo from Python

If you have an existing Python project that would benefit from Mojo's
high-performance computing, you shouldn't have to rewrite the whole thing in
Mojo. Instead, you can write just the performance-critical parts your code in
Mojo and then call it from Python.

:::experiment Beta feature

Calling Mojo code from Python is in early development. You should expect a lot
of changes to the API and ergonomics. Likewise, this documentation is still a
work in progress. See below for [known limitations](#known-limitations).

:::

## Import a Mojo module in Python

To illustrate what calling Mojo from Python looks like, we'll start with a
simple example, and then dig into the details of how it works and what is
possible today.

Consider a project with the following structure:

```text
project
├── 🐍 main.py
└── 🔥 mojo_module.mojo
```

The main entrypoint is a Python program called `main.py`, and the Mojo code
includes functions to call from Python.

For example, let's say we want a Mojo function to take a Python value as an
argument:

```mojo title="mojo_module.mojo"
def factorial(py_obj: PythonObject) raises -> Python
    var n = Int(py=py_obj)
    return math.factorial(n)
```

And we want to call it from Python like this:

```python title="main.py"
import mojo_module

print(mojo_module.factorial(5))
```

However, before we can call the Mojo function from Python, we must declare it
so Python knows it exists.

Because Python is trying to load `mojo_module`, it looks for a function called
`PyInit_mojo_module()`. (If our file was called `foo.mojo`, the function Python
looked for would be `PyInit_foo()`.) Within the `PyInit_mojo_module()`, we must
declare all Mojo functions and types that are callable from Python using
[`PythonModuleBuilder`](/docs/std/python/bindings/PythonModuleBuilder/).

So the complete Mojo code looks like this:

```mojo title="mojo_module.mojo"
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std import math
from std.os import abort

@export
def PyInit_mojo_module() -> PythonObject:
    try:
        var m = PythonModuleBuilder("mojo_module")
        m.def_function[factorial](https://mojolang.org/docs/manual/python/"factorial", docstring="Compute n!".md)
        return m.finalize()
    except e:
        abort(String("error creating Python Mojo module:", e))

def factorial(py_obj: PythonObject) raises -> PythonObject:
    # Raises an exception if `py_obj` is not convertible to a Mojo `Int`.
    var n = Int(py=py_obj)

    return math.factorial(n)
```

On the Python side, we add the directory containing `mojo_module.mojo` to the
Python path, and then use a normal `import` statement to load our Mojo code:

```python title="main.py"
import mojo.importer
import mojo_module

print(mojo_module.factorial(5))
```

That's it! Try it:

```sh
python main.py
```

```output
120
```

### How it works

Python supports a standard mechanism called [Python extension
modules](https://docs.python.org/3/extending/extending.html) that enables
compiled languages (like Mojo, C, C++, or Rust) to make themselves callable
from Python in an intuitive way. Concretely, a Python extension module is
simply a dynamic library that defines a suitable `PyInit_*()` function.

Mojo comes with built-in functionality for defining Python extension modules.
The special stuff happens in the `mojo.importer` module we imported.

If we have a look at the filesystem after Python imports the Mojo code, we'll
notice there's a new `__mojocache__` directory, with a dynamic library (`.so`)
file inside:

```text
project
├── main.py
├── mojo_module.mojo
└── __mojocache__
    └── mojo_module.hash-ABC123.so
```

Loading `mojo.importer` loads our Python Mojo
[import hook](https://docs.python.org/3/reference/import.html#import-hooks),
which behind the scenes looks for a `.mojo` file that matches the imported
module name, and if found, compiles it using
[`mojo build --emit shared-lib`](/docs/cli/build/#--emit-file_type) to generate
a dynamic library. The resulting file is stored in `__mojocache__`, and is
rebuilt only when it becomes stale (typically, when the Mojo source file
changes).

:::note Clearing cached build artifacts
The `__mojocache__` directory should contain only derived artifacts. It is
always safe to delete the contents of a `__mojocache__` directory. Needed
artifacts will simply be rebuilt the next time the Mojo module is imported.
:::

Now that we've looked at the basics of how Mojo can be used from Python, let's
dig into the available features and how you can leverage them to accelerate
your Python with Mojo.

## Bindings features

### Binding Mojo types

You can bind any Mojo type for use in Python using
[`PythonModuleBuilder`](/docs/std/python/bindings/PythonModuleBuilder/).
For example:

```mojo
@fieldwise_init
struct Person(Movable, Writable):
    var name: String
    var age: Int

@export
def PyInit_person_module() -> PythonObject:
    try:
        var mb = PythonModuleBuilder("person_module")
        var person_type = mb.add_type[Person](https://mojolang.org/docs/manual/python/"Person".md)
    except e:
        abort("error creating Mojo module")
```

When you call
[`add_type()`](/docs/std/python/bindings/PythonModuleBuilder/#add_type), it
returns a
[`PythonTypeBuilder`](/docs/std/python/bindings/PythonTypeBuilder/), which
you can then use to bind the type constructor (see [binding Python
initializers](#constructing-mojo-objects-in-python), below) and methods.

Any Mojo type bound using a `PythonTypeBuilder` has the resulting Python
'type' object globally registered, enabling two features:

- Constructing Python objects that wrap Mojo values for use from Python using
  `PythonObject(alloc=Person(..))`.

- Downcasting using `python_obj.downcast_value_ptr[Person]()`

:::note

Mojo types must implement
[`Writable`](/docs/std/format/Writable/) to be bound for use
in Python. Additional traits are required for specific binding features:
`Movable` for custom initializers (`def_py_init`), and both `Defaultable` and
`Movable` for default initializers (`def_init_defaultable`).

:::

However, merely binding a Mojo type to a Python `type` object isn't very useful
on its own. Next, we'll tell Python how to interact with our Mojo type—starting
with how to construct instances of our Mojo type from within Python.

### Constructing Mojo objects in Python

Mojo types can be constructed from Python by declaring a Mojo constructor
function as a Python-compatible object initializer using
[`def_py_init()`](/docs/std/python/bindings/PythonTypeBuilder/#def_py_init) when
you add the type to your module. For example:

```mojo
@export
def PyInit_person_module() -> PythonObject:
    try:
        var mb = PythonModuleBuilder("person_module")
        # highlight-start
        _ = mb.add_type[Person](https://mojolang.org/docs/manual/python/"Person".md).def_py_init[Person.py_init]()
        # highlight-end
        return mb.finalize()
    except e:
        abort(String("error creating Python Mojo module:", e))

@fieldwise_init
struct Person(Movable, Writable):
    var name: String
    var age: Int

    # highlight-start
    @staticmethod
    def py_init(
        out self: Person, args: PythonObject, kwargs: PythonObject
    ) raises:
        # Validate argument count
        if len(args) != 2:
            raise Error("Person() takes exactly 2 arguments")

        # Convert Python arguments to Mojo types
        var name = String(args[0])
        var age = Int(args[1])

        self = Self(name, age)
    # highlight-end
```

With this Mojo binding, you can create `Person` instances in Python:

```python
person = person_module.Person("Sarah", 32)
print(person)
```

```output
Person(name=Sarah, age=32)
```

For types that support default construction, you can use the simpler
[`def_init_defaultable()`](/docs/std/python/bindings/PythonTypeBuilder/#def_init_defaultable)
method:

```mojo
var counter_type = m.add_type[Counter](https://mojolang.org/docs/manual/python/"Counter".md)
counter_type.def_init_defaultable[Counter]()
```

This enables Python code to create instances without arguments:

```python
counter = counter_module.Counter()  # Creates Counter()
```

:::note "Constructor" vs "Initializer"

In Python, object construction happens across both the `__new__()` and
`__init__()` methods, so the `__init__()` method is technically just the
attribute initializer. However, in a Mojo struct, there's no `__new__()`
method, so we prefer to always call `__init__()` the constructor.

:::

### Returning Mojo objects to Python

Mojo functions called from Python don't just need to be able to accept
[`PythonObject`](/docs/std/python/python_object/PythonObject/) values as
arguments, they also need to be able to return new values. And sometimes, they
even need to be able to return Mojo native values back to Python. This is
possible by using the `PythonObject(alloc=<value>)` constructor.

An example of this looks like:

```mojo
def create_person() -> PythonObject:
    var person = Person("Sarah", 32)
    return PythonObject(alloc=person^)
```

:::caution

`PythonObject(alloc=...)` will raise an exception if the provided Mojo object
type had not previously been registered using
[`PythonModuleBuilder.add_type()`](/docs/std/python/bindings/PythonModuleBuilder/#add_type).

:::

### `PythonObject` to Mojo values

Within any Mojo code that is handling a
[`PythonObject`](/docs/std/python/python_object/PythonObject/), but
especially within Mojo functions called from Python, it's common to expect an
argument of a particular type.

There are two ways in which a `PythonObject` can be turned into a native
Mojo value:

- **Converting** a Python object into a newly constructed Mojo value that has
  the same logical value as the original Python object.
  This is handled by the [`ConvertibleFromPython`][ConvertibleFromPython] trait.

- **Downcasting** a Python object that holds a native Mojo value to a pointer
  to that inner value.
  This is handled by [`PythonObject.downcast_value_ptr()`][downcast_value_ptr].

#### `PythonObject` conversions

Many Mojo types support conversion directly from equivalent Python types, via
the [`ConvertibleFromPython`][ConvertibleFromPython] trait:

```mojo
# Given a person, clone them and give them a different name.
def create_person(
    name_obj: PythonObject,
    age_obj: PythonObject
) raises -> PythonObject:
    # These conversions will raise an exception if they fail
    var name = String(name_obj)
    var age = Int(age_obj)

    return PythonObject(alloc=Person(name, age))
```

Which could be called from Python using:

```python
person = mojo_module.create_person("John Smith")
```

Passing invalid arguments will result in a runtime argument error:

```python
person = mojo_module.create_person(42)
```

#### `PythonObject` downcasts

Downcasting from `PythonObject` values to the inner Mojo value:

```mojo
def print_age(person_obj: PythonObject) raises:
    # Raises if `obj` does not contain an instance of the Mojo `Person` type.
    var person = person_obj.downcast_value_ptr[Person]()

    print("Person is", person[].age, "years old")
```

Unsafe mutation via downcasting is also supported. It is up to the user to
ensure that this mutable pointer does not alias any other pointers to the same
object within Mojo:

```mojo
def birthday(person_obj: PythonObject):
    var person = person_obj.downcast_value_ptr[Person]()

    person[].age += 1
```

Entirely unchecked downcasting—which does no type checking—can be done using:

```mojo
def get_person(person_obj: PythonObject):
    var person = person_obj.unchecked_downcast_value_ptr[Person]()
```

Unchecked downcasting can be used to eliminate overhead when optimizing a tight
inner loop with Mojo, and you've benchmarked and measured that type checking
downcasts is a significant bottleneck.

### Methods

When binding Mojo objects for use from Python, you can expose chosen methods to
Python as well, using
[`PythonTypeBuilder.def_method()`](/docs/std/python/bindings/PythonTypeBuilder/#def_method).

Currently, Mojo methods being exposed to Python must be written with a
modification compared to normal Mojo methods: they must be a `@staticmethod`
that takes either `py_self: PythonObject` or `self_ptr: UnsafePointer[Self]`:

```mojo
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std.os import abort

@export
def PyInit_mojo_module() -> PythonObject:
    try:
        var mb = PythonModuleBuilder("mojo_module")
        # highlight-start
        _ = mb.add_type[Person](https://mojolang.org/docs/manual/python/"Person".md)
            .def_method[Person.get_name](https://mojolang.org/docs/manual/python/"get_name".md)
            .def_method[Person.set_age](https://mojolang.org/docs/manual/python/"set_age".md)
        # highlight-end
        return mb.finalize()
    except e:
        abort("error creating Mojo module")

struct Person(Writable):
    var name: String
    var age: Int

    # highlight-start
    @staticmethod
    def get_name(py_self: PythonObject) raises -> PythonObject:
        var self_ptr = py_self.downcast_value_ptr[Self]()
        return self_ptr[].name

    @staticmethod
    def set_age(
        self_ptr: UnsafePointer[mut=True, Self],
        new_age: PythonObject,
    ) raises:
        self_ptr[].age = Int(new_age)
    # highlight-end

    def write_to(self, mut writer: Some[Writer]):
        t"Person({self.name}, {self.age})".write_to(writer)
```

Taking `py_self: PythonObject` allows access to the full `PythonObject`
allocation that a Mojo object instance is stored inside of. Typically though,
taking `py_self: UnsafePointer[Self]` will minimize boilerplate in the common
case that a method merely needs to access the fields of an object.

Mojo methods called from Python are currently required to take non-standard self
types due to limitations that will be lifted in future versions of Python Mojo
bindings.

### Static methods

Python Mojo bindings supports exposing Python `@staticmethods`, bound using
[`PythonTypeBuilder.def_staticmethod()`](/docs/std/python/bindings/PythonTypeBuilder/#def_staticmethod).
A function declared using `def_staticmethod()` is callable as a static method on
the type within Python, without needing an object instance.

```mojo
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder
from std.os import abort

@export
def PyInit_mojo_module() -> PythonObject:
    try:
        var mb = PythonModuleBuilder("mojo_module")
        # highlight-start
        mb.add_type[Person](https://mojolang.org/docs/manual/python/"Person".md)
            .def_staticmethod[Person.is_valid_age](https://mojolang.org/docs/manual/python/"is_valid_age".md)
        # highlight-end
        return mb.finalize()
    except e:
        abort("error creating Mojo module")

struct Person(Writable):
    var name: String
    var age: Int

    # highlight-start
    @staticmethod
    def is_valid_age(age_obj: PythonObject) raises -> PythonObject:
        var age = Int(age_obj)
        return 0 <= age <= 130
    # highlight-end

    def write_to(self, mut writer: Some[Writer]):
        t"Person({self.name}, {self.age})".write_to(writer)
```

Calling a Mojo function bound as a static method looks like a typical Python
static method call directly on the type object:

```python title="main.py"
from mojo_module import Person

print(Person.is_valid_age(45)) # Prints 'True'
print(Person.is_valid_age(-1)) # Prints 'False'
```

### Keyword arguments

[Keyword arguments in Mojo](/docs/manual/functions/#keyword-arguments) come in
two forms:

1. Keyword-only arguments: `def foo(*, x: Int)`
   This is not currently supported in Python Mojo bindings.
2. [Variadic keyword arguments](/docs/manual/functions/#variadic-keyword-arguments):
   `def foo(**kwargs: Int)` This is supported in Python Mojo bindings when used
   in the unsugared form: `def foo(kwargs: OwnedKwargsDict)`. (The `**kwargs`
   syntax limitation will be removed in the future.)

You can define Mojo functions that accept variadic keyword arguments using
[`OwnedKwargsDict[PythonObject]`](/docs/std/collections/dict/OwnedKwargsDict/)
as the last argument. A simple example looks like:

```python
import mojo_module

result = mojo_module.sum_kwargs_ints(a=10, b=20, c=30)  # returns 60
```

```mojo
from std.collections import OwnedKwargsDict

def sum_kwargs_ints(kwargs: OwnedKwargsDict[PythonObject]) raises -> PythonObject:
    var total = 0
    for entry in kwargs.items():
        total += Int(entry.value)
    return PythonObject(total)
```

Keyword arguments are also supported following normal positional arguments.
Additionally, getting specific keyword arguments is a dictionary lookup on the
`OwnedKwargsDict`:

```mojo
from std.collections import OwnedKwargsDict

def duration_in_seconds(
    hours_obj: PythonObject,
    minutes_obj: PythonObject,
    kwargs: OwnedKwargsDict[PythonObject]
) raises -> PythonObject:
    var hours = Int(hours_obj)
    var minutes = Int(minutes_obj)

    var seconds = Int(kwargs["seconds"])

    return hours * 3600 + minutes * 60 + seconds
```

In this example, if a call to `duration_in_seconds()` is missing the required
`"seconds"` named argument, a runtime exception will occur:

```python title="main.py"
from mojo_module import duration_in_seconds

# Pass hours and minutes, missing "seconds"
duration_in_seconds(4, 5) # ERROR: KeyError
```

Keyword arguments are supported when bindings top-level functions, methods, and
static methods.

### Variadic arguments

Python and Mojo variadic arguments are normally written using the following
syntax:

```mojo
def foo(*args: Int):
    ...
```

However, this syntax is not yet supported in Python/Mojo bindings, because
functions bound using
[`def_function()`](/docs/std/python/bindings/PythonModuleBuilder/#def_function)
support only fixed-arity functions.

As a workaround, you can expose Mojo functions that accept a variadic number of
arguments to Python using the lower-level
[`def_py_function()`](/docs/std/python/bindings/PythonModuleBuilder/#def_py_function)
interface, which leaves it to the user to validate the number of arguments
provided:

```mojo
@export
def PyInit_mojo_module() -> PythonObject:
    try:
        var b = PythonModuleBuilder("mojo_module")
        b.def_py_function[count_args](https://mojolang.org/docs/manual/python/"count_args".md)
        b.def_py_function[sum_args](https://mojolang.org/docs/manual/python/"sum_args".md)
        b.def_py_function[lookup](https://mojolang.org/docs/manual/python/"lookup".md)

def count_args(py_self: PythonObject, args_tuple: PythonObject) raises:
    return len(args_tuple)

def sum_args(py_self: PythonObject, args_tuple: PythonObject) raises:
    var total = args_tuple[0]
    for i in range(1, len(args_tuple)):
        total += args_tuple[i]
    return total

def lookup(py_self: PythonObject, args_tuple: PythonObject) raises:
    if len(args_tuple) != 2 and len(args_tuple) != 3:
        raise Error("lookup() expects 2 or 3 arguments")

    var collection = args_tuple[0]
    var key = args_tuple[1]

    try:
        return collection[key]
    except e:
        if len(args) == 3:
            return args_tuple[2]
        else:
            raise e
```

## Strategies for porting Python to Mojo

### Writing Pythonic code in Mojo

In this approach to bindings, we embrace the flexibility of Python, and eschew
trying to convert `PythonObject` arguments into the narrowly constrained,
strongly-typed space of the Mojo type system, in favor of just writing some code
and letting it raise an exception at runtime if we got something wrong.

The flexibility of `PythonObject` enables a unique programming style, wherein
Python code can be "ported" to Mojo with relatively few changes.

```python
def foo(x, y, z):
    x[y] = int(z)
    x = y + z
```

Rule of thumb: Any Python builtin function should be accessible in Mojo using
`Python.<builtin>()`.

```mojo
def foo(x: PythonObject, y: PythonObject, z: PythonObject) -> PythonObject:
    x[y] = Python.int(z)
    x = y + z
```

## Building Mojo extension modules

You can create and distribute your Mojo modules for Python in the following
ways:

- As source files, compiled on demand using the Python Mojo importer hook.

  The advantage of this approach is that it's easy to get started with, and
  keeps your project structure simple, while ensuring that your imported Mojo
  code is always up to date after you make an edit.

- As pre-built Python extension module `.so` dynamic libraries, compiled using:

  ```bash
  mojo build mojo_module.mojo --emit shared-lib -o mojo_module.so
  ```

  This has the advantage that you can specify any other necessary build options
  manually (optimization or debug flags, import paths, etc.), providing an
  "escape hatch" from the Mojo import hook abstraction for advanced users.

## Known limitations

While we have big ambitions for Python to Mojo interoperability—our goal is for
Mojo to be the best way to extend Python—this feature is still in early and
active development, and there are some limitations to be aware of. These will
be lifted over time.

- **Functions taking more than 6 arguments.** Currently
  `PyTypeBuilder.add_function()` and related function bindings only support Mojo
  functions that take up to 6 `PythonObject` arguments:
  `def(PythonObject, PythonObject, PythonObject, PythonObject, PythonObject, PythonObject)`.

- **Keyword arguments syntax.**
  Currently, Mojo functions called from Python only accept keyword arguments
  when using a trailing `kwargs: OwnedKwargsDict[PythonObject]` argument.
  Support for native `**kwargs` syntax will be added in the future.

- **Mojo package dependencies.**
  Mojo code that has dependencies on packages other than the Mojo stdlib
  (like those in the ever-growing
  [Modular Community](https://github.com/modular/modular-community) package
  channel) are currently only supported when building Mojo extension modules
  manually, as the Mojo import hook does not currently support a way to
  specify import paths for Mojo package dependencies.

- **Properties.**
  Computed properties getter and setters are not currently supported.

- **Expected type conversions.**
  A handful of Mojo standard library types can be constructed directly from
  equivalent Python builtin object types, by implementing the
  [`ConvertibleFromPython`][ConvertibleFromPython] trait.
  However, many Mojo standard library types do not yet implement this trait,
  so may require manual conversion logic if needed.

[ConvertibleFromPython]: /docs/std/python/conversions/ConvertibleFromPython/

[downcast_value_ptr]: /docs/std/python/python_object/PythonObject#downcast_value_ptr
