use std::panic::AssertUnwindSafe;
use polars::frame::DataFrame;
use polars::series::IntoSeries;
use polars_error::PolarsResult;
use polars_error::signals::{KeyboardInterrupt, catch_keyboard_interrupt};
use pyo3::exceptions::PyKeyboardInterrupt;
use pyo3::marker::Ungil;
use pyo3::{PyErr, PyResult, Python};
use crate::dataframe::PyDataFrame;
use crate::error::PyPolarsErr;
use crate::series::PySeries;
use crate::timeout::{cancel_polars_timeout, schedule_polars_timeout};
#[macro_export]
macro_rules! apply_all_polars_dtypes {
($self:expr, $method:ident, $($args:expr),*) => {
match $self.dtype() {
DataType::Boolean => $self.bool().unwrap().$method($($args),*),
DataType::UInt8 => $self.u8().unwrap().$method($($args),*),
DataType::UInt16 => $self.u16().unwrap().$method($($args),*),
DataType::UInt32 => $self.u32().unwrap().$method($($args),*),
DataType::UInt64 => $self.u64().unwrap().$method($($args),*),
DataType::UInt128 => $self.u128().unwrap().$method($($args),*),
DataType::Int8 => $self.i8().unwrap().$method($($args),*),
DataType::Int16 => $self.i16().unwrap().$method($($args),*),
DataType::Int32 => $self.i32().unwrap().$method($($args),*),
DataType::Int64 => $self.i64().unwrap().$method($($args),*),
DataType::Int128 => $self.i128().unwrap().$method($($args),*),
DataType::Float16 => $self.f16().unwrap().$method($($args),*),
DataType::Float32 => $self.f32().unwrap().$method($($args),*),
DataType::Float64 => $self.f64().unwrap().$method($($args),*),
DataType::String => $self.str().unwrap().$method($($args),*),
DataType::Binary => $self.binary().unwrap().$method($($args),*),
DataType::Decimal(_, _) => $self.decimal().unwrap().$method($($args),*),
DataType::Date => $self.date().unwrap().$method($($args),*),
DataType::Datetime(_, _) => $self.datetime().unwrap().$method($($args),*),
DataType::Duration(_) => $self.duration().unwrap().$method($($args),*),
DataType::Time => $self.time().unwrap().$method($($args),*),
DataType::List(_) => $self.list().unwrap().$method($($args),*),
DataType::Struct(_) => $self.struct_().unwrap().$method($($args),*),
DataType::Array(_, _) => $self.array().unwrap().$method($($args),*),
dt @ (DataType::Categorical(_, _) | DataType::Enum(_, _)) => match dt.cat_physical().unwrap() {
CategoricalPhysical::U8 => $self.cat8().unwrap().$method($($args),*),
CategoricalPhysical::U16 => $self.cat16().unwrap().$method($($args),*),
CategoricalPhysical::U32 => $self.cat32().unwrap().$method($($args),*),
},
#[cfg(feature = "object")]
DataType::Object(_) => {
$self
.as_any()
.downcast_ref::<ObjectChunked<ObjectValue>>()
.unwrap()
.$method($($args),*)
},
DataType::Extension(_, _) => $self.ext().unwrap().$method($($args),*),
DataType::Null => $self.null().unwrap().$method($($args),*),
dt @ (DataType::BinaryOffset | DataType::Unknown(_)) => panic!("dtype {:?} not supported", dt)
}
}
}
#[allow(unused)]
pub(crate) fn to_py_err<E: Into<PyPolarsErr>>(e: E) -> PyErr {
e.into().into()
}
pub trait EnterPolarsExt {
fn enter_polars<T, E, F>(self, f: F) -> PyResult<T>
where
F: Ungil + Send + FnOnce() -> Result<T, E>,
T: Ungil + Send,
E: Ungil + Send + Into<PyPolarsErr>;
#[inline(always)]
fn enter_polars_ok<T, F>(self, f: F) -> PyResult<T>
where
Self: Sized,
F: Ungil + Send + FnOnce() -> T,
T: Ungil + Send,
{
self.enter_polars(move || PyResult::Ok(f()))
}
#[inline(always)]
fn enter_polars_df<F>(self, f: F) -> PyResult<PyDataFrame>
where
Self: Sized,
F: Ungil + Send + FnOnce() -> PolarsResult<DataFrame>,
{
self.enter_polars(f).map(PyDataFrame::new)
}
#[inline(always)]
fn enter_polars_series<T, F>(self, f: F) -> PyResult<PySeries>
where
Self: Sized,
T: Ungil + Send + IntoSeries,
F: Ungil + Send + FnOnce() -> PolarsResult<T>,
{
self.enter_polars(f).map(|s| PySeries::new(s.into_series()))
}
}
impl EnterPolarsExt for Python<'_> {
fn enter_polars<T, E, F>(self, f: F) -> PyResult<T>
where
F: Ungil + Send + FnOnce() -> Result<T, E>,
T: Ungil + Send,
E: Ungil + Send + Into<PyPolarsErr>,
{
let timeout = schedule_polars_timeout();
let ret = self.detach(|| catch_keyboard_interrupt(AssertUnwindSafe(f)));
cancel_polars_timeout(timeout);
match ret {
Ok(Ok(ret)) => Ok(ret),
Ok(Err(err)) => Err(PyErr::from(err.into())),
Err(KeyboardInterrupt) => Err(PyKeyboardInterrupt::new_err("")),
}
}
}