perspective-python 4.5.2__cp311-abi3-pyemscripten_2025_0_wasm32.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 +403 -0
  2. perspective/extension/finos-perspective-nbextension.json +5 -0
  3. perspective/handlers/__init__.py +11 -0
  4. perspective/handlers/aiohttp.py +69 -0
  5. perspective/handlers/starlette.py +63 -0
  6. perspective/handlers/tornado.py +192 -0
  7. perspective/perspective.abi3.so +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 +263 -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_join.py +115 -0
  33. perspective/tests/table/test_leaks.py +105 -0
  34. perspective/tests/table/test_ports.py +178 -0
  35. perspective/tests/table/test_remove.py +102 -0
  36. perspective/tests/table/test_table.py +641 -0
  37. perspective/tests/table/test_table_arrow.py +500 -0
  38. perspective/tests/table/test_table_datetime.py +2409 -0
  39. perspective/tests/table/test_table_infer.py +201 -0
  40. perspective/tests/table/test_table_limit.py +45 -0
  41. perspective/tests/table/test_table_numpy.py +1022 -0
  42. perspective/tests/table/test_table_page_to_disk.py +304 -0
  43. perspective/tests/table/test_table_pandas.py +1018 -0
  44. perspective/tests/table/test_table_polars.py +251 -0
  45. perspective/tests/table/test_table_view_table.py +130 -0
  46. perspective/tests/table/test_to_arrow.py +417 -0
  47. perspective/tests/table/test_to_arrow_lz4.py +32 -0
  48. perspective/tests/table/test_to_format.py +1024 -0
  49. perspective/tests/table/test_to_polars.py +26 -0
  50. perspective/tests/table/test_update.py +536 -0
  51. perspective/tests/table/test_update_arrow.py +980 -0
  52. perspective/tests/table/test_update_pandas.py +211 -0
  53. perspective/tests/table/test_view.py +2444 -0
  54. perspective/tests/table/test_view_expression.py +1940 -0
  55. perspective/tests/test_dependencies.py +53 -0
  56. perspective/tests/viewer/__init__.py +11 -0
  57. perspective/tests/viewer/test_viewer.py +246 -0
  58. perspective/tests/virtual_servers/__init__.py +12 -0
  59. perspective/tests/virtual_servers/test_coerce_types.py +209 -0
  60. perspective/tests/virtual_servers/test_duckdb.py +908 -0
  61. perspective/tests/virtual_servers/test_polars.py +1032 -0
  62. perspective/tests/widget/__init__.py +11 -0
  63. perspective/tests/widget/test_widget.py +278 -0
  64. perspective/tests/widget/test_widget_pandas.py +453 -0
  65. perspective/virtual_servers/__init__.py +142 -0
  66. perspective/virtual_servers/clickhouse.py +240 -0
  67. perspective/virtual_servers/duckdb.py +236 -0
  68. perspective/virtual_servers/polars.py +710 -0
  69. perspective/widget/__init__.py +349 -0
  70. perspective/widget/viewer/__init__.py +15 -0
  71. perspective/widget/viewer/validate.py +22 -0
  72. perspective/widget/viewer/viewer.py +343 -0
  73. perspective/widget/viewer/viewer_traitlets.py +101 -0
  74. perspective_python-4.5.2.dist-info/METADATA +29 -0
  75. perspective_python-4.5.2.dist-info/RECORD +79 -0
  76. perspective_python-4.5.2.dist-info/WHEEL +4 -0
  77. perspective_python-4.5.2.dist-info/licenses/LICENSE.md +193 -0
  78. perspective_python-4.5.2.dist-info/licenses/LICENSE_THIRDPARTY_cargo.yml +19255 -0
  79. perspective_python-4.5.2.dist-info/sboms/perspective-python.cyclonedx.json +5546 -0
