Skip to content

Commit

Permalink
Fix tornado, aiohttp and starlette handlers
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Stein <steinlink@gmail.com>

# Conflicts:
#	examples/python-tornado/server.py
#	rust/perspective-python/Cargo.toml
#	rust/perspective-python/perspective/__init__.py
  • Loading branch information
texodus committed Jul 9, 2024
1 parent add1f8f commit 38cdca5
Show file tree
Hide file tree
Showing 65 changed files with 928 additions and 1,246 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ jobs:
env:
PACKAGE: "perspective-python"
PSP_USE_CCACHE: 1
CI: 1

# - name: Docs Build
# run: pnpm run docs
Expand Down
15 changes: 4 additions & 11 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions cpp/perspective/src/cpp/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1834,10 +1834,10 @@ ProtoServer::_handle_request(std::uint32_t client_id, const Request& req) {

auto num_view_columns = 0;
const auto real_size = config->get_columns().size();
if (ncols > 0 && real_size > 0){
if (ncols > 0 && real_size > 0) {
num_view_columns = ncols
- (ncols / (config->get_columns().size() + num_hidden))
* num_hidden;
- (ncols / (config->get_columns().size() + num_hidden))
* num_hidden;
}

view_dims->set_num_view_columns(num_view_columns);
Expand Down
30 changes: 14 additions & 16 deletions examples/python-aiohttp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

from aiohttp import web

from perspective import Table, PerspectiveManager, PerspectiveAIOHTTPHandler
from perspective import Server
from perspective.handlers import PerspectiveAIOHTTPHandler


here = os.path.abspath(os.path.dirname(__file__))
Expand All @@ -27,37 +28,34 @@
)


def perspective_thread(manager):
def perspective_thread(server):
"""Perspective application thread starts its own event loop, and
adds the table with the name "data_source_one", which will be used
in the front-end."""
psp_loop = asyncio.new_event_loop()
manager.set_loop_callback(psp_loop.call_soon_threadsafe)
with open(file_path, mode="rb") as file:
table = Table(file.read(), index="Row ID")
manager.host_table("data_source_one", table)
client = server.new_client(loop_callback=psp_loop.call_soon_threadsafe)

def init():
with open(file_path, mode="rb") as file:
client.table(file.read(), index="Row ID", name="data_source_one")

psp_loop.call_soon_threadsafe(init)
psp_loop.run_forever()


def make_app():
manager = PerspectiveManager()

thread = threading.Thread(target=perspective_thread, args=(manager,))
server = Server()
thread = threading.Thread(target=perspective_thread, args=(server,))
thread.daemon = True
thread.start()

async def websocket_handler(request):
handler = PerspectiveAIOHTTPHandler(manager=manager, request=request)
handler = PerspectiveAIOHTTPHandler(perspective_server=server, request=request)
await handler.run()

app = web.Application()
app.router.add_get("/websocket", websocket_handler)
app.router.add_static(
"/node_modules/@finos", "../../node_modules/@finos", follow_symlinks=True
)
app.router.add_static(
"/node_modules", "../../node_modules/@finos", follow_symlinks=True
)
app.router.add_static("/node_modules", "../../node_modules/", follow_symlinks=True)
app.router.add_static("/", "../python-tornado", show_index=True)
return app

Expand Down
32 changes: 18 additions & 14 deletions examples/python-starlette/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
from starlette.responses import FileResponse
from starlette.staticfiles import StaticFiles

from perspective import Table, PerspectiveManager, PerspectiveStarletteHandler
from perspective import Server
from perspective.handlers import PerspectiveStarletteHandler


here = os.path.abspath(os.path.dirname(__file__))
Expand All @@ -31,41 +32,44 @@
)


def static_nodemodules_handler(rest_of_path):
if rest_of_path.startswith("@finos"):
return FileResponse("../../node_modules/{}".format(rest_of_path))
return FileResponse("../../node_modules/@finos/{}".format(rest_of_path))
def static_node_modules_handler(rest_of_path):
return FileResponse("../../node_modules/{}".format(rest_of_path))


