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.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +274 -0
  3. data/Cargo.lock +1465 -867
  4. data/Cargo.toml +3 -0
  5. data/LICENSE.txt +2 -2
  6. data/README.md +87 -37
  7. data/ext/polars/Cargo.toml +47 -16
  8. data/ext/polars/src/c_api/allocator.rs +7 -0
  9. data/ext/polars/src/c_api/mod.rs +1 -0
  10. data/ext/polars/src/catalog/mod.rs +1 -0
  11. data/ext/polars/src/catalog/unity.rs +470 -0
  12. data/ext/polars/src/conversion/any_value.rs +99 -84
  13. data/ext/polars/src/conversion/categorical.rs +30 -0
  14. data/ext/polars/src/conversion/chunked_array.rs +71 -62
  15. data/ext/polars/src/conversion/datetime.rs +63 -0
  16. data/ext/polars/src/conversion/mod.rs +796 -312
  17. data/ext/polars/src/dataframe/construction.rs +6 -18
  18. data/ext/polars/src/dataframe/export.rs +30 -39
  19. data/ext/polars/src/dataframe/general.rs +294 -362
  20. data/ext/polars/src/dataframe/io.rs +33 -150
  21. data/ext/polars/src/dataframe/map.rs +175 -0
  22. data/ext/polars/src/dataframe/mod.rs +37 -5
  23. data/ext/polars/src/dataframe/serde.rs +23 -8
  24. data/ext/polars/src/error.rs +44 -7
  25. data/ext/polars/src/exceptions.rs +21 -8
  26. data/ext/polars/src/expr/array.rs +86 -22
  27. data/ext/polars/src/expr/binary.rs +50 -1
  28. data/ext/polars/src/expr/bitwise.rs +39 -0
  29. data/ext/polars/src/expr/categorical.rs +20 -0
  30. data/ext/polars/src/expr/datatype.rs +51 -0
  31. data/ext/polars/src/expr/datetime.rs +99 -41
  32. data/ext/polars/src/expr/extension.rs +12 -0
  33. data/ext/polars/src/expr/general.rs +252 -128
  34. data/ext/polars/src/expr/list.rs +56 -60
  35. data/ext/polars/src/expr/meta.rs +30 -35
  36. data/ext/polars/src/expr/mod.rs +28 -6
  37. data/ext/polars/src/expr/name.rs +29 -14
  38. data/ext/polars/src/expr/rolling.rs +111 -3
  39. data/ext/polars/src/expr/selector.rs +219 -0
  40. data/ext/polars/src/expr/serde.rs +28 -0
  41. data/ext/polars/src/expr/string.rs +118 -20
  42. data/ext/polars/src/expr/struct.rs +14 -1
  43. data/ext/polars/src/file.rs +194 -86
  44. data/ext/polars/src/functions/aggregation.rs +13 -12
  45. data/ext/polars/src/functions/business.rs +2 -3
  46. data/ext/polars/src/functions/eager.rs +3 -2
  47. data/ext/polars/src/functions/io.rs +90 -18
  48. data/ext/polars/src/functions/lazy.rs +267 -118
  49. data/ext/polars/src/functions/meta.rs +8 -7
  50. data/ext/polars/src/functions/misc.rs +1 -1
  51. data/ext/polars/src/functions/mod.rs +2 -1
  52. data/ext/polars/src/functions/range.rs +88 -31
  53. data/ext/polars/src/functions/strings.rs +6 -0
  54. data/ext/polars/src/functions/utils.rs +8 -0
  55. data/ext/polars/src/interop/arrow/mod.rs +52 -1
  56. data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +37 -7
  57. data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
  58. data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
  59. data/ext/polars/src/interop/numo/to_numo_series.rs +72 -50
  60. data/ext/polars/src/io/cloud_options.rs +107 -0
  61. data/ext/polars/src/io/mod.rs +4 -0
  62. data/ext/polars/src/io/scan_options.rs +113 -0
  63. data/ext/polars/src/io/sink_options.rs +46 -0
  64. data/ext/polars/src/io/sink_output.rs +21 -0
  65. data/ext/polars/src/lazyframe/exitable.rs +39 -0
  66. data/ext/polars/src/lazyframe/general.rs +846 -368
  67. data/ext/polars/src/lazyframe/mod.rs +58 -5
  68. data/ext/polars/src/lazyframe/optflags.rs +59 -0
  69. data/ext/polars/src/lazyframe/serde.rs +36 -4
  70. data/ext/polars/src/lazyframe/sink.rs +46 -0
  71. data/ext/polars/src/lazygroupby.rs +38 -9
  72. data/ext/polars/src/lib.rs +574 -165
  73. data/ext/polars/src/map/lazy.rs +44 -74
  74. data/ext/polars/src/map/mod.rs +18 -254
  75. data/ext/polars/src/map/series.rs +241 -1087
  76. data/ext/polars/src/on_startup.rs +192 -9
  77. data/ext/polars/src/prelude.rs +1 -0
  78. data/ext/polars/src/rb_modules.rs +10 -57
  79. data/ext/polars/src/ruby/exceptions.rs +26 -0
  80. data/ext/polars/src/ruby/gvl.rs +104 -0
  81. data/ext/polars/src/ruby/lazy.rs +46 -0
  82. data/ext/polars/src/ruby/mod.rs +11 -0
  83. data/ext/polars/src/ruby/numo.rs +52 -0
  84. data/ext/polars/src/ruby/plan_callback.rs +198 -0
  85. data/ext/polars/src/ruby/rb_modules.rs +16 -0
  86. data/ext/polars/src/ruby/ruby_convert_registry.rs +51 -0
  87. data/ext/polars/src/ruby/ruby_function.rs +11 -0
  88. data/ext/polars/src/ruby/ruby_udf.rs +164 -0
  89. data/ext/polars/src/ruby/thread.rs +65 -0
  90. data/ext/polars/src/ruby/utils.rs +39 -0
  91. data/ext/polars/src/series/aggregation.rs +116 -91
  92. data/ext/polars/src/series/arithmetic.rs +16 -22
  93. data/ext/polars/src/series/comparison.rs +101 -222
  94. data/ext/polars/src/series/construction.rs +80 -70
  95. data/ext/polars/src/series/export.rs +98 -56
  96. data/ext/polars/src/series/general.rs +323 -440
  97. data/ext/polars/src/series/import.rs +22 -5
  98. data/ext/polars/src/series/map.rs +103 -0
  99. data/ext/polars/src/series/mod.rs +57 -15
  100. data/ext/polars/src/series/scatter.rs +139 -82
  101. data/ext/polars/src/sql.rs +16 -9
  102. data/ext/polars/src/testing/frame.rs +31 -0
  103. data/ext/polars/src/testing/mod.rs +5 -0
  104. data/ext/polars/src/testing/series.rs +31 -0
  105. data/ext/polars/src/timeout.rs +105 -0
  106. data/ext/polars/src/utils.rs +105 -4
  107. data/lib/polars/array_expr.rb +500 -22
  108. data/lib/polars/array_name_space.rb +384 -10
  109. data/lib/polars/batched_csv_reader.rb +48 -66
  110. data/lib/polars/binary_expr.rb +217 -0
  111. data/lib/polars/binary_name_space.rb +155 -1
  112. data/lib/polars/cat_expr.rb +224 -0
  113. data/lib/polars/cat_name_space.rb +132 -32
  114. data/lib/polars/catalog/unity/catalog_info.rb +20 -0
  115. data/lib/polars/catalog/unity/column_info.rb +31 -0
  116. data/lib/polars/catalog/unity/namespace_info.rb +21 -0
  117. data/lib/polars/catalog/unity/table_info.rb +50 -0
  118. data/lib/polars/catalog.rb +448 -0
  119. data/lib/polars/collect_batches.rb +22 -0
  120. data/lib/polars/config.rb +3 -3
  121. data/lib/polars/convert.rb +201 -36
  122. data/lib/polars/data_frame.rb +2851 -1017
  123. data/lib/polars/data_frame_plot.rb +173 -0
  124. data/lib/polars/data_type_expr.rb +52 -0
  125. data/lib/polars/data_type_group.rb +6 -0
  126. data/lib/polars/data_types.rb +118 -18
  127. data/lib/polars/date_time_expr.rb +426 -84
  128. data/lib/polars/date_time_name_space.rb +384 -111
  129. data/lib/polars/dynamic_group_by.rb +102 -10
  130. data/lib/polars/exceptions.rb +50 -5
  131. data/lib/polars/expr.rb +2159 -915
  132. data/lib/polars/extension_expr.rb +39 -0
  133. data/lib/polars/extension_name_space.rb +39 -0
  134. data/lib/polars/functions/aggregation/horizontal.rb +11 -6
  135. data/lib/polars/functions/aggregation/vertical.rb +2 -3
  136. data/lib/polars/functions/as_datatype.rb +290 -8
  137. data/lib/polars/functions/business.rb +95 -0
  138. data/lib/polars/functions/col.rb +6 -5
  139. data/lib/polars/functions/datatype.rb +62 -0
  140. data/lib/polars/functions/eager.rb +426 -24
  141. data/lib/polars/functions/escape_regex.rb +21 -0
  142. data/lib/polars/functions/lazy.rb +813 -195
  143. data/lib/polars/functions/lit.rb +21 -10
  144. data/lib/polars/functions/range/int_range.rb +74 -2
  145. data/lib/polars/functions/range/linear_space.rb +195 -0
  146. data/lib/polars/functions/range/time_range.rb +1 -1
  147. data/lib/polars/functions/repeat.rb +7 -12
  148. data/lib/polars/functions/whenthen.rb +2 -2
  149. data/lib/polars/group_by.rb +188 -58
  150. data/lib/polars/iceberg_dataset.rb +108 -0
  151. data/lib/polars/in_process_query.rb +37 -0
  152. data/lib/polars/io/cloud.rb +18 -0
  153. data/lib/polars/io/csv.rb +336 -128
  154. data/lib/polars/io/database.rb +19 -4
  155. data/lib/polars/io/delta.rb +134 -0
  156. data/lib/polars/io/iceberg.rb +34 -0
  157. data/lib/polars/io/ipc.rb +63 -63
  158. data/lib/polars/io/json.rb +16 -0
  159. data/lib/polars/io/lines.rb +172 -0
  160. data/lib/polars/io/ndjson.rb +176 -20
  161. data/lib/polars/io/parquet.rb +173 -95
  162. data/lib/polars/io/scan_options.rb +55 -0
  163. data/lib/polars/io/sink_options.rb +27 -0
  164. data/lib/polars/io/utils.rb +17 -0
  165. data/lib/polars/lazy_frame.rb +3017 -622
  166. data/lib/polars/lazy_group_by.rb +436 -2
  167. data/lib/polars/list_expr.rb +551 -59
  168. data/lib/polars/list_name_space.rb +465 -51
  169. data/lib/polars/meta_expr.rb +146 -24
  170. data/lib/polars/name_expr.rb +87 -2
  171. data/lib/polars/query_opt_flags.rb +264 -0
  172. data/lib/polars/rolling_group_by.rb +90 -5
  173. data/lib/polars/scan_cast_options.rb +86 -0
  174. data/lib/polars/schema.rb +128 -0
  175. data/lib/polars/selector.rb +245 -0
  176. data/lib/polars/selectors.rb +1048 -201
  177. data/lib/polars/series.rb +2522 -774
  178. data/lib/polars/series_plot.rb +72 -0
  179. data/lib/polars/slice.rb +1 -1
  180. data/lib/polars/sql_context.rb +13 -6
  181. data/lib/polars/string_cache.rb +19 -72
  182. data/lib/polars/string_expr.rb +561 -107
  183. data/lib/polars/string_name_space.rb +781 -109
  184. data/lib/polars/struct_expr.rb +139 -18
  185. data/lib/polars/struct_name_space.rb +19 -1
  186. data/lib/polars/testing.rb +24 -273
  187. data/lib/polars/utils/constants.rb +2 -0
  188. data/lib/polars/utils/construction/data_frame.rb +410 -0
  189. data/lib/polars/utils/construction/series.rb +350 -0
  190. data/lib/polars/utils/construction/utils.rb +9 -0
  191. data/lib/polars/utils/convert.rb +18 -8
  192. data/lib/polars/utils/deprecation.rb +11 -0
  193. data/lib/polars/utils/parse.rb +62 -9
  194. data/lib/polars/utils/reduce_balanced.rb +43 -0
  195. data/lib/polars/utils/serde.rb +22 -0
  196. data/lib/polars/utils/unstable.rb +19 -0
  197. data/lib/polars/utils/various.rb +86 -1
  198. data/lib/polars/utils.rb +63 -48
  199. data/lib/polars/version.rb +1 -1
  200. data/lib/polars.rb +85 -2
  201. metadata +80 -28
  202. data/ext/polars/src/allocator.rs +0 -13
  203. data/ext/polars/src/batched_csv.rs +0 -138
  204. data/ext/polars/src/functions/string_cache.rs +0 -25
  205. data/ext/polars/src/map/dataframe.rs +0 -338
  206. data/lib/polars/plot.rb +0 -109
