Skip to content

Commit ca50e93

Browse files
committed
feat: Add display.render_mode option to control DataFrame visualization
1 parent 7261a4e commit ca50e93

File tree

8 files changed

+145
-36
lines changed

8 files changed

+145
-36
lines changed

bigframes/display/html.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ def repr_mimebundle(
361361
if opts.repr_mode == "deferred":
362362
return repr_mimebundle_deferred(obj)
363363

364-
if opts.repr_mode == "anywidget":
364+
if opts.render_mode == "anywidget":
365365
try:
366366
return get_anywidget_bundle(obj, include=include, exclude=exclude)
367367
except ImportError:
@@ -374,4 +374,8 @@ def repr_mimebundle(
374374
f"Falling back to static HTML. Error: {traceback.format_exc()}"
375375
)
376376

377-
return repr_mimebundle_head(obj)
377+
bundle = repr_mimebundle_head(obj)
378+
if opts.render_mode == "plaintext":
379+
bundle.pop("text/html", None)
380+
381+
return bundle

notebooks/dataframes/anywidget_mode.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"outputs": [],
6464
"source": [
6565
"bpd.options.bigquery.ordering_mode = \"partial\"\n",
66-
"bpd.options.display.repr_mode = \"anywidget\""
66+
"bpd.options.display.render_mode = \"anywidget\""
6767
]
6868
},
6969
{

notebooks/ml/timeseries_analysis.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"source": [
2020
"import bigframes.pandas as bpd\n",
2121
"from bigframes.ml import forecasting\n",
22-
"bpd.options.display.repr_mode = \"anywidget\""
22+
"bpd.options.display.render_mode = \"anywidget\""
2323
]
2424
},
2525
{

tests/system/small/test_anywidget.py

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def table_widget(paginated_bf_df: bigframes.dataframe.DataFrame):
7070
from bigframes.display import TableWidget
7171

7272
with bigframes.option_context(
73-
"display.repr_mode", "anywidget", "display.max_rows", 2
73+
"display.render_mode", "anywidget", "display.max_rows", 2
7474
):
7575
# Delay context manager cleanup of `max_rows` until after tests finish.
7676
yield TableWidget(paginated_bf_df)
@@ -100,7 +100,7 @@ def small_widget(small_bf_df):
100100
"""Helper fixture for tests using a DataFrame smaller than the page size."""
101101
from bigframes.display import TableWidget
102102

103-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 5):
103+
with bf.option_context("display.render_mode", "anywidget", "display.max_rows", 5):
104104
yield TableWidget(small_bf_df)
105105

106106

@@ -126,7 +126,9 @@ def unknown_row_count_widget(session):
126126
mock_batches.return_value = blocks.PandasBatches(
127127
batches_iterator, total_rows=None
128128
)
129-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 2):
129+
with bf.option_context(
130+
"display.render_mode", "anywidget", "display.max_rows", 2
131+
):
130132
widget = TableWidget(bf_df)
131133
yield widget
132134

