polars-df 0.17.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Cargo.lock +725 -453
- data/ext/polars/Cargo.toml +8 -8
- data/ext/polars/src/conversion/any_value.rs +1 -1
- data/ext/polars/src/conversion/mod.rs +38 -7
- data/ext/polars/src/dataframe/export.rs +2 -2
- data/ext/polars/src/dataframe/general.rs +4 -1
- data/ext/polars/src/expr/array.rs +2 -2
- data/ext/polars/src/expr/datetime.rs +16 -9
- data/ext/polars/src/expr/general.rs +12 -14
- data/ext/polars/src/expr/list.rs +3 -3
- data/ext/polars/src/expr/rolling.rs +17 -2
- data/ext/polars/src/expr/string.rs +2 -2
- data/ext/polars/src/file.rs +56 -14
- data/ext/polars/src/functions/lazy.rs +26 -4
- data/ext/polars/src/functions/range.rs +4 -4
- data/ext/polars/src/lazyframe/general.rs +87 -48
- data/ext/polars/src/lazyframe/mod.rs +2 -0
- data/ext/polars/src/lazyframe/sink.rs +99 -0
- data/ext/polars/src/lib.rs +7 -9
- data/ext/polars/src/map/mod.rs +1 -1
- data/ext/polars/src/map/series.rs +4 -4
- data/ext/polars/src/on_startup.rs +15 -3
- data/ext/polars/src/series/export.rs +4 -4
- data/ext/polars/src/series/general.rs +2 -2
- data/lib/polars/array_expr.rb +4 -2
- data/lib/polars/expr.rb +28 -28
- data/lib/polars/functions/lit.rb +4 -9
- data/lib/polars/io/database.rb +2 -2
- data/lib/polars/lazy_frame.rb +78 -14
- data/lib/polars/list_expr.rb +10 -11
- data/lib/polars/series.rb +29 -12
- data/lib/polars/string_expr.rb +3 -3
- data/lib/polars/version.rb +1 -1
- metadata +4 -3
    
        data/ext/polars/Cargo.toml
    CHANGED
    
    | @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            [package]
         | 
| 2 2 | 
             
            name = "polars"
         | 
| 3 | 
            -
            version = "0. | 
| 3 | 
            +
            version = "0.19.0"
         | 
| 4 4 | 
             
            license = "MIT"
         | 
| 5 5 | 
             
            authors = ["Andrew Kane <andrew@ankane.org>"]
         | 
| 6 6 | 
             
            edition = "2021"
         | 
| 7 | 
            -
            rust-version = "1. | 
| 7 | 
            +
            rust-version = "1.85.0"
         | 
| 8 8 | 
             
            publish = false
         | 
| 9 9 |  | 
| 10 10 | 
             
            [lib]
         | 
| @@ -12,22 +12,22 @@ crate-type = ["cdylib"] | |
| 12 12 |  | 
| 13 13 | 
             
            [dependencies]
         | 
| 14 14 | 
             
            ahash = "0.8"
         | 
