Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ except ValidationError as e:
1 validation error for model
age
Input should be greater than or equal to 18
[kind=greater_than_equal, context={ge: 18}, input_value=11, input_type=int]
[type=greater_than_equal, context={ge: 18}, input_value=11, input_type=int]
"""
```

Expand Down
4 changes: 2 additions & 2 deletions pydantic_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ._pydantic_core import (
PydanticCustomError,
PydanticKindError,
PydanticKnownError,
PydanticOmit,
SchemaError,
SchemaValidator,
Expand All @@ -17,6 +17,6 @@
'SchemaError',
'ValidationError',
'PydanticCustomError',
'PydanticKindError',
'PydanticKnownError',
'PydanticOmit',
)
22 changes: 11 additions & 11 deletions pydantic_core/_pydantic_core.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import decimal
import sys
from typing import Any, TypedDict

from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorKind
from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorType

if sys.version_info < (3, 11):
from typing_extensions import NotRequired
Expand All @@ -16,7 +16,7 @@ __all__ = (
'SchemaError',
'ValidationError',
'PydanticCustomError',
'PydanticKindError',
'PydanticKnownError',
'PydanticOmit',
'list_all_errors',
)
Expand All @@ -42,7 +42,7 @@ class SchemaError(Exception):
pass

class ErrorDetails(TypedDict):
kind: str
type: str
loc: 'list[int | str]'
message: str
input_value: Any
Expand All @@ -55,33 +55,33 @@ class ValidationError(ValueError):
def errors(self, include_context: bool = True) -> 'list[ErrorDetails]': ...

class PydanticCustomError(ValueError):
kind: str
type: str
message_template: str
context: 'dict[str, Any] | None'

def __init__(self, kind: str, message_template: str, context: 'dict[str, Any] | None' = None) -> None: ...
def __init__(self, error_type: str, message_template: str, context: 'dict[str, Any] | None' = None) -> None: ...
def message(self) -> str: ...

class PydanticKindError(ValueError):
kind: ErrorKind
class PydanticKnownError(ValueError):
type: ErrorType
message_template: str
context: 'dict[str, str | int | float] | None'

def __init__(
self, kind: ErrorKind, context: 'dict[str, str | int | float | decimal.Decimal] | None' = None
self, error_type: ErrorType, context: 'dict[str, str | int | float | decimal.Decimal] | None' = None
) -> None: ...
def message(self) -> str: ...

class PydanticOmit(Exception):
def __init__(self) -> None: ...

class ErrorKindInfo(TypedDict):
kind: ErrorKind
class ErrorTypeInfo(TypedDict):
type: ErrorType
message_template: str
example_message: str
example_context: 'dict[str, str | int | float] | None'

def list_all_errors() -> 'list[ErrorKindInfo]':
def list_all_errors() -> 'list[ErrorTypeInfo]':
"""
Get information about all built-in errors.
"""
28 changes: 11 additions & 17 deletions pydantic_core/core_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,16 +736,10 @@ def nullable_schema(
return dict_not_none(type='nullable', schema=schema, strict=strict, ref=ref, extra=extra)


class CustomError(TypedDict, total=False):
kind: Required[str]
message: str
context: Dict[str, Union[str, int]]


class UnionSchema(TypedDict, total=False):
type: Required[Literal['union']]
choices: Required[List[CoreSchema]]
custom_error_kind: str
custom_error_type: str
custom_error_message: str
custom_error_context: Dict[str, Union[str, int, float]]
strict: bool
Expand All @@ -755,7 +749,7 @@ class UnionSchema(TypedDict, total=False):

def union_schema(
*choices: CoreSchema,
custom_error_kind: str | None = None,
custom_error_type: str | None = None,
custom_error_message: str | None = None,
custom_error_context: dict[str, str | int] | None = None,
strict: bool | None = None,
Expand All @@ -765,7 +759,7 @@ def union_schema(
return dict_not_none(
type='union',
choices=choices,
custom_error_kind=custom_error_kind,
custom_error_type=custom_error_type,
custom_error_message=custom_error_message,
custom_error_context=custom_error_context,
strict=strict,
Expand All @@ -780,7 +774,7 @@ class TaggedUnionSchema(TypedDict, total=False):
discriminator: Required[
Union[str, List[Union[str, int]], List[List[Union[str, int]]], Callable[[Any], Optional[str]]]
]
custom_error_kind: str
custom_error_type: str
custom_error_message: str
custom_error_context: Dict[str, Union[str, int, float]]
strict: bool
Expand All @@ -792,7 +786,7 @@ def tagged_union_schema(
choices: Dict[str, CoreSchema],
discriminator: str | list[str | int] | list[list[str | int]] | Callable[[Any], str | None],
*,
custom_error_kind: str | None = None,
custom_error_type: str | None = None,
custom_error_message: str | None = None,
custom_error_context: dict[str, int | str | float] | None = None,
strict: bool | None = None,
Expand All @@ -803,7 +797,7 @@ def tagged_union_schema(
type='tagged-union',
choices=choices,
discriminator=discriminator,
custom_error_kind=custom_error_kind,
custom_error_type=custom_error_type,
custom_error_message=custom_error_message,
custom_error_context=custom_error_context,
strict=strict,
Expand Down Expand Up @@ -989,7 +983,7 @@ def recursive_reference_schema(schema_ref: str) -> RecursiveReferenceSchema:
class CustomErrorSchema(TypedDict, total=False):
type: Required[Literal['custom_error']]
schema: Required[CoreSchema]
custom_error_kind: Required[str]
custom_error_type: Required[str]
custom_error_message: str
custom_error_context: Dict[str, Union[str, int, float]]
ref: str
Expand All @@ -998,7 +992,7 @@ class CustomErrorSchema(TypedDict, total=False):

def custom_error_schema(
schema: CoreSchema,
custom_error_kind: str,
custom_error_type: str,
*,
custom_error_message: str | None = None,
custom_error_context: dict[str, str | int | float] | None = None,
Expand All @@ -1008,7 +1002,7 @@ def custom_error_schema(
return dict_not_none(
type='custom_error',
schema=schema,
custom_error_kind=custom_error_kind,
custom_error_type=custom_error_type,
custom_error_message=custom_error_message,
custom_error_context=custom_error_context,
ref=ref,
Expand Down Expand Up @@ -1067,9 +1061,9 @@ def json_schema(schema: CoreSchema | None = None, *, ref: str | None = None, ext
JsonSchema,
]

# used in _pydantic_core.pyi::PydanticKindError
# used in _pydantic_core.pyi::PydanticKnownError
# to update this, call `pytest -k test_all_errors` and copy the output
ErrorKind = Literal[
ErrorType = Literal[
'json_invalid',
'json_type',
'recursion_loop',
Expand Down
36 changes: 18 additions & 18 deletions src/errors/line_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use pyo3::PyDowncastError;

use crate::input::{Input, JsonInput};

use super::kinds::ErrorKind;
use super::location::{LocItem, Location};
use super::types::ErrorType;
use super::validation_exception::{pretty_py_line_errors, PyLineError};

pub type ValResult<'a, T> = Result<T, ValError<'a>>;
Expand Down Expand Up @@ -36,16 +36,16 @@ impl<'a> From<Vec<ValLineError<'a>>> for ValError<'a> {
}

impl<'a> ValError<'a> {
pub fn new(kind: ErrorKind, input: &'a impl Input<'a>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new(kind, input)])
pub fn new(error_type: ErrorType, input: &'a impl Input<'a>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new(error_type, input)])
}

pub fn new_with_loc(kind: ErrorKind, input: &'a impl Input<'a>, loc: impl Into<LocItem>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new_with_loc(kind, input, loc)])
pub fn new_with_loc(error_type: ErrorType, input: &'a impl Input<'a>, loc: impl Into<LocItem>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new_with_loc(error_type, input, loc)])
}

pub fn new_custom_input(kind: ErrorKind, input_value: InputValue<'a>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new_custom_input(kind, input_value)])
pub fn new_custom_input(error_type: ErrorType, input_value: InputValue<'a>) -> ValError<'a> {
Self::LineErrors(vec![ValLineError::new_custom_input(error_type, input_value)])
}

/// helper function to call with_outer on line items if applicable
Expand Down Expand Up @@ -81,32 +81,32 @@ pub fn pretty_line_errors(py: Python, line_errors: Vec<ValLineError>) -> String
/// I don't like the name `ValLineError`, but it's the best I could come up with (for now).
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct ValLineError<'a> {
pub kind: ErrorKind,
pub error_type: ErrorType,
// location is reversed so that adding an "outer" location item is pushing, it's reversed before showing to the user
pub location: Location,
pub input_value: InputValue<'a>,
}

impl<'a> ValLineError<'a> {
pub fn new(kind: ErrorKind, input: &'a impl Input<'a>) -> ValLineError<'a> {
pub fn new(error_type: ErrorType, input: &'a impl Input<'a>) -> ValLineError<'a> {
Self {
kind,
error_type,
input_value: input.as_error_value(),
location: Location::default(),
}
}

pub fn new_with_loc(kind: ErrorKind, input: &'a impl Input<'a>, loc: impl Into<LocItem>) -> ValLineError<'a> {
pub fn new_with_loc(error_type: ErrorType, input: &'a impl Input<'a>, loc: impl Into<LocItem>) -> ValLineError<'a> {
Self {
kind,
error_type,
input_value: input.as_error_value(),
location: Location::new_some(loc.into()),
}
}

pub fn new_custom_input(kind: ErrorKind, input_value: InputValue<'a>) -> ValLineError<'a> {
pub fn new_custom_input(error_type: ErrorType, input_value: InputValue<'a>) -> ValLineError<'a> {
Self {
kind,
error_type,
input_value,
location: Location::default(),
}
Expand All @@ -119,16 +119,16 @@ impl<'a> ValLineError<'a> {
self
}

// change the kind on a error in place
pub fn with_kind(mut self, kind: ErrorKind) -> Self {
self.kind = kind;
// change the error_type on a error in place
pub fn with_type(mut self, error_type: ErrorType) -> Self {
self.error_type = error_type;
self
}

/// a bit like clone but change the lifetime to match py, used by ValError.duplicate above
pub fn duplicate<'py>(&'a self, py: Python<'py>) -> ValLineError<'py> {
ValLineError {
kind: self.kind.clone(),
error_type: self.error_type.clone(),
input_value: InputValue::<'py>::from(self.input_value.to_object(py)),
location: self.location.clone(),
}
Expand Down
11 changes: 8 additions & 3 deletions src/errors/location.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use pyo3::once_cell::GILOnceCell;
use std::fmt;

use pyo3::prelude::*;
use pyo3::types::PyList;
use pyo3::types::PyTuple;

/// Used to store individual items of the error location, e.g. a string for key/field names
/// or a number for array indices.
Expand Down Expand Up @@ -85,11 +86,15 @@ impl Default for Location {
}
}

static EMPTY_TUPLE: GILOnceCell<PyObject> = GILOnceCell::new();

impl ToPyObject for Location {
fn to_object(&self, py: Python<'_>) -> PyObject {
match self {
Self::List(loc) => loc.iter().rev().collect::<Vec<_>>().to_object(py),
Self::Empty => PyList::empty(py).to_object(py),
Self::List(loc) => PyTuple::new(py, loc.iter().rev()).to_object(py),
Self::Empty => EMPTY_TUPLE
.get_or_init(py, || PyTuple::empty(py).to_object(py))
.clone_ref(py),
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use pyo3::prelude::*;

mod kinds;
mod line_error;
mod location;
mod types;
mod validation_exception;
mod value_exception;

pub use self::kinds::{list_all_errors, ErrorKind};
pub use self::line_error::{pretty_line_errors, InputValue, ValError, ValLineError, ValResult};
pub use self::location::LocItem;
pub use self::types::{list_all_errors, ErrorType};
pub use self::validation_exception::ValidationError;
pub use self::value_exception::{PydanticCustomError, PydanticKindError, PydanticOmit};
pub use self::value_exception::{PydanticCustomError, PydanticKnownError, PydanticOmit};

pub fn py_err_string(py: Python, err: PyErr) -> String {
let value = err.value(py);
Expand Down
Loading