Build and publish python packages from marimo notebooks
Project description
marimo-dev
[!WARNING] This project is under active development and is not an official marimo tool - Mar 2026
Build Python packages (and applications[in progress]) from Marimo notebooks.
Why this exists
Marimo notebooks are excellent for development - they manage dependencies automatically, provide instant feedback, and let you import functions between notebooks without configuration. But publishing requires traditional Python packages with proper module structure and __init__.py files.
marimo-dev bridges this gap. It extracts decorated functions and classes from your notebooks and writes them to clean Python modules, leaving behind the exploratory code, UI elements, and notebook-specific logic.
Limitations
Unlike nbdev (which this project was heavily inspired by) from we rely on the marimo DAG to identify the functions that will be exported into the final function. This means you really have to write things functionaly. For myself this constraint has helped out a lot. but because of this marimo has its own enforced naming limitations mainly _foo() stays local to the cell; cannot use app() and another few random reserved function names. The workaround currently is a bit ugly but it seems to be working. we allow the user to specify a dict of renameing conventions (these are replaced globaly so be weary of this but the regx should be safe that it does not match unintented tagets internal_foo WILL not match internal_foobar). Id the replacement is a "dunder" it is both prepended and appended to the stem.
[tool.marimo-dev]
renames = {
dunder_ = "__", # __stem__
internal_ = "_", # _stem
app_ = "app" # exact substitution (empty stem)
}
Quick start
uv init --lib my-project
cd my-project
uv add marimo marimo-dev
mkdir notebooks
Create notebooks/a_core.py:
import marimo
app = marimo.App()
@app.function
def greet(name:str="World"):
"Return a greeting"
return f"Hello, {name}!"
Build and publish:
md build
md publish --test
Project structure
my-project/
├── pyproject.toml
├── notebooks/
│ ├── a_core.py # letter prefix avoids collision with 'core' package
│ ├── b_utils.py # avoids collision with 'utils' package
│ └── XX_draft.py # XX_ prefix = ignored during build
├── src/ # generated by md build
│ └── my_project/
│ ├── __init__.py
│ ├── core.py # letter prefix stripped
│ └── utils.py
└── docs/ # generated by md build
└── llms.txt # API signatures for LLM consumption
Module naming
Prefix notebooks with letters (a_, b_, c_) to avoid name collisions with common packages like requests, utils, or core. The prefix is stripped in the built package.
During development, import from other notebooks using their full names:
from a_core import greet
marimo-dev rewrites these to relative imports in the built package:
from .core import greet
What gets exported
- Constants in setup cells — any assignment in a setup cell becomes a constant
- Decorated functions and classes — self-contained functions and classes with
@app.functionor@app.class_definition - Export-named cells — name a cell
export(orexport_something) to export arbitrary code as a blob:
@app.cell
def export_main():
if __name__ == "__main__":
main()
return
This is useful for code that isn't a function or class, like if __name__ == "__main__" blocks.
Hash pipe directives
Control export and documentation behavior with #| directives on the line immediately after a decorator:
@app.function
#| nodoc
def helper():
pass # exported but not in llms.txt
@app.function
#| internal
def private():
pass # not added to __all__
@app.function
#| nodoc internal
def helper():
pass # neither exported nor documented
Documentation style
Use inline comments for parameter documentation:
@app.function
def add(
a:int, # first number
b:int, # second number
)->int: # sum of a and b
"Add two numbers"
return a + b
These comments appear in llms.txt, making your API documentation useful for LLM-assisted coding.
Configuration
Add to pyproject.toml to override defaults:
[tool.marimo-dev]
nbs = "notebooks" # notebook directory (default: "notebooks")
out = "src" # output directory (default: "src")
docs = "docs" # docs directory (default: "docs")
decorators = ["app.function", "app.class_definition"] # export markers
skip_prefixes = ["XX_", "test_"] # ignore these files
Commands
md build # build package from notebooks and make docs
md bundle # bundle into single file with PEP 723 dependencies
md bundle app.py # bundle to specific filename at project root
md docs # build the static docs (beta)
md publish --test # publish to Test PyPI
md publish # publish to PyPI
md tidy # remove __pycache__ and cache files
md nuke # remove all build artifacts (dist, docs, src, temp*)
If you make a temp folder it will be explicitly removed when running md nuke
Single-file applications
Use md bundle to create a standalone Python file with PEP 723 inline dependencies:
md bundle app.py
uv run app.py
The generated file includes a dependency header that uv reads automatically:
# /// script
# dependencies = ["fasthtml", "uvicorn"]
# ///
This lets you deploy a single .py file — anyone with uv can run it without manual dependency installation.
Dependencies
Marimo manages package dependencies automatically through its package tab. You do not need to manually maintain pyproject.toml dependencies during development.
When you build, ensure your pyproject.toml includes all packages your exported functions import.
Requirements
- Python 3.12+
- marimo
- uv
Tips
- Update
versioninpyproject.tomlbefore publishing - Use
uv sync --upgradeto update dependencies - Use
uv cache cleanif you encounter caching issues - Rebuild takes ~18ms, so you can run
md buildfrequently during development
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file marimo_dev-0.3.7.tar.gz.
File metadata
- Download URL: marimo_dev-0.3.7.tar.gz
- Upload date:
- Size: 19.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c16d3b7ecc0bf5ab79786cd91d5364bdb3f9c9b908444ec420d7457dd948f5c
|
|
| MD5 |
2dabf9d2b94aba7ef27de67a2d59adeb
|
|
| BLAKE2b-256 |
de21dcfbcbce91847a9287e4a6f72126626425de507d495cc39e9aa38b6e9905
|
File details
Details for the file marimo_dev-0.3.7-py3-none-any.whl.
File metadata
- Download URL: marimo_dev-0.3.7-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47e7e867d735d9e88d1871651cbb8bf2743b120c18e8bad0c1556315dd96a183
|
|
| MD5 |
d90bbe907e4440baa71ccebbb37d43f4
|
|
| BLAKE2b-256 |
a8f569f5bf33dd0b4f63e326bd7d3e69c5304c024f8582498434f99291af3a77
|