"""Format detection for notebooks — file-extension mapping and content sniffing.
Determines which notebook format a file or string is in by checking:
- **File extension** (for :func:`detect_format`): ``.ipynb``, ``.qmd``, ``.rmd``,
``.deepnote``, ``.md``, ``.py`` — maps directly to format name.
- **Content patterns** (for :func:`detect_text_format`): ``# %%`` markers,
``import marimo``, `````{r}```/`````{python}``` blocks, JSON structure,
YAML structure with ``project.notebooks`` keys.
Supports automatic detection for all 8+ notebook formats.
"""
from __future__ import annotations
from pathlib import Path
[docs]
def detect_text_format(content: str) -> str:
"""Detect notebook format from text content alone (content sniffing).
Detection order:
1. ``"percent"`` — if any line starts with ``# %%``
2. ``"marimo"`` — if contains ``import marimo`` or ``@app.cell``
3. ``"rmarkdown"`` — if contains `````{r}```
4. ``"quarto"`` — if contains `````{python}```
5. ``"markdown"`` — if contains `````python```
6. ``"ipynb"`` — if content is JSON with ``cells`` and ``nbformat`` keys
7. ``"deepnote"`` — if content is YAML with ``project.notebooks``
8. ``"percent"`` — fallback for unrecognized text
Args:
content: The raw text content to sniff.
Returns:
A format string: ``"percent"``, ``"marimo"``, ``"rmarkdown"``,
``"quarto"``, ``"markdown"``, ``"ipynb"``, or ``"deepnote"``.
"""
lines = content.splitlines()
# Check for percent format markers
for line in lines:
stripped = line.strip()
if stripped.startswith("# %%"):
return "percent"
# Check for marimo markers
for line in lines:
stripped = line.strip()
if stripped.startswith("import marimo") or stripped.startswith("@app.cell"):
return "marimo"
# Check for R Markdown markers
for line in lines:
stripped = line.strip()
if stripped.startswith("```{r}"):
return "rmarkdown"
# Check for quarto markers
for line in lines:
stripped = line.strip()
if stripped.startswith("```{python}"):
return "quarto"
# Check for markdown code blocks
for line in lines:
stripped = line.strip()
if stripped.startswith("```python"):
return "markdown"
# Check for ipynb JSON content (must be after structured formats)
if content.strip().startswith("{"):
try:
import json
obj = json.loads(content)
if "cells" in obj and "nbformat" in obj:
return "ipynb"
except Exception:
pass
# Check for Deepnote YAML format
if content.strip().startswith("metadata:") or "project:\n notebooks:" in content:
try:
import yaml
obj = yaml.safe_load(content)
if isinstance(obj, dict) and "project" in obj and "notebooks" in obj["project"]:
return "deepnote"
except Exception:
pass
# Fallback: treat as percent format
return "percent"