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 abort;
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 trait PolarsContext<T> {
395 fn context(self, ctx: &'static str) -> PolarsResult<T>;
396
397 fn with_context<F>(self, f: F) -> PolarsResult<T>
398 where
399 F: FnOnce() -> String;
400}
401
402impl<T> PolarsContext<T> for PolarsResult<T> {
403 fn context(self, ctx: &'static str) -> PolarsResult<T> {
404 self.map_err(|e| e.context(ErrString::new_static(ctx)))
405 }
406
407 fn with_context<F>(self, f: F) -> PolarsResult<T>
408 where
409 F: FnOnce() -> String,
410 {
411 self.map_err(|e| e.context(f().into()))
412 }
413}
414
415pub fn map_err<E: Error>(error: E) -> PolarsError {
416 PolarsError::ComputeError(format!("{error}").into())
417}
418
419#[macro_export]
420macro_rules! polars_err {
421 ($variant:ident: format!($_:tt) $(, _:tt)* $(,)?) => {
422 const { panic!("remove unnecessary format! from polars_(bail|err)! macro") }
423 };
424 ($variant:ident: $fmt:literal $(,)?) => {{
425 if const { $crate::__private::has_brace($fmt) } {
426 $crate::__private::must_use(
427 $crate::PolarsError::$variant(format!($fmt).into())
428 )
429 } else {
430 const {
431 $crate::__private::must_use(
432 $crate::PolarsError::$variant($crate::ErrString::new_static($fmt))
433 )
434 }
435 }
436 }};
437 ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
438 $crate::__private::must_use(
439 $crate::PolarsError::$variant(format!($fmt, $($arg),*).into())
440 )
441 };
442 ($variant:ident: $fmt:literal $(, $arg:expr)*, hint = $hint:literal) => {
443 $crate::__private::must_use(
444 $crate::PolarsError::$variant(format!(concat_str!($fmt, "\n\nHint: ", $hint), $($arg),*).into())
445 )
446 };
447 ($variant:ident: $err:expr $(,)?) => {
448 $crate::__private::must_use(
449 $crate::PolarsError::$variant($err.into())
450 )
451 };
452 (expr = $expr:expr, $variant:ident: $err:expr $(,)?) => {
453 $crate::__private::must_use(
454 $crate::PolarsError::$variant(
455 format!("{}\n\nError originated in expression: '{:?}'", $err, $expr).into()
456 )
457 )
458 };
459 (expr = $expr:expr, $variant:ident: $fmt:literal, $($arg:tt)+) => {
460 polars_err!(expr = $expr, $variant: format!($fmt, $($arg)+))
461 };
462 (op = $op:expr, got = $arg:expr, expected = $expected:expr) => {
463 $crate::polars_err!(
464 InvalidOperation: "{} operation not supported for dtype `{}` (expected: {})",
465 $op, $arg, $expected
466 )
467 };
468 (opq = $op:ident, got = $arg:expr, expected = $expected:expr) => {
469 $crate::polars_err!(
470 op = concat!("`", stringify!($op), "`"), got = $arg, expected = $expected
471 )
472 };
473 (un_impl = $op:ident) => {
474 $crate::polars_err!(
475 InvalidOperation: "{} operation is not implemented.", concat!("`", stringify!($op), "`")
476 )
477 };
478 (op = $op:expr, $arg:expr) => {
479 $crate::polars_err!(
480 InvalidOperation: "{} operation not supported for dtype `{}`", $op, $arg
481 )
482 };
483 (op = $op:expr, $arg:expr, hint = $hint:literal) => {
484 $crate::polars_err!(
485 InvalidOperation: "{} operation not supported for dtype `{}`\n\nHint: {}", $op, $arg, $hint
486 )
487 };
488 (op = $op:expr, $lhs:expr, $rhs:expr) => {
489 $crate::polars_err!(
490 InvalidOperation: "{} operation not supported for dtypes `{}` and `{}`", $op, $lhs, $rhs
491 )
492 };
493 (op = $op:expr, $arg1:expr, $arg2:expr, $arg3:expr) => {
494 $crate::polars_err!(
495 InvalidOperation: "{} operation not supported for dtypes `{}`, `{}` and `{}`", $op, $arg1, $arg2, $arg3
496 )
497 };
498 (opidx = $op:expr, idx = $idx:expr, $arg:expr) => {
499 $crate::polars_err!(
500 InvalidOperation: "`{}` operation not supported for dtype `{}` as argument {}", $op, $arg, $idx
501 )
502 };
503 (oos = $($tt:tt)+) => {
504 $crate::polars_err!(ComputeError: "out-of-spec: {}", $($tt)+)
505 };
506 (nyi = $($tt:tt)+) => {
507 $crate::polars_err!(ComputeError: "not yet implemented: {}", format!($($tt)+) )
508 };
509 (opq = $op:ident, $arg:expr) => {
510 $crate::polars_err!(op = concat!("`", stringify!($op), "`"), $arg)
511 };
512 (opq = $op:ident, $lhs:expr, $rhs:expr) => {
513 $crate::polars_err!(op = stringify!($op), $lhs, $rhs)
514 };
515 (bigidx, ctx = $ctx:expr, size = $size:expr) => {
516 $crate::polars_err!(ComputeError: "\
517{} produces {} rows which is more than maximum allowed pow(2, 32)-1 rows; \
518consider compiling with bigidx feature (pip install polars[rt64])",
519 $ctx,
520 $size,
521 )
522 };
523 (append) => {
524 polars_err!(SchemaMismatch: "cannot append series, data types don't match")
525 };
526 (extend) => {
527 polars_err!(SchemaMismatch: "cannot extend series, data types don't match")
528 };
529 (unpack) => {
530 polars_err!(SchemaMismatch: "cannot unpack series, data types don't match")
531 };
532 (not_in_enum,value=$value:expr,categories=$categories:expr) =>{
533 polars_err!(ComputeError: "value '{}' is not present in Enum: {:?}",$value,$categories)
534 };
535 (string_cache_mismatch) => {
536 polars_err!(StringCacheMismatch: r#"
537cannot compare categoricals coming from different sources, consider setting a global StringCache.
538
539Help: if you're using Python, this may look something like:
540
541 with pl.StringCache():
542 df1 = pl.DataFrame({'a': ['1', '2']}, schema={'a': pl.Categorical})
543 df2 = pl.DataFrame({'a': ['1', '3']}, schema={'a': pl.Categorical})
544 pl.concat([df1, df2])
545
546Alternatively, if the performance cost is acceptable, you could just set:
547
548 import polars as pl
549 pl.enable_string_cache()
550
551on startup."#.trim_start())
552 };
553 (duplicate = $name:expr) => {
554 $crate::polars_err!(Duplicate: "column with name '{}' has more than one occurrence", $name)
555 };
556 (duplicate_field = $name:expr) => {
557 $crate::polars_err!(Duplicate: "multiple fields with name '{}' found", $name)
558 };
559 (col_not_found = $name:expr) => {
560 $crate::polars_err!(ColumnNotFound: "{:?} not found", $name)
561 };
562 (mismatch, col=$name:expr, expected=$expected:expr, found=$found:expr) => {
563 $crate::polars_err!(
564 SchemaMismatch: "data type mismatch for column {}: expected: {}, found: {}",
565 $name,
566 $expected,
567 $found,
568 )
569 };
570 (oob = $idx:expr, $len:expr) => {
571 polars_err!(OutOfBounds: "index {} is out of bounds for sequence of length {}", $idx, $len)
572 };
573 (agg_len = $agg_len:expr, $groups_len:expr) => {
574 polars_err!(
575 ComputeError:
576 "returned aggregation is of different length: {} than the groups length: {}",
577 $agg_len, $groups_len
578 )
579 };
580 (parse_fmt_idk = $dtype:expr) => {
581 polars_err!(
582 ComputeError: "could not find an appropriate format to parse {}s, please define a format",
583 $dtype,
584 )
585 };
586 (length_mismatch = $operation:expr, $lhs:expr, $rhs:expr) => {
587 $crate::polars_err!(
588 ShapeMismatch: "arguments for `{}` have different lengths ({} != {})",
589 $operation, $lhs, $rhs
590 )
591 };
592 (length_mismatch = $operation:expr, $lhs:expr, $rhs:expr, argument = $argument:expr, argument_idx = $argument_idx:expr) => {
593 $crate::polars_err!(
594 ShapeMismatch: "argument {} called '{}' for `{}` have different lengths ({} != {})",
595 $argument_idx, $argument, $operation, $lhs, $rhs
596 )
597 };
598 (invalid_element_use) => {
599 $crate::polars_err!(InvalidOperation: "`element` is not allowed in this context")
600 };
601 (invalid_field_use) => {
602 $crate::polars_err!(InvalidOperation: "`field` is not allowed in this context")
603 };
604 (non_utf8_path) => {
605 $crate::polars_err!(ComputeError: "encountered non UTF-8 path characters")
606 };
607 (assertion_error = $objects:expr, $detail:expr, $lhs:expr, $rhs:expr) => {
608 $crate::polars_err!(
609 AssertionError: "{} are different ({})\n[left]: {}\n[right]: {}",
610 $objects, $detail, $lhs, $rhs
611 )
612 };
613 (to_datetime_tz_mismatch) => {
614 $crate::polars_err!(
615 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."
616 )
617 };
618 (item_agg_count_not_one = $n:expr, allow_empty = $allow_empty:expr) => {
619 if $n == 0 && !$allow_empty {
620 polars_err!(ComputeError:
621 "aggregation 'item' expected a single value, got none"
622 )
623 } else if $n > 100 {
624 if $allow_empty {
625 polars_err!(ComputeError: "aggregation 'item' expected no or a single value, got 100+ values")
626 } else {
627 polars_err!(ComputeError: "aggregation 'item' expected a single value, got 100+ values")
628 }
629 } else if $n > 1 {
630 if $allow_empty {
631 polars_err!(ComputeError:
632 "aggregation 'item' expected no or a single value, got {} values", $n
633 )
634 } else {
635 polars_err!(ComputeError:
636 "aggregation 'item' expected a single value, got {} values", $n
637 )
638 }
639 } else {
640 unreachable!()
641 }
642 };
643}
644
645#[macro_export]
646macro_rules! polars_bail {
647 ($($tt:tt)+) => {
648 return Err($crate::polars_err!($($tt)+))
649 };
650}
651
652#[macro_export]
653macro_rules! polars_ensure {
654 ($cond:expr, $($tt:tt)+) => {
655 if !$cond {
656 $crate::polars_bail!($($tt)+);
657 }
658 };
659}
660
661#[inline]
662#[cold]
663#[must_use]
664pub fn to_compute_err(err: impl Display) -> PolarsError {
665 PolarsError::ComputeError(err.to_string().into())
666}
667
668#[macro_export]
669macro_rules! feature_gated {
670 ($($feature:literal);*, $content:expr) => {{
671 #[cfg(all($(feature = $feature),*))]
672 {
673 $content
674 }
675 #[cfg(not(all($(feature = $feature),*)))]
676 {
677 panic!("activate '{}' feature", concat!($($feature, ", "),*))
678 }
679 }};
680}
681
682#[doc(hidden)]
684pub mod __private {
685 #[doc(hidden)]
686 #[inline]
687 #[cold]
688 #[must_use]
689 pub const fn must_use(error: crate::PolarsError) -> crate::PolarsError {
690 error
691 }
692
693 pub const fn has_brace(s: &str) -> bool {
694 let bytes = s.as_bytes();
695 let mut i: usize = 0;
696
697 while i < bytes.len() {
698 if bytes[i] == b'{' || bytes[i] == b'}' {
699 return true;
700 }
701
702 i += 1;
703 }
704
705 false
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 use crate::{ErrString, PolarsError};
712
713 #[test]
714 fn test_polars_error_roundtrips_through_std_io_error() {
715 use PolarsError::ComputeError;
716
717 let error_magic = "err_magic_3";
718 let error = ComputeError(ErrString::new_static(error_magic));
719
720 let io_error: std::io::Error = error.into();
721 let error: PolarsError = io_error.into();
722
723 match error {
724 ComputeError(s) if &*s == error_magic => {},
725 e => panic!("error type mismatch: {e}"),
726 };
727 }
728
729 #[test]
730 fn test_polars_error_format_str() {
731 use PolarsError::ComputeError;
732
733 let a = "A";
734
735 match polars_err!(ComputeError: "{a}") {
736 ComputeError(out) if &*out == a => {},
737 e => panic!("{e}"),
738 }
739
740 match polars_err!(ComputeError: a) {
741 ComputeError(out) if &*out == a => {},
742 e => panic!("{e}"),
743 }
744
745 match polars_err!(ComputeError: "{{") {
746 ComputeError(out) if &*out == "{" => {},
747 e => panic!("{e}"),
748 }
749
750 match polars_err!(ComputeError: "}}") {
751 ComputeError(out) if &*out == "}" => {},
752 e => panic!("{e}"),
753 }
754 }
755}