def test_async_call_loop_error_if_no_loop(self): tbl = Table({"a": int, "b": float, "c": str}) manager = PerspectiveManager() with raises(PerspectiveError): # loop not set - errors manager.call_loop(tbl.update, data) manager.set_loop_callback(TestAsync.loop.add_callback) manager.call_loop(tbl.update, data) manager.host(tbl) @syncify def _task(): return tbl.size() # subsequent calls to call_loop will work if loop_callback is set. assert _task() == 10 tbl.delete()
def benchmark_view_two_column_only_updates(self): """Benchmark how long it takes for each update to resolve fully, using the on update callback that forces resolution of updates across 25 views.""" table = Table(self._schema) views = [ table.view(split_by=["Category", "Sub-Category"]) for i in range(25) ] for v in views: v.on_update(empty_callback) update_data = self._get_update_data(1000) def resolve_update(): table.update(update_data) table.size() func = Benchmark(resolve_update, meta=make_meta("update", "two_column_only")) setattr(self, "update_two_column_only", func)
def test_view_computed_multiple_dependents(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view(computed_columns=[ { "column": "computed", "computed_function_name": "+", "inputs": ["a", "b"], }, { "column": "final", "computed_function_name": "pow2", "inputs": ["computed"], }, ]) assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "computed": [6, 8, 10, 12], "final": [36, 64, 100, 144], }
def test_viewer_delete_without_table(self): table = Table({"a": [1, 2, 3]}) viewer = PerspectiveViewer(plugin="x_bar", filters=[["a", "==", 2]]) viewer.load(table) assert viewer.filters == [["a", "==", 2]] viewer.delete(delete_table=False) assert viewer.table_name is not None assert viewer.table is not None assert viewer._perspective_view_name is not None assert viewer._view is not None assert viewer.filters == []
def test_manager_table_get_computation_input_types(self): post_callback = partial(self.validate_post, expected={ "id": 1, "data": ["string"] }) message = { "id": 1, "name": "table1", "cmd": "table_method", "method": "get_computation_input_types", "args": ["concat_comma"] } manager = PerspectiveManager() table = Table(data) view = table.view() manager.host_table("table1", table) manager.host_view("view1", view) manager._process(message, post_callback)
def test_manager_create_view_one(self): message = {"id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view", "config": {"row_pivots": ["a"]}} manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._views["view1"].to_dict() == { "__ROW_PATH__": [[], [1], [2], [3]], "a": [6, 1, 2, 3], "b": [3, 1, 1, 1] }
def test_to_arrow_start_end_col_end_row(self): data = { "a": [None, 1, None, 2, 3], "b": [1.5, 2.5, None, 3.5, None], "c": [1.5, 2.5, None, 4.5, None] } tbl = Table(data) assert tbl.schema() == {"a": int, "b": float, "c": float} arr = tbl.view().to_arrow(start_col=1, end_col=2, end_row=2) tbl2 = Table(arr) assert tbl2.view().to_dict() == tbl.view().to_dict(start_col=1, end_col=2, end_row=2)
def test_widget_load_table_server(self): table = Table({"a": np.arange(0, 50)}) widget = PerspectiveWidget(table, server=True) load_msg = widget._make_load_message() assert load_msg.to_dict() == { "id": -2, "type": "table", "data": { "table_name": widget.table_name } }
def test_manager_to_dict_unix_timestamps_from_datetime(self, sentinel): """The conversion from `datetime` to a Unix timestamp should not alter the timestamp in any way if both are in local time.""" s = sentinel(False) timestamp_data = {"a": [1580515140000]} schema = {"a": datetime} def handle_to_dict(msg): s.set(True) message = json.loads(msg) assert message["data"] == timestamp_data # convert back ts = datetime.fromtimestamp(message["data"]["a"][0] / 1000) assert ts == datetime(2020, 1, 31, 23, 59) message = { "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view" } manager = PerspectiveManager() table = Table(schema) table.update(timestamp_data) manager.host_table("table1", table) manager._process(message, self.post) to_dict_message = { "id": 2, "name": "view1", "cmd": "view_method", "method": "to_dict" } manager._process(to_dict_message, handle_to_dict) assert s.get() is True
def test_manager_view_computed_schema(self): post_callback = partial(self.validate_post, expected={ "id": 1, "data": { "abc": "float" } }) message = {"id": 1, "name": "view1", "cmd": "view_method", "method": "computed_schema", "args": [False]} manager = PerspectiveManager() table = Table(data) view = table.view(computed_columns=[ { "column": "abc", "computed_function_name": "+", "inputs": ["a", "a"] } ]) manager.host_table("table1", table) manager.host_view("view1", view) manager._process(message, post_callback)
def test_viewer_replace(self): table = Table({"a": [1, 2, 3]}) viewer = PerspectiveViewer() viewer.load(table) viewer.replace({"a": [4, 5, 6]}) assert viewer.table.size() == 3 assert viewer.table.schema() == { "a": int } assert viewer.table.view().to_dict() == { "a": [4, 5, 6] }
def test_locked_manager_create_view(self): message = { "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view" } manager = PerspectiveManager(lock=True) table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._get_view("view1").schema() == {"a": int, "b": str}
def test_manager_create_indexed_table(self): message = {"id": 1, "name": "table1", "cmd": "table", "args": [data], "options": {"index": "a"}} manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._tables["table1"].schema() == { "a": int, "b": str } assert manager._tables["table1"].get_index() == "a"
def test_manager_create_view_zero(self): message = { "id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view" } manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._views["view1"].num_rows() == 3
def test_view_month_of_year_date(self): table = Table({"a": [date(2020, i, 15) for i in range(1, 13)]}) view = table.view(expressions=['// bucket \n month_of_year("a")']) assert view.schema() == {"a": date, "bucket": str} assert view.to_columns() == { "a": [datetime(2020, i, 15) for i in range(1, 13)], "bucket": [ "01 January", "02 February", "03 March", "04 April", "05 May", "06 June", "07 July", "08 August", "09 September", "10 October", "11 November", "12 December", ], }
def test_to_arrow_start_end_row(self): data = {"a": [None, 1, None, 2, 3], "b": [1.5, 2.5, None, 3.5, None]} tbl = Table(data) assert tbl.schema() == {"a": int, "b": float} arr = tbl.view().to_arrow(start_row=2, end_row=3) tbl2 = Table(arr) assert tbl2.view().to_dict() == { "a": data["a"][2:3], "b": data["b"][2:3] }
def test_view_expression_append(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view(expressions=[ '// computed \n "a" + "b"', ]) assert view.schema() == {"a": int, "b": int, "computed": float} assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "computed": [6, 8, 10, 12], } table.update({"a": [5, 6], "b": [9, 10]}) assert view.to_columns() == { "a": [1, 2, 3, 4, 5, 6], "b": [5, 6, 7, 8, 9, 10], "computed": [6, 8, 10, 12, 14, 16], }
def test_exception_from_core_correct_types(self): tbl = Table({"a": [1, 2, 3]}) # `PerspectiveError` should be raised from the Python layer with raises(PerspectiveError) as ex: tbl.view() tbl.delete() assert str( ex.value ) == "Cannot delete a Table with active views still linked to it - call delete() on each view, and try again." with raises(PerspectiveCppError) as ex: tbl.view(row_pivots=["b"]) assert str(ex.value) == "Column b does not exist in schema."
def test_view_delete_with_scope(self): """Tests that `View`'s `__del__` method, when called by the Python reference counter, leaves an empty `Table` in a clean state. """ table = Table( { "id": int, "msg": str, "val": float }, index="id", ) table.view( computed_columns=[{ "column": "inverted", "computed_function_name": "invert", "inputs": ["val"], }], columns=["inverted"], ) table.update([{ "id": 1, "msg": "test", "val": 1.0, }])
def test_view_expression_multiple_views_should_all_clear(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view(expressions=[ '// computed \n "a" + "b"', ]) view2 = table.view(expressions=['// computed2 \n "a" - "b"']) assert view.schema() == {"a": int, "b": int, "computed": float} assert view2.schema() == {"a": int, "b": int, "computed2": float} assert view.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "computed": [6, 8, 10, 12], } assert view2.to_columns() == { "a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "computed2": [-4, -4, -4, -4], } table.clear() assert view.schema() == {"a": int, "b": int, "computed": float} assert view2.schema() == {"a": int, "b": int, "computed2": float} assert view.to_columns() == {} assert view2.to_columns() == {}
def test_async_multiple_managers_queue_process(self): tbl = Table({"a": int, "b": float, "c": str}) tbl2 = Table({"a": int, "b": float, "c": str}) manager = PerspectiveManager() manager2 = PerspectiveManager() manager.host_table("tbl", tbl) manager2.host_table("tbl2", tbl2) manager.set_loop_callback(TestAsync.loop.add_callback) manager2.set_loop_callback(TestAsync.loop.add_callback) @syncify def _update_task(): for i in range(5): tbl.update([data[i]]) tbl2.update([data[i]]) return tbl.size() assert _update_task() == 5 @syncify def _flush_to_process(): view = tbl2.view() records = view.to_records() for i in range(5): tbl2.update([data[i]]) view.delete() return records assert _flush_to_process() == data[:5] @syncify def _delete_task(): tbl2.delete() tbl.delete() _delete_task()
def test_async_multiple_managers_queue_process_multiple_ports(self): tbl = Table({"a": int, "b": float, "c": str}) tbl2 = Table({"a": int, "b": float, "c": str}) port_ids = [0] port_data = [{"a": 0, "b": 0, "c": "0"}] for i in range(10): port_id = tbl.make_port() port_id2 = tbl2.make_port() assert port_id == port_id2 port_ids.append(port_id) port_data.append({ "a": port_id, "b": port_id * 1.5, "c": str(port_id) }) manager = PerspectiveManager() manager2 = PerspectiveManager() manager.host_table("tbl", tbl) manager2.host_table("tbl2", tbl2) manager.set_loop_callback(TestAsync.loop.add_callback) manager2.set_loop_callback(TestAsync.loop.add_callback) @syncify def _task(): random.shuffle(port_ids) for port_id in port_ids: idx = port_id if port_id < len(port_ids) else len(port_ids) - 1 tbl.update([port_data[idx]], port_id=port_id) tbl2.update([port_data[idx]], port_id=port_id) return (tbl.size(), tbl2.size()) assert _task() == (11, 11)
def test_view_expression_delete_and_create_with_updates(self): table = Table({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) view = table.view(expressions=[ '// computed \n "a" + "b"', "upper(concat('abc', 'def'))" ]) assert view.schema() == { "a": int, "b": int, "computed": float, "upper(concat('abc', 'def'))": str } table.update({"a": [5, 6], "b": [9, 10]}) assert view.to_columns() == { "a": [1, 2, 3, 4, 5, 6], "b": [5, 6, 7, 8, 9, 10], "computed": [6, 8, 10, 12, 14, 16], "upper(concat('abc', 'def'))": ["ABCDEF" for _ in range(6)] } view.delete() view2 = table.view(expressions=[ '// computed2 \n "a" - "b"', ]) assert view2.schema() == {"a": int, "b": int, "computed2": float} table.update({"a": [5, 6], "b": [9, 10]}) table.update({"a": [5, 6], "b": [9, 10]}) assert view2.to_columns() == { "a": [1, 2, 3, 4, 5, 6, 5, 6, 5, 6], "b": [5, 6, 7, 8, 9, 10, 9, 10, 9, 10], "computed2": [-4, -4, -4, -4, -4, -4, -4, -4, -4, -4], }
def test_async_queue_process(self): tbl = Table({ "a": int, "b": float, "c": str }) manager = PerspectiveManager() manager._set_queue_process(TestAsync.wrapped_queue_process) manager.host(tbl) assert tbl.size() == 0 for i in range(5): tbl.update([data[i]]) table_id = tbl._table.get_id() pool = tbl._table.get_pool() assert _PerspectiveStateManager.TO_PROCESS == { table_id: pool } assert tbl.view().to_records() == data[:5] # should have flushed the process queue assert _PerspectiveStateManager.TO_PROCESS == {}
def test_to_arrow_date_symmetric(self): data = { "a": [date(2019, 7, 11), date(2016, 2, 29), date(2019, 12, 10)] } tbl = Table(data) assert tbl.schema() == {"a": date} arr = tbl.view().to_arrow() tbl2 = Table(arr) assert tbl2.schema() == tbl.schema() assert tbl2.view().to_dict() == { "a": [ datetime(2019, 7, 11), datetime(2016, 2, 29), datetime(2019, 12, 10) ] }
def test_widget_load_table(self): table = Table({"a": np.arange(0, 50)}) widget = PerspectiveWidget(table, plugin="x_bar") assert widget.plugin == "x_bar" load_msg = widget._make_load_message() assert load_msg.to_dict() == { "id": -2, "type": "table", "data": { "table_name": widget.table_name, "options": {} } }
def test_to_arrow_start_end_col_equiv_row(self): data = {"a": [None, 1, None, 2, 3], "b": [1.5, 2.5, None, 3.5, None]} tbl = Table(data) assert tbl.schema() == {"a": int, "b": float} arr = tbl.view().to_arrow(start_col=1, end_col=1, start_row=2, end_row=3) tbl2 = Table(arr) # start/end col is a range - thus start=end provides no columns assert tbl2.view().to_dict() == {}
def test_manager_clear_view_no_client_id(self): messages = [ {"id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view"}, {"id": 2, "table_name": "table1", "view_name": "view2", "cmd": "view"}, {"id": 3, "table_name": "table1", "view_name": "view3", "cmd": "view"} ] manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) for message in messages: manager._process(message, self.post) with raises(PerspectiveError): manager.clear_views(None)
def test_manager_clear_view(self): messages = [ {"id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view"}, {"id": 2, "table_name": "table1", "view_name": "view2", "cmd": "view"}, {"id": 3, "table_name": "table1", "view_name": "view3", "cmd": "view"} ] manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) for message in messages: manager._process(message, self.post, client_id=1) manager.clear_views(1) assert manager._views == {}
def test_manager_host_table(self): message = { "id": 1, "name": "table1", "cmd": "table_method", "method": "schema", "args": [] } manager = PerspectiveManager() table = Table(data) manager.host_table("table1", table) manager._process(message, self.post) assert manager._tables["table1"].schema() == {"a": int, "b": str}