perspective-python 3.0.0rc1__cp39-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 (70) hide show
  1. perspective/__init__.py +78 -0
  2. perspective/extension/finos-perspective-nbextension.json +5 -0
  3. perspective/handlers/__init__.py +26 -0
  4. perspective/handlers/aiohttp.py +54 -0
  5. perspective/handlers/starlette.py +51 -0
  6. perspective/handlers/tornado.py +61 -0
  7. perspective/perspective.pyd +0 -0
  8. perspective/templates/exported_widget.html.jinja +35 -0
  9. perspective/tests/__init__.py +11 -0
  10. perspective/tests/conftest.py +268 -0
  11. perspective/tests/core/__init__.py +11 -0
  12. perspective/tests/core/test_async.py +436 -0
  13. perspective/tests/core/test_threadpool.py +48 -0
  14. perspective/tests/server/__init__.py +11 -0
  15. perspective/tests/server/test_server.py +1062 -0
  16. perspective/tests/server/test_session.py +55 -0
  17. perspective/tests/single_threaded/test_single_threaded.py +61 -0
  18. perspective/tests/table/__init__.py +11 -0
  19. perspective/tests/table/arrow/date32.arrow +0 -0
  20. perspective/tests/table/arrow/date64.arrow +0 -0
  21. perspective/tests/table/arrow/dict.arrow +0 -0
  22. perspective/tests/table/arrow/dict_update.arrow +0 -0
  23. perspective/tests/table/arrow/int_float_str.arrow +0 -0
  24. perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
  25. perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
  26. perspective/tests/table/object_sequence.py +402 -0
  27. perspective/tests/table/test_delete.py +124 -0
  28. perspective/tests/table/test_exception.py +53 -0
  29. perspective/tests/table/test_leaks.py +54 -0
  30. perspective/tests/table/test_ports.py +178 -0
  31. perspective/tests/table/test_remove.py +102 -0
  32. perspective/tests/table/test_table.py +610 -0
  33. perspective/tests/table/test_table_arrow.py +452 -0
  34. perspective/tests/table/test_table_datetime.py +2409 -0
  35. perspective/tests/table/test_table_infer.py +201 -0
  36. perspective/tests/table/test_table_limit.py +43 -0
  37. perspective/tests/table/test_table_numpy.py +1022 -0
  38. perspective/tests/table/test_table_pandas.py +1018 -0
  39. perspective/tests/table/test_to_arrow.py +414 -0
  40. perspective/tests/table/test_to_arrow_lz4.py +33 -0
  41. perspective/tests/table/test_to_format.py +1024 -0
  42. perspective/tests/table/test_update.py +545 -0
  43. perspective/tests/table/test_update_arrow.py +980 -0
  44. perspective/tests/table/test_update_numpy.py +252 -0
  45. perspective/tests/table/test_update_pandas.py +211 -0
  46. perspective/tests/table/test_view.py +2235 -0
  47. perspective/tests/table/test_view_expression.py +1940 -0
  48. perspective/tests/viewer/__init__.py +11 -0
  49. perspective/tests/viewer/test_validate.py +70 -0
  50. perspective/tests/viewer/test_viewer.py +245 -0
  51. perspective/tests/widget/__init__.py +11 -0
  52. perspective/tests/widget/test_widget.py +278 -0
  53. perspective/tests/widget/test_widget_pandas.py +453 -0
  54. perspective/viewer/__init__.py +15 -0
  55. perspective/viewer/validate.py +22 -0
  56. perspective/viewer/viewer.py +331 -0
  57. perspective/viewer/viewer_traitlets.py +101 -0
  58. perspective/widget/__init__.py +16 -0
  59. perspective/widget/widget.py +269 -0
  60. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/install.json +5 -0
  61. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/package.json +81 -0
  62. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/253.6f17b87bb4eb1e656365.js +18 -0
  63. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/253.6f17b87bb4eb1e656365.js.LICENSE.txt +59 -0
  64. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/905.d3bbc3d5954582d507bb.js +1 -0
  65. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/remoteEntry.7044010cbbf2a7208035.js +1 -0
  66. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/style.js +4 -0
  67. perspective.data/data/share/jupyter/labextensions/@finos/perspective-jupyterlab/static/third-party-licenses.json +16 -0
  68. perspective_python-3.0.0rc1.dist-info/METADATA +13 -0
  69. perspective_python-3.0.0rc1.dist-info/RECORD +70 -0
  70. perspective_python-3.0.0rc1.dist-info/WHEEL +4 -0
