Skip to content

Commit b7cb4d2

Browse files
authored
add name to function validators (#303)
1 parent 9bbe0d9 commit b7cb4d2

File tree

5 files changed

+29
-12
lines changed

5 files changed

+29
-12
lines changed

src/validators/function.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,16 @@ macro_rules! impl_build {
5151
) -> PyResult<CombinedValidator> {
5252
let py = schema.py();
5353
let validator = build_validator(schema.get_as_req(intern!(py, "schema"))?, config, build_context)?;
54-
let name = format!("{}[{}]", $name, validator.get_name());
54+
let function = schema.get_as_req::<&PyAny>(intern!(py, "function"))?;
55+
let name = format!(
56+
"{}[{}(), {}]",
57+
$name,
58+
function_name(function)?,
59+
validator.get_name()
60+
);
5561
Ok(Self {
5662
validator: Box::new(validator),
57-
func: schema.get_as_req::<&PyAny>(intern!(py, "function"))?.into_py(py),
63+
func: function.into_py(py),
5864
config: match config {
5965
Some(c) => c.into(),
6066
None => py.None(),
@@ -67,6 +73,13 @@ macro_rules! impl_build {
6773
};
6874
}
6975

76+
fn function_name(f: &PyAny) -> PyResult<String> {
77+
match f.getattr(intern!(f.py(), "__name__")) {
78+
Ok(name) => name.extract(),
79+
_ => f.repr()?.extract(),
80+
}
81+
}
82+
7083
#[derive(Debug, Clone)]
7184
pub struct FunctionBeforeValidator {
7285
validator: Box<CombinedValidator>,
@@ -150,17 +163,20 @@ impl Validator for FunctionAfterValidator {
150163
pub struct FunctionPlainValidator {
151164
func: PyObject,
152165
config: PyObject,
166+
name: String,
153167
}
154168

155169
impl FunctionPlainValidator {
156170
pub fn build(schema: &PyDict, config: Option<&PyDict>) -> PyResult<CombinedValidator> {
157171
let py = schema.py();
172+
let function = schema.get_as_req::<&PyAny>(intern!(py, "function"))?;
158173
Ok(Self {
159-
func: schema.get_as_req::<&PyAny>(intern!(py, "function"))?.into_py(py),
174+
func: function.into_py(py),
160175
config: match config {
161176
Some(c) => c.into(),
162177
None => py.None(),
163178
},
179+
name: format!("function-plain[{}()]", function_name(function)?),
164180
}
165181
.into())
166182
}
@@ -182,7 +198,7 @@ impl Validator for FunctionPlainValidator {
182198
}
183199

184200
fn get_name(&self) -> &str {
185-
"function-plain"
201+
&self.name
186202
}
187203
}
188204

tests/test_errors.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,16 @@ def f(input_value, **kwargs):
5252

5353

5454
def test_pydantic_value_error_invalid_dict():
55-
def f(input_value, **kwargs):
55+
def my_function(input_value, **kwargs):
5656
raise PydanticCustomError('my_error', 'this is a custom error {foo}', {(): 'foobar'})
5757

58-
v = SchemaValidator({'type': 'function', 'mode': 'plain', 'function': f})
58+
v = SchemaValidator({'type': 'function', 'mode': 'plain', 'function': my_function})
5959

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

6363
assert str(exc_info.value) == (
64-
'1 validation error for function-plain\n'
64+
'1 validation error for function-plain[my_function()]\n'
6565
" (error rendering message: TypeError: 'tuple' object cannot be converted to 'PyString') "
6666
'[kind=my_error, input_value=42, input_type=int]'
6767
)

tests/validators/test_chain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def test_flatten():
9494
)
9595

9696
assert validator.validate_python('input') == 'input-1-2-3'
97-
assert validator.title == 'chain[function-plain,function-plain,function-plain]'
97+
assert validator.title == 'chain[function-plain[<lambda>()],function-plain[<lambda>()],function-plain[<lambda>()]]'
9898

9999

100100
def test_chain_empty():
@@ -107,7 +107,7 @@ def test_chain_one():
107107
{'type': 'chain', 'steps': [{'type': 'function', 'mode': 'plain', 'function': lambda v, **kwargs: f'{v}-1'}]}
108108
)
109109
assert validator.validate_python('input') == 'input-1'
110-
assert validator.title == 'function-plain'
110+
assert validator.title == 'function-plain[<lambda>()]'
111111

112112

113113
def test_ask():

tests/validators/test_function.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ def f(input_value, **kwargs):
4040

4141

4242
def test_function_before_error():
43-
def f(input_value, **kwargs):
43+
def my_function(input_value, **kwargs):
4444
return input_value + 'x'
4545

4646
v = SchemaValidator(
47-
{'type': 'function', 'mode': 'before', 'function': f, 'schema': {'type': 'str', 'max_length': 5}}
47+
{'type': 'function', 'mode': 'before', 'function': my_function, 'schema': {'type': 'str', 'max_length': 5}}
4848
)
4949

5050
assert v.validate_python('1234') == '1234x'
@@ -59,6 +59,7 @@ def f(input_value, **kwargs):
5959
'context': {'max_length': 5},
6060
}
6161
]
62+
assert repr(exc_info.value).startswith('1 validation error for function-before[my_function(), constrained-str]\n')
6263

6364

6465
def test_function_before_error_model():

tests/validators/test_recursive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ def f(input_value, **kwargs):
616616
assert exc_info.value.errors() == [
617617
{
618618
'kind': 'recursion_loop',
619-
'loc': ['function-after[...]'],
619+
'loc': ['function-after[f(), ...]'],
620620
'message': 'Recursion error - cyclic reference detected',
621621
'input_value': 'input value',
622622
},

0 commit comments

Comments
 (0)