Skip to content

Format Ruff-backed cells as function bodies#9910

Open
lxingy3 wants to merge 2 commits into
marimo-team:mainfrom
lxingy3:fix-ruff-cell-body-formatting
Open

Format Ruff-backed cells as function bodies#9910
lxingy3 wants to merge 2 commits into
marimo-team:mainfrom
lxingy3:fix-ruff-cell-body-formatting

Conversation

@lxingy3

@lxingy3 lxingy3 commented Jun 17, 2026

Copy link
Copy Markdown

Summary

Closes #9848

Cell formatting currently sends each cell directly to Ruff as stdin, so Ruff treats the cell as module-level code. That adds module-level spacing around nested function definitions, which is one blank line too many once the cell is serialized inside def _():.

This formats Ruff-backed cells inside a temporary async function wrapper, then unwraps the formatted body before returning it. That keeps Ruff's parser and formatter behavior aligned with the final marimo cell scope, including cells with top-level await. Empty or comment-only cells get a temporary pass only for formatting and have it removed afterward.

Tests

  • python -m pytest tests\_utils\test_formatter.py -q
  • python -m ruff check marimo\_utils\formatter.py tests\_utils\test_formatter.py
  • git diff --check

Review in cubic

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@vercel

vercel Bot commented Jun 17, 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 25, 2026 6:38am

Request Review

@lxingy3 lxingy3 force-pushed the fix-ruff-cell-body-formatting branch from ba5aed6 to 614c2e1 Compare June 25, 2026 06:34
@lxingy3 lxingy3 marked this pull request as ready for review June 25, 2026 06:36
@lxingy3

lxingy3 commented Jun 25, 2026

Copy link
Copy Markdown
Author

Rebased this on current main and updated the new regression test to match the formatter helper's format_as_cell=True call.

Local checks:

uv run --group test pytest tests/_utils/test_formatter.py -q
uv run --group dev ruff check marimo/_utils/formatter.py tests/_utils/test_formatter.py

The PR diff is still limited to the formatter change and its tests.

@kirangadhave

Copy link
Copy Markdown
Member

@cubic-dev-ai

@cubic-dev-ai

cubic-dev-ai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai

@kirangadhave I have started the AI code review. It will take a few minutes to complete.

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

This PR aims to make Ruff formatting of marimo cell code match the cell’s eventual execution scope (a function body), preventing Ruff from applying module-level blank-line rules that introduce extra spacing (e.g., around nested defs).

Changes:

  • Add an optional format_as_cell mode to the low-level ruff() helper that wraps code in a temporary async function and unwraps the formatted body.
  • Update RuffFormatter to call ruff(..., format_as_cell=True).
  • Extend formatter tests, including a new regression test for blank-line behavior.

Reviewed changes

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

File Description
marimo/_utils/formatter.py Adds wrap/unwrap helpers and a format_as_cell option to ruff(), and enables it in RuffFormatter.
tests/_utils/test_formatter.py Updates RuffFormatter mock expectations and adds a new test for cell-body formatting behavior.

"--line-length",
str(self.line_length),
stdin_filename=stdin_filename,
format_as_cell=True,
Comment on lines +344 to +359
async def test_ruff_function_formats_code_as_cell_body(self):
codes: CellCodes = {
"cell1": "x = 3\n\n\ndef _foo():\n return x + 1\n\n\nprint(_foo())"
}

result = await ruff(
codes,
"format",
"--line-length",
"88",
format_as_cell=True,
)

assert result == {
"cell1": "x = 3\n\ndef _foo():\n return x + 1\n\nprint(_foo())"
}

@cubic-dev-ai cubic-dev-ai Bot 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.

No issues found across 2 files

Architecture diagram
sequenceDiagram
    participant Fmt as `Formatter.format()`
    participant RuffFn as `ruff()`
    participant Wrap as `_wrap_cell_code()`
    participant SubProc as `_run_subprocess_safe()`
    participant RuffProc as Ruff (subprocess)
    participant Unwrap as `_unwrap_cell_code()`

    Note over Fmt,RuffFn: NEW: format as cell body flow
    Fmt->>RuffFn: format(codes, ..., format_as_cell=True)
    loop for each cell (key, code)
        alt format_as_cell=True
            RuffFn->>Wrap: _wrap_cell_code(code)
            Wrap->>Wrap: Check if cell has statement
            alt no statement (comment-only or empty)
                Wrap->>RuffFn: wrapped_code, added_pass=True
            else has statement
                Wrap->>RuffFn: wrapped_code, added_pass=False
            end
        else format_as_cell=False (unchanged)
            RuffFn->>RuffFn: input_code = code (no wrap)
        end
        RuffFn->>SubProc: _run_subprocess_safe(command, input_data=input_code)
        SubProc->>RuffProc: run ruff format --
        RuffProc-->>SubProc: stdout (formatted code)
        SubProc-->>RuffFn: (stdout, stderr, returncode)
        alt returncode != 0
            RuffFn-->>Fmt: FormatError
        else success
            alt format_as_cell=True
                RuffFn->>Unwrap: _unwrap_cell_code(stdout, added_pass)
                Unwrap->>Unwrap: Remove wrapper, optionally trailing pass
                Unwrap-->>RuffFn: unwrapped_code
            else format_as_cell=False
                RuffFn->>RuffFn: formatted = stdout (no unwrap)
            end
            RuffFn->>RuffFn: store formatted.strip() in result dict
        end
    end
    RuffFn-->>Fmt: formatted codes dict
Loading

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extra lines around nested functions conflict with Ruff formatting

3 participants