1pub mod constants;
2mod warning;
3
4use std::borrow::Cow;
5use std::collections::TryReserveError;
6use std::convert::Infallible;
7use std::error::Error;
8use std::fmt::{self, Display, Formatter, Write};
9use std::ops::Deref;
10use std::sync::{Arc, LazyLock};
11use std::{env, io};
12pub mod signals;
13
14pub use warning::*;
15
16#[cfg(feature = "python")]
17mod python;
18
19enum ErrorStrategy {
20 Panic,
21 WithBacktrace,
22 Normal,
23}
24
25static ERROR_STRATEGY: LazyLock<ErrorStrategy> = LazyLock::new(|| {
26 if env::var("POLARS_PANIC_ON_ERR").as_deref() == Ok("1") {
27 ErrorStrategy::Panic
28 } else if env::var("POLARS_BACKTRACE_IN_ERR").as_deref() == Ok("1") {
29 ErrorStrategy::WithBacktrace
30 } else {
31 ErrorStrategy::Normal
32 }
33});
34
35#[derive(Debug, Clone)]
36pub struct ErrString(Cow<'static, str>);
37
38impl ErrString {
39 pub const fn new_static(s: &'static str) -> Self {
40 Self(Cow::Borrowed(s))
41 }
42}
43
44impl<T> From<T> for ErrString
45where
46 T: Into<Cow<'static, str>>,
47{
48 #[track_caller]
49 fn from(msg: T) -> Self {
50 match &*ERROR_STRATEGY {
51 ErrorStrategy::Panic => panic!("{}", msg.into()),
52 ErrorStrategy::WithBacktrace => ErrString(Cow::Owned(format!(
53 "{}\n\nRust backtrace:\n{}",
54 msg.into(),
55 std::backtrace::Backtrace::force_capture()
56 ))),
57 ErrorStrategy::Normal => ErrString(msg.into()),
58 }
59 }
60}
61
62impl AsRef<str> for ErrString {
63 fn as_ref(&self) -> &str {
64 &self.0
65 }
66}
67
68impl Deref for ErrString {
69 type Target = str;
70
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74}
75
76impl Display for ErrString {
77 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
78 write!(f, "{}", self.0)
79 }
80}
81
82#[derive(Debug, Clone)]
83pub enum PolarsError {
84 AssertionError(ErrString),
85 ColumnNotFound(ErrString),
86 ComputeError(ErrString),
87 Duplicate(ErrString),
88 InvalidOperation(ErrString),
89 IO {
90 error: Arc<io::Error>,
91 msg: Option<ErrString>,
92 },
93 NoData(ErrString),
94 OutOfBounds(ErrString),
95 SchemaFieldNotFound(ErrString),
96 SchemaMismatch(ErrString),
97 ShapeMismatch(ErrString),
98 SQLInterface(ErrString),
99 SQLSyntax(ErrString),
100 StringCacheMismatch(ErrString),
101 StructFieldNotFound(ErrString),
102 Context {
103 error: Box<PolarsError>,
104 msg: ErrString,
105 },
106 ExprContext {
107 error: Box<PolarsError>,
108 expr: ErrString,
109 },
110 #[cfg(feature = "python")]
111 Python {
112 error: python::PyErrWrap,
113 },
114}
115
116impl Error for PolarsError {
117 fn source(&self) -> Option<&(dyn Error + 'static)> {
118 match self {
119 PolarsError::AssertionError(_)
120 | PolarsError::ColumnNotFound(_)
121 | PolarsError::ComputeError(_)
122 | PolarsError::Duplicate(_)
123 | PolarsError::InvalidOperation(_)
124 | PolarsError::NoData(_)
125 | PolarsError::OutOfBounds(_)
126 | PolarsError::SchemaFieldNotFound(_)
127 | PolarsError::SchemaMismatch(_)
128 | PolarsError::ShapeMismatch(_)
129 | PolarsError::SQLInterface(_)
130 | PolarsError::SQLSyntax(_)
131 | PolarsError::StringCacheMismatch(_)
132 | PolarsError::StructFieldNotFound(_) => None,
133 PolarsError::IO { error, .. } => Some(error.as_ref()),
134 PolarsError::Context { error, .. } => Some(error.as_ref()),
135 PolarsError::ExprContext { error, .. } => Some(error.as_ref()),
136 #[cfg(feature = "python")]
137 PolarsError::Python { error } => error.deref().source(),
138 }
139 }
140}
141
142impl Display for PolarsError {
143 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144 use PolarsError::*;
145 match self {
146 ComputeError(msg)
147 | InvalidOperation(msg)
148 | OutOfBounds(msg)
149 | SchemaMismatch(msg)
150 | SQLInterface(msg)
151 | SQLSyntax(msg) => write!(f, "{msg}"),
152
153 AssertionError(msg) => write!(f, "assertion failed: {msg}"),
154 ColumnNotFound(msg) => write!(f, "not found: {msg}"),
155 Duplicate(msg) => write!(f, "duplicate: {msg}"),
156 IO { error, msg } => match msg {
157 Some(m) => write!(f, "{m}"),
158 None => write!(f, "{error}"),
159 },
160 NoData(msg) => write!(f, "no data: {msg}"),
161 SchemaFieldNotFound(msg) => write!(f, "field not found: {msg}"),
162 ShapeMismatch(msg) => write!(f, "lengths don't match: {msg}"),
163 StringCacheMismatch(msg) => write!(f, "string caches don't match: {msg}"),
164 StructFieldNotFound(msg) => write!(f, "field not found: {msg}"),
165 Context { error, msg } => write!(f, "{error}: {msg}"),
166 ExprContext { error, expr: _ } => write!(f, "{error}"),
167 #[cfg(feature = "python")]
168 Python { error } => write!(f, "python: {error}"),
169 }
170 }
171}
172
173impl From<io::Error> for PolarsError {
174 fn from(mut value: io::Error) -> Self {
175 if let Some(polars_err) = value
176 .get_mut()
177 .and_then(|e| e.downcast_mut::<PolarsError>())
178 {
179 std::mem::replace(
180 polars_err,
181 PolarsError::ComputeError(ErrString::new_static("")),
182 )
183 } else {
184 PolarsError::IO {
185 error: Arc::new(value),
186 msg: None,
187 }
188 }
189 }
190}
191
192impl From<PolarsError> for io::Error {
193 fn from(value: PolarsError) -> Self {
194 io::Error::other(value)
195 }
196}
197
198#[cfg(feature = "regex")]
199impl From<regex::Error> for PolarsError {
200 fn from(err: regex::Error) -> Self {
201 PolarsError::ComputeError(format!("regex error: {err}").into())
202 }
203}
204
205#[cfg(feature = "avro-schema")]
206impl From<avro_schema::error::Error> for PolarsError {
207 fn from(value: avro_schema::error::Error) -> Self {
208 polars_err!(ComputeError: "avro-error: {}", value)
209 }
210}
211
212impl From<simdutf8::basic::Utf8Error> for PolarsError {
213 fn from(value: simdutf8::basic::Utf8Error) -> Self {
214 polars_err!(ComputeError: "invalid utf8: {}", value)
215 }
216}
217#[cfg(feature = "arrow-format")]
218impl From<arrow_format::ipc::planus::Error> for PolarsError {
219 fn from(err: arrow_format::ipc::planus::Error) -> Self {
220 polars_err!(ComputeError: "parquet error: {err:?}")
221 }
222}
223
224impl From<TryReserveError> for PolarsError {
225 fn from(value: TryReserveError) -> Self {
226 polars_err!(ComputeError: "OOM: {}", value)
227 }
228}
229
230impl From<Infallible> for PolarsError {
231 fn from(_: Infallible) -> Self {
232 unreachable!()
233 }
234}
235
236pub type PolarsResult<T> = Result<T, PolarsError>;
237
238impl PolarsError {
239 pub fn context_trace(self) -> Self {
240 use PolarsError::*;
241 if !matches!(self, Context { .. } | ExprContext { .. }) {
242 return self;
243 }
244
245 let mut context_msgs = Vec::new();
246 let mut context_exprs = Vec::new();
247 let mut error = self;
248 loop {
249 match error {
250 Context { error: e, msg } => {
251 context_msgs.push(msg);
252 error = *e;
253 },
254
255 ExprContext { error: e, expr } => {
256 context_exprs.push(expr);
257 error = *e;
258 },
259
260 e => {
261 error = e;
262 break;
263 },
264 }
265 }
266
267 error.wrap_msg(|msg| {
268 let mut out = msg.to_string();
269 if !context_exprs.is_empty() {
270 let first = context_exprs.first().unwrap();
271 let last = context_exprs.last().unwrap();
272 writeln!(
273 &mut out,
274 "\n\nThis error occurred in the following expression:"
275 )
276 .unwrap();
277 writeln!(&mut out, "\t{last}").unwrap();
278 if first.0 != last.0 {
279 writeln!(&mut out, "while evaluating this larger expression:").unwrap();
280 writeln!(&mut out, "\t{first}").unwrap();
281 }
282 }
283
284 if !context_msgs.is_empty() {
285 writeln!(
286 &mut out,
287 "\n\nThis error occurred with the following context stack:"
288 )
289 .unwrap();
290 for (i, m) in context_msgs.into_iter().rev().enumerate() {
291 writeln!(&mut out, "\t[{}] {}", i + 1, m).unwrap();
292 }
293 }
294 out
295 })
296 }
297
298 pub fn wrap_msg<F: FnOnce(&str) -> String>(&self, func: F) -> Self {
299 use PolarsError::*;
300 match self {
301 AssertionError(msg) => AssertionError(func(msg).into()),
302 ColumnNotFound(msg) => ColumnNotFound(func(msg).into()),
303 ComputeError(msg) => ComputeError(func(msg).into()),
304 Duplicate(msg) => Duplicate(func(msg).into()),
305 InvalidOperation(msg) => InvalidOperation(func(msg).into()),
306 IO { error, msg } => {
307 let msg = match msg {
308 Some(msg) => func(msg),
309 None => func(&format!("{error}")),
310 };
311 IO {
312 error: error.clone(),
313 msg: Some(msg.into()),
314 }
315 },
316 NoData(msg) => NoData(func(msg).into()),
317 OutOfBounds(msg) => OutOfBounds(func(msg).into()),
318 SchemaFieldNotFound(msg) => SchemaFieldNotFound(func(msg).into()),
319 SchemaMismatch(msg) => SchemaMismatch(func(msg).into()),
320 ShapeMismatch(msg) => ShapeMismatch(func(msg).into()),
321 StringCacheMismatch(msg) => StringCacheMismatch(func(msg).into()),
322 StructFieldNotFound(msg) => StructFieldNotFound(func(msg).into()),
323 SQLInterface(msg) => SQLInterface(func(msg).into()),
324 SQLSyntax(msg) => SQLSyntax(func(msg).into()),
325 Context {
326 error,
327 msg: context_msg,
328 } => Context {
329 error: Box::new(error.wrap_msg(func)),
330 msg: context_msg.clone(),
331 },
332 ExprContext { error, expr } => ExprContext {
333 error: Box::new(error.wrap_msg(func)),
334 expr: expr.clone(),
335 },
336 #[cfg(feature = "python")]
337 Python { error } => pyo3::Python::attach(|py| {
338 use pyo3::types::{PyAnyMethods, PyStringMethods};
339 use pyo3::{IntoPyObject, PyErr};
340
341 let value = error.value(py);
342
343 let msg = if let Ok(s) = value.str() {
344 func(&s.to_string_lossy())
345 } else {
346 func("<exception str() failed>")
347 };
348
349 let cls = value.get_type();
350
351 let out = PyErr::from_type(cls, (msg,));
352
353 let out = if let Ok(out_with_traceback) = (|| {
354 out.clone_ref(py)
355 .into_pyobject(py)?
356 .getattr("with_traceback")
357 .unwrap()
358 .call1((value.getattr("__traceback__").unwrap(),))
359 })() {
360 PyErr::from_value(out_with_traceback)
361 } else {
362 out
363 };
364
365 Python {
366 error: python::PyErrWrap(out),
367 }
368 }),
369 }
370 }
371
372 pub fn context(self, msg: ErrString) -> Self {
373 PolarsError::Context {
374 error: Box::new(self),
375 msg,
376 }
377 }
378
379 pub fn remove_context(mut self) -> Self {
380 while let Self::Context { error, .. } = self {
381 self = *error;
382 }
383 self
384 }
385
386 pub fn with_expr_context(self, expr: ErrString) -> Self {
387 PolarsError::ExprContext {
388 error: Box::new(self),
389 expr,
390 }
391 }
392}
393
394pub fn map_err<E: Error>(error: E) -> PolarsError {
395 PolarsError::ComputeError(format!("{error}").into())
396}
397
398#[macro_export]
399macro_rules! polars_err {
400 ($variant:ident: format!($_:tt) $(, _:tt)* $(,)?) => {
401 const { panic!("remove unnecessary format! from polars_(bail|err)! macro") }
402 };
403 ($variant:ident: $fmt:literal $(,)?) => {{
404 if const { $crate::__private::has_brace($fmt) } {
405 $crate::__private::must_use(
406 $crate::PolarsError::$variant(format!($fmt).into())
407 )
408 } else {
409 const {
410 $crate::__private::must_use(
411 $crate::PolarsError::$variant($crate::ErrString::new_static($fmt))
412 )
413 }
414 }
415 }};
416 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
417 $crate::__private::must_use(
418 $crate::PolarsError::$variant(format!($fmt, $($arg),*).into())
419 )
420 };
421 ($variant:ident: $fmt:literal $(, $arg:expr)*, hint = $hint:literal) => {
422 $crate::__private::must_use(
423 $crate::PolarsError::$variant(format!(concat_str!($fmt, "\n\nHint: ", $hint), $($arg),*).into())
424 )
425 };
426 ($variant:ident: $err:expr $(,)?) => {
427 $crate::__private::must_use(
428 $crate::PolarsError::$variant($err.into())
429 )
430 };
431 (expr = $expr:expr, $variant:ident: $err:expr $(,)?) => {
432 $crate::__private::must_use(
433 $crate::PolarsError::$variant(
434 format!("{}\n\nError originated in expression: '{:?}'", $err, $expr).into()
435 )
436 )
437 };
438 (expr = $expr:expr, $variant:ident: $fmt:literal, $($arg:tt)+) => {
439 polars_err!(expr = $expr, $variant: format!($fmt, $($arg)+))
440 };
441 (op = $op:expr, got = $arg:expr, expected = $expected:expr) => {
442 $crate::polars_err!(
443 InvalidOperation: "{} operation not supported for dtype `{}` (expected: {})",
444 $op, $arg, $expected
445 )
446 };
447 (opq = $op:ident, got = $arg:expr, expected = $expected:expr) => {
448 $crate::polars_err!(
449 op = concat!("`", stringify!($op), "`"), got = $arg, expected = $expected
450 )
451 };
452 (un_impl = $op:ident) => {
453 $crate::polars_err!(
454 InvalidOperation: "{} operation is not implemented.", concat!("`", stringify!($op), "`")
455 )
456 };
457 (op = $op:expr, $arg:expr) => {
458 $crate::polars_err!(
459 InvalidOperation: "{} operation not supported for dtype `{}`", $op, $arg
460 )
461 };
462 (op = $op:expr, $arg:expr, hint = $hint:literal) => {
463 $crate::polars_err!(
464 InvalidOperation: "{} operation not supported for dtype `{}`\n\nHint: {}", $op, $arg, $hint
465 )
466 };
467 (op = $op:expr, $lhs:expr, $rhs:expr) => {
468 $crate::polars_err!(
469 InvalidOperation: "{} operation not supported for dtypes `{}` and `{}`", $op, $lhs, $rhs
470 )
471 };
472 (op = $op:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {
473 $crate::polars_err!(
474 InvalidOperation: "{} operation not supported for dtypes `{}`, `{}` and `{}`", $op, $arg1, $arg2, $arg3
475 )
476 };
477 (opidx = $op:expr, idx = $idx:expr, $arg:expr) => {
478 $crate::polars_err!(
479 InvalidOperation: "`{}` operation not supported for dtype `{}` as argument {}", $op, $arg, $idx
480 )
481 };
482 (oos = $($tt:tt)+) => {
483 $crate::polars_err!(ComputeError: "out-of-spec: {}", $($tt)+)
484 };
485 (nyi = $($tt:tt)+) => {
486 $crate::polars_err!(ComputeError: "not yet implemented: {}", format!($($tt)+) )
487 };
488 (opq = $op:ident, $arg:expr) => {
489 $crate::polars_err!(op = concat!("`", stringify!($op), "`"), $arg)
490 };
491 (opq = $op:ident, $lhs:expr, $rhs:expr) => {
492 $crate::polars_err!(op = stringify!($op), $lhs, $rhs)
493 };
494 (bigidx, ctx = $ctx:expr, size = $size:expr) => {
495 $crate::polars_err!(ComputeError: "\
496{} produces {} rows which is more than maximum allowed pow(2, 32)-1 rows; \
497consider compiling with bigidx feature (pip install polars[rt64])",
498 $ctx,
499 $size,
500 )
501 };
502 (append) => {
503 polars_err!(SchemaMismatch: "cannot append series, data types don't match")
504 };
505 (extend) => {
506 polars_err!(SchemaMismatch: "cannot extend series, data types don't match")
507 };
508 (unpack) => {
509 polars_err!(SchemaMismatch: "cannot unpack series, data types don't match")
510 };
511 (not_in_enum,value=$value:expr,categories=$categories:expr) =>{
512 polars_err!(ComputeError: "value '{}' is not present in Enum: {:?}",$value,$categories)
513 };
514 (string_cache_mismatch) => {
515 polars_err!(StringCacheMismatch: r#"
516cannot compare categoricals coming from different sources, consider setting a global StringCache.
517
518Help: if you're using Python, this may look something like:
519
520 with pl.StringCache():
521 df1 = pl.DataFrame({'a': ['1', '2']}, schema={'a': pl.Categorical})
522 df2 = pl.DataFrame({'a': ['1', '3']}, schema={'a': pl.Categorical})
523 pl.concat([df1, df2])
524
525Alternatively, if the performance cost is acceptable, you could just set:
526
527 import polars as pl
528 pl.enable_string_cache()
529
530on startup."#.trim_start())
531 };
532 (duplicate = $name:expr) => {
533 $crate::polars_err!(Duplicate: "column with name '{}' has more than one occurrence", $name)
534 };
535 (duplicate_field = $name:expr) => {
536 $crate::polars_err!(Duplicate: "multiple fields with name '{}' found", $name)
537 };
538 (col_not_found = $name:expr) => {
539 $crate::polars_err!(ColumnNotFound: "{:?} not found", $name)
540 };
541 (mismatch, col=$name:expr, expected=$expected:expr, found=$found:expr) => {
542 $crate::polars_err!(
543 SchemaMismatch: "data type mismatch for column {}: expected: {}, found: {}",
544 $name,
545 $expected,
546 $found,
547 )
548 };
549 (oob = $idx:expr, $len:expr) => {
550 polars_err!(OutOfBounds: "index {} is out of bounds for sequence of length {}", $idx, $len)
551 };
552 (agg_len = $agg_len:expr, $groups_len:expr) => {
553 polars_err!(
554 ComputeError:
555 "returned aggregation is of different length: {} than the groups length: {}",
556 $agg_len, $groups_len
557 )
558 };
559 (parse_fmt_idk = $dtype:expr) => {
560 polars_err!(
561 ComputeError: "could not find an appropriate format to parse {}s, please define a format",
562 $dtype,
563 )
564 };
565 (length_mismatch = $operation:expr, $lhs:expr, $rhs:expr) => {
566 $crate::polars_err!(
567 ShapeMismatch: "arguments for `{}` have different lengths ({} != {})",
568 $operation, $lhs, $rhs
569 )
570 };
571 (length_mismatch = $operation:expr, $lhs:expr, $rhs:expr, argument = $argument:expr, argument_idx = $argument_idx:expr) => {
572 $crate::polars_err!(
573 ShapeMismatch: "argument {} called '{}' for `{}` have different lengths ({} != {})",
574 $argument_idx, $argument, $operation, $lhs, $rhs
575 )
576 };
577 (invalid_element_use) => {
578 $crate::polars_err!(InvalidOperation: "`element` is not allowed in this context")
579 };
580 (invalid_field_use) => {
581 $crate::polars_err!(InvalidOperation: "`field` is not allowed in this context")
582 };
583 (non_utf8_path) => {
584 $crate::polars_err!(ComputeError: "encountered non UTF-8 path characters")
585 };
586 (assertion_error = $objects:expr, $detail:expr, $lhs:expr, $rhs:expr) => {
587 $crate::polars_err!(
588 AssertionError: "{} are different ({})\n[left]: {}\n[right]: {}",
589 $objects, $detail, $lhs, $rhs
590 )
591 };
592 (to_datetime_tz_mismatch) => {
593 $crate::polars_err!(
594 ComputeError: "`strptime` / `to_datetime` was called with no format and no time zone, but a time zone is part of the data.\n\nThis was previously allowed but led to unpredictable and erroneous results. Give a format string, set a time zone or perform the operation eagerly on a Series instead of on an Expr."
595 )
596 };
597 (item_agg_count_not_one = $n:expr, allow_empty = $allow_empty:expr) => {
598 if $n == 0 && !$allow_empty {
599 polars_err!(ComputeError:
600 "aggregation 'item' expected a single value, got none"
601 )
602 } else if $n > 100 {
603 if $allow_empty {
604 polars_err!(ComputeError: "aggregation 'item' expected no or a single value, got 100+ values")
605 } else {
606 polars_err!(ComputeError: "aggregation 'item' expected a single value, got 100+ values")
607 }
608 } else if $n > 1 {
609 if $allow_empty {
610 polars_err!(ComputeError:
611 "aggregation 'item' expected no or a single value, got {} values", $n
612 )
613 } else {
614 polars_err!(ComputeError:
615 "aggregation 'item' expected a single value, got {} values", $n
616 )
617 }
618 } else {
619 unreachable!()
620 }
621 };
622}
623
624#[macro_export]
625macro_rules! polars_bail {
626 ($($tt:tt)+) => {
627 return Err($crate::polars_err!($($tt)+))
628 };
629}
630
631#[macro_export]
632macro_rules! polars_ensure {
633 ($cond:expr, $($tt:tt)+) => {
634 if !$cond {
635 $crate::polars_bail!($($tt)+);
636 }
637 };
638}
639
640#[inline]
641#[cold]
642#[must_use]
643pub fn to_compute_err(err: impl Display) -> PolarsError {
644 PolarsError::ComputeError(err.to_string().into())
645}
646
647#[macro_export]
648macro_rules! feature_gated {
649 ($($feature:literal);*, $content:expr) => {{
650 #[cfg(all($(feature = $feature),*))]
651 {
652 $content
653 }
654 #[cfg(not(all($(feature = $feature),*)))]
655 {
656 panic!("activate '{}' feature", concat!($($feature, ", "),*))
657 }
658 }};
659}
660
661#[doc(hidden)]
663pub mod __private {
664 #[doc(hidden)]
665 #[inline]
666 #[cold]
667 #[must_use]
668 pub const fn must_use(error: crate::PolarsError) -> crate::PolarsError {
669 error
670 }
671
672 pub const fn has_brace(s: &str) -> bool {
673 let bytes = s.as_bytes();
674 let mut i: usize = 0;
675
676 while i < bytes.len() {
677 if bytes[i] == b'{' || bytes[i] == b'}' {
678 return true;
679 }
680
681 i += 1;
682 }
683
684 false
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use crate::{ErrString, PolarsError};
691
692 #[test]
693 fn test_polars_error_roundtrips_through_std_io_error() {
694 use PolarsError::ComputeError;
695
696 let error_magic = "err_magic_3";
697 let error = ComputeError(ErrString::new_static(error_magic));
698
699 let io_error: std::io::Error = error.into();
700 let error: PolarsError = io_error.into();
701
702 match error {
703 ComputeError(s) if &*s == error_magic => {},
704 e => panic!("error type mismatch: {e}"),
705 };
706 }
707
708 #[test]
709 fn test_polars_error_format_str() {
710 use PolarsError::ComputeError;
711
712 let a = "A";
713
714 match polars_err!(ComputeError: "{a}") {
715 ComputeError(out) if &*out == a => {},
716 e => panic!("{e}"),
717 }
718
719 match polars_err!(ComputeError: a) {
720 ComputeError(out) if &*out == a => {},
721 e => panic!("{e}"),
722 }
723
724 match polars_err!(ComputeError: "{{") {
725 ComputeError(out) if &*out == "{" => {},
726 e => panic!("{e}"),
727 }
728
729 match polars_err!(ComputeError: "}}") {
730 ComputeError(out) if &*out == "}" => {},
731 e => panic!("{e}"),
732 }
733 }
734}