perspective-python 5.6.0__cp311-abi3-pyemscripten_2026_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 (90) 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 +39 -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/column_paths.py +33 -0
  13. perspective/tests/conftest.py +276 -0
  14. perspective/tests/core/__init__.py +11 -0
  15. perspective/tests/core/test_async.py +351 -0
  16. perspective/tests/multi_threaded/__init__.py +11 -0
  17. perspective/tests/multi_threaded/test_derived_threaded.py +103 -0
  18. perspective/tests/multi_threaded/test_multi_threaded.py +293 -0
  19. perspective/tests/server/__init__.py +11 -0
  20. perspective/tests/server/test_server.py +1016 -0
  21. perspective/tests/server/test_session.py +110 -0
  22. perspective/tests/table/__init__.py +11 -0
  23. perspective/tests/table/arrow/date32.arrow +0 -0
  24. perspective/tests/table/arrow/date64.arrow +0 -0
  25. perspective/tests/table/arrow/dict.arrow +0 -0
  26. perspective/tests/table/arrow/dict_update.arrow +0 -0
  27. perspective/tests/table/arrow/int_float_str.arrow +0 -0
  28. perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
  29. perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
  30. perspective/tests/table/object_sequence.py +402 -0
  31. perspective/tests/table/test_column_paths.py +88 -0
  32. perspective/tests/table/test_delete.py +124 -0
  33. perspective/tests/table/test_derived.py +145 -0
  34. perspective/tests/table/test_exception.py +73 -0
  35. perspective/tests/table/test_join.py +115 -0
  36. perspective/tests/table/test_leaks.py +105 -0
  37. perspective/tests/table/test_on_remove.py +140 -0
  38. perspective/tests/table/test_ports.py +178 -0
  39. perspective/tests/table/test_remove.py +102 -0
  40. perspective/tests/table/test_table.py +641 -0
  41. perspective/tests/table/test_table_arrow.py +500 -0
  42. perspective/tests/table/test_table_arrow_nested.py +535 -0
  43. perspective/tests/table/test_table_datetime.py +2544 -0
  44. perspective/tests/table/test_table_infer.py +201 -0
  45. perspective/tests/table/test_table_limit.py +45 -0
  46. perspective/tests/table/test_table_numpy.py +1022 -0
  47. perspective/tests/table/test_table_page_to_disk.py +304 -0
  48. perspective/tests/table/test_table_pandas.py +1018 -0
  49. perspective/tests/table/test_table_polars.py +251 -0
  50. perspective/tests/table/test_table_view_table.py +130 -0
  51. perspective/tests/table/test_to_arrow.py +416 -0
  52. perspective/tests/table/test_to_arrow_lz4.py +32 -0
  53. perspective/tests/table/test_to_arrow_zstd.py +41 -0
  54. perspective/tests/table/test_to_format.py +1030 -0
  55. perspective/tests/table/test_to_polars.py +26 -0
  56. perspective/tests/table/test_update.py +561 -0
  57. perspective/tests/table/test_update_arrow.py +980 -0
  58. perspective/tests/table/test_update_pandas.py +211 -0
  59. perspective/tests/table/test_view.py +2503 -0
  60. perspective/tests/table/test_view_expression.py +2256 -0
  61. perspective/tests/test_dependencies.py +53 -0
  62. perspective/tests/viewer/__init__.py +11 -0
  63. perspective/tests/viewer/test_viewer.py +246 -0
  64. perspective/tests/virtual_servers/__init__.py +12 -0
  65. perspective/tests/virtual_servers/test_coerce_types.py +464 -0
  66. perspective/tests/virtual_servers/test_duckdb.py +1280 -0
  67. perspective/tests/virtual_servers/test_polars.py +1075 -0
  68. perspective/tests/virtual_servers/test_postgres.py +964 -0
  69. perspective/tests/widget/__init__.py +11 -0
  70. perspective/tests/widget/test_anywidget_smoke.py +109 -0
  71. perspective/tests/widget/test_widget.py +278 -0
  72. perspective/tests/widget/test_widget_html_export.py +113 -0
  73. perspective/tests/widget/test_widget_pandas.py +453 -0
  74. perspective/virtual_servers/__init__.py +206 -0
  75. perspective/virtual_servers/clickhouse.py +296 -0
  76. perspective/virtual_servers/duckdb.py +367 -0
  77. perspective/virtual_servers/polars.py +750 -0
  78. perspective/virtual_servers/postgres.py +448 -0
  79. perspective/widget/__init__.py +351 -0
  80. perspective/widget/static/perspective-anywidget.css +1 -0
  81. perspective/widget/static/perspective-anywidget.js +31 -0
  82. perspective/widget/viewer/__init__.py +15 -0
  83. perspective/widget/viewer/viewer.py +355 -0
  84. perspective/widget/viewer/viewer_traitlets.py +98 -0
  85. perspective_python-5.6.0.dist-info/METADATA +127 -0
  86. perspective_python-5.6.0.dist-info/RECORD +90 -0
  87. perspective_python-5.6.0.dist-info/WHEEL +4 -0
  88. perspective_python-5.6.0.dist-info/licenses/LICENSE.md +193 -0
  89. perspective_python-5.6.0.dist-info/licenses/LICENSE_THIRDPARTY_cargo.yml +16075 -0
  90. perspective_python-5.6.0.dist-info/sboms/perspective-python.cyclonedx.json +5334 -0
