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 StringFunction::Normalize { form } => (
810 PyStringFunction::Normalize,
811 match form {
812 UnicodeForm::NFC => "nfc",
813 UnicodeForm::NFKC => "nfkc",
814 UnicodeForm::NFD => "nfd",
815 UnicodeForm::NFKD => "nfkd",
816 },
817 )
818 .into_py_any(py),
819 StringFunction::Reverse => (PyStringFunction::Reverse,).into_py_any(py),
820 StringFunction::PadStart { length, fill_char } => {
821 (PyStringFunction::PadStart, length, fill_char).into_py_any(py)
822 },
823 StringFunction::PadEnd { length, fill_char } => {
824 (PyStringFunction::PadEnd, length, fill_char).into_py_any(py)
825 },
826 StringFunction::Slice => (PyStringFunction::Slice,).into_py_any(py),
827 StringFunction::Head => (PyStringFunction::Head,).into_py_any(py),
828 StringFunction::Tail => (PyStringFunction::Tail,).into_py_any(py),
829 StringFunction::HexEncode => (PyStringFunction::HexEncode,).into_py_any(py),
830 #[cfg(feature = "binary_encoding")]
831 StringFunction::HexDecode(strict) => {
832 (PyStringFunction::HexDecode, strict).into_py_any(py)
833 },
834 StringFunction::Base64Encode => {
835 (PyStringFunction::Base64Encode,).into_py_any(py)
836 },
837 #[cfg(feature = "binary_encoding")]
838 StringFunction::Base64Decode(strict) => {
839 (PyStringFunction::Base64Decode, strict).into_py_any(py)
840 },
841 StringFunction::StartsWith => (PyStringFunction::StartsWith,).into_py_any(py),
842 StringFunction::StripChars => (PyStringFunction::StripChars,).into_py_any(py),
843 StringFunction::StripCharsStart => {
844 (PyStringFunction::StripCharsStart,).into_py_any(py)
845 },
846 StringFunction::StripCharsEnd => {
847 (PyStringFunction::StripCharsEnd,).into_py_any(py)
848 },
849 StringFunction::StripPrefix => (PyStringFunction::StripPrefix,).into_py_any(py),
850 StringFunction::StripSuffix => (PyStringFunction::StripSuffix,).into_py_any(py),
851 StringFunction::SplitExact { n, inclusive } => {
852 (PyStringFunction::SplitExact, n, inclusive).into_py_any(py)
853 },
854 StringFunction::SplitN(n) => (PyStringFunction::SplitN, n).into_py_any(py),
855 StringFunction::Strptime(_, options) => (
856 PyStringFunction::Strptime,
857 options.format.as_ref().map(|s| s.as_str()),
858 options.strict,
859 options.exact,
860 options.cache,
861 )
862 .into_py_any(py),
863 StringFunction::Split(inclusive) => {
864 (PyStringFunction::Split, inclusive).into_py_any(py)
865 },
866 StringFunction::ToDecimal(inference_length) => {
867 (PyStringFunction::ToDecimal, inference_length).into_py_any(py)
868 },
869 #[cfg(feature = "nightly")]
870 StringFunction::Titlecase => (PyStringFunction::Titlecase,).into_py_any(py),
871 StringFunction::Uppercase => (PyStringFunction::Uppercase,).into_py_any(py),
872 StringFunction::ZFill => (PyStringFunction::ZFill,).into_py_any(py),
873 #[cfg(feature = "find_many")]
874 StringFunction::ContainsAny {
875 ascii_case_insensitive,
876 } => (PyStringFunction::ContainsAny, ascii_case_insensitive).into_py_any(py),
877 #[cfg(feature = "find_many")]
878 StringFunction::ReplaceMany {
879 ascii_case_insensitive,
880 } => (PyStringFunction::ReplaceMany, ascii_case_insensitive).into_py_any(py),
881 #[cfg(feature = "find_many")]
882 StringFunction::ExtractMany { .. } => {
883 return Err(PyNotImplementedError::new_err("extract_many"));
884 },
885 #[cfg(feature = "find_many")]
886 StringFunction::FindMany { .. } => {
887 return Err(PyNotImplementedError::new_err("find_many"));
888 },
889 #[cfg(feature = "regex")]
890 StringFunction::EscapeRegex => (PyStringFunction::EscapeRegex,).into_py_any(py),
891 },
892 FunctionExpr::StructExpr(_) => {
893 return Err(PyNotImplementedError::new_err("struct expr"));
894 },
895 FunctionExpr::TemporalExpr(fun) => match fun {
896 TemporalFunction::Millennium => {
897 (PyTemporalFunction::Millennium,).into_py_any(py)
898 },
899 TemporalFunction::Century => (PyTemporalFunction::Century,).into_py_any(py),
900 TemporalFunction::Year => (PyTemporalFunction::Year,).into_py_any(py),
901 TemporalFunction::IsLeapYear => {
902 (PyTemporalFunction::IsLeapYear,).into_py_any(py)
903 },
904 TemporalFunction::IsoYear => (PyTemporalFunction::IsoYear,).into_py_any(py),
905 TemporalFunction::Quarter => (PyTemporalFunction::Quarter,).into_py_any(py),
906 TemporalFunction::Month => (PyTemporalFunction::Month,).into_py_any(py),
907 TemporalFunction::Week => (PyTemporalFunction::Week,).into_py_any(py),
908 TemporalFunction::WeekDay => (PyTemporalFunction::WeekDay,).into_py_any(py),
909 TemporalFunction::Day => (PyTemporalFunction::Day,).into_py_any(py),
910 TemporalFunction::OrdinalDay => {
911 (PyTemporalFunction::OrdinalDay,).into_py_any(py)
912 },
913 TemporalFunction::Time => (PyTemporalFunction::Time,).into_py_any(py),
914 TemporalFunction::Date => (PyTemporalFunction::Date,).into_py_any(py),
915 TemporalFunction::Datetime => (PyTemporalFunction::Datetime,).into_py_any(py),
916 TemporalFunction::Duration(time_unit) => {
917 (PyTemporalFunction::Duration, Wrap(*time_unit)).into_py_any(py)
918 },
919 TemporalFunction::Hour => (PyTemporalFunction::Hour,).into_py_any(py),
920 TemporalFunction::Minute => (PyTemporalFunction::Minute,).into_py_any(py),
921 TemporalFunction::Second => (PyTemporalFunction::Second,).into_py_any(py),
922 TemporalFunction::Millisecond => {
923 (PyTemporalFunction::Millisecond,).into_py_any(py)
924 },
925 TemporalFunction::Microsecond => {
926 (PyTemporalFunction::Microsecond,).into_py_any(py)
927 },
928 TemporalFunction::Nanosecond => {
929 (PyTemporalFunction::Nanosecond,).into_py_any(py)
930 },
931 TemporalFunction::TotalDays => (PyTemporalFunction::TotalDays,).into_py_any(py),
932 TemporalFunction::TotalHours => {
933 (PyTemporalFunction::TotalHours,).into_py_any(py)
934 },
935 TemporalFunction::TotalMinutes => {
936 (PyTemporalFunction::TotalMinutes,).into_py_any(py)
937 },
938 TemporalFunction::TotalSeconds => {
939 (PyTemporalFunction::TotalSeconds,).into_py_any(py)
940 },
941 TemporalFunction::TotalMilliseconds => {
942 (PyTemporalFunction::TotalMilliseconds,).into_py_any(py)
943 },
944 TemporalFunction::TotalMicroseconds => {
945 (PyTemporalFunction::TotalMicroseconds,).into_py_any(py)
946 },
947 TemporalFunction::TotalNanoseconds => {
948 (PyTemporalFunction::TotalNanoseconds,).into_py_any(py)
949 },
950 TemporalFunction::ToString(format) => {
951 (PyTemporalFunction::ToString, format).into_py_any(py)
952 },
953 TemporalFunction::CastTimeUnit(time_unit) => {
954 (PyTemporalFunction::CastTimeUnit, Wrap(*time_unit)).into_py_any(py)
955 },
956 TemporalFunction::WithTimeUnit(time_unit) => {
957 (PyTemporalFunction::WithTimeUnit, Wrap(*time_unit)).into_py_any(py)
958 },
959 #[cfg(feature = "timezones")]
960 TemporalFunction::ConvertTimeZone(time_zone) => {
961 (PyTemporalFunction::ConvertTimeZone, time_zone.as_str()).into_py_any(py)
962 },
963 TemporalFunction::TimeStamp(time_unit) => {
964 (PyTemporalFunction::TimeStamp, Wrap(*time_unit)).into_py_any(py)
965 },
966 TemporalFunction::Truncate => (PyTemporalFunction::Truncate,).into_py_any(py),
967 TemporalFunction::OffsetBy => (PyTemporalFunction::OffsetBy,).into_py_any(py),
968 TemporalFunction::MonthStart => {
969 (PyTemporalFunction::MonthStart,).into_py_any(py)
970 },
971 TemporalFunction::MonthEnd => (PyTemporalFunction::MonthEnd,).into_py_any(py),
972 #[cfg(feature = "timezones")]
973 TemporalFunction::BaseUtcOffset => {
974 (PyTemporalFunction::BaseUtcOffset,).into_py_any(py)
975 },
976 #[cfg(feature = "timezones")]
977 TemporalFunction::DSTOffset => (PyTemporalFunction::DSTOffset,).into_py_any(py),
978 TemporalFunction::Round => (PyTemporalFunction::Round,).into_py_any(py),
979 TemporalFunction::Replace => (PyTemporalFunction::Replace).into_py_any(py),
980 #[cfg(feature = "timezones")]
981 TemporalFunction::ReplaceTimeZone(time_zone, non_existent) => (
982 PyTemporalFunction::ReplaceTimeZone,
983 time_zone.as_ref().map(|s| s.as_str()),
984 Into::<&str>::into(non_existent),
985 )
986 .into_py_any(py),
987 TemporalFunction::Combine(time_unit) => {
988 (PyTemporalFunction::Combine, Wrap(*time_unit)).into_py_any(py)
989 },
990 TemporalFunction::DatetimeFunction {
991 time_unit,
992 time_zone,
993 } => (
994 PyTemporalFunction::DatetimeFunction,
995 Wrap(*time_unit),
996 time_zone.as_ref().map(|s| s.as_str()),
997 )
998 .into_py_any(py),
999 },
1000 FunctionExpr::Boolean(boolfun) => match boolfun {
1001 BooleanFunction::Any { ignore_nulls } => {
1002 (PyBooleanFunction::Any, *ignore_nulls).into_py_any(py)
1003 },
1004 BooleanFunction::All { ignore_nulls } => {
1005 (PyBooleanFunction::All, *ignore_nulls).into_py_any(py)
1006 },
1007 BooleanFunction::IsNull => (PyBooleanFunction::IsNull,).into_py_any(py),
1008 BooleanFunction::IsNotNull => (PyBooleanFunction::IsNotNull,).into_py_any(py),
1009 BooleanFunction::IsFinite => (PyBooleanFunction::IsFinite,).into_py_any(py),
1010 BooleanFunction::IsInfinite => (PyBooleanFunction::IsInfinite,).into_py_any(py),
1011 BooleanFunction::IsNan => (PyBooleanFunction::IsNan,).into_py_any(py),
1012 BooleanFunction::IsNotNan => (PyBooleanFunction::IsNotNan,).into_py_any(py),
1013 BooleanFunction::IsFirstDistinct => {
1014 (PyBooleanFunction::IsFirstDistinct,).into_py_any(py)
1015 },
1016 BooleanFunction::IsLastDistinct => {
1017 (PyBooleanFunction::IsLastDistinct,).into_py_any(py)
1018 },
1019 BooleanFunction::IsUnique => (PyBooleanFunction::IsUnique,).into_py_any(py),
1020 BooleanFunction::IsDuplicated => {
1021 (PyBooleanFunction::IsDuplicated,).into_py_any(py)
1022 },
1023 BooleanFunction::IsBetween { closed } => {
1024 (PyBooleanFunction::IsBetween, Into::<&str>::into(closed)).into_py_any(py)
1025 },
1026 #[cfg(feature = "is_in")]
1027 BooleanFunction::IsIn { nulls_equal } => {
1028 (PyBooleanFunction::IsIn, nulls_equal).into_py_any(py)
1029 },
1030 BooleanFunction::AllHorizontal => {
1031 (PyBooleanFunction::AllHorizontal,).into_py_any(py)
1032 },
1033 BooleanFunction::AnyHorizontal => {
1034 (PyBooleanFunction::AnyHorizontal,).into_py_any(py)
1035 },
1036 BooleanFunction::Not => (PyBooleanFunction::Not,).into_py_any(py),
1037 },
1038 FunctionExpr::Abs => ("abs",).into_py_any(py),
1039 #[cfg(feature = "hist")]
1040 FunctionExpr::Hist {
1041 bin_count,
1042 include_category,
1043 include_breakpoint,
1044 } => ("hist", bin_count, include_category, include_breakpoint).into_py_any(py),
1045 FunctionExpr::NullCount => ("null_count",).into_py_any(py),
1046 FunctionExpr::Pow(f) => match f {
1047 PowFunction::Generic => ("pow",).into_py_any(py),
1048 PowFunction::Sqrt => ("sqrt",).into_py_any(py),
1049 PowFunction::Cbrt => ("cbrt",).into_py_any(py),
1050 },
1051 FunctionExpr::Hash(seed, seed_1, seed_2, seed_3) => {
1052 ("hash", seed, seed_1, seed_2, seed_3).into_py_any(py)
1053 },
1054 FunctionExpr::ArgWhere => ("argwhere",).into_py_any(py),
1055 #[cfg(feature = "index_of")]
1056 FunctionExpr::IndexOf => ("index_of",).into_py_any(py),
1057 #[cfg(feature = "search_sorted")]
1058 FunctionExpr::SearchSorted(side) => (
1059 "search_sorted",
1060 match side {
1061 SearchSortedSide::Any => "any",
1062 SearchSortedSide::Left => "left",
1063 SearchSortedSide::Right => "right",
1064 },
1065 )
1066 .into_py_any(py),
1067 FunctionExpr::Range(_) => return Err(PyNotImplementedError::new_err("range")),
1068 #[cfg(feature = "trigonometry")]
1069 FunctionExpr::Trigonometry(trigfun) => {
1070 use polars_plan::dsl::function_expr::trigonometry::TrigonometricFunction;
1071
1072 match trigfun {
1073 TrigonometricFunction::Cos => ("cos",),
1074 TrigonometricFunction::Cot => ("cot",),
1075 TrigonometricFunction::Sin => ("sin",),
1076 TrigonometricFunction::Tan => ("tan",),
1077 TrigonometricFunction::ArcCos => ("arccos",),
1078 TrigonometricFunction::ArcSin => ("arcsin",),
1079 TrigonometricFunction::ArcTan => ("arctan",),
1080 TrigonometricFunction::Cosh => ("cosh",),
1081 TrigonometricFunction::Sinh => ("sinh",),
1082 TrigonometricFunction::Tanh => ("tanh",),
1083 TrigonometricFunction::ArcCosh => ("arccosh",),
1084 TrigonometricFunction::ArcSinh => ("arcsinh",),
1085 TrigonometricFunction::ArcTanh => ("arctanh",),
1086 TrigonometricFunction::Degrees => ("degrees",),
1087 TrigonometricFunction::Radians => ("radians",),
1088 }
1089 .into_py_any(py)
1090 },
1091 #[cfg(feature = "trigonometry")]
1092 FunctionExpr::Atan2 => ("atan2",).into_py_any(py),
1093 #[cfg(feature = "sign")]
1094 FunctionExpr::Sign => ("sign",).into_py_any(py),
1095 FunctionExpr::FillNull => ("fill_null",).into_py_any(py),
1096 FunctionExpr::RollingExpr(rolling) => {
1097 return Err(PyNotImplementedError::new_err(format!("{}", rolling)));
1098 },
1099 FunctionExpr::RollingExprBy(rolling) => match rolling {
1100 RollingFunctionBy::MinBy(_) => {
1101 return Err(PyNotImplementedError::new_err("rolling min by"));
1102 },
1103 RollingFunctionBy::MaxBy(_) => {
1104 return Err(PyNotImplementedError::new_err("rolling max by"));
1105 },
1106 RollingFunctionBy::MeanBy(_) => {
1107 return Err(PyNotImplementedError::new_err("rolling mean by"));
1108 },
1109 RollingFunctionBy::SumBy(_) => {
1110 return Err(PyNotImplementedError::new_err("rolling sum by"));
1111 },
1112 RollingFunctionBy::QuantileBy(_) => {
1113 return Err(PyNotImplementedError::new_err("rolling quantile by"));
1114 },
1115 RollingFunctionBy::VarBy(_) => {
1116 return Err(PyNotImplementedError::new_err("rolling var by"));
1117 },
1118 RollingFunctionBy::StdBy(_) => {
1119 return Err(PyNotImplementedError::new_err("rolling std by"));
1120 },
1121 },
1122 FunctionExpr::ShiftAndFill => ("shift_and_fill",).into_py_any(py),
1123 FunctionExpr::Shift => ("shift",).into_py_any(py),
1124 FunctionExpr::DropNans => ("drop_nans",).into_py_any(py),
1125 FunctionExpr::DropNulls => ("drop_nulls",).into_py_any(py),
1126 FunctionExpr::Mode => ("mode",).into_py_any(py),
1127 FunctionExpr::Skew(bias) => ("skew", bias).into_py_any(py),
1128 FunctionExpr::Kurtosis(fisher, bias) => ("kurtosis", fisher, bias).into_py_any(py),
1129 FunctionExpr::Reshape(_) => return Err(PyNotImplementedError::new_err("reshape")),
1130 #[cfg(feature = "repeat_by")]
1131 FunctionExpr::RepeatBy => ("repeat_by",).into_py_any(py),
1132 FunctionExpr::ArgUnique => ("arg_unique",).into_py_any(py),
1133 FunctionExpr::Repeat => ("repeat",).into_py_any(py),
1134 FunctionExpr::Rank {
1135 options: _,
1136 seed: _,
1137 } => return Err(PyNotImplementedError::new_err("rank")),
1138 FunctionExpr::Clip { has_min, has_max } => {
1139 ("clip", has_min, has_max).into_py_any(py)
1140 },
1141 FunctionExpr::AsStruct => ("as_struct",).into_py_any(py),
1142 #[cfg(feature = "top_k")]
1143 FunctionExpr::TopK { descending } => ("top_k", descending).into_py_any(py),
1144 FunctionExpr::CumCount { reverse } => ("cum_count", reverse).into_py_any(py),
1145 FunctionExpr::CumSum { reverse } => ("cum_sum", reverse).into_py_any(py),
1146 FunctionExpr::CumProd { reverse } => ("cum_prod", reverse).into_py_any(py),
1147 FunctionExpr::CumMin { reverse } => ("cum_min", reverse).into_py_any(py),
1148 FunctionExpr::CumMax { reverse } => ("cum_max", reverse).into_py_any(py),
1149 FunctionExpr::Reverse => ("reverse",).into_py_any(py),
1150 FunctionExpr::ValueCounts {
1151 sort,
1152 parallel,
1153 name,
1154 normalize,
1155 } => ("value_counts", sort, parallel, name.as_str(), normalize).into_py_any(py),
1156 FunctionExpr::UniqueCounts => ("unique_counts",).into_py_any(py),
1157 FunctionExpr::ApproxNUnique => ("approx_n_unique",).into_py_any(py),
1158 FunctionExpr::Coalesce => ("coalesce",).into_py_any(py),
1159 FunctionExpr::ShrinkType => ("shrink_dtype",).into_py_any(py),
1160 FunctionExpr::Diff(null_behaviour) => (
1161 "diff",
1162 match null_behaviour {
1163 NullBehavior::Drop => "drop",
1164 NullBehavior::Ignore => "ignore",
1165 },
1166 )
1167 .into_py_any(py),
1168 #[cfg(feature = "pct_change")]
1169 FunctionExpr::PctChange => ("pct_change",).into_py_any(py),
1170 FunctionExpr::Interpolate(method) => (
1171 "interpolate",
1172 match method {
1173 InterpolationMethod::Linear => "linear",
1174 InterpolationMethod::Nearest => "nearest",
1175 },
1176 )
1177 .into_py_any(py),
1178 FunctionExpr::InterpolateBy => ("interpolate_by",).into_py_any(py),
1179 FunctionExpr::Entropy { base, normalize } => {
1180 ("entropy", base, normalize).into_py_any(py)
1181 },
1182 FunctionExpr::Log { base } => ("log", base).into_py_any(py),
1183 FunctionExpr::Log1p => ("log1p",).into_py_any(py),
1184 FunctionExpr::Exp => ("exp",).into_py_any(py),
1185 FunctionExpr::Unique(maintain_order) => ("unique", maintain_order).into_py_any(py),
1186 FunctionExpr::Round { decimals, mode } => {
1187 ("round", decimals, Into::<&str>::into(mode)).into_py_any(py)
1188 },
1189 FunctionExpr::RoundSF { digits } => ("round_sig_figs", digits).into_py_any(py),
1190 FunctionExpr::Floor => ("floor",).into_py_any(py),
1191 FunctionExpr::Ceil => ("ceil",).into_py_any(py),
1192 FunctionExpr::UpperBound => ("upper_bound",).into_py_any(py),
1193 FunctionExpr::LowerBound => ("lower_bound",).into_py_any(py),
1194 FunctionExpr::Fused(_) => return Err(PyNotImplementedError::new_err("fused")),
1195 FunctionExpr::ConcatExpr(_) => {
1196 return Err(PyNotImplementedError::new_err("concat expr"));
1197 },
1198 FunctionExpr::Correlation { .. } => {
1199 return Err(PyNotImplementedError::new_err("corr"));
1200 },
1201 #[cfg(feature = "peaks")]
1202 FunctionExpr::PeakMin => ("peak_max",).into_py_any(py),
1203 #[cfg(feature = "peaks")]
1204 FunctionExpr::PeakMax => ("peak_min",).into_py_any(py),
1205 #[cfg(feature = "cutqcut")]
1206 FunctionExpr::Cut { .. } => return Err(PyNotImplementedError::new_err("cut")),
1207 #[cfg(feature = "cutqcut")]
1208 FunctionExpr::QCut { .. } => return Err(PyNotImplementedError::new_err("qcut")),
1209 #[cfg(feature = "rle")]
1210 FunctionExpr::RLE => ("rle",).into_py_any(py),
1211 #[cfg(feature = "rle")]
1212 FunctionExpr::RLEID => ("rle_id",).into_py_any(py),
1213 FunctionExpr::ToPhysical => ("to_physical",).into_py_any(py),
1214 FunctionExpr::Random { .. } => {
1215 return Err(PyNotImplementedError::new_err("random"));
1216 },
1217 FunctionExpr::SetSortedFlag(sorted) => (
1218 "set_sorted",
1219 match sorted {
1220 IsSorted::Ascending => "ascending",
1221 IsSorted::Descending => "descending",
1222 IsSorted::Not => "not",
1223 },
1224 )
1225 .into_py_any(py),
1226 #[cfg(feature = "ffi_plugin")]
1227 FunctionExpr::FfiPlugin { .. } => {
1228 return Err(PyNotImplementedError::new_err("ffi plugin"));
1229 },
1230 FunctionExpr::SumHorizontal { ignore_nulls } => {
1231 ("sum_horizontal", ignore_nulls).into_py_any(py)
1232 },
1233 FunctionExpr::MaxHorizontal => ("max_horizontal",).into_py_any(py),
1234 FunctionExpr::MeanHorizontal { ignore_nulls } => {
1235 ("mean_horizontal", ignore_nulls).into_py_any(py)
1236 },
1237 FunctionExpr::MinHorizontal => ("min_horizontal",).into_py_any(py),
1238 FunctionExpr::EwmMean { options: _ } => {
1239 return Err(PyNotImplementedError::new_err("ewm mean"));
1240 },
1241 FunctionExpr::EwmStd { options: _ } => {
1242 return Err(PyNotImplementedError::new_err("ewm std"));
1243 },
1244 FunctionExpr::EwmVar { options: _ } => {
1245 return Err(PyNotImplementedError::new_err("ewm var"));
1246 },
1247 FunctionExpr::Replace => ("replace",).into_py_any(py),
1248 FunctionExpr::ReplaceStrict { return_dtype: _ } => {
1249 ("replace_strict",).into_py_any(py)
1251 },
1252 FunctionExpr::Negate => ("negate",).into_py_any(py),
1253 FunctionExpr::FillNullWithStrategy(_) => {
1254 return Err(PyNotImplementedError::new_err("fill null with strategy"));
1255 },
1256 FunctionExpr::GatherEvery { n, offset } => {
1257 ("gather_every", offset, n).into_py_any(py)
1258 },
1259 FunctionExpr::Reinterpret(signed) => ("reinterpret", signed).into_py_any(py),
1260 FunctionExpr::ExtendConstant => ("extend_constant",).into_py_any(py),
1261 FunctionExpr::Business(_) => {
1262 return Err(PyNotImplementedError::new_err("business"));
1263 },
1264 #[cfg(feature = "top_k")]
1265 FunctionExpr::TopKBy { descending } => ("top_k_by", descending).into_py_any(py),
1266 FunctionExpr::EwmMeanBy { half_life: _ } => {
1267 return Err(PyNotImplementedError::new_err("ewm_mean_by"));
1268 },
1269 }?,
1270 options: py.None(),
1271 }
1272 .into_py_any(py),
1273 AExpr::Window {
1274 function,
1275 partition_by,
1276 order_by,
1277 options,
1278 } => {
1279 let function = function.0;
1280 let partition_by = partition_by.iter().map(|n| n.0).collect();
1281 let order_by_descending = order_by
1282 .map(|(_, options)| options.descending)
1283 .unwrap_or(false);
1284 let order_by_nulls_last = order_by
1285 .map(|(_, options)| options.nulls_last)
1286 .unwrap_or(false);
1287 let order_by = order_by.map(|(n, _)| n.0);
1288
1289 let options = match options {
1290 WindowType::Over(options) => PyWindowMapping { inner: *options }.into_py_any(py)?,
1291 WindowType::Rolling(options) => PyRollingGroupOptions {
1292 inner: options.clone(),
1293 }
1294 .into_py_any(py)?,
1295 };
1296 Window {
1297 function,
1298 partition_by,
1299 order_by,
1300 order_by_descending,
1301 order_by_nulls_last,
1302 options,
1303 }
1304 .into_py_any(py)
1305 },
1306 AExpr::Slice {
1307 input,
1308 offset,
1309 length,
1310 } => Slice {
1311 input: input.0,
1312 offset: offset.0,
1313 length: length.0,
1314 }
1315 .into_py_any(py),
1316 AExpr::Len => Len {}.into_py_any(py),
1317 }
1318}