proper error handling
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
py_any_extras::PyAnyExtras,
|
||||
};
|
||||
use prost_reflect::{
|
||||
prost_types::{
|
||||
field_descriptor_proto::Type, DescriptorProto, FieldDescriptorProto, FileDescriptorProto,
|
||||
},
|
||||
DescriptorPool, MessageDescriptor,
|
||||
};
|
||||
use pyo3::{exceptions::PyRuntimeError, PyAny, PyResult};
|
||||
use pyo3::PyAny;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::py_any_extras::PyAnyExtras;
|
||||
|
||||
pub fn create_cached_descriptor(obj: &PyAny) -> PyResult<MessageDescriptor> {
|
||||
pub fn create_cached_descriptor(obj: &PyAny) -> Result<MessageDescriptor> {
|
||||
static DESCRIPTOR_POOL: OnceLock<Mutex<DescriptorPool>> = OnceLock::new();
|
||||
let mut pool = DESCRIPTOR_POOL
|
||||
.get_or_init(|| Mutex::new(DescriptorPool::new()))
|
||||
@@ -22,7 +24,7 @@ pub fn create_cached_descriptor(obj: &PyAny) -> PyResult<MessageDescriptor> {
|
||||
fn create_cached_descriptor_in_pool<'py>(
|
||||
obj: &'py PyAny,
|
||||
pool: &mut DescriptorPool,
|
||||
) -> PyResult<MessageDescriptor> {
|
||||
) -> Result<MessageDescriptor> {
|
||||
let name = obj.qualified_class_name()?;
|
||||
if let Some(desc) = pool.get_message_by_name(&name) {
|
||||
return Ok(desc);
|
||||
@@ -69,13 +71,12 @@ fn create_cached_descriptor_in_pool<'py>(
|
||||
|
||||
file.message_type.push(message);
|
||||
|
||||
pool.add_file_descriptor_proto(file)
|
||||
.map_err(|_| PyRuntimeError::new_err("Error on proto registration"))?;
|
||||
pool.add_file_descriptor_proto(file)?;
|
||||
|
||||
Ok(pool.get_message_by_name(&name).expect("Just registered..."))
|
||||
}
|
||||
|
||||
fn map_type(str: &str) -> PyResult<Type> {
|
||||
fn map_type(str: &str) -> Result<Type> {
|
||||
match str {
|
||||
"enum" => Ok(Type::Enum),
|
||||
"bool" => Ok(Type::Bool),
|
||||
@@ -94,6 +95,6 @@ fn map_type(str: &str) -> PyResult<Type> {
|
||||
"string" => Ok(Type::String),
|
||||
"bytes" => Ok(Type::Bytes),
|
||||
"message" => Ok(Type::Message),
|
||||
_ => Err(PyRuntimeError::new_err("Unsupported type")),
|
||||
_ => Err(Error::UnsupportedType(str.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
23
betterproto-extras/src/error.rs
Normal file
23
betterproto-extras/src/error.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use prost_reflect::{prost::DecodeError, DescriptorError};
|
||||
use pyo3::{exceptions::PyRuntimeError, PyErr};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Given object is not a valid betterproto message.")]
|
||||
NoBetterprotoMessage(#[from] PyErr),
|
||||
#[error("Unsupported type `{0}`.")]
|
||||
UnsupportedType(String),
|
||||
#[error("Error on proto registration")]
|
||||
FailedToRegisterDescriptor(#[from] DescriptorError),
|
||||
#[error("The given binary data does not match the protobuf schema.")]
|
||||
FailedToDecode(#[from] DecodeError),
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
impl From<Error> for PyErr {
|
||||
fn from(value: Error) -> Self {
|
||||
PyRuntimeError::new_err(value.to_string())
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
mod descriptor_pool;
|
||||
mod error;
|
||||
mod merging;
|
||||
mod py_any_extras;
|
||||
|
||||
use descriptor_pool::create_cached_descriptor;
|
||||
use error::Result;
|
||||
use merging::merge_msg_into_pyobj;
|
||||
use prost_reflect::DynamicMessage;
|
||||
use pyo3::{exceptions::PyRuntimeError, prelude::*};
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn deserialize(obj: &PyAny, buf: &[u8]) -> PyResult<()> {
|
||||
fn deserialize(obj: &PyAny, buf: &[u8]) -> Result<()> {
|
||||
let desc = create_cached_descriptor(obj)?;
|
||||
let msg = DynamicMessage::decode(desc, buf)
|
||||
.map_err(|_| PyRuntimeError::new_err("Error on deserializing."))?;
|
||||
let msg = DynamicMessage::decode(desc, buf)?;
|
||||
merge_msg_into_pyobj(obj, &msg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
py_any_extras::PyAnyExtras,
|
||||
};
|
||||
use prost_reflect::{DynamicMessage, ReflectMessage, Value};
|
||||
use pyo3::{exceptions::PyRuntimeError, IntoPy, PyAny, PyResult};
|
||||
use pyo3::{IntoPy, PyAny};
|
||||
|
||||
use crate::py_any_extras::PyAnyExtras;
|
||||
|
||||
pub fn merge_msg_into_pyobj(obj: &PyAny, msg: &DynamicMessage) -> PyResult<()> {
|
||||
pub fn merge_msg_into_pyobj(obj: &PyAny, msg: &DynamicMessage) -> Result<()> {
|
||||
for field in msg.descriptor().fields() {
|
||||
let field_name = field.name();
|
||||
let proto_meta = obj.get_proto_meta()?;
|
||||
@@ -21,7 +23,7 @@ fn map_field_value<'py>(
|
||||
field_name: &str,
|
||||
field_value: &Value,
|
||||
proto_meta: &'py PyAny,
|
||||
) -> PyResult<&'py PyAny> {
|
||||
) -> Result<&'py PyAny> {
|
||||
let py = proto_meta.py();
|
||||
match field_value {
|
||||
Value::Bool(x) => Ok(x.into_py(py).into_ref(py)),
|
||||
@@ -38,6 +40,6 @@ fn map_field_value<'py>(
|
||||
merge_msg_into_pyobj(obj, msg)?;
|
||||
Ok(obj)
|
||||
}
|
||||
_ => Err(PyRuntimeError::new_err("Unsupported type")),
|
||||
value => Err(Error::UnsupportedType(value.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
use pyo3::{PyAny, PyResult};
|
||||
use crate::error::Result;
|
||||
use pyo3::PyAny;
|
||||
|
||||
pub trait PyAnyExtras<'py> {
|
||||
fn qualified_class_name(&self) -> PyResult<String>;
|
||||
fn get_proto_meta(&self) -> PyResult<&'py PyAny>;
|
||||
fn create_instance(&self, field_name: &str) -> PyResult<&'py PyAny>;
|
||||
fn qualified_class_name(&self) -> Result<String>;
|
||||
fn get_proto_meta(&self) -> Result<&'py PyAny>;
|
||||
fn create_instance(&self, field_name: &str) -> Result<&'py PyAny>;
|
||||
}
|
||||
|
||||
impl<'py> PyAnyExtras<'py> for &'py PyAny {
|
||||
fn qualified_class_name(&self) -> PyResult<String> {
|
||||
fn qualified_class_name(&self) -> Result<String> {
|
||||
let class = self.getattr("__class__")?;
|
||||
let module = class.getattr("__module__")?;
|
||||
let name = class.getattr("__name__")?;
|
||||
Ok(format!("{module}.{name}"))
|
||||
}
|
||||
|
||||
fn get_proto_meta(&self) -> PyResult<&'py PyAny> {
|
||||
self.getattr("_betterproto")
|
||||
fn get_proto_meta(&self) -> Result<&'py PyAny> {
|
||||
Ok(self.getattr("_betterproto")?)
|
||||
}
|
||||
|
||||
fn create_instance(&self, field_name: &str) -> PyResult<&'py PyAny> {
|
||||
let res = self.getattr("cls_by_field")?
|
||||
fn create_instance(&self, field_name: &str) -> Result<&'py PyAny> {
|
||||
let res = self
|
||||
.getattr("cls_by_field")?
|
||||
.get_item(field_name)?
|
||||
.call0()?;
|
||||
Ok(res)
|
||||
|
||||
Reference in New Issue
Block a user