@@ -0,0 +1,55 @@
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
14
+
15
+
16
+ client = Server().new_local_client()
17
+ Table = client.table
18
+
19
+
20
+ data = {"a": [1, 2, 3], "b": ["a", "b", "c"]}
21
+
22
+
23
+ class TestProxySession(object):
24
+ def test_proxy_client(self):
25
+ server = Server()
26
+ client = server.new_local_client()
27
+
28
+ def handle_request(bytes):
29
+ sub_session.handle_request(bytes)
30
+
31
+ def handle_response(bytes):
32
+ sub_client.handle_response(bytes)
33
+
34
+ sub_session = ProxySession(client, handle_response)
35
+ sub_client = Client(handle_request)
36
+ table = sub_client.table(data, name="table1")
37
+ assert table.schema() == {"a": "integer", "b": "string"}
38
+
39
+ def test_proxy_and_local_client_can_share_handles(self):
40
+ server = Server()
41
+ client = server.new_local_client()
42
+
43
+ def handle_request(bytes):
44
+ sub_session.handle_request(bytes)
45
+
46
+ def handle_response(bytes):
47
+ sub_client.handle_response(bytes)
48
+
49
+ sub_session = ProxySession(client, handle_response)
50
+ sub_client = Client(handle_request)
51
+ table = sub_client.table(data, name="table1")
52
+ table2 = client.open_table("table1")
53
+ assert table.size() == 3
54
+ table2.update([{"a": 4, "d": 5}])
55
+ assert table.size() == 4
@@ -0,0 +1,61 @@
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
+ import perspective as psp
15
+
16
+ server = psp.Server()
17
+ server.set_threadpool_size(1)
18
+ client = server.new_local_client()
19
+ Table = client.table
20
+
21
+
22
+ class TestThreadPoolOne:
23
+ def test_threadpool_one_does_not_block_view(self):
24
+ t = Table(
25
+ {
26
+ "id": "integer",
27
+ "symbol": "string",
28
+ "valid": "boolean",
29
+ "value": "integer",
30
+ "value2": "integer",
31
+ },
32
+ index="id",
33
+ )
34
+ t.update(
35
+ [
36
+ {"id": 1, "symbol": "A", "valid": False, "value": 5, "value2": 15},
37
+ {"id": 2, "symbol": "A", "valid": True, "value": 10, "value2": 20},
38
+ ]
39
+ )
40
+
41
+ v = t.view(
42
+ columns=["symbol", "value", "value3"],
43
+ expressions={"value3": """"value" + "value2\""""},
44
+ )
45
+
46
+ v_agg = t.view(
47
+ columns=["symbol", "value", "value3"],
48
+ expressions={"value3": """"value" + "value2\""""},
49
+ group_by=["symbol"],
50
+ aggregates={"symbol": "first", "value": "sum", "value2": "sum"},
51
+ )
52
+
53
+ assert v.to_json() == [
54
+ {"symbol": "A", "value": 5, "value3": 20.0},
55
+ {"symbol": "A", "value": 10, "value3": 30.0},
56
+ ]
57
+
58
+ assert v_agg.to_json() == [
59
+ {"__ROW_PATH__": [], "symbol": "A", "value": 15, "value3": 50.0},
60
+ {"__ROW_PATH__": ["A"], "symbol": "A", "value": 15, "value3": 50.0},
61
+ ]
@@ -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,124 @@
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
+ Table = client.table
17
+
18
+
19
+ class TestDelete(object):
20
+ # delete
21
+
22
+ def test_table_delete(self):
23
+ data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
24
+ tbl = Table(data)
25
+ tbl.delete()
26
+ # don't segfault
27
+
28
+ def test_table_delete_callback(self, sentinel):
29
+ s = sentinel(False)
30
+
31
+ def callback():
32
+ s.set(True)
33
+
34
+ data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
35
+ tbl = Table(data)
36
+ tbl.on_delete(callback)
37
+ tbl.delete()
38
+ assert s.get() is True
39
+
40
+ def test_table_delete_with_view(self, sentinel):
41
+ s = sentinel(False)
42
+
43
+ def callback():
44
+ s.set(True)
45
+
46
+ data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
47
+ tbl = Table(data)
48
+ tbl.on_delete(callback)
49
+ view = tbl.view()
50
+ view.delete()
51
+ tbl.delete()
52
+ assert s.get() is True
53
+
54
+ def test_table_delete_multiple_callback(self, sentinel):
55
+ s1 = sentinel(False)
56
+ s2 = sentinel(False)
57
+
58
+ def callback1():
59
+ s1.set(True)
60
+
61
+ def callback2():
62
+ s2.set(True)
63
+
64
+ tbl = Table([{"a": 1}])
65
+ tbl.on_delete(callback1)
66
+ tbl.on_delete(callback2)
67
+
68
+ tbl.delete()
69
+
70
+ assert s1.get() is True
71
+ assert s2.get() is True
72
+
73
+ def test_table_remove_delete_callback(self, sentinel):
74
+ s = sentinel(False)
75
+
76
+ def callback():
77
+ s.set(True)
78
+
79
+ tbl = Table([{"a": 1}])
80
+ callback_id = tbl.on_delete(callback)
81
+ tbl.remove_delete(callback_id)
82
+
83
+ tbl.delete()
84
+
85
+ assert s.get() is False
86
+
87
+ def test_view_delete_multiple_callback(self, sentinel):
88
+ s1 = sentinel(False)
89
+ s2 = sentinel(False)
90
+
91
+ def callback1():
92
+ s1.set(True)
93
+
94
+ def callback2():
95
+ s2.set(True)
96
+
97
+ tbl = Table([{"a": 1}])
98
+ view = tbl.view()
99
+
100
+ view.on_delete(callback1)
101
+ view.on_delete(callback2)
102
+
103
+ view.delete()
104
+ tbl.delete()
105
+
106
+ assert s1.get() is True
107
+ assert s2.get() is True
108
+
109
+ def test_view_remove_delete_callback(self, sentinel):
110
+ s = sentinel(False)
111
+
112
+ def callback():
113
+ s.set(True)
114
+
115
+ tbl = Table([{"a": 1}])
116
+ view = tbl.view()
117
+
118
+ callback_id = view.on_delete(callback)
119
+ view.remove_delete(callback_id)
120
+
121
+ view.delete()
122
+ tbl.delete()
123
+
124
+ assert s.get() is False