Skip to content

Commit

Permalink
feat[python]: turn invalid datatype panic ot error (#4658)
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchie46 committed Sep 1, 2022
1 parent 467a663 commit 96e897c
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 15 deletions.
14 changes: 10 additions & 4 deletions py-polars/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,12 @@ impl FromPyObject<'_> for Wrap<DataType> {
"List" => DataType::List(Box::new(DataType::Boolean)),
"Null" => DataType::Null,
"Unknown" => DataType::Unknown,
dt => panic!("{} not expected as Python type for dtype conversion", dt),
dt => {
return Err(PyValueError::new_err(format!(
"{} is not a correct polars DataType.",
dt
)))
}
}
}
"Duration" => {
Expand Down Expand Up @@ -352,10 +357,11 @@ impl FromPyObject<'_> for Wrap<DataType> {
DataType::Struct(fields)
}
dt => {
panic!(
"{} not expected in Python dtype to Rust dtype conversion",
return Err(PyValueError::new_err(format!(
"A {} object is not a correct polars DataType.\
Hint: use the class without instantiating it.",
dt
)
)))
}
};
Ok(Wrap(dtype))
Expand Down
25 changes: 15 additions & 10 deletions py-polars/src/lazy/dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use polars::prelude::*;
use polars::series::ops::NullBehavior;
use polars_core::prelude::QuantileInterpolOptions;
use pyo3::class::basic::CompareOp;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyBool, PyBytes, PyFloat, PyInt, PyString};

Expand Down Expand Up @@ -1667,34 +1668,38 @@ pub fn fold(acc: PyExpr, lambda: PyObject, exprs: Vec<PyExpr>) -> PyExpr {
polars::lazy::dsl::fold_exprs(acc.inner, func, exprs).into()
}

pub fn lit(value: &PyAny) -> PyExpr {
pub fn lit(value: &PyAny) -> PyResult<PyExpr> {
if let Ok(true) = value.is_instance_of::<PyBool>() {
let val = value.extract::<bool>().unwrap();
dsl::lit(val).into()
Ok(dsl::lit(val).into())
} else if let Ok(int) = value.downcast::<PyInt>() {
let val = int.extract::<i64>().unwrap();

if val > 0 && val < i32::MAX as i64 || val < 0 && val > i32::MIN as i64 {
dsl::lit(val as i32).into()
Ok(dsl::lit(val as i32).into())
} else {
dsl::lit(val).into()
Ok(dsl::lit(val).into())
}
} else if let Ok(float) = value.downcast::<PyFloat>() {
let val = float.extract::<f64>().unwrap();
dsl::lit(val).into()
Ok(dsl::lit(val).into())
} else if let Ok(pystr) = value.downcast::<PyString>() {
dsl::lit(
Ok(dsl::lit(
pystr
.to_str()
.expect("could not transform Python string to Rust Unicode"),
)
.into()
.into())
} else if let Ok(series) = value.extract::<PySeries>() {
dsl::lit(series.series).into()
Ok(dsl::lit(series.series).into())
} else if value.is_none() {
dsl::lit(Null {}).into()
Ok(dsl::lit(Null {}).into())
} else {
panic!("could not convert value {:?} as a Literal", value)
let value = value.str()?;
Err(PyValueError::new_err(format!(
"could not convert value {:?} as a Literal",
value
)))
}
}

Expand Down
2 changes: 1 addition & 1 deletion py-polars/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn dtype_str_repr(dtype: Wrap<DataType>) -> PyResult<String> {
}

#[pyfunction]
fn lit(value: &PyAny) -> dsl::PyExpr {
fn lit(value: &PyAny) -> PyResult<dsl::PyExpr> {
dsl::lit(value)
}

Expand Down
8 changes: 8 additions & 0 deletions py-polars/tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io
import typing
from datetime import date

import numpy as np
import pytest
Expand Down Expand Up @@ -147,3 +148,10 @@ def test_getitem_errs() -> None:
r"'foo' of type: '<class 'str'>'",
):
df[{"some"}] = "foo"


def test_err_bubbling_up_to_lit() -> None:
df = pl.DataFrame({"date": [date(2020, 1, 1)], "value": [42]})

with pytest.raises(ValueError):
df.filter(pl.col("date") == pl.Date("2020-01-01"))

0 comments on commit 96e897c

Please sign in to comment.