Files
smart-home-in-rust-with-hom…/python-utils/src/lib.rs
2026-07-15 00:32:04 -04:00

90 lines
3.1 KiB
Rust

use pyo3::{
exceptions::{PyException, PyTypeError},
types::{PyAnyMethods as _, PyStringMethods, PyTypeMethods as _},
Borrowed, Bound, Py, PyAny, PyErr,
};
use snafu::{ResultExt, Snafu};
#[cfg(feature = "macros")]
pub use python_utils_macros::{FromPyFromStr, ToStrToPy};
pub mod from_pyobject_via_parse;
pub mod into_pyobject_via_display;
pub mod none;
pub use from_pyobject_via_parse::{ExtractPyObjectViaParseError, FromPyObjectViaParse};
pub use into_pyobject_via_display::IntoPyObjectViaDisplay;
pub use none::IsNone;
/// Create a GIL-independent reference
pub fn detach<T>(borrowed: Borrowed<'_, '_, T>) -> Py<T> {
let py = borrowed.py();
borrowed.as_unbound().clone_ref(py)
}
/// Create a GIL-independent reference
pub fn detach_bound<T>(bound: &Bound<T>) -> Py<T> {
detach(bound.as_borrowed())
}
#[derive(Debug, Snafu)]
pub enum TypeByNameValidationError {
/// error getting the type name of this object
GetTypeNameError { source: PyErr },
/// error extracting the (successfully retrieved) type name as an [`&str`]
ExtractTypeNameError { source: PyErr },
/// error getting the fully qualified type name of this object
GetFullyQualifiedTypeNameError { source: PyErr },
/// error extracting the (successfully retrieved) fully qualified type name as an [`&str`]
ExtractFullyQualifiedTypeNameError { source: PyErr },
/// expected an instance of {expected} but got an instance of {actual}
UnexpectedType { expected: String, actual: String },
}
impl From<TypeByNameValidationError> for PyErr {
fn from(error: TypeByNameValidationError) -> Self {
match &error {
TypeByNameValidationError::GetTypeNameError { .. } => {
PyException::new_err(error.to_string())
}
TypeByNameValidationError::ExtractTypeNameError { .. } => {
PyException::new_err(error.to_string())
}
TypeByNameValidationError::GetFullyQualifiedTypeNameError { .. } => {
PyException::new_err(error.to_string())
}
TypeByNameValidationError::ExtractFullyQualifiedTypeNameError { .. } => {
PyException::new_err(error.to_string())
}
TypeByNameValidationError::UnexpectedType { .. } => {
PyTypeError::new_err(error.to_string())
}
}
}
}
pub fn validate_type_by_name(
bound: &Bound<PyAny>,
expected_type_name: &str,
) -> Result<(), TypeByNameValidationError> {
let py_type = bound.get_type();
let type_name = py_type.name().context(GetTypeNameSnafu)?;
let type_name = type_name.to_str().context(ExtractTypeNameSnafu)?;
if type_name != expected_type_name {
let fully_qualified_type_name = py_type
.fully_qualified_name()
.context(GetFullyQualifiedTypeNameSnafu)?;
let fully_qualified_type_name = fully_qualified_type_name
.to_str()
.context(ExtractFullyQualifiedTypeNameSnafu)?;
return Err(TypeByNameValidationError::UnexpectedType {
expected: expected_type_name.to_owned(),
actual: fully_qualified_type_name.to_owned(),
});
}
Ok(())
}