-
Notifications
You must be signed in to change notification settings - Fork 243
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
skip recursion checks for non-recursive definitions #989
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,11 +27,11 @@ impl BuildSerializer for DefinitionsSerializerBuilder { | |
let schema_definitions: &PyList = schema.get_as_req(intern!(py, "definitions"))?; | ||
|
||
for schema_definition in schema_definitions { | ||
let reference = schema_definition | ||
.extract::<&PyDict>()? | ||
.get_as_req::<String>(intern!(py, "ref"))?; | ||
let serializer = CombinedSerializer::build(schema_definition.downcast()?, config, definitions)?; | ||
definitions.add_definition(reference, serializer)?; | ||
let schema = schema_definition.downcast::<PyDict>()?; | ||
let reference = schema.get_as_req::<String>(intern!(py, "ref"))?; | ||
definitions.build_definition(reference, |definitions| { | ||
CombinedSerializer::build(schema, config, definitions) | ||
})?; | ||
} | ||
|
||
let inner_schema: &PyDict = schema.get_as_req(intern!(py, "schema"))?; | ||
|
@@ -69,8 +69,8 @@ impl TypeSerializer for DefinitionRefSerializer { | |
extra: &Extra, | ||
) -> PyResult<PyObject> { | ||
let value_id = extra.rec_guard.add(value, self.serializer_id)?; | ||
let comb_serializer = extra.definitions.get(self.serializer_id).unwrap(); | ||
let r = comb_serializer.to_python(value, include, exclude, extra); | ||
let definition = extra.definitions.get(self.serializer_id).unwrap(); | ||
let r = definition.value.to_python(value, include, exclude, extra); | ||
extra.rec_guard.pop(value_id, self.serializer_id); | ||
r | ||
} | ||
|
@@ -88,8 +88,10 @@ impl TypeSerializer for DefinitionRefSerializer { | |
extra: &Extra, | ||
) -> Result<S::Ok, S::Error> { | ||
let value_id = extra.rec_guard.add(value, self.serializer_id).map_err(py_err_se_err)?; | ||
let comb_serializer = extra.definitions.get(self.serializer_id).unwrap(); | ||
let r = comb_serializer.serde_serialize(value, serializer, include, exclude, extra); | ||
let definition = extra.definitions.get(self.serializer_id).unwrap(); | ||
let r = definition | ||
.value | ||
.serde_serialize(value, serializer, include, exclude, extra); | ||
extra.rec_guard.pop(value_id, self.serializer_id); | ||
Comment on lines
90
to
95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we can / should skip recursion on serializing too? |
||
r | ||
} | ||
|
@@ -99,7 +101,7 @@ impl TypeSerializer for DefinitionRefSerializer { | |
} | ||
|
||
fn retry_with_lax_check(&self, definitions: &Definitions<CombinedSerializer>) -> bool { | ||
let comb_serializer = definitions.get(self.serializer_id).unwrap(); | ||
comb_serializer.retry_with_lax_check(definitions) | ||
let definition = definitions.get(self.serializer_id).unwrap(); | ||
definition.value.retry_with_lax_check(definitions) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,8 +28,9 @@ impl BuildValidator for DefinitionsValidatorBuilder { | |
let reference = schema_definition | ||
.extract::<&PyDict>()? | ||
.get_as_req::<String>(intern!(py, "ref"))?; | ||
let validator = build_validator(schema_definition, config, definitions)?; | ||
definitions.add_definition(reference, validator)?; | ||
definitions.build_definition(reference, |definitions| { | ||
build_validator(schema_definition, config, definitions) | ||
})?; | ||
} | ||
|
||
let inner_schema: &PyAny = schema.get_as_req(intern!(py, "schema"))?; | ||
|
@@ -82,22 +83,25 @@ impl Validator for DefinitionRefValidator { | |
input: &'data impl Input<'data>, | ||
state: &mut ValidationState, | ||
) -> ValResult<'data, PyObject> { | ||
if let Some(id) = input.identity() { | ||
if state.recursion_guard.contains_or_insert(id, self.validator_id) { | ||
// we don't remove id here, we leave that to the validator which originally added id to `recursion_guard` | ||
Err(ValError::new(ErrorTypeDefaults::RecursionLoop, input)) | ||
} else { | ||
if state.recursion_guard.incr_depth() { | ||
return Err(ValError::new(ErrorTypeDefaults::RecursionLoop, input)); | ||
} | ||
let output = validate(self.validator_id, py, input, state); | ||
state.recursion_guard.remove(id, self.validator_id); | ||
state.recursion_guard.decr_depth(); | ||
output | ||
let definition = state.definitions.get(self.validator_id).unwrap(); | ||
if definition.recursive { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I comment out this block I get a segfault in the test suite which gives me confidence the recursion detection is working correctly. |
||
if let Some(id) = input.identity() { | ||
return if state.recursion_guard.contains_or_insert(id, self.validator_id) { | ||
// we don't remove id here, we leave that to the validator which originally added id to `recursion_guard` | ||
Err(ValError::new(ErrorTypeDefaults::RecursionLoop, input)) | ||
} else { | ||
if state.recursion_guard.incr_depth() { | ||
return Err(ValError::new(ErrorTypeDefaults::RecursionLoop, input)); | ||
} | ||
let output = definition.value.validate(py, input, state); | ||
state.recursion_guard.remove(id, self.validator_id); | ||
state.recursion_guard.decr_depth(); | ||
output | ||
}; | ||
} | ||
} else { | ||
validate(self.validator_id, py, input, state) | ||
} | ||
}; | ||
|
||
definition.value.validate(py, input, state) | ||
} | ||
|
||
fn validate_assignment<'data>( | ||
|
@@ -108,22 +112,29 @@ impl Validator for DefinitionRefValidator { | |
field_value: &'data PyAny, | ||
state: &mut ValidationState, | ||
) -> ValResult<'data, PyObject> { | ||
if let Some(id) = obj.identity() { | ||
if state.recursion_guard.contains_or_insert(id, self.validator_id) { | ||
// we don't remove id here, we leave that to the validator which originally added id to `recursion_guard` | ||
Err(ValError::new(ErrorTypeDefaults::RecursionLoop, obj)) | ||
} else { | ||
if state.recursion_guard.incr_depth() { | ||
return Err(ValError::new(ErrorTypeDefaults::RecursionLoop, obj)); | ||
} | ||
let output = validate_assignment(self.validator_id, py, obj, field_name, field_value, state); | ||
state.recursion_guard.remove(id, self.validator_id); | ||
state.recursion_guard.decr_depth(); | ||
output | ||
let definition = state.definitions.get(self.validator_id).unwrap(); | ||
if definition.recursive { | ||
if let Some(id) = obj.identity() { | ||
return if state.recursion_guard.contains_or_insert(id, self.validator_id) { | ||
// we don't remove id here, we leave that to the validator which originally added id to `recursion_guard` | ||
Err(ValError::new(ErrorTypeDefaults::RecursionLoop, obj)) | ||
} else { | ||
if state.recursion_guard.incr_depth() { | ||
return Err(ValError::new(ErrorTypeDefaults::RecursionLoop, obj)); | ||
} | ||
let output = definition | ||
.value | ||
.validate_assignment(py, obj, field_name, field_value, state); | ||
state.recursion_guard.remove(id, self.validator_id); | ||
state.recursion_guard.decr_depth(); | ||
output | ||
}; | ||
} | ||
} else { | ||
validate_assignment(self.validator_id, py, obj, field_name, field_value, state) | ||
} | ||
}; | ||
|
||
definition | ||
.value | ||
.validate_assignment(py, obj, field_name, field_value, state) | ||
} | ||
|
||
fn different_strict_behavior( | ||
|
@@ -151,26 +162,3 @@ impl Validator for DefinitionRefValidator { | |
Ok(()) | ||
} | ||
} | ||
|
||
fn validate<'data>( | ||
validator_id: usize, | ||
py: Python<'data>, | ||
input: &'data impl Input<'data>, | ||
state: &mut ValidationState, | ||
) -> ValResult<'data, PyObject> { | ||
let validator = state.definitions.get(validator_id).unwrap(); | ||
validator.validate(py, input, state) | ||
} | ||
|
||
#[allow(clippy::too_many_arguments)] | ||
fn validate_assignment<'data>( | ||
validator_id: usize, | ||
py: Python<'data>, | ||
obj: &'data PyAny, | ||
field_name: &'data str, | ||
field_value: &'data PyAny, | ||
state: &mut ValidationState, | ||
) -> ValResult<'data, PyObject> { | ||
let validator = state.definitions.get(validator_id).unwrap(); | ||
validator.validate_assignment(py, obj, field_name, field_value, state) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar question about serializer recursion.