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,110 @@
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 perspective import Server, Client, ProxySession, AsyncClient
14
+ import pytest
15
+
16
+
17
+ client = Server().new_local_client()
18
+ Table = client.table
19
+
20
+
21
+ data = {"a": [1, 2, 3], "b": ["a", "b", "c"]}
22
+
23
+
24
+ class TestProxySession(object):
25
+ def test_proxy_client(self):
26
+ server = Server()
27
+ client = server.new_local_client()
28
+
29
+ def handle_request(bytes):
30
+ sub_session.handle_request(bytes)
31
+
32
+ def handle_response(bytes):
33
+ sub_client.handle_response(bytes)
34
+
35
+ sub_session = ProxySession(client, handle_response)
36
+ sub_client = Client(handle_request, sub_session.close)
37
+ table = sub_client.table(data, name="table1")
38
+ assert table.schema() == {"a": "integer", "b": "string"}
39
+
40
+ def test_proxy_and_local_client_can_share_handles(self):
41
+ server = Server()
42
+ client = server.new_local_client()
43
+
44
+ def handle_request(bytes):
45
+ sub_session.handle_request(bytes)
46
+
47
+ def handle_response(bytes):
48
+ sub_client.handle_response(bytes)
49
+
50
+ sub_session = ProxySession(client, handle_response)
51
+ sub_client = Client(handle_request, sub_session.close)
52
+ table = sub_client.table(data, name="table1")
53
+ table2 = client.open_table("table1")
54
+ assert table.size() == 3
55
+ table2.update([{"a": 4, "d": 5}])
56
+ assert table.size() == 4
57
+
58
+ @pytest.mark.asyncio
59
+ async def test_handle_request_async(self):
60
+ """tests use of ProxySession.handle_request_async() in an AsyncClient callback"""
61
+ server = Server()
62
+ client = server.new_local_client()
63
+
64
+ def handle_response(bytes):
65
+ import asyncio
66
+
67
+ asyncio.create_task(sub_client.handle_response(bytes))
68
+
69
+ sub_session = ProxySession(client, handle_response)
70
+ sub_client = AsyncClient(sub_session.handle_request_async, sub_session.close)
71
+ table = await sub_client.table(data, name="table1")
72
+ table2 = client.open_table("table1")
73
+ assert (await table.size()) == 3
74
+ table2.update([{"a": 4, "d": 5}])
75
+ assert (await table.size()) == 4
76
+
77
+ @pytest.mark.asyncio
78
+ async def test_dismabiguate_message_ids_in_proxy_session(self):
79
+ """tests concurrent calls from clients on Session + ProxySession"""
80
+ server = Server()
81
+ client = server.new_local_client()
82
+
83
+ def send_response(bytes):
84
+ import asyncio
85
+
86
+ asyncio.create_task(sub_client.handle_response(bytes))
87
+
88
+ async def send_request(bytes):
89
+ await sub_session.handle_request_async(bytes)
90
+
91
+ sub_session = ProxySession(client, send_response)
92
+ sub_client = AsyncClient(send_request, sub_session.close)
93
+
94
+ table = client.table("x\n1", name="test_table")
95
+ table2 = await sub_client.open_table("test_table")
96
+ view = table.view()
97
+ view2 = await table2.view()
98
+
99
+ sentinel = []
100
+
101
+ def callback1(*args):
102
+ sentinel.append("Callback 1")
103
+
104
+ def callback2(*args):
105
+ sentinel.append("Callback 2")
106
+
107
+ view.on_update(callback1)
108
+ await view2.on_update(callback2)
109
+ await table2.update("x\n2")
110
+ assert sentinel == ["Callback 1", "Callback 2"]
@@ -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
+ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
Binary file
@@ -0,0 +1,402 @@
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 sys
14
+ from random import randint, choice
15
+ import perspective as psp
16
+
17
+ client = psp.Server().new_local_client()
18
+ Table = client.table
19
+
20
+
21
+ class CustomObjectStore(object):
22
+ def __init__(self, value):
23
+ self._value = value
24
+
25
+ def _psp_dtype_(self):
26
+ return "object"
27
+
28
+ def __int__(self):
29
+ return int(self._value)
30
+
31
+ def __repr__(self):
32
+ return "test" if self._value == 1 else "test{}".format(self._value)
33
+
34
+
35
+ def run():
36
+ t = CustomObjectStore(1)
37
+ t2 = CustomObjectStore(2)
38
+
39
+ assert sys.getrefcount(t) == 2
40
+ assert sys.getrefcount(t2) == 2
41
+
42
+ data = {"a": [0], "b": [t]}
43
+ assert sys.getrefcount(t) == 3
44
+
45
+ tbl = Table(data, index="a")
46
+ assert sys.getrefcount(t) == 4
47
+
48
+ assert tbl.schema() == {"a": int, "b": object}
49
+ assert tbl.size() == 1
50
+ assert tbl.view().to_columns() == {"a": [0], "b": [t]}
51
+
52
+ # Count references
53
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
54
+ print(sys.getrefcount(t), "should be", 4)
55
+ print("t:", id(t))
56
+ assert sys.getrefcount(t) == 4
57
+
58
+ print(sys.getrefcount(t2), "should be", 2)
59
+ print("t2:", id(t2))
60
+ assert sys.getrefcount(t2) == 2
61
+
62
+ tbl.update([{"a": i, "b": None} for i in range(1, 6)])
63
+
64
+ assert tbl.size() == 6
65
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
66
+ print(sys.getrefcount(t), "should be", 4)
67
+ assert sys.getrefcount(t) == 4
68
+
69
+ print()
70
+ tbl.update([data])
71
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
72
+ print(sys.getrefcount(t), "should be", 4)
73
+ assert sys.getrefcount(t) == 4
74
+
75
+ print()
76
+ tbl.update([data])
77
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
78
+ print(sys.getrefcount(t), "should be", 4)
79
+ assert sys.getrefcount(t) == 4
80
+
81
+ print()
82
+ tbl.update([data])
83
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
84
+ print(sys.getrefcount(t), "should be", 4)
85
+ assert sys.getrefcount(t) == 4
86
+
87
+ print()
88
+ tbl.update([{"a": 1, "b": t}])
89
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 2 for the table
90
+ print(sys.getrefcount(t), "should be", 5)
91
+ assert sys.getrefcount(t) == 5
92
+
93
+ print()
94
+ tbl.update([{"a": 1, "b": t}])
95
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 2 for the table
96
+ print(sys.getrefcount(t), "should be", 5)
97
+ assert sys.getrefcount(t) == 5
98
+
99
+ print()
100
+ tbl.update([{"a": 3, "b": t}])
101
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
102
+ print(sys.getrefcount(t), "should be", 6)
103
+ assert sys.getrefcount(t) == 6
104
+
105
+ print()
106
+ tbl.update([{"a": 3, "b": t}])
107
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
108
+ print(sys.getrefcount(t), "should be", 6)
109
+ assert sys.getrefcount(t) == 6
110
+
111
+ print()
112
+ tbl.update([{"a": 5, "b": t}])
113
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 4for the table
114
+ print(sys.getrefcount(t), "should be", 7)
115
+ # assert sys.getrefcount(t) == 7
116
+ print(tbl.view().to_columns()["b"])
117
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
118
+ True,
119
+ True,
120
+ False,
121
+ True,
122
+ False,
123
+ True,
124
+ ]
125
+
126
+ print()
127
+ # clear some, overwrite some with same
128
+ tbl.update([{"a": 0, "b": t}])
129
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 4 for the table
130
+ print(sys.getrefcount(t), "should be", 7)
131
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
132
+ True,
133
+ True,
134
+ False,
135
+ True,
136
+ False,
137
+ True,
138
+ ]
139
+
140
+ print()
141
+ tbl.update([{"a": 1, "b": t2}])
142
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
143
+ print(sys.getrefcount(t), "should be", 6)
144
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
145
+ print(sys.getrefcount(t2), "should be", 3)
146
+ print(tbl.view().to_columns()["b"])
147
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
148
+ True,
149
+ True,
150
+ False,
151
+ True,
152
+ False,
153
+ True,
154
+ ]
155
+
156
+ print()
157
+ tbl.update([{"a": 1, "b": t2}])
158
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
159
+ print(sys.getrefcount(t), "should be", 6)
160
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
161
+ print(sys.getrefcount(t2), "should be", 3)
162
+ print(tbl.view().to_columns()["b"])
163
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
164
+ True,
165
+ True,
166
+ False,
167
+ True,
168
+ False,
169
+ True,
170
+ ]
171
+
172
+ print()
173
+ tbl.update([{"a": 1, "b": t2}])
174
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
175
+ print(sys.getrefcount(t), "should be", 6)
176
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
177
+ print(sys.getrefcount(t2), "should be", 3)
178
+ print(tbl.view().to_columns()["b"])
179
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
180
+ True,
181
+ True,
182
+ False,
183
+ True,
184
+ False,
185
+ True,
186
+ ]
187
+
188
+ print()
189
+ tbl.update([{"a": 1, "b": t2}])
190
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
191
+ print(sys.getrefcount(t), "should be", 6)
192
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
193
+ print(sys.getrefcount(t2), "should be", 3)
194
+ print(tbl.view().to_columns()["b"])
195
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
196
+ True,
197
+ True,
198
+ False,
199
+ True,
200
+ False,
201
+ True,
202
+ ]
203
+
204
+ print()
205
+ tbl.update([{"a": 2, "b": t2}])
206
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
207
+ print(sys.getrefcount(t), "should be", 6)
208
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
209
+ print(sys.getrefcount(t2), "should be", 4)
210
+ print(tbl.view().to_columns()["b"])
211
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
212
+ True,
213
+ True,
214
+ True,
215
+ True,
216
+ False,
217
+ True,
218
+ ]
219
+
220
+ print()
221
+ tbl.update([{"a": 2, "b": None}])
222
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
223
+ print(sys.getrefcount(t), "should be", 6)
224
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
225
+ print(sys.getrefcount(t2), "should be", 3)
226
+ print(tbl.view().to_columns()["b"])
227
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
228
+ True,
229
+ True,
230
+ False,
231
+ True,
232
+ False,
233
+ True,
234
+ ]
235
+
236
+ print()
237
+ tbl.update([{"a": 2, "b": t2}])
238
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
239
+ print(sys.getrefcount(t), "should be", 6)
240
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
241
+ print(sys.getrefcount(t2), "should be", 4)
242
+ print(tbl.view().to_columns()["b"])
243
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
244
+ True,
245
+ True,
246
+ True,
247
+ True,
248
+ False,
249
+ True,
250
+ ]
251
+
252
+ print()
253
+ tbl.update([{"a": 2, "b": None}])
254
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 3 for the table
255
+ print(sys.getrefcount(t), "should be", 6)
256
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
257
+ print(sys.getrefcount(t2), "should be", 3)
258
+ print(tbl.view().to_columns()["b"])
259
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
260
+ True,
261
+ True,
262
+ False,
263
+ True,
264
+ False,
265
+ True,
266
+ ]
267
+
268
+ print()
269
+ tbl.update([{"a": 3, "b": None}])
270
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 2 for the table
271
+ print(sys.getrefcount(t), "should be", 5)
272
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
273
+ print(sys.getrefcount(t2), "should be", 3)
274
+ print(tbl.view().to_columns()["b"])
275
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
276
+ True,
277
+ True,
278
+ False,
279
+ False,
280
+ False,
281
+ True,
282
+ ]
283
+
284
+ print()
285
+ tbl.update([{"a": 3, "b": None}])
286
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 2 for the table
287
+ print(sys.getrefcount(t), "should be", 5)
288
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
289
+ print(sys.getrefcount(t2), "should be", 3)
290
+ print(tbl.view().to_columns()["b"])
291
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
292
+ True,
293
+ True,
294
+ False,
295
+ False,
296
+ False,
297
+ True,
298
+ ]
299
+
300
+ print()
301
+ tbl.update([{"a": 5, "b": None}])
302
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
303
+ print(sys.getrefcount(t), "should be", 4)
304
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
305
+ print(sys.getrefcount(t2), "should be", 3)
306
+ print(tbl.view().to_columns()["b"])
307
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
308
+ True,
309
+ True,
310
+ False,
311
+ False,
312
+ False,
313
+ False,
314
+ ]
315
+
316
+ print()
317
+ tbl.update([{"a": 5, "b": None}])
318
+ # 1 for `t`, 1 for `data`, 1 for argument to sys.getrefcount, and 1 for the table
319
+ print(sys.getrefcount(t), "should be", 4)
320
+ # 1 for `t2`, 1 for argument to sys.getrefcount, and 1 for the table
321
+ print(sys.getrefcount(t2), "should be", 3)
322
+ print(tbl.view().to_columns()["b"])
323
+ assert list(_ is not None for _ in tbl.view().to_columns()["b"]) == [
324
+ True,
325
+ True,
326
+ False,
327
+ False,
328
+ False,
329
+ False,
330
+ ]
331
+
332
+ print()
333
+ tbl.clear()
334
+ assert tbl.size() == 0
335
+ assert tbl.view().to_columns() == {}
336
+ # 1 for `t`, one for `data`, one for argument to sys.getrefcount
337
+ print(sys.getrefcount(t), "should be", 3)
338
+ assert sys.getrefcount(t) == 3
339
+
340
+
341
+ def run2():
342
+ t = CustomObjectStore(1)
343
+ t_ref_count = 2
344
+ assert sys.getrefcount(t) == t_ref_count
345
+
346
+ indexes = set([0])
347
+
348
+ tbl = Table({"a": [0], "b": [t]}, index="a")
349
+ assert sys.getrefcount(t) == 3
350
+ t_ref_count += 1
351
+
352
+ assert tbl.schema() == {"a": int, "b": object}
353
+ assert tbl.size() == 1
354
+ assert tbl.view().to_columns() == {"a": [0], "b": [t]}
355
+
356
+ # seed a few to check
357
+ tbl.remove([1])
358
+ tbl.remove([1])
359
+ tbl.remove([1])
360
+
361
+ for _ in range(10):
362
+ pick = randint(1, 2) if indexes else 1
363
+ if pick == 1:
364
+ ind = randint(1, 10)
365
+ while ind in indexes:
366
+ ind = randint(1, 100)
367
+
368
+ print(
369
+ "adding", ind, "refcount", t_ref_count, "should be", sys.getrefcount(t)
370
+ )
371
+ tbl.update({"a": [ind], "b": [t]})
372
+ t_ref_count += 1
373
+ indexes.add(ind)
374
+ assert sys.getrefcount(t) == t_ref_count
375
+
376
+ else:
377
+ ind = choice(list(indexes))
378
+ indexes.remove(ind)
379
+ tbl.remove([ind])
380
+ t_ref_count -= 1
381
+ print(
382
+ "removing",
383
+ ind,
384
+ "refcount",
385
+ t_ref_count,
386
+ "should be",
387
+ sys.getrefcount(t),
388
+ )
389
+ assert sys.getrefcount(t) == t_ref_count
390
+
391
+ print(t_ref_count)
392
+ print(tbl.view().to_columns())
393
+
394
+ assert sys.getrefcount(t) == t_ref_count
395
+
396
+ print()
397
+ tbl.clear()
398
+ assert tbl.size() == 0
399
+ assert tbl.view().to_columns() == {}
400
+ # 1 for `t`, one for `data`, one for argument to sys.getrefcount
401
+ print(sys.getrefcount(t), "should be", 2)
402
+ assert sys.getrefcount(t) == 2
@@ -0,0 +1,89 @@
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 perspective as psp
14
+
15
+ client = psp.Server().new_local_client()
16
+
17
+
18
+ class TestViewColumnPaths(object):
19
+ def test_column_paths(self, superstore):
20
+ tbl = client.table(superstore)
21
+ view = tbl.view()
22
+ paths = view.column_paths()
23
+ assert paths == [
24
+ "index",
25
+ "Row ID",
26
+ "Order ID",
27
+ "Order Date",
28
+ "Ship Date",
29
+ "Ship Mode",
30
+ "Customer ID",
31
+ "Segment",
32
+ "Country",
33
+ "City",
34
+ "State",
35
+ "Postal Code",
36
+ "Region",
37
+ "Product ID",
38
+ "Category",
39
+ "Sub-Category",
40
+ "Sales",
41
+ "Quantity",
42
+ "Discount",
43
+ "Profit",
44
+ ]
45
+
46
+ view.delete()
47
+ tbl.delete()
48
+
49
+ def test_column_paths_split_by(self, superstore):
50
+ tbl = client.table(superstore)
51
+ view = tbl.view(group_by=["State"], columns=["Sales"], split_by=["Ship Mode"])
52
+ paths = view.column_paths()
53
+ assert paths == [
54
+ "First Class|Sales",
55
+ "Second Class|Sales",
56
+ "Standard Class|Sales",
57
+ ]
58
+
59
+ view.delete()
60
+ tbl.delete()
61
+
62
+ def test_column_paths_split_by_range(self, superstore):
63
+ tbl = client.table(superstore)
64
+ view = tbl.view(group_by=["State"], columns=["Sales"], split_by=["Ship Mode"])
65
+ paths = view.column_paths(start_col=1)
66
+ assert paths == [
67
+ "Second Class|Sales",
68
+ "Standard Class|Sales",
69
+ ]
70
+
71
+ view.delete()
72
+ view = tbl.view(group_by=["State"], columns=["Sales"], split_by=["Ship Mode"])
73
+ paths = view.column_paths(start_col=1, end_col=2)
74
+ assert paths == [
75
+ "Second Class|Sales",
76
+ "Standard Class|Sales",
77
+ ]
78
+
79
+ view.delete()
80
+ tbl = client.table(superstore)
81
+ view = tbl.view(group_by=["State"], columns=["Sales"], split_by=["Ship Mode"])
82
+ paths = view.column_paths(end_col=1)
83
+ assert paths == [
84
+ "First Class|Sales",
85
+ "Second Class|Sales",
86
+ ]
87
+
88
+ view.delete()
89
+ tbl.delete()