@@ -0,0 +1,15 @@
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 .viewer import PerspectiveViewer
14
+
15
+ __all__ = ["PerspectiveViewer"]
@@ -0,0 +1,355 @@
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 random import random
14
+ from .viewer_traitlets import PerspectiveTraitlets
15
+ import perspective
16
+
17
+ # from .. import Server
18
+
19
+ # ─ │ ┌ ┬ ┐
20
+ # ┄ ┆ ├ ┼ ┤ ╲ ╱
21
+ # ┈ ┊ └ ┴ ┘
22
+ # ━ ┃ ┏ ┳ ┓ ┏ ┯ ┓ ┏ ┳ ┓ ┏ ┯ ┓
23
+ # ┅ ┇ ┣ ╋ ┫ ┣ ┿ ┫ ┠ ╂ ┨ ┠ ┼ ┨
24
+ # ┉ ┋ ┗ ┻ ┛ ┗ ┷ ┛ ┗ ┻ ┛ ┗ ┷ ┛
25
+
26
+ # global_server = PySyncServer()
27
+
28
+
29
+ class PerspectiveViewer(PerspectiveTraitlets, object):
30
+ """PerspectiveViewer wraps the `perspective.Table` API and exposes an API
31
+ around creating views, loading data, and updating data.
32
+ """
33
+
34
+ # Viewer attributes that should be saved in `save()` and restored using
35
+ # `restore()`. Symmetric to `PERSISTENT_ATTRIBUTES` in `perspective-viewer`.
36
+ PERSISTENT_ATTRIBUTES = (
37
+ "group_by",
38
+ "split_by",
39
+ "filter",
40
+ "sort",
41
+ "aggregates",
42
+ "columns",
43
+ "expressions",
44
+ "plugin",
45
+ "plugin_config",
46
+ "theme",
47
+ "settings",
48
+ "title",
49
+ "version",
50
+ )
51
+
52
+ def __init__(
53
+ self,
54
+ plugin="Datagrid",
55
+ columns=None,
56
+ columns_config=None,
57
+ group_by=None,
58
+ split_by=None,
59
+ group_rollup_mode=None,
60
+ split_rollup_mode=None,
61
+ aggregates=None,
62
+ sort=None,
63
+ filter=None,
64
+ expressions=None,
65
+ plugin_config=None,
66
+ settings=True,
67
+ theme=None,
68
+ title=None,
69
+ # ignored, here for restore compatibility
70
+ version=None,
71
+ ):
72
+ """Initialize an instance of `PerspectiveViewer` with the given viewer
73
+ configuration. Do not pass a `Table` or data into the constructor -
74
+ use the :func:`load()` method to provide the viewer with data.
75
+
76
+ Keyword Arguments:
77
+ columns (:obj:`list` of :obj:`str`): A list of column names to be
78
+ visible to the user.
79
+ columns_config (:obj:`dict`): Per-column configuration (styling,
80
+ formatting, etc.), keyed by column name.
81
+ group_by (:obj:`list` of :obj:`str`): A list of column names to
82
+ use as group by.
83
+ split_by (:obj:`list` of :obj:`str`): A list of column names
84
+ to use as split by.
85
+ group_rollup_mode (:obj:`str`): How group-by rollups render; one
86
+ of ``rollup``, ``flat``, or ``total``.
87
+ split_rollup_mode (:obj:`str`): Whether split-by emits subtotal
88
+ and grand-total column groups; one of ``flat`` or ``rollup``.
89
+ aggregates (:obj:`dict` of :obj:`str` to :obj:`str`): A dictionary
90
+ of column names to aggregate types, which specify aggregates
91
+ for individual columns.
92
+ sort (:obj:`list` of :obj:`list` of :obj:`str`): A list of lists,
93
+ each list containing a column name and a sort direction
94
+ (``asc``, ``desc``, ``asc abs``, ``desc abs``, ``col asc``,
95
+ ``col desc``, ``col asc abs``, ``col desc abs``).
96
+ filter (:obj:`list` of :obj:`list` of :obj:`str`): A list of lists,
97
+ each list containing a column name, a filter comparator, and a
98
+ value to filter by.
99
+ expressions (:obj:`list` of :obj:`str`): A list of string
100
+ expressions which are applied to the view.
101
+ plugin (:obj:`str`/:obj:`perspective.Plugin`): Which plugin to
102
+ select by default.
103
+ plugin_config (:obj:`dict`): A configuration for the plugin, i.e.
104
+ the datagrid plugin or a chart plugin.
105
+ settings(:obj:`bool`): Whether the perspective query settings
106
+ panel should be open.
107
+ theme (:obj:`str`): The color theme to use.
108
+ version (:obj:`str`): The version this configuration is restored from.
109
+ This should only be used when restoring a configuration,
110
+ and should not be set manually.
111
+
112
+ Examples:
113
+ >>> viewer = PerspectiveViewer(
114
+ ... aggregates={"a": "avg"},
115
+ ... group_by=["a"],
116
+ ... sort=[["b", "desc"]],
117
+ ... filter=[["a", ">", 1]],
118
+ ... expressions=["\"a\" + 100"]
119
+ ... )
120
+ """
121
+
122
+ # The Table under management by this viewer and its
123
+ # attached PerspectiveManager
124
+ self._table = None
125
+ self._client = None
126
+
127
+ # Viewer configuration
128
+ self.plugin = plugin # validate_plugin(plugin)
129
+ self.columns = columns or [] # validate_columns(columns) or []
130
+ self.columns_config = columns_config or {}
131
+ self.group_by = group_by or [] # validate_group_by(group_by) or []
132
+ self.split_by = split_by or [] # validate_split_by(split_by) or []
133
+ self.group_rollup_mode = group_rollup_mode or "rollup"
134
+ self.split_rollup_mode = split_rollup_mode or "flat"
135
+ self.aggregates = aggregates or {} # validate_aggregates(aggregates) or {}
136
+ self.sort = sort or [] # validate_sort(sort) or []
137
+ self.filter = filter or [] # validate_filter(filter) or []
138
+ self.expressions = expressions or {} # validate_expressions(expressions) or {}
139
+ self.plugin_config = (
140
+ plugin_config or {}
141
+ ) # validate_plugin_config(plugin_config) or {}
142
+ self.settings = settings
143
+ self.theme = theme
144
+ self.title = title
145
+
146
+ def new_proxy_session(self, cb):
147
+ return perspective.ProxySession(self._client, cb)
148
+
149
+ @property
150
+ def client(self):
151
+ """Returns the ``perspective.Client`` or ``perspective.AsyncClient`` under management by the viewer."""
152
+ return self._client
153
+
154
+ @property
155
+ def table(self):
156
+ """Returns the ``perspective.Table`` or ``perspective.AsyncTable`` under management by the viewer."""
157
+ return self._table
158
+
159
+ def is_async(self):
160
+ """Returns whether this widget has an async interface or synchronous"""
161
+ return isinstance(self._table, perspective.perspective.AsyncTable)
162
+
163
+ def load(self, data, **options):
164
+ """Given a ``perspective.Table``, a ``perspective.AsyncTable``,
165
+ or data that can be handled by ``perspective.Table``, pass it to the
166
+ viewer. Like `__init__`, load accepts a `perspective.Table`, a dataset,
167
+ or a schema.
168
+
169
+ ``load()`` resets the state of the viewer: if a ``perspective.Table``
170
+ has already been loaded, ``**options`` is ignored as the options
171
+ already set on the ``Table`` take precedence.
172
+
173
+ If data is passed in, a ``perspective.Table`` is automatically created
174
+ by this method, and the options passed to ``**config`` are extended to
175
+ the new Table. If the widget already has a dataset, and the new data
176
+ has different columns to the old one, then the widget state (pivots,
177
+ sort, etc.) is cleared to prevent applying settings on columns that
178
+ don't exist.
179
+
180
+ When a ``perspective.AsyncTable`` is loaded, the widget's interface
181
+ becomes async. Methods which operate on the underlying Perspective
182
+ view, inclusive of the ``load()`` call itself, return coroutine values
183
+ which must be awaited.
184
+
185
+ Loading a ``perspective.Table`` or plain data will make the interface
186
+ synchronous again.
187
+
188
+ Args:
189
+ data (:obj:`Table`|:obj:`AsyncTable`|:obj:`dict`|:obj:`list`|:obj:`pandas.DataFrame`|:obj:`bytes`|:obj:`str`): a
190
+ `perspective.Table` instance, a `perspective.AsyncTable`
191
+ instance, or a dataset to be loaded in the viewer.
192
+
193
+ Keyword Arguments:
194
+ name (:obj:`str`): An optional name to reference the table by so it can
195
+ be accessed from the front-end. If not provided, a name will
196
+ be generated.
197
+ index (:obj:`str`): A column name to be used as the primary key.
198
+ Ignored if a ``Table`` or ``AsyncTable`` is supplied.
199
+ limit (:obj:`int`): A upper limit on the number of rows in the Table.
200
+ Cannot be set at the same time as `index`. Ignored if a
201
+ ``Table`` or ``AsyncTable`` is supplied.
202
+
203
+ Returns:
204
+ coro (:obj:`coroutine`): when `AsyncTable` is passed, the `load()` call must be awaited
205
+ """
206
+ name = options.pop("name", str(random()))
207
+
208
+ # Reset the viewer when `load()` is called multiple times.
209
+ if self.table is not None:
210
+ self.reset()
211
+
212
+ if isinstance(data, perspective.perspective.AsyncTable):
213
+ self._table = data
214
+ async def load_table():
215
+ self._client = await self.table.get_client()
216
+ self.table_name = self.table.get_name()
217
+ # If the user does not set columns to show, synchronize viewer state
218
+ # with dataset.
219
+ if len(self.columns) == 0:
220
+ self.columns = await self.table.columns()
221
+
222
+ return load_table()
223
+ elif isinstance(data, perspective.perspective.Table):
224
+ self._table = data
225
+ self._client = data.get_client()
226
+ elif isinstance(data, perspective.perspective.View) or isinstance(data, perspective.perspective.AsyncView):
227
+ raise TypeError(
228
+ "Views cannot be loaded directly, load a table or raw data instead"
229
+ )
230
+ else:
231
+ self._table = perspective.table(data, name=name, **options)
232
+ self._client = perspective.GLOBAL_CLIENT
233
+
234
+ # If the user does not set columns to show, synchronize viewer state
235
+ # with dataset.
236
+ if len(self.columns) == 0:
237
+ self.columns = self.table.columns()
238
+
239
+ self.table_name = self.table.get_name()
240
+
241
+ def update(self, data):
242
+ """Update the table under management by the viewer with new data.
243
+ This function follows the semantics of `Table.update()`, and will be
244
+ affected by whether an index is set on the underlying table.
245
+
246
+ When this widget has loaded an ``AsyncTable``, returns a coroutine
247
+ which must be awaited.
248
+
249
+ Args:
250
+ data (:obj:`dict`|:obj:`list`|:obj:`pandas.DataFrame`): the
251
+ update data for the table.
252
+
253
+ Returns:
254
+ coro (:obj:`coroutine`): when async, must be awaited
255
+ """
256
+ return self.table.update(data)
257
+
258
+ def clear(self):
259
+ """Clears the rows of this viewer's ``Table``."""
260
+ if self.table is not None:
261
+ return self.table.clear()
262
+
263
+ def replace(self, data):
264
+ """Replaces the rows of this viewer's `Table` with new data.
265
+
266
+ Args:
267
+ data (:obj:`dict`|:obj:`list`|:obj:`pandas.DataFrame`): new data
268
+ to set into the table - must conform to the table's schema.
269
+
270
+ Returns:
271
+ coro (:obj:`coroutine`): when async, must be awaited
272
+ """
273
+ if self.table is not None:
274
+ return self.table.replace(data)
275
+
276
+ def save(self):
277
+ """Get the viewer's attributes as a dictionary, symmetric with `restore`
278
+ so that a viewer's configuration can be reproduced."""
279
+ return {
280
+ attr: getattr(self, attr)
281
+ for attr in PerspectiveViewer.PERSISTENT_ATTRIBUTES
282
+ }
283
+
284
+ def restore(self, **kwargs):
285
+ """Restore a given set of attributes, passed as kwargs
286
+ (e.g. dictionary). Symmetric with `save` so that a given viewer's
287
+ configuration can be reproduced."""
288
+ for k, v in kwargs.items():
289
+ if k in PerspectiveViewer.PERSISTENT_ATTRIBUTES:
290
+ setattr(self, k, v)
291
+
292
+ def to_kwargs(self):
293
+ """Get the viewer's attributes as a list of kwargs, which can be passed to
294
+ the viewer constructor"""
295
+ attrs = self.save()
296
+ defaults = {
297
+ "columns": [],
298
+ "group_by": [],
299
+ "split_by": [],
300
+ "aggregates": {},
301
+ "sort": [],
302
+ "filter": [],
303
+ "expressions": {},
304
+ "plugin_config": {},
305
+ "version": "2.10.0",
306
+ }
307
+ kwargs = {}
308
+ for key, default in defaults.items():
309
+ if attrs.get(key) != default:
310
+ kwargs[key] = attrs[key]
311
+ return ", ".join(
312
+ ["{}={}".format(attr, repr(val)) for attr, val in kwargs.items()]
313
+ )
314
+
315
+ def reset(self):
316
+ """Resets the viewer's attributes and state, but does not delete or
317
+ modify the underlying `Table`.
318
+
319
+ Example:
320
+ widget = PerspectiveWidget(data, group_by=["date"], plugin=Plugin.XBAR)
321
+ widget.reset()
322
+ widget.plugin #
323
+ """
324
+ self.group_by = []
325
+ self.split_by = []
326
+ self.filter = []
327
+ self.sort = []
328
+ self.expressions = {}
329
+ self.aggregates = {}
330
+ self.columns = []
331
+ self.plugin = "Datagrid"
332
+ self.plugin_config = {}
333
+
334
+ def delete(self, delete_table=True):
335
+ """Delete the Viewer's data and clears its internal state. If
336
+ ``delete_table`` is True, the underlying `perspective.Table` and the
337
+ internal `View` object will be deleted.
338
+
339
+ Args:
340
+ delete_table (:obj:`bool`) : whether the underlying `Table` will be
341
+ deleted. Defaults to True.
342
+
343
+ Returns:
344
+ coro (:obj:`coroutine`): when async and `delete_table` is `True`,
345
+ must be awaited
346
+ """
347
+ ret = None
348
+ if delete_table:
349
+ # Delete table
350
+ ret = self.table.delete()
351
+ self.table_name = None
352
+ self._table = None
353
+
354
+ self.reset()
355
+ return ret
@@ -0,0 +1,98 @@
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 traitlets import HasTraits, Unicode, List, Bool, Dict, Enum
14
+
15
+ import importlib.metadata
16
+
17
+ __version__ = importlib.metadata.version("perspective-python")
18
+
19
+
20
+ class PerspectiveTraitlets(HasTraits):
21
+ """Define the traitlet interface with `PerspectiveJupyterWidget` on the
22
+ front end. Attributes which are set here are synchronized between the
23
+ front-end and back-end.
24
+
25
+ Examples:
26
+ >>> widget = perspective.PerspectiveWidget(
27
+ ... data, group_by=["a", "b", "c"])
28
+ PerspectiveWidget(group_by=["a", "b", "c"])
29
+ >>> widget.split_by=["b"]
30
+ >>> widget
31
+ PerspectiveWidget(group_by=["a", "b", "c"], split_by=["b"])
32
+ """
33
+
34
+ # `perspective-viewer` options
35
+ plugin = Unicode("Datagrid").tag(sync=True)
36
+ columns = List(default_value=[]).tag(sync=True)
37
+ columns_config = Dict(default_value={}).tag(sync=True)
38
+ group_by = List(trait=Unicode(), default_value=[]).tag(sync=True, o=True)
39
+ split_by = List(trait=Unicode(), default_value=[]).tag(sync=True)
40
+ group_rollup_mode = Enum(("rollup", "flat", "total"), default_value="rollup").tag(
41
+ sync=True
42
+ )
43
+ split_rollup_mode = Enum(("flat", "rollup"), default_value="flat").tag(sync=True)
44
+ aggregates = Dict(default_value={}).tag(sync=True)
45
+ sort = List(default_value=[]).tag(sync=True)
46
+ filter = List(default_value=[]).tag(sync=True)
47
+ expressions = Dict(default_value=[]).tag(sync=True)
48
+ plugin_config = Dict(default_value={}).tag(sync=True)
49
+ settings = Bool(True).tag(sync=True)
50
+ theme = Unicode("Pro Light", allow_none=True).tag(sync=True)
51
+
52
+ # used to tell the frontend which table to connect to
53
+ table_name = Unicode(None, allow_none=True).tag(sync=True)
54
+
55
+ server = Bool(False).tag(sync=True)
56
+ binding_mode = Enum(("server", "client-server")).tag(default="server", sync=True)
57
+ title = Unicode(None, allow_none=True).tag(sync=True)
58
+ version = Unicode(__version__).tag(sync=True)
59
+
60
+ # @validate("plugin")
61
+ # def _validate_plugin(self, proposal):
62
+ # return validate_plugin(proposal.value)
63
+
64
+ # @validate("columns")
65
+ # def _validate_columns(self, proposal):
66
+ # return validate_columns(proposal.value)
67
+
68
+ # @validate("group_by")
69
+ # def _validate_group_by(self, proposal):
70
+ # return validate_group_by(proposal.value)
71
+
72
+ # @validate("split_by")
73
+ # def _validate_split_by(self, proposal):
74
+ # return validate_split_by(proposal.value)
75
+
76
+ # @validate("aggregates")
77
+ # def _validate_aggregates(self, proposal):
78
+ # return validate_aggregates(proposal.value)
79
+
80
+ # @validate("sort")
81
+ # def _validate_sort(self, proposal):
82
+ # return validate_sort(proposal.value)
83
+
84
+ # @validate("filter")
85
+ # def _validate_filter(self, proposal):
86
+ # return validate_filter(proposal.value)
87
+
88
+ # @validate("expressions")
89
+ # def _validate_expressions(self, proposal):
90
+ # return validate_expressions(proposal.value)
91
+
92
+ # @validate("plugin_config")
93
+ # def _validate_plugin_config(self, proposal):
94
+ # return validate_plugin_config(proposal.value)
95
+
96
+ # @validate("title")
97
+ # def _validate_title(self, proposal):
98
+ # return validate_title(proposal.value)
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: perspective-python
3
+ Version: 5.6.0
4
+ Classifier: Development Status :: 5 - Production/Stable
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Intended Audience :: Financial and Insurance Industry
7
+ Classifier: Intended Audience :: Science/Research
8
+ Classifier: Topic :: Scientific/Engineering :: Visualization
9
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
10
+ Classifier: Topic :: Office/Business :: Financial
11
+ Classifier: Programming Language :: Rust
12
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Framework :: Jupyter
21
+ Classifier: Framework :: Jupyter :: JupyterLab
22
+ Classifier: Framework :: Jupyter :: JupyterLab :: 3
23
+ Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
24
+ Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
25
+ Requires-Dist: aiohttp>=2,<4 ; extra == 'aiohttp'
26
+ Requires-Dist: anywidget>=0.9 ; extra == 'jupyter'
27
+ Requires-Dist: starlette<1 ; extra == 'starlette'
28
+ Requires-Dist: tornado>=5,<7 ; extra == 'tornado'
29
+ Provides-Extra: aiohttp
30
+ Provides-Extra: jupyter
31
+ Provides-Extra: starlette
32
+ Provides-Extra: tornado
33
+ License-File: LICENSE.md
34
+ License-File: LICENSE_THIRDPARTY_cargo.yml
35
+ Summary: Interactive data grid, pivot table and charts for large, real-time and streaming datasets, with a Jupyter widget and WebSocket server.
36
+ Keywords: datagrid,pivot,visualization,analytics,dashboard,jupyter,anywidget,streaming,real-time,arrow,pandas,polars,duckdb,webassembly
37
+ Home-Page: https://perspective-dev.github.io
38
+ License-Expression: Apache-2.0
39
+ Requires-Python: >=3.9
40
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
41
+ Project-URL: Changelog, https://github.com/perspective-dev/perspective/blob/master/CHANGELOG.md
42
+ Project-URL: Documentation, https://perspective-dev.github.io/guide/
43
+ Project-URL: Homepage, https://perspective-dev.github.io
44
+ Project-URL: Issues, https://github.com/perspective-dev/perspective/issues
45
+ Project-URL: Repository, https://github.com/perspective-dev/perspective
46
+
47
+ # `perspective-python`
48
+
49
+ [![PyPI](https://img.shields.io/pypi/v/perspective-python.svg?style=for-the-badge)](https://pypi.org/project/perspective-python/)
50
+
51
+ Perspective is an open-source data grid, pivot table and charting component for
52
+ large, real-time and streaming datasets.
53
+
54
+ `perspective-python` is the Python distribution of Perspective:
55
+
56
+ - A streaming query engine — `Table` and `View` — with incremental pivots,
57
+ aggregates, filters, expressions and joins, reading and writing
58
+ [Apache Arrow](https://arrow.apache.org/), CSV, JSON, `pandas`, `polars` and
59
+ `pyarrow`. Tables can be memory-mapped from disk with `page_to_disk=True`
60
+ to exceed available memory.
61
+ - `PerspectiveWidget`, an interactive data grid, pivot table and charting
62
+ widget for JupyterLab, Jupyter Notebook, VS Code notebooks, Google Colab and
63
+ marimo, built on [anywidget](https://anywidget.dev/).
64
+ - WebSocket handlers for Tornado, Starlette (FastAPI) and aiohttp, which serve
65
+ real-time dashboards to `<perspective-viewer>` in the browser.
66
+ - Virtual servers which run the same UI directly against DuckDB, ClickHouse,
67
+ PostgreSQL and Polars, with no data copy.
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ pip install perspective-python
73
+ ```
74
+
75
+ For notebooks, install the `jupyter` extra:
76
+
77
+ ```bash
78
+ pip install "perspective-python[jupyter]"
79
+ ```
80
+
81
+ ## An interactive pivot table in Jupyter
82
+
83
+ ```python
84
+ import pandas as pd
85
+ from perspective.widget import PerspectiveWidget
86
+
87
+ df = pd.read_csv("superstore.csv")
88
+ PerspectiveWidget(df, plugin="Y Bar", group_by=["Region"], columns=["Sales"])
89
+ ```
90
+
91
+ ## A streaming `Table`
92
+
93
+ ```python
94
+ import perspective
95
+
96
+ server = perspective.Server()
97
+ client = server.new_local_client()
98
+ table = client.table({"symbol": "string", "price": "float"}, name="prices")
99
+ table.update([{"symbol": "AAPL", "price": 189.5}])
100
+ ```
101
+
102
+ ## A real-time dashboard server
103
+
104
+ ```python
105
+ import tornado.ioloop
106
+ import tornado.web
107
+ from perspective.handlers.tornado import PerspectiveTornadoHandler
108
+
109
+ app = tornado.web.Application([
110
+ (r"/websocket", PerspectiveTornadoHandler, {"perspective_server": server}),
111
+ ])
112
+
113
+ app.listen(8888)
114
+ tornado.ioloop.IOLoop.current().start()
115
+ ```
116
+
117
+ Any number of browser clients can then open the `prices` table over WebSocket
118
+ and receive every `update()` as it happens.
119
+
120
+ ## Documentation
121
+
122
+ - [Project site and live examples](https://perspective-dev.github.io/)
123
+ - [User guide](https://perspective-dev.github.io/guide/)
124
+ - [Example gallery](https://perspective-dev.github.io/gallery/index.html)
125
+ - [GitHub](https://github.com/perspective-dev/perspective)
126
+ - [Python API reference](https://perspective-dev.github.io/python/index.html)
127
+