def perspective_thread(manager):
def perspective_thread(server):
"""Perspective application thread starts its own event loop, and
adds the table with the name "data_source_one", which will be used
in the front-end."""
psp_loop = asyncio.new_event_loop()
manager.set_loop_callback(psp_loop.call_soon_threadsafe)
with open(file_path, mode="rb") as file:
table = Table(file.read(), index="Row ID")
manager.host_table("data_source_one", table)
client = server.new_client(loop_callback=psp_loop.call_soon_threadsafe)

def init():
with open(file_path, mode="rb") as file:
client.table(file.read(), index="Row ID", name="data_source_one")

psp_loop.call_soon_threadsafe(init)
psp_loop.run_forever()


def make_app():
manager = PerspectiveManager()
server = Server()

thread = threading.Thread(target=perspective_thread, args=(manager,))
thread = threading.Thread(target=perspective_thread, args=(server,))
thread.daemon = True
thread.start()

async def websocket_handler(websocket: WebSocket):
handler = PerspectiveStarletteHandler(manager=manager, websocket=websocket)
handler = PerspectiveStarletteHandler(
perspective_server=server, websocket=websocket
)
await handler.run()

# static_html_files = StaticFiles(directory="../python-tornado", html=True)
static_html_files = StaticFiles(directory="../python-tornado", html=True)

app = FastAPI()
app.add_api_websocket_route("/websocket", websocket_handler)
app.get("/node_modules/{rest_of_path:path}")(static_nodemodules_handler)
app.get("/node_modules/{rest_of_path:path}")(static_node_modules_handler)
app.mount("/", static_html_files)

