Skip to content

feat: Add basic direct edit/run support for .ipynb-formatted notebooks.#9899

Open
asford wants to merge 5 commits into
marimo-team:mainfrom
asford:main
Open

feat: Add basic direct edit/run support for .ipynb-formatted notebooks.#9899
asford wants to merge 5 commits into
marimo-team:mainfrom
asford:main

Conversation

@asford

@asford asford commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

📝 Summary

From: #5318

  • Add .ipynb handler paralleling existing .py and .md serializer handlers.
  • Light updates as needed to extend hardcoded .py and .md path logic.
  • Expand tests with basic coverage of .ipynb format support.
  • Consolidate content-based marimo compatibility checks into notebook serializer handlers.
  • Expand docs to discuss direct .ipynb support in edit/run.

Goal: Enable Marimo to support .ipynb as a primary file format in addition to marimo-script and markdown formats.
This allows teams to progressively adopt Marimo while maintaining compatibility with existing Jupyter-based tool stacks.

The primary motivating factor for this work is direct integration with .ipynb support in IDE assisted editing workflows.
Marimo is an excellent viewer application for AI-assisted editing, but tools like VSCode Copilot can not reliably edit marimo .py format files; breaking workflows where both code and notebook files are being updated in a single AI-assisted session.
Many toolstacks already natively support .ipynb notebook (e.g. Pyrefly checking, Copilot assistance, etc...).
Extending marimo to directly edit .ipynb files allows seamless integration with existing tooling.

Current Architecture Summary

NotebookSerializer marimo/_session/notebook/serializer.py

Marimo already has a pluggable serializer architecture in place.
This is semi-trivially extended with additional extension-specific format handlers.

Integration Points

.ipynb Conversion

Marimo already implements isomorphic conversion between Marimo .py and .ipynb formats.
Note however, not every .ipynb is a valid marimo notebook, as Marimo requires dag-like cell ordering and unique varible definitions.
Valid marimo-style .ipynb files can be trivially serialized to/from the .ipynb and .py formats using existing conversion code.
Notebooks which are not valid marimo notebooks can be converted to marimo-compatible forms via marimo convert.

Approach Desision:

There are two approaches:

  • (a) Implicit Conversion — As most .ipynb can be converted to a marimo-compatible format,
    allow editing any .ipynb file by applying the automatic .ipynb conversion heuristics.
    This may fail for some notebooks and will semi-transparently change notebook formats,
    but will make onboarding marimo in .ipynb workflows trivial.

  • (b) Explicit Converstion — Require an explicit conversion call for .ipynb notebooks to inject marimo metadata.
    This means any newly created or explicitly converted notebook will be editable via marimo, but requires manual import flow.

Decision for PR .ipynb support will be explicit opt-in, rather than automatically opening and converting existing .ipynb notebooks.
The marimo edit and marimo run commands and file tree will only show notebooks that have explicit marimo metadata.
This should be reconsidered later if the feature proves useful and UI patterns are established to support .ipynb formats.

Existing .ipynb Conversion Code

.py and .md|.qmd Hardcoded Extensions

Marimo has a long-running assumption that all valid notebook files are either python or markdown formatted.
The Marimo codebase has multiple locations which explicitly check for these file extensions.
While server-side components could all be refactored to rely on a dynamic serializer registry,
this may require adding undesirable circular dependencies between modules.
Additionally, there are limited number of frontend components which also rely on explicit extension detection.

Approach Decision:

There are two approaches:

  • (a) Continue hardcoding — just add .ipynb to each list as needed. Simple, but every new
    format requires touching many files.
  • (b) Dynamic from registry — read extensions from DEFAULT_NOTEBOOK_SERIALIZERS at runtime.
    More effort upfront but stays in sync automatically.

Decision for PR Use approach (a). Add .ipynb to all hardcoded extension lists.
Add TODO comments at locations that need a more fundamental refactor.

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

Review in cubic

Copilot AI review requested due to automatic review settings June 16, 2026 03:37
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 16, 2026 3:39am

Request Review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds first-class handling for Jupyter .ipynb notebooks across serialization, CLI validation, and workspace discovery.