| 15 | 
            -
            arrow = { package = "polars-arrow", version = "=0. | 
| 15 | 
            +
            arrow = { package = "polars-arrow", version = "=0.48.0" }
         | 
| 16 16 | 
             
            bytes = "1"
         | 
| 17 17 | 
             
            chrono = "0.4"
         | 
| 18 18 | 
             
            either = "1.8"
         | 
| 19 19 | 
             
            magnus = "0.7"
         | 
| 20 20 | 
             
            num-traits = "0.2"
         | 
| 21 | 
            -
            polars-core = "=0. | 
| 22 | 
            -
            polars-plan = "=0. | 
| 23 | 
            -
            polars-parquet = "=0. | 
| 24 | 
            -
            polars-utils = "=0. | 
| 21 | 
            +
            polars-core = "=0.48.0"
         | 
| 22 | 
            +
            polars-plan = "=0.48.0"
         | 
| 23 | 
            +
            polars-parquet = "=0.48.0"
         | 
| 24 | 
            +
            polars-utils = "=0.48.0"
         | 
| 25 25 | 
             
            rayon = "1.9"
         | 
| 26 26 | 
             
            regex = "1"
         | 
| 27 27 | 
             
            serde_json = "1"
         | 
| 28 28 |  | 
| 29 29 | 
             
            [dependencies.polars]
         | 
| 30 | 
            -
            version = "=0. | 
| 30 | 
            +
            version = "=0.48.0"
         | 
| 31 31 | 
             
            features = [
         | 
| 32 32 | 
             
                "abs",
         | 
| 33 33 | 
             
                "approx_unique",
         | 
| @@ -173,7 +173,7 @@ pub(crate) fn rb_object_to_any_value<'s>(ob: Value, strict: bool) -> RbResult<An | |
| 173 173 | 
             
                }
         | 
| 174 174 |  | 
| 175 175 | 
             
                fn get_struct(ob: Value, _strict: bool) -> RbResult<AnyValue<'static>> {
         | 
| 176 | 
            -
                    let dict = RHash:: | 
| 176 | 
            +
                    let dict = RHash::try_convert(ob)?;
         | 
| 177 177 | 
             
                    let len = dict.len();
         | 
| 178 178 | 
             
                    let mut keys = Vec::with_capacity(len);
         | 
| 179 179 | 
             
                    let mut vals = Vec::with_capacity(len);
         | 
| @@ -21,7 +21,7 @@ use polars::prelude::*; | |
| 21 21 | 
             
            use polars::series::ops::NullBehavior;
         | 
| 22 22 | 
             
            use polars_core::utils::arrow::array::Array;
         | 
| 23 23 | 
             
            use polars_core::utils::materialize_dyn_int;
         | 
| 24 | 
            -
            use polars_plan:: | 
| 24 | 
            +
            use polars_plan::dsl::ScanSources;
         | 
| 25 25 | 
             
            use polars_utils::mmap::MemSlice;
         | 
| 26 26 | 
             
            use polars_utils::total_ord::{TotalEq, TotalHash};
         | 
| 27 27 |  | 
| @@ -210,7 +210,10 @@ impl IntoValue for Wrap<DataType> { | |
| 210 210 | 
             
                        DataType::Datetime(tu, tz) => {
         | 
| 211 211 | 
             
                            let datetime_class = pl.const_get::<_, Value>("Datetime").unwrap();
         | 
| 212 212 | 
             
                            datetime_class
         | 
| 213 | 
            -
                                .funcall::<_, _, Value>( | 
| 213 | 
            +
                                .funcall::<_, _, Value>(
         | 
| 214 | 
            +
                                    "new",
         | 
| 215 | 
            +
                                    (tu.to_ascii(), tz.as_deref().map(|x| x.as_str())),
         | 
| 216 | 
            +
                                )
         | 
| 214 217 | 
             
                                .unwrap()
         | 
| 215 218 | 
             
                        }
         | 
| 216 219 | 
             
                        DataType::Duration(tu) => {
         | 
| @@ -219,7 +222,7 @@ impl IntoValue for Wrap<DataType> { | |
| 219 222 | 
             
                                .funcall::<_, _, Value>("new", (tu.to_ascii(),))
         | 
| 220 223 | 
             
                                .unwrap()
         | 
| 221 224 | 
             
                        }
         | 
| 222 | 
            -
                        DataType::Object(_ | 
| 225 | 
            +
                        DataType::Object(_) => {
         | 
| 223 226 | 
             
                            let class = pl.const_get::<_, Value>("Object").unwrap();
         | 
| 224 227 | 
             
                            class.funcall("new", ()).unwrap()
         | 
| 225 228 | 
             
                        }
         | 
| @@ -332,7 +335,7 @@ impl TryConvert for Wrap<DataType> { | |
| 332 335 | 
             
                            "Polars::Array" => DataType::Array(Box::new(DataType::Null), 0),
         | 
| 333 336 | 
             
                            "Polars::Struct" => DataType::Struct(vec![]),
         | 
| 334 337 | 
             
                            "Polars::Null" => DataType::Null,
         | 
| 335 | 
            -
                            "Polars::Object" => DataType::Object(OBJECT_NAME | 
| 338 | 
            +
                            "Polars::Object" => DataType::Object(OBJECT_NAME),
         | 
| 336 339 | 
             
                            "Polars::Unknown" => DataType::Unknown(Default::default()),
         | 
| 337 340 | 
             
                            dt => {
         | 
| 338 341 | 
             
                                return Err(RbValueError::new_err(format!(
         | 
| @@ -375,7 +378,10 @@ impl TryConvert for Wrap<DataType> { | |
| 375 378 | 
             
                                let time_unit: Value = ob.funcall("time_unit", ()).unwrap();
         | 
| 376 379 | 
             
                                let time_unit = Wrap::<TimeUnit>::try_convert(time_unit)?.0;
         | 
| 377 380 | 
             
                                let time_zone: Option<String> = ob.funcall("time_zone", ())?;
         | 
| 378 | 
            -
                                DataType::Datetime( | 
| 381 | 
            +
                                DataType::Datetime(
         | 
| 382 | 
            +
                                    time_unit,
         | 
| 383 | 
            +
                                    TimeZone::opt_try_new(time_zone.as_deref()).map_err(RbPolarsErr::from)?,
         | 
| 384 | 
            +
                                )
         | 
| 379 385 | 
             
                            }
         | 
| 380 386 | 
             
                            "Polars::Duration" => {
         | 
| 381 387 | 
             
                                let time_unit: Value = ob.funcall("time_unit", ()).unwrap();
         | 
| @@ -408,7 +414,7 @@ impl TryConvert for Wrap<DataType> { | |
| 408 414 | 
             
                                DataType::Struct(fields)
         | 
| 409 415 | 
             
                            }
         | 
| 410 416 | 
             
                            "Polars::Null" => DataType::Null,
         | 
| 411 | 
            -
                            "Object" => DataType::Object(OBJECT_NAME | 
| 417 | 
            +
                            "Object" => DataType::Object(OBJECT_NAME),
         | 
| 412 418 | 
             
                            "Polars::Unknown" => DataType::Unknown(Default::default()),
         | 
| 413 419 | 
             
                            dt => {
         | 
| 414 420 | 
             
                                return Err(RbTypeError::new_err(format!(
         | 
| @@ -437,7 +443,7 @@ impl TryConvert for Wrap<DataType> { | |
| 437 443 | 
             
                            "time" => DataType::Time,
         | 
| 438 444 | 
             
                            "dur" => DataType::Duration(TimeUnit::Microseconds),
         | 
| 439 445 | 
             
                            "f64" => DataType::Float64,
         | 
| 440 | 
            -
                            "obj" => DataType::Object(OBJECT_NAME | 
| 446 | 
            +
                            "obj" => DataType::Object(OBJECT_NAME),
         | 
| 441 447 | 
             
                            "list" => DataType::List(Box::new(DataType::Boolean)),
         | 
| 442 448 | 
             
                            "null" => DataType::Null,
         | 
| 443 449 | 
             
                            "unk" => DataType::Unknown(Default::default()),
         | 
| @@ -761,6 +767,21 @@ impl TryConvert for Wrap<ClosedWindow> { | |
| 761 767 | 
             
                }
         | 
| 762 768 | 
             
            }
         | 
| 763 769 |  | 
| 770 | 
            +
            impl TryConvert for Wrap<RoundMode> {
         | 
| 771 | 
            +
                fn try_convert(ob: Value) -> RbResult<Self> {
         | 
| 772 | 
            +
                    let parsed = match String::try_convert(ob)?.as_str() {
         | 
| 773 | 
            +
                        "half_to_even" => RoundMode::HalfToEven,
         | 
| 774 | 
            +
                        "half_away_from_zero" => RoundMode::HalfAwayFromZero,
         | 
| 775 | 
            +
                        v => {
         | 
| 776 | 
            +
                            return Err(RbValueError::new_err(format!(
         | 
| 777 | 
            +
                                "`mode` must be one of {{'half_to_even', 'half_away_from_zero'}}, got {v}",
         | 
| 778 | 
            +
                            )));
         | 
| 779 | 
            +
                        }
         | 
| 780 | 
            +
                    };
         | 
| 781 | 
            +
                    Ok(Wrap(parsed))
         | 
| 782 | 
            +
                }
         | 
| 783 | 
            +
            }
         | 
| 784 | 
            +
             | 
| 764 785 | 
             
            impl TryConvert for Wrap<CsvEncoding> {
         | 
| 765 786 | 
             
                fn try_convert(ob: Value) -> RbResult<Self> {
         | 
| 766 787 | 
             
                    let parsed = match String::try_convert(ob)?.as_str() {
         | 
| @@ -1214,3 +1235,13 @@ impl TryConvert for RbCompatLevel { | |
| 1214 1235 | 
             
                    }))
         | 
| 1215 1236 | 
             
                }
         | 
| 1216 1237 | 
             
            }
         | 
| 1238 | 
            +
             | 
| 1239 | 
            +
            impl TryConvert for Wrap<Option<TimeZone>> {
         | 
| 1240 | 
            +
                fn try_convert(ob: Value) -> RbResult<Self> {
         | 
| 1241 | 
            +
                    let tz = Option::<Wrap<PlSmallStr>>::try_convert(ob)?;
         | 
| 1242 | 
            +
             | 
| 1243 | 
            +
                    let tz = tz.map(|x| x.0);
         | 
| 1244 | 
            +
             | 
| 1245 | 
            +
                    Ok(Wrap(TimeZone::opt_try_new(tz).map_err(RbPolarsErr::from)?))
         | 
| 1246 | 
            +
                }
         | 
| 1247 | 
            +
            }
         | 
| @@ -18,7 +18,7 @@ impl RbDataFrame { | |
| 18 18 | 
             
                            .get_columns()
         | 
| 19 19 | 
             
                            .iter()
         | 
| 20 20 | 
             
                            .map(|s| match s.dtype() {
         | 
| 21 | 
            -
                                DataType::Object(_ | 
| 21 | 
            +
                                DataType::Object(_) => {
         | 
| 22 22 | 
             
                                    let obj: Option<&ObjectValue> = s.get_object(idx).map(|any| any.into());
         | 
| 23 23 | 
             
                                    obj.unwrap().to_value()
         | 
| 24 24 | 
             
                                }
         | 
| @@ -37,7 +37,7 @@ impl RbDataFrame { | |
| 37 37 | 
             
                                .get_columns()
         | 
| 38 38 | 
             
                                .iter()
         | 
| 39 39 | 
             
                                .map(|s| match s.dtype() {
         | 
| 40 | 
            -
                                    DataType::Object(_ | 
| 40 | 
            +
                                    DataType::Object(_) => {
         | 
| 41 41 | 
             
                                        let obj: Option<&ObjectValue> = s.get_object(idx).map(|any| any.into());
         | 
| 42 42 | 
             
                                        obj.unwrap().to_value()
         | 
| 43 43 | 
             
                                    }
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            use std::hash::BuildHasher;
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            use either::Either;
         | 
| 2 4 | 
             
            use magnus::{prelude::*, typed_data::Obj, IntoValue, RArray, Value};
         | 
| 3 5 | 
             
            use polars::prelude::pivot::{pivot, pivot_stable};
         | 
| @@ -494,7 +496,8 @@ impl RbDataFrame { | |
| 494 496 | 
             
                }
         | 
| 495 497 |  | 
| 496 498 | 
             
                pub fn hash_rows(&self, k0: u64, k1: u64, k2: u64, k3: u64) -> RbResult<RbSeries> {
         | 
| 497 | 
            -
                    let  | 
| 499 | 
            +
                    let seed = PlFixedStateQuality::default().hash_one((k0, k1, k2, k3));
         | 
| 500 | 
            +
                    let hb = PlSeedableRandomStateQuality::seed_from_u64(seed);
         | 
| 498 501 | 
             
                    let hash = self
         | 
| 499 502 | 
             
                        .df
         | 
| 500 503 | 
             
                        .borrow_mut()
         | 
| @@ -75,11 +75,11 @@ impl RbExpr { | |
| 75 75 | 
             
                        .into()
         | 
| 76 76 | 
             
                }
         | 
| 77 77 |  | 
| 78 | 
            -
                pub fn arr_contains(&self, other: &RbExpr) -> Self {
         | 
| 78 | 
            +
                pub fn arr_contains(&self, other: &RbExpr, nulls_equal: bool) -> Self {
         | 
| 79 79 | 
             
                    self.inner
         | 
| 80 80 | 
             
                        .clone()
         | 
| 81 81 | 
             
                        .arr()
         | 
| 82 | 
            -
                        .contains(other.inner.clone())
         | 
| 82 | 
            +
                        .contains(other.inner.clone(), nulls_equal)
         | 
| 83 83 | 
             
                        .into()
         | 
| 84 84 | 
             
                }
         | 
| 85 85 |  | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            use polars::prelude::*;
         | 
| 2 2 |  | 
| 3 3 | 
             
            use crate::conversion::Wrap;
         | 
| 4 | 
            -
            use crate::RbExpr;
         | 
| 4 | 
            +
            use crate::{RbExpr, RbPolarsErr, RbResult};
         | 
| 5 5 |  | 
| 6 6 | 
             
            impl RbExpr {
         | 
| 7 7 | 
             
                pub fn dt_to_string(&self, format: String) -> Self {
         | 
| @@ -30,12 +30,17 @@ impl RbExpr { | |
| 30 30 | 
             
                    self.inner.clone().dt().with_time_unit(tu.0).into()
         | 
| 31 31 | 
             
                }
         | 
| 32 32 |  | 
| 33 | 
            -
                pub fn dt_convert_time_zone(&self, time_zone: String) -> Self {
         | 
| 34 | 
            -
                    self | 
| 33 | 
            +
                pub fn dt_convert_time_zone(&self, time_zone: String) -> RbResult<Self> {
         | 
| 34 | 
            +
                    Ok(self
         | 
| 35 | 
            +
                        .inner
         | 
| 35 36 | 
             
                        .clone()
         | 
| 36 37 | 
             
                        .dt()
         | 
| 37 | 
            -
                        .convert_time_zone( | 
| 38 | 
            -
             | 
| 38 | 
            +
                        .convert_time_zone(
         | 
| 39 | 
            +
                            TimeZone::opt_try_new(Some(PlSmallStr::from(time_zone)))
         | 
| 40 | 
            +
                                .map_err(RbPolarsErr::from)?
         | 
| 41 | 
            +
                                .unwrap_or(TimeZone::UTC),
         | 
| 42 | 
            +
                        )
         | 
| 43 | 
            +
                        .into())
         | 
| 39 44 | 
             
                }
         | 
| 40 45 |  | 
| 41 46 | 
             
                pub fn dt_cast_time_unit(&self, tu: Wrap<TimeUnit>) -> Self {
         | 
| @@ -47,16 +52,18 @@ impl RbExpr { | |
| 47 52 | 
             
                    time_zone: Option<String>,
         | 
| 48 53 | 
             
                    ambiguous: &Self,
         | 
| 49 54 | 
             
                    non_existent: Wrap<NonExistent>,
         | 
| 50 | 
            -
                ) -> Self {
         | 
| 51 | 
            -
                    self | 
| 55 | 
            +
                ) -> RbResult<Self> {
         | 
| 56 | 
            +
                    Ok(self
         | 
| 57 | 
            +
                        .inner
         | 
| 52 58 | 
             
                        .clone()
         | 
| 53 59 | 
             
                        .dt()
         | 
| 54 60 | 
             
                        .replace_time_zone(
         | 
| 55 | 
            -
                            time_zone.map( | 
| 61 | 
            +
                            TimeZone::opt_try_new(time_zone.map(PlSmallStr::from_string))
         | 
| 62 | 
            +
                                .map_err(RbPolarsErr::from)?,
         | 
| 56 63 | 
             
                            ambiguous.inner.clone(),
         | 
| 57 64 | 
             
                            non_existent.0,
         | 
| 58 65 | 
             
                        )
         | 
| 59 | 
            -
                        .into()
         | 
| 66 | 
            +
                        .into())
         | 
| 60 67 | 
             
                }
         | 
| 61 68 |  | 
| 62 69 | 
             
                pub fn dt_truncate(&self, every: &Self) -> Self {
         | 
| @@ -371,14 +371,6 @@ impl RbExpr { | |
| 371 371 | 
             
                        .into())
         | 
| 372 372 | 
             
                }
         | 
| 373 373 |  | 
| 374 | 
            -
                pub fn backward_fill(&self, limit: FillNullLimit) -> Self {
         | 
| 375 | 
            -
                    self.inner.clone().backward_fill(limit).into()
         | 
| 376 | 
            -
                }
         | 
| 377 | 
            -
             | 
| 378 | 
            -
                pub fn forward_fill(&self, limit: FillNullLimit) -> Self {
         | 
| 379 | 
            -
                    self.inner.clone().forward_fill(limit).into()
         | 
| 380 | 
            -
                }
         | 
| 381 | 
            -
             | 
| 382 374 | 
             
                pub fn shift(&self, n: &Self, fill_value: Option<&Self>) -> Self {
         | 
| 383 375 | 
             
                    let expr = self.inner.clone();
         | 
| 384 376 | 
             
                    let out = match fill_value {
         | 
| @@ -497,8 +489,8 @@ impl RbExpr { | |
| 497 489 | 
             
                        .into()
         | 
| 498 490 | 
             
                }
         | 
| 499 491 |  | 
| 500 | 
            -
                pub fn round(&self, decimals: u32) -> Self {
         | 
| 501 | 
            -
                    self.inner.clone().round(decimals).into()
         | 
| 492 | 
            +
                pub fn round(&self, decimals: u32, mode: Wrap<RoundMode>) -> Self {
         | 
| 493 | 
            +
                    self.inner.clone().round(decimals, mode.0).into()
         | 
| 502 494 | 
             
                }
         | 
| 503 495 |  | 
| 504 496 | 
             
                pub fn floor(&self) -> Self {
         | 
| @@ -597,8 +589,11 @@ impl RbExpr { | |
| 597 589 | 
             
                    self.inner.clone().or(expr.inner.clone()).into()
         | 
| 598 590 | 
             
                }
         | 
| 599 591 |  | 
| 600 | 
            -
                pub fn is_in(&self, expr: &Self) -> Self {
         | 
| 601 | 
            -
                    self.inner | 
| 592 | 
            +
                pub fn is_in(&self, expr: &Self, nulls_equal: bool) -> Self {
         | 
| 593 | 
            +
                    self.inner
         | 
| 594 | 
            +
                        .clone()
         | 
| 595 | 
            +
                        .is_in(expr.inner.clone(), nulls_equal)
         | 
| 596 | 
            +
                        .into()
         | 
| 602 597 | 
             
                }
         | 
| 603 598 |  | 
| 604 599 | 
             
                pub fn repeat_by(&self, by: &Self) -> Self {
         | 
| @@ -698,8 +693,11 @@ impl RbExpr { | |
| 698 693 | 
             
                    self.inner.clone().rank(options, seed).into()
         | 
| 699 694 | 
             
                }
         | 
| 700 695 |  | 
| 701 | 
            -
                pub fn diff(&self, n:  | 
| 702 | 
            -
                    self.inner | 
| 696 | 
            +
                pub fn diff(&self, n: &Self, null_behavior: Wrap<NullBehavior>) -> Self {
         | 
| 697 | 
            +
                    self.inner
         | 
| 698 | 
            +
                        .clone()
         | 
| 699 | 
            +
                        .diff(n.inner.clone(), null_behavior.0)
         | 
| 700 | 
            +
                        .into()
         | 
| 703 701 | 
             
                }
         | 
| 704 702 |  | 
| 705 703 | 
             
                pub fn pct_change(&self, n: &Self) -> Self {
         | 
    
        data/ext/polars/src/expr/list.rs
    CHANGED
    
    | @@ -23,11 +23,11 @@ impl RbExpr { | |
| 23 23 | 
             
                    self.inner.clone().list().arg_min().into()
         | 
| 24 24 | 
             
                }
         | 
| 25 25 |  | 
| 26 | 
            -
                pub fn list_contains(&self, other: &RbExpr) -> Self {
         | 
| 26 | 
            +
                pub fn list_contains(&self, other: &RbExpr, nulls_equal: bool) -> Self {
         | 
| 27 27 | 
             
                    self.inner
         | 
| 28 28 | 
             
                        .clone()
         | 
| 29 29 | 
             
                        .list()
         | 
| 30 | 
            -
                        .contains(other.inner.clone())
         | 
| 30 | 
            +
                        .contains(other.inner.clone(), nulls_equal)
         | 
| 31 31 | 
             
                        .into()
         | 
| 32 32 | 
             
                }
         | 
| 33 33 |  | 
| @@ -180,7 +180,7 @@ impl RbExpr { | |
| 180 180 | 
             
                    &self,
         | 
| 181 181 | 
             
                    width_strat: Wrap<ListToStructWidthStrategy>,
         | 
| 182 182 | 
             
                    name_gen: Option<Value>,
         | 
| 183 | 
            -
                    upper_bound: usize | 
| 183 | 
            +
                    upper_bound: Option<usize>,
         | 
| 184 184 | 
             
                ) -> RbResult<Self> {
         | 
| 185 185 | 
             
                    let name_gen = name_gen.map(|lambda| {
         | 
| 186 186 | 
             
                        let lambda = Opaque::from(lambda);
         | 
| @@ -319,7 +319,22 @@ impl RbExpr { | |
| 319 319 | 
             
                        .into()
         | 
| 320 320 | 
             
                }
         | 
| 321 321 |  | 
| 322 | 
            -
                pub fn rolling_skew( | 
| 323 | 
            -
                    self | 
| 322 | 
            +
                pub fn rolling_skew(
         | 
| 323 | 
            +
                    &self,
         | 
| 324 | 
            +
                    window_size: usize,
         | 
| 325 | 
            +
                    bias: bool,
         | 
| 326 | 
            +
                    min_periods: Option<usize>,
         | 
| 327 | 
            +
                    center: bool,
         | 
| 328 | 
            +
                ) -> Self {
         | 
| 329 | 
            +
                    let min_periods = min_periods.unwrap_or(window_size);
         | 
| 330 | 
            +
                    let options = RollingOptionsFixedWindow {
         | 
| 331 | 
            +
                        window_size,
         | 
| 332 | 
            +
                        weights: None,
         | 
| 333 | 
            +
                        min_periods,
         | 
| 334 | 
            +
                        center,
         | 
| 335 | 
            +
                        fn_params: Some(RollingFnParams::Skew { bias }),
         | 
| 336 | 
            +
                    };
         | 
| 337 | 
            +
             | 
| 338 | 
            +
                    self.inner.clone().rolling_skew(options).into()
         | 
| 324 339 | 
             
                }
         | 
| 325 340 | 
             
            }
         | 
| @@ -35,14 +35,14 @@ impl RbExpr { | |
| 35 35 | 
             
                    &self,
         | 
| 36 36 | 
             
                    format: Option<String>,
         | 
| 37 37 | 
             
                    time_unit: Option<Wrap<TimeUnit>>,
         | 
| 38 | 
            -
                    time_zone: Option< | 
| 38 | 
            +
                    time_zone: Wrap<Option<TimeZone>>,
         | 
| 39 39 | 
             
                    strict: bool,
         | 
| 40 40 | 
             
                    exact: bool,
         | 
| 41 41 | 
             
                    cache: bool,
         | 
| 42 42 | 
             
                    ambiguous: &Self,
         | 
| 43 43 | 
             
                ) -> Self {
         | 
| 44 44 | 
             
                    let format = format.map(|x| x.into());
         | 
| 45 | 
            -
                    let time_zone = time_zone. | 
| 45 | 
            +
                    let time_zone = time_zone.0;
         | 
| 46 46 |  | 
| 47 47 | 
             
                    let options = StrptimeOptions {
         | 
| 48 48 | 
             
                        format,
         | 
    
        data/ext/polars/src/file.rs
    CHANGED
    
    | @@ -3,9 +3,12 @@ use std::io; | |
| 3 3 | 
             
            use std::io::{Cursor, Read, Seek, SeekFrom, Write};
         | 
| 4 4 | 
             
            use std::path::PathBuf;
         | 
| 5 5 |  | 
| 6 | 
            -
            use magnus::{exception, prelude::*, Error, RString, Value};
         | 
| 6 | 
            +
            use magnus::{exception, prelude::*, value::Opaque, Error, RString, Ruby, Value};
         | 
| 7 7 | 
             
            use polars::io::cloud::CloudOptions;
         | 
| 8 8 | 
             
            use polars::io::mmap::MmapBytesReader;
         | 
| 9 | 
            +
            use polars::prelude::file::DynWriteable;
         | 
| 10 | 
            +
            use polars::prelude::sync_on_close::SyncOnCloseType;
         | 
| 11 | 
            +
            use polars_utils::file::ClosableFile;
         | 
| 9 12 | 
             
            use polars_utils::mmap::MemSlice;
         | 
| 10 13 |  | 
| 11 14 | 
             
            use crate::error::RbPolarsErr;
         | 
| @@ -14,7 +17,22 @@ use crate::RbResult; | |
| 14 17 |  | 
| 15 18 | 
             
            #[derive(Clone)]
         | 
| 16 19 | 
             
            pub struct RbFileLikeObject {
         | 
| 17 | 
            -
                inner: Value | 
| 20 | 
            +
                inner: Opaque<Value>,
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            impl DynWriteable for RbFileLikeObject {
         | 
| 24 | 
            +
                fn as_dyn_write(&self) -> &(dyn io::Write + Send + 'static) {
         | 
| 25 | 
            +
                    self as _
         | 
| 26 | 
            +
                }
         | 
| 27 | 
            +
                fn as_mut_dyn_write(&mut self) -> &mut (dyn io::Write + Send + 'static) {
         | 
| 28 | 
            +
                    self as _
         | 
| 29 | 
            +
                }
         | 
| 30 | 
            +
                fn close(self: Box<Self>) -> io::Result<()> {
         | 
| 31 | 
            +
                    Ok(())
         | 
| 32 | 
            +
                }
         | 
| 33 | 
            +
                fn sync_on_close(&mut self, _sync_on_close: SyncOnCloseType) -> io::Result<()> {
         | 
| 34 | 
            +
                    Ok(())
         | 
| 35 | 
            +
                }
         | 
| 18 36 | 
             
            }
         | 
| 19 37 |  | 
| 20 38 | 
             
            /// Wraps a `Value`, and implements read, seek, and write for it.
         | 
| @@ -23,7 +41,9 @@ impl RbFileLikeObject { | |
| 23 41 | 
             
                /// To assert the object has the required methods methods,
         | 
| 24 42 | 
             
                /// instantiate it with `RbFileLikeObject::require`
         | 
| 25 43 | 
             
                pub fn new(object: Value) -> Self {
         | 
| 26 | 
            -
                    RbFileLikeObject { | 
| 44 | 
            +
                    RbFileLikeObject {
         | 
| 45 | 
            +
                        inner: object.into(),
         | 
| 46 | 
            +
                    }
         | 
| 27 47 | 
             
                }
         | 
| 28 48 |  | 
| 29 49 | 
             
                pub fn as_bytes(&self) -> bytes::Bytes {
         | 
| @@ -31,8 +51,9 @@ impl RbFileLikeObject { | |
| 31 51 | 
             
                }
         | 
| 32 52 |  | 
| 33 53 | 
             
                pub fn as_file_buffer(&self) -> Cursor<Vec<u8>> {
         | 
| 34 | 
            -
                    let bytes =  | 
| 35 | 
            -
                        . | 
| 54 | 
            +
                    let bytes = Ruby::get()
         | 
| 55 | 
            +
                        .unwrap()
         | 
| 56 | 
            +
                        .get_inner(self.inner)
         | 
| 36 57 | 
             
                        .funcall::<_, _, RString>("read", ())
         | 
| 37 58 | 
             
                        .expect("no read method found");
         | 
| 38 59 |  | 
| @@ -77,8 +98,9 @@ fn rberr_to_io_err(e: Error) -> io::Error { | |
| 77 98 |  | 
| 78 99 | 
             
            impl Read for RbFileLikeObject {
         | 
| 79 100 | 
             
                fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
         | 
| 80 | 
            -
                    let bytes =  | 
| 81 | 
            -
                        . | 
| 101 | 
            +
                    let bytes = Ruby::get()
         | 
| 102 | 
            +
                        .unwrap()
         | 
| 103 | 
            +
                        .get_inner(self.inner)
         | 
| 82 104 | 
             
                        .funcall::<_, _, RString>("read", (buf.len(),))
         | 
| 83 105 | 
             
                        .map_err(rberr_to_io_err)?;
         | 
| 84 106 |  | 
| @@ -92,8 +114,9 @@ impl Write for RbFileLikeObject { | |
| 92 114 | 
             
                fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
         | 
| 93 115 | 
             
                    let rbbytes = RString::from_slice(buf);
         | 
| 94 116 |  | 
| 95 | 
            -
                    let number_bytes_written =  | 
| 96 | 
            -
                        . | 
| 117 | 
            +
                    let number_bytes_written = Ruby::get()
         | 
| 118 | 
            +
                        .unwrap()
         | 
| 119 | 
            +
                        .get_inner(self.inner)
         | 
| 97 120 | 
             
                        .funcall::<_, _, usize>("write", (rbbytes,))
         | 
| 98 121 | 
             
                        .map_err(rberr_to_io_err)?;
         | 
| 99 122 |  | 
| @@ -101,7 +124,9 @@ impl Write for RbFileLikeObject { | |
| 101 124 | 
             
                }
         | 
| 102 125 |  | 
| 103 126 | 
             
                fn flush(&mut self) -> Result<(), io::Error> {
         | 
| 104 | 
            -
                     | 
| 127 | 
            +
                    Ruby::get()
         | 
| 128 | 
            +
                        .unwrap()
         | 
| 129 | 
            +
                        .get_inner(self.inner)
         | 
| 105 130 | 
             
                        .funcall::<_, _, Value>("flush", ())
         | 
| 106 131 | 
             
                        .map_err(rberr_to_io_err)?;
         | 
| 107 132 |  | 
| @@ -117,8 +142,9 @@ impl Seek for RbFileLikeObject { | |
| 117 142 | 
             
                        SeekFrom::End(i) => (2, i),
         | 
| 118 143 | 
             
                    };
         | 
| 119 144 |  | 
| 120 | 
            -
                    let new_position =  | 
| 121 | 
            -
                        . | 
| 145 | 
            +
                    let new_position = Ruby::get()
         | 
| 146 | 
            +
                        .unwrap()
         | 
| 147 | 
            +
                        .get_inner(self.inner)
         | 
| 122 148 | 
             
                        .funcall("seek", (offset, whence))
         | 
| 123 149 | 
             
                        .map_err(rberr_to_io_err)?;
         | 
| 124 150 |  | 
| @@ -129,11 +155,12 @@ impl Seek for RbFileLikeObject { | |
| 129 155 | 
             
            pub trait FileLike: Read + Write + Seek {}
         | 
| 130 156 |  | 
| 131 157 | 
             
            impl FileLike for File {}
         | 
| 158 | 
            +
            impl FileLike for ClosableFile {}
         | 
| 132 159 | 
             
            impl FileLike for RbFileLikeObject {}
         | 
| 133 160 |  | 
| 134 161 | 
             
            pub enum EitherRustRubyFile {
         | 
| 135 162 | 
             
                Rb(RbFileLikeObject),
         | 
| 136 | 
            -
                Rust( | 
| 163 | 
            +
                Rust(ClosableFile),
         | 
| 137 164 | 
             
            }
         | 
| 138 165 |  | 
| 139 166 | 
             
            impl EitherRustRubyFile {
         | 
| @@ -144,6 +171,13 @@ impl EitherRustRubyFile { | |
| 144 171 | 
             
                    }
         | 
| 145 172 | 
             
                }
         | 
| 146 173 |  | 
| 174 | 
            +
                pub(crate) fn into_writeable(self) -> Box<dyn DynWriteable> {
         | 
| 175 | 
            +
                    match self {
         | 
| 176 | 
            +
                        Self::Rb(f) => Box::new(f),
         | 
| 177 | 
            +
                        Self::Rust(f) => Box::new(f),
         | 
| 178 | 
            +
                    }
         | 
| 179 | 
            +
                }
         | 
| 180 | 
            +
             | 
| 147 181 | 
             
                pub fn into_dyn_writeable(self) -> Box<dyn Write> {
         | 
| 148 182 | 
             
                    match self {
         | 
| 149 183 | 
             
                        EitherRustRubyFile::Rb(f) => Box::new(f),
         | 
| @@ -159,6 +193,14 @@ pub enum RubyScanSourceInput { | |
| 159 193 | 
             
                File(File),
         | 
| 160 194 | 
             
            }
         | 
| 161 195 |  | 
| 196 | 
            +
            pub(crate) fn try_get_rbfile(
         | 
| 197 | 
            +
                rb_f: Value,
         | 
| 198 | 
            +
                write: bool,
         | 
| 199 | 
            +
            ) -> RbResult<(EitherRustRubyFile, Option<PathBuf>)> {
         | 
| 200 | 
            +
                let f = RbFileLikeObject::with_requirements(rb_f, !write, write, !write)?;
         | 
| 201 | 
            +
                Ok((EitherRustRubyFile::Rb(f), None))
         | 
| 202 | 
            +
            }
         | 
| 203 | 
            +
             | 
| 162 204 | 
             
            pub fn get_ruby_scan_source_input(rb_f: Value, write: bool) -> RbResult<RubyScanSourceInput> {
         | 
| 163 205 | 
             
                if let Ok(file_path) = PathBuf::try_convert(rb_f) {
         | 
| 164 206 | 
             
                    // TODO resolve_homedir
         | 
| @@ -184,7 +226,7 @@ pub fn get_either_file(rb_f: Value, truncate: bool) -> RbResult<EitherRustRubyFi | |
| 184 226 | 
             
                    } else {
         | 
| 185 227 | 
             
                        polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
         | 
| 186 228 | 
             
                    };
         | 
| 187 | 
            -
                    Ok(EitherRustRubyFile::Rust(f))
         | 
| 229 | 
            +
                    Ok(EitherRustRubyFile::Rust(f.into()))
         | 
| 188 230 | 
             
                } else {
         | 
| 189 231 | 
             
                    let f = RbFileLikeObject::with_requirements(rb_f, !truncate, truncate, !truncate)?;
         | 
| 190 232 | 
             
                    Ok(EitherRustRubyFile::Rb(f))
         | 
| @@ -278,7 +278,13 @@ pub fn first() -> RbExpr { | |
| 278 278 | 
             
                dsl::first().into()
         | 
| 279 279 | 
             
            }
         | 
| 280 280 |  | 
| 281 | 
            -
            pub fn fold( | 
| 281 | 
            +
            pub fn fold(
         | 
| 282 | 
            +
                acc: &RbExpr,
         | 
| 283 | 
            +
                lambda: Value,
         | 
| 284 | 
            +
                exprs: RArray,
         | 
| 285 | 
            +
                returns_scalar: bool,
         | 
| 286 | 
            +
                return_dtype: Option<Wrap<DataType>>,
         | 
| 287 | 
            +
            ) -> RbResult<RbExpr> {
         | 
| 282 288 | 
             
                let exprs = rb_exprs_to_exprs(exprs)?;
         | 
| 283 289 | 
             
                let lambda = Opaque::from(lambda);
         | 
| 284 290 |  | 
| @@ -290,14 +296,21 @@ pub fn fold(acc: &RbExpr, lambda: Value, exprs: RArray) -> RbResult<RbExpr> { | |
| 290 296 | 
             
                    )
         | 
| 291 297 | 
             
                    .map(|v| v.map(Column::from))
         | 
| 292 298 | 
             
                };
         | 
| 293 | 
            -
                Ok(dsl::fold_exprs( | 
| 299 | 
            +
                Ok(dsl::fold_exprs(
         | 
| 300 | 
            +
                    acc.inner.clone(),
         | 
| 301 | 
            +
                    func,
         | 
| 302 | 
            +
                    exprs,
         | 
| 303 | 
            +
                    returns_scalar,
         | 
| 304 | 
            +
                    return_dtype.map(|w| w.0),
         | 
| 305 | 
            +
                )
         | 
| 306 | 
            +
                .into())
         | 
| 294 307 | 
             
            }
         | 
| 295 308 |  | 
| 296 309 | 
             
            pub fn last() -> RbExpr {
         | 
| 297 310 | 
             
                dsl::last().into()
         | 
| 298 311 | 
             
            }
         | 
| 299 312 |  | 
| 300 | 
            -
            pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> {
         | 
| 313 | 
            +
            pub fn lit(value: Value, allow_object: bool, is_scalar: bool) -> RbResult<RbExpr> {
         | 
| 301 314 | 
             
                if value.is_kind_of(class::true_class()) || value.is_kind_of(class::false_class()) {
         | 
| 302 315 | 
             
                    Ok(dsl::lit(bool::try_convert(value)?).into())
         | 
| 303 316 | 
             
                } else if let Some(v) = Integer::from_value(value) {
         | 
| @@ -323,7 +336,16 @@ pub fn lit(value: Value, allow_object: bool) -> RbResult<RbExpr> { | |
| 323 336 | 
             
                        Ok(dsl::lit(unsafe { v.as_slice() }).into())
         | 
| 324 337 | 
             
                    }
         | 
| 325 338 | 
             
                } else if let Ok(series) = Obj::<RbSeries>::try_convert(value) {
         | 
| 326 | 
            -
                     | 
| 339 | 
            +
                    let s = series.series.borrow();
         | 
| 340 | 
            +
                    if is_scalar {
         | 
| 341 | 
            +
                        let av = s
         | 
| 342 | 
            +
                            .get(0)
         | 
| 343 | 
            +
                            .map_err(|_| RbValueError::new_err("expected at least 1 value"))?;
         | 
| 344 | 
            +
                        let av = av.into_static();
         | 
| 345 | 
            +
                        Ok(dsl::lit(Scalar::new(s.dtype().clone(), av)).into())
         | 
| 346 | 
            +
                    } else {
         | 
| 347 | 
            +
                        Ok(dsl::lit(s.clone()).into())
         | 
| 348 | 
            +
                    }
         | 
| 327 349 | 
             
                } else if value.is_nil() {
         | 
| 328 350 | 
             
                    Ok(dsl::lit(Null {}).into())
         | 
| 329 351 | 
             
                } else if allow_object {
         | 
| @@ -56,14 +56,14 @@ pub fn datetime_range( | |
| 56 56 | 
             
                every: String,
         | 
| 57 57 | 
             
                closed: Wrap<ClosedWindow>,
         | 
| 58 58 | 
             
                time_unit: Option<Wrap<TimeUnit>>,
         | 
| 59 | 
            -
                time_zone: Option< | 
| 59 | 
            +
                time_zone: Wrap<Option<TimeZone>>,
         | 
| 60 60 | 
             
            ) -> RbExpr {
         | 
| 61 61 | 
             
                let start = start.inner.clone();
         | 
| 62 62 | 
             
                let end = end.inner.clone();
         | 
| 63 63 | 
             
                let every = Duration::parse(&every);
         | 
| 64 64 | 
             
                let closed = closed.0;
         | 
| 65 65 | 
             
                let time_unit = time_unit.map(|x| x.0);
         | 
| 66 | 
            -
                let time_zone = time_zone. | 
| 66 | 
            +
                let time_zone = time_zone.0;
         | 
| 67 67 | 
             
                dsl::datetime_range(start, end, every, closed, time_unit, time_zone).into()
         | 
| 68 68 | 
             
            }
         | 
| 69 69 |  | 
| @@ -73,14 +73,14 @@ pub fn datetime_ranges( | |
| 73 73 | 
             
                every: String,
         | 
| 74 74 | 
             
                closed: Wrap<ClosedWindow>,
         | 
| 75 75 | 
             
                time_unit: Option<Wrap<TimeUnit>>,
         | 
| 76 | 
            -
                time_zone: Option< | 
| 76 | 
            +
                time_zone: Wrap<Option<TimeZone>>,
         | 
| 77 77 | 
             
            ) -> RbExpr {
         | 
| 78 78 | 
             
                let start = start.inner.clone();
         | 
| 79 79 | 
             
                let end = end.inner.clone();
         | 
| 80 80 | 
             
                let every = Duration::parse(&every);
         | 
| 81 81 | 
             
                let closed = closed.0;
         | 
| 82 82 | 
             
                let time_unit = time_unit.map(|x| x.0);
         | 
| 83 | 
            -
                let time_zone = time_zone. | 
| 83 | 
            +
                let time_zone = time_zone.0;
         | 
| 84 84 | 
             
                dsl::datetime_ranges(start, end, every, closed, time_unit, time_zone).into()
         | 
| 85 85 | 
             
            }
         | 
| 86 86 |  |