perspective-python 4.2.0__cp311-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. perspective/__init__.py +396 -0
  2. perspective/extension/finos-perspective-nbextension.json +5 -0
  3. perspective/handlers/__init__.py +11 -0
  4. perspective/handlers/aiohttp.py +61 -0
  5. perspective/handlers/starlette.py +55 -0
  6. perspective/handlers/tornado.py +184 -0
  7. perspective/perspective.pyd +0 -0
  8. perspective/templates/exported_widget.html.template +35 -0
  9. perspective/tests/__init__.py +11 -0
  10. perspective/tests/async/test_async_client.py +83 -0
  11. perspective/tests/async/test_websocket_client.py +124 -0
  12. perspective/tests/conftest.py +272 -0
  13. perspective/tests/core/__init__.py +11 -0
  14. perspective/tests/core/test_async.py +351 -0
  15. perspective/tests/multi_threaded/__init__.py +11 -0
  16. perspective/tests/multi_threaded/test_multi_threaded.py +201 -0
  17. perspective/tests/server/__init__.py +11 -0
  18. perspective/tests/server/test_server.py +1016 -0
  19. perspective/tests/server/test_session.py +110 -0
  20. perspective/tests/table/__init__.py +11 -0
  21. perspective/tests/table/arrow/date32.arrow +0 -0
  22. perspective/tests/table/arrow/date64.arrow +0 -0
  23. perspective/tests/table/arrow/dict.arrow +0 -0
  24. perspective/tests/table/arrow/dict_update.arrow +0 -0
  25. perspective/tests/table/arrow/int_float_str.arrow +0 -0
  26. perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
  27. perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
  28. perspective/tests/table/object_sequence.py +402 -0
  29. perspective/tests/table/test_column_paths.py +89 -0
  30. perspective/tests/table/test_delete.py +124 -0
  31. perspective/tests/table/test_exception.py +65 -0
  32. perspective/tests/table/test_leaks.py +54 -0
  33. perspective/tests/table/test_ports.py +178 -0
  34. perspective/tests/table/test_remove.py +102 -0
  35. perspective/tests/table/test_table.py +641 -0
  36. perspective/tests/table/test_table_arrow.py +503 -0
  37. perspective/tests/table/test_table_datetime.py +2409 -0
  38. perspective/tests/table/test_table_infer.py +201 -0
  39. perspective/tests/table/test_table_limit.py +45 -0
  40. perspective/tests/table/test_table_numpy.py +1022 -0
  41. perspective/tests/table/test_table_pandas.py +1018 -0
  42. perspective/tests/table/test_table_polars.py +251 -0
  43. perspective/tests/table/test_table_view_table.py +130 -0
  44. perspective/tests/table/test_to_arrow.py +417 -0
  45. perspective/tests/table/test_to_arrow_lz4.py +32 -0
  46. perspective/tests/table/test_to_format.py +1024 -0
  47. perspective/tests/table/test_to_polars.py +26 -0
  48. perspective/tests/table/test_update.py +545 -0
  49. perspective/tests/table/test_update_arrow.py +980 -0
  50. perspective/tests/table/test_update_pandas.py +211 -0
  51. perspective/tests/table/test_view.py +2261 -0
  52. perspective/tests/table/test_view_expression.py +1940 -0
  53. perspective/tests/test_dependencies.py +53 -0
  54. perspective/tests/viewer/__init__.py +11 -0
  55. perspective/tests/viewer/test_viewer.py +246 -0
  56. perspective/tests/widget/__init__.py +11 -0
  57. perspective/tests/widget/test_widget.py +278 -0
  58. perspective/tests/widget/test_widget_pandas.py +453 -0
  59. perspective/virtual_servers/__init__.py +134 -0
  60. perspective/virtual_servers/clickhouse.py +245 -0
  61. perspective/virtual_servers/duckdb.py +236 -0
  62. perspective/widget/__init__.py +349 -0
  63. perspective/widget/viewer/__init__.py +15 -0
  64. perspective/widget/viewer/validate.py +22 -0
  65. perspective/widget/viewer/viewer.py +343 -0
  66. perspective/widget/viewer/viewer_traitlets.py +101 -0
  67. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/install.json +5 -0
  68. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json +71 -0
  69. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js +2 -0
  70. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js.LICENSE.txt +25 -0
  71. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/523.c030af5d3c4f67ff83f6.js +1 -0
  72. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/remoteEntry.95a8ea1b44d96032833f.js +1 -0
  73. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/style.js +4 -0
  74. perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/third-party-licenses.json +16 -0
  75. perspective_python-4.2.0.dist-info/METADATA +27 -0
  76. perspective_python-4.2.0.dist-info/RECORD +79 -0
  77. perspective_python-4.2.0.dist-info/WHEEL +4 -0
  78. perspective_python-4.2.0.dist-info/licenses/LICENSE.md +193 -0
  79. perspective_python-4.2.0.dist-info/licenses/LICENSE_THIRDPARTY_cargo.yml +17395 -0