@@ -1,32 +1,215 @@
1
+ #![allow(unsafe_op_in_unsafe_fn)]
1
2
  use std::any::Any;
2
3
  use std::sync::Arc;
4
+ use std::sync::OnceLock;
3
5
 
4
- use magnus::IntoValue;
6
+ use arrow::array::Array;
7
+ use magnus::{IntoValue, Ruby, Value, prelude::*, value::Opaque};
8
+ use polars::chunked_array::object::ObjectArray;
5
9
  use polars::prelude::*;
6
10
  use polars_core::chunked_array::object::builder::ObjectChunkedBuilder;
7
11
  use polars_core::chunked_array::object::registry;
8
12
  use polars_core::chunked_array::object::registry::AnonymousObjectBuilder;
9
13
  use polars_core::prelude::AnyValue;
14
+ use polars_error::PolarsWarning;
15
+ use polars_error::signals::register_polars_keyboard_interrupt_hook;
10
16
 
11
- use crate::prelude::ObjectValue;
12
17
  use crate::Wrap;
18
+ use crate::dataframe::RbDataFrame;
19
+ use crate::lazyframe::RbLazyFrame;
20
+ use crate::map::lazy::call_lambda_with_series;
21
+ use crate::prelude::ObjectValue;
22
+ use crate::rb_modules::pl_utils;
23
+ use crate::ruby::gvl::GvlExt;
24
+ use crate::ruby::ruby_convert_registry::{FromRubyConvertRegistry, RubyConvertRegistry};
25
+ use crate::ruby::ruby_udf;
26
+ use crate::ruby::thread::{is_non_ruby_thread, run_in_ruby_thread};
27
+ use crate::ruby::utils::{TryIntoValue, to_pl_err};
28
+ use crate::series::RbSeries;
29
+
30
+ fn ruby_function_caller_series(
31
+ s: &[Column],
32
+ output_dtype: Option<DataType>,
33
+ lambda: Opaque<Value>,
34
+ ) -> PolarsResult<Column> {
35
+ if is_non_ruby_thread() {
36
+ let s2 = s.to_vec();
37
+ return run_in_ruby_thread(move || ruby_function_caller_series(&s2, output_dtype, lambda));
38
+ }
39
+
40
+ Ruby::attach(|rb| call_lambda_with_series(rb, s, output_dtype, lambda))
41
+ }
42
+
43
+ fn ruby_function_caller_df(df: DataFrame, lambda: Opaque<Value>) -> PolarsResult<DataFrame> {
44
+ Ruby::attach(|rb| {
45
+ let lambda = rb.get_inner(lambda);
46
+
47
+ let rbpolars = pl_utils(rb);
48
+
49
+ // create a RbSeries struct/object for Ruby
50
+ let rbdf = RbDataFrame::new(df);
51
+ // Wrap this RbSeries object in the Ruby side Series wrapper
52
+ let ruby_df_wrapper: Value = rbpolars.funcall("wrap_df", (rbdf,)).map_err(to_pl_err)?;
53
+
54
+ // call the lambda and get a Ruby side df wrapper
55
+ let result_df_wrapper: Value = lambda
56
+ .funcall("call", (ruby_df_wrapper,))
57
+ .map_err(to_pl_err)?;
58
+
59
+ // unpack the wrapper in a RbDataFrame
60
+ let rbdf: &RbDataFrame = result_df_wrapper.funcall("_df", ()).map_err(|_| {
61
+ let rbtype = unsafe { result_df_wrapper.classname() };
62
+ PolarsError::ComputeError(
63
+ format!("Expected 'LazyFrame.map' to return a 'DataFrame', got a '{rbtype}'",)
64
+ .into(),
65
+ )
66
+ })?;
67
+ Ok(rbdf.clone().df.into_inner())
68
+ })
69
+ }
70
+
71
+ fn warning_function(msg: &str, _warning: PolarsWarning) {
72
+ Ruby::attach(|rb| {
73
+ if let Err(e) = pl_utils(rb).funcall::<_, _, Value>("_polars_warn", (msg.to_string(),)) {
74
+ eprintln!("{e}")
75
+ }
76
+ })
77
+ }
13
78
 
