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.

31 changes: 31 additions & 0 deletions examples/python-duckdb-virtual/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@finos/perspective-viewer/dist/css/themes.css" />
<style>
perspective-viewer {
position: absolute;
inset: 0;
}
</style>
</head>
<body>
<perspective-viewer id="viewer" ,> </perspective-viewer>
<script type="module">
import "/node_modules/@finos/perspective-viewer/dist/cdn/perspective-viewer.js";
import "/node_modules/@finos/perspective-viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js";
import "/node_modules/@finos/perspective-viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js";
import perspective from "/node_modules/@finos/perspective/dist/cdn/perspective.js";
const viewer = document.getElementById("viewer");

// Create a client that expects a Perspective server to accept
// Websocket connections at the specified URL.
const websocket = await perspective.websocket("ws://localhost:3000/websocket");
const table = await websocket.open_table("data_source_one");

// Load this in the `<perspective-viewer>`.
viewer.load(table);
</script>
</body>
</html>
22 changes: 22 additions & 0 deletions examples/python-duckdb-virtual/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "python-duckdb-virtual",
"private": true,
"version": "3.7.4",
"description": "An example of streaming a `perspective-python` server to the browser.",
"scripts": {
"start": "PYTHONPATH=../../python/perspective python3 server.py"
},
"keywords": [],
"license": "Apache-2.0",
"dependencies": {
"@finos/perspective": "workspace:^",
"@finos/perspective-viewer": "workspace:^",
"@finos/perspective-viewer-d3fc": "workspace:^",
"@finos/perspective-viewer-datagrid": "workspace:^",
"@finos/perspective-workspace": "workspace:^",
"superstore-arrow": "catalog:"
},
"devDependencies": {
"npm-run-all": "catalog:"
}
}
66 changes: 66 additions & 0 deletions examples/python-duckdb-virtual/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
# ┃ 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). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

from pathlib import Path

import duckdb
import perspective
import perspective.handlers.tornado
import perspective.virtual_servers.duckdb
import tornado.ioloop
import tornado.web
import tornado.websocket

from loguru import logger
from tornado.web import StaticFileHandler


INPUT_FILE = (
Path(__file__).parent.resolve()
/ "node_modules"
/ "superstore-arrow"
/ "superstore.parquet"
)


if __name__ == "__main__":
db = duckdb.connect(":memory:perspective")
db.sql(
f"""
SET default_null_order=NULLS_FIRST_ON_ASC_LAST_ON_DESC;
CREATE TABLE data_source_one AS
SELECT * FROM '{INPUT_FILE}';
""",
)

virtual_server = perspective.virtual_servers.duckdb.DuckDBVirtualServer(db)
app = tornado.web.Application(
[
(
r"/websocket",
perspective.handlers.tornado.PerspectiveTornadoHandler,
{"perspective_server": virtual_server},
),
(r"/node_modules/(.*)", StaticFileHandler, {"path": "../../node_modules/"}),
(
r"/(.*)",
StaticFileHandler,
{"path": "./", "default_filename": "index.html"},
),
],
websocket_max_message_size=100 * 1024 * 1024,
)

app.listen(3000)
logger.info("Listening on http://localhost:3000")
loop = tornado.ioloop.IOLoop.current()
loop.start()
25 changes: 25 additions & 0 deletions pnpm-lock.yaml

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
Loading
Loading