Skip to content

Commit dc86e77

Browse files
committed
refactor: improve error handling and type extraction in python_value_to_scalar_value function
1 parent bf9d7da commit dc86e77

1 file changed

Lines changed: 75 additions & 28 deletions

File tree

src/dataframe.rs

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -886,43 +886,90 @@ async fn collect_record_batches_to_display(
886886
/// Convert a Python value to a DataFusion ScalarValue
887887
fn python_value_to_scalar_value(value: &PyObject, py: Python) -> PyDataFusionResult<ScalarValue> {
888888
if value.is_none(py) {
889-
return Err(PyDataFusionError::Common(
890-
"Cannot use None as fill value".to_string(),
891-
));
892-
} else if let Ok(val) = value.extract::<i64>(py) {
889+
let msg = "Cannot use None as fill value";
890+
return Err(PyDataFusionError::Common(msg.to_string()));
891+
}
892+
893+
// Integer types - try different sizes
894+
if let Ok(val) = value.extract::<i64>(py) {
893895
return Ok(ScalarValue::Int64(Some(val)));
894-
} else if let Ok(val) = value.extract::<f64>(py) {
896+
} else if let Ok(val) = value.extract::<i32>(py) {
897+
return Ok(ScalarValue::Int32(Some(val)));
898+
} else if let Ok(val) = value.extract::<i16>(py) {
899+
return Ok(ScalarValue::Int16(Some(val)));
900+
} else if let Ok(val) = value.extract::<i8>(py) {
901+
return Ok(ScalarValue::Int8(Some(val)));
902+
}
903+
904+
// Unsigned integer types
905+
if let Ok(val) = value.extract::<u64>(py) {
906+
return Ok(ScalarValue::UInt64(Some(val)));
907+
} else if let Ok(val) = value.extract::<u32>(py) {
908+
return Ok(ScalarValue::UInt32(Some(val)));
909+
} else if let Ok(val) = value.extract::<u16>(py) {
910+
return Ok(ScalarValue::UInt16(Some(val)));
911+
} else if let Ok(val) = value.extract::<u8>(py) {
912+
return Ok(ScalarValue::UInt8(Some(val)));
913+
}
914+
915+
// Float types
916+
if let Ok(val) = value.extract::<f64>(py) {
895917
return Ok(ScalarValue::Float64(Some(val)));
896-
} else if let Ok(val) = value.extract::<bool>(py) {
918+
} else if let Ok(val) = value.extract::<f32>(py) {
919+
return Ok(ScalarValue::Float32(Some(val)));
920+
}
921+
922+
// Boolean
923+
if let Ok(val) = value.extract::<bool>(py) {
897924
return Ok(ScalarValue::Boolean(Some(val)));
898-
} else if let Ok(val) = value.extract::<String>(py) {
925+
}
926+
927+
// String types
928+
if let Ok(val) = value.extract::<String>(py) {
899929
return Ok(ScalarValue::Utf8(Some(val)));
900-
} else if let Ok(dt) = py
901-
.import("datetime")
902-
.and_then(|m| m.getattr("datetime"))
903-
.and_then(|dt| value.is_instance(dt))
904-
{
905-
if value.is_instance_of::<pyo3::types::PyDateTime>(py) {
906-
let naive_dt = value.extract::<chrono::NaiveDateTime>(py)?;
907-
return Ok(ScalarValue::TimestampNanosecond(
908-
Some(naive_dt.timestamp_nanos()),
909-
None,
910-
));
911-
} else {
912-
return Err(PyDataFusionError::Common(
913-
"Unsupported datetime type".to_string(),
914-
));
930+
}
931+
932+
// Handle datetime types
933+
let datetime_result = py.import("datetime").and_then(|m| m.getattr("datetime"));
934+
935+
if let Ok(datetime_cls) = datetime_result {
936+
if let Ok(true) = value.is_instance(datetime_cls) {
937+
if value.is_instance_of::<pyo3::types::PyDateTime>(py) {
938+
if let Ok(naive_dt) = value.extract::<chrono::NaiveDateTime>(py) {
939+
return Ok(ScalarValue::TimestampNanosecond(
940+
Some(naive_dt.timestamp_nanos()),
941+
None,
942+
));
943+
}
944+
}
945+
// Check for date (not datetime)
946+
let date_result = py.import("datetime").and_then(|m| m.getattr("date"));
947+
if let Ok(date_cls) = date_result {
948+
if let Ok(true) = value.is_instance(date_cls) {
949+
if let Ok(naive_date) = value.extract::<chrono::NaiveDate>(py) {
950+
return Ok(ScalarValue::Date32(Some(
951+
naive_date.num_days_from_ce() - 719163, // Convert from CE to Unix epoch
952+
)));
953+
}
954+
}
955+
}
956+
let msg = "Unsupported datetime type format";
957+
return Err(PyDataFusionError::Common(msg.to_string()));
915958
}
916959
}
917960

918961
// Try to convert to string as fallback
919962
match value.str(py) {
920-
Ok(py_str) => {
921-
let s = py_str.to_string()?;
922-
Ok(ScalarValue::Utf8(Some(s)))
963+
Ok(py_str) => match py_str.to_string() {
964+
Ok(s) => Ok(ScalarValue::Utf8(Some(s))),
965+
Err(_) => {
966+
let msg = "Failed to convert Python object to string";
967+
Err(PyDataFusionError::Common(msg.to_string()))
968+
}
969+
},
970+
Err(_) => {
971+
let msg = "Unsupported Python type for fill_null";
972+
Err(PyDataFusionError::Common(msg.to_string()))
923973
}
924-
Err(_) => Err(PyDataFusionError::Common(
925-
"Unsupported Python type for fill_null".to_string(),
926-
)),
927974
}
928975
}

0 commit comments

Comments
 (0)