From ad0ece6a8fd7a72e22b0fdc76397e89ef6436eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Swe=C3=B1a?= Date: Thu, 9 Oct 2025 18:57:31 +0000 Subject: [PATCH] fix: `blob.display()` shows for null rows --- bigframes/operations/blob.py | 9 ++++++-- noxfile.py | 5 +++++ tests/system/small/blob/test_io.py | 33 ++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/bigframes/operations/blob.py b/bigframes/operations/blob.py index 7f419bc5d8..d505f096f4 100644 --- a/bigframes/operations/blob.py +++ b/bigframes/operations/blob.py @@ -228,9 +228,14 @@ def display( df._set_internal_query_job(query_job) def display_single_url( - read_url: str, content_type: Union[str, pd._libs.missing.NAType] + read_url: Union[str, pd._libs.missing.NAType], + content_type: Union[str, pd._libs.missing.NAType], ): - if content_type is pd.NA: # display as raw data or error + if pd.isna(read_url): + ipy_display.display("") + return + + if pd.isna(content_type): # display as raw data or error response = requests.get(read_url) ipy_display.display(response.content) return diff --git a/noxfile.py b/noxfile.py index a46dc36b3e..988847fc14 100644 --- a/noxfile.py +++ b/noxfile.py @@ -126,6 +126,11 @@ # Sessions are executed in the order so putting the smaller sessions # ahead to fail fast at presubmit running. nox.options.sessions = [ + # Include unit_noextras to ensure at least some unit tests contribute to + # coverage. + # TODO(tswast): Consider removing this when unit_noextras and cover is run + # from GitHub actions. + "unit_noextras", "system-3.9", # No extras. "system-3.11", "cover", diff --git a/tests/system/small/blob/test_io.py b/tests/system/small/blob/test_io.py index d3b4c4faa0..5ada4fabb0 100644 --- a/tests/system/small/blob/test_io.py +++ b/tests/system/small/blob/test_io.py @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from unittest import mock + +import IPython.display import pandas as pd import bigframes @@ -92,3 +95,33 @@ def test_blob_create_read_gbq_object_table( pd.testing.assert_frame_equal( pd_blob_df, expected_df, check_dtype=False, check_index_type=False ) + + +def test_display_images(monkeypatch, images_mm_df: bpd.DataFrame): + mock_display = mock.Mock() + monkeypatch.setattr(IPython.display, "display", mock_display) + + images_mm_df["blob_col"].blob.display() + + for call in mock_display.call_args_list: + args, _ = call + arg = args[0] + assert isinstance(arg, IPython.display.Image) + + +def test_display_nulls( + monkeypatch, + bq_connection: str, + session: bigframes.Session, +): + uri_series = bpd.Series([None, None, None], dtype="string", session=session) + blob_series = uri_series.str.to_blob(connection=bq_connection) + mock_display = mock.Mock() + monkeypatch.setattr(IPython.display, "display", mock_display) + + blob_series.blob.display() + + for call in mock_display.call_args_list: + args, _ = call + arg = args[0] + assert arg == ""