@@ -6,8 +6,9 @@ use nuts_rs::Value;
66use pyo3:: {
77 exceptions:: PyRuntimeError ,
88 pyclass, pymethods,
9- types:: { PyAnyMethods , PyDict , PyDictMethods , PyList , PyListMethods , PyType } ,
10- Bound , FromPyObject , IntoPyObject , IntoPyObjectExt , Py , PyAny , PyErr , Python ,
9+ types:: { PyAnyMethods , PyDict , PyDictMethods , PyList , PyListMethods , PyType , PyTypeMethods } ,
10+ Borrowed , Bound , BoundObject , FromPyObject , IntoPyObject , IntoPyObjectExt , Py , PyAny , PyErr ,
11+ Python ,
1112} ;
1213use smallvec:: SmallVec ;
1314
@@ -75,15 +76,29 @@ impl<'py> IntoPyObject<'py> for &ItemType {
7576 nuts_rs:: ItemType :: F32 => "float32" ,
7677 nuts_rs:: ItemType :: Bool => "bool" ,
7778 nuts_rs:: ItemType :: String => "object" ,
79+ nuts_rs:: ItemType :: DateTime64 ( unit) => match unit {
80+ nuts_rs:: DateTimeUnit :: Seconds => "datetime64[s]" ,
81+ nuts_rs:: DateTimeUnit :: Milliseconds => "datetime64[ms]" ,
82+ nuts_rs:: DateTimeUnit :: Microseconds => "datetime64[us]" ,
83+ nuts_rs:: DateTimeUnit :: Nanoseconds => "datetime64[ns]" ,
84+ } ,
85+ nuts_rs:: ItemType :: TimeDelta64 ( unit) => match unit {
86+ nuts_rs:: DateTimeUnit :: Seconds => "timedelta64[s]" ,
87+ nuts_rs:: DateTimeUnit :: Milliseconds => "timedelta64[ms]" ,
88+ nuts_rs:: DateTimeUnit :: Microseconds => "timedelta64[us]" ,
89+ nuts_rs:: DateTimeUnit :: Nanoseconds => "timedelta64[ns]" ,
90+ } ,
7891 } ;
7992 let numpy = py. import ( "numpy" ) ?;
8093 let dtype = numpy. getattr ( "dtype" ) ?. call1 ( ( dtype_str, ) ) ?;
8194 Ok ( dtype)
8295 }
8396}
8497
85- impl < ' py > FromPyObject < ' py > for ItemType {
86- fn extract_bound ( ob : & Bound < ' _ , PyAny > ) -> std:: result:: Result < Self , PyErr > {
98+ impl < ' a , ' py > FromPyObject < ' a , ' py > for ItemType {
99+ type Error = PyErr ;
100+
101+ fn extract ( ob : Borrowed < ' a , ' py , PyAny > ) -> std:: result:: Result < Self , PyErr > {
87102 let dtype_str: & str = ob. extract ( ) ?;
88103 let item_type = match dtype_str {
89104 "uint64" => nuts_rs:: ItemType :: U64 ,
@@ -106,12 +121,14 @@ impl<'py> FromPyObject<'py> for ItemType {
106121#[ pyclass]
107122pub struct PyValue ( Value ) ;
108123
109- impl < ' py > FromPyObject < ' py > for PyValue {
110- fn extract_bound ( ob : & Bound < ' py , PyAny > ) -> std:: result:: Result < Self , PyErr > {
124+ impl < ' a , ' py > FromPyObject < ' a , ' py > for PyValue {
125+ type Error = PyErr ;
126+
127+ fn extract ( ob : Borrowed < ' a , ' py , PyAny > ) -> std:: result:: Result < Self , PyErr > {
111128 let ob = if ob. hasattr ( "values" ) ? {
112- & ob. getattr ( "values" ) ?
129+ ob. getattr ( "values" ) ?
113130 } else {
114- ob
131+ ob. into_bound ( )
115132 } ;
116133 if let Ok ( arr) = ob. extract :: < PyReadonlyArray1 < f64 > > ( ) {
117134 let vec = arr
@@ -166,9 +183,35 @@ impl<'py> FromPyObject<'py> for PyValue {
166183 . collect :: < Result < _ , _ > > ( ) ?;
167184 return Ok ( PyValue ( Value :: Strings ( vals_as_str) ) ) ;
168185 }
169- Err ( PyRuntimeError :: new_err (
170- "Could not convert to Value. Unsupported type." ,
171- ) )
186+
187+ macro_rules! extract_time {
188+ ( $unit: ident, $type: ident, $value: ident) => {
189+ if let Ok ( arr) = ob. extract:: <PyReadonlyArray1 <numpy:: datetime:: $type<numpy:: datetime:: units:: $unit>>>( ) {
190+ let vec = arr
191+ . as_slice( )
192+ . map_err( |_| PyRuntimeError :: new_err( "Array is not contiguous" ) ) ?;
193+ let vals_as_i64 = vec. iter( ) . map( |& dt| dt. into( ) ) . collect( ) ;
194+ return Ok ( PyValue ( Value :: $value(
195+ nuts_rs:: DateTimeUnit :: $unit,
196+ vals_as_i64,
197+ ) ) ) ;
198+ }
199+ } ;
200+ }
201+
202+ extract_time ! ( Seconds , Datetime , DateTime64 ) ;
203+ extract_time ! ( Milliseconds , Datetime , DateTime64 ) ;
204+ extract_time ! ( Microseconds , Datetime , DateTime64 ) ;
205+ extract_time ! ( Nanoseconds , Datetime , DateTime64 ) ;
206+ extract_time ! ( Seconds , Timedelta , TimeDelta64 ) ;
207+ extract_time ! ( Milliseconds , Timedelta , TimeDelta64 ) ;
208+ extract_time ! ( Microseconds , Timedelta , TimeDelta64 ) ;
209+ extract_time ! ( Nanoseconds , Timedelta , TimeDelta64 ) ;
210+
211+ Err ( PyRuntimeError :: new_err ( format ! (
212+ "Could not convert to Value. Unsupported type: {}" ,
213+ ob. get_type( ) . name( ) ?
214+ ) ) )
172215 }
173216}
174217
@@ -178,6 +221,23 @@ impl PyValue {
178221 }
179222
180223 pub fn into_array ( self , py : Python ) -> Result < Bound < PyAny > > {
224+ macro_rules! from_time {
225+ ( $unit: ident, $items: expr, $type: ident) => {
226+ Ok (
227+ PyArray1 :: <numpy:: datetime:: $type<numpy:: datetime:: units:: $unit>>:: from_vec(
228+ py,
229+ $items
230+ . into_iter( )
231+ . map( |ts| {
232+ numpy:: datetime:: $type:: <numpy:: datetime:: units:: $unit>:: from( ts)
233+ } )
234+ . collect( ) ,
235+ )
236+ . into_any( ) ,
237+ )
238+ } ;
239+ }
240+
181241 match self . 0 {
182242 Value :: F64 ( vec) => Ok ( PyArray1 :: from_vec ( py, vec) . into_any ( ) ) ,
183243 Value :: F32 ( vec) => Ok ( PyArray1 :: from_vec ( py, vec) . into_any ( ) ) ,
@@ -191,6 +251,18 @@ impl PyValue {
191251 Value :: ScalarF64 ( val) => Ok ( val. into_bound_py_any ( py) ?) ,
192252 Value :: ScalarF32 ( val) => Ok ( val. into_bound_py_any ( py) ?) ,
193253 Value :: ScalarBool ( val) => Ok ( val. into_bound_py_any ( py) ?) ,
254+ Value :: DateTime64 ( date_time_unit, items) => match date_time_unit {
255+ nuts_rs:: DateTimeUnit :: Seconds => from_time ! ( Seconds , items, Datetime ) ,
256+ nuts_rs:: DateTimeUnit :: Milliseconds => from_time ! ( Milliseconds , items, Datetime ) ,
257+ nuts_rs:: DateTimeUnit :: Microseconds => from_time ! ( Microseconds , items, Datetime ) ,
258+ nuts_rs:: DateTimeUnit :: Nanoseconds => from_time ! ( Nanoseconds , items, Datetime ) ,
259+ } ,
260+ Value :: TimeDelta64 ( date_time_unit, items) => match date_time_unit {
261+ nuts_rs:: DateTimeUnit :: Seconds => from_time ! ( Seconds , items, Timedelta ) ,
262+ nuts_rs:: DateTimeUnit :: Milliseconds => from_time ! ( Milliseconds , items, Timedelta ) ,
263+ nuts_rs:: DateTimeUnit :: Microseconds => from_time ! ( Microseconds , items, Timedelta ) ,
264+ nuts_rs:: DateTimeUnit :: Nanoseconds => from_time ! ( Nanoseconds , items, Timedelta ) ,
265+ } ,
194266 }
195267 }
196268}
0 commit comments