marimo-dev 0.2.8
Build and publish python packages from marimo notebooks
License: MIT
Requires Python: >=3.12

# marimo-dev

Build Python packages 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.

## Quick start

```bash
uv init --lib my-project
cd my-project
uv add marimo marimo-dev
mkdir notebooks
```

Create `notebooks/a_core.py`:

```python
import marimo
app = marimo.App()

@app.function
def greet(name:str="World"):
    "Return a greeting"
    return f"Hello, {name}!"
```

Build and publish:

```bash
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:

```python
from a_core import greet
```

marimo-dev rewrites these to relative imports in the built package:

```python
from .core import greet
```

## What gets exported

1. **Constants in setup cells** — any assignment in a setup cell becomes a constant
2. **Decorated functions and classes** — [self-contained functions and classes](https://docs.marimo.io/guides/reusing_functions/) with `@app.function` or `@app.class_definition`
3. **Export-named cells** — name a cell `export` (or `export_something`) to export arbitrary code as a blob:

```python
@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:

```python
@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:

```python
@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:

```toml
[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

```bash
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](https://peps.python.org/pep-0723/) inline dependencies:

```bash
md bundle app.py
uv run app.py
```

The generated file includes a dependency header that `uv` reads automatically:

```python
# /// 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 `version` in `pyproject.toml` before publishing
- Use `uv sync --upgrade` to update dependencies
- Use `uv cache clean` if you encounter caching issues
- Rebuild takes ~18ms, so you can run `md build` frequently during development