Skip to main content

Modules and packages

Mojo provides a packaging system that allows you to organize and compile code libraries into importable files. This page introduces the necessary concepts about how to organize your code into modules and packages (which is a lot like Python), and shows you how to create a packaged binary with the mojo package command.

Mojo modules

To understand Mojo packages, you first need to understand Mojo modules. A Mojo module is a single Mojo source file that includes code suitable for use by other files that import it. For example, you can create a module to define a struct such as this one:

mymodule.mojo
struct MyPair:
var first: Int
var second: Int

fn __init__(inout self, first: Int, second: Int):
self.first = first
self.second = second

fn dump(self):
print(self.first, self.second)
struct MyPair:
var first: Int
var second: Int

fn __init__(inout self, first: Int, second: Int):
self.first = first
self.second = second

fn dump(self):
print(self.first, self.second)

Notice that this code has no main() function, so you can't execute mymodule.mojo. However, you can import this into another file with a main() function and use it there.

For example, here's how you can import MyPair into a file named main.mojo that's in the same directory as mymodule.mojo:

main.mojo
from mymodule import MyPair

fn main():
var mine = MyPair(2, 4)
mine.dump()
from mymodule import MyPair

fn main():
var mine = MyPair(2, 4)
mine.dump()

Alternatively, you can import the whole module and then access its members through the module name. For example:

main.mojo
import mymodule

fn main():
var mine = mymodule.MyPair(2, 4)
mine.dump()
import mymodule

fn main():
var mine = mymodule.MyPair(2, 4)
mine.dump()

You can also create an alias for an imported member with as, like this:

main.mojo
import mymodule as my

fn main():
var mine = my.MyPair(2, 4)
mine.dump()
import mymodule as my

fn main():
var mine = my.MyPair(2, 4)
mine.dump()

In this example, it only works when mymodule.mojo is in the same directory as main.mojo. Currently, you can't import .mojo files as modules if they reside in other directories. That is, unless you treat the directory as a Mojo package, as described in the next section.

Mojo packages

A Mojo package is just a collection of Mojo modules in a directory that includes an __init__.mojo file. By organizing modules together in a directory, you can then import all the modules together or individually. Optionally, you can also compile the package into a .mojopkg or .📦 file that's easier to share and still compatible with other system architectures.

You can import a package and its modules either directly from source files or from a compiled .mojopkg/.📦 file. It makes no real difference to Mojo which way you import a package. When importing from source files, the directory name works as the package name, whereas when importing from a compiled package, the filename is the package name (which you specify with the mojo package command—it can differ from the directory name).

For example, consider a project with these files:

main.mojo
mypackage/
__init__.mojo
mymodule.mojo
main.mojo
mypackage/
__init__.mojo
mymodule.mojo

mymodule.mojo is the same code from examples above (with the MyPair struct) and __init__.mojo is empty.

In this case, the main.mojo file can now import MyPair through the package name like this:

main.mojo
from mypackage.mymodule import MyPair

fn main():
var mine = MyPair(2, 4)
mine.dump()
from mypackage.mymodule import MyPair

fn main():
var mine = MyPair(2, 4)
mine.dump()

Notice that the __init__.mojo is crucial here. If you delete it, then Mojo doesn't recognize the directory as a package and it cannot import mymodule.

Then, let's say you don't want the mypackage source code in the same location as main.mojo. So, you can compile it into a package file like this:

mojo package mypackage -o mypack.mojopkg
mojo package mypackage -o mypack.mojopkg

Now, you can move the mypackage source somewhere else, and the project files now look like this:

main.mojo
mypack.mojopkg
main.mojo
mypack.mojopkg

Because we named the package file different from the directory, we need to fix the import statement and it all works the same:

main.mojo
from mypack.mymodule import MyPair
from mypack.mymodule import MyPair

The __init__ file

As mentioned above, the __init__.mojo file is required to indicate that a directory should be treated as a Mojo package, and it can be empty.

Currently, top-level code is not supported in .mojo files, so unlike Python, you can't write code in __init__.mojo that executes upon import. You can, however, add structs and functions, which you can then import from the package name.

However, instead of adding APIs in the __init__.mojo file, you can import module members, which has the same effect by making your APIs accessible from the package name, instead of requiring the <package_name>.<module_name> notation.

For example, again let's say you have these files:

main.mojo
mypackage/
__init__.mojo
mymodule.mojo
main.mojo
mypackage/
__init__.mojo
mymodule.mojo

Let's now add the following line in __init__.mojo:

__init__.mojo
from .mymodule import MyPair
from .mymodule import MyPair

That's all that's in there. Now, we can simplify the import statement in main.mojo like this:

main.mojo
from mypackage import MyPair
from mypackage import MyPair

This feature explains why some members in the Mojo standard library can be imported from their package name, while others required the <package_name>.<module_name> notation. For example, the functional module resides in the algorithm package, so you can import members of that module (such as the map() function) like this:

from algorithm.functional import map
from algorithm.functional import map

However, the algorithm/__init__.mojo file also includes these lines:

algorithm/__init__.mojo
from .functional import *
from .reduction import *
from .functional import *
from .reduction import *

So you can actually import anything from functional or reduction simply by naming the package. That is, you can drop the functional name from the import statement, and it also works:

from algorithm import map
from algorithm import map

Was this page helpful?