Skip to content

Commit 962a6b4

Browse files
committed
Add hide_input to ValidationError
1 parent 26fa27d commit 962a6b4

File tree

11 files changed

+230
-24
lines changed

11 files changed

+230
-24
lines changed

pydantic_core/_pydantic_core.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ class SchemaError(Exception):
173173
class ValidationError(ValueError):
174174
@staticmethod
175175
def from_exception_data(
176-
title: str, errors: 'list[InitErrorDetails]', error_mode: Literal['python', 'json'] = 'python'
176+
title: str,
177+
errors: 'list[InitErrorDetails]',
178+
error_mode: Literal['python', 'json'] = 'python',
179+
hide_input: bool = False,
177180
) -> ValidationError:
178181
"""
179182
Provisory constructor for a Validation Error.

pydantic_core/core_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class CoreConfig(TypedDict, total=False):
5151
# the config options are used to customise serialization to JSON
5252
ser_json_timedelta: Literal['iso8601', 'float'] # default: 'iso8601'
5353
ser_json_bytes: Literal['utf8', 'base64'] # default: 'utf8'
54+
hide_input_in_errors: bool
5455

5556

5657
IncExCall: TypeAlias = 'set[int | str] | dict[int | str, IncExCall] | None'

src/build_tools.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ impl SchemaError {
140140
match error {
141141
ValError::LineErrors(raw_errors) => {
142142
let line_errors = raw_errors.into_iter().map(|e| e.into_py(py)).collect();
143-
let validation_error = ValidationError::new(line_errors, "Schema".to_object(py), ErrorMode::Python);
143+
let validation_error =
144+
ValidationError::new(line_errors, "Schema".to_object(py), ErrorMode::Python, false);
144145
let schema_error = SchemaError(SchemaErrorEnum::ValidationError(validation_error));
145146
match Py::new(py, schema_error) {
146147
Ok(err) => PyErr::from_value(err.into_ref(py)),
@@ -177,21 +178,21 @@ impl SchemaError {
177178
fn errors(&self, py: Python) -> PyResult<Py<PyList>> {
178179
match &self.0 {
179180
SchemaErrorEnum::Message(_) => Ok(PyList::empty(py).into_py(py)),
180-
SchemaErrorEnum::ValidationError(error) => error.errors(py, false, true),
181+
SchemaErrorEnum::ValidationError(error) => error.errors(py, false, false),
181182
}
182183
}
183184

184185
fn __str__(&self, py: Python) -> String {
185186
match &self.0 {
186187
SchemaErrorEnum::Message(message) => message.to_owned(),
187-
SchemaErrorEnum::ValidationError(error) => error.display(py, Some("Invalid Schema:")),
188+
SchemaErrorEnum::ValidationError(error) => error.display(py, Some("Invalid Schema:"), false),
188189
}
189190
}
190191

191192
fn __repr__(&self, py: Python) -> String {
192193
match &self.0 {
193194
SchemaErrorEnum::Message(message) => format!("SchemaError({message:?})"),
194-
SchemaErrorEnum::ValidationError(error) => error.display(py, Some("Invalid Schema:")),
195+
SchemaErrorEnum::ValidationError(error) => error.display(py, Some("Invalid Schema:"), false),
195196
}
196197
}
197198
}

src/errors/validation_exception.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,16 @@ pub struct ValidationError {
3131
line_errors: Vec<PyLineError>,
3232
error_mode: ErrorMode,
3333
title: PyObject,
34+
hide_input: bool,
3435
}
3536

3637
impl ValidationError {
37-
pub fn new(line_errors: Vec<PyLineError>, title: PyObject, error_mode: ErrorMode) -> Self {
38+
pub fn new(line_errors: Vec<PyLineError>, title: PyObject, error_mode: ErrorMode, hide_input: bool) -> Self {
3839
Self {
3940
line_errors,
4041
title,
4142
error_mode,
43+
hide_input,
4244
}
4345
}
4446

@@ -48,6 +50,7 @@ impl ValidationError {
4850
error_mode: ErrorMode,
4951
error: ValError,
5052
outer_location: Option<LocItem>,
53+
hide_input: bool,
5154
) -> PyErr {
5255
match error {
5356
ValError::LineErrors(raw_errors) => {
@@ -58,7 +61,7 @@ impl ValidationError {
5861
.collect(),
5962
None => raw_errors.into_iter().map(|e| e.into_py(py)).collect(),
6063
};
61-
let validation_error = Self::new(line_errors, title, error_mode);
64+
let validation_error = Self::new(line_errors, title, error_mode, hide_input);
6265
match Py::new(py, validation_error) {
6366
Ok(err) => PyErr::from_value(err.into_ref(py)),
6467
Err(err) => err,
@@ -69,9 +72,9 @@ impl ValidationError {
6972
}
7073
}
7174

72-
pub fn display(&self, py: Python, prefix_override: Option<&'static str>) -> String {
75+
pub fn display(&self, py: Python, prefix_override: Option<&'static str>, hide_input: bool) -> String {
7376
let url_prefix = get_url_prefix(py, include_url_env(py));
74-
let line_errors = pretty_py_line_errors(py, &self.error_mode, self.line_errors.iter(), url_prefix);
77+
let line_errors = pretty_py_line_errors(py, &self.error_mode, self.line_errors.iter(), url_prefix, hide_input);
7578
if let Some(prefix) = prefix_override {
7679
format!("{prefix}\n{line_errors}")
7780
} else {
@@ -129,13 +132,18 @@ impl ValidationError {
129132
title: PyObject,
130133
line_errors: &PyList,
131134
error_mode: Option<&str>,
135+
hide_input: Option<bool>,
132136
) -> PyResult<Py<Self>> {
133137
Py::new(
134138
py,
135139
Self {
136140
line_errors: line_errors.iter().map(PyLineError::try_from).collect::<PyResult<_>>()?,
137141
title,
138142
error_mode: ErrorMode::try_from(error_mode)?,
143+
hide_input: match hide_input {
144+
Some(t) => t,
145+
None => false,
146+
},
139147
},
140148
)
141149
}
@@ -210,7 +218,7 @@ impl ValidationError {
210218
}
211219

212220
fn __repr__(&self, py: Python) -> String {
213-
self.display(py, None)
221+
self.display(py, None, self.hide_input)
214222
}
215223

216224
fn __str__(&self, py: Python) -> String {
@@ -238,9 +246,10 @@ pub fn pretty_py_line_errors<'a>(
238246
error_mode: &ErrorMode,
239247
line_errors_iter: impl Iterator<Item = &'a PyLineError>,
240248
url_prefix: Option<&str>,
249+
hide_input: bool,
241250
) -> String {
242251
line_errors_iter
243-
.map(|i| i.pretty(py, error_mode, url_prefix))
252+
.map(|i| i.pretty(py, error_mode, url_prefix, hide_input))
244253
.collect::<Result<Vec<_>, _>>()
245254
.unwrap_or_else(|err| vec![format!("[error formatting line errors: {err}]")])
246255
.join("\n")
@@ -349,7 +358,13 @@ impl PyLineError {
349358
Ok(dict.into_py(py))
350359
}
351360

352-
fn pretty(&self, py: Python, error_mode: &ErrorMode, url_prefix: Option<&str>) -> Result<String, fmt::Error> {
361+
fn pretty(
362+
&self,
363+
py: Python,
364+
error_mode: &ErrorMode,
365+
url_prefix: Option<&str>,
366+
hide_input: bool,
367+
) -> Result<String, fmt::Error> {
353368
let mut output = String::with_capacity(200);
354369
write!(output, "{}", self.location)?;
355370

@@ -359,12 +374,15 @@ impl PyLineError {
359374
};
360375
write!(output, " {message} [type={}", self.error_type.type_string())?;
361376

362-
let input_value = self.input_value.as_ref(py);
363-
let input_str = safe_repr(input_value);
364-
truncate_input_value!(output, input_str);
377+
println!("{}", hide_input);
378+
if !hide_input {
379+
let input_value = self.input_value.as_ref(py);
380+
let input_str = safe_repr(input_value);
381+
truncate_input_value!(output, input_str);
365382

366-
if let Ok(type_) = input_value.get_type().name() {
367-
write!(output, ", input_type={type_}")?;
383+
if let Ok(type_) = input_value.get_type().name() {
384+
write!(output, ", input_type={type_}")?;
385+
}
368386
}
369387
if let Some(url_prefix) = url_prefix {
370388
match self.error_type {

src/validators/function.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ macro_rules! impl_build {
4646
function_name(function)?,
4747
validator.get_name()
4848
);
49+
let config_hide_input = match config {
50+
Some(c) => c.get_item("hide_input_in_errors"),
51+
None => None,
52+
};
53+
let hide_input = match config_hide_input {
54+
Some(t) => t.is_true().unwrap_or(false),
55+
None => false,
56+
};
4957
Ok(Self {
5058
validator: Box::new(validator),
5159
func: function.into_py(py),
@@ -56,6 +64,7 @@ macro_rules! impl_build {
5664
name,
5765
is_field_validator,
5866
info_arg,
67+
hide_input,
5968
}
6069
.into())
6170
}
@@ -127,6 +136,7 @@ pub struct FunctionBeforeValidator {
127136
name: String,
128137
is_field_validator: bool,
129138
info_arg: bool,
139+
hide_input: bool,
130140
}
131141

132142
impl_build!(FunctionBeforeValidator, "function-before");
@@ -160,6 +170,7 @@ pub struct FunctionAfterValidator {
160170
name: String,
161171
is_field_validator: bool,
162172
info_arg: bool,
173+
hide_input: bool,
163174
}
164175

165176
impl_build!(FunctionAfterValidator, "function-after");
@@ -262,6 +273,7 @@ pub struct FunctionWrapValidator {
262273
name: String,
263274
is_field_validator: bool,
264275
info_arg: bool,
276+
hide_input: bool,
265277
}
266278

267279
impl_build!(FunctionWrapValidator, "function-wrap");
@@ -301,6 +313,7 @@ impl Validator for FunctionWrapValidator {
301313
definitions,
302314
extra,
303315
recursion_guard,
316+
self.hide_input,
304317
),
305318
};
306319
self._validate(
@@ -329,6 +342,7 @@ impl Validator for FunctionWrapValidator {
329342
definitions,
330343
extra,
331344
recursion_guard,
345+
self.hide_input,
332346
),
333347
updated_field_name: field_name.to_string(),
334348
updated_field_value: field_value.to_object(py),

src/validators/generator.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub struct GeneratorValidator {
1818
min_length: Option<usize>,
1919
max_length: Option<usize>,
2020
name: String,
21+
hide_input: bool,
2122
}
2223

2324
impl BuildValidator for GeneratorValidator {
@@ -33,11 +34,20 @@ impl BuildValidator for GeneratorValidator {
3334
Some(ref v) => format!("{}[{}]", Self::EXPECTED_TYPE, v.get_name()),
3435
None => format!("{}[any]", Self::EXPECTED_TYPE),
3536
};
37+
let config_hide_input = match config {
38+
Some(c) => c.get_item("hide_input_in_errors"),
39+
None => None,
40+
};
41+
let hide_input = match config_hide_input {
42+
Some(t) => t.is_true().unwrap_or(false),
43+
None => false,
44+
};
3645
Ok(Self {
3746
item_validator,
3847
name,
3948
min_length: schema.get_as(pyo3::intern!(schema.py(), "min_length"))?,
4049
max_length: schema.get_as(pyo3::intern!(schema.py(), "max_length"))?,
50+
hide_input: hide_input,
4151
}
4252
.into())
4353
}
@@ -53,16 +63,24 @@ impl Validator for GeneratorValidator {
5363
recursion_guard: &'s mut RecursionGuard,
5464
) -> ValResult<'data, PyObject> {
5565
let iterator = input.validate_iter()?;
56-
let validator = self
57-
.item_validator
58-
.as_ref()
59-
.map(|v| InternalValidator::new(py, "ValidatorIterator", v, definitions, extra, recursion_guard));
66+
let validator = self.item_validator.as_ref().map(|v| {
67+
InternalValidator::new(
68+
py,
69+
"ValidatorIterator",
70+
v,
71+
definitions,
72+
extra,
73+
recursion_guard,
74+
self.hide_input,
75+
)
76+
});
6077

6178
let v_iterator = ValidatorIterator {
6279
iterator,
6380
validator,
6481
min_length: self.min_length,
6582
max_length: self.max_length,
83+
hide_input: self.hide_input,
6684
};
6785
Ok(v_iterator.into_py(py))
6886
}
@@ -98,6 +116,7 @@ struct ValidatorIterator {
98116
validator: Option<InternalValidator>,
99117
min_length: Option<usize>,
100118
max_length: Option<usize>,
119+
hide_input: bool,
101120
}
102121

103122
#[pymethods]
@@ -109,6 +128,7 @@ impl ValidatorIterator {
109128
fn __next__(mut slf: PyRefMut<'_, Self>, py: Python) -> PyResult<Option<PyObject>> {
110129
let min_length = slf.min_length;
111130
let max_length = slf.max_length;
131+
let hide_input = slf.hide_input;
112132
let Self {
113133
validator, iterator, ..
114134
} = &mut *slf;
@@ -133,6 +153,7 @@ impl ValidatorIterator {
133153
ErrorMode::Python,
134154
val_error,
135155
None,
156+
hide_input,
136157
));
137158
}
138159
}
@@ -157,6 +178,7 @@ impl ValidatorIterator {
157178
ErrorMode::Python,
158179
val_error,
159180
None,
181+
hide_input,
160182
));
161183
}
162184
}
@@ -203,6 +225,7 @@ pub struct InternalValidator {
203225
self_instance: Option<PyObject>,
204226
recursion_guard: RecursionGuard,
205227
validation_mode: InputType,
228+
hide_input: bool,
206229
}
207230

208231
impl fmt::Debug for InternalValidator {
@@ -219,6 +242,7 @@ impl InternalValidator {
219242
definitions: &[CombinedValidator],
220243
extra: &Extra,
221244
recursion_guard: &RecursionGuard,
245+
hide_input: bool,
222246
) -> Self {
223247
Self {
224248
name: name.to_string(),
@@ -230,6 +254,7 @@ impl InternalValidator {
230254
self_instance: extra.self_instance.map(|d| d.into_py(py)),
231255
recursion_guard: recursion_guard.clone(),
232256
validation_mode: extra.mode,
257+
hide_input: hide_input,
233258
}
234259
}
235260

@@ -261,7 +286,14 @@ impl InternalValidator {
261286
&mut self.recursion_guard,
262287
)
263288
.map_err(|e| {
264-
ValidationError::from_val_error(py, self.name.to_object(py), ErrorMode::Python, e, outer_location)
289+
ValidationError::from_val_error(
290+
py,
291+
self.name.to_object(py),
292+
ErrorMode::Python,
293+
e,
294+
outer_location,
295+
self.hide_input,
296+
)
265297
})
266298
}
267299

@@ -286,7 +318,14 @@ impl InternalValidator {
286318
self.validator
287319
.validate(py, input, &extra, &self.definitions, &mut self.recursion_guard)
288320
.map_err(|e| {
289-
ValidationError::from_val_error(py, self.name.to_object(py), ErrorMode::Python, e, outer_location)
321+
ValidationError::from_val_error(
322+
py,
323+
self.name.to_object(py),
324+
ErrorMode::Python,
325+
e,
326+
outer_location,
327+
self.hide_input,
328+
)
290329
})
291330
}
292331
}

0 commit comments

Comments
 (0)