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.
- perspective/__init__.py +396 -0
- perspective/extension/finos-perspective-nbextension.json +5 -0
- perspective/handlers/__init__.py +11 -0
- perspective/handlers/aiohttp.py +61 -0
- perspective/handlers/starlette.py +55 -0
- perspective/handlers/tornado.py +184 -0
- perspective/perspective.pyd +0 -0
- perspective/templates/exported_widget.html.template +35 -0
- perspective/tests/__init__.py +11 -0
- perspective/tests/async/test_async_client.py +83 -0
- perspective/tests/async/test_websocket_client.py +124 -0
- perspective/tests/conftest.py +272 -0
- perspective/tests/core/__init__.py +11 -0
- perspective/tests/core/test_async.py +351 -0
- perspective/tests/multi_threaded/__init__.py +11 -0
- perspective/tests/multi_threaded/test_multi_threaded.py +201 -0
- perspective/tests/server/__init__.py +11 -0
- perspective/tests/server/test_server.py +1016 -0
- perspective/tests/server/test_session.py +110 -0
- perspective/tests/table/__init__.py +11 -0
- perspective/tests/table/arrow/date32.arrow +0 -0
- perspective/tests/table/arrow/date64.arrow +0 -0
- perspective/tests/table/arrow/dict.arrow +0 -0
- perspective/tests/table/arrow/dict_update.arrow +0 -0
- perspective/tests/table/arrow/int_float_str.arrow +0 -0
- perspective/tests/table/arrow/int_float_str_file.arrow +0 -0
- perspective/tests/table/arrow/int_float_str_update.arrow +0 -0
- perspective/tests/table/object_sequence.py +402 -0
- perspective/tests/table/test_column_paths.py +89 -0
- perspective/tests/table/test_delete.py +124 -0
- perspective/tests/table/test_exception.py +65 -0
- perspective/tests/table/test_leaks.py +54 -0
- perspective/tests/table/test_ports.py +178 -0
- perspective/tests/table/test_remove.py +102 -0
- perspective/tests/table/test_table.py +641 -0
- perspective/tests/table/test_table_arrow.py +503 -0
- perspective/tests/table/test_table_datetime.py +2409 -0
- perspective/tests/table/test_table_infer.py +201 -0
- perspective/tests/table/test_table_limit.py +45 -0
- perspective/tests/table/test_table_numpy.py +1022 -0
- perspective/tests/table/test_table_pandas.py +1018 -0
- perspective/tests/table/test_table_polars.py +251 -0
- perspective/tests/table/test_table_view_table.py +130 -0
- perspective/tests/table/test_to_arrow.py +417 -0
- perspective/tests/table/test_to_arrow_lz4.py +32 -0
- perspective/tests/table/test_to_format.py +1024 -0
- perspective/tests/table/test_to_polars.py +26 -0
- perspective/tests/table/test_update.py +545 -0
- perspective/tests/table/test_update_arrow.py +980 -0
- perspective/tests/table/test_update_pandas.py +211 -0
- perspective/tests/table/test_view.py +2261 -0
- perspective/tests/table/test_view_expression.py +1940 -0
- perspective/tests/test_dependencies.py +53 -0
- perspective/tests/viewer/__init__.py +11 -0
- perspective/tests/viewer/test_viewer.py +246 -0
- perspective/tests/widget/__init__.py +11 -0
- perspective/tests/widget/test_widget.py +278 -0
- perspective/tests/widget/test_widget_pandas.py +453 -0
- perspective/virtual_servers/__init__.py +134 -0
- perspective/virtual_servers/clickhouse.py +245 -0
- perspective/virtual_servers/duckdb.py +236 -0
- perspective/widget/__init__.py +349 -0
- perspective/widget/viewer/__init__.py +15 -0
- perspective/widget/viewer/validate.py +22 -0
- perspective/widget/viewer/viewer.py +343 -0
- perspective/widget/viewer/viewer_traitlets.py +101 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/install.json +5 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json +71 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js +2 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/253.5f5c9e80605aa4106a28.js.LICENSE.txt +25 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/523.c030af5d3c4f67ff83f6.js +1 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/remoteEntry.95a8ea1b44d96032833f.js +1 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/style.js +4 -0
- perspective_python-4.2.0.data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/static/third-party-licenses.json +16 -0
- perspective_python-4.2.0.dist-info/METADATA +27 -0
- perspective_python-4.2.0.dist-info/RECORD +79 -0
- perspective_python-4.2.0.dist-info/WHEEL +4 -0
- perspective_python-4.2.0.dist-info/licenses/LICENSE.md +193 -0
- perspective_python-4.2.0.dist-info/licenses/LICENSE_THIRDPARTY_cargo.yml +17395 -0
|
@@ -0,0 +1,251 @@
|
|
|
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 datetime import date, datetime
|
|
14
|
+
import numpy as np
|
|
15
|
+
import polars as pl
|
|
16
|
+
from pytest import mark
|
|
17
|
+
import perspective as psp
|
|
18
|
+
|
|
19
|
+
client = psp.Server().new_local_client()
|
|
20
|
+
Table = client.table
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def arrow_bytes_to_polars(view):
|
|
24
|
+
import pyarrow
|
|
25
|
+
|
|
26
|
+
with pyarrow.ipc.open_stream(pyarrow.BufferReader(view.to_arrow())) as reader:
|
|
27
|
+
return pl.from_dataframe(reader.read_pandas())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestTablePolars(object):
|
|
31
|
+
def test_empty_table(self):
|
|
32
|
+
tbl = Table([])
|
|
33
|
+
assert tbl.size() == 0
|
|
34
|
+
assert tbl.schema() == {}
|
|
35
|
+
|
|
36
|
+
def test_table_dataframe(self):
|
|
37
|
+
d = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
38
|
+
data = pl.DataFrame(d)
|
|
39
|
+
tbl = Table(data)
|
|
40
|
+
assert tbl.size() == 2
|
|
41
|
+
assert tbl.schema() == {"a": "integer", "b": "integer"}
|
|
42
|
+
assert tbl.view().to_records() == [
|
|
43
|
+
{"a": 1, "b": 2},
|
|
44
|
+
{"a": 3, "b": 4},
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
def test_table_lazyframe(self):
|
|
48
|
+
d = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
49
|
+
data = pl.DataFrame(d).lazy()
|
|
50
|
+
tbl = Table(data)
|
|
51
|
+
assert tbl.size() == 2
|
|
52
|
+
assert tbl.schema() == {"a": "integer", "b": "integer"}
|
|
53
|
+
assert tbl.view().to_records() == [
|
|
54
|
+
{"a": 1, "b": 2},
|
|
55
|
+
{"a": 3, "b": 4},
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
def test_table_dataframe_column_order(self):
|
|
59
|
+
d = [{"a": 1, "b": 2, "c": 3, "d": 4}, {"a": 3, "b": 4, "c": 5, "d": 6}]
|
|
60
|
+
data = pl.DataFrame(d).select(["b", "c", "a", "d"])
|
|
61
|
+
tbl = Table(data)
|
|
62
|
+
assert tbl.size() == 2
|
|
63
|
+
assert tbl.columns() == ["b", "c", "a", "d"]
|
|
64
|
+
|
|
65
|
+
def test_table_dataframe_selective_column_order(self):
|
|
66
|
+
d = [{"a": 1, "b": 2, "c": 3, "d": 4}, {"a": 3, "b": 4, "c": 5, "d": 6}]
|
|
67
|
+
data = pl.DataFrame(d).select(["b", "c", "a"])
|
|
68
|
+
tbl = Table(data)
|
|
69
|
+
assert tbl.size() == 2
|
|
70
|
+
assert tbl.columns() == ["b", "c", "a"]
|
|
71
|
+
|
|
72
|
+
def test_table_dataframe_does_not_mutate(self):
|
|
73
|
+
# make sure we don't mutate the dataframe that a user passes in
|
|
74
|
+
data = pl.DataFrame(
|
|
75
|
+
{
|
|
76
|
+
"a": [None, 1, None, 2],
|
|
77
|
+
"b": [1.5, None, 2.5, None],
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
assert data["a"].to_list() == [None, 1, None, 2]
|
|
81
|
+
assert data["b"].to_list() == [1.5, None, 2.5, None]
|
|
82
|
+
|
|
83
|
+
tbl = Table(data)
|
|
84
|
+
assert tbl.size() == 4
|
|
85
|
+
assert tbl.schema() == {"a": "integer", "b": "float"}
|
|
86
|
+
|
|
87
|
+
assert data["a"].to_list() == [None, 1, None, 2]
|
|
88
|
+
assert data["b"].to_list() == [1.5, None, 2.5, None]
|
|
89
|
+
|
|
90
|
+
def test_table_polars_from_schema_int(self):
|
|
91
|
+
data = [None, 1, None, 2, None, 3, 4]
|
|
92
|
+
df = pl.DataFrame({"a": data})
|
|
93
|
+
table = Table({"a": "integer"})
|
|
94
|
+
table.update(df)
|
|
95
|
+
assert table.view().to_columns()["a"] == data
|
|
96
|
+
|
|
97
|
+
def test_table_polars_from_schema_bool(self):
|
|
98
|
+
data = [True, False, True, False]
|
|
99
|
+
df = pl.DataFrame({"a": data})
|
|
100
|
+
table = Table({"a": "boolean"})
|
|
101
|
+
table.update(df)
|
|
102
|
+
assert table.view().to_columns()["a"] == data
|
|
103
|
+
|
|
104
|
+
def test_table_polars_from_schema_float(self):
|
|
105
|
+
data = [None, 1.5, None, 2.5, None, 3.5, 4.5]
|
|
106
|
+
df = pl.DataFrame({"a": data})
|
|
107
|
+
table = Table({"a": "float"})
|
|
108
|
+
table.update(df)
|
|
109
|
+
assert table.view().to_columns()["a"] == data
|
|
110
|
+
|
|
111
|
+
def test_table_polars_from_schema_float_all_nan(self):
|
|
112
|
+
data = [np.nan, np.nan, np.nan, np.nan]
|
|
113
|
+
df = pl.DataFrame({"a": data})
|
|
114
|
+
table = Table({"a": "float"})
|
|
115
|
+
table.update(df)
|
|
116
|
+
assert table.view().to_columns()["a"] == [None, None, None, None]
|
|
117
|
+
|
|
118
|
+
def test_table_polars_from_schema_float_to_int(self):
|
|
119
|
+
data = [None, 1.5, None, 2.5, None, 3.5, 4.5]
|
|
120
|
+
df = pl.DataFrame({"a": data})
|
|
121
|
+
table = Table({"a": "integer"})
|
|
122
|
+
table.update(df)
|
|
123
|
+
# truncates decimal
|
|
124
|
+
assert table.view().to_columns()["a"] == [None, 1, None, 2, None, 3, 4]
|
|
125
|
+
|
|
126
|
+
def test_table_polars_from_schema_int_to_float(self):
|
|
127
|
+
data = [None, 1, None, 2, None, 3, 4]
|
|
128
|
+
df = pl.DataFrame({"a": data})
|
|
129
|
+
table = Table({"a": "float"})
|
|
130
|
+
table.update(df)
|
|
131
|
+
assert table.view().to_columns()["a"] == [None, 1.0, None, 2.0, None, 3.0, 4.0]
|
|
132
|
+
|
|
133
|
+
def test_table_polars_from_schema_date(self, util):
|
|
134
|
+
data = [date(2019, 8, 15), None, date(2019, 8, 16)]
|
|
135
|
+
df = pl.DataFrame({"a": data})
|
|
136
|
+
table = Table({"a": "date"})
|
|
137
|
+
table.update(df)
|
|
138
|
+
assert table.view().to_columns()["a"] == [
|
|
139
|
+
util.to_timestamp(datetime(2019, 8, 15)),
|
|
140
|
+
None,
|
|
141
|
+
util.to_timestamp(datetime(2019, 8, 16)),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
def test_table_polars_from_schema_str(self):
|
|
145
|
+
data = ["a", None, "b", None, "c"]
|
|
146
|
+
df = pl.DataFrame({"a": data})
|
|
147
|
+
table = Table({"a": "string"})
|
|
148
|
+
table.update(df)
|
|
149
|
+
assert table.view().to_columns()["a"] == data
|
|
150
|
+
|
|
151
|
+
def test_table_polars_none(self):
|
|
152
|
+
data = [None, None, None]
|
|
153
|
+
df = pl.DataFrame({"a": data})
|
|
154
|
+
table = Table(df)
|
|
155
|
+
assert table.view().to_columns()["a"] == data
|
|
156
|
+
|
|
157
|
+
def test_table_polars_symmetric_table(self):
|
|
158
|
+
# make sure that updates are symmetric to table creation
|
|
159
|
+
df = pl.DataFrame({"a": [1, 2, 3, 4], "b": [1.5, 2.5, 3.5, 4.5]})
|
|
160
|
+
t1 = Table(df)
|
|
161
|
+
t2 = Table({"a": "integer", "b": "float"})
|
|
162
|
+
t2.update(df)
|
|
163
|
+
assert t1.view().to_columns() == {
|
|
164
|
+
"a": [1, 2, 3, 4],
|
|
165
|
+
"b": [1.5, 2.5, 3.5, 4.5],
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
def test_table_polars_symmetric_stacked_updates(self):
|
|
169
|
+
# make sure that updates are symmetric to table creation
|
|
170
|
+
df = pl.DataFrame({"a": [1, 2, 3, 4], "b": [1.5, 2.5, 3.5, 4.5]})
|
|
171
|
+
|
|
172
|
+
t1 = Table(df)
|
|
173
|
+
t1.update(df)
|
|
174
|
+
|
|
175
|
+
t2 = Table({"a": "integer", "b": "float"})
|
|
176
|
+
t2.update(df)
|
|
177
|
+
t2.update(df)
|
|
178
|
+
|
|
179
|
+
assert t1.view().to_columns() == {
|
|
180
|
+
"a": [1, 2, 3, 4, 1, 2, 3, 4],
|
|
181
|
+
"b": [1.5, 2.5, 3.5, 4.5, 1.5, 2.5, 3.5, 4.5],
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@mark.skip(reason="Not supported, polars doesnt like input")
|
|
185
|
+
def test_table_polars_transitive(self):
|
|
186
|
+
# serialized output -> table -> serialized output
|
|
187
|
+
records = {
|
|
188
|
+
"a": [1, 2, 3, 4],
|
|
189
|
+
"b": [1.5, 2.5, 3.5, 4.5],
|
|
190
|
+
"c": [np.nan, np.nan, "abc", np.nan],
|
|
191
|
+
"d": [None, True, None, False],
|
|
192
|
+
"e": [
|
|
193
|
+
float("nan"),
|
|
194
|
+
datetime(2019, 7, 11, 12, 30),
|
|
195
|
+
float("nan"),
|
|
196
|
+
datetime(2019, 7, 11, 12, 30),
|
|
197
|
+
],
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
df = pl.DataFrame(records, strict=False)
|
|
201
|
+
t1 = Table(df)
|
|
202
|
+
out1 = arrow_bytes_to_polars(t1.view(columns=["a", "b", "c", "d", "e"]))
|
|
203
|
+
t2 = Table(out1)
|
|
204
|
+
assert t1.schema() == t2.schema()
|
|
205
|
+
out2 = t2.view().to_columns()
|
|
206
|
+
assert t1.view().to_columns() == out2
|
|
207
|
+
|
|
208
|
+
# dtype=object should have correct inferred types
|
|
209
|
+
|
|
210
|
+
def test_table_polars_object_to_int(self):
|
|
211
|
+
df = pl.DataFrame({"a": [1, 2, None, 2, None, 3, 4]})
|
|
212
|
+
table = Table(df)
|
|
213
|
+
assert table.schema() == {"a": "integer"}
|
|
214
|
+
assert table.view().to_columns()["a"] == [1, 2, None, 2, None, 3, 4]
|
|
215
|
+
|
|
216
|
+
def test_table_polars_object_to_float(self):
|
|
217
|
+
df = pl.DataFrame({"a": [None, 1, None, 2, None, 3, 4]})
|
|
218
|
+
table = Table(df)
|
|
219
|
+
assert table.schema() == {"a": "integer"}
|
|
220
|
+
assert table.view().to_columns()["a"] == [None, 1.0, None, 2.0, None, 3.0, 4.0]
|
|
221
|
+
|
|
222
|
+
def test_table_polars_object_to_bool(self):
|
|
223
|
+
df = pl.DataFrame({"a": [True, False, True, False, True, False]})
|
|
224
|
+
table = Table(df)
|
|
225
|
+
assert table.schema() == {"a": "boolean"}
|
|
226
|
+
assert table.view().to_columns()["a"] == [True, False, True, False, True, False]
|
|
227
|
+
|
|
228
|
+
def test_table_polars_object_to_datetime(self):
|
|
229
|
+
df = pl.DataFrame(
|
|
230
|
+
{
|
|
231
|
+
"a": [
|
|
232
|
+
datetime(2019, 7, 11, 1, 2, 3),
|
|
233
|
+
datetime(2019, 7, 12, 1, 2, 3),
|
|
234
|
+
None,
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
table = Table(df)
|
|
240
|
+
assert table.schema() == {"a": "datetime"}
|
|
241
|
+
assert table.view().to_columns()["a"] == [
|
|
242
|
+
datetime(2019, 7, 11, 1, 2, 3).timestamp() * 1000,
|
|
243
|
+
datetime(2019, 7, 12, 1, 2, 3).timestamp() * 1000,
|
|
244
|
+
None,
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
def test_table_polars_object_to_str(self):
|
|
248
|
+
df = pl.DataFrame({"a": np.array(["abc", "def", None, "ghi"], dtype=object)})
|
|
249
|
+
table = Table(df)
|
|
250
|
+
assert table.schema() == {"a": "string"}
|
|
251
|
+
assert table.view().to_columns()["a"] == ["abc", "def", None, "ghi"]
|
|
@@ -0,0 +1,130 @@
|
|
|
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 pandas as pd
|
|
14
|
+
import numpy as np
|
|
15
|
+
from perspective import PerspectiveError
|
|
16
|
+
from datetime import date, datetime
|
|
17
|
+
from pytest import approx, mark, raises
|
|
18
|
+
|
|
19
|
+
import perspective as psp
|
|
20
|
+
|
|
21
|
+
client = psp.Server().new_local_client()
|
|
22
|
+
Table = client.table
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def date_timestamp(date):
|
|
26
|
+
return int(datetime.combine(date, datetime.min.time()).timestamp()) * 1000
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def compare_delta(received, expected):
|
|
30
|
+
"""Compare an arrow-serialized row delta by constructing a Table."""
|
|
31
|
+
tbl = Table(received)
|
|
32
|
+
assert tbl.view().to_columns() == expected
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestView(object):
|
|
36
|
+
def test_view_zero(self):
|
|
37
|
+
data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
38
|
+
tbl = Table(data)
|
|
39
|
+
view = tbl.view()
|
|
40
|
+
tbl2 = Table(view)
|
|
41
|
+
view2 = tbl2.view()
|
|
42
|
+
dimms = view2.dimensions()
|
|
43
|
+
assert dimms["num_view_rows"] == 2
|
|
44
|
+
assert dimms["num_view_columns"] == 2
|
|
45
|
+
assert view2.schema() == {"a": "integer", "b": "integer"}
|
|
46
|
+
assert view2.to_records() == data
|
|
47
|
+
|
|
48
|
+
def test_view_one(self):
|
|
49
|
+
data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
50
|
+
tbl = Table(data)
|
|
51
|
+
view = tbl.view(group_by=["a"])
|
|
52
|
+
tbl2 = Table(view)
|
|
53
|
+
view2 = tbl2.view()
|
|
54
|
+
|
|
55
|
+
dimms = view2.dimensions()
|
|
56
|
+
assert dimms["num_view_rows"] == 3
|
|
57
|
+
assert dimms["num_view_columns"] == 3
|
|
58
|
+
assert view2.schema() == {
|
|
59
|
+
"a (Group by 1)": "integer",
|
|
60
|
+
"a": "integer",
|
|
61
|
+
"b": "integer",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
assert view2.to_records() == [
|
|
65
|
+
{"a (Group by 1)": None, "a": 4, "b": 6},
|
|
66
|
+
{"a (Group by 1)": 1, "a": 1, "b": 2},
|
|
67
|
+
{"a (Group by 1)": 3, "a": 3, "b": 4},
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
def test_view_two(self):
|
|
71
|
+
data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
72
|
+
tbl = Table(data)
|
|
73
|
+
view = tbl.view(group_by=["a"], split_by=["b"])
|
|
74
|
+
tbl2 = Table(view)
|
|
75
|
+
view2 = tbl2.view()
|
|
76
|
+
dimms = view2.dimensions()
|
|
77
|
+
assert dimms["num_view_rows"] == 3
|
|
78
|
+
assert dimms["num_view_columns"] == 5
|
|
79
|
+
assert view2.schema() == {
|
|
80
|
+
"a (Group by 1)": "integer",
|
|
81
|
+
"2|a": "integer",
|
|
82
|
+
"2|b": "integer",
|
|
83
|
+
"4|a": "integer",
|
|
84
|
+
"4|b": "integer",
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
assert view2.to_records() == [
|
|
88
|
+
{
|
|
89
|
+
"a (Group by 1)": None,
|
|
90
|
+
"2|a": 1,
|
|
91
|
+
"2|b": 2,
|
|
92
|
+
"4|a": 3,
|
|
93
|
+
"4|b": 4,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"a (Group by 1)": 1,
|
|
97
|
+
"2|a": 1,
|
|
98
|
+
"2|b": 2,
|
|
99
|
+
"4|a": None,
|
|
100
|
+
"4|b": None,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"a (Group by 1)": 3,
|
|
104
|
+
"2|a": None,
|
|
105
|
+
"2|b": None,
|
|
106
|
+
"4|a": 3,
|
|
107
|
+
"4|b": 4,
|
|
108
|
+
},
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
def test_view_two_column_only(self):
|
|
112
|
+
data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
|
|
113
|
+
tbl = Table(data)
|
|
114
|
+
view = tbl.view(split_by=["b"])
|
|
115
|
+
tbl2 = Table(view)
|
|
116
|
+
view2 = tbl2.view()
|
|
117
|
+
dimms = view2.dimensions()
|
|
118
|
+
assert dimms["num_view_rows"] == 2
|
|
119
|
+
assert dimms["num_view_columns"] == 4
|
|
120
|
+
assert view2.schema() == {
|
|
121
|
+
"2|a": "integer",
|
|
122
|
+
"2|b": "integer",
|
|
123
|
+
"4|a": "integer",
|
|
124
|
+
"4|b": "integer",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
assert view2.to_records() == [
|
|
128
|
+
{"2|a": 1, "2|b": 2, "4|a": None, "4|b": None},
|
|
129
|
+
{"2|a": None, "2|b": None, "4|a": 3, "4|b": 4},
|
|
130
|
+
]
|