8000 fix borrow error when serializing recursive models by davidhewitt · Pull Request #764 · pydantic/pydantic-core · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
8000

fix borrow error when serializing recursive models #764

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

Merged
merged 1 commit into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/serializers/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub(crate) fn infer_to_python_known(

let serialize_with_serializer = || {
let py_serializer = value.getattr(intern!(py, "__pydantic_serializer__"))?;
let serializer: SchemaSerializer = py_serializer.extract()?;
let serializer: PyRef<SchemaSerializer> = py_serializer.extract()?;
let extra = serializer.build_extra(
py,
extra.mode,
Expand Down Expand Up @@ -464,7 +464,7 @@ pub(crate) fn infer_serialize_known<S: Serializer>(
let py_serializer = value
.getattr(intern!(py, "__pydantic_serializer__"))
.map_err(py_err_se_err)?;
let extracted_serializer: SchemaSerializer = py_serializer.extract().map_err(py_err_se_err)?;
let extracted_serializer: PyRef<SchemaSerializer> = py_serializer.extract().map_err(py_err_se_err)?;
let extra = extracted_serializer.build_extra(
py,
extra.mode,
Expand Down
13 changes: 7 additions & 6 deletions src/serializers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt::Debug;
use std::sync::atomic::{AtomicUsize, Ordering};

use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyDict};
Expand Down Expand Up @@ -26,11 +27,11 @@ mod shared;
mod type_serializers;

#[pyclass(module = "pydantic_core._pydantic_core")]
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct SchemaSerializer {
serializer: CombinedSerializer,
definitions: Vec<CombinedSerializer>,
json_size: usize,
expected_json_size: AtomicUsize,
config: SerializationConfig,
}

Expand Down Expand Up @@ -80,7 +81,7 @@ impl SchemaSerializer {
Ok(Self {
serializer,
definitions: definitions_builder.finish()?,
json_size: 1024,
expected_json_size: AtomicUsize::new(1024),
config: SerializationConfig::from_config(config)?,
})
}
Expand Down Expand Up @@ -130,7 +131,7 @@ impl SchemaSerializer {
exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = true,
fallback = None))]
pub fn to_json(
&mut self,
&self,
py: Python,
value: &PyAny,
indent: Option<usize>,
Expand Down Expand Up @@ -166,12 +167,12 @@ impl SchemaSerializer {
exclude,
&extra,
indent,
self.json_size,
self.expected_json_size.load(Ordering::Relaxed),
)?;

warnings.final_check(py)?;

self.json_size = bytes.len();
self.expected_json_size.store(bytes.len(), Ordering::Relaxed);
let py_bytes = PyBytes::new(py, &bytes);
Ok(py_bytes.into())
}
Expand Down
4 changes: 2 additions & 2 deletions src/serializers/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,11 @@ pub(crate) fn to_json_bytes(
exclude: Option<&PyAny>,
extra: &Extra,
indent: Option<usize>,
json_size: usize,
expected_json_size: usize,
) -> PyResult<Vec<u8>> {
let serializer = PydanticSerializer::new(value, serializer, include, exclude, extra);

let writer: Vec<u8> = Vec::with_capacity(json_size);
let writer: Vec<u8> = Vec::with_capacity(expected_json_size);
let bytes = match indent {
Some(indent) => {
let indent = vec![b' '; indent];
Expand Down
4 changes: 1 addition & 3 deletions tests/serializers/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,7 @@ def bad_recursive(value):
s.to_json(42)
# insert_assert(str(exc_info.value))
assert str(exc_info.value) == (
'Error serializing to JSON: '
'PydanticSerializationError: Error calling function `bad_recursive`: '
'RuntimeError: Already mutably borrowed'
'Error serializing to JSON: PydanticSerializationError: Error calling function `bad_recursive`: RecursionError'
)


Expand Down
16 changes: 16 additions & 0 deletions tests/serializers/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ def test_model_allow_extra():
assert j == b'{"bar":"more","foo":1,"c":3}'


def test_model_recursive_in_extra():
# See https://github.com/pydantic/pydantic/issues/6571

class Model(BasicModel):
__slots__ = '__pydantic_extra__'

s = SchemaSerializer(
core_schema.model_schema(
Model, core_schema.model_fields_schema({}, extra_behavior='allow'), extra_behavior='allow'
)
)
Model.__pydantic_serializer__ = s

assert s.to_json(Model(__pydantic_extra__=dict(other=Model(__pydantic_extra__={})))) == b'{"other":{}}'


@pytest.mark.parametrize(
'params',
[
Expand Down
0