@@ -0,0 +1,453 @@
1
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ # ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ # ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ # ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ # ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ # ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ # ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ # ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ # ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import pandas as pd
14
+ import numpy as np
15
+ import pytest
16
+ from perspective.widget import PerspectiveWidget
17
+ import perspective as psp
18
+
19
+ client = psp.Server().new_local_client()
20
+ Table = client.table
21
+
22
+
23
+ pytest.skip(allow_module_level=True)
24
+
25
+
26
+ class TestWidgetPandas:
27
+ def test_widget_load_table_df(self, superstore):
28
+ table = Table(superstore)
29
+ widget = PerspectiveWidget(table)
30
+ assert widget.table.schema() == {
31
+ "index": "integer",
32
+ "Country": "string",
33
+ "Region": "string",
34
+ "Category": "string",
35
+ "City": "string",
36
+ "Customer ID": "string",
37
+ "Discount": "float",
38
+ "Order Date": "date",
39
+ "Order ID": "string",
40
+ "Postal Code": "string",
41
+ "Product ID": "string",
42
+ "Profit": "float",
43
+ "Quantity": "integer",
44
+ "Row ID": "integer",
45
+ "Sales": "integer",
46
+ "Segment": "string",
47
+ "Ship Date": "date",
48
+ "Ship Mode": "string",
49
+ "State": "string",
50
+ "Sub-Category": "string",
51
+ }
52
+
53
+ assert sorted(widget.columns) == sorted(
54
+ [
55
+ "index",
56
+ "Category",
57
+ "City",
58
+ "Country",
59
+ "Customer ID",
60
+ "Discount",
61
+ "Order Date",
62
+ "Order ID",
63
+ "Postal Code",
64
+ "Product ID",
65
+ "Profit",
66
+ "Quantity",
67
+ "Region",
68
+ "Row ID",
69
+ "Sales",
70
+ "Segment",
71
+ "Ship Date",
72
+ "Ship Mode",
73
+ "State",
74
+ "Sub-Category",
75
+ ]
76
+ )
77
+ view = widget.table.view()
78
+ assert view.num_rows() == len(superstore)
79
+ assert view.num_columns() == len(superstore.columns) + 1 # index
80
+
81
+ def test_widget_load_data_df(self, superstore):
82
+ widget = PerspectiveWidget(superstore)
83
+ assert sorted(widget.columns) == sorted(
84
+ [
85
+ "index",
86
+ "Category",
87
+ "City",
88
+ "Country",
89
+ "Customer ID",
90
+ "Discount",
91
+ "Order Date",
92
+ "Order ID",
93
+ "Postal Code",
94
+ "Product ID",
95
+ "Profit",
96
+ "Quantity",
97
+ "Region",
98
+ "Row ID",
99
+ "Sales",
100
+ "Segment",
101
+ "Ship Date",
102
+ "Ship Mode",
103
+ "State",
104
+ "Sub-Category",
105
+ ]
106
+ )
107
+ view = widget.table.view()
108
+ assert view.num_rows() == len(superstore)
109
+ assert view.num_columns() == 20
110
+
111
+ def test_widget_load_series(self, superstore):
112
+ series = pd.Series(superstore["Profit"].values, name="profit")
113
+ widget = PerspectiveWidget(series)
114
+ assert widget.table.schema() == {"index": "integer", "profit": "float"}
115
+
116
+ assert sorted(widget.columns) == sorted(["index", "profit"])
117
+ view = widget.table.view()
118
+ assert view.num_rows() == len(superstore)
119
+ assert view.num_columns() == 2
120
+
121
+ def test_widget_load_pivot_table(self, superstore):
122
+ pivot_table = pd.pivot_table(
123
+ superstore,
124
+ values="Discount",
125
+ index=["Country", "Region"],
126
+ columns=["Category", "Segment"],
127
+ )
128
+ widget = PerspectiveWidget(pivot_table)
129
+ assert widget.group_by == ["Country", "Region"]
130
+ assert widget.split_by == ["Category", "Segment"]
131
+ assert widget.columns == ["value"]
132
+ # table should host flattened data
133
+ view = widget.table.view()
134
+ assert view.num_rows() == 60
135
+ assert view.num_columns() == 6
136
+
137
+ def test_widget_load_pivot_table_with_user_pivots(self, superstore):
138
+ pivot_table = pd.pivot_table(
139
+ superstore,
140
+ values="Discount",
141
+ index=["Country", "Region"],
142
+ columns="Category",
143
+ )
144
+ widget = PerspectiveWidget(pivot_table, group_by=["Category", "Segment"])
145
+ assert widget.group_by == ["Category", "Segment"]
146
+ assert widget.split_by == []
147
+ assert widget.columns == [
148
+ "index",
149
+ "Country",
150
+ "Region",
151
+ "Financials",
152
+ "Industrials",
153
+ "Technology",
154
+ ]
155
+ # table should host flattened data
156
+ view = widget.table.view()
157
+ assert view.num_rows() == 5
158
+ assert view.num_columns() == 6
159
+
160
+ def test_widget_load_group_by(self, superstore):
161
+ df_pivoted = superstore.set_index(["Country", "Region"])
162
+ widget = PerspectiveWidget(df_pivoted)
163
+ assert widget.group_by == ["Country", "Region"]
164
+ assert widget.split_by == []
165
+ assert sorted(widget.columns) == sorted(
166
+ [
167
+ "index",
168
+ "Category",
169
+ "Country",
170
+ "City",
171
+ "Customer ID",
172
+ "Discount",
173
+ "Order Date",
174
+ "Order ID",
175
+ "Postal Code",
176
+ "Product ID",
177
+ "Profit",
178
+ "Quantity",
179
+ "Region",
180
+ "Row ID",
181
+ "Sales",
182
+ "Segment",
183
+ "Ship Date",
184
+ "Ship Mode",
185
+ "State",
186
+ "Sub-Category",
187
+ ]
188
+ )
189
+ assert widget.table.size() == 100
190
+ view = widget.table.view()
191
+ assert view.num_rows() == len(superstore)
192
+ assert view.num_columns() == len(superstore.columns) + 1 # index
193
+
194
+ def test_widget_load_group_by_with_user_pivots(self, superstore):
195
+ df_pivoted = superstore.set_index(["Country", "Region"])
196
+ widget = PerspectiveWidget(df_pivoted, group_by=["Category", "Segment"])
197
+ assert widget.group_by == ["Category", "Segment"]
198
+ assert widget.split_by == []
199
+ assert sorted(widget.columns) == sorted(
200
+ [
201
+ "index",
202
+ "Category",
203
+ "Country",
204
+ "City",
205
+ "Customer ID",
206
+ "Discount",
207
+ "Order Date",
208
+ "Order ID",
209
+ "Postal Code",
210
+ "Product ID",
211
+ "Profit",
212
+ "Quantity",
213
+ "Region",
214
+ "Row ID",
215
+ "Sales",
216
+ "Segment",
217
+ "Ship Date",
218
+ "Ship Mode",
219
+ "State",
220
+ "Sub-Category",
221
+ ]
222
+ )
223
+ assert widget.table.size() == 100
224
+ view = widget.table.view()
225
+ assert view.num_rows() == len(superstore)
226
+ assert view.num_columns() == len(superstore.columns) + 1 # index
227
+
228
+ def test_widget_load_split_by(self, superstore):
229
+ arrays = [
230
+ np.array(
231
+ [
232
+ "bar",
233
+ "bar",
234
+ "bar",
235
+ "bar",
236
+ "baz",
237
+ "baz",
238
+ "baz",
239
+ "baz",
240
+ "foo",
241
+ "foo",
242
+ "foo",
243
+ "foo",
244
+ "qux",
245
+ "qux",
246
+ "qux",
247
+ "qux",
248
+ ]
249
+ ),
250
+ np.array(
251
+ [
252
+ "one",
253
+ "one",
254
+ "two",
255
+ "two",
256
+ "one",
257
+ "one",
258
+ "two",
259
+ "two",
260
+ "one",
261
+ "one",
262
+ "two",
263
+ "two",
264
+ "one",
265
+ "one",
266
+ "two",
267
+ "two",
268
+ ]
269
+ ),
270
+ np.array(
271
+ [
272
+ "X",
273
+ "Y",
274
+ "X",
275
+ "Y",
276
+ "X",
277
+ "Y",
278
+ "X",
279
+ "Y",
280
+ "X",
281
+ "Y",
282
+ "X",
283
+ "Y",
284
+ "X",
285
+ "Y",
286
+ "X",
287
+ "Y",
288
+ ]
289
+ ),
290
+ ]
291
+ tuples = list(zip(*arrays))
292
+ index = pd.MultiIndex.from_tuples(tuples, names=["first", "second", "third"])
293
+ df_both = pd.DataFrame(
294
+ np.random.randn(3, 16), index=["A", "B", "C"], columns=index
295
+ )
296
+ widget = PerspectiveWidget(df_both)
297
+ assert widget.columns == ["value"]
298
+ assert widget.split_by == ["first", "second", "third"]
299
+ assert widget.group_by == ["index"]
300
+
301
+ def test_widget_load_split_by_preserve_user_settings(self, superstore):
302
+ arrays = [
303
+ np.array(
304
+ [
305
+ "bar",
306
+ "bar",
307
+ "bar",
308
+ "bar",
309
+ "baz",
310
+ "baz",
311
+ "baz",
312
+ "baz",
313
+ "foo",
314
+ "foo",
315
+ "foo",
316
+ "foo",
317
+ "qux",
318
+ "qux",
319
+ "qux",
320
+ "qux",
321
+ ]
322
+ ),
323
+ np.array(
324
+ [
325
+ "one",
326
+ "one",
327
+ "two",
328
+ "two",
329
+ "one",
330
+ "one",
331
+ "two",
332
+ "two",
333
+ "one",
334
+ "one",
335
+ "two",
336
+ "two",
337
+ "one",
338
+ "one",
339
+ "two",
340
+ "two",
341
+ ]
342
+ ),
343
+ np.array(
344
+ [
345
+ "X",
346
+ "Y",
347
+ "X",
348
+ "Y",
349
+ "X",
350
+ "Y",
351
+ "X",
352
+ "Y",
353
+ "X",
354
+ "Y",
355
+ "X",
356
+ "Y",
357
+ "X",
358
+ "Y",
359
+ "X",
360
+ "Y",
361
+ ]
362
+ ),
363
+ ]
364
+ tuples = list(zip(*arrays))
365
+ index = pd.MultiIndex.from_tuples(tuples, names=["first", "second", "third"])
366
+ df_both = pd.DataFrame(
367
+ np.random.randn(3, 16), index=["A", "B", "C"], columns=index
368
+ )
369
+ widget = PerspectiveWidget(df_both, columns=["first", "third"])
370
+ assert widget.columns == ["first", "third"]
371
+ assert widget.split_by == ["first", "second", "third"]
372
+ assert widget.group_by == ["index"]
373
+
374
+ def test_pivottable_values_index(self, superstore):
375
+ arrays = {
376
+ "A": [
377
+ "bar",
378
+ "bar",
379
+ "bar",
380
+ "bar",
381
+ "baz",
382
+ "baz",
383
+ "baz",
384
+ "baz",
385
+ "foo",
386
+ "foo",
387
+ "foo",
388
+ "foo",
389
+ "qux",
390
+ "qux",
391
+ "qux",
392
+ "qux",
393
+ ],
394
+ "B": [
395
+ "one",
396
+ "one",
397
+ "two",
398
+ "two",
399
+ "one",
400
+ "one",
401
+ "two",
402
+ "two",
403
+ "one",
404
+ "one",
405
+ "two",
406
+ "two",
407
+ "one",
408
+ "one",
409
+ "two",
410
+ "two",
411
+ ],
412
+ "C": [
413
+ "X",
414
+ "Y",
415
+ "X",
416
+ "Y",
417
+ "X",
418
+ "Y",
419
+ "X",
420
+ "Y",
421
+ "X",
422
+ "Y",
423
+ "X",
424
+ "Y",
425
+ "X",
426
+ "Y",
427
+ "X",
428
+ "Y",
429
+ ],
430
+ "D": np.arange(16),
431
+ }
432
+
433
+ df = pd.DataFrame(arrays)
434
+ df_pivot = df.pivot_table(
435
+ values=["D"], index=["A"], columns=["B", "C"], aggfunc={"D": "count"}
436
+ )
437
+ widget = PerspectiveWidget(df_pivot)
438
+ assert widget.columns == ["value"]
439
+ assert widget.split_by == ["B", "C"]
440
+ assert widget.group_by == ["A"]
441
+
442
+ def test_pivottable_multi_values(self, superstore):
443
+ pt = pd.pivot_table(
444
+ superstore,
445
+ values=["Discount", "Sales"],
446
+ index=["Country", "Region"],
447
+ aggfunc={"Discount": "count", "Sales": "sum"},
448
+ columns=["State", "Quantity"],
449
+ )
450
+ widget = PerspectiveWidget(pt)
451
+ assert widget.columns == ["Discount", "Sales"]
452
+ assert widget.split_by == ["State", "Quantity"]
453
+ assert widget.group_by == ["Country", "Region"]
@@ -0,0 +1,134 @@
1
+ # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ # ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ # ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ # ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ # ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ # ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ # ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ # ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ # ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ # ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+
14
+ class VirtualSessionModel:
15
+ """
16
+ An interface for implementing a Perspective `VirtualServer`. It operates
17
+ thusly:
18
+
19
+ - A table is selected by name (validated via `get_hosted_tables`).
20
+
21
+ - The UI will ask the model to create a temporary table with the results
22
+ of querying this table with a specific query `config`, a simple struct
23
+ which reflects the UI configurable fields (see `get_features`).
24
+
25
+ - The UI will query slices of the temporary table as it needs them to
26
+ render. This may be a rectangular slice, a whole column or the entire
27
+ set, and it is returned from teh model via a custom push-only
28
+ struct `PerspectiveColumn` for now, though in the future we will support
29
+ e.g. Polars and other arrow-native formats directly.
30
+
31
+ - The UI will delete its own temporary tables via `view_delete` but it is
32
+ ok for them to die intermittently, the UI will recover automatically.
33
+ """
34
+
35
+ def get_features(self):
36
+ """
37
+ [OPTIONAL] Toggle UI features through data model support. For example,
38
+ setting `"group_by": False` would hide the "Group By" UI control, as
39
+ well as prevent this field from appearing in `config` dicts later
40
+ provided to `table_make_view`.
41
+
42
+ This API defaults to just "columns", e.g. a simple flat datagrid in
43
+ which you can just scroll, select and format columns.
44
+
45
+ # Example
46
+
47
+ ```python
48
+ return {
49
+ "group_by": True,
50
+ "split_by": True,
51
+ "sort": True,
52
+ "expressions": True,
53
+ "filter_ops": {
54
+ "integer": ["==", "<"],
55
+ },
56
+ "aggregates": {
57
+ "string": ["count"],
58
+ "float": ["count", "sum"],
59
+ },
60
+ }
61
+ ```
62
+ """
63
+
64
+ pass
65
+
66
+ def get_hosted_tables(self) -> list[str]:
67
+ """
68
+ List of `Table` names available to query from.
69
+ """
70
+
71
+ pass
72
+
73
+ def table_schema(self, table_name):
74
+ """
75
+ Get the _Perspective Schema_ for a `Table`, a mapping of column name to
76
+ Perspective column types, a simplified set of six visually-relevant
77
+ types mapped from DuckDB's much richer type system. Optionally,
78
+ a model may also implement `view_schema` which describes temporary
79
+ tables, but for DuckDB this method is identical.
80
+ """
81
+
82
+ pass
83
+
84
+ def table_size(self, table_name):
85
+ """
86
+ Get a table's row count. Optionally, a model may also implement the
87
+ `view_size` method to get the row count for temporary tables, but for
88
+ DuckDB this method is identical.
89
+ """
90
+
91
+ pass
92
+
93
+ def view_schema(self, view_name, config):
94
+ return self.table_schema(view_name)
95
+
96
+ def view_size(self, view_name):
97
+ return self.table_size(view_name)
98
+
99
+ def table_make_view(self, table_name, view_name, config):
100
+ """
101
+ Create a temporary table `view_name` from the results of querying
102
+ `table_name` with a query configuration `config`.
103
+ """
104
+
105
+ pass
106
+
107
+ def table_validate_expression(self, view_name, expression):
108
+ """
109
+ [OPTIONAL] Given a temporary table `view_name`, validate the type of
110
+ a column expression string `expression`, or raise an error if the
111
+ expression is invalid. This is enabeld by `"expressions"` via
112
+ `get_features` and defaults to allow all expressions.
113
+ """
114
+
115
+ pass
116
+
117
+ def view_delete(self, view_name):
118
+ """
119
+ Delete a temporary table. The UI will do this automatically, and it
120
+ can recover.
121
+ """
122
+
123
+ pass
124
+
125
+ def view_get_data(self, view_name, config, viewport, data):
126
+ """
127
+ Serialize a rectangular slice `viewport` from temporary table
128
+ `view_name`, into the `PerspectiveColumn` serialization API injected
129
+ via `data`. The push-only `PerspectiveColumn` type can handle casting
130
+ Python types as input, but once a type is pushed to a column name it
131
+ must not be changed.
132
+ """
133
+
134
+ pass