polars-df 0.15.0 → 0.26.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 +274 -0
- data/Cargo.lock +1465 -867
- data/Cargo.toml +3 -0
- data/LICENSE.txt +2 -2
- data/README.md +87 -37
- data/ext/polars/Cargo.toml +47 -16
- data/ext/polars/src/c_api/allocator.rs +7 -0
- data/ext/polars/src/c_api/mod.rs +1 -0
- data/ext/polars/src/catalog/mod.rs +1 -0
- data/ext/polars/src/catalog/unity.rs +470 -0
- data/ext/polars/src/conversion/any_value.rs +99 -84
- data/ext/polars/src/conversion/categorical.rs +30 -0
- data/ext/polars/src/conversion/chunked_array.rs +71 -62
- data/ext/polars/src/conversion/datetime.rs +63 -0
- data/ext/polars/src/conversion/mod.rs +796 -312
- data/ext/polars/src/dataframe/construction.rs +6 -18
- data/ext/polars/src/dataframe/export.rs +30 -39
- data/ext/polars/src/dataframe/general.rs +294 -362
- data/ext/polars/src/dataframe/io.rs +33 -150
- data/ext/polars/src/dataframe/map.rs +175 -0
- data/ext/polars/src/dataframe/mod.rs +37 -5
- data/ext/polars/src/dataframe/serde.rs +23 -8
- data/ext/polars/src/error.rs +44 -7
- data/ext/polars/src/exceptions.rs +21 -8
- data/ext/polars/src/expr/array.rs +86 -22
- data/ext/polars/src/expr/binary.rs +50 -1
- data/ext/polars/src/expr/bitwise.rs +39 -0
- data/ext/polars/src/expr/categorical.rs +20 -0
- data/ext/polars/src/expr/datatype.rs +51 -0
- data/ext/polars/src/expr/datetime.rs +99 -41
- data/ext/polars/src/expr/extension.rs +12 -0
- data/ext/polars/src/expr/general.rs +252 -128
- data/ext/polars/src/expr/list.rs +56 -60
- data/ext/polars/src/expr/meta.rs +30 -35
- data/ext/polars/src/expr/mod.rs +28 -6
- data/ext/polars/src/expr/name.rs +29 -14
- data/ext/polars/src/expr/rolling.rs +111 -3
- data/ext/polars/src/expr/selector.rs +219 -0
- data/ext/polars/src/expr/serde.rs +28 -0
- data/ext/polars/src/expr/string.rs +118 -20
- data/ext/polars/src/expr/struct.rs +14 -1
- data/ext/polars/src/file.rs +194 -86
- data/ext/polars/src/functions/aggregation.rs +13 -12
- data/ext/polars/src/functions/business.rs +2 -3
- data/ext/polars/src/functions/eager.rs +3 -2
- data/ext/polars/src/functions/io.rs +90 -18
- data/ext/polars/src/functions/lazy.rs +267 -118
- data/ext/polars/src/functions/meta.rs +8 -7
- data/ext/polars/src/functions/misc.rs +1 -1
- data/ext/polars/src/functions/mod.rs +2 -1
- data/ext/polars/src/functions/range.rs +88 -31
- data/ext/polars/src/functions/strings.rs +6 -0
- data/ext/polars/src/functions/utils.rs +8 -0
- data/ext/polars/src/interop/arrow/mod.rs +52 -1
- data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
- data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
- data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
- data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
- data/ext/polars/src/io/cloud_options.rs +107 -0
- data/ext/polars/src/io/mod.rs +4 -0
- data/ext/polars/src/io/scan_options.rs +113 -0
- data/ext/polars/src/io/sink_options.rs +46 -0
- data/ext/polars/src/io/sink_output.rs +21 -0
- data/ext/polars/src/lazyframe/exitable.rs +39 -0
- data/ext/polars/src/lazyframe/general.rs +846 -368
- data/ext/polars/src/lazyframe/mod.rs +58 -5
- data/ext/polars/src/lazyframe/optflags.rs +59 -0
- data/ext/polars/src/lazyframe/serde.rs +36 -4
- data/ext/polars/src/lazyframe/sink.rs +46 -0
- data/ext/polars/src/lazygroupby.rs +38 -9
- data/ext/polars/src/lib.rs +574 -165
- data/ext/polars/src/map/lazy.rs +44 -74
- data/ext/polars/src/map/mod.rs +18 -254
- data/ext/polars/src/map/series.rs +241 -1087
- data/ext/polars/src/on_startup.rs +192 -9
- data/ext/polars/src/prelude.rs +1 -0
- data/ext/polars/src/rb_modules.rs +10 -57
- data/ext/polars/src/ruby/exceptions.rs +26 -0
- data/ext/polars/src/ruby/gvl.rs +104 -0
- data/ext/polars/src/ruby/lazy.rs +46 -0
- data/ext/polars/src/ruby/mod.rs +11 -0
- data/ext/polars/src/ruby/numo.rs +52 -0
- data/ext/polars/src/ruby/plan_callback.rs +198 -0
- data/ext/polars/src/ruby/rb_modules.rs +16 -0
- data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
- data/ext/polars/src/ruby/ruby_function.rs +11 -0
- data/ext/polars/src/ruby/ruby_udf.rs +164 -0
- data/ext/polars/src/ruby/thread.rs +65 -0
- data/ext/polars/src/ruby/utils.rs +39 -0
- data/ext/polars/src/series/aggregation.rs +116 -91
- data/ext/polars/src/series/arithmetic.rs +16 -22
- data/ext/polars/src/series/comparison.rs +101 -222
- data/ext/polars/src/series/construction.rs +80 -70
- data/ext/polars/src/series/export.rs +98 -56
- data/ext/polars/src/series/general.rs +323 -440
- data/ext/polars/src/series/import.rs +22 -5
- data/ext/polars/src/series/map.rs +103 -0
- data/ext/polars/src/series/mod.rs +57 -15
- data/ext/polars/src/series/scatter.rs +139 -82
- data/ext/polars/src/sql.rs +16 -9
- data/ext/polars/src/testing/frame.rs +31 -0
- data/ext/polars/src/testing/mod.rs +5 -0
- data/ext/polars/src/testing/series.rs +31 -0
- data/ext/polars/src/timeout.rs +105 -0
- data/ext/polars/src/utils.rs +105 -4
- data/lib/polars/array_expr.rb +500 -22
- data/lib/polars/array_name_space.rb +384 -10
- data/lib/polars/batched_csv_reader.rb +48 -66
- data/lib/polars/binary_expr.rb +217 -0
- data/lib/polars/binary_name_space.rb +155 -1
- data/lib/polars/cat_expr.rb +224 -0
- data/lib/polars/cat_name_space.rb +132 -32
- data/lib/polars/catalog/unity/catalog_info.rb +20 -0
- data/lib/polars/catalog/unity/column_info.rb +31 -0
- data/lib/polars/catalog/unity/namespace_info.rb +21 -0
- data/lib/polars/catalog/unity/table_info.rb +50 -0
- data/lib/polars/catalog.rb +448 -0
- data/lib/polars/collect_batches.rb +22 -0
- data/lib/polars/config.rb +3 -3
- data/lib/polars/convert.rb +201 -36
- data/lib/polars/data_frame.rb +2851 -1017
- data/lib/polars/data_frame_plot.rb +173 -0
- data/lib/polars/data_type_expr.rb +52 -0
- data/lib/polars/data_type_group.rb +6 -0
- data/lib/polars/data_types.rb +118 -18
- data/lib/polars/date_time_expr.rb +426 -84
- data/lib/polars/date_time_name_space.rb +384 -111
- data/lib/polars/dynamic_group_by.rb +102 -10
- data/lib/polars/exceptions.rb +50 -5
- data/lib/polars/expr.rb +2159 -915
- data/lib/polars/extension_expr.rb +39 -0
- data/lib/polars/extension_name_space.rb +39 -0
- data/lib/polars/functions/aggregation/horizontal.rb +11 -6
- data/lib/polars/functions/aggregation/vertical.rb +2 -3
- data/lib/polars/functions/as_datatype.rb +290 -8
- data/lib/polars/functions/business.rb +95 -0
- data/lib/polars/functions/col.rb +6 -5
- data/lib/polars/functions/datatype.rb +62 -0
- data/lib/polars/functions/eager.rb +426 -24
- data/lib/polars/functions/escape_regex.rb +21 -0
- data/lib/polars/functions/lazy.rb +813 -195
- data/lib/polars/functions/lit.rb +21 -10
- data/lib/polars/functions/range/int_range.rb +74 -2
- data/lib/polars/functions/range/linear_space.rb +195 -0
- data/lib/polars/functions/range/time_range.rb +1 -1
- data/lib/polars/functions/repeat.rb +7 -12
- data/lib/polars/functions/whenthen.rb +2 -2
- data/lib/polars/group_by.rb +188 -58
- data/lib/polars/iceberg_dataset.rb +108 -0
- data/lib/polars/in_process_query.rb +37 -0
- data/lib/polars/io/cloud.rb +18 -0
- data/lib/polars/io/csv.rb +336 -128
- data/lib/polars/io/database.rb +19 -4
- data/lib/polars/io/delta.rb +134 -0
- data/lib/polars/io/iceberg.rb +34 -0
- data/lib/polars/io/ipc.rb +63 -63
- data/lib/polars/io/json.rb +16 -0
- data/lib/polars/io/lines.rb +172 -0
- data/lib/polars/io/ndjson.rb +176 -20
- data/lib/polars/io/parquet.rb +173 -95
- data/lib/polars/io/scan_options.rb +55 -0
- data/lib/polars/io/sink_options.rb +27 -0
- data/lib/polars/io/utils.rb +17 -0
- data/lib/polars/lazy_frame.rb +3017 -622
- data/lib/polars/lazy_group_by.rb +436 -2
- data/lib/polars/list_expr.rb +551 -59
- data/lib/polars/list_name_space.rb +465 -51
- data/lib/polars/meta_expr.rb +146 -24
- data/lib/polars/name_expr.rb +87 -2
- data/lib/polars/query_opt_flags.rb +264 -0
- data/lib/polars/rolling_group_by.rb +90 -5
- data/lib/polars/scan_cast_options.rb +86 -0
- data/lib/polars/schema.rb +128 -0
- data/lib/polars/selector.rb +245 -0
- data/lib/polars/selectors.rb +1048 -201
- data/lib/polars/series.rb +2522 -774
- data/lib/polars/series_plot.rb +72 -0
- data/lib/polars/slice.rb +1 -1
- data/lib/polars/sql_context.rb +13 -6
- data/lib/polars/string_cache.rb +19 -72
- data/lib/polars/string_expr.rb +561 -107
- data/lib/polars/string_name_space.rb +781 -109
- data/lib/polars/struct_expr.rb +139 -18
- data/lib/polars/struct_name_space.rb +19 -1
- data/lib/polars/testing.rb +24 -273
- data/lib/polars/utils/constants.rb +2 -0
- data/lib/polars/utils/construction/data_frame.rb +410 -0
- data/lib/polars/utils/construction/series.rb +350 -0
- data/lib/polars/utils/construction/utils.rb +9 -0
- data/lib/polars/utils/convert.rb +18 -8
- data/lib/polars/utils/deprecation.rb +11 -0
- data/lib/polars/utils/parse.rb +62 -9
- data/lib/polars/utils/reduce_balanced.rb +43 -0
- data/lib/polars/utils/serde.rb +22 -0
- data/lib/polars/utils/unstable.rb +19 -0
- data/lib/polars/utils/various.rb +86 -1
- data/lib/polars/utils.rb +63 -48
- data/lib/polars/version.rb +1 -1
- data/lib/polars.rb +85 -2
- metadata +80 -28
- data/ext/polars/src/allocator.rs +0 -13
- data/ext/polars/src/batched_csv.rs +0 -138
- data/ext/polars/src/functions/string_cache.rs +0 -25
- data/ext/polars/src/map/dataframe.rs +0 -338
- data/lib/polars/plot.rb +0 -109
data/ext/polars/src/file.rs
CHANGED
|
@@ -3,16 +3,49 @@ use std::io;
|
|
|
3
3
|
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
|
4
4
|
use std::path::PathBuf;
|
|
5
5
|
|
|
6
|
-
use magnus::{
|
|
6
|
+
use magnus::{Error, RString, Ruby, Value, prelude::*, value::Opaque};
|
|
7
7
|
use polars::io::mmap::MmapBytesReader;
|
|
8
|
+
use polars::prelude::PlRefPath;
|
|
9
|
+
use polars::prelude::file::{Writeable, WriteableTrait};
|
|
10
|
+
use polars_buffer::Buffer;
|
|
11
|
+
use polars_utils::create_file;
|
|
8
12
|
|
|
9
13
|
use crate::error::RbPolarsErr;
|
|
10
14
|
use crate::prelude::resolve_homedir;
|
|
11
|
-
use crate::
|
|
15
|
+
use crate::ruby::gvl::GvlExt;
|
|
16
|
+
use crate::ruby::thread::{is_non_ruby_thread, run_in_ruby_thread, start_background_ruby_thread};
|
|
17
|
+
use crate::utils::to_rb_err;
|
|
18
|
+
use crate::{RbErr, RbResult};
|
|
12
19
|
|
|
13
|
-
#[derive(Clone)]
|
|
14
20
|
pub struct RbFileLikeObject {
|
|
15
|
-
inner: Value
|
|
21
|
+
inner: Opaque<Value>,
|
|
22
|
+
expects_str: bool,
|
|
23
|
+
has_flush: bool,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl WriteableTrait for RbFileLikeObject {
|
|
27
|
+
fn close(&mut self) -> io::Result<()> {
|
|
28
|
+
Ok(())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fn sync_all(&self) -> std::io::Result<()> {
|
|
32
|
+
self.flush()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn sync_data(&self) -> std::io::Result<()> {
|
|
36
|
+
self.flush()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl Clone for RbFileLikeObject {
|
|
41
|
+
fn clone(&self) -> Self {
|
|
42
|
+
// Skip attach
|
|
43
|
+
Self {
|
|
44
|
+
inner: self.inner,
|
|
45
|
+
expects_str: self.expects_str,
|
|
46
|
+
has_flush: self.has_flush,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
16
49
|
}
|
|
17
50
|
|
|
18
51
|
/// Wraps a `Value`, and implements read, seek, and write for it.
|
|
@@ -20,107 +53,142 @@ impl RbFileLikeObject {
|
|
|
20
53
|
/// Creates an instance of a `RbFileLikeObject` from a `Value`.
|
|
21
54
|
/// To assert the object has the required methods methods,
|
|
22
55
|
/// instantiate it with `RbFileLikeObject::require`
|
|
23
|
-
pub fn new(object: Value) -> Self {
|
|
24
|
-
RbFileLikeObject {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
56
|
+
pub fn new(object: Value, expects_str: bool, has_flush: bool) -> Self {
|
|
57
|
+
RbFileLikeObject {
|
|
58
|
+
inner: object.into(),
|
|
59
|
+
expects_str,
|
|
60
|
+
has_flush,
|
|
61
|
+
}
|
|
29
62
|
}
|
|
30
63
|
|
|
31
|
-
pub fn
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let buf = unsafe { bytes.as_slice() }.to_vec();
|
|
64
|
+
pub(crate) fn to_buffer(&self) -> Buffer<u8> {
|
|
65
|
+
Ruby::attach(|rb| {
|
|
66
|
+
let bytes = rb
|
|
67
|
+
.get_inner(self.inner)
|
|
68
|
+
.funcall::<_, _, RString>("read", ())
|
|
69
|
+
.expect("no read method found");
|
|
38
70
|
|
|
39
|
-
|
|
71
|
+
let b = unsafe { bytes.as_slice() }.to_vec();
|
|
72
|
+
Buffer::from_vec(b)
|
|
73
|
+
})
|
|
40
74
|
}
|
|
41
75
|
|
|
42
76
|
/// Same as `RbFileLikeObject::new`, but validates that the underlying
|
|
43
77
|
/// ruby object has a `read`, `write`, and `seek` methods in respect to parameters.
|
|
44
78
|
/// Will return a `TypeError` if object does not have `read`, `seek`, and `write` methods.
|
|
45
|
-
pub fn
|
|
79
|
+
pub fn ensure_requirements(object: Value, read: bool, write: bool, seek: bool) -> RbResult<()> {
|
|
80
|
+
let ruby = Ruby::get_with(object);
|
|
81
|
+
|
|
46
82
|
if read && !object.respond_to("read", false)? {
|
|
47
83
|
return Err(Error::new(
|
|
48
|
-
|
|
84
|
+
ruby.exception_type_error(),
|
|
49
85
|
"Object does not have a .read() method.",
|
|
50
86
|
));
|
|
51
87
|
}
|
|
52
88
|
|
|
53
89
|
if seek && !object.respond_to("seek", false)? {
|
|
54
90
|
return Err(Error::new(
|
|
55
|
-
|
|
91
|
+
ruby.exception_type_error(),
|
|
56
92
|
"Object does not have a .seek() method.",
|
|
57
93
|
));
|
|
58
94
|
}
|
|
59
95
|
|
|
60
96
|
if write && !object.respond_to("write", false)? {
|
|
61
97
|
return Err(Error::new(
|
|
62
|
-
|
|
98
|
+
ruby.exception_type_error(),
|
|
63
99
|
"Object does not have a .write() method.",
|
|
64
100
|
));
|
|
65
101
|
}
|
|
66
102
|
|
|
67
|
-
Ok(
|
|
103
|
+
Ok(())
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn flush(&self) -> std::io::Result<()> {
|
|
107
|
+
if self.has_flush {
|
|
108
|
+
if is_non_ruby_thread() {
|
|
109
|
+
let self2 = self.clone();
|
|
110
|
+
return run_in_ruby_thread(move || self2.flush());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Ruby::attach(|rb| {
|
|
114
|
+
rb.get_inner(self.inner)
|
|
115
|
+
.funcall::<_, _, Value>("flush", ())
|
|
116
|
+
.map_err(rberr_to_io_err)
|
|
117
|
+
})?;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
Ok(())
|
|
68
121
|
}
|
|
69
122
|
}
|
|
70
123
|
|
|
71
124
|
/// Extracts a string repr from, and returns an IO error to send back to rust.
|
|
72
|
-
fn rberr_to_io_err(e:
|
|
73
|
-
io::Error::
|
|
125
|
+
fn rberr_to_io_err(e: RbErr) -> io::Error {
|
|
126
|
+
Ruby::attach(|_rb| io::Error::other(e.to_string()))
|
|
74
127
|
}
|
|
75
128
|
|
|
76
129
|
impl Read for RbFileLikeObject {
|
|
77
130
|
fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
Ruby::attach(|rb| {
|
|
132
|
+
let bytes = rb
|
|
133
|
+
.get_inner(self.inner)
|
|
134
|
+
.funcall::<_, _, RString>("read", (buf.len(),))
|
|
135
|
+
.map_err(rberr_to_io_err)?;
|
|
82
136
|
|
|
83
|
-
|
|
137
|
+
buf.write_all(unsafe { bytes.as_slice() })?;
|
|
84
138
|
|
|
85
|
-
|
|
139
|
+
Ok(bytes.len())
|
|
140
|
+
})
|
|
86
141
|
}
|
|
87
142
|
}
|
|
88
143
|
|
|
89
144
|
impl Write for RbFileLikeObject {
|
|
90
145
|
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
|
91
|
-
|
|
146
|
+
if is_non_ruby_thread() {
|
|
147
|
+
let mut self2 = self.clone();
|
|
148
|
+
let buf2 = buf.to_vec();
|
|
149
|
+
return run_in_ruby_thread(move || self2.write(&buf2));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let expects_str = self.expects_str;
|
|
153
|
+
|
|
154
|
+
Ruby::attach(|rb| {
|
|
155
|
+
if expects_str {
|
|
156
|
+
todo!();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let rbbytes = rb.str_from_slice(buf);
|
|
92
160
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
161
|
+
let number_bytes_written = rb
|
|
162
|
+
.get_inner(self.inner)
|
|
163
|
+
.funcall::<_, _, usize>("write", (rbbytes,))
|
|
164
|
+
.map_err(rberr_to_io_err)?;
|
|
97
165
|
|
|
98
|
-
|
|
166
|
+
Ok(number_bytes_written)
|
|
167
|
+
})
|
|
99
168
|
}
|
|
100
169
|
|
|
101
170
|
fn flush(&mut self) -> Result<(), io::Error> {
|
|
102
|
-
self
|
|
103
|
-
.funcall::<_, _, Value>("flush", ())
|
|
104
|
-
.map_err(rberr_to_io_err)?;
|
|
105
|
-
|
|
106
|
-
Ok(())
|
|
171
|
+
Self::flush(self)
|
|
107
172
|
}
|
|
108
173
|
}
|
|
109
174
|
|
|
110
175
|
impl Seek for RbFileLikeObject {
|
|
111
176
|
fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
177
|
+
Ruby::attach(|rb| {
|
|
178
|
+
let (whence, offset) = match pos {
|
|
179
|
+
SeekFrom::Start(i) => (0, i as i64),
|
|
180
|
+
SeekFrom::Current(i) => (1, i),
|
|
181
|
+
SeekFrom::End(i) => (2, i),
|
|
182
|
+
};
|
|
117
183
|
|
|
118
|
-
|
|
119
|
-
.inner
|
|
120
|
-
.funcall("seek", (offset, whence))
|
|
121
|
-
.map_err(rberr_to_io_err)?;
|
|
184
|
+
let inner = rb.get_inner(self.inner);
|
|
122
185
|
|
|
123
|
-
|
|
186
|
+
inner
|
|
187
|
+
.funcall::<_, _, Value>("seek", (offset, whence))
|
|
188
|
+
.map_err(rberr_to_io_err)?;
|
|
189
|
+
|
|
190
|
+
inner.funcall("tell", ()).map_err(rberr_to_io_err)
|
|
191
|
+
})
|
|
124
192
|
}
|
|
125
193
|
}
|
|
126
194
|
|
|
@@ -131,7 +199,7 @@ impl FileLike for RbFileLikeObject {}
|
|
|
131
199
|
|
|
132
200
|
pub enum EitherRustRubyFile {
|
|
133
201
|
Rb(RbFileLikeObject),
|
|
134
|
-
Rust(File),
|
|
202
|
+
Rust(std::fs::File),
|
|
135
203
|
}
|
|
136
204
|
|
|
137
205
|
impl EitherRustRubyFile {
|
|
@@ -141,43 +209,85 @@ impl EitherRustRubyFile {
|
|
|
141
209
|
EitherRustRubyFile::Rust(f) => Box::new(f),
|
|
142
210
|
}
|
|
143
211
|
}
|
|
212
|
+
|
|
213
|
+
fn into_scan_source_input(self) -> RubyScanSourceInput {
|
|
214
|
+
match self {
|
|
215
|
+
EitherRustRubyFile::Rb(f) => RubyScanSourceInput::Buffer(f.to_buffer()),
|
|
216
|
+
EitherRustRubyFile::Rust(f) => RubyScanSourceInput::File(f),
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
pub(crate) fn into_writeable(self) -> Writeable {
|
|
221
|
+
match self {
|
|
222
|
+
Self::Rb(f) => Writeable::Dyn(Box::new(f)),
|
|
223
|
+
Self::Rust(f) => Writeable::Local(f),
|
|
224
|
+
}
|
|
225
|
+
}
|
|
144
226
|
}
|
|
145
227
|
|
|
146
228
|
pub enum RubyScanSourceInput {
|
|
147
|
-
Buffer(
|
|
148
|
-
Path(
|
|
149
|
-
|
|
150
|
-
File(File),
|
|
229
|
+
Buffer(Buffer<u8>),
|
|
230
|
+
Path(PlRefPath),
|
|
231
|
+
File(std::fs::File),
|
|
151
232
|
}
|
|
152
233
|
|
|
153
|
-
pub fn
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
234
|
+
pub(crate) fn try_get_rbfile(
|
|
235
|
+
rb: &Ruby,
|
|
236
|
+
rb_f: Value,
|
|
237
|
+
write: bool,
|
|
238
|
+
) -> RbResult<(EitherRustRubyFile, Option<PathBuf>)> {
|
|
239
|
+
RbFileLikeObject::ensure_requirements(rb_f, !write, write, !write)?;
|
|
240
|
+
let expects_str = false;
|
|
241
|
+
let has_flush = rb_f.respond_to("flush", false)?;
|
|
242
|
+
let f = RbFileLikeObject::new(rb_f, expects_str, has_flush);
|
|
243
|
+
|
|
244
|
+
// TODO move
|
|
245
|
+
if write {
|
|
246
|
+
start_background_ruby_thread(rb);
|
|
160
247
|
}
|
|
248
|
+
|
|
249
|
+
Ok((EitherRustRubyFile::Rb(f), None))
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
pub fn get_ruby_scan_source_input(rb_f: Value, write: bool) -> RbResult<RubyScanSourceInput> {
|
|
253
|
+
Ruby::attach(|rb| {
|
|
254
|
+
if let Ok(s) = PathBuf::try_convert(rb_f) {
|
|
255
|
+
let file_path =
|
|
256
|
+
PlRefPath::try_from_path(resolve_homedir(&s).as_ref()).map_err(to_rb_err)?;
|
|
257
|
+
|
|
258
|
+
Ok(RubyScanSourceInput::Path(file_path))
|
|
259
|
+
} else {
|
|
260
|
+
Ok(try_get_rbfile(rb, rb_f, write)?.0.into_scan_source_input())
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
pub fn get_either_buffer_or_path(
|
|
266
|
+
rb_f: Value,
|
|
267
|
+
write: bool,
|
|
268
|
+
) -> RbResult<(EitherRustRubyFile, Option<PathBuf>)> {
|
|
269
|
+
Ruby::attach(|rb| {
|
|
270
|
+
if let Ok(rstring) = RString::try_convert(rb_f) {
|
|
271
|
+
let s = unsafe { rstring.as_str() }?;
|
|
272
|
+
let file_path = std::path::Path::new(&s);
|
|
273
|
+
let file_path = resolve_homedir(&file_path);
|
|
274
|
+
let f = if write {
|
|
275
|
+
create_file(&file_path).map_err(RbPolarsErr::from)?
|
|
276
|
+
} else {
|
|
277
|
+
polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
|
|
278
|
+
};
|
|
279
|
+
Ok((EitherRustRubyFile::Rust(f), Some(file_path.into_owned())))
|
|
280
|
+
} else {
|
|
281
|
+
try_get_rbfile(rb, rb_f, write)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
161
284
|
}
|
|
162
285
|
|
|
163
286
|
///
|
|
164
287
|
/// # Arguments
|
|
165
|
-
/// * `
|
|
166
|
-
pub fn get_either_file(rb_f: Value,
|
|
167
|
-
|
|
168
|
-
let s = unsafe { rstring.as_str() }?;
|
|
169
|
-
let file_path = std::path::Path::new(&s);
|
|
170
|
-
let file_path = resolve_homedir(file_path);
|
|
171
|
-
let f = if truncate {
|
|
172
|
-
File::create(file_path).map_err(RbPolarsErr::from)?
|
|
173
|
-
} else {
|
|
174
|
-
polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
|
|
175
|
-
};
|
|
176
|
-
Ok(EitherRustRubyFile::Rust(f))
|
|
177
|
-
} else {
|
|
178
|
-
let f = RbFileLikeObject::with_requirements(rb_f, !truncate, truncate, !truncate)?;
|
|
179
|
-
Ok(EitherRustRubyFile::Rb(f))
|
|
180
|
-
}
|
|
288
|
+
/// * `write` - open for writing; will truncate existing file and create new file if not.
|
|
289
|
+
pub fn get_either_file(rb_f: Value, write: bool) -> RbResult<EitherRustRubyFile> {
|
|
290
|
+
Ok(get_either_buffer_or_path(rb_f, write)?.0)
|
|
181
291
|
}
|
|
182
292
|
|
|
183
293
|
pub fn get_file_like(f: Value, truncate: bool) -> RbResult<Box<dyn FileLike>> {
|
|
@@ -204,11 +314,9 @@ pub fn get_mmap_bytes_reader_and_path<'a>(
|
|
|
204
314
|
) -> RbResult<(Box<dyn MmapBytesReader + 'a>, Option<PathBuf>)> {
|
|
205
315
|
match rb_f {
|
|
206
316
|
RbReadBytes::Bytes(v) => Ok((Box::new(Cursor::new(unsafe { v.as_slice() })), None)),
|
|
207
|
-
RbReadBytes::Other(v) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
Ok((Box::new(f), Some(path)))
|
|
212
|
-
}
|
|
317
|
+
RbReadBytes::Other(v) => match get_either_buffer_or_path(*v, false)? {
|
|
318
|
+
(EitherRustRubyFile::Rust(f), path) => Ok((Box::new(f), path)),
|
|
319
|
+
(EitherRustRubyFile::Rb(f), path) => Ok((Box::new(Cursor::new(f.to_buffer())), path)),
|
|
320
|
+
},
|
|
213
321
|
}
|
|
214
322
|
}
|
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
use magnus::RArray;
|
|
2
2
|
use polars::lazy::dsl;
|
|
3
3
|
|
|
4
|
-
use crate::
|
|
5
|
-
use crate::
|
|
4
|
+
use crate::error::RbPolarsErr;
|
|
5
|
+
use crate::expr::ToExprs;
|
|
6
|
+
use crate::{RbExpr, RbResult};
|
|
6
7
|
|
|
7
8
|
pub fn all_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
8
|
-
let exprs =
|
|
9
|
+
let exprs = exprs.to_exprs()?;
|
|
9
10
|
let e = dsl::all_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
10
11
|
Ok(e.into())
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
pub fn any_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
14
|
-
let exprs =
|
|
15
|
+
let exprs = exprs.to_exprs()?;
|
|
15
16
|
let e = dsl::any_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
16
17
|
Ok(e.into())
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
pub fn max_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
20
|
-
let exprs =
|
|
21
|
+
let exprs = exprs.to_exprs()?;
|
|
21
22
|
let e = dsl::max_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
22
23
|
Ok(e.into())
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
pub fn min_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
26
|
-
let exprs =
|
|
27
|
+
let exprs = exprs.to_exprs()?;
|
|
27
28
|
let e = dsl::min_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
28
29
|
Ok(e.into())
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
pub fn sum_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
32
|
-
let exprs =
|
|
33
|
-
let e = dsl::sum_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
32
|
+
pub fn sum_horizontal(exprs: RArray, ignore_nulls: bool) -> RbResult<RbExpr> {
|
|
33
|
+
let exprs = exprs.to_exprs()?;
|
|
34
|
+
let e = dsl::sum_horizontal(exprs, ignore_nulls).map_err(RbPolarsErr::from)?;
|
|
34
35
|
Ok(e.into())
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
pub fn mean_horizontal(exprs: RArray) -> RbResult<RbExpr> {
|
|
38
|
-
let exprs =
|
|
39
|
-
let e = dsl::mean_horizontal(exprs).map_err(RbPolarsErr::from)?;
|
|
38
|
+
pub fn mean_horizontal(exprs: RArray, ignore_nulls: bool) -> RbResult<RbExpr> {
|
|
39
|
+
let exprs = exprs.to_exprs()?;
|
|
40
|
+
let e = dsl::mean_horizontal(exprs, ignore_nulls).map_err(RbPolarsErr::from)?;
|
|
40
41
|
Ok(e.into())
|
|
41
42
|
}
|
|
@@ -2,14 +2,13 @@ use polars::lazy::dsl;
|
|
|
2
2
|
|
|
3
3
|
use crate::RbExpr;
|
|
4
4
|
|
|
5
|
-
// TODO add to Ruby
|
|
6
5
|
pub fn business_day_count(
|
|
7
6
|
start: &RbExpr,
|
|
8
7
|
end: &RbExpr,
|
|
9
8
|
week_mask: [bool; 7],
|
|
10
|
-
holidays:
|
|
9
|
+
holidays: &RbExpr,
|
|
11
10
|
) -> RbExpr {
|
|
12
11
|
let start = start.inner.clone();
|
|
13
12
|
let end = end.inner.clone();
|
|
14
|
-
dsl::business_day_count(start, end, week_mask, holidays).into()
|
|
13
|
+
dsl::business_day_count(start, end, week_mask, holidays.inner.clone()).into()
|
|
15
14
|
}
|
|
@@ -59,11 +59,12 @@ pub fn concat_df_diagonal(seq: RArray) -> RbResult<RbDataFrame> {
|
|
|
59
59
|
Ok(df.into())
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
pub fn concat_df_horizontal(seq: RArray) -> RbResult<RbDataFrame> {
|
|
62
|
+
pub fn concat_df_horizontal(seq: RArray, strict: bool) -> RbResult<RbDataFrame> {
|
|
63
63
|
let mut dfs = Vec::new();
|
|
64
64
|
for item in seq.into_iter() {
|
|
65
65
|
dfs.push(get_df(item)?);
|
|
66
66
|
}
|
|
67
|
-
let df =
|
|
67
|
+
let df =
|
|
68
|
+
functions::concat_df_horizontal(&dfs, true, strict, false).map_err(RbPolarsErr::from)?;
|
|
68
69
|
Ok(df.into())
|
|
69
70
|
}
|
|
@@ -1,19 +1,80 @@
|
|
|
1
|
-
use
|
|
1
|
+
use std::io::BufReader;
|
|
2
|
+
|
|
3
|
+
use magnus::{RHash, Ruby, Value};
|
|
4
|
+
use polars::prelude::ArrowSchema;
|
|
5
|
+
use polars::prelude::CloudScheme;
|
|
2
6
|
|
|
3
7
|
use crate::conversion::Wrap;
|
|
4
|
-
use crate::file::
|
|
5
|
-
use crate::
|
|
8
|
+
use crate::file::{EitherRustRubyFile, get_either_file};
|
|
9
|
+
use crate::io::cloud_options::OptRbCloudOptions;
|
|
10
|
+
use crate::ruby::gvl::GvlExt;
|
|
11
|
+
use crate::ruby::utils::TryIntoValue;
|
|
6
12
|
use crate::{RbPolarsErr, RbResult};
|
|
7
13
|
|
|
8
|
-
pub fn read_ipc_schema(rb_f: Value) -> RbResult<RHash> {
|
|
9
|
-
use
|
|
10
|
-
let
|
|
11
|
-
|
|
14
|
+
pub fn read_ipc_schema(rb: &Ruby, rb_f: Value) -> RbResult<RHash> {
|
|
15
|
+
use arrow::io::ipc::read::read_file_metadata;
|
|
16
|
+
let metadata = match get_either_file(rb_f, false)? {
|
|
17
|
+
EitherRustRubyFile::Rust(r) => {
|
|
18
|
+
read_file_metadata(&mut BufReader::new(r)).map_err(RbPolarsErr::from)?
|
|
19
|
+
}
|
|
20
|
+
EitherRustRubyFile::Rb(mut r) => read_file_metadata(&mut r).map_err(RbPolarsErr::from)?,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let dict = rb.hash_new();
|
|
24
|
+
fields_to_rbdict(&metadata.schema, &dict)?;
|
|
25
|
+
Ok(dict)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn read_parquet_metadata(
|
|
29
|
+
rb: &Ruby,
|
|
30
|
+
rb_f: Value,
|
|
31
|
+
storage_options: OptRbCloudOptions,
|
|
32
|
+
credential_provider: Option<Value>,
|
|
33
|
+
) -> RbResult<RHash> {
|
|
34
|
+
use std::io::Cursor;
|
|
35
|
+
|
|
36
|
+
use polars_parquet::read::read_metadata;
|
|
37
|
+
use polars_parquet::read::schema::read_custom_key_value_metadata;
|
|
38
|
+
|
|
39
|
+
use crate::file::{RubyScanSourceInput, get_ruby_scan_source_input};
|
|
12
40
|
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
41
|
+
let metadata = match get_ruby_scan_source_input(rb_f, false)? {
|
|
42
|
+
RubyScanSourceInput::Buffer(buf) => {
|
|
43
|
+
read_metadata(&mut Cursor::new(buf)).map_err(RbPolarsErr::from)?
|
|
44
|
+
}
|
|
45
|
+
RubyScanSourceInput::Path(p) => {
|
|
46
|
+
let cloud_options = storage_options.extract_opt_cloud_options(
|
|
47
|
+
CloudScheme::from_path(p.as_str()),
|
|
48
|
+
credential_provider,
|
|
49
|
+
)?;
|
|
50
|
+
|
|
51
|
+
if p.has_scheme() {
|
|
52
|
+
use polars::prelude::ParquetObjectStore;
|
|
53
|
+
use polars_error::PolarsResult;
|
|
54
|
+
|
|
55
|
+
rb.detach(|| {
|
|
56
|
+
polars_core::runtime::ASYNC.block_on(async {
|
|
57
|
+
let mut reader =
|
|
58
|
+
ParquetObjectStore::from_uri(p, cloud_options.as_ref(), None).await?;
|
|
59
|
+
let result = reader.get_metadata().await?;
|
|
60
|
+
PolarsResult::Ok((**result).clone())
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
.map_err(RbPolarsErr::from)?
|
|
64
|
+
} else {
|
|
65
|
+
let file = polars_utils::open_file(p.as_std_path()).map_err(RbPolarsErr::from)?;
|
|
66
|
+
read_metadata(&mut BufReader::new(file)).map_err(RbPolarsErr::from)?
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
RubyScanSourceInput::File(f) => {
|
|
70
|
+
read_metadata(&mut BufReader::new(f)).map_err(RbPolarsErr::from)?
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let key_value_metadata = read_custom_key_value_metadata(metadata.key_value_metadata());
|
|
75
|
+
let dict = rb.hash_new();
|
|
76
|
+
for (key, value) in key_value_metadata.into_iter() {
|
|
77
|
+
dict.aset(key.as_str(), value.as_str())?;
|
|
17
78
|
}
|
|
18
79
|
Ok(dict)
|
|
19
80
|
}
|
|
@@ -21,14 +82,25 @@ pub fn read_ipc_schema(rb_f: Value) -> RbResult<RHash> {
|
|
|
21
82
|
pub fn read_parquet_schema(rb_f: Value) -> RbResult<RHash> {
|
|
22
83
|
use polars_parquet::read::{infer_schema, read_metadata};
|
|
23
84
|
|
|
24
|
-
let
|
|
25
|
-
|
|
85
|
+
let metadata = match get_either_file(rb_f, false)? {
|
|
86
|
+
EitherRustRubyFile::Rust(r) => {
|
|
87
|
+
read_metadata(&mut BufReader::new(r)).map_err(RbPolarsErr::from)?
|
|
88
|
+
}
|
|
89
|
+
EitherRustRubyFile::Rb(mut r) => read_metadata(&mut r).map_err(RbPolarsErr::from)?,
|
|
90
|
+
};
|
|
26
91
|
let arrow_schema = infer_schema(&metadata).map_err(RbPolarsErr::from)?;
|
|
27
92
|
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
dict.aset(field.name.as_str(), dt)?;
|
|
32
|
-
}
|
|
93
|
+
let ruby = Ruby::get_with(rb_f);
|
|
94
|
+
let dict = ruby.hash_new();
|
|
95
|
+
fields_to_rbdict(&arrow_schema, &dict)?;
|
|
33
96
|
Ok(dict)
|
|
34
97
|
}
|
|
98
|
+
|
|
99
|
+
fn fields_to_rbdict(schema: &ArrowSchema, dict: &RHash) -> RbResult<()> {
|
|
100
|
+
let ruby = &Ruby::get_with(*dict);
|
|
101
|
+
for field in schema.iter_values() {
|
|
102
|
+
let dt = Wrap(polars::prelude::DataType::from_arrow_field(field));
|
|
103
|
+
dict.aset(field.name.as_str(), dt.try_into_value_with(ruby)?)?;
|
|
104
|
+
}
|
|
105
|
+
Ok(())
|
|
106
|
+
}
|