Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-python/src/utils.rs
7884 views
1
use std::panic::AssertUnwindSafe;
2
3
use polars::frame::DataFrame;
4
use polars::series::IntoSeries;
5
use polars_error::PolarsResult;
6
use polars_error::signals::{KeyboardInterrupt, catch_keyboard_interrupt};
7
use pyo3::exceptions::PyKeyboardInterrupt;
8
use pyo3::marker::Ungil;
9
use pyo3::{PyErr, PyResult, Python};
10
11
use crate::dataframe::PyDataFrame;
12
use crate::error::PyPolarsErr;
13
use crate::series::PySeries;
14
use crate::timeout::{cancel_polars_timeout, schedule_polars_timeout};
15
16
/// Calls method on downcasted ChunkedArray for all possible publicly exposed Polars dtypes.
17
#[macro_export]
18
macro_rules! apply_all_polars_dtypes {
19
($self:expr, $method:ident, $($args:expr),*) => {
20
match $self.dtype() {
21
DataType::Boolean => $self.bool().unwrap().$method($($args),*),
22
DataType::UInt8 => $self.u8().unwrap().$method($($args),*),
23
DataType::UInt16 => $self.u16().unwrap().$method($($args),*),
24
DataType::UInt32 => $self.u32().unwrap().$method($($args),*),
25
DataType::UInt64 => $self.u64().unwrap().$method($($args),*),
26
DataType::UInt128 => $self.u128().unwrap().$method($($args),*),
27
DataType::Int8 => $self.i8().unwrap().$method($($args),*),
28
DataType::Int16 => $self.i16().unwrap().$method($($args),*),
29
DataType::Int32 => $self.i32().unwrap().$method($($args),*),
30
DataType::Int64 => $self.i64().unwrap().$method($($args),*),
31
DataType::Int128 => $self.i128().unwrap().$method($($args),*),
32
DataType::Float16 => $self.f16().unwrap().$method($($args),*),
33
DataType::Float32 => $self.f32().unwrap().$method($($args),*),
34
DataType::Float64 => $self.f64().unwrap().$method($($args),*),
35
DataType::String => $self.str().unwrap().$method($($args),*),
36
DataType::Binary => $self.binary().unwrap().$method($($args),*),
37
DataType::Decimal(_, _) => $self.decimal().unwrap().$method($($args),*),
38
39
DataType::Date => $self.date().unwrap().$method($($args),*),
40
DataType::Datetime(_, _) => $self.datetime().unwrap().$method($($args),*),
41
DataType::Duration(_) => $self.duration().unwrap().$method($($args),*),
42
DataType::Time => $self.time().unwrap().$method($($args),*),
43
44
DataType::List(_) => $self.list().unwrap().$method($($args),*),
45
DataType::Struct(_) => $self.struct_().unwrap().$method($($args),*),
46
DataType::Array(_, _) => $self.array().unwrap().$method($($args),*),
47
48
dt @ (DataType::Categorical(_, _) | DataType::Enum(_, _)) => match dt.cat_physical().unwrap() {
49
CategoricalPhysical::U8 => $self.cat8().unwrap().$method($($args),*),
50
CategoricalPhysical::U16 => $self.cat16().unwrap().$method($($args),*),
51
CategoricalPhysical::U32 => $self.cat32().unwrap().$method($($args),*),
52
},
53
54
#[cfg(feature = "object")]
55
DataType::Object(_) => {
56
$self
57
.as_any()
58
.downcast_ref::<ObjectChunked<ObjectValue>>()
59
.unwrap()
60
.$method($($args),*)
61
},
62
DataType::Extension(_, _) => $self.ext().unwrap().$method($($args),*),
63
64
DataType::Null => $self.null().unwrap().$method($($args),*),
65
66
dt @ (DataType::BinaryOffset | DataType::Unknown(_)) => panic!("dtype {:?} not supported", dt)
67
}
68
}
69
}
70
71
/// Boilerplate for `|e| PyPolarsErr::from(e).into()`
72
#[allow(unused)]
73
pub(crate) fn to_py_err<E: Into<PyPolarsErr>>(e: E) -> PyErr {
74
e.into().into()
75
}
76
77
pub trait EnterPolarsExt {
78
/// Whenever you have a block of code in the public Python API that
79
/// (potentially) takes a long time, wrap it in enter_polars. This will
80
/// ensure we release the GIL and catch KeyboardInterrupts.
81
///
82
/// This not only can increase performance and usability, it can avoid
83
/// deadlocks on the GIL for Python UDFs.
84
fn enter_polars<T, E, F>(self, f: F) -> PyResult<T>
85
where
86
F: Ungil + Send + FnOnce() -> Result<T, E>,
87
T: Ungil + Send,
88
E: Ungil + Send + Into<PyPolarsErr>;
89
90
/// Same as enter_polars, but wraps the result in PyResult::Ok, useful
91
/// shorthand for infallible functions.
92
#[inline(always)]
93
fn enter_polars_ok<T, F>(self, f: F) -> PyResult<T>
94
where
95
Self: Sized,
96
F: Ungil + Send + FnOnce() -> T,
97
T: Ungil + Send,
98
{
99
self.enter_polars(move || PyResult::Ok(f()))
100
}
101
102
/// Same as enter_polars, but expects a PolarsResult<DataFrame> as return
103
/// which is converted to a PyDataFrame.
104
#[inline(always)]
105
fn enter_polars_df<F>(self, f: F) -> PyResult<PyDataFrame>
106
where
107
Self: Sized,
108
F: Ungil + Send + FnOnce() -> PolarsResult<DataFrame>,
109
{
110
self.enter_polars(f).map(PyDataFrame::new)
111
}
112
113
/// Same as enter_polars, but expects a PolarsResult<S> as return which
114
/// is converted to a PySeries through S: IntoSeries.
115
#[inline(always)]
116
fn enter_polars_series<T, F>(self, f: F) -> PyResult<PySeries>
117
where
118
Self: Sized,
119
T: Ungil + Send + IntoSeries,
120
F: Ungil + Send + FnOnce() -> PolarsResult<T>,
121
{
122
self.enter_polars(f).map(|s| PySeries::new(s.into_series()))
123
}
124
}
125
126
impl EnterPolarsExt for Python<'_> {
127
fn enter_polars<T, E, F>(self, f: F) -> PyResult<T>
128
where
129
F: Ungil + Send + FnOnce() -> Result<T, E>,
130
T: Ungil + Send,
131
E: Ungil + Send + Into<PyPolarsErr>,
132
{
133
let timeout = schedule_polars_timeout();
134
let ret = self.detach(|| catch_keyboard_interrupt(AssertUnwindSafe(f)));
135
cancel_polars_timeout(timeout);
136
match ret {
137
Ok(Ok(ret)) => Ok(ret),
138
Ok(Err(err)) => Err(PyErr::from(err.into())),
139
Err(KeyboardInterrupt) => Err(PyKeyboardInterrupt::new_err("")),
140
}
141
}
142
}
143
144