@@ -206,7 +208,7 @@ def test_widget_initialization_should_calculate_total_row_count(
206208
from bigframes.display import TableWidget
207209

208210
with bigframes.option_context(
209-
"display.repr_mode", "anywidget", "display.max_rows", 2
211+
"display.render_mode", "anywidget", "display.max_rows", 2
210212
):
211213
widget = TableWidget(paginated_bf_df)
212214

@@ -316,7 +318,7 @@ def test_widget_pagination_should_work_with_custom_page_size(
316318
):
317319
"""Test that a widget paginates correctly with a custom page size."""
318320
with bigframes.option_context(
319-
"display.repr_mode", "anywidget", "display.max_rows", 3
321+
"display.render_mode", "anywidget", "display.max_rows", 3
320322
):
321323
from bigframes.display import TableWidget
322324

@@ -370,7 +372,7 @@ def test_global_options_change_should_not_affect_existing_widget_page_size(
370372
then the widget's page size should remain unchanged.
371373
"""
372374
with bigframes.option_context(
373-
"display.repr_mode", "anywidget", "display.max_rows", 2
375+
"display.render_mode", "anywidget", "display.max_rows", 2
374376
):
375377
from bigframes.display import TableWidget
376378

@@ -395,7 +397,7 @@ def test_widget_with_empty_dataframe_should_have_zero_row_count(
395397
then its row_count should be 0.
396398
"""
397399

398-
with bigframes.option_context("display.repr_mode", "anywidget"):
400+
with bigframes.option_context("display.render_mode", "anywidget"):
399401
from bigframes.display import TableWidget
400402

401403
widget = TableWidget(empty_bf_df)
@@ -421,7 +423,7 @@ def test_widget_with_empty_dataframe_should_render_table_headers(
421423
422424
"""
423425

424-
with bigframes.option_context("display.repr_mode", "anywidget"):
426+
with bigframes.option_context("display.render_mode", "anywidget"):
425427

426428
from bigframes.display import TableWidget
427429

@@ -550,7 +552,7 @@ def test_widget_row_count_should_be_immutable_after_creation(
550552

551553
# Use a context manager to ensure the option is reset
552554
with bigframes.option_context(
553-
"display.repr_mode", "anywidget", "display.max_rows", 2
555+
"display.render_mode", "anywidget", "display.max_rows", 2
554556
):
555557
widget = TableWidget(paginated_bf_df)
556558
initial_row_count = widget.row_count
@@ -585,7 +587,7 @@ def test_widget_should_show_error_on_batch_failure(
585587
)
586588

587589
# Create the TableWidget under the error condition.
588-
with bigframes.option_context("display.repr_mode", "anywidget"):
590+
with bigframes.option_context("display.render_mode", "anywidget"):
589591
from bigframes.display import TableWidget
590592

591593
# The widget should handle the faulty data from the mock without crashing.
@@ -609,7 +611,7 @@ def test_widget_row_count_reflects_actual_data_available(
609611

610612
# Set up display options that define a page size.
611613
with bigframes.option_context(
612-
"display.repr_mode", "anywidget", "display.max_rows", 2
614+
"display.render_mode", "anywidget", "display.max_rows", 2
613615
):
614616
widget = TableWidget(paginated_bf_df)
615617

@@ -639,7 +641,7 @@ def test_widget_with_unknown_row_count_should_auto_navigate_to_last_page(
639641
bf_df = session.read_pandas(test_data)
640642

641643
with bigframes.option_context(
642-
"display.repr_mode", "anywidget", "display.max_rows", 2
644+
"display.render_mode", "anywidget", "display.max_rows", 2
643645
):
644646
widget = TableWidget(bf_df)
645647

@@ -681,7 +683,7 @@ def test_widget_with_unknown_row_count_should_set_none_state_for_frontend(
681683
bf_df = session.read_pandas(test_data)
682684

683685
with bigframes.option_context(
684-
"display.repr_mode", "anywidget", "display.max_rows", 2
686+
"display.render_mode", "anywidget", "display.max_rows", 2
685687
):
686688
widget = TableWidget(bf_df)
687689

@@ -718,7 +720,7 @@ def test_widget_with_unknown_row_count_should_allow_forward_navigation(
718720
bf_df = session.read_pandas(test_data)
719721

720722
with bigframes.option_context(
721-
"display.repr_mode", "anywidget", "display.max_rows", 2
723+
"display.render_mode", "anywidget", "display.max_rows", 2
722724
):
723725
widget = TableWidget(bf_df)
724726
widget.row_count = None
@@ -755,7 +757,7 @@ def test_widget_with_unknown_row_count_empty_dataframe(
755757
empty_data = pd.DataFrame(columns=["id", "value"])
756758
bf_df = session.read_pandas(empty_data)
757759

758-
with bigframes.option_context("display.repr_mode", "anywidget"):
760+
with bigframes.option_context("display.render_mode", "anywidget"):
759761
widget = TableWidget(bf_df)
760762
widget.row_count = None
761763

@@ -908,7 +910,7 @@ def test_repr_mimebundle_should_fallback_to_html_if_anywidget_is_unavailable(
908910
Test that _repr_mimebundle_ falls back to static html when anywidget is not available.
909911
"""
910912
with bigframes.option_context(
911-
"display.repr_mode", "anywidget", "display.max_rows", 2
913+
"display.render_mode", "anywidget", "display.max_rows", 2
912914
):
913915
# Mock the ANYWIDGET_INSTALLED flag to simulate absence of anywidget
914916
with mock.patch("bigframes.display.anywidget._ANYWIDGET_INSTALLED", False):
@@ -927,7 +929,7 @@ def test_repr_mimebundle_should_return_widget_view_if_anywidget_is_available(
927929
"""
928930
Test that _repr_mimebundle_ returns a widget view when anywidget is available.
929931
"""
930-
with bigframes.option_context("display.repr_mode", "anywidget"):
932+
with bigframes.option_context("display.render_mode", "anywidget"):
931933
bundle = paginated_bf_df._repr_mimebundle_()
932934
assert isinstance(bundle, tuple)
933935
data, metadata = bundle
@@ -943,7 +945,7 @@ def test_repr_in_anywidget_mode_should_not_be_deferred(
943945
Test that repr(df) is not deferred in anywidget mode.
944946
This is to ensure that print(df) works as expected.
945947
"""
946-
with bigframes.option_context("display.repr_mode", "anywidget"):
948+
with bigframes.option_context("display.render_mode", "anywidget"):
947949
representation = repr(paginated_bf_df)
948950
assert "Computation deferred" not in representation
949951
assert "page_1_row_1" in representation
@@ -954,7 +956,7 @@ def test_dataframe_repr_mimebundle_should_return_widget_with_metadata_in_anywidg
954956
session: bigframes.Session, # Add session as a fixture
955957
):
956958
"""Test that _repr_mimebundle_ returns a widget view with metadata when anywidget is available."""
957-
with bigframes.option_context("display.repr_mode", "anywidget"):
959+
with bigframes.option_context("display.render_mode", "anywidget"):
958960
# Create a real DataFrame object (or a mock that behaves like one minimally)
959961
# for _repr_mimebundle_ to operate on.
960962
test_df = bigframes.dataframe.DataFrame(
@@ -1051,7 +1053,7 @@ def test_widget_with_default_index_should_display_index_column_with_empty_header
10511053

10521054
from bigframes.display.anywidget import TableWidget
10531055

1054-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 2):
1056+
with bf.option_context("display.render_mode", "anywidget", "display.max_rows", 2):
10551057
widget = TableWidget(paginated_bf_df)
10561058
html = widget.table_html
10571059

@@ -1075,7 +1077,7 @@ def test_widget_with_custom_index_should_display_index_column(
10751077
"""
10761078
from bigframes.display.anywidget import TableWidget
10771079

1078-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 2):
1080+
with bf.option_context("display.render_mode", "anywidget", "display.max_rows", 2):
10791081
widget = TableWidget(custom_index_bf_df)
10801082
html = widget.table_html
10811083

@@ -1095,7 +1097,7 @@ def test_widget_with_custom_index_pagination_preserves_index(
10951097
"""
10961098
from bigframes.display.anywidget import TableWidget
10971099

1098-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 2):
1100+
with bf.option_context("display.render_mode", "anywidget", "display.max_rows", 2):
10991101
widget = TableWidget(custom_index_bf_df)
11001102

11011103
widget.page = 1 # Navigate to page 2
@@ -1116,7 +1118,7 @@ def test_widget_with_custom_index_matches_pandas_output(
11161118
"""
11171119
from bigframes.display.anywidget import TableWidget
11181120

1119-
with bf.option_context("display.repr_mode", "anywidget", "display.max_rows", 3):
1121+
with bf.option_context("display.render_mode", "anywidget", "display.max_rows", 3):
11201122
widget = TableWidget(custom_index_bf_df)
11211123
html = widget.table_html
11221124

@@ -1137,7 +1139,7 @@ def test_series_anywidget_integration_with_notebook_display(
11371139
"""Test Series display integration in Jupyter-like environment."""
11381140
pytest.importorskip("anywidget")
11391141

1140-
with bf.option_context("display.repr_mode", "anywidget"):
1142+
with bf.option_context("display.render_mode", "anywidget"):
11411143
series = paginated_bf_df["value"]
11421144

11431145
# Test the full display pipeline
@@ -1162,7 +1164,7 @@ def test_series_different_data_types_anywidget(session: bf.Session):
11621164
)
11631165
bf_df = session.read_pandas(test_data)
11641166

1165-
with bf.option_context("display.repr_mode", "anywidget"):
1167+
with bf.option_context("display.render_mode", "anywidget"):
11661168
for col_name in test_data.columns:
11671169
series = bf_df[col_name]
11681170
widget = bigframes.display.TableWidget(series.to_frame())

tests/system/small/test_progress_bar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def test_query_job_dry_run_series(penguins_df_default_index: bf.dataframe.DataFr
151151

152152
def test_repr_anywidget_dataframe(penguins_df_default_index: bf.dataframe.DataFrame):
153153
pytest.importorskip("anywidget")
154-
with bf.option_context("display.repr_mode", "anywidget"):
154+
with bf.option_context("display.render_mode", "anywidget"):
155155
actual_repr = repr(penguins_df_default_index)
156156
assert "species" in actual_repr
157157
assert "island" in actual_repr
@@ -160,7 +160,7 @@ def test_repr_anywidget_dataframe(penguins_df_default_index: bf.dataframe.DataFr
160160

161161
def test_repr_anywidget_index(penguins_df_default_index: bf.dataframe.DataFrame):
162162
pytest.importorskip("anywidget")
163-
with bf.option_context("display.repr_mode", "anywidget"):
163+
with bf.option_context("display.render_mode", "anywidget"):
164164
index = penguins_df_default_index.index
165165
actual_repr = repr(index)
166166
# In non-interactive environments, should still get a useful summary.

tests/unit/display/test_anywidget.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_navigation_to_invalid_page_resets_to_valid_page_without_deadlock():
4343
# We mock _initial_load to avoid complex setup
4444
with mock.patch.object(TableWidget, "_initial_load"):
4545
with bigframes.option_context(
46-
"display.repr_mode", "anywidget", "display.max_rows", 10
46+
"display.render_mode", "anywidget", "display.max_rows", 10
4747
):
4848
widget = TableWidget(mock_df)
4949

@@ -130,7 +130,7 @@ def test_sorting_single_column(mock_df):
130130
"""Test that the widget can be sorted by a single column."""
131131
from bigframes.display.anywidget import TableWidget
132132

133-
with bigframes.option_context("display.repr_mode", "anywidget"):
133+
with bigframes.option_context("display.render_mode", "anywidget"):
134134
widget = TableWidget(mock_df)
135135

136136
# Verify initial state
@@ -149,7 +149,7 @@ def test_sorting_multi_column(mock_df):
149149
"""Test that the widget can be sorted by multiple columns."""
150150
from bigframes.display.anywidget import TableWidget
151151

152-
with bigframes.option_context("display.repr_mode", "anywidget"):
152+
with bigframes.option_context("display.render_mode", "anywidget"):
153153
widget = TableWidget(mock_df)
154154

155155
# Apply multi-column sort
@@ -165,7 +165,7 @@ def test_page_size_change_resets_sort(mock_df):
165165
"""Test that changing the page size resets the sorting."""
166166
from bigframes.display.anywidget import TableWidget
167167

168-
with bigframes.option_context("display.repr_mode", "anywidget"):
168+
with bigframes.option_context("display.render_mode", "anywidget"):
169169
widget = TableWidget(mock_df)
170170

171171
# Set sort state

0 commit comments

Comments
 (0)