14
- pub(crate) fn register_startup_deps() {
15
- if !registry::is_object_builder_registered() {
79
+ static POLARS_REGISTRY_INIT_LOCK: OnceLock<()> = OnceLock::new();
80
+
81
+ pub unsafe fn register_startup_deps(catch_keyboard_interrupt: bool) {
82
+ POLARS_REGISTRY_INIT_LOCK.get_or_init(|| {
16
83
  let object_builder = Box::new(|name: PlSmallStr, capacity: usize| {
17
84
  Box::new(ObjectChunkedBuilder::<ObjectValue>::new(name, capacity))
18
85
  as Box<dyn AnonymousObjectBuilder>
19
86
  });
20
87
 
21
88
  let object_converter = Arc::new(|av: AnyValue| {
22
- let object = ObjectValue {
23
- inner: Wrap(av).into_value().into(),
24
- };
89
+ let object = Ruby::attach(|rb| ObjectValue {
90
+ inner: Wrap(av).try_into_value_with(rb).unwrap().into(),
91
+ });
92
+ Box::new(object) as Box<dyn Any>
93
+ });
94
+ let rbobject_converter = Arc::new(|av: AnyValue| {
95
+ let object = Ruby::attach(|rb| Wrap(av).try_into_value_with(rb).unwrap());
25
96
  Box::new(object) as Box<dyn Any>
26
97
  });
98
+ fn object_array_getter(arr: &dyn Array, idx: usize) -> Option<AnyValue<'_>> {
99
+ let arr = arr
100
+ .as_any()
101
+ .downcast_ref::<ObjectArray<ObjectValue>>()
102
+ .unwrap();
103
+ arr.get(idx).map(|v| AnyValue::Object(v))
104
+ }
105
+ fn with_gil(f: &mut dyn FnMut()) {
106
+ Ruby::attach(|_| f())
107
+ }
108
+
109
+ crate::ruby::ruby_convert_registry::register_converters(RubyConvertRegistry {
110
+ from_rb: FromRubyConvertRegistry {
111
+ file_provider_result: Arc::new(|_rb_f| Ruby::attach(|_rb| todo!())),
112
+ series: Arc::new(|rb_f| {
113
+ Ruby::attach(|rb| {
114
+ Ok(Box::new(
115
+ <&RbSeries>::try_convert(rb.get_inner(rb_f))?
116
+ .clone()
117
+ .series
118
+ .into_inner(),
119
+ ) as _)
120
+ })
121
+ }),
122
+ df: Arc::new(|rb_f| {
123
+ Ruby::attach(|rb| {
124
+ Ok(Box::new(
125
+ <&RbDataFrame>::try_convert(rb.get_inner(rb_f))?
126
+ .clone()
127
+ .df
128
+ .into_inner(),
129
+ ) as _)
130
+ })
131
+ }),
132
+ dsl_plan: Arc::new(|rb_f| {
133
+ Ruby::attach(|rb| {
134
+ Ok(Box::new(
135
+ <&RbLazyFrame>::try_convert(rb.get_inner(rb_f))?
136
+ .clone()
137
+ .ldf
138
+ .into_inner()
139
+ .logical_plan,
140
+ ) as _)
141
+ })
142
+ }),
143
+ schema: Arc::new(|rb_f| {
144
+ Ruby::attach(|rb| {
145
+ Ok(Box::new(
146
+ Wrap::<polars_core::schema::Schema>::try_convert(rb.get_inner(rb_f))?.0,
147
+ ) as _)
148
+ })
149
+ }),
150
+ },
151
+ to_rb: crate::ruby::ruby_convert_registry::ToRubyConvertRegistry {
152
+ df: Arc::new(|df| {
153
+ Ruby::attach(|rb| {
154
+ Ok(
155
+ RbDataFrame::new(df.downcast_ref::<DataFrame>().unwrap().clone())
156
+ .into_value_with(rb),
157
+ )
158
+ })
159
+ }),
160
+ series: Arc::new(|series| {
161
+ Ruby::attach(|rb| {
162
+ Ok(
163
+ RbSeries::new(series.downcast_ref::<Series>().unwrap().clone())
164
+ .into_value_with(rb),
165
+ )
166
+ })
167
+ }),
168
+ dsl_plan: Arc::new(|dsl_plan| {
169
+ Ruby::attach(|rb| {
170
+ Ok(RbLazyFrame::from(LazyFrame::from(
171
+ dsl_plan
172
+ .downcast_ref::<polars_plan::dsl::DslPlan>()
173
+ .unwrap()
174
+ .clone(),
175
+ ))
176
+ .into_value_with(rb))
177
+ })
178
+ }),
179
+ schema: Arc::new(|schema| {
180
+ Ruby::attach(|rb| {
181
+ Wrap(
182
+ schema
183
+ .downcast_ref::<polars_core::schema::Schema>()
184
+ .unwrap()
185
+ .clone(),
186
+ )
187
+ .try_into_value_with(rb)
188
+ })
189
+ }),
190
+ },
191
+ });
27
192
 
28
193
  let object_size = std::mem::size_of::<ObjectValue>();
29
194
  let physical_dtype = ArrowDataType::FixedSizeBinary(object_size);
30
- registry::register_object_builder(object_builder, object_converter, physical_dtype)
31
- }
195
+ registry::register_object_builder(
196
+ object_builder,
197
+ object_converter,
198
+ rbobject_converter,
199
+ physical_dtype,
200
+ Arc::new(object_array_getter),
201
+ Arc::new(with_gil),
202
+ );
203
+
204
+ // Register SERIES UDF.
205
+ ruby_udf::CALL_COLUMNS_UDF_RUBY = Some(ruby_function_caller_series);
206
+ // Register DATAFRAME UDF.
207
+ ruby_udf::CALL_DF_UDF_RUBY = Some(ruby_function_caller_df);
208
+ // Register warning function for `polars_warn!`.
209
+ polars_error::set_warning_function(warning_function);
210
+
211
+ if catch_keyboard_interrupt {
212
+ register_polars_keyboard_interrupt_hook();
213
+ }
214
+ });
32
215
  }
@@ -1,3 +1,4 @@
1
1
  pub use polars::prelude::*;
2
2
 
3
3
  pub use crate::conversion::*;
4
+ pub(crate) use crate::rb_modules;
@@ -1,64 +1,17 @@
1
- use magnus::{value::Lazy, ExceptionClass, Module, RClass, RModule, Ruby};
1
+ use magnus::{Module, RClass, RModule, Ruby, value::Lazy};
2
2
 
3
- static POLARS: Lazy<RModule> = Lazy::new(|ruby| ruby.class_object().const_get("Polars").unwrap());
3
+ static POLARS: Lazy<RModule> = Lazy::new(|rb| rb.class_object().const_get("Polars").unwrap());
4
+ static UTILS: Lazy<RModule> = Lazy::new(|rb| rb.get_inner(&POLARS).const_get("Utils").unwrap());
5
+ static SERIES: Lazy<RClass> = Lazy::new(|rb| rb.get_inner(&POLARS).const_get("Series").unwrap());
4
6
 
5
- pub(crate) fn polars() -> RModule {
6
- Ruby::get().unwrap().get_inner(&POLARS)
7
+ pub(crate) fn polars(rb: &Ruby) -> RModule {
8
+ rb.get_inner(&POLARS)
7
9
  }
8
10
 
9
- static SERIES: Lazy<RClass> =
10
- Lazy::new(|ruby| ruby.get_inner(&POLARS).const_get("Series").unwrap());
11
-
12
- pub(crate) fn series() -> RClass {
13
- Ruby::get().unwrap().get_inner(&SERIES)
14
- }
15
-
16
- static UTILS: Lazy<RModule> = Lazy::new(|ruby| ruby.get_inner(&POLARS).const_get("Utils").unwrap());
17
-
18
- pub(crate) fn utils() -> RModule {
19
- Ruby::get().unwrap().get_inner(&UTILS)
20
- }
21
-
22
- static BIGDECIMAL: Lazy<RClass> =
23
- Lazy::new(|ruby| ruby.class_object().const_get("BigDecimal").unwrap());
24
-
25
- pub(crate) fn bigdecimal() -> RClass {
26
- Ruby::get().unwrap().get_inner(&BIGDECIMAL)
27
- }
28
-
29
- static DATE: Lazy<RClass> = Lazy::new(|ruby| ruby.class_object().const_get("Date").unwrap());
30
-
31
- pub(crate) fn date() -> RClass {
32
- Ruby::get().unwrap().get_inner(&DATE)
11
+ pub(crate) fn pl_utils(rb: &Ruby) -> RModule {
12
+ rb.get_inner(&UTILS)
33
13
  }
34
14
 
35
- static DATETIME: Lazy<RClass> =
36
- Lazy::new(|ruby| ruby.class_object().const_get("DateTime").unwrap());
37
-
38
- pub(crate) fn datetime() -> RClass {
39
- Ruby::get().unwrap().get_inner(&DATETIME)
40
- }
41
-
42
- static ERROR: Lazy<ExceptionClass> =
43
- Lazy::new(|ruby| ruby.get_inner(&POLARS).const_get("Error").unwrap());
44
-
45
- pub(crate) fn error() -> ExceptionClass {
46
- Ruby::get().unwrap().get_inner(&ERROR)
47
- }
48
-
49
- static COMPUTE_ERROR: Lazy<ExceptionClass> =
50
- Lazy::new(|ruby| ruby.get_inner(&POLARS).const_get("ComputeError").unwrap());
51
-
52
- pub(crate) fn compute_error() -> ExceptionClass {
53
- Ruby::get().unwrap().get_inner(&COMPUTE_ERROR)
54
- }
55
-
56
- static INVALID_OPERATION_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
57
- ruby.get_inner(&POLARS)
58
- .const_get("InvalidOperationError")
59
- .unwrap()
60
- });
61
-
62
- pub(crate) fn invalid_operation_error() -> ExceptionClass {
63
- Ruby::get().unwrap().get_inner(&INVALID_OPERATION_ERROR)
15
+ pub(crate) fn pl_series(rb: &Ruby) -> RClass {
16
+ rb.get_inner(&SERIES)
64
17
  }
@@ -0,0 +1,26 @@
1
+ use magnus::{Error, Ruby};
2
+ use std::borrow::Cow;
3
+
4
+ macro_rules! create_ruby_exception {
5
+ ($type:ident, $cls:ident) => {
6
+ pub struct $type {}
7
+
8
+ impl $type {
9
+ pub fn new_err<T>(message: T) -> Error
10
+ where
11
+ T: Into<Cow<'static, str>>,
12
+ {
13
+ let cls = Ruby::get().unwrap().$cls();
14
+ Error::new(cls, message)
15
+ }
16
+ }
17
+ };
18
+ }
19
+
20
+ create_ruby_exception!(RbKeyboardInterrupt, exception_interrupt);
21
+ create_ruby_exception!(RbIndexError, exception_index_error);
22
+ create_ruby_exception!(RbIOError, exception_io_error);
23
+ create_ruby_exception!(RbOverflowError, exception_range_error);
24
+ create_ruby_exception!(RbRuntimeError, exception_runtime_error);
25
+ create_ruby_exception!(RbTypeError, exception_type_error);
26
+ create_ruby_exception!(RbValueError, exception_arg_error);
@@ -0,0 +1,104 @@
1
+ use std::ffi::c_void;
2
+ use std::ptr::null_mut;
3
+
4
+ use magnus::Ruby;
5
+ use magnus::error::RubyUnavailableError;
6
+ use rb_sys::{rb_thread_call_with_gvl, rb_thread_call_without_gvl};
7
+
8
+ pub trait GvlExt {
9
+ fn attach<T, F>(func: F) -> T
10
+ where
11
+ F: FnOnce(&Ruby) -> T;
12
+
13
+ fn detach<T, F>(&self, func: F) -> T
14
+ where
15
+ F: Send + FnOnce() -> T,
16
+ T: Send;
17
+ }
18
+
19
+ unsafe extern "C" {
20
+ fn ruby_thread_has_gvl_p() -> std::ffi::c_int;
21
+ }
22
+
23
+ impl GvlExt for Ruby {
24
+ fn attach<T, F>(func: F) -> T
25
+ where
26
+ F: FnOnce(&Ruby) -> T,
27
+ {
28
+ // recheck GVL state since cached value can be incorrect
29
+ // https://github.com/matsadler/magnus/pull/161
30
+ if let Ok(rb) = Ruby::get()
31
+ && unsafe { ruby_thread_has_gvl_p() } != 0
32
+ {
33
+ func(&rb)
34
+ } else if !matches!(Ruby::get(), Err(RubyUnavailableError::NonRubyThread)) {
35
+ let mut data = CallbackData {
36
+ func: Some(func),
37
+ result: None,
38
+ };
39
+
40
+ unsafe {
41
+ rb_thread_call_with_gvl(
42
+ Some(call_with_gvl::<F, T>),
43
+ &mut data as *mut _ as *mut c_void,
44
+ );
45
+ }
46
+
47
+ data.result.unwrap()
48
+ } else {
49
+ panic!("Non-Ruby thread");
50
+ }
51
+ }
52
+
53
+ fn detach<T, F>(&self, func: F) -> T
54
+ where
55
+ F: Send + FnOnce() -> T,
56
+ T: Send,
57
+ {
58
+ if std::env::var("POLARS_GVL").is_ok() {
59
+ func()
60
+ } else {
61
+ let mut data = CallbackData {
62
+ func: Some(func),
63
+ result: None,
64
+ };
65
+
66
+ unsafe {
67
+ rb_thread_call_without_gvl(
68
+ Some(call_without_gvl::<F, T>),
69
+ &mut data as *mut _ as *mut c_void,
70
+ None,
71
+ null_mut(),
72
+ );
73
+ }
74
+
75
+ data.result.unwrap()
76
+ }
77
+ }
78
+ }
79
+
80
+ struct CallbackData<F, T> {
81
+ func: Option<F>,
82
+ result: Option<T>,
83
+ }
84
+
85
+ extern "C" fn call_without_gvl<F, T>(data: *mut c_void) -> *mut c_void
86
+ where
87
+ F: FnOnce() -> T,
88
+ {
89
+ let data = unsafe { &mut *(data as *mut CallbackData<F, T>) };
90
+ let func = data.func.take().unwrap();
91
+ data.result = Some(func());
92
+ null_mut()
93
+ }
94
+
95
+ extern "C" fn call_with_gvl<F, T>(data: *mut c_void) -> *mut c_void
96
+ where
97
+ F: FnOnce(&Ruby) -> T,
98
+ {
99
+ let rb = Ruby::get().unwrap();
100
+ let data = unsafe { &mut *(data as *mut CallbackData<F, T>) };
101
+ let func = data.func.take().unwrap();
102
+ data.result = Some(func(&rb));
103
+ null_mut()
104
+ }
@@ -0,0 +1,46 @@
1
+ use magnus::{Ruby, Value, value::Opaque};
2
+ use polars::prelude::{AllowedOptimizations, DataFrame, LazyFrame, SchemaRef};
3
+
4
+ use crate::ruby::ruby_function::RubyFunction;
5
+ use crate::ruby::ruby_udf::CALL_DF_UDF_RUBY;
6
+ use crate::ruby::thread::{is_non_ruby_thread, run_in_ruby_thread, start_background_ruby_thread};
7
+ use crate::ruby::utils::ArcValue;
8
+
9
+ pub trait RubyUdfLazyFrameExt {
10
+ fn map_ruby(
11
+ self,
12
+ function: RubyFunction,
13
+ optimizations: AllowedOptimizations,
14
+ schema: Option<SchemaRef>,
15
+ validate_output: bool,
16
+ ) -> LazyFrame;
17
+ }
18
+
19
+ impl RubyUdfLazyFrameExt for LazyFrame {
20
+ fn map_ruby(
21
+ self,
22
+ function: RubyFunction,
23
+ optimizations: AllowedOptimizations,
24
+ _schema: Option<SchemaRef>,
25
+ _validate_output: bool,
26
+ ) -> LazyFrame {
27
+ let f = move |df: DataFrame, lambda: Opaque<Value>| {
28
+ let func = unsafe { CALL_DF_UDF_RUBY.unwrap() };
29
+ func(df, lambda)
30
+ };
31
+
32
+ // handle non-Ruby threads
33
+ start_background_ruby_thread(&Ruby::get().unwrap());
34
+ let udf = ArcValue::new(function.0);
35
+ let f = move |df| {
36
+ if is_non_ruby_thread() {
37
+ let udf = udf.clone();
38
+ return run_in_ruby_thread(move || f(df, *udf.0));
39
+ }
40
+ f(df, *udf.0)
41
+ };
42
+
43
+ let schema = None;
44
+ self.map(f, optimizations, schema, Some("RUBY UDF"))
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ pub mod exceptions;
2
+ pub mod gvl;
3
+ pub mod lazy;
4
+ pub mod numo;
5
+ pub mod plan_callback;
6
+ pub mod rb_modules;
7
+ pub mod ruby_convert_registry;
8
+ pub mod ruby_function;
9
+ pub mod ruby_udf;
10
+ pub mod thread;
11
+ pub mod utils;
@@ -0,0 +1,52 @@
1
+ use magnus::{IntoValue, Module, RClass, RModule, Ruby, Value, prelude::*};
2
+
3
+ use crate::RbResult;
4
+
5
+ pub trait Element: IntoValue {
6
+ fn class_name() -> &'static str;
7
+ }
8
+
9
+ macro_rules! create_element {
10
+ ($type:ty, $name:expr) => {
11
+ impl Element for $type {
12
+ fn class_name() -> &'static str {
13
+ $name
14
+ }
15
+ }
16
+ };
17
+ }
18
+
19
+ create_element!(i8, "Int8");
20
+ create_element!(i16, "Int16");
21
+ create_element!(i32, "Int32");
22
+ create_element!(i64, "Int64");
23
+ create_element!(u8, "UInt8");
24
+ create_element!(u16, "UInt16");
25
+ create_element!(u32, "UInt32");
26
+ create_element!(u64, "UInt64");
27
+ create_element!(f32, "SFloat");
28
+ create_element!(f64, "DFloat");
29
+ create_element!(bool, "Bit");
30
+
31
+ impl<T> Element for Option<T>
32
+ where
33
+ Option<T>: IntoValue,
34
+ {
35
+ fn class_name() -> &'static str {
36
+ "RObject"
37
+ }
38
+ }
39
+
40
+ pub struct RbArray1<T>(T);
41
+
42
+ impl<T: Element> RbArray1<T> {
43
+ pub fn from_iter<I>(rb: &Ruby, values: I) -> RbResult<Value>
44
+ where
45
+ I: IntoIterator<Item = T>,
46
+ {
47
+ rb.class_object()
48
+ .const_get::<_, RModule>("Numo")?
49
+ .const_get::<_, RClass>(T::class_name())?
50
+ .funcall("cast", (rb.ary_from_iter(values),))
51
+ }
52
+ }