1#[cfg(feature = "iejoin")]
2use polars::prelude::InequalityOperator;
3use polars::series::ops::NullBehavior;
4use polars_core::series::IsSorted;
5#[cfg(feature = "string_normalize")]
6use polars_ops::chunked_array::UnicodeForm;
7use polars_ops::series::InterpolationMethod;
8#[cfg(feature = "search_sorted")]
9use polars_ops::series::SearchSortedSide;
10use polars_plan::dsl::function_expr::rolling_by::RollingFunctionBy;
11use polars_plan::dsl::{BooleanFunction, StringFunction, TemporalFunction};
12use polars_plan::plans::DynLiteralValue;
13use polars_plan::prelude::{
14 AExpr, FunctionExpr, GroupbyOptions, IRAggExpr, LiteralValue, Operator, PowFunction,
15 WindowMapping, WindowType,
16};
17use polars_time::prelude::RollingGroupOptions;
18use polars_time::{Duration, DynamicGroupOptions};
19use pyo3::IntoPyObjectExt;
20use pyo3::exceptions::PyNotImplementedError;
21use pyo3::prelude::*;
22use pyo3::types::PyTuple;
23
24use crate::Wrap;
25use crate::series::PySeries;
26
27#[pyclass]
28pub struct Alias {
29 #[pyo3(get)]
30 expr: usize,
31 #[pyo3(get)]
32 name: PyObject,
33}
34
35#[pyclass]
36pub struct Column {
37 #[pyo3(get)]
38 name: PyObject,
39}
40
41#[pyclass]
42pub struct Literal {
43 #[pyo3(get)]
44 value: PyObject,
45 #[pyo3(get)]
46 dtype: PyObject,
47}
48
49#[pyclass(name = "Operator", eq)]
50#[derive(Copy, Clone, PartialEq)]
51pub enum PyOperator {
52 Eq,
53 EqValidity,
54 NotEq,
55 NotEqValidity,
56 Lt,
57 LtEq,
58 Gt,
59 GtEq,
60 Plus,
61 Minus,
62 Multiply,
63 Divide,
64 TrueDivide,
65 FloorDivide,
66 Modulus,
67 And,
68 Or,
69 Xor,
70 LogicalAnd,
71 LogicalOr,
72}
73
74#[pymethods]
75impl PyOperator {
76 fn __hash__(&self) -> isize {
77 *self as isize
78 }
79}
80
81impl<'py> IntoPyObject<'py> for Wrap<Operator> {
82 type Target = PyOperator;
83 type Output = Bound<'py, Self::Target>;
84 type Error = PyErr;
85
86 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
87 match self.0 {
88 Operator::Eq => PyOperator::Eq,
89 Operator::EqValidity => PyOperator::EqValidity,
90 Operator::NotEq => PyOperator::NotEq,
91 Operator::NotEqValidity => PyOperator::NotEqValidity,
92 Operator::Lt => PyOperator::Lt,
93 Operator::LtEq => PyOperator::LtEq,
94 Operator::Gt => PyOperator::Gt,
95 Operator::GtEq => PyOperator::GtEq,
96 Operator::Plus => PyOperator::Plus,
97 Operator::Minus => PyOperator::Minus,
98 Operator::Multiply => PyOperator::Multiply,
99 Operator::Divide => PyOperator::Divide,
100 Operator::TrueDivide => PyOperator::TrueDivide,
101 Operator::FloorDivide => PyOperator::FloorDivide,
102 Operator::Modulus => PyOperator::Modulus,
103 Operator::And => PyOperator::And,
104 Operator::Or => PyOperator::Or,
105 Operator::Xor => PyOperator::Xor,
106 Operator::LogicalAnd => PyOperator::LogicalAnd,
107 Operator::LogicalOr => PyOperator::LogicalOr,
108 }
109 .into_pyobject(py)
110 }
111}
112
113#[cfg(feature = "iejoin")]
114impl<'py> IntoPyObject<'py> for Wrap<InequalityOperator> {
115 type Target = PyOperator;
116 type Output = Bound<'py, Self::Target>;
117 type Error = PyErr;
118
119 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
120 match self.0 {
121 InequalityOperator::Lt => PyOperator::Lt,
122 InequalityOperator::LtEq => PyOperator::LtEq,
123 InequalityOperator::Gt => PyOperator::Gt,
124 InequalityOperator::GtEq => PyOperator::GtEq,
125 }
126 .into_pyobject(py)
127 }
128}
129
130#[pyclass(name = "StringFunction", eq)]
131#[derive(Copy, Clone, PartialEq)]
132pub enum PyStringFunction {
133 ConcatHorizontal,
134 ConcatVertical,
135 Contains,
136 CountMatches,
137 EndsWith,
138 Extract,
139 ExtractAll,
140 ExtractGroups,
141 Find,
142 ToInteger,
143 LenBytes,
144 LenChars,
145 Lowercase,
146 JsonDecode,
147 JsonPathMatch,
148 Replace,
149 Reverse,
150 PadStart,
151 PadEnd,
152 Slice,
153 Head,
154 Tail,
155 HexEncode,
156 HexDecode,
157 Base64Encode,
158 Base64Decode,
159 StartsWith,
160 StripChars,
161 StripCharsStart,
162 StripCharsEnd,
163 StripPrefix,
164 StripSuffix,
165 SplitExact,
166 SplitN,
167 Strptime,
168 Split,
169 ToDecimal,
170 Titlecase,
171 Uppercase,
172 ZFill,
173 ContainsAny,
174 ReplaceMany,
175 EscapeRegex,
176 Normalize,
177}
178
179#[pymethods]
180impl PyStringFunction {
181 fn __hash__(&self) -> isize {
182 *self as isize
183 }
184}
185
186#[pyclass(name = "BooleanFunction", eq)]
187#[derive(Copy, Clone, PartialEq)]
188pub enum PyBooleanFunction {
189 Any,
190 All,
191 IsNull,
192 IsNotNull,
193 IsFinite,
194 IsInfinite,
195 IsNan,
196 IsNotNan,
197 IsFirstDistinct,
198 IsLastDistinct,
199 IsUnique,
200 IsDuplicated,
201 IsBetween,
202 IsIn,
203 AllHorizontal,
204 AnyHorizontal,
205 Not,
206}
207
208#[pymethods]
209impl PyBooleanFunction {
210 fn __hash__(&self) -> isize {
211 *self as isize
212 }
213}
214
215#[pyclass(name = "TemporalFunction", eq)]
216#[derive(Copy, Clone, PartialEq)]
217pub enum PyTemporalFunction {
218 Millennium,
219 Century,
220 Year,
221 IsLeapYear,
222 IsoYear,
223 Quarter,
224 Month,
225 Week,
226 WeekDay,
227 Day,
228 OrdinalDay,
229 Time,
230 Date,
231 Datetime,
232 Duration,
233 Hour,
234 Minute,
235 Second,
236 Millisecond,
237 Microsecond,
238 Nanosecond,
239 TotalDays,
240 TotalHours,
241 TotalMinutes,
242 TotalSeconds,
243 TotalMilliseconds,
244 TotalMicroseconds,
245 TotalNanoseconds,
246 ToString,
247 CastTimeUnit,
248 WithTimeUnit,
249 ConvertTimeZone,
250 TimeStamp,
251 Truncate,
252 OffsetBy,
253 MonthStart,
254 MonthEnd,
255 BaseUtcOffset,
256 DSTOffset,
257 Round,
258 Replace,
259 ReplaceTimeZone,
260 Combine,
261 DatetimeFunction,
262}
263
264#[pymethods]
265impl PyTemporalFunction {
266 fn __hash__(&self) -> isize {
267 *self as isize
268 }
269}
270
271#[pyclass]
272pub struct BinaryExpr {
273 #[pyo3(get)]
274 left: usize,
275 #[pyo3(get)]
276 op: PyObject,
277 #[pyo3(get)]
278 right: usize,
279}
280
281#[pyclass]
282pub struct Cast {
283 #[pyo3(get)]
284 expr: usize,
285 #[pyo3(get)]
286 dtype: PyObject,
287 #[pyo3(get)]
291 options: u8,
292}
293
294#[pyclass]
295pub struct Sort {
296 #[pyo3(get)]
297 expr: usize,
298 #[pyo3(get)]
299 options: (bool, bool, bool),
301}
302
303#[pyclass]
304pub struct Gather {
305 #[pyo3(get)]
306 expr: usize,
307 #[pyo3(get)]
308 idx: usize,
309 #[pyo3(get)]
310 scalar: bool,
311}
312
313#[pyclass]
314pub struct Filter {
315 #[pyo3(get)]
316 input: usize,
317 #[pyo3(get)]
318 by: usize,
319}
320
321#[pyclass]
322pub struct SortBy {
323 #[pyo3(get)]
324 expr: usize,
325 #[pyo3(get)]
326 by: Vec<usize>,
327 #[pyo3(get)]
328 sort_options: (bool, Vec<bool>, Vec<bool>),
330}
331
332#[pyclass]
333pub struct Agg {
334 #[pyo3(get)]
335 name: PyObject,
336 #[pyo3(get)]
337 arguments: Vec<usize>,
338 #[pyo3(get)]
339 options: PyObject,
341}
342
343#[pyclass]
344pub struct Ternary {
345 #[pyo3(get)]
346 predicate: usize,
347 #[pyo3(get)]
348 truthy: usize,
349 #[pyo3(get)]
350 falsy: usize,
351}
352
353#[pyclass]
354pub struct Function {
355 #[pyo3(get)]
356 input: Vec<usize>,
357 #[pyo3(get)]
358 function_data: PyObject,
359 #[pyo3(get)]
360 options: PyObject,
361}
362
363#[pyclass]
364pub struct Slice {
365 #[pyo3(get)]
366 input: usize,
367 #[pyo3(get)]
368 offset: usize,
369 #[pyo3(get)]
370 length: usize,
371}
372
373#[pyclass]
374pub struct Len {}
375
376#[pyclass]
377pub struct Window {
378 #[pyo3(get)]
379 function: usize,
380 #[pyo3(get)]
381 partition_by: Vec<usize>,
382 #[pyo3(get)]
383 order_by: Option<usize>,
384 #[pyo3(get)]
385 order_by_descending: bool,
386 #[pyo3(get)]
387 order_by_nulls_last: bool,
388 #[pyo3(get)]
389 options: PyObject,
390}
391
392#[pyclass(name = "WindowMapping")]
393pub struct PyWindowMapping {
394 inner: WindowMapping,
395}
396
397#[pymethods]
398impl PyWindowMapping {
399 #[getter]
400 fn kind(&self) -> &str {
401 self.inner.into()
402 }
403}
404
405impl<'py> IntoPyObject<'py> for Wrap<Duration> {
406 type Target = PyTuple;
407 type Output = Bound<'py, Self::Target>;
408 type Error = PyErr;
409
410 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
411 (
412 self.0.months(),
413 self.0.weeks(),
414 self.0.days(),
415 self.0.nanoseconds(),
416 self.0.parsed_int,
417 self.0.negative(),
418 )
419 .into_pyobject(py)
420 }
421}
422
423#[pyclass(name = "RollingGroupOptions")]
424pub struct PyRollingGroupOptions {
425 inner: RollingGroupOptions,
426}
427
428#[pymethods]
429impl PyRollingGroupOptions {
430 #[getter]
431 fn index_column(&self) -> &str {
432 self.inner.index_column.as_str()
433 }
434
435 #[getter]
436 fn period(&self) -> Wrap<Duration> {
437 Wrap(self.inner.period)
438 }
439
440 #[getter]
441 fn offset(&self) -> Wrap<Duration> {
442 Wrap(self.inner.offset)
443 }
444
445 #[getter]
446 fn closed_window(&self) -> &str {
447 self.inner.closed_window.into()
448 }
449}
450
451#[pyclass(name = "DynamicGroupOptions")]
452pub struct PyDynamicGroupOptions {
453 inner: DynamicGroupOptions,
454}
455
456#[pymethods]
457impl PyDynamicGroupOptions {
458 #[getter]
459 fn index_column(&self) -> &str {
460 self.inner.index_column.as_str()
461 }
462
463 #[getter]
464 fn every(&self) -> Wrap<Duration> {
465 Wrap(self.inner.every)
466 }
467
468 #[getter]
469 fn period(&self) -> Wrap<Duration> {
470 Wrap(self.inner.period)
471 }
472
473 #[getter]
474 fn offset(&self) -> Wrap<Duration> {
475 Wrap(self.inner.offset)
476 }
477
478 #[getter]
479 fn label(&self) -> &str {
480 self.inner.label.into()
481 }
482
483 #[getter]
484 fn include_boundaries(&self) -> bool {
485 self.inner.include_boundaries
486 }
487
488 #[getter]
489 fn closed_window(&self) -> &str {
490 self.inner.closed_window.into()
491 }
492 #[getter]
493 fn start_by(&self) -> &str {
494 self.inner.start_by.into()
495 }
496}
497
498#[pyclass(name = "GroupbyOptions")]
499pub struct PyGroupbyOptions {
500 inner: GroupbyOptions,
501}
502
503impl PyGroupbyOptions {
504 pub(crate) fn new(inner: GroupbyOptions) -> Self {
505 Self { inner }
506 }
507}
508
509#[pymethods]
510impl PyGroupbyOptions {
511 #[getter]
512 fn slice(&self) -> Option<(i64, usize)> {
513 self.inner.slice
514 }
515
516 #[getter]
517 fn dynamic(&self) -> Option<PyDynamicGroupOptions> {
518 self.inner
519 .dynamic
520 .as_ref()
521 .map(|f| PyDynamicGroupOptions { inner: f.clone() })
522 }
523
524 #[getter]
525 fn rolling(&self) -> Option<PyRollingGroupOptions> {
526 self.inner
527 .rolling
528 .as_ref()
529 .map(|f| PyRollingGroupOptions { inner: f.clone() })
530 }
531}
532
533pub(crate) fn into_py(py: Python<'_>, expr: &AExpr) -> PyResult<PyObject> {
534 match expr {
535 AExpr::Explode { .. } => Err(PyNotImplementedError::new_err("explode")),
536 AExpr::Alias(inner, name) => Alias {
537 expr: inner.0,
538 name: name.into_py_any(py)?,
539 }
540 .into_py_any(py),
541 AExpr::Column(name) => Column {
542 name: name.into_py_any(py)?,
543 }
544 .into_py_any(py),
545 AExpr::Literal(lit) => {
546 use polars_core::prelude::AnyValue;
547 let dtype: PyObject = Wrap(lit.get_datatype()).into_py_any(py)?;
548 let py_value = match lit {
549 LiteralValue::Dyn(d) => match d {
550 DynLiteralValue::Int(v) => v.into_py_any(py)?,
551 DynLiteralValue::Float(v) => v.into_py_any(py)?,
552 DynLiteralValue::Str(v) => v.into_py_any(py)?,
553 DynLiteralValue::List(_) => todo!(),
554 },
555 LiteralValue::Scalar(sc) => {
556 match sc.as_any_value() {
557 AnyValue::Duration(delta, _) => delta.into_py_any(py)?,
562 any => Wrap(any).into_py_any(py)?,
563 }
564 },
565 LiteralValue::Range(_) => {
566 return Err(PyNotImplementedError::new_err("range literal"));
567 },
568 LiteralValue::Series(s) => PySeries::new((**s).clone()).into_py_any(py)?,
569 };
570
571 Literal {
572 value: py_value,
573 dtype,
574 }
575 }
576 .into_py_any(py),
577 AExpr::BinaryExpr { left, op, right } => BinaryExpr {
578 left: left.0,
579 op: Wrap(*op).into_py_any(py)?,
580 right: right.0,
581 }
582 .into_py_any(py),
583 AExpr::Cast {
584 expr,
585 dtype,
586 options,
587 } => Cast {
588 expr: expr.0,
589 dtype: Wrap(dtype.clone()).into_py_any(py)?,
590 options: *options as u8,
591 }
592 .into_py_any(py),
593 AExpr::Sort { expr, options } => Sort {
594 expr: expr.0,
595 options: (
596 options.maintain_order,
597 options.nulls_last,
598 options.descending,
599 ),
600 }
601 .into_py_any(py),
602 AExpr::Gather {
603 expr,
604 idx,
605 returns_scalar,
606 } => Gather {
607 expr: expr.0,
608 idx: idx.0,
609 scalar: *returns_scalar,
610 }
611 .into_py_any(py),
612 AExpr::Filter { input, by } => Filter {
613 input: input.0,
614 by: by.0,
615 }
616 .into_py_any(py),
617 AExpr::SortBy {
618 expr,
619 by,
620 sort_options,
621 } => SortBy {
622 expr: expr.0,
623 by: by.iter().map(|n| n.0).collect(),
624 sort_options: (
625 sort_options.maintain_order,
626 sort_options.nulls_last.clone(),
627 sort_options.descending.clone(),
628 ),
629 }
630 .into_py_any(py),
631 AExpr::Agg(aggexpr) => match aggexpr {
632 IRAggExpr::Min {
633 input,
634 propagate_nans,
635 } => Agg {
636 name: "min".into_py_any(py)?,
637 arguments: vec![input.0],
638 options: propagate_nans.into_py_any(py)?,
639 },
640 IRAggExpr::Max {
641 input,
642 propagate_nans,
643 } => Agg {
644 name: "max".into_py_any(py)?,
645 arguments: vec![input.0],
646 options: propagate_nans.into_py_any(py)?,
647 },
648 IRAggExpr::Median(n) => Agg {
649 name: "median".into_py_any(py)?,
650 arguments: vec![n.0],
651 options: py.None(),
652 },
653 IRAggExpr::NUnique(n) => Agg {
654 name: "n_unique".into_py_any(py)?,
655 arguments: vec![n.0],
656 options: py.None(),
657 },
658 IRAggExpr::First(n) => Agg {
659 name: "first".into_py_any(py)?,
660 arguments: vec![n.0],
661 options: py.None(),
662 },
663 IRAggExpr::Last(n) => Agg {
664 name: "last".into_py_any(py)?,
665 arguments: vec![n.0],
666 options: py.None(),
667 },
668 IRAggExpr::Mean(n) => Agg {
669 name: "mean".into_py_any(py)?,
670 arguments: vec![n.0],
671 options: py.None(),
672 },
673 IRAggExpr::Implode(n) => Agg {
674 name: "implode".into_py_any(py)?,
675 arguments: vec![n.0],
676 options: py.None(),
677 },
678 IRAggExpr::Quantile {
679 expr,
680 quantile,
681 method: interpol,
682 } => Agg {
683 name: "quantile".into_py_any(py)?,
684 arguments: vec![expr.0, quantile.0],
685 options: Into::<&str>::into(interpol).into_py_any(py)?,
686 },
687 IRAggExpr::Sum(n) => Agg {
688 name: "sum".into_py_any(py)?,
689 arguments: vec![n.0],
690 options: py.None(),
691 },
692 IRAggExpr::Count(n, include_null) => Agg {
693 name: "count".into_py_any(py)?,
694 arguments: vec![n.0],
695 options: include_null.into_py_any(py)?,
696 },
697 IRAggExpr::Std(n, ddof) => Agg {
698 name: "std".into_py_any(py)?,
699 arguments: vec![n.0],
700 options: ddof.into_py_any(py)?,
701 },
702 IRAggExpr::Var(n, ddof) => Agg {
703 name: "var".into_py_any(py)?,
704 arguments: vec![n.0],
705 options: ddof.into_py_any(py)?,
706 },
707 IRAggExpr::AggGroups(n) => Agg {
708 name: "agg_groups".into_py_any(py)?,
709 arguments: vec![n.0],
710 options: py.None(),
711 },
712 }
713 .into_py_any(py),
714 AExpr::Ternary {
715 predicate,
716 truthy,
717 falsy,
718 } => Ternary {
719 predicate: predicate.0,
720 truthy: truthy.0,
721 falsy: falsy.0,
722 }
723 .into_py_any(py),
724 AExpr::AnonymousFunction { .. } => Err(PyNotImplementedError::new_err("anonymousfunction")),
725 AExpr::Function {
726 input,
727 function,
728 options: _,
730 } => Function {
731 input: input.iter().map(|n| n.node().0).collect(),
732 function_data: match function {
733 FunctionExpr::ArrayExpr(_) => {
734 return Err(PyNotImplementedError::new_err("array expr"));
735 },
736 FunctionExpr::BinaryExpr(_) => {
737 return Err(PyNotImplementedError::new_err("binary expr"));
738 },
739 FunctionExpr::Categorical(_) => {
740 return Err(PyNotImplementedError::new_err("categorical expr"));
741 },
742 FunctionExpr::ListExpr(_) => {
743 return Err(PyNotImplementedError::new_err("list expr"));
744 },
745 FunctionExpr::Bitwise(_) => {
746 return Err(PyNotImplementedError::new_err("bitwise expr"));
747 },
748 FunctionExpr::StringExpr(strfun) => match strfun {
749 StringFunction::ConcatHorizontal {
750 delimiter,
751 ignore_nulls,
752 } => (
753 PyStringFunction::ConcatHorizontal,
754 delimiter.as_str(),
755 ignore_nulls,
756 )
757 .into_py_any(py),
758 StringFunction::ConcatVertical {
759 delimiter,
760 ignore_nulls,
761 } => (
762 PyStringFunction::ConcatVertical,
763 delimiter.as_str(),
764 ignore_nulls,
765 )
766 .into_py_any(py),
767 #[cfg(feature = "regex")]
768 StringFunction::Contains { literal, strict } => {
769 (PyStringFunction::Contains, literal, strict).into_py_any(py)
770 },
771 StringFunction::CountMatches(literal) => {
772 (PyStringFunction::CountMatches, literal).into_py_any(py)
773 },
774 StringFunction::EndsWith => (PyStringFunction::EndsWith,).into_py_any(py),
775 StringFunction::Extract(group_index) => {
776 (PyStringFunction::Extract, group_index).into_py_any(py)
777 },
778 StringFunction::ExtractAll => (PyStringFunction::ExtractAll,).into_py_any(py),
779 #[cfg(feature = "extract_groups")]
780 StringFunction::ExtractGroups { dtype, pat } => (
781 PyStringFunction::ExtractGroups,
782 &Wrap(dtype.clone()),
783 pat.as_str(),
784 )
785 .into_py_any(py),
786 #[cfg(feature = "regex")]
787 StringFunction::Find { literal, strict } => {
788 (PyStringFunction::Find, literal, strict).into_py_any(py)
789 },
790 StringFunction::ToInteger(strict) => {
791 (PyStringFunction::ToInteger, strict).into_py_any(py)
792 },
793 StringFunction::LenBytes => (PyStringFunction::LenBytes,).into_py_any(py),
794 StringFunction::LenChars => (PyStringFunction::LenChars,).into_py_any(py),
795 StringFunction::Lowercase => (PyStringFunction::Lowercase,).into_py_any(py),
796 #[cfg(feature = "extract_jsonpath")]
797 StringFunction::JsonDecode {
798 dtype: _,
799 infer_schema_len,
800 } => (PyStringFunction::JsonDecode, infer_schema_len).into_py_any(py),
801 #[cfg(feature = "extract_jsonpath")]
802 StringFunction::JsonPathMatch => {
803 (PyStringFunction::JsonPathMatch,).into_py_any(py)
804 },
805 #[cfg(feature = "regex")]
806 StringFunction::Replace { n, literal } => {
807 (PyStringFunction::Replace, n, literal).into_py_any(py)
808 },
809 #[cfg(feature = "string_normalize")]
810 StringFunction::Normalize { form } => (
811 PyStringFunction::Normalize,
812 match form {
813 UnicodeForm::NFC => "nfc",
814 UnicodeForm::NFKC => "nfkc",
815 UnicodeForm::NFD => "nfd",
816 UnicodeForm::NFKD => "nfkd",
817 },
818 )
819 .into_py_any(py),
820 StringFunction::Reverse => (PyStringFunction::Reverse,).into_py_any(py),
821 StringFunction::PadStart { length, fill_char } => {
822 (PyStringFunction::PadStart, length, fill_char).into_py_any(py)
823 },
824 StringFunction::PadEnd { length, fill_char } => {
825 (PyStringFunction::PadEnd, length, fill_char).into_py_any(py)
826 },
827 StringFunction::Slice => (PyStringFunction::Slice,).into_py_any(py),
828 StringFunction::Head => (PyStringFunction::Head,).into_py_any(py),
829 StringFunction::Tail => (PyStringFunction::Tail,).into_py_any(py),
830 StringFunction::HexEncode => (PyStringFunction::HexEncode,).into_py_any(py),
831 #[cfg(feature = "binary_encoding")]
832 StringFunction::HexDecode(strict) => {
833 (PyStringFunction::HexDecode, strict).into_py_any(py)
834 },
835 StringFunction::Base64Encode => {
836 (PyStringFunction::Base64Encode,).into_py_any(py)
837 },
838 #[cfg(feature = "binary_encoding")]
839 StringFunction::Base64Decode(strict) => {
840 (PyStringFunction::Base64Decode, strict).into_py_any(py)
841 },
842 StringFunction::StartsWith => (PyStringFunction::StartsWith,).into_py_any(py),
843 StringFunction::StripChars => (PyStringFunction::StripChars,).into_py_any(py),
844 StringFunction::StripCharsStart => {
845 (PyStringFunction::StripCharsStart,).into_py_any(py)
846 },
847 StringFunction::StripCharsEnd => {
848 (PyStringFunction::StripCharsEnd,).into_py_any(py)
849 },
850 StringFunction::StripPrefix => (PyStringFunction::StripPrefix,).into_py_any(py),
851 StringFunction::StripSuffix => (PyStringFunction::StripSuffix,).into_py_any(py),
852 StringFunction::SplitExact { n, inclusive } => {
853 (PyStringFunction::SplitExact, n, inclusive).into_py_any(py)
854 },
855 StringFunction::SplitN(n) => (PyStringFunction::SplitN, n).into_py_any(py),
856 StringFunction::Strptime(_, options) => (
857 PyStringFunction::Strptime,
858 options.format.as_ref().map(|s| s.as_str()),
859 options.strict,
860 options.exact,
861 options.cache,
862 )
863 .into_py_any(py),
864 StringFunction::Split(inclusive) => {
865 (PyStringFunction::Split, inclusive).into_py_any(py)
866 },
867 StringFunction::ToDecimal(inference_length) => {
868 (PyStringFunction::ToDecimal, inference_length).into_py_any(py)
869 },
870 #[cfg(feature = "nightly")]
871 StringFunction::Titlecase => (PyStringFunction::Titlecase,).into_py_any(py),
872 StringFunction::Uppercase => (PyStringFunction::Uppercase,).into_py_any(py),
873 StringFunction::ZFill => (PyStringFunction::ZFill,).into_py_any(py),
874 #[cfg(feature = "find_many")]
875 StringFunction::ContainsAny {
876 ascii_case_insensitive,
877 } => (PyStringFunction::ContainsAny, ascii_case_insensitive).into_py_any(py),
878 #[cfg(feature = "find_many")]
879 StringFunction::ReplaceMany {
880 ascii_case_insensitive,
881 } => (PyStringFunction::ReplaceMany, ascii_case_insensitive).into_py_any(py),
882 #[cfg(feature = "find_many")]
883 StringFunction::ExtractMany { .. } => {
884 return Err(PyNotImplementedError::new_err("extract_many"));
885 },
886 #[cfg(feature = "find_many")]
887 StringFunction::FindMany { .. } => {
888 return Err(PyNotImplementedError::new_err("find_many"));
889 },
890 #[cfg(feature = "regex")]
891 StringFunction::EscapeRegex => (PyStringFunction::EscapeRegex,).into_py_any(py),
892 },
893 FunctionExpr::StructExpr(_) => {
894 return Err(PyNotImplementedError::new_err("struct expr"));
895 },
896 FunctionExpr::TemporalExpr(fun) => match fun {
897 TemporalFunction::Millennium => {
898 (PyTemporalFunction::Millennium,).into_py_any(py)
899 },
900 TemporalFunction::Century => (PyTemporalFunction::Century,).into_py_any(py),
901 TemporalFunction::Year => (PyTemporalFunction::Year,).into_py_any(py),
902 TemporalFunction::IsLeapYear => {
903 (PyTemporalFunction::IsLeapYear,).into_py_any(py)
904 },
905 TemporalFunction::IsoYear => (PyTemporalFunction::IsoYear,).into_py_any(py),
906 TemporalFunction::Quarter => (PyTemporalFunction::Quarter,).into_py_any(py),
907 TemporalFunction::Month => (PyTemporalFunction::Month,).into_py_any(py),
908 TemporalFunction::Week => (PyTemporalFunction::Week,).into_py_any(py),
909 TemporalFunction::WeekDay => (PyTemporalFunction::WeekDay,).into_py_any(py),
910 TemporalFunction::Day => (PyTemporalFunction::Day,).into_py_any(py),
911 TemporalFunction::OrdinalDay => {
912 (PyTemporalFunction::OrdinalDay,).into_py_any(py)
913 },
914 TemporalFunction::Time => (PyTemporalFunction::Time,).into_py_any(py),
915 TemporalFunction::Date => (PyTemporalFunction::Date,).into_py_any(py),
916 TemporalFunction::Datetime => (PyTemporalFunction::Datetime,).into_py_any(py),
917 TemporalFunction::Duration(time_unit) => {
918 (PyTemporalFunction::Duration, Wrap(*time_unit)).into_py_any(py)
919 },
920 TemporalFunction::Hour => (PyTemporalFunction::Hour,).into_py_any(py),
921 TemporalFunction::Minute => (PyTemporalFunction::Minute,).into_py_any(py),
922 TemporalFunction::Second => (PyTemporalFunction::Second,).into_py_any(py),
923 TemporalFunction::Millisecond => {
924 (PyTemporalFunction::Millisecond,).into_py_any(py)
925 },
926 TemporalFunction::Microsecond => {
927 (PyTemporalFunction::Microsecond,).into_py_any(py)
928 },
929 TemporalFunction::Nanosecond => {
930 (PyTemporalFunction::Nanosecond,).into_py_any(py)
931 },
932 TemporalFunction::TotalDays => (PyTemporalFunction::TotalDays,).into_py_any(py),
933 TemporalFunction::TotalHours => {
934 (PyTemporalFunction::TotalHours,).into_py_any(py)
935 },
936 TemporalFunction::TotalMinutes => {
937 (PyTemporalFunction::TotalMinutes,).into_py_any(py)
938 },
939 TemporalFunction::TotalSeconds => {
940 (PyTemporalFunction::TotalSeconds,).into_py_any(py)
941 },
942 TemporalFunction::TotalMilliseconds => {
943 (PyTemporalFunction::TotalMilliseconds,).into_py_any(py)
944 },
945 TemporalFunction::TotalMicroseconds => {
946 (PyTemporalFunction::TotalMicroseconds,).into_py_any(py)
947 },
948 TemporalFunction::TotalNanoseconds => {
949 (PyTemporalFunction::TotalNanoseconds,).into_py_any(py)
950 },
951 TemporalFunction::ToString(format) => {
952 (PyTemporalFunction::ToString, format).into_py_any(py)
953 },
954 TemporalFunction::CastTimeUnit(time_unit) => {
955 (PyTemporalFunction::CastTimeUnit, Wrap(*time_unit)).into_py_any(py)
956 },
957 TemporalFunction::WithTimeUnit(time_unit) => {
958 (PyTemporalFunction::WithTimeUnit, Wrap(*time_unit)).into_py_any(py)
959 },
960 #[cfg(feature = "timezones")]
961 TemporalFunction::ConvertTimeZone(time_zone) => {
962 (PyTemporalFunction::ConvertTimeZone, time_zone.as_str()).into_py_any(py)
963 },
964 TemporalFunction::TimeStamp(time_unit) => {
965 (PyTemporalFunction::TimeStamp, Wrap(*time_unit)).into_py_any(py)
966 },
967 TemporalFunction::Truncate => (PyTemporalFunction::Truncate,).into_py_any(py),
968 TemporalFunction::OffsetBy => (PyTemporalFunction::OffsetBy,).into_py_any(py),
969 TemporalFunction::MonthStart => {
970 (PyTemporalFunction::MonthStart,).into_py_any(py)
971 },
972 TemporalFunction::MonthEnd => (PyTemporalFunction::MonthEnd,).into_py_any(py),
973 #[cfg(feature = "timezones")]
974 TemporalFunction::BaseUtcOffset => {
975 (PyTemporalFunction::BaseUtcOffset,).into_py_any(py)
976 },
977 #[cfg(feature = "timezones")]
978 TemporalFunction::DSTOffset => (PyTemporalFunction::DSTOffset,).into_py_any(py),
979 TemporalFunction::Round => (PyTemporalFunction::Round,).into_py_any(py),
980 TemporalFunction::Replace => (PyTemporalFunction::Replace).into_py_any(py),
981 #[cfg(feature = "timezones")]
982 TemporalFunction::ReplaceTimeZone(time_zone, non_existent) => (
983 PyTemporalFunction::ReplaceTimeZone,
984 time_zone.as_ref().map(|s| s.as_str()),
985 Into::<&str>::into(non_existent),
986 )
987 .into_py_any(py),
988 TemporalFunction::Combine(time_unit) => {
989 (PyTemporalFunction::Combine, Wrap(*time_unit)).into_py_any(py)
990 },
991 TemporalFunction::DatetimeFunction {
992 time_unit,
993 time_zone,
994 } => (
995 PyTemporalFunction::DatetimeFunction,
996 Wrap(*time_unit),
997 time_zone.as_ref().map(|s| s.as_str()),
998 )
999 .into_py_any(py),
1000 },
1001 FunctionExpr::Boolean(boolfun) => match boolfun {
1002 BooleanFunction::Any { ignore_nulls } => {
1003 (PyBooleanFunction::Any, *ignore_nulls).into_py_any(py)
1004 },
1005 BooleanFunction::All { ignore_nulls } => {
1006 (PyBooleanFunction::All, *ignore_nulls).into_py_any(py)
1007 },
1008 BooleanFunction::IsNull => (PyBooleanFunction::IsNull,).into_py_any(py),
1009 BooleanFunction::IsNotNull => (PyBooleanFunction::IsNotNull,).into_py_any(py),
1010 BooleanFunction::IsFinite => (PyBooleanFunction::IsFinite,).into_py_any(py),
1011 BooleanFunction::IsInfinite => (PyBooleanFunction::IsInfinite,).into_py_any(py),
1012 BooleanFunction::IsNan => (PyBooleanFunction::IsNan,).into_py_any(py),
1013 BooleanFunction::IsNotNan => (PyBooleanFunction::IsNotNan,).into_py_any(py),
1014 BooleanFunction::IsFirstDistinct => {
1015 (PyBooleanFunction::IsFirstDistinct,).into_py_any(py)
1016 },
1017 BooleanFunction::IsLastDistinct => {
1018 (PyBooleanFunction::IsLastDistinct,).into_py_any(py)
1019 },
1020 BooleanFunction::IsUnique => (PyBooleanFunction::IsUnique,).into_py_any(py),
1021 BooleanFunction::IsDuplicated => {
1022 (PyBooleanFunction::IsDuplicated,).into_py_any(py)
1023 },
1024 BooleanFunction::IsBetween { closed } => {
1025 (PyBooleanFunction::IsBetween, Into::<&str>::into(closed)).into_py_any(py)
1026 },
1027 #[cfg(feature = "is_in")]
1028 BooleanFunction::IsIn { nulls_equal } => {
1029 (PyBooleanFunction::IsIn, nulls_equal).into_py_any(py)
1030 },
1031 BooleanFunction::AllHorizontal => {
1032 (PyBooleanFunction::AllHorizontal,).into_py_any(py)
1033 },
1034 BooleanFunction::AnyHorizontal => {
1035 (PyBooleanFunction::AnyHorizontal,).into_py_any(py)
1036 },
1037 BooleanFunction::Not => (PyBooleanFunction::Not,).into_py_any(py),
1038 },
1039 FunctionExpr::Abs => ("abs",).into_py_any(py),
1040 #[cfg(feature = "hist")]
1041 FunctionExpr::Hist {
1042 bin_count,
1043 include_category,
1044 include_breakpoint,
1045 } => ("hist", bin_count, include_category, include_breakpoint).into_py_any(py),
1046 FunctionExpr::NullCount => ("null_count",).into_py_any(py),
1047 FunctionExpr::Pow(f) => match f {
1048 PowFunction::Generic => ("pow",).into_py_any(py),
1049 PowFunction::Sqrt => ("sqrt",).into_py_any(py),
1050 PowFunction::Cbrt => ("cbrt",).into_py_any(py),
1051 },
1052 FunctionExpr::Hash(seed, seed_1, seed_2, seed_3) => {
1053 ("hash", seed, seed_1, seed_2, seed_3).into_py_any(py)
1054 },
1055 FunctionExpr::ArgWhere => ("argwhere",).into_py_any(py),
1056 #[cfg(feature = "index_of")]
1057 FunctionExpr::IndexOf => ("index_of",).into_py_any(py),
1058 #[cfg(feature = "search_sorted")]
1059 FunctionExpr::SearchSorted(side) => (
1060 "search_sorted",
1061 match side {
1062 SearchSortedSide::Any => "any",
1063 SearchSortedSide::Left => "left",
1064 SearchSortedSide::Right => "right",
1065 },
1066 )
1067 .into_py_any(py),
1068 FunctionExpr::Range(_) => return Err(PyNotImplementedError::new_err("range")),
1069 #[cfg(feature = "trigonometry")]
1070 FunctionExpr::Trigonometry(trigfun) => {
1071 use polars_plan::dsl::function_expr::trigonometry::TrigonometricFunction;
1072
1073 match trigfun {
1074 TrigonometricFunction::Cos => ("cos",),
1075 TrigonometricFunction::Cot => ("cot",),
1076 TrigonometricFunction::Sin => ("sin",),
1077 TrigonometricFunction::Tan => ("tan",),
1078 TrigonometricFunction::ArcCos => ("arccos",),
1079 TrigonometricFunction::ArcSin => ("arcsin",),
1080 TrigonometricFunction::ArcTan => ("arctan",),
1081 TrigonometricFunction::Cosh => ("cosh",),
1082 TrigonometricFunction::Sinh => ("sinh",),
1083 TrigonometricFunction::Tanh => ("tanh",),
1084 TrigonometricFunction::ArcCosh => ("arccosh",),
1085 TrigonometricFunction::ArcSinh => ("arcsinh",),
1086 TrigonometricFunction::ArcTanh => ("arctanh",),
1087 TrigonometricFunction::Degrees => ("degrees",),
1088 TrigonometricFunction::Radians => ("radians",),
1089 }
1090 .into_py_any(py)
1091 },
1092 #[cfg(feature = "trigonometry")]
1093 FunctionExpr::Atan2 => ("atan2",).into_py_any(py),
1094 #[cfg(feature = "sign")]
1095 FunctionExpr::Sign => ("sign",).into_py_any(py),
1096 FunctionExpr::FillNull => ("fill_null",).into_py_any(py),
1097 FunctionExpr::RollingExpr(rolling) => {
1098 return Err(PyNotImplementedError::new_err(format!("{}", rolling)));
1099 },
1100 FunctionExpr::RollingExprBy(rolling) => match rolling {
1101 RollingFunctionBy::MinBy(_) => {
1102 return Err(PyNotImplementedError::new_err("rolling min by"));
1103 },
1104 RollingFunctionBy::MaxBy(_) => {
1105 return Err(PyNotImplementedError::new_err("rolling max by"));
1106 },
1107 RollingFunctionBy::MeanBy(_) => {
1108 return Err(PyNotImplementedError::new_err("rolling mean by"));
1109 },
1110 RollingFunctionBy::SumBy(_) => {
1111 return Err(PyNotImplementedError::new_err("rolling sum by"));
1112 },
1113 RollingFunctionBy::QuantileBy(_) => {
1114 return Err(PyNotImplementedError::new_err("rolling quantile by"));
1115 },
1116 RollingFunctionBy::VarBy(_) => {
1117 return Err(PyNotImplementedError::new_err("rolling var by"));
1118 },
1119 RollingFunctionBy::StdBy(_) => {
1120 return Err(PyNotImplementedError::new_err("rolling std by"));
1121 },
1122 },
1123 FunctionExpr::ShiftAndFill => ("shift_and_fill",).into_py_any(py),
1124 FunctionExpr::Shift => ("shift",).into_py_any(py),
1125 FunctionExpr::DropNans => ("drop_nans",).into_py_any(py),
1126 FunctionExpr::DropNulls => ("drop_nulls",).into_py_any(py),
1127 FunctionExpr::Mode => ("mode",).into_py_any(py),
1128 FunctionExpr::Skew(bias) => ("skew", bias).into_py_any(py),
1129 FunctionExpr::Kurtosis(fisher, bias) => ("kurtosis", fisher, bias).into_py_any(py),
1130 FunctionExpr::Reshape(_) => return Err(PyNotImplementedError::new_err("reshape")),
1131 #[cfg(feature = "repeat_by")]
1132 FunctionExpr::RepeatBy => ("repeat_by",).into_py_any(py),
1133 FunctionExpr::ArgUnique => ("arg_unique",).into_py_any(py),
1134 FunctionExpr::Repeat => ("repeat",).into_py_any(py),
1135 FunctionExpr::Rank {
1136 options: _,
1137 seed: _,
1138 } => return Err(PyNotImplementedError::new_err("rank")),
1139 FunctionExpr::Clip { has_min, has_max } => {
1140 ("clip", has_min, has_max).into_py_any(py)
1141 },
1142 FunctionExpr::AsStruct => ("as_struct",).into_py_any(py),
1143 #[cfg(feature = "top_k")]
1144 FunctionExpr::TopK { descending } => ("top_k", descending).into_py_any(py),
1145 FunctionExpr::CumCount { reverse } => ("cum_count", reverse).into_py_any(py),
1146 FunctionExpr::CumSum { reverse } => ("cum_sum", reverse).into_py_any(py),
1147 FunctionExpr::CumProd { reverse } => ("cum_prod", reverse).into_py_any(py),
1148 FunctionExpr::CumMin { reverse } => ("cum_min", reverse).into_py_any(py),
1149 FunctionExpr::CumMax { reverse } => ("cum_max", reverse).into_py_any(py),
1150 FunctionExpr::Reverse => ("reverse",).into_py_any(py),
1151 FunctionExpr::ValueCounts {
1152 sort,
1153 parallel,
1154 name,
1155 normalize,
1156 } => ("value_counts", sort, parallel, name.as_str(), normalize).into_py_any(py),
1157 FunctionExpr::UniqueCounts => ("unique_counts",).into_py_any(py),
1158 FunctionExpr::ApproxNUnique => ("approx_n_unique",).into_py_any(py),
1159 FunctionExpr::Coalesce => ("coalesce",).into_py_any(py),
1160 FunctionExpr::ShrinkType => ("shrink_dtype",).into_py_any(py),
1161 FunctionExpr::Diff(null_behaviour) => (
1162 "diff",
1163 match null_behaviour {
1164 NullBehavior::Drop => "drop",
1165 NullBehavior::Ignore => "ignore",
1166 },
1167 )
1168 .into_py_any(py),
1169 #[cfg(feature = "pct_change")]
1170 FunctionExpr::PctChange => ("pct_change",).into_py_any(py),
1171 FunctionExpr::Interpolate(method) => (
1172 "interpolate",
1173 match method {
1174 InterpolationMethod::Linear => "linear",
1175 InterpolationMethod::Nearest => "nearest",
1176 },
1177 )
1178 .into_py_any(py),
1179 FunctionExpr::InterpolateBy => ("interpolate_by",).into_py_any(py),
1180 FunctionExpr::Entropy { base, normalize } => {
1181 ("entropy", base, normalize).into_py_any(py)
1182 },
1183 FunctionExpr::Log { base } => ("log", base).into_py_any(py),
1184 FunctionExpr::Log1p => ("log1p",).into_py_any(py),
1185 FunctionExpr::Exp => ("exp",).into_py_any(py),
1186 FunctionExpr::Unique(maintain_order) => ("unique", maintain_order).into_py_any(py),
1187 FunctionExpr::Round { decimals, mode } => {
1188 ("round", decimals, Into::<&str>::into(mode)).into_py_any(py)
1189 },
1190 FunctionExpr::RoundSF { digits } => ("round_sig_figs", digits).into_py_any(py),
1191 FunctionExpr::Floor => ("floor",).into_py_any(py),
1192 FunctionExpr::Ceil => ("ceil",).into_py_any(py),
1193 FunctionExpr::UpperBound => ("upper_bound",).into_py_any(py),
1194 FunctionExpr::LowerBound => ("lower_bound",).into_py_any(py),
1195 FunctionExpr::Fused(_) => return Err(PyNotImplementedError::new_err("fused")),
1196 FunctionExpr::ConcatExpr(_) => {
1197 return Err(PyNotImplementedError::new_err("concat expr"));
1198 },
1199 FunctionExpr::Correlation { .. } => {
1200 return Err(PyNotImplementedError::new_err("corr"));
1201 },
1202 #[cfg(feature = "peaks")]
1203 FunctionExpr::PeakMin => ("peak_max",).into_py_any(py),
1204 #[cfg(feature = "peaks")]
1205 FunctionExpr::PeakMax => ("peak_min",).into_py_any(py),
1206 #[cfg(feature = "cutqcut")]
1207 FunctionExpr::Cut { .. } => return Err(PyNotImplementedError::new_err("cut")),
1208 #[cfg(feature = "cutqcut")]
1209 FunctionExpr::QCut { .. } => return Err(PyNotImplementedError::new_err("qcut")),
1210 #[cfg(feature = "rle")]
1211 FunctionExpr::RLE => ("rle",).into_py_any(py),
1212 #[cfg(feature = "rle")]
1213 FunctionExpr::RLEID => ("rle_id",).into_py_any(py),
1214 FunctionExpr::ToPhysical => ("to_physical",).into_py_any(py),
1215 FunctionExpr::Random { .. } => {
1216 return Err(PyNotImplementedError::new_err("random"));
1217 },
1218 FunctionExpr::SetSortedFlag(sorted) => (
1219 "set_sorted",
1220 match sorted {
1221 IsSorted::Ascending => "ascending",
1222 IsSorted::Descending => "descending",
1223 IsSorted::Not => "not",
1224 },
1225 )
1226 .into_py_any(py),
1227 #[cfg(feature = "ffi_plugin")]
1228 FunctionExpr::FfiPlugin { .. } => {
1229 return Err(PyNotImplementedError::new_err("ffi plugin"));
1230 },
1231 FunctionExpr::SumHorizontal { ignore_nulls } => {
1232 ("sum_horizontal", ignore_nulls).into_py_any(py)
1233 },
1234 FunctionExpr::MaxHorizontal => ("max_horizontal",).into_py_any(py),
1235 FunctionExpr::MeanHorizontal { ignore_nulls } => {
1236 ("mean_horizontal", ignore_nulls).into_py_any(py)
1237 },
1238 FunctionExpr::MinHorizontal => ("min_horizontal",).into_py_any(py),
1239 FunctionExpr::EwmMean { options: _ } => {
1240 return Err(PyNotImplementedError::new_err("ewm mean"));
1241 },
1242 FunctionExpr::EwmStd { options: _ } => {
1243 return Err(PyNotImplementedError::new_err("ewm std"));
1244 },
1245 FunctionExpr::EwmVar { options: _ } => {
1246 return Err(PyNotImplementedError::new_err("ewm var"));
1247 },
1248 FunctionExpr::Replace => ("replace",).into_py_any(py),
1249 FunctionExpr::ReplaceStrict { return_dtype: _ } => {
1250 ("replace_strict",).into_py_any(py)
1252 },
1253 FunctionExpr::Negate => ("negate",).into_py_any(py),
1254 FunctionExpr::FillNullWithStrategy(_) => {
1255 return Err(PyNotImplementedError::new_err("fill null with strategy"));
1256 },
1257 FunctionExpr::GatherEvery { n, offset } => {
1258 ("gather_every", offset, n).into_py_any(py)
1259 },
1260 FunctionExpr::Reinterpret(signed) => ("reinterpret", signed).into_py_any(py),
1261 FunctionExpr::ExtendConstant => ("extend_constant",).into_py_any(py),
1262 FunctionExpr::Business(_) => {
1263 return Err(PyNotImplementedError::new_err("business"));
1264 },
1265 #[cfg(feature = "top_k")]
1266 FunctionExpr::TopKBy { descending } => ("top_k_by", descending).into_py_any(py),
1267 FunctionExpr::EwmMeanBy { half_life: _ } => {
1268 return Err(PyNotImplementedError::new_err("ewm_mean_by"));
1269 },
1270 }?,
1271 options: py.None(),
1272 }
1273 .into_py_any(py),
1274 AExpr::Window {
1275 function,
1276 partition_by,
1277 order_by,
1278 options,
1279 } => {
1280 let function = function.0;
1281 let partition_by = partition_by.iter().map(|n| n.0).collect();
1282 let order_by_descending = order_by
1283 .map(|(_, options)| options.descending)
1284 .unwrap_or(false);
1285 let order_by_nulls_last = order_by
1286 .map(|(_, options)| options.nulls_last)
1287 .unwrap_or(false);
1288 let order_by = order_by.map(|(n, _)| n.0);
1289
1290 let options = match options {
1291 WindowType::Over(options) => PyWindowMapping { inner: *options }.into_py_any(py)?,
1292 WindowType::Rolling(options) => PyRollingGroupOptions {
1293 inner: options.clone(),
1294 }
1295 .into_py_any(py)?,
1296 };
1297 Window {
1298 function,
1299 partition_by,
1300 order_by,
1301 order_by_descending,
1302 order_by_nulls_last,
1303 options,
1304 }
1305 .into_py_any(py)
1306 },
1307 AExpr::Slice {
1308 input,
1309 offset,
1310 length,
1311 } => Slice {
1312 input: input.0,
1313 offset: offset.0,
1314 length: length.0,
1315 }
1316 .into_py_any(py),
1317 AExpr::Len => Len {}.into_py_any(py),
1318 }
1319}