Skip to content

Commit d4b457d

Browse files
committed
Virtual Server API
Signed-off-by: Andrew Stein <[email protected]>
1 parent c3bacb9 commit d4b457d

File tree

14 files changed

+1306
-3
lines changed

14 files changed

+1306
-3
lines changed

Cargo.lock

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/perspective-client/src/rust/lib.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ pub mod config;
4444

4545
#[rustfmt::skip]
4646
#[allow(clippy::all)]
47-
mod proto;
47+
pub mod proto;
4848

4949
pub mod utils;
5050

5151
pub use crate::client::{Client, ClientHandler, Features, ReconnectCallback, SystemInfo};
52+
use crate::proto::HostedTable;
5253
pub use crate::session::{ProxySession, Session};
5354
pub use crate::table::{
5455
DeleteOptions, ExprValidationResult, Table, TableInitOptions, TableReadFormat, UpdateOptions,
@@ -66,6 +67,16 @@ pub mod vendor {
6667
pub use paste;
6768
}
6869

70+
impl From<&str> for HostedTable {
71+
fn from(entity_id: &str) -> Self {
72+
HostedTable {
73+
entity_id: entity_id.to_string(),
74+
index: None,
75+
limit: None,
76+
}
77+
}
78+
}
79+
6980
/// Assert that an implementation of domain language wrapper for [`Table`]
7081
/// implements the expected API. As domain languages have different API needs,
7182
/// a trait isn't useful for asserting that the entire API is implemented,

rust/perspective-python/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,15 @@ python-config-rs = "0.1.2"
5959
[dependencies]
6060
perspective-client = { version = "3.8.0" }
6161
perspective-server = { version = "3.8.0" }
62+
bytes = "1.10.1"
63+
chrono = "0.4"
6264
macro_rules_attribute = "0.2.0"
6365
async-lock = "2.5.0"
6466
pollster = "0.3.0"
6567
extend = "1.1.2"
68+
indexmap = "2.2.6"
6669
futures = "0.3.28"
70+
serde = { version = "1.0" }
6771
pyo3 = { version = "0.25.1", features = [
6872
"experimental-async",
6973
"extension-module",

rust/perspective-python/perspective/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"ProxySession",
2222
"AsyncClient",
2323
"AsyncServer",
24+
"VirtualServer",
2425
"num_cpus",
2526
"set_num_cpus",
2627
"system_info",
@@ -351,6 +352,7 @@ def delete_callback():
351352
Server,
352353
AsyncServer,
353354
AsyncClient,
355+
VirtualServer,
354356
# NOTE: these are classes without constructors,
355357
# so we import them just for type hinting
356358
Table, # noqa: F401
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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+
class VirtualSessionModel:
15+
"""
16+
An interface for implementing a Perspective `VirtualServer`. It operates
17+
thusly:
18+
19+
- A table is selected by name (validated via `get_hosted_tables`).
20+
21+
- The UI will ask the model to create a temporary table with the results
22+
of querying this table with a specific query `config`, a simple struct
23+
which reflects the UI configurable fields (see `get_features`).
24+
25+
- The UI will query slices of the temporary table as it needs them to
26+
render. This may be a rectangular slice, a whole column or the entire
27+
set, and it is returned from teh model via a custom push-only
28+
struct `PerspectiveColumn` for now, though in the future we will support
29+
e.g. Polars and other arrow-native formats directly.
30+
31+
- The UI will delete its own temporary tables via `view_delete` but it is
32+
ok for them to die intermittently, the UI will recover automatically.
33+
"""
34+
35+
def get_features(self):
36+
"""
37+
[OPTIONAL] Toggle UI features through data model support. For example,
38+
setting `"group_by": False` would hide the "Group By" UI control, as
39+
well as prevent this field from appearing in `config` dicts later
40+
provided to `table_make_view`.
41+
42+
This API defaults to just "columns", e.g. a simple flat datagrid in
43+
which you can just scroll, select and format columns.
44+
45+
# Example
46+
47+
```python
48+
return {
49+
"group_by": True,
50+
"split_by": True,
51+
"sort": True,
52+
"expressions": True,
53+
"filter_ops": {
54+
"integer": ["==", "<"],
55+
},
56+
"aggregates": {
57+
"string": ["count"],
58+
"float": ["count", "sum"],
59+
},
60+
}
61+
```
62+
"""
63+
64+
pass
65+
66+
def get_hosted_tables(self) -> list[str]:
67+
"""
68+
List of `Table` names available to query from.
69+
"""
70+
71+
pass
72+
73+
def table_schema(self, table_name):
74+
"""
75+
Get the _Perspective Schema_ for a `Table`, a mapping of column name to
76+
Perspective column types, a simplified set of six visually-relevant
77+
types mapped from DuckDB's much richer type system. Optionally,
78+
a model may also implement `view_schema` which describes temporary
79+
tables, but for DuckDB this method is identical.
80+
"""
81+
82+
pass
83+
84+
def table_columns_size(self, table_name, config):
85+
pass
86+
87+
def table_size(self, table_name):
88+
"""
89+
Get a table's row count. Optionally, a model may also implement the
90+
`view_size` method to get the row count for temporary tables, but for
91+
DuckDB this method is identical.
92+
"""
93+
94+
pass
95+
96+
def view_schema(self, view_name, config):
97+
return self.table_schema(view_name)
98+
99+
def view_size(self, view_name):
100+
return self.table_size(view_name)
101+
102+
def table_make_view(self, table_name, view_name, config):
103+
"""
104+
Create a temporary table `view_name` from the results of querying
105+
`table_name` with a query configuration `config`.
106+
"""
107+
108+
pass
109+
110+
def table_validate_expression(self, view_name, expression):
111+
"""
112+
[OPTIONAL] Given a temporary table `view_name`, validate the type of
113+
a column expression string `expression`, or raise an error if the
114+
expression is invalid. This is enabeld by `"expressions"` via
115+
`get_features` and defaults to allow all expressions.
116+
"""
117+
118+
pass
119+
120+
def view_delete(self, view_name):
121+
"""
122+
Delete a temporary table. The UI will do this automatically, and it
123+
can recover.
124+
"""
125+
126+
pass
127+
128+
def view_get_data(self, view_name, config, viewport, data):
129+
"""
130+
Serialize a rectangular slice `viewport` from temporary table
131+
`view_name`, into the `PerspectiveColumn` serialization API injected
132+
via `data`. The push-only `PerspectiveColumn` type can handle casting
133+
Python types as input, but once a type is pushed to a column name it
134+
must not be changed.
135+
"""
136+
137+
pass

rust/perspective-python/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ fn perspective(py: Python, m: &Bound<PyModule>) -> PyResult<()> {
7171
m.add_class::<client::client_async::AsyncTable>()?;
7272
m.add_class::<client::client_async::AsyncView>()?;
7373
m.add_class::<client::proxy_session::ProxySession>()?;
74+
m.add_class::<server::virtual_server_sync::PyVirtualServer>()?;
7475
m.add("PerspectiveError", py.get_type::<PyPerspectiveError>())?;
7576
m.add_function(wrap_pyfunction!(num_cpus, m)?)?;
7677
m.add_function(wrap_pyfunction!(set_num_cpus, m)?)?;

rust/perspective-python/src/server/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod server_async;
1414
mod server_sync;
1515
pub(crate) mod session_async;
1616
pub(crate) mod session_sync;
17+
pub(crate) mod virtual_server_sync;
1718

1819
pub use server_async::*;
1920
pub use server_sync::*;

0 commit comments

Comments
 (0)