Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error details from python errors PydanticValueError #185

Merged
merged 7 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
rust class working
  • Loading branch information
samuelcolvin committed Jul 21, 2022
commit 1cb3b5b65ae05d310da68cd182711859cc7a3499
4 changes: 2 additions & 2 deletions pydantic_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._pydantic_core import SchemaError, SchemaValidator, ValidationError, __version__
from ._pydantic_core import PydanticValueError, SchemaError, SchemaValidator, ValidationError, __version__
from ._types import Config, Schema

__all__ = '__version__', 'Config', 'Schema', 'SchemaError', 'SchemaValidator', 'ValidationError'
__all__ = '__version__', 'Config', 'Schema', 'SchemaValidator', 'SchemaError', 'ValidationError', 'PydanticValueError'
10 changes: 5 additions & 5 deletions pydantic_core/_pydantic_core.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from typing import Any, Dict, List, Optional, Union, TypedDict
from typing import Any, Dict, List, Optional, TypedDict, Union

from pydantic_core._types import Config, Schema

Expand All @@ -8,7 +8,7 @@ if sys.version_info < (3, 11):
else:
from typing import NotRequired

__all__ = '__version__', 'SchemaValidator', 'SchemaError', 'ValidationError'
__all__ = '__version__', 'SchemaValidator', 'SchemaError', 'ValidationError', 'PydanticValueError'
__version__: str

class SchemaValidator:
Expand Down Expand Up @@ -39,7 +39,7 @@ class ValidationError(ValueError):
def error_count(self) -> int: ...
def errors(self) -> List[ErrorDetails]: ...


class PydanticValueError(ValueError):
def __init__(self, kind: str, message_template: str, context: Optional[Dict[str, Union[str, int]]] = None) -> None:
...
def __init__(
self, kind: str, message_template: str, context: Optional[Dict[str, Union[str, int]]] = None
) -> None: ...
11 changes: 9 additions & 2 deletions src/errors/kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ macro_rules! py_dict {
}

impl ErrorKind {
pub fn kind(&self) -> String {
match self {
Self::CustomError { value_error } => value_error.get_kind(),
_ => self.to_string(),
}
}

pub fn render(&self, py: Python) -> String {
match self {
Self::InvalidJson { error } => render!(self, error),
Expand Down Expand Up @@ -309,7 +316,7 @@ impl ErrorKind {
}
}

pub fn py_dict(&self, py: Python) -> PyResult<Option<PyObject>> {
pub fn py_dict(&self, py: Python) -> PyResult<Option<Py<PyDict>>> {
match self {
Self::InvalidJson { error } => py_dict!(py, error),
Self::GetAttributeError { error } => py_dict!(py, error),
Expand Down Expand Up @@ -343,7 +350,7 @@ impl ErrorKind {
Self::BytesTooLong { max_length } => py_dict!(py, max_length),
Self::ValueError { error } => py_dict!(py, error),
Self::AssertionError { error } => py_dict!(py, error),
// Self::CustomError { value_error } => Ok(value_error.context),
Self::CustomError { value_error } => Ok(value_error.get_context(py)),
Self::LiteralSingleError { expected } => py_dict!(py, expected),
Self::LiteralMultipleError { expected } => py_dict!(py, expected),
Self::DateParsing { error } => py_dict!(py, error),
Expand Down
9 changes: 3 additions & 6 deletions src/errors/validation_exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,7 @@ macro_rules! truncate_input_value {
};
}

pub fn pretty_py_line_errors<'a>(
py: Python,
line_errors_iter: impl Iterator<Item = &'a PyLineError>,
) -> String {
pub fn pretty_py_line_errors<'a>(py: Python, line_errors_iter: impl Iterator<Item = &'a PyLineError>) -> String {
line_errors_iter
.map(|i| i.pretty(py))
.collect::<Result<Vec<_>, _>>()
Expand Down Expand Up @@ -144,7 +141,7 @@ impl<'a> From<PyLineError> for ValLineError<'a> {
impl PyLineError {
pub fn as_dict(&self, py: Python) -> PyResult<PyObject> {
let dict = PyDict::new(py);
dict.set_item("kind", self.kind.to_string())?;
dict.set_item("kind", self.kind.kind())?;
dict.set_item("loc", self.location.to_object(py))?;
dict.set_item("message", self.kind.render(py))?;
dict.set_item("input_value", &self.input_value)?;
Expand All @@ -158,7 +155,7 @@ impl PyLineError {
let mut output = String::with_capacity(200);
write!(output, "{}", self.location)?;

write!(output, " {} [kind={}", self.kind.render(py), self.kind)?;
write!(output, " {} [kind={}", self.kind.render(py), self.kind.kind())?;

let input_value = self.input_value.as_ref(py);
let input_str = match repr_string(input_value) {
Expand Down
12 changes: 10 additions & 2 deletions src/errors/value_exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use pyo3::types::{PyDict, PyString};

use crate::input::Input;

use super::{ValError, ErrorKind};
use super::{ErrorKind, ValError};

#[pyclass(extends=PyValueError, module="pydantic_core._pydantic_core")]
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -55,7 +55,15 @@ impl PydanticValueError {

impl PydanticValueError {
pub fn into_val_error<'a>(self, input: &'a impl Input<'a>) -> ValError<'a> {
let kind = ErrorKind::CustomError {value_error: self};
let kind = ErrorKind::CustomError { value_error: self };
ValError::new(kind, input)
}

pub fn get_kind(&self) -> String {
self.kind.clone()
}

pub fn get_context(&self, py: Python) -> Option<Py<PyDict>> {
self.context.as_ref().map(|c| c.clone_ref(py))
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod validators;

// required for benchmarks
pub use build_tools::SchemaError;
pub use errors::{ValidationError, PydanticValueError};
pub use errors::{PydanticValueError, ValidationError};
pub use validators::SchemaValidator;

pub fn get_version() -> String {
Expand Down
2 changes: 1 addition & 1 deletion src/validators/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use pyo3::prelude::*;
use pyo3::types::{PyAny, PyDict};

use crate::build_tools::{py_error, SchemaDict};
use crate::errors::{ErrorKind, ValError, ValResult, ValidationError, PydanticValueError};
use crate::errors::{ErrorKind, PydanticValueError, ValError, ValResult, ValidationError};
use crate::input::Input;
use crate::recursion_guard::RecursionGuard;

Expand Down
22 changes: 21 additions & 1 deletion tests/validators/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import pytest

from pydantic_core import SchemaError, SchemaValidator, ValidationError
from pydantic_core import PydanticValueError, SchemaError, SchemaValidator, ValidationError

from ..conftest import plain_repr

Expand Down Expand Up @@ -354,3 +354,23 @@ def f(input_value, **kwargs):

with pytest.raises(TypeError, match='^foobar$'):
v.validate_python('input value')


def test_pydantic_value_error():
def f(input_value, **kwargs):
raise PydanticValueError('my_error', 'this is a custom error {foo}', {'foo': 'FOOBAR'})

v = SchemaValidator({'type': 'function', 'mode': 'plain', 'function': f})

with pytest.raises(ValidationError) as exc_info:
v.validate_python(42)

assert exc_info.value.errors() == [
{
'kind': 'my_error',
'loc': [],
'message': 'this is a custom error FOOBAR',
'input_value': 42,
'context': {'foo': 'FOOBAR'},
}
]