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(borrowed: Borrowed<'_, '_, T>) -> Py { let py = borrowed.py(); borrowed.as_unbound().clone_ref(py) } /// Create a GIL-independent reference pub fn detach_bound(bound: &Bound) -> Py { 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 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, 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(()) }