polars_python/conversion/
chunked_array.rs1use chrono::NaiveTime;
2use polars_core::utils::arrow::temporal_conversions::date32_to_date;
3use pyo3::prelude::*;
4use pyo3::types::{PyBytes, PyList, PyNone, PyTuple};
5use pyo3::{intern, BoundObject};
6
7use super::datetime::{
8 datetime_to_py_object, elapsed_offset_to_timedelta, nanos_since_midnight_to_naivetime,
9};
10use super::{decimal_to_digits, struct_dict};
11use crate::prelude::*;
12use crate::py_modules::pl_utils;
13
14impl<'py> IntoPyObject<'py> for &Wrap<&StringChunked> {
15 type Target = PyList;
16 type Output = Bound<'py, Self::Target>;
17 type Error = PyErr;
18
19 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
20 let iter = self.0.iter();
21 PyList::new(py, iter)
22 }
23}
24
25impl<'py> IntoPyObject<'py> for &Wrap<&BinaryChunked> {
26 type Target = PyList;
27 type Output = Bound<'py, Self::Target>;
28 type Error = PyErr;
29
30 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
31 let iter = self
32 .0
33 .iter()
34 .map(|opt_bytes| opt_bytes.map(|bytes| PyBytes::new(py, bytes)));
35 PyList::new(py, iter)
36 }
37}
38
39impl<'py> IntoPyObject<'py> for &Wrap<&StructChunked> {
40 type Target = PyList;
41 type Output = Bound<'py, Self::Target>;
42 type Error = PyErr;
43 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
44 let s = self.0.clone().into_series();
45 let s = s.rechunk();
48 let iter = s.iter().map(|av| match av {
49 AnyValue::Struct(_, _, flds) => struct_dict(py, av._iter_struct_av(), flds)
50 .unwrap()
51 .into_any(),
52 AnyValue::Null => PyNone::get(py).into_bound().into_any(),
53 _ => unreachable!(),
54 });
55
56 PyList::new(py, iter)
57 }
58}
59
60impl<'py> IntoPyObject<'py> for &Wrap<&DurationChunked> {
61 type Target = PyList;
62 type Output = Bound<'py, Self::Target>;
63 type Error = PyErr;
64
65 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
66 let time_unit = self.0.time_unit();
67 let iter = self
68 .0
69 .iter()
70 .map(|opt_v| opt_v.map(|v| elapsed_offset_to_timedelta(v, time_unit)));
71 PyList::new(py, iter)
72 }
73}
74
75impl<'py> IntoPyObject<'py> for &Wrap<&DatetimeChunked> {
76 type Target = PyList;
77 type Output = Bound<'py, Self::Target>;
78 type Error = PyErr;
79
80 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
81 let time_zone = self.0.time_zone().as_ref();
82 let time_unit = self.0.time_unit();
83 let iter = self.0.iter().map(|opt_v| {
84 opt_v.map(|v| datetime_to_py_object(py, v, time_unit, time_zone).unwrap())
85 });
86 PyList::new(py, iter)
87 }
88}
89
90impl<'py> IntoPyObject<'py> for &Wrap<&TimeChunked> {
91 type Target = PyList;
92 type Output = Bound<'py, Self::Target>;
93 type Error = PyErr;
94
95 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
96 let iter = time_to_pyobject_iter(self.0);
97 PyList::new(py, iter)
98 }
99}
100
101pub(crate) fn time_to_pyobject_iter(
102 ca: &TimeChunked,
103) -> impl '_ + ExactSizeIterator<Item = Option<NaiveTime>> {
104 ca.0.iter()
105 .map(move |opt_v| opt_v.map(nanos_since_midnight_to_naivetime))
106}
107
108impl<'py> IntoPyObject<'py> for &Wrap<&DateChunked> {
109 type Target = PyList;
110 type Output = Bound<'py, Self::Target>;
111 type Error = PyErr;
112
113 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
114 let iter = self.0.into_iter().map(|opt_v| opt_v.map(date32_to_date));
115 PyList::new(py, iter)
116 }
117}
118
119impl<'py> IntoPyObject<'py> for &Wrap<&DecimalChunked> {
120 type Target = PyList;
121 type Output = Bound<'py, Self::Target>;
122 type Error = PyErr;
123 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
124 let iter = decimal_to_pyobject_iter(py, self.0)?;
125 PyList::new(py, iter)
126 }
127}
128
129pub(crate) fn decimal_to_pyobject_iter<'py, 'a>(
130 py: Python<'py>,
131 ca: &'a DecimalChunked,
132) -> PyResult<impl ExactSizeIterator<Item = Option<Bound<'py, PyAny>>> + use<'py, 'a>> {
133 let utils = pl_utils(py).bind(py);
134 let convert = utils.getattr(intern!(py, "to_py_decimal"))?;
135 let py_scale = (-(ca.scale() as i32)).into_pyobject(py)?;
136 let py_precision = ca.precision().unwrap_or(39).into_pyobject(py)?;
138 Ok(ca.iter().map(move |opt_v| {
139 opt_v.map(|v| {
140 const N: usize = 3;
142 let mut buf = [0_u128; N];
143 let n_digits = decimal_to_digits(v.abs(), &mut buf);
144 let buf = unsafe {
145 std::slice::from_raw_parts(
146 buf.as_slice().as_ptr() as *const u8,
147 N * size_of::<u128>(),
148 )
149 };
150 let digits = PyTuple::new(py, buf.iter().take(n_digits)).unwrap();
151 convert
152 .call1((v.is_negative() as u8, digits, &py_precision, &py_scale))
153 .unwrap()
154 })
155 }))
156}