@@ -0,0 +1,11 @@
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
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -0,0 +1,278 @@
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
+ from functools import partial
14
+ from types import MethodType
15
+ import numpy as np
16
+ from perspective.widget import PerspectiveWidget
17
+ from pytest import raises
18
+ import pytest
19
+ import perspective as psp
20
+
21
+ client = psp.Server().new_local_client()
22
+ Table = client.table
23
+
24
+ pytest.skip(allow_module_level=True)
25
+
26
+
27
+ def mock_post(self, msg, msg_id=None, assert_msg=None):
28
+ """Mock the widget's `post()` method so we can introspect the contents."""
29
+ assert msg == assert_msg
30
+
31
+
32
+ class TestWidget:
33
+ def test_widget(self):
34
+ data = {"a": np.arange(0, 50)}
35
+ widget = PerspectiveWidget(data, plugin="X Bar")
36
+ assert widget.plugin == "X Bar"
37
+ load_msg = widget._make_load_message()
38
+ assert load_msg.to_columns() == {
39
+ "id": -2,
40
+ "type": "table",
41
+ "data": {"table_name": widget.table_name, "options": {}},
42
+ }
43
+
44
+ def test_widget_indexed(self):
45
+ data = {"a": np.arange(0, 50)}
46
+ widget = PerspectiveWidget(data, plugin="X Bar", index="a")
47
+ assert widget.plugin == "X Bar"
48
+ load_msg = widget._make_load_message()
49
+ assert load_msg.to_columns() == {
50
+ "id": -2,
51
+ "type": "table",
52
+ "data": {"table_name": widget.table_name, "options": {"index": "a"}},
53
+ }
54
+
55
+ def test_widget_no_data(self):
56
+ widget = PerspectiveWidget(None, plugin="X Bar", group_by=["a"])
57
+ assert widget.plugin == "X Bar"
58
+ assert widget.group_by == ["a"]
59
+
60
+ def test_widget_schema(self):
61
+ schema = {
62
+ "a": "integer",
63
+ "b": "float",
64
+ "c": "boolean",
65
+ "d": "date",
66
+ "e": "datetime",
67
+ "f": "string",
68
+ }
69
+ widget = PerspectiveWidget(schema)
70
+ assert widget.table.schema() == schema
71
+
72
+ def test_widget_schema_with_index(self):
73
+ widget = PerspectiveWidget({"a": "integer"}, index="a")
74
+ assert widget.table.get_index() == "a"
75
+
76
+ def test_widget_schema_with_limit(self):
77
+ widget = PerspectiveWidget({"a": "integer"}, limit=5)
78
+ assert widget.table.get_limit() == 5
79
+
80
+ def test_widget_no_data_with_index(self):
81
+ # should fail
82
+ with raises(TypeError):
83
+ PerspectiveWidget(None, index="a")
84
+
85
+ def test_widget_no_data_with_limit(self):
86
+ # should fail
87
+ with raises(TypeError):
88
+ PerspectiveWidget(None, limit=5)
89
+
90
+ def test_widget_eventual_data(self):
91
+ table = Table({"a": np.arange(0, 50)})
92
+ widget = PerspectiveWidget(None, plugin="X Bar")
93
+ assert widget.plugin == "X Bar"
94
+
95
+ with raises(TypeError):
96
+ widget._make_load_message()
97
+
98
+ widget.load(table)
99
+
100
+ load_msg = widget._make_load_message()
101
+ assert load_msg.to_columns() == {
102
+ "id": -2,
103
+ "type": "table",
104
+ "data": {"table_name": widget.table_name, "options": {}},
105
+ }
106
+
107
+ def test_widget_eventual_data_server(self):
108
+ widget = PerspectiveWidget(None, plugin="X Bar", server=True)
109
+ assert widget.plugin == "X Bar"
110
+ widget.load({"a": np.arange(0, 50)}, index="a")
111
+ load_msg = widget._make_load_message()
112
+ assert load_msg.to_columns() == {
113
+ "id": -2,
114
+ "type": "table",
115
+ "data": {
116
+ "table_name": widget.table_name,
117
+ },
118
+ }
119
+
120
+ def test_widget_eventual_data_indexed(self):
121
+ widget = PerspectiveWidget(None, plugin="X Bar")
122
+ assert widget.plugin == "X Bar"
123
+ widget.load({"a": np.arange(0, 50)}, index="a")
124
+ load_msg = widget._make_load_message()
125
+ assert load_msg.to_columns() == {
126
+ "id": -2,
127
+ "type": "table",
128
+ "data": {"table_name": widget.table_name, "options": {"index": "a"}},
129
+ }
130
+
131
+ def test_widget_eventual_table_indexed(self):
132
+ table = Table({"a": np.arange(0, 50)}, index="a")
133
+ widget = PerspectiveWidget(None, plugin="X Bar")
134
+ assert widget.plugin == "X Bar"
135
+ widget.load(table)
136
+ load_msg = widget._make_load_message()
137
+ assert load_msg.to_columns() == {
138
+ "id": -2,
139
+ "type": "table",
140
+ "data": {"table_name": widget.table_name, "options": {"index": "a"}},
141
+ }
142
+
143
+ def test_widget_load_table(self):
144
+ table = Table({"a": np.arange(0, 50)})
145
+ widget = PerspectiveWidget(table, plugin="X Bar")
146
+ assert widget.plugin == "X Bar"
147
+ load_msg = widget._make_load_message()
148
+ assert load_msg.to_columns() == {
149
+ "id": -2,
150
+ "type": "table",
151
+ "data": {"table_name": widget.table_name, "options": {}},
152
+ }
153
+
154
+ def test_widget_load_table_indexed(self):
155
+ table = Table({"a": np.arange(0, 50)}, index="a")
156
+ widget = PerspectiveWidget(table, plugin="X Bar")
157
+ assert widget.plugin == "X Bar"
158
+ load_msg = widget._make_load_message()
159
+ assert load_msg.to_columns() == {
160
+ "id": -2,
161
+ "type": "table",
162
+ "data": {"table_name": widget.table_name, "options": {"index": "a"}},
163
+ }
164
+
165
+ def test_widget_load_table_ignore_limit(self):
166
+ table = Table({"a": np.arange(0, 50)})
167
+ widget = PerspectiveWidget(table, limit=1)
168
+ assert widget.table.size() == 50
169
+
170
+ def test_widget_pass_index(self):
171
+ data = {"a": [1, 2, 3, 1]}
172
+ widget = PerspectiveWidget(data, index="a")
173
+ assert widget.table.size() == 3
174
+
175
+ def test_widget_pass_limit(self):
176
+ data = {"a": np.arange(0, 50)}
177
+ widget = PerspectiveWidget(data, limit=1)
178
+ assert widget.table.size() == 1
179
+
180
+ def test_widget_pass_options_invalid(self):
181
+ data = {"a": np.arange(0, 50)}
182
+ with raises(TypeError):
183
+ PerspectiveWidget(data, index="index", limit=1)
184
+
185
+ # server mode
186
+ def test_widget_load_table_server(self):
187
+ table = Table({"a": np.arange(0, 50)})
188
+ widget = PerspectiveWidget(table, server=True)
189
+ load_msg = widget._make_load_message()
190
+ assert load_msg.to_columns() == {
191
+ "id": -2,
192
+ "type": "table",
193
+ "data": {"table_name": widget.table_name},
194
+ }
195
+
196
+ def test_widget_no_data_with_server(self):
197
+ # should fail
198
+ widget = PerspectiveWidget(None, server=True)
199
+ with raises(TypeError):
200
+ widget._make_load_message()
201
+
202
+ def test_widget_eventual_data_with_server(self):
203
+ # should fail
204
+ widget = PerspectiveWidget(None, server=True)
205
+
206
+ with raises(TypeError):
207
+ widget._make_load_message()
208
+
209
+ # then succeed
210
+ widget.load(Table({"a": np.arange(0, 50)}))
211
+ load_msg = widget._make_load_message()
212
+ assert load_msg.to_columns() == {
213
+ "id": -2,
214
+ "type": "table",
215
+ "data": {"table_name": widget.table_name},
216
+ }
217
+
218
+ # clear
219
+
220
+ def test_widget_clear(self):
221
+ data = {"a": np.arange(0, 50)}
222
+ widget = PerspectiveWidget(data)
223
+ widget.clear()
224
+ assert widget.table.size() == 0
225
+
226
+ def test_widget_clear_server(self):
227
+ data = {"a": np.arange(0, 50)}
228
+ widget = PerspectiveWidget(data, server=True)
229
+ widget.clear()
230
+ assert widget.table.size() == 0
231
+
232
+ # replace
233
+
234
+ def test_widget_replace(self):
235
+ data = {"a": np.arange(0, 50)}
236
+ widget = PerspectiveWidget(data)
237
+ widget.replace({"a": [1]})
238
+ assert widget.table.size() == 1
239
+
240
+ def test_widget_replace_server(self):
241
+ data = {"a": np.arange(0, 50)}
242
+ widget = PerspectiveWidget(data, server=True)
243
+ widget.replace({"a": [1]})
244
+ assert widget.table.size() == 1
245
+
246
+ # delete
247
+
248
+ def test_widget_delete(self):
249
+ data = {"a": np.arange(0, 50)}
250
+ widget = PerspectiveWidget(data)
251
+ mocked_post = partial(mock_post, assert_msg={"cmd": "delete"})
252
+ widget.post = MethodType(mocked_post, widget)
253
+ widget.delete()
254
+ assert widget.table is None
255
+
256
+ def test_widget_delete_with_view(self):
257
+ data = {"a": np.arange(0, 50)}
258
+ widget = PerspectiveWidget(data)
259
+
260
+ # create a view on the manager
261
+ table_name, table = list(widget.manager._tables.items())[0]
262
+ make_view_message = {
263
+ "id": 1,
264
+ "table_name": table_name,
265
+ "view_name": "view1",
266
+ "cmd": "view",
267
+ "config": {"group_by": ["a"]},
268
+ }
269
+ widget.manager._process(make_view_message, lambda x: True)
270
+
271
+ assert len(widget.manager._views) == 1
272
+
273
+ mocked_post = partial(mock_post, assert_msg={"cmd": "delete"})
274
+
275
+ widget.post = MethodType(mocked_post, widget)
276
+ widget.delete()
277
+
278
+ assert widget.table is None