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

Add full type to validation error #1276

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/errors/validation_exception.rs
Expand Up @@ -18,7 +18,7 @@ use crate::errors::LocItem;
use crate::get_pydantic_version;
use crate::input::InputType;
use crate::serializers::{DuckTypingSerMode, Extra, SerMode, SerializationState};
use crate::tools::{safe_repr, SchemaDict};
use crate::tools::{get_type_name, safe_repr, SchemaDict};

use super::line_error::ValLineError;
use super::location::Location;
Expand Down Expand Up @@ -572,7 +572,7 @@ impl PyLineError {
let input_str = safe_repr(input_value);
truncate_input_value!(output, &input_str.to_cow());

if let Ok(type_) = input_value.get_type().qualname() {
if let Ok(type_) = get_type_name(input_value.get_type()) {
write!(output, ", input_type={type_}")?;
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/tools.rs
Expand Up @@ -2,7 +2,7 @@ use std::borrow::Cow;

use pyo3::exceptions::PyKeyError;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyString};
use pyo3::types::{PyDict, PyString, PyType};
use pyo3::{ffi, intern, FromPyObject};

use jiter::{cached_py_string, pystring_fast_new, StringCacheMode};
Expand Down Expand Up @@ -114,6 +114,18 @@ impl std::fmt::Display for ReprOutput<'_> {
}
}

pub fn get_type_name(t: Bound<'_, PyType>) -> Result<String, ()> {
if let Ok(repr) = t.repr() {
let repr = repr.to_string();
if repr.starts_with("<class '") {
return Ok(repr[8..repr.len() - 2].to_owned());
}
// For example repr(typing.Literal) is 'typing.Literal'
return Ok(repr);
}
Err(())
}

pub fn safe_repr<'py>(v: &Bound<'py, PyAny>) -> ReprOutput<'py> {
if let Ok(s) = v.repr() {
ReprOutput::Python(s)
Expand Down
6 changes: 3 additions & 3 deletions tests/emscripten_runner.js
Expand Up @@ -83,9 +83,9 @@ async function main() {
const pyodide = await loadPyodide();
const FS = pyodide.FS;
setupStreams(FS, pyodide._module.TTY);
FS.mkdir('/test_dir');
FS.mount(FS.filesystems.NODEFS, {root: path.join(root_dir, 'tests')}, '/test_dir');
FS.chdir('/test_dir');
FS.mkdir('/tests');
FS.mount(FS.filesystems.NODEFS, {root: path.join(root_dir, 'tests')}, '/tests');
FS.chdir('/tests');
await pyodide.loadPackage(['micropip', 'pytest', 'pytz']);
// language=python
errcode = await pyodide.runPythonAsync(`
Expand Down
2 changes: 1 addition & 1 deletion tests/test_errors.py
Expand Up @@ -730,7 +730,7 @@ def test_error_on_repr(pydantic_version):
assert str(exc_info.value) == (
'1 validation error for int\n'
' Input should be a valid integer '
'[type=int_type, input_value=<unprintable BadRepr object>, input_type=BadRepr]\n'
'[type=int_type, input_value=<unprintable BadRepr object>, input_type=tests.test_errors.BadRepr]\n'
f' For further information visit https://errors.pydantic.dev/{pydantic_version}/v/int_type'
)
assert exc_info.value.errors(include_url=False) == [
Expand Down
2 changes: 1 addition & 1 deletion tests/validators/test_arguments.py
Expand Up @@ -1018,7 +1018,7 @@ def test_error_display(pydantic_version):
'1 validation error for arguments\n'
'b\n'
' Missing required argument [type=missing_argument, '
"input_value=ArgsKwargs((), {'a': 1}), input_type=ArgsKwargs]\n"
"input_value=ArgsKwargs((), {'a': 1}), input_type=pydantic_core._pydantic_core.ArgsKwargs]\n"
f' For further information visit https://errors.pydantic.dev/{pydantic_version}/v/missing_argument'
)
# insert_assert(exc_info.value.json(include_url=False))
Expand Down
10 changes: 6 additions & 4 deletions tests/validators/test_decimal.py
Expand Up @@ -358,7 +358,9 @@ def test_non_finite_json_values(py_and_json: PyAndJson, input_value, allow_inf_n
(
Decimal('nan'),
False,
Err("Input should be a finite number [type=finite_number, input_value=Decimal('NaN'), input_type=Decimal]"),
Err(
"Input should be a finite number [type=finite_number, input_value=Decimal('NaN'), input_type=decimal.Decimal]"
),
),
],
)
Expand All @@ -379,21 +381,21 @@ def test_non_finite_decimal_values(strict, input_value, allow_inf_nan, expected)
Decimal('+inf'),
False,
Err(
"Input should be a finite number [type=finite_number, input_value=Decimal('Infinity'), input_type=Decimal]"
"Input should be a finite number [type=finite_number, input_value=Decimal('Infinity'), input_type=decimal.Decimal]"
),
),
(
Decimal('-inf'),
True,
Err(
"Input should be greater than 0 [type=greater_than, input_value=Decimal('-Infinity'), input_type=Decimal]"
"Input should be greater than 0 [type=greater_than, input_value=Decimal('-Infinity'), input_type=decimal.Decimal]"
),
),
(
Decimal('-inf'),
False,
Err(
"Input should be a finite number [type=finite_number, input_value=Decimal('-Infinity'), input_type=Decimal]"
"Input should be a finite number [type=finite_number, input_value=Decimal('-Infinity'), input_type=decimal.Decimal]"
),
),
],
Expand Down
4 changes: 2 additions & 2 deletions tests/validators/test_int.py
Expand Up @@ -119,7 +119,7 @@ def test_int_py_and_json(py_and_json: PyAndJson, input_value, expected):
Decimal('1.001'),
Err(
'Input should be a valid integer, got a number with a fractional part '
"[type=int_from_float, input_value=Decimal('1.001'), input_type=Decimal]"
"[type=int_from_float, input_value=Decimal('1.001'), input_type=decimal.Decimal]"
),
id='decimal-remainder',
),
Expand Down Expand Up @@ -166,7 +166,7 @@ def test_int(input_value, expected):
Decimal('1.001'),
Err(
'Input should be a valid integer, got a number with a fractional part '
"[type=int_from_float, input_value=Decimal('1.001'), input_type=Decimal]"
"[type=int_from_float, input_value=Decimal('1.001'), input_type=decimal.Decimal]"
),
id='decimal-remainder',
),
Expand Down
4 changes: 3 additions & 1 deletion tests/validators/test_string.py
Expand Up @@ -50,7 +50,9 @@ def test_str(py_and_json: PyAndJson, input_value, expected):
(123, Err('Input should be a valid string [type=string_type, input_value=123, input_type=int]')),
(
Decimal('123'),
Err("Input should be a valid string [type=string_type, input_value=Decimal('123'), input_type=Decimal]"),
Err(
"Input should be a valid string [type=string_type, input_value=Decimal('123'), input_type=decimal.Decimal]"
),
),
],
)
Expand Down