Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion rust/perspective-client/src/rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ pub mod config;

#[rustfmt::skip]
#[allow(clippy::all)]
mod proto;
pub mod proto;

pub mod utils;

pub use crate::client::{Client, ClientHandler, Features, ReconnectCallback, SystemInfo};
use crate::proto::HostedTable;
pub use crate::session::{ProxySession, Session};
pub use crate::table::{
DeleteOptions, ExprValidationResult, Table, TableInitOptions, TableReadFormat, UpdateOptions,
Expand All @@ -66,6 +67,16 @@ pub mod vendor {
pub use paste;
}

impl From<&str> for HostedTable {
fn from(entity_id: &str) -> Self {
HostedTable {
entity_id: entity_id.to_string(),
index: None,
limit: None,
}
}
}

/// Assert that an implementation of domain language wrapper for [`Table`]
/// implements the expected API. As domain languages have different API needs,
/// a trait isn't useful for asserting that the entire API is implemented,
Expand Down
4 changes: 4 additions & 0 deletions rust/perspective-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,15 @@ python-config-rs = "0.1.2"
[dependencies]
perspective-client = { version = "3.8.0" }
perspective-server = { version = "3.8.0" }
bytes = "1.10.1"
chrono = "0.4"
macro_rules_attribute = "0.2.0"
async-lock = "2.5.0"
pollster = "0.3.0"
extend = "1.1.2"
indexmap = "2.2.6"
futures = "0.3.28"
serde = { version = "1.0" }
pyo3 = { version = "0.25.1", features = [
"experimental-async",
"extension-module",
Expand Down
2 changes: 2 additions & 0 deletions rust/perspective-python/perspective/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"ProxySession",
"AsyncClient",
"AsyncServer",
"VirtualServer",
"num_cpus",
"set_num_cpus",
"system_info",
Expand Down Expand Up @@ -351,6 +352,7 @@ def delete_callback():
Server,
AsyncServer,
AsyncClient,
VirtualServer,
# NOTE: these are classes without constructors,
# so we import them just for type hinting
Table, # noqa: F401
Expand Down
137 changes: 137 additions & 0 deletions rust/perspective-python/perspective/virtual_servers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
# ┃ Copyright (c) 2017, the Perspective Authors. ┃
# ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
# ┃ This file is part of the Perspective library, distributed under the terms ┃
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛


class VirtualSessionModel:
"""
An interface for implementing a Perspective `VirtualServer`. It operates
thusly:

- A table is selected by name (validated via `get_hosted_tables`).

- The UI will ask the model to create a temporary table with the results
of querying this table with a specific query `config`, a simple struct
which reflects the UI configurable fields (see `get_features`).

- The UI will query slices of the temporary table as it needs them to
render. This may be a rectangular slice, a whole column or the entire
set, and it is returned from teh model via a custom push-only
struct `PerspectiveColumn` for now, though in the future we will support
e.g. Polars and other arrow-native formats directly.

- The UI will delete its own temporary tables via `view_delete` but it is
ok for them to die intermittently, the UI will recover automatically.
"""

def get_features(self):
"""
[OPTIONAL] Toggle UI features through data model support. For example,
setting `"group_by": False` would hide the "Group By" UI control, as
well as prevent this field from appearing in `config` dicts later
provided to `table_make_view`.

This API defaults to just "columns", e.g. a simple flat datagrid in
which you can just scroll, select and format columns.

# Example

```python
return {
"group_by": True,
"split_by": True,
"sort": True,
"expressions": True,
"filter_ops": {
"integer": ["==", "<"],
},
"aggregates": {
"string": ["count"],
"float": ["count", "sum"],
},
}
```
"""

pass

def get_hosted_tables(self) -> list[str]:
"""
List of `Table` names available to query from.
"""

pass

def table_schema(self, table_name):
"""
Get the _Perspective Schema_ for a `Table`, a mapping of column name to
Perspective column types, a simplified set of six visually-relevant
types mapped from DuckDB's much richer type system. Optionally,
a model may also implement `view_schema` which describes temporary
tables, but for DuckDB this method is identical.
"""

pass

def table_columns_size(self, table_name, config):
pass

def table_size(self, table_name):
"""
Get a table's row count. Optionally, a model may also implement the
`view_size` method to get the row count for temporary tables, but for
DuckDB this method is identical.
"""

pass

def view_schema(self, view_name, config):
return self.table_schema(view_name)

def view_size(self, view_name):
return self.table_size(view_name)

def table_make_view(self, table_name, view_name, config):
"""
Create a temporary table `view_name` from the results of querying
`table_name` with a query configuration `config`.
"""

pass

def table_validate_expression(self, view_name, expression):
"""
[OPTIONAL] Given a temporary table `view_name`, validate the type of
a column expression string `expression`, or raise an error if the
expression is invalid. This is enabeld by `"expressions"` via
`get_features` and defaults to allow all expressions.
"""

pass

def view_delete(self, view_name):
"""
Delete a temporary table. The UI will do this automatically, and it
can recover.
"""

pass

def view_get_data(self, view_name, config, viewport, data):
"""
Serialize a rectangular slice `viewport` from temporary table
`view_name`, into the `PerspectiveColumn` serialization API injected
via `data`. The push-only `PerspectiveColumn` type can handle casting
Python types as input, but once a type is pushed to a column name it
must not be changed.
"""

pass
1 change: 1 addition & 0 deletions rust/perspective-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fn perspective(py: Python, m: &Bound<PyModule>) -> PyResult<()> {
m.add_class::<client::client_async::AsyncTable>()?;
m.add_class::<client::client_async::AsyncView>()?;
m.add_class::<client::proxy_session::ProxySession>()?;
m.add_class::<server::virtual_server_sync::PyVirtualServer>()?;
m.add("PerspectiveError", py.get_type::<PyPerspectiveError>())?;
m.add_function(wrap_pyfunction!(num_cpus, m)?)?;
m.add_function(wrap_pyfunction!(set_num_cpus, m)?)?;
Expand Down
1 change: 1 addition & 0 deletions rust/perspective-python/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod server_async;
mod server_sync;
pub(crate) mod session_async;
pub(crate) mod session_sync;
pub(crate) mod virtual_server_sync;

pub use server_async::*;
pub use server_sync::*;
Expand Down
Loading
Loading