app.add_middleware(
Expand Down
9 changes: 4 additions & 5 deletions examples/python-tornado/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

<script type="module" src="http://localhost:8082/node_modules/@finos/perspective-viewer/dist/cdn/perspective-viewer.js"></script>
<script type="module" src="http://localhost:8082/node_modules/@finos/perspective-viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js"></script>
<script type="module" src="http://localhost:8082/node_modules/@finos/perspective-viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js"></script>
<script type="module" src="/node_modules/@finos/perspective-viewer/dist/cdn/perspective-viewer.js"></script>
<script type="module" src="/node_modules/@finos/perspective-viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js"></script>
<script type="module" src="/node_modules/@finos/perspective-viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js"></script>

<link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@finos/perspective-viewer/dist/css/themes.css" />

Expand Down Expand Up @@ -43,11 +43,10 @@
* Use `perspective.websocket` to set up Perspective in server or
* distributed modes.
*/
const websocket = await perspective.websocket("ws://localhost:8082/websocket");
const websocket = await perspective.websocket("ws://localhost:8080/websocket");

const server_table = await websocket.open_table("data_source_one");


// const table = worker.table(server_table);

// Load the local table in the `<perspective-viewer>`.
Expand Down
62 changes: 9 additions & 53 deletions examples/python-tornado/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@
import tornado.websocket
import tornado.web
import tornado.ioloop
import threading
import concurrent.futures


from perspective import PySyncClient, PySyncServer

# from perspective.core.globalpsp import shared_client
# from perspective.handlers.new_tornado import PerspectiveTornadoHandler

import perspective

here = os.path.abspath(os.path.dirname(__file__))
file_path = os.path.join(
Expand All @@ -50,21 +44,20 @@ def perspective_thread(manager, table):


async def init_table(client):
# client = PySyncClient(handle_request)
with open(file_path, mode="rb") as file:
data = file.read()
table = client.table(data, name="data_source_one")
for _ in range(10):
table.update(data)


def make_app(server):
def make_app(perspective_server):
return tornado.web.Application(
[
(
r"/websocket",
PerspectiveTornadoHandler,
{"psp_server": server},
perspective.handlers.tornado.PerspectiveTornadoHandler,
{"perspective_server": perspective_server},
),
(
r"/node_modules/(.*)",
Expand All @@ -80,49 +73,12 @@ def make_app(server):
)


class PerspectiveTornadoHandler(tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
psp_server = kwargs.pop("psp_server")
super().__init__(*args, **kwargs)
self.server = psp_server

def open(self):
def inner(msg):
self.write_message(msg, binary=True)

self.session = self.server.new_session(inner)

def on_close(self) -> None:
self.session.close()
del self.session

def on_message(self, msg: bytes):
if not isinstance(msg, bytes):
return

self.session.handle_request(msg)
self.session.poll()


def new_client(server):
def handle_sync_client(bytes):
sync_session.handle_request(bytes)
sync_session.poll()

def handle_new_session(bytes):
local_sync_client.handle_response(bytes)

sync_session = server.new_session(handle_new_session)
local_sync_client = PySyncClient(handle_sync_client)
return local_sync_client


if __name__ == "__main__":
psp_server = PySyncServer()
app = make_app(psp_server)
app.listen(8082)
client = new_client(psp_server)
logging.critical("Listening on http://localhost:8082")
perspective_server = perspective.Server()
app = make_app(perspective_server)
app.listen(8080)
logging.critical("Listening on http://localhost:8080")
loop = tornado.ioloop.IOLoop.current()
client = perspective_server.new_client(loop_callback=loop.add_callback)
loop.call_later(0, init_table, client)
loop.start()
2 changes: 1 addition & 1 deletion rust/perspective-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ default-features = false
features = ["prost-derive", "std"]

[dependencies.ts-rs]
version = "8.1.0"
version = "9.0.1"
features = ["serde-json-impl", "no-serde-warnings"]
1 change: 1 addition & 0 deletions rust/perspective-client/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn prost_build() -> Result<()> {
prost_build::Config::new()
// .bytes(["ViewToArrowResp.arrow", "from_arrow"])
.type_attribute("ViewOnUpdateResp", "#[derive(ts_rs::TS)]")
.type_attribute("ViewOnUpdateResp", "#[ts(as = \"Vec::<u8>\")]")
.field_attribute("ViewOnUpdateResp.delta", "#[serde(with = \"serde_bytes\")]")
.field_attribute("ViewToArrowResp.arrow", "#[serde(skip)]")
.field_attribute("from_arrow", "#[serde(skip)]")
Expand Down
2 changes: 1 addition & 1 deletion rust/perspective-js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11"
serde_json = { version = "1.0.107", features = ["raw_value"] }
serde-wasm-bindgen = "0.6.0"
ts-rs = { version = "8.1.0", features = [
ts-rs = { version = "9.0.1", features = [
"serde-json-impl",
"no-serde-warnings",
] }
Expand Down
13 changes: 9 additions & 4 deletions rust/perspective-python/bench/tornado/server/new_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import os.path
import time
from perspective.core.globalpsp import shared_client
from perspective.handlers.new_tornado import PerspectiveTornadoHandler
from perspective.handlers.tornado import PerspectiveTornadoHandler
from tornado.websocket import websocket_connect
import tornado
import threading
Expand Down Expand Up @@ -55,6 +55,7 @@ async def session():

# Server


def make_app(queue: multiprocessing.Queue, port: int = 8080):
"""Make a tornado server for the thread local event loop."""
app = tornado.web.Application(
Expand All @@ -67,8 +68,11 @@ def make_app(queue: multiprocessing.Queue, port: int = 8080):
(
r"/(.*)",
tornado.web.StaticFileHandler,
{"path": os.path.join(os.path.dirname(__file__), "../client/dist"), "default_filename": "index.html"},
)
{
"path": os.path.join(os.path.dirname(__file__), "../client/dist"),
"default_filename": "index.html",
},
),
]
)

Expand All @@ -89,6 +93,7 @@ def start_server(queue: multiprocessing.Queue):
server_process.start()
return server_process


def main():
"""Runs an entire test scenario, server and client pool, then prints run
statistics. The server under test can be run in `sync` or `async` modes.
Expand All @@ -105,4 +110,4 @@ def main():


if __name__ == "__main__":
main()
main()
Loading

0 comments on commit 38cdca5

Please sign in to comment.