-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add aiohttp server and client implementation, add aiohttp server exam…
…ple, add aiohttp handler tests
- Loading branch information
Showing
44 changed files
with
1,112 additions
and
595 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# python-aiohttp | ||
|
||
This example contains a simple `perspective-python` folder that uses AIOHTTP to serve a static dataset to the user through various [data bindings](https://perspective.finos.org/docs/md/server.html): | ||
|
||
- `index.html`: a [client/server replicated](https://perspective.finos.org/docs/md/server.html#clientserver-replicated) setup that synchronizes the client and server data using Apache Arrow. | ||
- `server_mode.html`: a [server-only](https://perspective.finos.org/docs/md/server.html#server-only) setup that reads data and performs operations directly on the server using commands sent through the Websocket. | ||
- `client_server_editing`: a client-server replicated setup that also enables editing, where edits from multiple clients are applied properly to the server, and then synchronized back to the clients. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "python-aiohttp", | ||
"private": true, | ||
"version": "1.4.0", | ||
"description": "An example of editing a `perspective-python` server from the browser.", | ||
"scripts": { | ||
"start": "PYTHONPATH=../../python/perspective python3 server.py" | ||
}, | ||
"keywords": [], | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"@finos/perspective": "^1.4.0", | ||
"@finos/perspective-viewer": "^1.4.0", | ||
"@finos/perspective-viewer-d3fc": "^1.4.0", | ||
"@finos/perspective-viewer-datagrid": "^1.4.0", | ||
"@finos/perspective-workspace": "^1.4.0", | ||
"superstore-arrow": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"@finos/perspective-webpack-plugin": "^1.4.0", | ||
"npm-run-all": "^4.1.3", | ||
"rimraf": "^2.5.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
################################################################################ | ||
# | ||
# Copyright (c) 2019, the Perspective Authors. | ||
# | ||
# This file is part of the Perspective library, distributed under the terms of | ||
# the Apache License 2.0. The full license can be found in the LICENSE file. | ||
# | ||
import asyncio | ||
import os | ||
import os.path | ||
import logging | ||
import threading | ||
|
||
from aiohttp import web | ||
|
||
from perspective import Table, PerspectiveManager, PerspectiveAIOHTTPHandler | ||
|
||
|
||
here = os.path.abspath(os.path.dirname(__file__)) | ||
file_path = os.path.join( | ||
here, "..", "..", "node_modules", "superstore-arrow", "superstore.arrow" | ||
) | ||
|
||
|
||
def perspective_thread(manager): | ||
"""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) | ||
psp_loop.run_forever() | ||
|
||
|
||
def make_app(): | ||
manager = PerspectiveManager() | ||
|
||
thread = threading.Thread(target=perspective_thread, args=(manager,)) | ||
thread.daemon = True | ||
thread.start() | ||
|
||
async def websocket_handler(request): | ||
handler = PerspectiveAIOHTTPHandler(manager=manager, 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("/", "../python-tornado", show_index=True) | ||
return app | ||
|
||
|
||
if __name__ == "__main__": | ||
app = make_app() | ||
logging.critical("Listening on http://localhost:8080") | ||
web.run_app(app, host="0.0.0.0", port=8080) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,91 @@ | ||
################################################################################ | ||
# | ||
# Copyright (c) 2022, the Perspective Authors. | ||
# Copyright (c) 2019, the Perspective Authors. | ||
# | ||
# This file is part of the Perspective library, distributed under the terms of | ||
# the Apache License 2.0. The full license can be found in the LICENSE file. | ||
# | ||
|
||
import aiohttp | ||
import asyncio | ||
|
||
from .websocket import ( | ||
PerspectiveWebsocketClient, | ||
PerspectiveWebsocketConnection, | ||
Periodic, | ||
) | ||
|
||
|
||
class AIOHTTPPeriodic(Periodic): | ||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self._running = True | ||
|
||
async def _run(self): | ||
while self._running: | ||
await self._callback() | ||
await asyncio.sleep(self._interval) | ||
|
||
async def start(self): | ||
return asyncio.create_task(self._run()) | ||
|
||
async def stop(self): | ||
self._running = False | ||
|
||
|
||
class PerspectiveAIOHTTPWebsocketConnection(PerspectiveWebsocketConnection): | ||
def __init__(self, session=None): | ||
self._ws = None | ||
self._session = session or aiohttp.ClientSession() | ||
self._run = True | ||
|
||
async def _receive_messages(self): | ||
async for msg in self._ws: | ||
if msg.type == aiohttp.WSMsgType.TEXT: | ||
self._on_message(msg.data) | ||
elif msg.type == aiohttp.WSMsgType.BINARY: | ||
self._on_message(msg.data) | ||
elif msg.type == aiohttp.WSMsgType.CLOSE: | ||
return | ||
|
||
async def connect(self, url, on_message, max_message_size) -> None: | ||
self._ws_cm = self._session.ws_connect(url) | ||
self._ws = await self._ws_cm.__aenter__() | ||
self._on_message = on_message | ||
self._task = asyncio.create_task(self._receive_messages()) | ||
|
||
def periodic(self, callback, interval) -> Periodic: | ||
return AIOHTTPPeriodic(callback=callback, interval=interval) | ||
|
||
async def write(self, message, binary=False): | ||
if binary: | ||
return await self._ws.send_bytes(message) | ||
else: | ||
return await self._ws.send_str(message) | ||
|
||
async def close(self): | ||
try: | ||
self._task.cancel() | ||
await self._task | ||
except asyncio.CancelledError: | ||
... | ||
await self._ws.close() | ||
|
||
|
||
class PerspectiveAIOHTTPClient(PerspectiveWebsocketClient): | ||
def __init__(self, session=None): | ||
"""Create a `PerspectiveAIOHTTPClient` that interfaces with a Perspective server over a Websocket""" | ||
super(PerspectiveAIOHTTPClient, self).__init__( | ||
PerspectiveAIOHTTPWebsocketConnection(session=session) | ||
) | ||
|
||
|
||
async def websocket(url, session=None): | ||
"""Create a new websocket client at the given `url`. | ||
Args: | ||
session (:obj:`aiohttp.ClientSession`): An optional aiohtttp session | ||
""" | ||
client = PerspectiveAIOHTTPClient(session=session) | ||
await client.connect(url) | ||
return client |
Oops, something went wrong.