Skip to content

Commit 046fc64

Browse files
committed
change how row_count is updated
1 parent 83b7d92 commit 046fc64

File tree

3 files changed

+59
-39
lines changed

3 files changed

+59
-39
lines changed

bigframes/display/anywidget.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,11 @@ def __init__(self, dataframe: bigframes.dataframe.DataFrame):
7676
# Initialize data fetching attributes.
7777
self._batches = dataframe.to_pandas_batches(page_size=initial_page_size)
7878

79-
# Access total_rows through type casting (internal use only)
80-
from bigframes.core.blocks import PandasBatches
81-
82-
if isinstance(self._batches, PandasBatches):
83-
self.row_count = self._batches.total_rows or 0
84-
else:
85-
# Fallback for compatibility
86-
self.row_count = 0
79+
# Get total rows efficiently by executing the query once
80+
execute_result = dataframe._block.session._executor.execute(
81+
dataframe._block.expr, ordered=True
82+
)
83+
self.row_count = execute_result.total_rows or 0
8784

8885
# set traitlets properties that trigger observers
8986
self.page_size = initial_page_size

notebooks/dataframes/dataframe.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5366,7 +5366,7 @@
53665366
],
53675367
"metadata": {
53685368
"kernelspec": {
5369-
"display_name": "Python 3 (ipykernel)",
5369+
"display_name": "venv",
53705370
"language": "python",
53715371
"name": "python3"
53725372
},
@@ -5380,7 +5380,7 @@
53805380
"name": "python",
53815381
"nbconvert_exporter": "python",
53825382
"pygments_lexer": "ipython3",
5383-
"version": "3.11.1"
5383+
"version": "3.10.15"
53845384
}
53855385
},
53865386
"nbformat": 4,

tests/system/small/test_anywidget.py

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,32 @@ def empty_bf_df(
112112
return session.read_pandas(empty_pandas_df)
113113

114114

115+
def mock_execute_total_rows_is_none(self, schema, *args, **kwargs):
116+
"""Mocks an execution result where the total row count is missing."""
117+
from bigframes.session.executor import ExecuteResult
118+
119+
return ExecuteResult(
120+
iter([]), # arrow_batches
121+
schema=schema,
122+
query_job=None,
123+
total_bytes=None,
124+
total_rows=None, # The specific failure condition for this case
125+
)
126+
127+
128+
def mock_execute_batches_are_invalid(self, schema, *args, **kwargs):
129+
"""Mocks an execution result where the batch data is an invalid type."""
130+
from bigframes.session.executor import ExecuteResult
131+
132+
return ExecuteResult(
133+
None, # Invalid type for arrow_batches, which should be an iterator
134+
schema=schema,
135+
query_job=None,
136+
total_bytes=None,
137+
total_rows=100, # A valid row count, as the error is in the batch data
138+
)
139+
140+
115141
def _assert_html_matches_pandas_slice(
116142
table_html: str,
117143
expected_pd_slice: pd.DataFrame,
@@ -461,46 +487,43 @@ def test_widget_row_count_should_be_immutable_after_creation(
461487
assert widget.row_count == initial_row_count
462488

463489

464-
def test_widget_should_fallback_to_zero_rows_when_total_rows_is_none(
465-
paginated_bf_df: bf.dataframe.DataFrame, monkeypatch: pytest.MonkeyPatch
466-
):
467-
"""
468-
Given an internal component that fails to provide a total row count,
469-
when the widget is created, the row_count should safely fall back to 0.
470-
"""
471-
from bigframes.core.blocks import PandasBatches
472-
473-
# Simulate an internal failure where total_rows returns None
474-
monkeypatch.setattr(PandasBatches, "total_rows", property(lambda self: None))
475-
476-
with bf.option_context("display.repr_mode", "anywidget"):
477-
from bigframes.display import TableWidget
478-
479-
widget = TableWidget(paginated_bf_df)
480-
481-
assert widget.row_count == 0
482-
483-
484-
def test_widget_should_fallback_to_zero_rows_when_batches_are_invalid_type(
485-
paginated_bf_df: bf.dataframe.DataFrame, monkeypatch: pytest.MonkeyPatch
490+
@pytest.mark.parametrize(
491+
"mock_function",
492+
[
493+
mock_execute_total_rows_is_none,
494+
mock_execute_batches_are_invalid,
495+
],
496+
# 'ids' provides descriptive names for each test run in the pytest report.
497+
ids=[
498+
"when_total_rows_is_None",
499+
"when_arrow_batches_are_invalid",
500+
],
501+
)
502+
def test_widget_should_fallback_to_zero_rows_on_error(
503+
paginated_bf_df: bf.dataframe.DataFrame,
504+
monkeypatch: pytest.MonkeyPatch,
505+
mock_function,
486506
):
487507
"""
488-
Given an internal component that returns an unexpected data type,
489-
when the widget is created, the row_count should safely fall back to 0.
508+
Given an internal component fails to return valid execution data,
509+
when the TableWidget is created, its row_count should safely fall back to 0.
490510
"""
491-
# Simulate internal method returning an unexpected type (a simple iterator)
492-
def mock_to_pandas_batches(self, **kwargs):
493-
return iter([paginated_bf_df.to_pandas().iloc[:2]])
494-
511+
# The 'self' argument is automatically handled when monkeypatch calls the method.
512+
# We use a lambda to pass the DataFrame's schema to our mock function.
495513
monkeypatch.setattr(
496-
"bigframes.dataframe.DataFrame.to_pandas_batches", mock_to_pandas_batches
514+
"bigframes.session.bq_caching_executor.BigQueryCachingExecutor.execute",
515+
lambda self, *args, **kwargs: mock_function(
516+
self, paginated_bf_df._block.expr.schema, *args, **kwargs
517+
),
497518
)
498519

499520
with bf.option_context("display.repr_mode", "anywidget"):
500521
from bigframes.display import TableWidget
501522

523+
# The widget should handle the faulty data from the mock without crashing.
502524
widget = TableWidget(paginated_bf_df)
503525

526+
# The key assertion: The widget safely defaults to 0 rows.
504527
assert widget.row_count == 0
505528

506529

0 commit comments

Comments
 (0)