perspective_python/client/
client_sync.rs1use std::collections::HashMap;
14use std::future::Future;
15
16use macro_rules_attribute::apply;
17#[cfg(doc)]
18use perspective_client::{Schema, TableInitOptions, UpdateOptions, config::ViewConfigUpdate};
19use perspective_client::{assert_table_api, assert_view_api};
20use pyo3::exceptions::PyTypeError;
21use pyo3::marker::Ungil;
22use pyo3::prelude::*;
23use pyo3::types::*;
24
25use super::client_async::*;
26use crate::inherit_doc;
27use crate::server::PySyncServer;
28
29pub(crate) trait PyFutureExt: Future {
30 fn py_block_on(self, py: Python<'_>) -> Self::Output
31 where
32 Self: Sized + Send,
33 Self::Output: Ungil,
34 {
35 use pollster::FutureExt;
36 py.allow_threads(move || self.block_on())
37 }
38}
39
40impl<F: Future> PyFutureExt for F {}
41
42#[apply(inherit_doc)]
43#[inherit_doc = "client.md"]
44#[pyclass(subclass, module = "perspective")]
45pub struct Client(pub(crate) AsyncClient);
46
47#[pymethods]
48impl Client {
49 #[new]
50 #[pyo3(signature = (handle_request, close_cb=None))]
51 pub fn new(handle_request: Py<PyAny>, close_cb: Option<Py<PyAny>>) -> PyResult<Self> {
52 let client = AsyncClient::new(handle_request, close_cb);
53 Ok(Client(client))
54 }
55
56 #[staticmethod]
57 #[pyo3(signature = (server, loop_callback=None))]
58 pub fn from_server(
59 py: Python<'_>,
60 server: Py<PySyncServer>,
61 loop_callback: Option<Py<PyAny>>,
62 ) -> PyResult<Self> {
63 server.borrow(py).new_local_client(py, loop_callback)
64 }
65
66 pub fn handle_response(&self, py: Python<'_>, response: Py<PyBytes>) -> PyResult<bool> {
67 self.0.handle_response(response).py_block_on(py)
68 }
69
70 #[apply(inherit_doc)]
71 #[inherit_doc = "client/table.md"]
72 #[pyo3(signature = (input, limit=None, index=None, name=None, format=None))]
73 pub fn table(
74 &self,
75 py: Python<'_>,
76 input: Py<PyAny>,
77 limit: Option<u32>,
78 index: Option<Py<PyString>>,
79 name: Option<Py<PyString>>,
80 format: Option<Py<PyString>>,
81 ) -> PyResult<Table> {
82 Ok(Table(
83 self.0
84 .table(input, limit, index, name, format)
85 .py_block_on(py)?,
86 ))
87 }
88
89 #[apply(inherit_doc)]
90 #[inherit_doc = "client/open_table.md"]
91 pub fn open_table(&self, py: Python<'_>, name: String) -> PyResult<Table> {
92 let client = self.0.clone();
93 let table = client.open_table(name).py_block_on(py)?;
94 Ok(Table(table))
95 }
96
97 #[apply(inherit_doc)]
98 #[inherit_doc = "client/get_hosted_table_names.md"]
99 pub fn get_hosted_table_names(&self, py: Python<'_>) -> PyResult<Vec<String>> {
100 self.0.get_hosted_table_names().py_block_on(py)
101 }
102
103 #[apply(inherit_doc)]
104 #[inherit_doc = "client/on_hosted_tables_update.md"]
105 pub fn on_hosted_tables_update(&self, py: Python<'_>, callback: Py<PyAny>) -> PyResult<u32> {
106 self.0.on_hosted_tables_update(callback).py_block_on(py)
107 }
108
109 #[apply(inherit_doc)]
110 #[inherit_doc = "client/remove_hosted_tables_update.md"]
111 pub fn remove_hosted_tables_update(&self, py: Python<'_>, callback_id: u32) -> PyResult<()> {
112 self.0
113 .remove_hosted_tables_update(callback_id)
114 .py_block_on(py)
115 }
116
117 #[apply(inherit_doc)]
118 #[inherit_doc = "client/set_loop_callback.md"]
119 pub fn set_loop_callback(&self, py: Python<'_>, loop_cb: Py<PyAny>) -> PyResult<()> {
120 self.0.set_loop_callback(loop_cb).py_block_on(py)
121 }
122
123 #[apply(inherit_doc)]
124 #[inherit_doc = "client/terminate.md"]
125 pub fn terminate(&self, py: Python<'_>) -> PyResult<()> {
126 self.0.terminate(py)
127 }
128}
129
130#[pyclass(subclass, name = "Table", module = "perspective")]
131pub struct Table(AsyncTable);
132
133assert_table_api!(Table);
134
135#[pymethods]
136impl Table {
137 #[new]
138 fn new() -> PyResult<Self> {
139 Err(PyTypeError::new_err(
140 "Do not call Table's constructor directly, construct from a Client instance.",
141 ))
142 }
143
144 #[apply(inherit_doc)]
145 #[inherit_doc = "table/get_index.md"]
146 pub fn get_index(&self) -> Option<String> {
147 self.0.get_index()
148 }
149
150 #[apply(inherit_doc)]
151 #[inherit_doc = "table/get_client.md"]
152 pub fn get_client(&self, py: Python<'_>) -> Client {
153 Client(self.0.get_client().py_block_on(py))
154 }
155
156 #[apply(inherit_doc)]
157 #[inherit_doc = "table/get_client.md"]
158 pub fn get_limit(&self) -> Option<u32> {
159 self.0.get_limit()
160 }
161
162 pub fn get_name(&self) -> String {
163 self.0.get_name()
164 }
165
166 #[apply(inherit_doc)]
167 #[inherit_doc = "table/clear.md"]
168 pub fn clear(&self, py: Python<'_>) -> PyResult<()> {
169 self.0.clear().py_block_on(py)
170 }
171
172 #[apply(inherit_doc)]
173 #[inherit_doc = "table/columns.md"]
174 pub fn columns(&self, py: Python<'_>) -> PyResult<Vec<String>> {
175 self.0.columns().py_block_on(py)
176 }
177
178 #[apply(inherit_doc)]
179 #[inherit_doc = "table/delete.md"]
180 pub fn delete(&self, py: Python<'_>) -> PyResult<()> {
181 self.0.delete().py_block_on(py)
182 }
183
184 #[apply(inherit_doc)]
185 #[inherit_doc = "table/make_port.md"]
186 pub fn make_port(&self, py: Python<'_>) -> PyResult<i32> {
187 let table = self.0.clone();
188 table.make_port().py_block_on(py)
189 }
190
191 #[apply(inherit_doc)]
192 #[inherit_doc = "table/on_delete.md"]
193 pub fn on_delete(&self, py: Python<'_>, callback: Py<PyAny>) -> PyResult<u32> {
194 let table = self.0.clone();
195 table.on_delete(callback).py_block_on(py)
196 }
197
198 #[apply(inherit_doc)]
199 #[inherit_doc = "table/remove.md"]
200 #[pyo3(signature = (input, format=None))]
201 pub fn remove(&self, py: Python<'_>, input: Py<PyAny>, format: Option<String>) -> PyResult<()> {
202 let table = self.0.clone();
203 table.remove(input, format).py_block_on(py)
204 }
205
206 #[apply(inherit_doc)]
207 #[inherit_doc = "table/remove_delete.md"]
208 pub fn remove_delete(&self, py: Python<'_>, callback_id: u32) -> PyResult<()> {
209 let table = self.0.clone();
210 table.remove_delete(callback_id).py_block_on(py)
211 }
212
213 #[apply(inherit_doc)]
214 #[inherit_doc = "table/schema.md"]
215 pub fn schema(&self, py: Python<'_>) -> PyResult<HashMap<String, String>> {
216 let table = self.0.clone();
217 table.schema().py_block_on(py)
218 }
219
220 #[apply(inherit_doc)]
221 #[inherit_doc = "table/validate_expressions.md"]
222 pub fn validate_expressions(
223 &self,
224 py: Python<'_>,
225 expression: Py<PyAny>,
226 ) -> PyResult<Py<PyAny>> {
227 let table = self.0.clone();
228 table.validate_expressions(expression).py_block_on(py)
229 }
230
231 #[apply(inherit_doc)]
232 #[inherit_doc = "table/view.md"]
233 #[pyo3(signature = (**config))]
234 pub fn view(&self, py: Python<'_>, config: Option<Py<PyDict>>) -> PyResult<View> {
235 Ok(View(self.0.view(config).py_block_on(py)?))
236 }
237
238 #[apply(inherit_doc)]
239 #[inherit_doc = "table/size.md"]
240 pub fn size(&self, py: Python<'_>) -> PyResult<usize> {
241 self.0.size().py_block_on(py)
242 }
243
244 #[apply(inherit_doc)]
245 #[inherit_doc = "table/update.md"]
246 #[pyo3(signature = (input, format=None))]
247 pub fn replace(
248 &self,
249 py: Python<'_>,
250 input: Py<PyAny>,
251 format: Option<String>,
252 ) -> PyResult<()> {
253 self.0.replace(input, format).py_block_on(py)
254 }
255
256 #[apply(inherit_doc)]
257 #[inherit_doc = "table/update.md"]
258 #[pyo3(signature = (input, port_id=None, format=None))]
259 pub fn update(
260 &self,
261 py: Python<'_>,
262 input: Py<PyAny>,
263 port_id: Option<u32>,
264 format: Option<String>,
265 ) -> PyResult<()> {
266 self.0.update(input, port_id, format).py_block_on(py)
267 }
268}
269
270#[apply(inherit_doc)]
271#[inherit_doc = "view.md"]
272#[pyclass(subclass, name = "View", module = "perspective")]
273pub struct View(AsyncView);
274
275assert_view_api!(View);
276
277#[pymethods]
278impl View {
279 #[new]
280 fn new() -> PyResult<Self> {
281 Err(PyTypeError::new_err(
282 "Do not call View's constructor directly, construct from a Table instance.",
283 ))
284 }
285
286 #[apply(inherit_doc)]
287 #[inherit_doc = "view/column_paths.md"]
288 pub fn column_paths(&self, py: Python<'_>) -> PyResult<Vec<String>> {
289 self.0.column_paths().py_block_on(py)
290 }
291
292 #[apply(inherit_doc)]
293 #[inherit_doc = "view/to_columns_string.md"]
294 #[pyo3(signature = (**window))]
295 pub fn to_columns_string(
296 &self,
297 py: Python<'_>,
298 window: Option<Py<PyDict>>,
299 ) -> PyResult<String> {
300 self.0.to_columns_string(window).py_block_on(py)
301 }
302
303 #[apply(inherit_doc)]
304 #[inherit_doc = "view/to_json_string.md"]
305 #[pyo3(signature = (**window))]
306 pub fn to_json_string(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<String> {
307 self.0.to_json_string(window).py_block_on(py)
308 }
309
310 #[apply(inherit_doc)]
311 #[inherit_doc = "view/to_ndjson.md"]
312 #[pyo3(signature = (**window))]
313 pub fn to_ndjson(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<String> {
314 self.0.to_ndjson(window).py_block_on(py)
315 }
316
317 #[pyo3(signature = (**window))]
318 pub fn to_records<'a>(
319 &self,
320 py: Python<'a>,
321 window: Option<Py<PyDict>>,
322 ) -> PyResult<Bound<'a, PyAny>> {
323 let json = self.0.to_json_string(window).py_block_on(py)?;
324 let json_module = PyModule::import(py, "json")?;
325 json_module.call_method1("loads", (json,))
326 }
327
328 #[apply(inherit_doc)]
329 #[inherit_doc = "view/to_json.md"]
330 #[pyo3(signature = (**window))]
331 pub fn to_json<'a>(
332 &self,
333 py: Python<'a>,
334 window: Option<Py<PyDict>>,
335 ) -> PyResult<Bound<'a, PyAny>> {
336 self.to_records(py, window)
337 }
338
339 #[apply(inherit_doc)]
340 #[inherit_doc = "view/to_columns.md"]
341 #[pyo3(signature = (**window))]
342 pub fn to_columns<'a>(
343 &self,
344 py: Python<'a>,
345 window: Option<Py<PyDict>>,
346 ) -> PyResult<Bound<'a, PyAny>> {
347 let json = self.0.to_columns_string(window).py_block_on(py)?;
348 let json_module = PyModule::import(py, "json")?;
349 json_module.call_method1("loads", (json,))
350 }
351
352 #[apply(inherit_doc)]
353 #[inherit_doc = "view/to_csv.md"]
354 #[pyo3(signature = (**window))]
355 pub fn to_csv(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<String> {
356 self.0.to_csv(window).py_block_on(py)
357 }
358
359 #[doc = include_str!("../../docs/client/to_pandas.md")]
360 #[pyo3(signature = (**window))]
361 pub fn to_dataframe(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<Py<PyAny>> {
363 self.0.to_dataframe(window).py_block_on(py)
364 }
365
366 #[doc = include_str!("../../docs/client/to_pandas.md")]
367 #[pyo3(signature = (**window))]
368 pub fn to_pandas(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<Py<PyAny>> {
369 self.0.to_dataframe(window).py_block_on(py)
370 }
371
372 #[doc = include_str!("../../docs/client/to_polars.md")]
373 #[pyo3(signature = (**window))]
374 pub fn to_polars(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<Py<PyAny>> {
375 self.0.to_polars(window).py_block_on(py)
376 }
377
378 #[apply(inherit_doc)]
379 #[inherit_doc = "view/to_arrow.md"]
380 #[pyo3(signature = (**window))]
381 pub fn to_arrow(&self, py: Python<'_>, window: Option<Py<PyDict>>) -> PyResult<Py<PyBytes>> {
382 self.0.to_arrow(window).py_block_on(py)
383 }
384
385 #[apply(inherit_doc)]
386 #[inherit_doc = "view/delete.md"]
387 pub fn delete(&self, py: Python<'_>) -> PyResult<()> {
388 self.0.delete().py_block_on(py)
389 }
390
391 #[apply(inherit_doc)]
392 #[inherit_doc = "view/expand.md"]
393 pub fn expand(&self, py: Python<'_>, index: u32) -> PyResult<u32> {
394 self.0.expand(index).py_block_on(py)
395 }
396
397 #[apply(inherit_doc)]
398 #[inherit_doc = "view/collapse.md"]
399 pub fn collapse(&self, py: Python<'_>, index: u32) -> PyResult<u32> {
400 self.0.collapse(index).py_block_on(py)
401 }
402
403 #[apply(inherit_doc)]
404 #[inherit_doc = "view/dimensions.md"]
405 pub fn dimensions(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
406 self.0.dimensions().py_block_on(py)
407 }
408
409 #[apply(inherit_doc)]
410 #[inherit_doc = "view/expression_schema.md"]
411 pub fn expression_schema(&self, py: Python<'_>) -> PyResult<HashMap<String, String>> {
412 self.0.expression_schema().py_block_on(py)
413 }
414
415 #[apply(inherit_doc)]
416 #[inherit_doc = "view/get_config.md"]
417 pub fn get_config(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
418 self.0.get_config().py_block_on(py)
419 }
420
421 #[apply(inherit_doc)]
422 #[inherit_doc = "view/get_min_max.md"]
423 pub fn get_min_max(&self, py: Python<'_>, column_name: String) -> PyResult<(String, String)> {
424 self.0.get_min_max(column_name).py_block_on(py)
425 }
426
427 #[apply(inherit_doc)]
428 #[inherit_doc = "view/num_rows.md"]
429 pub fn num_rows(&self, py: Python<'_>) -> PyResult<u32> {
430 self.0.num_rows().py_block_on(py)
431 }
432
433 #[apply(inherit_doc)]
434 #[inherit_doc = "view/schema.md"]
435 pub fn schema(&self, py: Python<'_>) -> PyResult<HashMap<String, String>> {
436 self.0.schema().py_block_on(py)
437 }
438
439 #[apply(inherit_doc)]
440 #[inherit_doc = "view/on_delete.md"]
441 pub fn on_delete(&self, py: Python<'_>, callback: Py<PyAny>) -> PyResult<u32> {
442 self.0.on_delete(callback).py_block_on(py)
443 }
444
445 #[apply(inherit_doc)]
446 #[inherit_doc = "view/remove_delete.md"]
447 pub fn remove_delete(&self, py: Python<'_>, callback_id: u32) -> PyResult<()> {
448 self.0.remove_delete(callback_id).py_block_on(py)
449 }
450
451 #[apply(inherit_doc)]
452 #[inherit_doc = "view/on_update.md"]
453 #[pyo3(signature = (callback, mode=None))]
454 pub fn on_update(
455 &self,
456 py: Python<'_>,
457 callback: Py<PyAny>,
458 mode: Option<String>,
459 ) -> PyResult<u32> {
460 self.0.on_update(callback, mode).py_block_on(py)
461 }
462
463 #[apply(inherit_doc)]
464 #[inherit_doc = "view/remove_update.md"]
465 pub fn remove_update(&self, py: Python<'_>, callback_id: u32) -> PyResult<()> {
466 self.0.remove_update(callback_id).py_block_on(py)
467 }
468}