Changes:

  • Introduces IpynbNotebookSerializer and registers .ipynb in the serializer registry, plus conversion IR↔ipynb helpers.
  • Extends discovery/validation paths (directory scanning, OS filesystem, CLI file handling) to recognize marimo .ipynb notebooks.
  • Adds/updates tests and test dependencies (nbformat) to cover .ipynb behavior and round-trips.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/_session/notebook/test_serializer.py Adds unit + round-trip tests for .ipynb serializer and routing.
tests/_session/notebook/test_app_file_manager.py Tests rename-driven format conversion .py.ipynb.
tests/_server/test_directory_scanner.py Adds .ipynb discovery/detection tests for scanner + OS filesystem.
tests/_cli/test_file_path.py Expands CLI validation tests for .ipynb (missing/invalid/valid).
pyproject.toml Adds nbformat to test dependencies for ipynb round-trip tests.
marimo/_utils/marimo_path.py Treats .ipynb as a valid marimo path type.
marimo/_session/notebook/serializer.py Adds marimo detection API + new .ipynb serializer + registry update.
marimo/_server/files/os_file_system.py Enables creating new .ipynb notebooks from IR.
marimo/_server/files/directory_scanner.py Delegates marimo-file detection to serializers and includes .ipynb in scans.
marimo/_server/asgi.py Adds TODO notes for .ipynb routing/caching gaps.
marimo/_server/api/endpoints/editing.py Makes formatting treat all non-.py as Python for formatter context.
marimo/_convert/ipynb/to_ir.py Propagates filepath into IR (filename).
marimo/_convert/ipynb/from_ir.py Adds ir_to_ipynb to serialize without requiring an InternalApp.
marimo/_cli/files/file_path.py Accepts .ipynb in readers and validates local .ipynb inputs.
frontend/src/components/pages/home-page.tsx Adds TODO for .ipynb icon/badge handling.
frontend/src/components/editor/header/filename-input.tsx Adds .ipynb to allowed extension suggestions (kept in sync note).

Comment on lines +56 to +65
def is_marimo_notebook(self, path: Path) -> bool:
"""Check if a file is a marimo notebook.

Args:
path: File path to check

Returns:
True if the file is a marimo notebook, False otherwise
"""
...
Comment on lines +215 to +223
import json

try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
metadata = data.get("metadata", {})
return "marimo" in metadata
except Exception:
return False

def is_valid(self) -> bool:
return self.is_python() or self.is_markdown()
return self.is_python() or self.is_markdown() or self.is_ipynb()
Comment on lines +56 to 58
if not path_parts[-1].endswith((".py", ".md", ".ipynb")):
raise ValueError("No python or markdown files found in the Gist")
return url
Comment on lines 319 to +344
if path.suffix == ".ipynb":
prefix = str(path)[: -len(".ipynb")]
raise click.ClickException(
f"Invalid NAME - {name} is not a Python file.\n\n"
f" {green('Tip:')} Convert {name} to a marimo notebook with"
"\n\n"
f" marimo convert {name} -o {prefix}.py\n\n"
f" then open with marimo edit {prefix}.py"
if not path.exists():
if self.allow_new_file:
return name, None
raise click.ClickException(
f"Invalid NAME - {name} does not exist"
)
# Verify the ipynb can be loaded by the registered serializer
from marimo._session.notebook.serializer import (
IpynbNotebookSerializer,
)

try:
IpynbNotebookSerializer().deserialize(
path.read_text(encoding="utf-8")
)
except Exception:
prefix = str(path)[: -len(".ipynb")]
raise click.ClickException(
f"Invalid NAME - {name} is not a valid Jupyter notebook.\n\n"
f" {green('Tip:')} Convert {name} to a marimo notebook with"
"\n\n"
f" marimo convert {name} -o {prefix}.py\n\n"
f" then open with marimo edit {prefix}.py"
)
return name, None
Comment on lines +115 to +123
# Verify the .ipynb content is valid JSON with marimo metadata
with open(ipynb_path, "r", encoding="utf-8") as f:
ipynb_data = json.load(f)
assert "cells" in ipynb_data
assert "metadata" in ipynb_data
assert "marimo" in ipynb_data["metadata"]
assert "marimo_version" in ipynb_data["metadata"]["marimo"]
# Verify cell content is preserved
assert any("x = 1" in cell["source"] for cell in ipynb_data["cells"])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants