Skip to content

Commit 988ea15

Browse files
committed
Fix widget RecordBatchReader compatibility
DuckDB's .arrow() returns RecordBatchReader instead of Table in some environments. Call .read_all() when available to ensure we always get a Table for subscripting.
1 parent 7dc418f commit 988ea15

2 files changed

Lines changed: 324 additions & 1 deletion

File tree

examples/widget_demo_simple.ipynb

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "pep723-metadata",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"# /// script\n",
11+
"# requires-python = \">=3.10\"\n",
12+
"# dependencies = [\n",
13+
"# \"sidemantic[widget]>=0.5.10\",\n",
14+
"# \"duckdb\",\n",
15+
"# \"numpy\",\n",
16+
"# \"pandas\",\n",
17+
"# ]\n",
18+
"# ///"
19+
]
20+
},
21+
{
22+
"cell_type": "code",
23+
"execution_count": 2,
24+
"id": "load-data",
25+
"metadata": {},
26+
"outputs": [
27+
{
28+
"data": {
29+
"text/html": [
30+
"<div>\n",
31+
"<style scoped>\n",
32+
" .dataframe tbody tr th:only-of-type {\n",
33+
" vertical-align: middle;\n",
34+
" }\n",
35+
"\n",
36+
" .dataframe tbody tr th {\n",
37+
" vertical-align: top;\n",
38+
" }\n",
39+
"\n",
40+
" .dataframe thead th {\n",
41+
" text-align: right;\n",
42+
" }\n",
43+
"</style>\n",
44+
"<table border=\"1\" class=\"dataframe\">\n",
45+
" <thead>\n",
46+
" <tr style=\"text-align: right;\">\n",
47+
" <th></th>\n",
48+
" <th>has_bid_floor_cnt</th>\n",
49+
" <th>bid_request_cnt</th>\n",
50+
" <th>bid_floor</th>\n",
51+
" <th>app_site_name</th>\n",
52+
" <th>app_site_domain</th>\n",
53+
" <th>pub_name</th>\n",
54+
" <th>app_site_cat</th>\n",
55+
" <th>ad_size</th>\n",
56+
" <th>device_region</th>\n",
57+
" <th>device_osv</th>\n",
58+
" <th>...</th>\n",
59+
" <th>platform_browser</th>\n",
60+
" <th>device_os</th>\n",
61+
" <th>device_type</th>\n",
62+
" <th>ad_position</th>\n",
63+
" <th>video_max_duration_bucket</th>\n",
64+
" <th>video_min_duration_bucket</th>\n",
65+
" <th>placement_type</th>\n",
66+
" <th>auction_type</th>\n",
67+
" <th>app_or_site</th>\n",
68+
" <th>__time</th>\n",
69+
" </tr>\n",
70+
" </thead>\n",
71+
" <tbody>\n",
72+
" <tr>\n",
73+
" <th>0</th>\n",
74+
" <td>1</td>\n",
75+
" <td>200</td>\n",
76+
" <td>0.1218</td>\n",
77+
" <td>FreeCell Solitaire: Card Games</td>\n",
78+
" <td>Not Available</td>\n",
79+
" <td>GG Software</td>\n",
80+
" <td>Arts &amp; Entertainment</td>\n",
81+
" <td>320x50</td>\n",
82+
" <td>USA/NH</td>\n",
83+
" <td>13</td>\n",
84+
" <td>...</td>\n",
85+
" <td>Chrome Mobile</td>\n",
86+
" <td>Android</td>\n",
87+
" <td>Mobile/Tablet</td>\n",
88+
" <td>Not Available</td>\n",
89+
" <td>Not Available</td>\n",
90+
" <td>Not Available</td>\n",
91+
" <td>BANNER</td>\n",
92+
" <td>First Price</td>\n",
93+
" <td>App</td>\n",
94+
" <td>2023-09-09 22:00:00</td>\n",
95+
" </tr>\n",
96+
" <tr>\n",
97+
" <th>1</th>\n",
98+
" <td>1</td>\n",
99+
" <td>200</td>\n",
100+
" <td>0.4250</td>\n",
101+
" <td>Spider Solitaire: Card Games</td>\n",
102+
" <td>Not Available</td>\n",
103+
" <td>Singular One</td>\n",
104+
" <td>Not Available</td>\n",
105+
" <td>320x50</td>\n",
106+
" <td>USA/ME</td>\n",
107+
" <td>13</td>\n",
108+
" <td>...</td>\n",
109+
" <td>Chrome Mobile</td>\n",
110+
" <td>Android</td>\n",
111+
" <td>Mobile/Tablet</td>\n",
112+
" <td>Not Available</td>\n",
113+
" <td>Not Available</td>\n",
114+
" <td>Not Available</td>\n",
115+
" <td>BANNER</td>\n",
116+
" <td>First Price</td>\n",
117+
" <td>App</td>\n",
118+
" <td>2023-09-09 06:00:00</td>\n",
119+
" </tr>\n",
120+
" <tr>\n",
121+
" <th>2</th>\n",
122+
" <td>1</td>\n",
123+
" <td>200</td>\n",
124+
" <td>0.8900</td>\n",
125+
" <td>Not Available</td>\n",
126+
" <td>lockercodes.io</td>\n",
127+
" <td>Not Available</td>\n",
128+
" <td>Technology &amp; Computing</td>\n",
129+
" <td>300x250</td>\n",
130+
" <td>USA/ME</td>\n",
131+
" <td>13</td>\n",
132+
" <td>...</td>\n",
133+
" <td>Chrome Mobile</td>\n",
134+
" <td>Android</td>\n",
135+
" <td>Mobile/Tablet</td>\n",
136+
" <td>Not Available</td>\n",
137+
" <td>Not Available</td>\n",
138+
" <td>Not Available</td>\n",
139+
" <td>BANNER</td>\n",
140+
" <td>First Price</td>\n",
141+
" <td>Site</td>\n",
142+
" <td>2023-09-09 23:00:00</td>\n",
143+
" </tr>\n",
144+
" <tr>\n",
145+
" <th>3</th>\n",
146+
" <td>1</td>\n",
147+
" <td>200</td>\n",
148+
" <td>0.0078</td>\n",
149+
" <td>Connatix ORTB Desktop Supply</td>\n",
150+
" <td>tasteofhome.com</td>\n",
151+
" <td>Not Available</td>\n",
152+
" <td>Not Available</td>\n",
153+
" <td>300x250</td>\n",
154+
" <td>USA/ME</td>\n",
155+
" <td>13</td>\n",
156+
" <td>...</td>\n",
157+
" <td>Chrome Mobile</td>\n",
158+
" <td>Android</td>\n",
159+
" <td>Mobile/Tablet</td>\n",
160+
" <td>Not Available</td>\n",
161+
" <td>Not Available</td>\n",
162+
" <td>Not Available</td>\n",
163+
" <td>BANNER</td>\n",
164+
" <td>First Price</td>\n",
165+
" <td>Site</td>\n",
166+
" <td>2023-09-09 13:00:00</td>\n",
167+
" </tr>\n",
168+
" <tr>\n",
169+
" <th>4</th>\n",
170+
" <td>1</td>\n",
171+
" <td>200</td>\n",
172+
" <td>0.5300</td>\n",
173+
" <td>Not Available</td>\n",
174+
" <td>audacy.com</td>\n",
175+
" <td>Not Available</td>\n",
176+
" <td>Technology &amp; Computing</td>\n",
177+
" <td>320x50</td>\n",
178+
" <td>USA/NH</td>\n",
179+
" <td>13</td>\n",
180+
" <td>...</td>\n",
181+
" <td>Chrome Mobile</td>\n",
182+
" <td>Android</td>\n",
183+
" <td>Mobile/Tablet</td>\n",
184+
" <td>Not Available</td>\n",
185+
" <td>Not Available</td>\n",
186+
" <td>Not Available</td>\n",
187+
" <td>BANNER</td>\n",
188+
" <td>First Price</td>\n",
189+
" <td>Site</td>\n",
190+
" <td>2023-09-09 20:00:00</td>\n",
191+
" </tr>\n",
192+
" </tbody>\n",
193+
"</table>\n",
194+
"<p>5 rows × 21 columns</p>\n",
195+
"</div>"
196+
],
197+
"text/plain": [
198+
" has_bid_floor_cnt bid_request_cnt bid_floor \\\n",
199+
"0 1 200 0.1218 \n",
200+
"1 1 200 0.4250 \n",
201+
"2 1 200 0.8900 \n",
202+
"3 1 200 0.0078 \n",
203+
"4 1 200 0.5300 \n",
204+
"\n",
205+
" app_site_name app_site_domain pub_name \\\n",
206+
"0 FreeCell Solitaire: Card Games Not Available GG Software \n",
207+
"1 Spider Solitaire: Card Games Not Available Singular One \n",
208+
"2 Not Available lockercodes.io Not Available \n",
209+
"3 Connatix ORTB Desktop Supply tasteofhome.com Not Available \n",
210+
"4 Not Available audacy.com Not Available \n",
211+
"\n",
212+
" app_site_cat ad_size device_region device_osv ... \\\n",
213+
"0 Arts & Entertainment 320x50 USA/NH 13 ... \n",
214+
"1 Not Available 320x50 USA/ME 13 ... \n",
215+
"2 Technology & Computing 300x250 USA/ME 13 ... \n",
216+
"3 Not Available 300x250 USA/ME 13 ... \n",
217+
"4 Technology & Computing 320x50 USA/NH 13 ... \n",
218+
"\n",
219+
" platform_browser device_os device_type ad_position \\\n",
220+
"0 Chrome Mobile Android Mobile/Tablet Not Available \n",
221+
"1 Chrome Mobile Android Mobile/Tablet Not Available \n",
222+
"2 Chrome Mobile Android Mobile/Tablet Not Available \n",
223+
"3 Chrome Mobile Android Mobile/Tablet Not Available \n",
224+
"4 Chrome Mobile Android Mobile/Tablet Not Available \n",
225+
"\n",
226+
" video_max_duration_bucket video_min_duration_bucket placement_type \\\n",
227+
"0 Not Available Not Available BANNER \n",
228+
"1 Not Available Not Available BANNER \n",
229+
"2 Not Available Not Available BANNER \n",
230+
"3 Not Available Not Available BANNER \n",
231+
"4 Not Available Not Available BANNER \n",
232+
"\n",
233+
" auction_type app_or_site __time \n",
234+
"0 First Price App 2023-09-09 22:00:00 \n",
235+
"1 First Price App 2023-09-09 06:00:00 \n",
236+
"2 First Price Site 2023-09-09 23:00:00 \n",
237+
"3 First Price Site 2023-09-09 13:00:00 \n",
238+
"4 First Price Site 2023-09-09 20:00:00 \n",
239+
"\n",
240+
"[5 rows x 21 columns]"
241+
]
242+
},
243+
"execution_count": 2,
244+
"metadata": {},
245+
"output_type": "execute_result"
246+
}
247+
],
248+
"source": [
249+
"import duckdb\n",
250+
"\n",
251+
"conn = duckdb.connect(\":memory:\")\n",
252+
"conn.execute(\"\"\"\n",
253+
" create table auctions as\n",
254+
" select * from 'https://sampledata.sidequery.dev/sidemantic-demo/auction_data.parquet'\n",
255+
"\"\"\")\n",
256+
"\n",
257+
"conn.execute(\"SELECT * FROM auctions LIMIT 5\").fetchdf()"
258+
]
259+
},
260+
{
261+
"cell_type": "code",
262+
"execution_count": 3,
263+
"id": "show-widget",
264+
"metadata": {},
265+
"outputs": [
266+
{
267+
"data": {
268+
"application/vnd.jupyter.widget-view+json": {
269+
"model_id": "72c6ce3c24b04fa1a68c663c3aee31f7",
270+
"version_major": 2,
271+
"version_minor": 1
272+
},
273+
"text/plain": [
274+
"<sidemantic.widget._widget.MetricsExplorer object at 0x1160f7ef0>"
275+
]
276+
},
277+
"execution_count": 3,
278+
"metadata": {},
279+
"output_type": "execute_result"
280+
}
281+
],
282+
"source": [
283+
"from sidemantic.widget import MetricsExplorer\n",
284+
"\n",
285+
"widget = MetricsExplorer(conn.table(\"auctions\"))\n",
286+
"widget"
287+
]
288+
},
289+
{
290+
"cell_type": "code",
291+
"execution_count": null,
292+
"id": "69a94279-b665-4118-9a80-8f9ee71f8fad",
293+
"metadata": {},
294+
"outputs": [],
295+
"source": []
296+
}
297+
],
298+
"metadata": {
299+
"kernelspec": {
300+
"display_name": "Python 3 (ipykernel)",
301+
"language": "python",
302+
"name": "python3"
303+
},
304+
"language_info": {
305+
"codemirror_mode": {
306+
"name": "ipython",
307+
"version": 3
308+
},
309+
"file_extension": ".py",
310+
"mimetype": "text/x-python",
311+
"name": "python",
312+
"nbconvert_exporter": "python",
313+
"pygments_lexer": "ipython3",
314+
"version": "3.12.6"
315+
}
316+
},
317+
"nbformat": 4,
318+
"nbformat_minor": 5
319+
}

sidemantic/widget/_widget.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,11 @@ def _execute_arrow(self, sql: str):
696696
result = self._layer.adapter.execute(sql)
697697
reader = result.fetch_record_batch()
698698
return reader.read_all()
699-
return self._conn.execute(sql).arrow()
699+
# DuckDB's .arrow() may return RecordBatchReader in some environments
700+
reader = self._conn.execute(sql).arrow()
701+
if hasattr(reader, "read_all"):
702+
return reader.read_all()
703+
return reader
700704

701705
def _sync_status(self) -> None:
702706
error = self._metric_error or self._dimension_error

0 commit comments

Comments
 (0)