Skip to content

Commit 088a089

Browse files
committed
fix: resolve double printing issue in anywidget mode
1 parent 0a1ba4f commit 088a089

File tree

3 files changed

+47
-43
lines changed

3 files changed

+47
-43
lines changed

bigframes/dataframe.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -792,13 +792,10 @@ def __repr__(self) -> str:
792792
if opts.repr_mode == "anywidget":
793793
# Try to display with anywidget, fall back to deferred if not in IPython
794794
try:
795-
from IPython.display import display as ipython_display
796-
797795
from bigframes import display
798796

799797
widget = display.TableWidget(self.copy())
800-
ipython_display(widget)
801-
return "" # Return empty string since we used display()
798+
return widget._repr_html_() # Return widget's HTML representation
802799
except (AttributeError, ValueError, ImportError):
803800
# Not in IPython environment, fall back to deferred mode
804801
return formatter.repr_query_job(self._compute_dry_run())

notebooks/dataframes/anywidget_mode.ipynb

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,25 @@
7373
"id": "f289d250",
7474
"metadata": {},
7575
"outputs": [
76+
{
77+
"data": {
78+
"application/vnd.jupyter.widget-view+json": {
79+
"model_id": "071c0a905297406ba6c990cbbb8fc28d",
80+
"version_major": 2,
81+
"version_minor": 0
82+
},
83+
"text/plain": [
84+
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
85+
]
86+
},
87+
"metadata": {},
88+
"output_type": "display_data"
89+
},
7690
{
7791
"name": "stdout",
7892
"output_type": "stream",
7993
"text": [
80-
"Computation deferred. Computation will process 171.4 MB\n"
94+
"\n"
8195
]
8296
}
8397
],
@@ -130,9 +144,9 @@
130144
{
131145
"data": {
132146
"application/vnd.jupyter.widget-view+json": {
133-
"model_id": "9e3e413eb0774a62818c58d217af8488",
147+
"model_id": "042a3d55c51b4a3192cf1a942c6797e8",
134148
"version_major": 2,
135-
"version_minor": 1
149+
"version_minor": 0
136150
},
137151
"text/plain": [
138152
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
@@ -143,11 +157,23 @@
143157
},
144158
{
145159
"data": {
146-
"text/html": [],
160+
"application/vnd.jupyter.widget-view+json": {
161+
"model_id": "06e46087ecbe4e6d8bdc2ed7c9284a7d",
162+
"version_major": 2,
163+
"version_minor": 0
164+
},
147165
"text/plain": [
148-
"Computation deferred. Computation will process 171.4 MB"
166+
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
149167
]
150168
},
169+
"metadata": {},
170+
"output_type": "display_data"
171+
},
172+
{
173+
"data": {
174+
"text/html": [],
175+
"text/plain": []
176+
},
151177
"execution_count": 6,
152178
"metadata": {},
153179
"output_type": "execute_result"
@@ -181,17 +207,16 @@
181207
{
182208
"data": {
183209
"application/vnd.jupyter.widget-view+json": {
184-
"model_id": "df5e93f0d03f45cda67aa6da7f9ef1ae",
210+
"model_id": "4da2aaa13f074229b022c7256ba7510b",
185211
"version_major": 2,
186-
"version_minor": 1
212+
"version_minor": 0
187213
},
188214
"text/plain": [
189215
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
190216
]
191217
},
192-
"execution_count": 7,
193218
"metadata": {},
194-
"output_type": "execute_result"
219+
"output_type": "display_data"
195220
}
196221
],
197222
"source": [
@@ -267,17 +292,16 @@
267292
{
268293
"data": {
269294
"application/vnd.jupyter.widget-view+json": {
270-
"model_id": "a4ec5248708442fabc59c446c78a1304",
295+
"model_id": "d2739b180f7d40b1b52ebab121ab7a8a",
271296
"version_major": 2,
272-
"version_minor": 1
297+
"version_minor": 0
273298
},
274299
"text/plain": [
275300
"TableWidget(page_size=10, row_count=5, table_html='<table border=\"1\" class=\"dataframe table table-striped tabl…"
276301
]
277302
},
278-
"execution_count": 9,
279303
"metadata": {},
280-
"output_type": "execute_result"
304+
"output_type": "display_data"
281305
}
282306
],
283307
"source": [
@@ -287,14 +311,6 @@
287311
"print(f\"Small dataset pages: {math.ceil(small_widget.row_count / small_widget.page_size)}\")\n",
288312
"small_widget"
289313
]
290-
},
291-
{
292-
"cell_type": "code",
293-
"execution_count": null,
294-
"id": "c4e5836b-c872-4a9c-b9ec-14f6f338176d",
295-
"metadata": {},
296-
"outputs": [],
297-
"source": []
298314
}
299315
],
300316
"metadata": {

tests/system/small/test_anywidget.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from __future__ import annotations
16+
17+
from unittest import mock
18+
1519
import pandas as pd
1620
import pytest
1721

1822
import bigframes as bf
1923

20-
pytest.importorskip("anywidget")
21-
2224
# Test constants to avoid change detector tests
2325
EXPECTED_ROW_COUNT = 6
2426
EXPECTED_PAGE_SIZE = 2
@@ -418,25 +420,15 @@ def test_setting_page_size_above_max_should_be_clamped(table_widget):
418420
# The page size is clamped to the maximum.
419421
assert table_widget.page_size == expected_clamped_size
420422

421-
422-
def test_widget_creation_should_load_css_for_rendering(table_widget):
423423
"""
424-
Given a TableWidget is created, when its resources are accessed,
425-
it should contain the CSS content required for styling.
424+
Test that the widget's CSS is loaded correctly.
426425
"""
427-
# The table_widget fixture creates the widget.
428-
# No additional setup is needed.
429-
430-
# Access the CSS content.
431426
css_content = table_widget._css
432-
433-
# The content is a non-empty string containing a known selector.
434-
assert isinstance(css_content, str)
435-
assert len(css_content) > 0
436427
assert ".bigframes-widget .footer" in css_content
437428

438429

439-
def test_sql_anywidget_mode(session: bf.Session):
430+
@mock.patch("bigframes.display.TableWidget")
431+
def test_sql_anywidget_mode(mock_table_widget, session: bf.Session):
440432
"""
441433
Test that a SQL query runs in anywidget mode.
442434
"""
@@ -446,9 +438,8 @@ def test_sql_anywidget_mode(session: bf.Session):
446438
df = session.read_gbq(sql)
447439
# In a real environment, this would display a widget.
448440
# For testing, we just want to make sure we're in the anywidget code path.
449-
# The `_repr_html_` method in anywidget mode will return an empty string
450-
# and display the widget via IPython's display mechanism.
451-
assert df._repr_html_() == ""
441+
df._repr_html_()
442+
mock_table_widget.assert_called_once()
452443

453444

454445
# TODO(shuowei): Add tests for custom index and multiindex

0 commit comments

Comments
 (0)