Skip to content

Commit 189d8bc

Browse files
committed
still have zero total rows issue
1 parent e41b3ba commit 189d8bc

File tree

7 files changed

+104
-131
lines changed

7 files changed

+104
-131
lines changed

bigframes/display/anywidget.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
import bigframes
2626
import bigframes.core.blocks
2727
import bigframes.display.html
28+
import bigframes.session.execution_spec
2829

29-
# anywidget and traitlets are optional dependencies. We don't want the import of this
30-
# module to fail if they aren't installed, though. Instead, we try to limit the surface that
31-
# these packages could affect. This makes unit testing easier and ensures we don't
32-
# accidentally make these required packages.
30+
# anywidget and traitlets are optional dependencies. We don't want the import of
31+
# this module to fail if they aren't installed, though. Instead, we try to
32+
# limit the surface that these packages could affect. This makes unit testing
33+
# easier and ensures we don't accidentally make these required packages.
3334
try:
3435
import anywidget
3536
import traitlets
@@ -65,7 +66,8 @@ def __init__(self, dataframe: bigframes.dataframe.DataFrame):
6566
"""
6667
if not ANYWIDGET_INSTALLED:
6768
raise ImportError(
68-
"Please `pip install anywidget traitlets` or `pip install 'bigframes[anywidget]'` to use TableWidget."
69+
"Please `pip install anywidget traitlets` or "
70+
"`pip install 'bigframes[anywidget]'` to use TableWidget."
6971
)
7072

7173
self._dataframe = dataframe
@@ -87,16 +89,22 @@ def __init__(self, dataframe: bigframes.dataframe.DataFrame):
8789
# Respect display options for initial page size
8890
self.page_size = bigframes.options.display.max_rows
8991

90-
self._batches: bigframes.core.blocks.PandasBatches = cast(
92+
# Force execution with explicit destination to get total_rows metadata
93+
execute_result = dataframe._block.session._executor.execute(
94+
dataframe._block.expr,
95+
execution_spec=bigframes.session.execution_spec.ExecutionSpec(
96+
ordered=True, promise_under_10gb=False
97+
),
98+
)
99+
# The query issued by `to_pandas_batches()` already contains
100+
# metadata about how many results there were. Use that to avoid
101+
# doing an extra COUNT(*) query that `len(...)` would do.
102+
self.row_count = execute_result.total_rows or 0
103+
self._batches = cast(
91104
bigframes.core.blocks.PandasBatches,
92-
dataframe.to_pandas_batches(page_size=self.page_size),
105+
execute_result.to_pandas_batches(page_size=self.page_size),
93106
)
94107

95-
# The query issued by `to_pandas_batches()` already contains metadata
96-
# about how many results there were. Use that to avoid doing an extra
97-
# COUNT(*) query that `len(...)` would do.
98-
self.row_count = self._batches.total_rows or 0
99-
100108
self._set_table_html()
101109
self._initializing = False
102110

@@ -186,17 +194,27 @@ def _cached_data(self) -> pd.DataFrame:
186194
return pd.DataFrame(columns=self._dataframe.columns)
187195
return pd.concat(self._cached_batches, ignore_index=True)
188196

189-
def _reset_batches_for_new_page_size(self):
197+
def _reset_batches_for_new_page_size(self) -> None:
190198
"""Reset the batch iterator when page size changes."""
199+
# Execute with explicit destination for consistency with __init__
200+
execute_result = self._dataframe._block.session._executor.execute(
201+
self._dataframe._block.expr,
202+
execution_spec=bigframes.session.execution_spec.ExecutionSpec(
203+
ordered=True, promise_under_10gb=False
204+
),
205+
)
206+
207+
# Create pandas batches from the ExecuteResult
191208
self._batches = cast(
192209
bigframes.core.blocks.PandasBatches,
193-
self._dataframe.to_pandas_batches(page_size=self.page_size),
210+
execute_result.to_pandas_batches(page_size=self.page_size),
194211
)
212+
195213
self._cached_batches = []
196214
self._batch_iter = None
197215
self._all_data_loaded = False
198216

199-
def _set_table_html(self):
217+
def _set_table_html(self) -> None:
200218
"""Sets the current html data based on the current page and page size."""
201219
start = self.page * self.page_size
202220
end = start + self.page_size
@@ -219,14 +237,14 @@ def _set_table_html(self):
219237
)
220238

221239
@traitlets.observe("page")
222-
def _page_changed(self, _change: Dict[str, Any]):
240+
def _page_changed(self, _change: Dict[str, Any]) -> None:
223241
"""Handler for when the page number is changed from the frontend."""
224242
if self._initializing:
225243
return
226244
self._set_table_html()
227245

228246
@traitlets.observe("page_size")
229-
def _page_size_changed(self, _change: Dict[str, Any]):
247+
def _page_size_changed(self, _change: Dict[str, Any]) -> None:
230248
"""Handler for when the page size is changed from the frontend."""
231249
if self._initializing:
232250
return

bigframes/session/bq_caching_executor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,11 +643,14 @@ def _execute_plan_gbq(
643643
)
644644

645645
table_info: Optional[bigquery.Table] = None
646+
total_rows = None
646647
if query_job and query_job.destination:
647648
table_info = self.bqclient.get_table(query_job.destination)
648649
size_bytes = table_info.num_bytes
650+
total_rows = table_info.num_rows
649651
else:
650652
size_bytes = None
653+
total_rows = iterator.total_rows
651654

652655
# we could actually cache even when caching is not explicitly requested, but being conservative for now
653656
if cache_spec is not None:
@@ -673,7 +676,7 @@ def _execute_plan_gbq(
673676
schema=og_schema,
674677
query_job=query_job,
675678
total_bytes=size_bytes,
676-
total_rows=iterator.total_rows,
679+
total_rows=total_rows,
677680
total_bytes_processed=iterator.total_bytes_processed,
678681
)
679682

notebooks/dataframes/anywidget_mode.ipynb

Lines changed: 34 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
},
3333
{
3434
"cell_type": "code",
35-
"execution_count": 1,
35+
"execution_count": 2,
3636
"id": "ca22f059",
3737
"metadata": {},
3838
"outputs": [],
@@ -50,7 +50,7 @@
5050
},
5151
{
5252
"cell_type": "code",
53-
"execution_count": 2,
53+
"execution_count": 3,
5454
"id": "1bc5aaf3",
5555
"metadata": {},
5656
"outputs": [],
@@ -69,7 +69,7 @@
6969
},
7070
{
7171
"cell_type": "code",
72-
"execution_count": 3,
72+
"execution_count": 4,
7373
"id": "f289d250",
7474
"metadata": {},
7575
"outputs": [
@@ -96,7 +96,7 @@
9696
},
9797
{
9898
"cell_type": "code",
99-
"execution_count": 4,
99+
"execution_count": 5,
100100
"id": "42bb02ab",
101101
"metadata": {},
102102
"outputs": [
@@ -123,27 +123,27 @@
123123
},
124124
{
125125
"cell_type": "code",
126-
"execution_count": 5,
126+
"execution_count": 6,
127127
"id": "ce250157",
128128
"metadata": {},
129129
"outputs": [
130130
{
131-
"data": {
132-
"application/vnd.jupyter.widget-view+json": {
133-
"model_id": "a85f5799996d4de1a7912182c43fdf54",
134-
"version_major": 2,
135-
"version_minor": 1
136-
},
137-
"text/plain": [
138-
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
139-
]
140-
},
141-
"metadata": {},
142-
"output_type": "display_data"
131+
"ename": "TypeError",
132+
"evalue": "BigQueryCachingExecutor.execute() got an unexpected keyword argument 'ordered'",
133+
"output_type": "error",
134+
"traceback": [
135+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
136+
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
137+
"\u001b[0;32m~/.pyenv/versions/3.10.18/lib/python3.10/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
138+
"\u001b[0;32m~/src/github.com/googleapis/python-bigquery-dataframes/bigframes/core/log_adapter.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 193\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mPANDAS_PARAM_TRACKING_TASK\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 194\u001b[0m )\n\u001b[0;32m--> 195\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 196\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0m_call_stack\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
139+
"\u001b[0;32m~/src/github.com/googleapis/python-bigquery-dataframes/bigframes/core/log_adapter.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 179\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 180\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 181\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mNotImplementedError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 182\u001b[0m \u001b[0;31m# Log method parameters that are implemented in pandas but either missing (TypeError)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
140+
"\u001b[0;32m~/src/github.com/googleapis/python-bigquery-dataframes/bigframes/dataframe.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 857\u001b[0m \u001b[0;31m# This ensures that each cell gets its own widget and prevents\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 858\u001b[0m \u001b[0;31m# unintended sharing between cells\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 859\u001b[0;31m \u001b[0mwidget\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTableWidget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 860\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 861\u001b[0m \u001b[0mipython_display\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwidget\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
141+
"\u001b[0;32m~/src/github.com/googleapis/python-bigquery-dataframes/bigframes/display/anywidget.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dataframe)\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;31m# Force execution with explicit destination to get total_rows metadata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 92\u001b[0;31m execute_result = dataframe._block.session._executor.execute( \n\u001b[0m\u001b[1;32m 93\u001b[0m \u001b[0mbigframes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mArrayValue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataframe\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_block\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# Wrap in ArrayValue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0mordered\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
142+
"\u001b[0;31mTypeError\u001b[0m: BigQueryCachingExecutor.execute() got an unexpected keyword argument 'ordered'"
143+
]
143144
},
144145
{
145146
"data": {
146-
"text/html": [],
147147
"text/plain": [
148148
"Computation deferred. Computation will process 171.4 MB"
149149
]
@@ -167,31 +167,21 @@
167167
},
168168
{
169169
"cell_type": "code",
170-
"execution_count": 6,
170+
"execution_count": 7,
171171
"id": "6920d49b",
172172
"metadata": {},
173173
"outputs": [
174174
{
175-
"name": "stdout",
176-
"output_type": "stream",
177-
"text": [
178-
"Total pages: 555246\n"
175+
"ename": "TypeError",
176+
"evalue": "BigQueryCachingExecutor.execute() got an unexpected keyword argument 'ordered'",
177+
"output_type": "error",
178+
"traceback": [
179+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
180+
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
181+
"\u001b[0;32m<ipython-input-7-49e9807f0359>\u001b[0m in \u001b[0;36m<cell line: 5>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# Create widget programmatically\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mwidget\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTableWidget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Total pages: {math.ceil(widget.row_count / widget.page_size)}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
182+
"\u001b[0;32m~/src/github.com/googleapis/python-bigquery-dataframes/bigframes/display/anywidget.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dataframe)\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;31m# Force execution with explicit destination to get total_rows metadata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 92\u001b[0;31m execute_result = dataframe._block.session._executor.execute( \n\u001b[0m\u001b[1;32m 93\u001b[0m \u001b[0mbigframes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mArrayValue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataframe\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_block\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;31m# Wrap in ArrayValue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0mordered\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
183+
"\u001b[0;31mTypeError\u001b[0m: BigQueryCachingExecutor.execute() got an unexpected keyword argument 'ordered'"
179184
]
180-
},
181-
{
182-
"data": {
183-
"application/vnd.jupyter.widget-view+json": {
184-
"model_id": "261075a0d1d2487f804926884fe55eb2",
185-
"version_major": 2,
186-
"version_minor": 1
187-
},
188-
"text/plain": [
189-
"TableWidget(page_size=10, row_count=5552452, table_html='<table border=\"1\" class=\"dataframe table table-stripe…"
190-
]
191-
},
192-
"execution_count": 7,
193-
"metadata": {},
194-
"output_type": "execute_result"
195185
}
196186
],
197187
"source": [
@@ -216,20 +206,10 @@
216206
},
217207
{
218208
"cell_type": "code",
219-
"execution_count": 7,
209+
"execution_count": null,
220210
"id": "12b68f15",
221211
"metadata": {},
222-
"outputs": [
223-
{
224-
"name": "stdout",
225-
"output_type": "stream",
226-
"text": [
227-
"Current page: 0\n",
228-
"After next: 1\n",
229-
"After prev: 0\n"
230-
]
231-
}
232-
],
212+
"outputs": [],
233213
"source": [
234214
"# Simulate button clicks programmatically\n",
235215
"print(\"Current page:\", widget.page)\n",
@@ -253,61 +233,22 @@
253233
},
254234
{
255235
"cell_type": "code",
256-
"execution_count": 8,
236+
"execution_count": null,
257237
"id": "a9d5d13a",
258238
"metadata": {},
259-
"outputs": [
260-
{
261-
"name": "stderr",
262-
"output_type": "stream",
263-
"text": [
264-
"/usr/local/google/home/swast/src/github.com/googleapis/python-bigquery-dataframes/bigframes/core/array_value.py:231: AmbiguousWindowWarning: Window ordering may be ambiguous, this can cause unstable results.\n",
265-
" warnings.warn(msg, bfe.AmbiguousWindowWarning)\n"
266-
]
267-
},
268-
{
269-
"name": "stdout",
270-
"output_type": "stream",
271-
"text": [
272-
"Small dataset pages: 1\n"
273-
]
274-
},
275-
{
276-
"data": {
277-
"application/vnd.jupyter.widget-view+json": {
278-
"model_id": "b37aec5d1c534e9f810e18c4fa7b8491",
279-
"version_major": 2,
280-
"version_minor": 1
281-
},
282-
"text/plain": [
283-
"TableWidget(page_size=10, row_count=5, table_html='<table border=\"1\" class=\"dataframe table table-striped tabl…"
284-
]
285-
},
286-
"execution_count": 9,
287-
"metadata": {},
288-
"output_type": "execute_result"
289-
}
290-
],
239+
"outputs": [],
291240
"source": [
292241
"# Test with very small dataset\n",
293242
"small_df = df.sort_values([\"name\", \"year\", \"state\"]).head(5)\n",
294243
"small_widget = TableWidget(small_df)\n",
295244
"print(f\"Small dataset pages: {math.ceil(small_widget.row_count / small_widget.page_size)}\")\n",
296245
"small_widget"
297246
]
298-
},
299-
{
300-
"cell_type": "code",
301-
"execution_count": null,
302-
"id": "c4e5836b-c872-4a9c-b9ec-14f6f338176d",
303-
"metadata": {},
304-
"outputs": [],
305-
"source": []
306247
}
307248
],
308249
"metadata": {
309250
"kernelspec": {
310-
"display_name": "Python 3 (ipykernel)",
251+
"display_name": "3.10.18",
311252
"language": "python",
312253
"name": "python3"
313254
},
@@ -321,7 +262,7 @@
321262
"name": "python",
322263
"nbconvert_exporter": "python",
323264
"pygments_lexer": "ipython3",
324-
"version": "3.10.16"
265+
"version": "3.10.18"
325266
}
326267
},
327268
"nbformat": 4,

0 commit comments

Comments
 (0)