diff --git a/rust/perspective-python/Cargo.toml b/rust/perspective-python/Cargo.toml index c951aa1894..f7a0f370e5 100644 --- a/rust/perspective-python/Cargo.toml +++ b/rust/perspective-python/Cargo.toml @@ -57,9 +57,8 @@ futures = "0.3.28" pyo3 = { version = "0.21.2", features = [ "extension-module", "serde", - # "abi3-py38", + "abi3-py39", ] } -# pyo3-asyncio = { version = "0.20", features = ["attributes", "tokio-runtime"] } pythonize = "0.21.1" tracing = { version = ">=0.1.36" } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/rust/perspective-python/src/client/client_async.rs b/rust/perspective-python/src/client/client_async.rs deleted file mode 100644 index f808a90aaa..0000000000 --- a/rust/perspective-python/src/client/client_async.rs +++ /dev/null @@ -1,356 +0,0 @@ -// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ -// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ -// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ -// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ -// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ -// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ -// ┃ 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). ┃ -// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - -use perspective_client::{assert_table_api, assert_view_api}; -use pyo3::exceptions::PyValueError; -use pyo3::prelude::*; -use pyo3::types::{PyBytes, PyDict, PyFunction, PyString}; -use pyo3_asyncio::tokio::future_into_py; - -use super::python::*; -use crate::server::PyAsyncServer; - -#[pyclass] -pub struct PyAsyncClient(PyClient); - -#[pymethods] -impl PyAsyncClient { - #[doc = include_str!("../../docs/table.md")] - #[pyo3(signature = (input, limit=None, index=None, name=None))] - pub fn table<'a>( - &self, - py: Python<'a>, - input: Py, - limit: Option, - index: Option>, - name: Option>, - ) -> PyResult<&'a PyAny> { - let client = self.0.clone(); - future_into_py(py, async move { - let table = client.table(input, limit, index, name).await?; - Ok(PyAsyncTable(table)) - }) - } - - pub fn open_table<'a>(&self, py: Python<'a>, name: String) -> PyResult<&'a PyAny> { - let client = self.0.clone(); - future_into_py(py, async move { - let table = client.open_table(name).await?; - Ok(PyAsyncTable(table)) - }) - } - - pub fn get_hosted_table_names<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let client = self.0.clone(); - future_into_py(py, async move { client.get_hosted_table_names().await }) - } -} - -#[pyfunction] -#[pyo3(name = "create_async_client", signature = (loop_cb, server = None, client_id = None))] -pub fn create_async_client( - py: Python<'_>, - loop_cb: Py, - server: Option>, - client_id: Option, -) -> PyResult<&'_ PyAny> { - let server = server.and_then(|x| { - x.extract::(py) - .map_err(|e| { - tracing::error!("Failed to extract PyAsyncServer: {:?}", e); - }) - .ok() - }); - - future_into_py(py, async move { - Ok(PyAsyncClient(PyClient::new(server, client_id, loop_cb))) - }) -} - -#[pyclass] -#[repr(transparent)] -pub struct PyAsyncTable(PyTable); - -assert_table_api!(PyAsyncTable); - -#[pymethods] -impl PyAsyncTable { - #[doc = include_str!("../../docs/table/columns.md")] - pub fn columns<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.columns().await }) - } - - #[doc = include_str!("../../docs/table/schema.md")] - pub fn schema<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.schema().await }) - } - - #[doc = include_str!("../../docs/table/size.md")] - pub fn size<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.size().await }) - } - - #[doc = include_str!("../../docs/table/update.md")] - pub fn update<'a>( - &self, - py: Python<'a>, - input: Py, - format: Option, - port_id: Option, - ) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py( - py, - async move { table.update(input, format, port_id).await }, - ) - } - - #[doc = include_str!("../../docs/table/delete.md")] - pub fn delete<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.delete().await }) - } - - #[doc = include_str!("../../docs/table/clear.md")] - fn clear<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.clear().await }) - } - - #[doc = include_str!("../../docs/table/get_index.md")] - pub fn get_index<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { Ok(table.get_index().await) }) - } - - #[doc = include_str!("../../docs/table/get_limit.md")] - pub fn get_limit<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { Ok(table.get_limit().await) }) - } - - #[doc = include_str!("../../docs/table/make_port.md")] - pub fn make_port<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.make_port().await }) - } - - #[doc = include_str!("../../docs/table/on_delete.md")] - pub fn on_delete<'a>(&self, py: Python<'a>, callback: Py) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.on_delete(callback).await }) - } - - #[doc = include_str!("../../docs/table/remove_delete.md")] - pub fn remove_delete<'a>( - &self, - py: Python<'a>, - callback: Py, - ) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.remove_delete(callback).await }) - } - - #[doc = include_str!("../../docs/table/remove.md")] - pub fn remove<'a>(&self, py: Python<'a>, input: Py) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.remove(input).await }) - } - - #[doc = include_str!("../../docs/table/replace.md")] - pub fn replace<'a>(&self, py: Python<'a>, data: Py) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py(py, async move { table.replace(data).await }) - } - - #[doc = include_str!("../../docs/table/validate_expressions.md")] - pub fn validate_expressions<'a>( - &self, - py: Python<'a>, - expressions: Py, - ) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py( - py, - async move { table.validate_expressions(expressions).await }, - ) - } - - #[doc = include_str!("../../docs/table/view.md")] - #[pyo3(signature = (**config))] - pub fn view<'a>(&self, py: Python<'a>, config: Option>) -> PyResult<&'a PyAny> { - let table = self.0.clone(); - future_into_py( - py, - async move { Ok(PyAsyncView(table.view(config).await?)) }, - ) - } -} - -#[pyclass] -pub struct PyAsyncView(PyView); - -assert_view_api!(PyAsyncView); - -#[pymethods] -impl PyAsyncView { - #[doc = include_str!("../../docs/view/column_paths.md")] - pub fn column_paths<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.column_paths().await }) - } - - #[doc = include_str!("../../docs/view/to_columns_string.md")] - pub fn to_columns_string<'a>( - &self, - py: Python<'a>, - window: Option>, - ) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.to_columns_string(window).await }) - } - - #[doc = include_str!("../../docs/view/to_json_string.md")] - pub fn to_json_string<'a>( - &self, - py: Python<'a>, - window: Option>, - ) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.to_json_string(window).await }) - } - - #[doc = include_str!("../../docs/view/to_json.md")] - pub fn to_json<'a>(&self, py: Python<'a>, window: Option>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { - let json = view.to_json_string(window).await?; - Python::with_gil(move |py| { - let json_module = PyModule::import(py, "json")?; - json_module - .call_method1("loads", (json,)) - .map(|x| x.to_object(py)) - }) - }) - } - - #[doc = include_str!("../../docs/view/to_columns.md")] - #[pyo3(signature = (**window))] - pub fn to_columns<'a>( - &self, - py: Python<'a>, - window: Option>, - ) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { - let json = view.to_columns_string(window).await?; - Python::with_gil(move |py| { - let json_module = PyModule::import(py, "json")?; - json_module - .call_method1("loads", (json,)) - .map(|x| x.to_object(py)) - }) - }) - } - - #[doc = include_str!("../../docs/view/to_csv.md")] - #[pyo3(signature = (**window))] - pub fn to_csv<'a>(&self, py: Python<'a>, window: Option>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.to_csv(window).await }) - } - - #[doc = include_str!("../../docs/view/to_arrow.md")] - #[pyo3(signature = (**window))] - pub fn to_arrow<'a>(&self, py: Python<'a>, window: Option>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.to_arrow(window).await }) - } - - #[doc = include_str!("../../docs/view/delete.md")] - pub fn delete<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.delete().await }) - } - - #[doc = include_str!("../../docs/view/dimensions.md")] - pub fn dimensions<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.dimensions().await }) - } - - #[doc = include_str!("../../docs/view/expression_schema.md")] - pub fn expression_schema<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.expression_schema().await }) - } - - #[doc = include_str!("../../docs/view/get_config.md")] - pub fn get_config<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.get_config().await }) - } - - #[doc = include_str!("../../docs/view/get_min_max.md")] - pub fn get_min_max<'a>(&self, py: Python<'a>, column_name: String) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.get_min_max(column_name).await }) - } - - #[doc = include_str!("../../docs/view/num_rows.md")] - pub fn num_rows<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.num_rows().await }) - } - - #[doc = include_str!("../../docs/view/schema.md")] - pub fn schema<'a>(&self, py: Python<'a>) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.schema().await }) - } - - #[doc = include_str!("../../docs/view/on_delete.md")] - pub fn on_delete<'a>(&self, py: Python<'a>, callback: Py) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.on_delete(callback).await }) - } - - #[doc = include_str!("../../docs/view/remove_delete.md")] - pub fn remove_delete<'a>( - &self, - py: Python<'a>, - callback: Py, - ) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.remove_delete(callback).await }) - } - - #[doc = include_str!("../../docs/view/on_update.md")] - pub fn on_update<'a>( - &self, - py: Python<'a>, - callback: Py, - mode: Option, - ) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.on_update(callback, mode).await }) - } - - #[doc = include_str!("../../docs/view/remove_update.md")] - pub fn remove_update<'a>(&self, py: Python<'a>, callback_id: u32) -> PyResult<&'a PyAny> { - let view = self.0.clone(); - future_into_py(py, async move { view.remove_update(callback_id).await }) - } -} diff --git a/rust/perspective-python/src/client/client_sync.rs b/rust/perspective-python/src/client/client_sync.rs index d15740c828..6a730503fa 100644 --- a/rust/perspective-python/src/client/client_sync.rs +++ b/rust/perspective-python/src/client/client_sync.rs @@ -40,7 +40,7 @@ pub struct PySyncClient(PyClient); #[pymethods] impl PySyncClient { #[new] - pub fn new(callback: Py) -> PyResult { + pub fn new(callback: Py) -> PyResult { let client = PyClient::new(callback); Ok(PySyncClient(client)) } @@ -77,7 +77,7 @@ impl PySyncClient { } #[doc = include_str!("../../docs/client/set_loop_callback.md")] - pub fn set_loop_callback(&self, loop_cb: Py) -> PyResult<()> { + pub fn set_loop_callback(&self, loop_cb: Py) -> PyResult<()> { self.0.set_loop_cb(loop_cb).block_on() } } @@ -119,7 +119,7 @@ impl PySyncTable { } #[doc = include_str!("../../docs/table/on_delete.md")] - fn on_delete(&self, callback: Py) -> PyResult { + fn on_delete(&self, callback: Py) -> PyResult { let table = self.0.clone(); table.on_delete(callback).block_on() } @@ -131,7 +131,7 @@ impl PySyncTable { } #[doc = include_str!("../../docs/table/remove_delete.md")] - fn remove_delete(&self, callback: Py) -> PyResult<()> { + fn remove_delete(&self, callback: Py) -> PyResult<()> { let table = self.0.clone(); table.remove_delete(callback).block_on() } @@ -292,17 +292,17 @@ impl PySyncView { } #[doc = include_str!("../../docs/view/on_delete.md")] - fn on_delete(&self, callback: Py) -> PyResult { + fn on_delete(&self, callback: Py) -> PyResult { self.0.on_delete(callback).block_on() } #[doc = include_str!("../../docs/view/remove_delete.md")] - fn remove_delete(&self, callback: Py) -> PyResult<()> { + fn remove_delete(&self, callback: Py) -> PyResult<()> { self.0.remove_delete(callback).block_on() } #[doc = include_str!("../../docs/view/on_update.md")] - fn on_update(&self, callback: Py, mode: Option) -> PyResult { + fn on_update(&self, callback: Py, mode: Option) -> PyResult { self.0.on_update(callback, mode).block_on() } diff --git a/rust/perspective-python/src/client/mod.rs b/rust/perspective-python/src/client/mod.rs index d3a668aec4..af6fe27cd4 100644 --- a/rust/perspective-python/src/client/mod.rs +++ b/rust/perspective-python/src/client/mod.rs @@ -10,7 +10,6 @@ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ -// pub mod client_async; pub mod client_sync; mod python; diff --git a/rust/perspective-python/src/client/python.rs b/rust/perspective-python/src/client/python.rs index 3bcf029631..a666c36e17 100644 --- a/rust/perspective-python/src/client/python.rs +++ b/rust/perspective-python/src/client/python.rs @@ -25,13 +25,13 @@ use perspective_client::{ use pyo3::create_exception; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; -use pyo3::types::{PyBytes, PyDict, PyFunction, PyList, PyString}; +use pyo3::types::{PyAny, PyBytes, PyDict, PyList, PyString}; use pythonize::depythonize_bound; #[derive(Clone)] pub struct PyClient { client: Client, - loop_cb: Arc>>>, + loop_cb: Arc>>>, } #[extend::ext] @@ -259,7 +259,7 @@ fn pandas_to_arrow_bytes<'py>( } impl PyClient { - pub fn new(handle_request: Py) -> Self { + pub fn new(handle_request: Py) -> Self { let client = Client::new_with_callback({ move |msg| { clone!(handle_request); @@ -351,7 +351,7 @@ impl PyClient { self.client.get_hosted_table_names().await.into_pyerr() } - pub async fn set_loop_cb(&self, loop_cb: Py) -> PyResult<()> { + pub async fn set_loop_cb(&self, loop_cb: Py) -> PyResult<()> { *self.loop_cb.write().await = Some(loop_cb); Ok(()) } @@ -394,7 +394,7 @@ impl PyTable { self.table.make_port().await.into_pyerr() } - pub async fn on_delete(&self, callback_py: Py) -> PyResult { + pub async fn on_delete(&self, callback_py: Py) -> PyResult { let loop_cb = self.client.loop_cb.read().await.clone(); let callback = { let callback_py = callback_py.clone(); @@ -417,7 +417,7 @@ impl PyTable { Ok(callback_id) } - pub async fn remove_delete(&self, callback: Py) -> PyResult<()> { + pub async fn remove_delete(&self, callback: Py) -> PyResult<()> { let callback_id = Python::with_gil(|py| callback.getattr(py, PSP_CALLBACK_ID)?.extract(py))?; self.table.remove_delete(callback_id).await.into_pyerr() @@ -559,7 +559,7 @@ impl PyView { .collect()) } - pub async fn on_delete(&self, callback_py: Py) -> PyResult { + pub async fn on_delete(&self, callback_py: Py) -> PyResult { let callback = { let callback_py = callback_py.clone(); let loop_cb = self.client.loop_cb.read().await.clone(); @@ -583,13 +583,13 @@ impl PyView { Ok(callback_id) } - pub async fn remove_delete(&self, callback: Py) -> PyResult<()> { + pub async fn remove_delete(&self, callback: Py) -> PyResult<()> { let callback_id = Python::with_gil(|py| callback.getattr(py, PSP_CALLBACK_ID)?.extract(py))?; self.view.remove_delete(callback_id).await.into_pyerr() } - pub async fn on_update(&self, callback: Py, mode: Option) -> PyResult { + pub async fn on_update(&self, callback: Py, mode: Option) -> PyResult { let loop_cb = self.client.loop_cb.read().await.clone(); let callback = move |x: ViewOnUpdateResp| { let loop_cb = loop_cb.clone(); diff --git a/rust/perspective-python/src/server/server_sync.rs b/rust/perspective-python/src/server/server_sync.rs index 3695673e75..d1123a2100 100644 --- a/rust/perspective-python/src/server/server_sync.rs +++ b/rust/perspective-python/src/server/server_sync.rs @@ -16,7 +16,7 @@ use perspective_server::{Server, Session, SessionHandler}; use pollster::FutureExt; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; -use pyo3::types::{PyBytes, PyFunction}; +use pyo3::types::{PyAny, PyBytes}; #[pyclass] #[derive(Clone)] @@ -31,7 +31,7 @@ pub struct PySyncServer { } #[derive(Clone)] -struct PyConnection(Py); +struct PyConnection(Py); impl SessionHandler for PyConnection { async fn send_response<'a>( @@ -50,7 +50,7 @@ impl PySyncServer { Self::default() } - pub fn new_session(&self, _py: Python, response_cb: Py) -> PySyncSession { + pub fn new_session(&self, _py: Python, response_cb: Py) -> PySyncSession { let session = self .server .new_session(PyConnection(response_cb))