#[cfg(feature = "derive")]
pub mod derive;
pub mod error;
#[cfg(feature = "derive")]
pub mod export;
mod ffi;
use crate::error::PyPolarsErr;
use crate::ffi::to_py::to_py_array;
use polars::export::arrow;
use polars::prelude::*;
use pyo3::ffi::Py_uintptr_t;
use pyo3::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
#[cfg(feature = "lazy")]
use {polars_lazy::frame::LazyFrame, polars_plan::logical_plan::LogicalPlan};
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct PySeries(pub Series);
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct PyDataFrame(pub DataFrame);
#[cfg(feature = "lazy")]
#[repr(transparent)]
#[derive(Clone)]
pub struct PyLazyFrame(pub LazyFrame);
impl From<PyDataFrame> for DataFrame {
fn from(value: PyDataFrame) -> Self {
value.0
}
}
impl From<PySeries> for Series {
fn from(value: PySeries) -> Self {
value.0
}
}
#[cfg(feature = "lazy")]
impl From<PyLazyFrame> for LazyFrame {
fn from(value: PyLazyFrame) -> Self {
value.0
}
}
impl AsRef<Series> for PySeries {
fn as_ref(&self) -> &Series {
&self.0
}
}
impl AsRef<DataFrame> for PyDataFrame {
fn as_ref(&self) -> &DataFrame {
&self.0
}
}
#[cfg(feature = "lazy")]
impl AsRef<LazyFrame> for PyLazyFrame {
fn as_ref(&self) -> &LazyFrame {
&self.0
}
}
impl<'a> FromPyObject<'a> for PySeries {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
let ob = ob.call_method0("rechunk")?;
let name = ob.getattr("name")?;
let name = name.str()?.to_str()?;
let arr = ob.call_method0("to_arrow")?;
let arr = ffi::to_rust::array_to_rust(arr)?;
Ok(PySeries(
Series::try_from((name, arr)).map_err(PyPolarsErr::from)?,
))
}
}
impl<'a> FromPyObject<'a> for PyDataFrame {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
let series = ob.call_method0("get_columns")?;
let n = ob.getattr("width")?.extract::<usize>()?;
let mut columns = Vec::with_capacity(n);
for pyseries in series.iter()? {
let pyseries = pyseries?;
let s = pyseries.extract::<PySeries>()?.0;
columns.push(s);
}
unsafe { Ok(PyDataFrame(DataFrame::new_no_checks(columns))) }
}
}
#[cfg(feature = "lazy")]
impl<'a> FromPyObject<'a> for PyLazyFrame {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
let s = ob.call_method0("__getstate__")?.extract::<Vec<u8>>()?;
let lp: LogicalPlan = ciborium::de::from_reader(&*s).map_err(
|e| PyPolarsErr::Other(
format!("Error when deserializing LazyFrame. This may be due to mismatched polars versions. {}", e)
)
)?;
Ok(PyLazyFrame(LazyFrame::from(lp)))
}
}
impl IntoPy<PyObject> for PySeries {
fn into_py(self, py: Python<'_>) -> PyObject {
let polars = py.import("polars").expect("polars not installed");
let s = polars.getattr("Series").unwrap();
match s.getattr("_import_from_c") {
Ok(import_from_c) => {
let mut chunk_ptrs = Vec::with_capacity(self.0.n_chunks());
for i in 0..self.0.n_chunks() {
let array = self.0.to_arrow(i, true);
let schema = Box::leak(Box::new(arrow::ffi::export_field_to_c(
&ArrowField::new("", array.data_type().clone(), true),
)));
let array = Box::leak(Box::new(arrow::ffi::export_array_to_c(array.clone())));
let schema_ptr: *const arrow::ffi::ArrowSchema = &*schema;
let array_ptr: *const arrow::ffi::ArrowArray = &*array;
chunk_ptrs.push((schema_ptr as Py_uintptr_t, array_ptr as Py_uintptr_t))
}
let pyseries = import_from_c
.call1((self.0.name(), chunk_ptrs.clone()))
.unwrap();
for (schema_ptr, array_ptr) in chunk_ptrs {
let schema_ptr = schema_ptr as *mut arrow::ffi::ArrowSchema;
let array_ptr = array_ptr as *mut arrow::ffi::ArrowArray;
unsafe {
let _ = Box::from_raw(schema_ptr);
let array = Box::from_raw(array_ptr);
let array = *array;
std::mem::forget(array);
}
}
pyseries.to_object(py)
}
Err(_) => {
let s = self.0.rechunk();
let name = s.name();
let arr = s.to_arrow(0, false);
let pyarrow = py.import("pyarrow").expect("pyarrow not installed");
let arg = to_py_array(arr, py, pyarrow).unwrap();
let s = polars.call_method1("from_arrow", (arg,)).unwrap();
let s = s.call_method1("rename", (name,)).unwrap();
s.to_object(py)
}
}
}
}
impl IntoPy<PyObject> for PyDataFrame {
fn into_py(self, py: Python<'_>) -> PyObject {
let pyseries = self
.0
.get_columns()
.iter()
.map(|s| PySeries(s.clone()).into_py(py))
.collect::<Vec<_>>();
let polars = py.import("polars").expect("polars not installed");
let df_object = polars.call_method1("DataFrame", (pyseries,)).unwrap();
df_object.into_py(py)
}
}
#[cfg(feature = "lazy")]
impl IntoPy<PyObject> for PyLazyFrame {
fn into_py(self, py: Python<'_>) -> PyObject {
let polars = py.import("polars").expect("polars not installed");
let cls = polars.getattr("LazyFrame").unwrap();
let instance = cls.call_method1("__new__", (cls,)).unwrap();
let mut writer: Vec<u8> = vec![];
ciborium::ser::into_writer(&self.0.logical_plan, &mut writer).unwrap();
instance.call_method1("__setstate__", (&*writer,)).unwrap();
instance.into_py(py)
}
}