Fileformat
File Format
marimo apps are stored as pure Python files.
These files are:
- 🤖 legible for both humans and machines
- ✏️ formattable using your tool of choice
- ➕ easily versioned with git, producing small diffs
- 🐍 usable as Python scripts, with UI elements taking their default values
- 🧩 modular, exposing functions and classes that can be imported from the notebook ## Example
Consider a marimo notebook with the following four cells.
First cell:
print(text.value)
Second cell:
def say_hello(name="World"):
return f"Hello, {name}!"
Third cell:
text = mo.ui.text(value=say_hello())
text
Fourth cell:
import marimo as mo
For the above example, marimo would generate the following file contents:
import marimo
__generated_with = "0.0.0"
app = marimo.App()
@app.cell
def _(text):
print(text.value)
return
@app.function
def say_hello(name="World"):
return f"Hello, {name}!"
@app.cell
def _(mo):
text = mo.ui.text(value="Hello, World!")
text
return (text,)
@app.cell
def _():
import marimo as mo
return mo,
if __name__ == "__main__":
app.run()
As you can see, this is pure Python. This is part of the reason why marimo’s generated files are git-friendly: small changes made using the marimo editor result in small changes to the file that marimo generates.
Moreover, the cell defining a single pure function say_hello
was saved “top-level” in the notebook file, making it possible for you to import it into other Python files or notebooks. ## Properties
marimo’s file format was designed to be easy to read and easy to work with, while also serving the needs of the marimo library. You can even edit the generated file’s cells directly, using your favorite text editor, and format the file with your favorite code formatter.
We explain some properties of marimo’s file format below.
dataflow
tutorial, we saw that cells are like functions mapping
their refs (the global variables they uses but don't define) to their
defs (the global variables they define). The generated code makes this
analogy explicit.
In the generated code, there is a function for each cell. The arguments
of the function are the cell's refs , and its returned variables are
its defs that are referenced by other cells.
For example, the code
@app.cell
def _(mo):
text = mo.ui.text(value="Hello, World!")
text
return text,
mo
, and it creates
a global variable called text
(that is read by another cell).
In contrast, the code
@app.cell
def _():
import marimo as mo
return mo,
mo
which the previous
cell requires as input.@app.cell
def _(text):
print(text.value)
return
@app.cell
def echo(text):
print(text.value)
return
app
is created.
This object collects the cells into a dataflow graph, using the cell
decorator.marimo edit
.
For example: running our example as a script would print Hello
World!
to the console.Importing functions and classes from notebooks
The details of marimo’s file format are important if you want to import functions and classes defined in your notebook into other Python modules. If you don’t intend to do so, you can skip this section. ### Declaring imports used by functions and classes
marimo can serialize functions and classes into the top-level of a file, so you can import them with regular Python syntax:
from my_notebook import my_function
In particular, if a cell defines just a single function or class, and if that function or class is pure save for references to variables defined in a special setup cell, it will be serialized top-level.
Setup cell. Notebooks can optionally include a setup cell that imports modules, written in the file as:
with app.setup:
import marimo as mo
import dataclasses
Modules imported in a setup cell can be used in “top-level” functions or classes. You can add the setup cell from the general menu of the editor under: ::lucide:diamond-plus:: Add setup cell. ### Functions and classes
Notebook files expose functions and classes that depend only on variables defined in the setup cell (or on other such functions or classes). For example, the following cell:
… is saved in the notebook file as
@app.function
def roll_die():
'''
A reusable function.
Notice the indicator in the bottom right of the cell.
'''
return random.randint(1, 7)
Making it importable as
from my_notebook import roll_die
Standalone classes are also exposed:
This class is saved in the file as
@app.class_definition
@dataclasses.dataclass
class SimulationExample:
int
n_rolls:
def simulate(self) -> list[int]:
return [roll_die() for _ in range(self.n_rolls)]
/// attention | Heads up ///
Not all standalone functions will be exposed in the module. If your function depends on variables that are defined in other cells, then it won’t be exposed top-level.
For example, this function will not be exposed:
FAQ
I want to edit notebooks in a different editor, what do I need to know?
See the docs on using your own editor.
I want to import functions from a marimo notebook, what do I need to know?
See the docs on reusable functions and classes.
I want to run pytest on marimo notebooks, what do I need to know?
See the docs on testing. ## This notebook’s source code
The source code of this notebook is shown below: