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,26 @@
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 TestToPolars(object):
20
+ def test_to_polars(self, superstore):
21
+ original_tbl = Table(superstore.to_dict(orient="records"))
22
+ df = original_tbl.view().to_polars()
23
+ tbl = Table(df)
24
+ assert tbl.size() == original_tbl.size()
25
+ df2 = tbl.view().to_polars()
26
+ assert df.equals(df2)
@@ -0,0 +1,545 @@
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 numpy as np
14
+ import pyarrow as pa
15
+ import pandas as pd
16
+ from datetime import date, datetime
17
+ from pytest import mark
18
+ import perspective as psp
19
+
20
+ client = psp.Server().new_local_client()
21
+ Table = client.table
22
+
23
+
24
+ class TestUpdate(object):
25
+ @mark.skip(reason="Fix for null values update yet to be implemented")
26
+ def test_update_with_missing_or_null_values(self):
27
+ tbl = Table([{"a": "1", "b": "2"}, {"a": "3", "b": "4"}], index="a")
28
+ tbl.update([{"a": "1", "b": None}])
29
+ assert tbl.view().to_records() == [{"a": "1", "b": None}, {"a": "3", "b": "4"}]
30
+
31
+ data = pd.DataFrame({"a": ["1"], "b": [None]})
32
+
33
+ arrow_table = pa.Table.from_pandas(data, preserve_index=False)
34
+
35
+ # write arrow to stream
36
+ stream = pa.BufferOutputStream()
37
+ writer = pa.RecordBatchStreamWriter(
38
+ stream, arrow_table.schema
39
+ )
40
+ writer.write_table(arrow_table)
41
+ writer.close()
42
+ arrow = stream.getvalue().to_pybytes()
43
+
44
+ tbl.update(arrow)
45
+ assert tbl.size() == 2
46
+ assert tbl.view().to_records() == [{"a": "1", "b": ""}, {"a": "3", "b": "4"}]
47
+
48
+ def test_update_from_schema(self):
49
+ tbl = Table({"a": "string", "b": "integer"})
50
+ tbl.update([{"a": "abc", "b": 123}])
51
+ assert tbl.view().to_records() == [{"a": "abc", "b": 123}]
52
+
53
+ def test_update_columnar_from_schema(self):
54
+ tbl = Table({"a": "string", "b": "integer"})
55
+ tbl.update({"a": ["abc"], "b": [123]})
56
+ assert tbl.view().to_records() == [{"a": "abc", "b": 123}]
57
+
58
+ def test_update_csv(self):
59
+ tbl = Table({"a": "string", "b": "integer"})
60
+
61
+ view = tbl.view()
62
+ tbl.update("a,b\nxyz,123\ndef,100000000")
63
+
64
+ assert view.to_columns() == {"a": ["xyz", "def"], "b": [123, 100000000]}
65
+
66
+ def test_update_csv_indexed(self):
67
+ tbl = Table({"a": "string", "b": "float"}, index="a")
68
+
69
+ view = tbl.view()
70
+ tbl.update("a,b\nxyz,1.23456718\ndef,100000000.1")
71
+
72
+ assert view.to_columns() == {
73
+ "a": ["def", "xyz"],
74
+ "b": [100000000.1, 1.23456718],
75
+ }
76
+
77
+ tbl.update("a,b\nxyz,0.00000001\ndef,1234.5678\nefg,100.2")
78
+
79
+ assert view.to_columns() == {
80
+ "a": ["def", "efg", "xyz"],
81
+ "b": [1234.5678, 100.2, 0.00000001],
82
+ }
83
+
84
+ def test_update_append(self):
85
+ tbl = Table([{"a": "abc", "b": 123}])
86
+ tbl.update([{"a": "def", "b": 456}])
87
+ assert tbl.view().to_records() == [
88
+ {"a": "abc", "b": 123},
89
+ {"a": "def", "b": 456},
90
+ ]
91
+
92
+ def test_update_partial(self):
93
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
94
+ tbl.update([{"a": "abc", "b": 456}])
95
+ assert tbl.view().to_records() == [{"a": "abc", "b": 456}]
96
+
97
+ def test_update_partial_add_row(self):
98
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
99
+ tbl.update([{"a": "abc", "b": 456}, {"a": "def"}])
100
+ assert tbl.view().to_records() == [
101
+ {"a": "abc", "b": 456},
102
+ {"a": "def", "b": None},
103
+ ]
104
+
105
+ def test_update_partial_noop(self):
106
+ tbl = Table([{"a": "abc", "b": 1, "c": 2}], index="a")
107
+ tbl.update([{"a": "abc", "b": 456}])
108
+ assert tbl.view().to_records() == [{"a": "abc", "b": 456, "c": 2}]
109
+
110
+ def test_update_partial_unset(self):
111
+ tbl = Table(
112
+ [{"a": "abc", "b": 1, "c": 2}, {"a": "def", "b": 3, "c": 4}], index="a"
113
+ )
114
+ tbl.update([{"a": "abc"}, {"a": "def", "c": None}])
115
+ assert tbl.view().to_records() == [
116
+ {"a": "abc", "b": 1, "c": 2},
117
+ {"a": "def", "b": 3, "c": None},
118
+ ]
119
+
120
+ def test_update_columnar_partial_add_row(self):
121
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
122
+
123
+ tbl.update({"a": ["abc", "def"], "b": [456, None]})
124
+
125
+ assert tbl.view().to_records() == [
126
+ {"a": "abc", "b": 456},
127
+ {"a": "def", "b": None},
128
+ ]
129
+
130
+ def test_update_columnar_partial_noop(self):
131
+ tbl = Table([{"a": "abc", "b": 1, "c": 2}], index="a")
132
+
133
+ # no-op because "c" is not in the update dataset
134
+ tbl.update({"a": ["abc"], "b": [456]})
135
+
136
+ assert tbl.view().to_records() == [{"a": "abc", "b": 456, "c": 2}]
137
+
138
+ def test_update_columnar_partial_unset(self):
139
+ tbl = Table(
140
+ [{"a": "abc", "b": 1, "c": 2}, {"a": "def", "b": 3, "c": 4}], index="a"
141
+ )
142
+
143
+ tbl.update({"a": ["abc"], "b": [None]})
144
+
145
+ assert tbl.view().to_records() == [
146
+ {"a": "abc", "b": None, "c": 2},
147
+ {"a": "def", "b": 3, "c": 4},
148
+ ]
149
+
150
+ def test_update_partial_subcolumn(self):
151
+ tbl = Table([{"a": "abc", "b": 123, "c": 456}], index="a")
152
+ tbl.update([{"a": "abc", "c": 1234}])
153
+ assert tbl.view().to_records() == [{"a": "abc", "b": 123, "c": 1234}]
154
+
155
+ def test_update_partial_subcolumn_dict(self):
156
+ tbl = Table([{"a": "abc", "b": 123, "c": 456}], index="a")
157
+ tbl.update({"a": ["abc"], "c": [1234]})
158
+ assert tbl.view().to_records() == [{"a": "abc", "b": 123, "c": 1234}]
159
+
160
+ def test_update_partial_cols_more_columns_than_table(self):
161
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
162
+ tbl.update([{"a": "abc", "b": 456, "c": 789}])
163
+ assert tbl.view().to_records() == [{"a": "abc", "b": 456}]
164
+
165
+ def test_update_columnar_append(self):
166
+ tbl = Table({"a": ["abc"], "b": [123]})
167
+ tbl.update({"a": ["def"], "b": [456]})
168
+ assert tbl.view().to_records() == [
169
+ {"a": "abc", "b": 123},
170
+ {"a": "def", "b": 456},
171
+ ]
172
+
173
+ def test_update_columnar_partial(self):
174
+ tbl = Table({"a": ["abc"], "b": [123]}, index="a")
175
+ tbl.update({"a": ["abc"], "b": [456]})
176
+ assert tbl.view().to_records() == [{"a": "abc", "b": 456}]
177
+
178
+ # make sure already created views are notified properly
179
+ def test_update_from_schema_notify(self):
180
+ tbl = Table({"a": "string", "b": "integer"})
181
+ view = tbl.view()
182
+ assert view.num_rows() == 0
183
+ tbl.update([{"a": "abc", "b": 123}])
184
+ assert view.to_records() == [{"a": "abc", "b": 123}]
185
+
186
+ def test_update_columnar_from_schema_notify(self):
187
+ tbl = Table({"a": "string", "b": "integer"})
188
+ view = tbl.view()
189
+ assert view.num_rows() == 0
190
+ tbl.update({"a": ["abc"], "b": [123]})
191
+ assert view.to_records() == [{"a": "abc", "b": 123}]
192
+
193
+ def test_update_append_notify(self):
194
+ tbl = Table([{"a": "abc", "b": 123}])
195
+ view = tbl.view()
196
+ assert view.num_rows() == 1
197
+ tbl.update([{"a": "def", "b": 456}])
198
+ assert view.to_records() == [{"a": "abc", "b": 123}, {"a": "def", "b": 456}]
199
+
200
+ def test_update_partial_notify(self):
201
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
202
+ view = tbl.view()
203
+ assert view.num_rows() == 1
204
+ tbl.update([{"a": "abc", "b": 456}])
205
+ assert view.to_records() == [{"a": "abc", "b": 456}]
206
+
207
+ def test_update_partial_cols_not_in_schema_notify(self):
208
+ tbl = Table([{"a": "abc", "b": 123}], index="a")
209
+ view = tbl.view()
210
+ assert view.num_rows() == 1
211
+ tbl.update([{"a": "abc", "b": 456, "c": 789}])
212
+ assert view.to_records() == [{"a": "abc", "b": 456}]
213
+
214
+ def test_update_columnar_append_notify(self):
215
+ tbl = Table({"a": ["abc"], "b": [123]})
216
+ view = tbl.view()
217
+ assert view.num_rows() == 1
218
+ tbl.update({"a": ["def"], "b": [456]})
219
+ assert view.to_records() == [{"a": "abc", "b": 123}, {"a": "def", "b": 456}]
220
+
221
+ def test_update_columnar_partial_notify(self):
222
+ tbl = Table({"a": ["abc"], "b": [123]}, index="a")
223
+ view = tbl.view()
224
+ assert view.num_rows() == 1
225
+ tbl.update({"a": ["abc"], "b": [456]})
226
+ assert view.to_records() == [{"a": "abc", "b": 456}]
227
+
228
+ # bool
229
+
230
+ def test_update_bool_from_schema(self):
231
+ bool_data = [{"a": True, "b": False}, {"a": True, "b": True}]
232
+ tbl = Table({"a": "boolean", "b": "boolean"})
233
+ tbl.update(bool_data)
234
+ assert tbl.size() == 2
235
+ assert tbl.view().to_records() == bool_data
236
+
237
+ def test_update_bool_str_from_schema(self):
238
+ bool_data = [{"a": "True", "b": "False"}, {"a": "True", "b": "True"}]
239
+ tbl = Table({"a": "boolean", "b": "boolean"})
240
+ tbl.update(bool_data)
241
+ assert tbl.size() == 2
242
+ assert tbl.view().to_records() == [
243
+ {"a": True, "b": False},
244
+ {"a": True, "b": True},
245
+ ]
246
+
247
+ @mark.skip(reason="We may not want to support this")
248
+ def test_update_bool_str_all_formats_from_schema(self):
249
+ bool_data = [
250
+ {"a": "True", "b": "False"},
251
+ {"a": "t", "b": "f"},
252
+ {"a": "true", "b": "false"},
253
+ {"a": 1, "b": 0},
254
+ {"a": "on", "b": "off"},
255
+ ]
256
+ tbl = Table({"a": "boolean", "b": "boolean"})
257
+ tbl.update(bool_data)
258
+ assert tbl.size() == 5
259
+ assert tbl.view().to_columns() == {
260
+ "a": [True, True, True, True, True],
261
+ "b": [False, False, False, False, False],
262
+ }
263
+
264
+ def test_update_bool_int_from_schema(self):
265
+ bool_data = [{"a": 1, "b": 0}, {"a": 1, "b": 0}]
266
+ tbl = Table({"a": "boolean", "b": "boolean"})
267
+ tbl.update(bool_data)
268
+ assert tbl.size() == 2
269
+ assert tbl.view().to_columns() == {"a": [True, True], "b": [False, False]}
270
+
271
+ # dates and datetimes
272
+ def test_update_date(self, util):
273
+ tbl = Table({"a": [date(2019, 7, 11)]})
274
+ tbl.update([{"a": date(2019, 7, 12)}])
275
+ assert tbl.view().to_records() == [
276
+ {"a": util.to_timestamp(datetime(2019, 7, 11))},
277
+ {"a": util.to_timestamp(datetime(2019, 7, 12))},
278
+ ]
279
+
280
+ @mark.skip # We do not support numpy.
281
+ def test_update_date_np(self, util):
282
+ tbl = Table({"a": [date(2019, 7, 11)]})
283
+ tbl.update([{"a": np.datetime64(date(2019, 7, 12))}])
284
+ assert tbl.view().to_records() == [
285
+ {"a": util.to_timestamp(datetime(2019, 7, 11))},
286
+ {"a": util.to_timestamp(datetime(2019, 7, 12))},
287
+ ]
288
+
289
+ def test_update_datetime(self, util):
290
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
291
+ tbl.update([{"a": datetime(2019, 7, 12, 11, 0)}])
292
+ assert tbl.view().to_records() == [
293
+ {"a": util.to_timestamp(datetime(2019, 7, 11, 11, 0))},
294
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))},
295
+ ]
296
+
297
+ @mark.skip # We do not support numpy.
298
+ def test_update_datetime_np(self, util):
299
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
300
+ tbl.update([{"a": np.datetime64(datetime(2019, 7, 12, 11, 0))}])
301
+ assert tbl.view().to_records() == [
302
+ {"a": util.to_timestamp(datetime(2019, 7, 11, 11, 0))},
303
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))},
304
+ ]
305
+
306
+ @mark.skip # We do not support numpy.
307
+ def test_update_datetime_np_ts(self, util):
308
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
309
+ tbl.update([{"a": np.datetime64("2019-07-12T11:00")}])
310
+ assert tbl.view().to_records() == [
311
+ {"a": util.to_timestamp(datetime(2019, 7, 11, 11, 0))},
312
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))},
313
+ ]
314
+
315
+ def test_update_datetime_timestamp_seconds(self, util):
316
+ ts = util.to_timestamp(datetime(2019, 7, 12, 11, 0, 0))
317
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
318
+ tbl.update([{"a": ts}])
319
+ assert tbl.view().to_records() == [
320
+ {"a": util.to_timestamp(datetime(2019, 7, 11, 11, 0))},
321
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))},
322
+ ]
323
+
324
+ def test_update_datetime_timestamp_ms(self, util):
325
+ ts = util.to_timestamp(datetime(2019, 7, 12, 11, 0, 0))
326
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
327
+ tbl.update([{"a": ts}])
328
+ assert tbl.view().to_records() == [
329
+ {"a": util.to_timestamp(datetime(2019, 7, 11, 11, 0))},
330
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))},
331
+ ]
332
+
333
+ # partial date & datetime updates
334
+
335
+ def test_update_date_partial(self, util):
336
+ tbl = Table({"a": [date(2019, 7, 11)], "b": [1]}, index="b")
337
+ tbl.update([{"a": date(2019, 7, 12), "b": 1}])
338
+ assert tbl.view().to_records() == [
339
+ {"a": util.to_timestamp(datetime(2019, 7, 12)), "b": 1}
340
+ ]
341
+
342
+ @mark.skip # We do not support numpy anymore.
343
+ def test_update_date_np_partial(self, util):
344
+ tbl = Table({"a": [date(2019, 7, 11)], "b": [1]}, index="b")
345
+ tbl.update([{"a": np.datetime64(date(2019, 7, 12)), "b": 1}])
346
+ assert tbl.view().to_records() == [
347
+ {"a": util.to_timestamp(datetime(2019, 7, 12)), "b": 1}
348
+ ]
349
+
350
+ def test_update_datetime_partial(self, util):
351
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)], "b": [1]}, index="b")
352
+ tbl.update([{"a": datetime(2019, 7, 12, 11, 0), "b": 1}])
353
+ assert tbl.view().to_records() == [
354
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0)), "b": 1}
355
+ ]
356
+
357
+ @mark.skip # We do not support numpy anymore.
358
+ def test_update_datetime_np_partial(self, util):
359
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)], "b": [1]}, index="b")
360
+ tbl.update([{"a": np.datetime64(datetime(2019, 7, 12, 11, 0)), "b": 1}])
361
+ assert tbl.view().to_records() == [
362
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0)), "b": 1}
363
+ ]
364
+
365
+ @mark.skip # We do not support numpy anymore.
366
+ def test_update_datetime_np_ts_partial(self, util):
367
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)], "b": [1]}, index="b")
368
+ tbl.update([{"a": np.datetime64("2019-07-12T11:00"), "b": 1}])
369
+ assert tbl.view().to_records() == [
370
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0)), "b": 1}
371
+ ]
372
+
373
+ def test_update_datetime_timestamp_seconds_partial(self, util):
374
+ ts = util.to_timestamp(datetime(2019, 7, 12, 11, 0, 0))
375
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)], "idx": [1]}, index="idx")
376
+ tbl.update([{"a": ts, "idx": 1}])
377
+ assert tbl.view().to_records() == [
378
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0)), "idx": 1}
379
+ ]
380
+
381
+ def test_update_datetime_timestamp_ms_partial(self, util):
382
+ ts = util.to_timestamp(datetime(2019, 7, 12, 11, 0, 0))
383
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)], "idx": [1]}, index="idx")
384
+ tbl.update([{"a": ts, "idx": 1}])
385
+ assert tbl.view().to_records() == [
386
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0)), "idx": 1}
387
+ ]
388
+
389
+ # updating dates using implicit index
390
+
391
+ def test_update_date_partial_implicit(self, util):
392
+ tbl = Table({"a": [date(2019, 7, 11)]})
393
+ tbl.update([{"a": date(2019, 7, 12), "__INDEX__": 0}])
394
+ assert tbl.view().to_records() == [
395
+ {"a": util.to_timestamp(datetime(2019, 7, 12))}
396
+ ]
397
+
398
+ @mark.skip # We do not support numpy anymore.
399
+ def test_update_date_np_partial_implicit(self, util):
400
+ tbl = Table({"a": [date(2019, 7, 11)]})
401
+ tbl.update([{"a": np.datetime64(date(2019, 7, 12)), "__INDEX__": 0}])
402
+ assert tbl.view().to_records() == [
403
+ {"a": util.to_timestamp(datetime(2019, 7, 12))}
404
+ ]
405
+
406
+ def test_update_datetime_partial_implicit(self, util):
407
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
408
+ tbl.update([{"a": datetime(2019, 7, 12, 11, 0), "__INDEX__": 0}])
409
+ assert tbl.view().to_records() == [
410
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))}
411
+ ]
412
+
413
+ @mark.skip # We do not support numpy anymore.
414
+ def test_update_datetime_np_partial_implicit(self, util):
415
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
416
+ tbl.update([{"a": np.datetime64(datetime(2019, 7, 12, 11, 0)), "__INDEX__": 0}])
417
+ assert tbl.view().to_records() == [
418
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))}
419
+ ]
420
+
421
+ @mark.skip # We do not support numpy anymore.
422
+ def test_update_datetime_np_ts_partial_implicit(self, util):
423
+ tbl = Table({"a": [datetime(2019, 7, 11, 11, 0)]})
424
+ tbl.update([{"a": np.datetime64("2019-07-12T11:00"), "__INDEX__": 0}])
425
+ assert tbl.view().to_records() == [
426
+ {"a": util.to_timestamp(datetime(2019, 7, 12, 11, 0))}
427
+ ]
428
+
429
+ # implicit index
430
+
431
+ def test_update_implicit_index(self):
432
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
433
+ tbl = Table(data)
434
+ view = tbl.view()
435
+ tbl.update([{"__INDEX__": 0, "a": 3, "b": 15}])
436
+ assert view.to_records() == [{"a": 3, "b": 15}, {"a": 2, "b": 3}]
437
+
438
+ def test_update_implicit_index_dict_noop(self):
439
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
440
+ tbl = Table(data)
441
+ view = tbl.view()
442
+
443
+ # "b" does not exist in dataset, so no-op
444
+ tbl.update(
445
+ {
446
+ "__INDEX__": [0],
447
+ "a": [3],
448
+ }
449
+ )
450
+ assert view.to_records() == [{"a": 3, "b": 2}, {"a": 2, "b": 3}]
451
+
452
+ def test_update_implicit_index_dict_unset_with_null(self):
453
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
454
+ tbl = Table(data)
455
+ view = tbl.view()
456
+
457
+ # unset because "b" is null
458
+ tbl.update({"__INDEX__": [0], "a": [3], "b": [None]})
459
+ assert view.to_records() == [{"a": 3, "b": None}, {"a": 2, "b": 3}]
460
+
461
+ def test_update_implicit_index_multi(self):
462
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}, {"a": 4, "b": 5}]
463
+ tbl = Table(data)
464
+ view = tbl.view()
465
+ tbl.update(
466
+ [
467
+ {
468
+ "__INDEX__": 0,
469
+ "a": 3,
470
+ },
471
+ {"__INDEX__": 2, "a": 5},
472
+ ]
473
+ )
474
+ assert view.to_records() == [
475
+ {"a": 3, "b": 2},
476
+ {"a": 2, "b": 3},
477
+ {"a": 5, "b": 5},
478
+ ]
479
+
480
+ def test_update_implicit_index_symmetric(self):
481
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
482
+ tbl = Table(data)
483
+ view = tbl.view()
484
+ records = view.to_records(index=True)
485
+ idx = records[0]["__INDEX__"][0] # XXX: How should we grab this?
486
+ tbl.update([{"__INDEX__": idx, "a": 3}])
487
+ assert view.to_records() == [{"a": 3, "b": 2}, {"a": 2, "b": 3}]
488
+
489
+ def test_update_explicit_index(self):
490
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
491
+ tbl = Table(data, index="a")
492
+ view = tbl.view()
493
+ tbl.update([{"a": 1, "b": 3}])
494
+ assert view.to_records() == [{"a": 1, "b": 3}, {"a": 2, "b": 3}]
495
+
496
+ def test_update_explicit_index_multi(self):
497
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}, {"a": 3, "b": 4}]
498
+ tbl = Table(data, index="a")
499
+ view = tbl.view()
500
+ tbl.update([{"a": 1, "b": 3}, {"a": 3, "b": 5}])
501
+ assert view.to_records() == [
502
+ {"a": 1, "b": 3},
503
+ {"a": 2, "b": 3},
504
+ {"a": 3, "b": 5},
505
+ ]
506
+
507
+ def test_update_explicit_index_multi_append(self):
508
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}, {"a": 3, "b": 4}]
509
+ tbl = Table(data, index="a")
510
+ view = tbl.view()
511
+ tbl.update([{"a": 1, "b": 3}, {"a": 12, "b": 5}])
512
+ assert view.to_records() == [
513
+ {"a": 1, "b": 3},
514
+ {"a": 2, "b": 3},
515
+ {"a": 3, "b": 4},
516
+ {"a": 12, "b": 5},
517
+ ]
518
+
519
+ def test_update_explicit_index_multi_append_noindex(self):
520
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}, {"a": 3, "b": 4}]
521
+ tbl = Table(data, index="a")
522
+ view = tbl.view()
523
+ tbl.update([{"a": 1, "b": 3}, {"b": 5}])
524
+ assert view.to_records() == [
525
+ {"a": None, "b": 5},
526
+ {"a": 1, "b": 3},
527
+ {"a": 2, "b": 3},
528
+ {"a": 3, "b": 4},
529
+ ]
530
+
531
+ def test_update_implicit_index_with_explicit_unset(self):
532
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
533
+ tbl = Table(data, index="a")
534
+ view = tbl.view()
535
+ tbl.update([{"__INDEX__": 1, "b": 3}])
536
+ assert view.to_records() == [{"a": 1, "b": 3}, {"a": 2, "b": 3}]
537
+
538
+ def test_update_implicit_index_with_explicit_set(self):
539
+ data = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
540
+ tbl = Table(data, index="a")
541
+ view = tbl.view()
542
+ tbl.update(
543
+ [{"__INDEX__": 1, "a": 1, "b": 3}]
544
+ ) # should ignore re-specification of pkey
545
+ assert view.to_records() == [{"a": 1, "b": 3}, {"a": 2, "b": 3}]