1010// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
1111// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1212
13- use std:: any:: Any ;
1413use std:: collections:: HashMap ;
1514use std:: str:: FromStr ;
1615use std:: sync:: Arc ;
1716
1817use async_lock:: RwLock ;
1918use futures:: FutureExt ;
2019use perspective_client:: {
21- assert_table_api, assert_view_api, clone, Client , ClientError , OnUpdateMode , OnUpdateOptions ,
22- Table , TableData , TableInitOptions , TableReadFormat , UpdateData , UpdateOptions , View ,
20+ assert_table_api, assert_view_api, clone, Client , OnUpdateMode , OnUpdateOptions , Table ,
21+ TableData , TableInitOptions , TableReadFormat , UpdateData , UpdateOptions , View ,
2322 ViewOnUpdateResp , ViewWindow ,
2423} ;
25- use pyo3:: create_exception;
2624use pyo3:: exceptions:: PyValueError ;
2725use pyo3:: prelude:: * ;
28- use pyo3:: types:: { PyAny , PyBytes , PyDict , PyList , PyString } ;
26+ use pyo3:: types:: { PyAny , PyBytes , PyDict , PyString } ;
2927use pythonize:: depythonize_bound;
3028
3129use super :: pandas:: arrow_to_pandas;
30+ use super :: table_data:: TableDataExt ;
31+ use super :: update_data:: UpdateDataExt ;
3232use super :: { pandas, pyarrow} ;
33+ use crate :: py_err:: { PyPerspectiveError , ResultTClientErrorExt } ;
3334
3435#[ derive( Clone ) ]
3536pub struct PyClient {
@@ -38,138 +39,6 @@ pub struct PyClient {
3839 close_cb : Option < Py < PyAny > > ,
3940}
4041
41- #[ extend:: ext]
42- pub impl < T > Result < T , ClientError > {
43- fn into_pyerr ( self ) -> PyResult < T > {
44- match self {
45- Ok ( x) => Ok ( x) ,
46- Err ( x) => Err ( PyPerspectiveError :: new_err ( format ! ( "{}" , x) ) ) ,
47- }
48- }
49- }
50-
51- create_exception ! (
52- perspective,
53- PyPerspectiveError ,
54- pyo3:: exceptions:: PyException
55- ) ;
56-
57- #[ extend:: ext]
58- impl UpdateData {
59- fn from_py_partial (
60- py : Python < ' _ > ,
61- input : & Py < PyAny > ,
62- format : Option < TableReadFormat > ,
63- ) -> Result < Option < UpdateData > , PyErr > {
64- if let Ok ( pybytes) = input. downcast_bound :: < PyBytes > ( py) {
65- // TODO need to explicitly qualify this b/c bug in
66- // rust-analyzer - should be: just `pybytes.as_bytes()`.
67- let vec = pyo3:: prelude:: PyBytesMethods :: as_bytes ( pybytes) . to_vec ( ) ;
68-
69- match format {
70- Some ( TableReadFormat :: Csv ) => Ok ( Some ( UpdateData :: Csv ( String :: from_utf8 ( vec) ?) ) ) ,
71- Some ( TableReadFormat :: JsonString ) => {
72- Ok ( Some ( UpdateData :: JsonRows ( String :: from_utf8 ( vec) ?) ) )
73- } ,
74- Some ( TableReadFormat :: ColumnsString ) => {
75- Ok ( Some ( UpdateData :: JsonColumns ( String :: from_utf8 ( vec) ?) ) )
76- } ,
77- None | Some ( TableReadFormat :: Arrow ) => Ok ( Some ( UpdateData :: Arrow ( vec. into ( ) ) ) ) ,
78- }
79- } else if let Ok ( pystring) = input. downcast_bound :: < PyString > ( py) {
80- let string = pystring. extract :: < String > ( ) ?;
81- match format {
82- None | Some ( TableReadFormat :: Csv ) => Ok ( Some ( UpdateData :: Csv ( string) ) ) ,
83- Some ( TableReadFormat :: JsonString ) => Ok ( Some ( UpdateData :: JsonRows ( string) ) ) ,
84- Some ( TableReadFormat :: ColumnsString ) => Ok ( Some ( UpdateData :: JsonColumns ( string) ) ) ,
85- Some ( TableReadFormat :: Arrow ) => {
86- Ok ( Some ( UpdateData :: Arrow ( string. into_bytes ( ) . into ( ) ) ) )
87- } ,
88- }
89- } else if let Ok ( pylist) = input. downcast_bound :: < PyList > ( py) {
90- let json_module = PyModule :: import_bound ( py, "json" ) ?;
91- let string = json_module. call_method ( "dumps" , ( pylist, ) , None ) ?;
92- Ok ( Some ( UpdateData :: JsonRows ( string. extract :: < String > ( ) ?) ) )
93- } else if let Ok ( pydict) = input. downcast_bound :: < PyDict > ( py) {
94- if pydict. keys ( ) . is_empty ( ) {
95- return Err ( PyValueError :: new_err ( "Cannot infer type of empty dict" ) ) ;
96- }
97-
98- let first_key = pydict. keys ( ) . get_item ( 0 ) ?;
99- let first_item = pydict
100- . get_item ( first_key) ?
101- . ok_or_else ( || PyValueError :: new_err ( "Bad Input" ) ) ?;
102-
103- if first_item. downcast :: < PyList > ( ) . is_ok ( ) {
104- let json_module = PyModule :: import_bound ( py, "json" ) ?;
105- let string = json_module. call_method ( "dumps" , ( pydict, ) , None ) ?;
106- Ok ( Some ( UpdateData :: JsonColumns ( string. extract :: < String > ( ) ?) ) )
107- } else {
108- Ok ( None )
109- }
110- } else {
111- Ok ( None )
112- }
113- }
114-
115- fn from_py (
116- py : Python < ' _ > ,
117- input : & Py < PyAny > ,
118- format : Option < TableReadFormat > ,
119- ) -> Result < UpdateData , PyErr > {
120- if let Some ( x) = Self :: from_py_partial ( py, input, format) ? {
121- Ok ( x)
122- } else {
123- Err ( PyValueError :: new_err ( format ! (
124- "Unknown input type {:?}" ,
125- input. type_id( )
126- ) ) )
127- }
128- }
129- }
130-
131- #[ extend:: ext]
132- impl TableData {
133- fn from_py (
134- py : Python < ' _ > ,
135- input : Py < PyAny > ,
136- format : Option < TableReadFormat > ,
137- ) -> Result < TableData , PyErr > {
138- if let Some ( update) = UpdateData :: from_py_partial ( py, & input, format) ? {
139- Ok ( TableData :: Update ( update) )
140- } else if let Ok ( pylist) = input. downcast_bound :: < PyList > ( py) {
141- let json_module = PyModule :: import_bound ( py, "json" ) ?;
142- let string = json_module. call_method ( "dumps" , ( pylist, ) , None ) ?;
143- Ok ( UpdateData :: JsonRows ( string. extract :: < String > ( ) ?) . into ( ) )
144- } else if let Ok ( pydict) = input. downcast_bound :: < PyDict > ( py) {
145- let first_key = pydict. keys ( ) . get_item ( 0 ) ?;
146- let first_item = pydict
147- . get_item ( first_key) ?
148- . ok_or_else ( || PyValueError :: new_err ( "Bad Input" ) ) ?;
149- if first_item. downcast :: < PyList > ( ) . is_ok ( ) {
150- let json_module = PyModule :: import_bound ( py, "json" ) ?;
151- let string = json_module. call_method ( "dumps" , ( pydict, ) , None ) ?;
152- Ok ( UpdateData :: JsonColumns ( string. extract :: < String > ( ) ?) . into ( ) )
153- } else {
154- let mut schema = vec ! [ ] ;
155- for ( key, val) in pydict. into_iter ( ) {
156- schema. push ( (
157- key. extract :: < String > ( ) ?,
158- val. extract :: < String > ( ) ?. as_str ( ) . try_into ( ) . into_pyerr ( ) ?,
159- ) ) ;
160- }
161-
162- Ok ( TableData :: Schema ( schema) )
163- }
164- } else {
165- Err ( PyValueError :: new_err ( format ! (
166- "Unknown input type {:?}" ,
167- input. type_id( )
168- ) ) )
169- }
170- }
171- }
172-
17342impl PyClient {
17443 pub fn new ( handle_request : Py < PyAny > , handle_close : Option < Py < PyAny > > ) -> Self {
17544 let client = Client :: new_with_callback ( {
0 commit comments