Skip to content

Commit 29ae322

Browse files
authored
adding PydanticOmit exception (#282)
* adding PydanticOmit exception * more errors
1 parent 1fe1cb9 commit 29ae322

File tree

7 files changed

+82
-7
lines changed

7 files changed

+82
-7
lines changed

pydantic_core/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from ._pydantic_core import (
22
PydanticCustomError,
33
PydanticErrorKind,
4+
PydanticOmit,
45
SchemaError,
56
SchemaValidator,
67
ValidationError,
@@ -17,4 +18,5 @@
1718
'ValidationError',
1819
'PydanticCustomError',
1920
'PydanticErrorKind',
21+
'PydanticOmit',
2022
)

pydantic_core/_pydantic_core.pyi

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ if sys.version_info < (3, 11):
88
else:
99
from typing import NotRequired
1010

11-
__all__ = '__version__', 'SchemaValidator', 'SchemaError', 'ValidationError', 'PydanticCustomError', 'PydanticErrorKind'
11+
__all__ = (
12+
'__version__',
13+
'SchemaValidator',
14+
'SchemaError',
15+
'ValidationError',
16+
'PydanticCustomError',
17+
'PydanticErrorKind',
18+
'PydanticOmit',
19+
)
1220
__version__: str
1321
build_profile: str
1422

@@ -58,3 +66,6 @@ class PydanticErrorKind(ValueError):
5866

5967
def __init__(self, kind: str, context: 'dict[str, str | int] | None' = None) -> None: ...
6068
def message(self) -> str: ...
69+
70+
class PydanticOmit(Exception):
71+
def __init__(self) -> None: ...

src/errors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub use self::kinds::ErrorKind;
1010
pub use self::line_error::{pretty_line_errors, InputValue, ValError, ValLineError, ValResult};
1111
pub use self::location::LocItem;
1212
pub use self::validation_exception::ValidationError;
13-
pub use self::value_exception::{PydanticCustomError, PydanticErrorKind};
13+
pub use self::value_exception::{PydanticCustomError, PydanticErrorKind, PydanticOmit};
1414

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

src/errors/value_exception.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1-
use pyo3::exceptions::{PyTypeError, PyValueError};
1+
use pyo3::exceptions::{PyException, PyTypeError, PyValueError};
22
use pyo3::prelude::*;
33
use pyo3::types::{PyDict, PyString};
44

55
use crate::input::Input;
66

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

9+
#[pyclass(extends=PyException, module="pydantic_core._pydantic_core")]
10+
#[derive(Debug, Clone)]
11+
pub struct PydanticOmit {}
12+
13+
#[pymethods]
14+
impl PydanticOmit {
15+
#[new]
16+
pub fn py_new() -> Self {
17+
Self {}
18+
}
19+
20+
fn __str__(&self) -> &'static str {
21+
self.__repr__()
22+
}
23+
24+
fn __repr__(&self) -> &'static str {
25+
"PydanticOmit()"
26+
}
27+
}
28+
929
#[pyclass(extends=PyValueError, module="pydantic_core._pydantic_core")]
1030
#[derive(Debug, Clone, Default)]
1131
pub struct PydanticCustomError {

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod validators;
1818

1919
// required for benchmarks
2020
pub use build_tools::SchemaError;
21-
pub use errors::{PydanticCustomError, PydanticErrorKind, ValidationError};
21+
pub use errors::{PydanticCustomError, PydanticErrorKind, PydanticOmit, ValidationError};
2222
pub use validators::SchemaValidator;
2323

2424
pub fn get_version() -> String {
@@ -40,5 +40,6 @@ fn _pydantic_core(_py: Python, m: &PyModule) -> PyResult<()> {
4040
m.add_class::<SchemaError>()?;
4141
m.add_class::<PydanticCustomError>()?;
4242
m.add_class::<PydanticErrorKind>()?;
43+
m.add_class::<PydanticOmit>()?;
4344
Ok(())
4445
}

src/validators/function.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use pyo3::prelude::*;
44
use pyo3::types::{PyAny, PyDict};
55

66
use crate::build_tools::{py_error, SchemaDict};
7-
use crate::errors::{ErrorKind, LocItem, PydanticCustomError, PydanticErrorKind, ValError, ValResult, ValidationError};
7+
use crate::errors::{
8+
ErrorKind, LocItem, PydanticCustomError, PydanticErrorKind, PydanticOmit, ValError, ValResult, ValidationError,
9+
};
810
use crate::input::Input;
911
use crate::questions::Question;
1012
use crate::recursion_guard::RecursionGuard;
@@ -292,6 +294,8 @@ pub fn convert_err<'a>(py: Python<'a>, err: PyErr, input: &'a impl Input<'a>) ->
292294
}
293295
} else if err.is_instance_of::<PyAssertionError>(py) {
294296
py_err_string!(err.value(py), AssertionError, input)
297+
} else if err.is_instance_of::<PydanticOmit>(py) {
298+
ValError::Omit
295299
} else {
296300
ValError::InternalErr(err)
297301
}

tests/validators/test_function.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@
55

66
import pytest
77

8-
from pydantic_core import PydanticCustomError, PydanticErrorKind, SchemaError, SchemaValidator, ValidationError
8+
from pydantic_core import (
9+
PydanticCustomError,
10+
PydanticErrorKind,
11+
PydanticOmit,
12+
SchemaError,
13+
SchemaValidator,
14+
ValidationError,
15+
)
916

10-
from ..conftest import plain_repr
17+
from ..conftest import PyAndJson, plain_repr
1118

1219

1320
def test_function_before():
@@ -635,3 +642,33 @@ def test_error_kind(kind, message, context):
635642
assert e.message() == message
636643
assert e.kind == kind
637644
assert e.context == context
645+
646+
647+
def test_pydantic_value_error_plain(py_and_json: PyAndJson):
648+
def f(input_value, **kwargs):
649+
raise PydanticCustomError
650+
651+
v = py_and_json({'type': 'function', 'mode': 'plain', 'function': f})
652+
with pytest.raises(TypeError, match='missing 2 required positional arguments'):
653+
v.validate_test('4')
654+
655+
656+
@pytest.mark.parametrize('exception', [PydanticOmit(), PydanticOmit])
657+
def test_list_omit_exception(py_and_json: PyAndJson, exception):
658+
def f(input_value, **kwargs):
659+
if input_value % 2 == 0:
660+
raise exception
661+
return input_value
662+
663+
v = py_and_json(
664+
{
665+
'type': 'list',
666+
'items_schema': {'type': 'function', 'schema': {'type': 'int'}, 'mode': 'after', 'function': f},
667+
}
668+
)
669+
assert v.validate_test([1, 2, '3', '4']) == [1, 3]
670+
671+
672+
def test_omit_exc_repr():
673+
assert repr(PydanticOmit()) == 'PydanticOmit()'
674+
assert str(PydanticOmit()) == 'PydanticOmit()'

0 commit comments

Comments
 (0)