Spaces:
Running
Running
Merge pull request #115 from marimo-team/haleshot/add-empty-cell-workflow
Browse files
.github/workflows/check-empty-cells.yml
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Check for Empty Cells
|
2 |
+
|
3 |
+
on:
|
4 |
+
pull_request:
|
5 |
+
branches: [main]
|
6 |
+
paths:
|
7 |
+
- '**/*.py'
|
8 |
+
|
9 |
+
jobs:
|
10 |
+
check-empty-cells:
|
11 |
+
name: Check for empty cells in marimo notebooks
|
12 |
+
runs-on: ubuntu-latest
|
13 |
+
steps:
|
14 |
+
- name: π Cancel Previous Runs
|
15 |
+
uses: styfle/cancel-workflow-action@0.12.1
|
16 |
+
|
17 |
+
- name: π Checkout code
|
18 |
+
uses: actions/checkout@v4
|
19 |
+
|
20 |
+
- name: π Set up Python
|
21 |
+
uses: actions/setup-python@v5
|
22 |
+
with:
|
23 |
+
python-version: 3.12
|
24 |
+
|
25 |
+
- name: π Check for empty cells
|
26 |
+
run: |
|
27 |
+
python scripts/check_empty_cells.py
|
28 |
+
|
29 |
+
- name: π Report results
|
30 |
+
if: failure()
|
31 |
+
run: |
|
32 |
+
echo "β Empty cells found in marimo notebooks!"
|
33 |
+
echo "Please remove or add content to empty cells before merging."
|
34 |
+
echo "Empty cells look like:"
|
35 |
+
echo "@app.cell"
|
36 |
+
echo "def _():"
|
37 |
+
echo " return"
|
polars/06_Dataframe_Transformer.py
CHANGED
@@ -62,7 +62,7 @@ def _(json_data, pl):
|
|
62 |
def _(mo):
|
63 |
mo.md(
|
64 |
r"""
|
65 |
-
Above, you will notice that when you reference the object as a standalone, you get out-of-the-box
|
66 |
|
67 |
- π‘ Try out the `Table` view! You can click the `Preview data` button to get a quick view of your data.
|
68 |
- π‘ Take a look at the `Query plan`. Learn more about Polar's query plan here: https://docs.pola.rs/user-guide/lazy/query-plan/
|
@@ -111,7 +111,7 @@ def _(mo):
|
|
111 |
r"""
|
112 |
Note how much functionality we have right out-of-the-box. Click on column names to see rich features like sorting, freezing, filtering, searching, and more!
|
113 |
|
114 |
-
Notice how `order_quantity` has a green bar chart under it indicating the
|
115 |
|
116 |
Don't miss the `Download` feature as well which supports downloading in CSV, json, or parquet format!
|
117 |
"""
|
|
|
62 |
def _(mo):
|
63 |
mo.md(
|
64 |
r"""
|
65 |
+
Above, you will notice that when you reference the object as a standalone, you get out-of-the-box convenience from `marimo`. You have the `Table` and `Query Plan` options to choose from.
|
66 |
|
67 |
- π‘ Try out the `Table` view! You can click the `Preview data` button to get a quick view of your data.
|
68 |
- π‘ Take a look at the `Query plan`. Learn more about Polar's query plan here: https://docs.pola.rs/user-guide/lazy/query-plan/
|
|
|
111 |
r"""
|
112 |
Note how much functionality we have right out-of-the-box. Click on column names to see rich features like sorting, freezing, filtering, searching, and more!
|
113 |
|
114 |
+
Notice how `order_quantity` has a green bar chart under it indicating the distribution of values for the field!
|
115 |
|
116 |
Don't miss the `Download` feature as well which supports downloading in CSV, json, or parquet format!
|
117 |
"""
|
scripts/check_empty_cells.py
ADDED
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Script to detect empty cells in marimo notebooks.
|
4 |
+
|
5 |
+
An empty cell is defined as:
|
6 |
+
@app.cell
|
7 |
+
def _():
|
8 |
+
return
|
9 |
+
|
10 |
+
This script will:
|
11 |
+
1. Find all Python files in the repository
|
12 |
+
2. Check if they contain marimo app definitions
|
13 |
+
3. Look for empty cell patterns
|
14 |
+
4. Report any empty cells found
|
15 |
+
"""
|
16 |
+
|
17 |
+
import os
|
18 |
+
import re
|
19 |
+
import sys
|
20 |
+
from pathlib import Path
|
21 |
+
from typing import List, Tuple
|
22 |
+
|
23 |
+
|
24 |
+
def is_marimo_notebook(file_path: Path) -> bool:
|
25 |
+
"""Check if a Python file is a marimo notebook."""
|
26 |
+
try:
|
27 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
28 |
+
content = f.read()
|
29 |
+
# Look for marimo app defn
|
30 |
+
return 'marimo.App' in content and '@app.cell' in content
|
31 |
+
except (UnicodeDecodeError, IOError):
|
32 |
+
return False
|
33 |
+
|
34 |
+
|
35 |
+
def find_empty_cells(file_path: Path) -> List[Tuple[int, str]]:
|
36 |
+
"""Find empty cells in a marimo notebook."""
|
37 |
+
empty_cells = []
|
38 |
+
|
39 |
+
try:
|
40 |
+
with open(file_path, 'r', encoding='utf-8') as f:
|
41 |
+
lines = f.readlines()
|
42 |
+
except (UnicodeDecodeError, IOError):
|
43 |
+
return empty_cells
|
44 |
+
|
45 |
+
i = 0
|
46 |
+
while i < len(lines):
|
47 |
+
line = lines[i].strip()
|
48 |
+
|
49 |
+
# if line starts with @app.cell decorator
|
50 |
+
if line.startswith('@app.cell'):
|
51 |
+
# look for the function definition on the next non-empty line
|
52 |
+
j = i + 1
|
53 |
+
while j < len(lines) and not lines[j].strip():
|
54 |
+
j += 1
|
55 |
+
|
56 |
+
if j < len(lines) and lines[j].strip() == 'def _():':
|
57 |
+
# found function definition, now look for return statement
|
58 |
+
k = j + 1
|
59 |
+
while k < len(lines) and not lines[k].strip():
|
60 |
+
k += 1
|
61 |
+
|
62 |
+
if k < len(lines) and lines[k].strip() == 'return':
|
63 |
+
# if any content after return (before next @app.cell or end of file)
|
64 |
+
has_content = False
|
65 |
+
m = k + 1
|
66 |
+
while m < len(lines):
|
67 |
+
line_content = lines[m].strip()
|
68 |
+
if line_content.startswith('@app.cell'):
|
69 |
+
break
|
70 |
+
if line_content and not line_content.startswith('#'):
|
71 |
+
has_content = True
|
72 |
+
break
|
73 |
+
m += 1
|
74 |
+
|
75 |
+
if not has_content:
|
76 |
+
empty_cells.append((i + 1, lines[i].strip()))
|
77 |
+
|
78 |
+
i = k + 1
|
79 |
+
else:
|
80 |
+
i += 1
|
81 |
+
else:
|
82 |
+
i += 1
|
83 |
+
|
84 |
+
return empty_cells
|
85 |
+
|
86 |
+
|
87 |
+
def main():
|
88 |
+
"""Main function to check for empty cells."""
|
89 |
+
print("π Checking for empty cells in marimo notebooks...")
|
90 |
+
|
91 |
+
python_files = []
|
92 |
+
for root, dirs, files in os.walk('.'):
|
93 |
+
# skip hidden directories and common build/cache
|
94 |
+
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['__pycache__', 'node_modules', 'build', 'dist']]
|
95 |
+
|
96 |
+
for file in files:
|
97 |
+
if file.endswith('.py'):
|
98 |
+
python_files.append(Path(root) / file)
|
99 |
+
|
100 |
+
marimo_notebooks = []
|
101 |
+
for file_path in python_files:
|
102 |
+
if is_marimo_notebook(file_path):
|
103 |
+
marimo_notebooks.append(file_path)
|
104 |
+
|
105 |
+
print(f"π Found {len(marimo_notebooks)} marimo notebooks")
|
106 |
+
|
107 |
+
# each notebook checked for empty cells
|
108 |
+
total_empty_cells = 0
|
109 |
+
files_with_empty_cells = []
|
110 |
+
|
111 |
+
for notebook_path in marimo_notebooks:
|
112 |
+
empty_cells = find_empty_cells(notebook_path)
|
113 |
+
if empty_cells:
|
114 |
+
total_empty_cells += len(empty_cells)
|
115 |
+
files_with_empty_cells.append((notebook_path, empty_cells))
|
116 |
+
print(f"β {notebook_path}: {len(empty_cells)} empty cell(s)")
|
117 |
+
for line_num, line_content in empty_cells:
|
118 |
+
print(f" Line {line_num}: {line_content}")
|
119 |
+
|
120 |
+
if files_with_empty_cells:
|
121 |
+
print(f"\nπ₯ Found {total_empty_cells} empty cells in {len(files_with_empty_cells)} files")
|
122 |
+
print("\nEmpty cells should be removed or contain meaningful content.")
|
123 |
+
print("An empty cell looks like:")
|
124 |
+
print("@app.cell")
|
125 |
+
print("def _():")
|
126 |
+
print(" return")
|
127 |
+
print("\nConsider either:")
|
128 |
+
print("1. Removing the empty cell entirely")
|
129 |
+
print("2. Adding meaningful content to the cell")
|
130 |
+
print("3. Adding a comment explaining why the cell is empty")
|
131 |
+
|
132 |
+
sys.exit(1)
|
133 |
+
else:
|
134 |
+
print("β
No empty cells found!")
|
135 |
+
sys.exit(0)
|
136 |
+
|
137 |
+
|
138 |
+
if __name__ == "__main__":
|
139 |
+
main()
|