Skip to content

Commit c26fd87

Browse files
committed
wip support json cow
1 parent 352d40f commit c26fd87

File tree

8 files changed

+71
-49
lines changed

8 files changed

+71
-49
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ base64 = "0.21.7"
4444
num-bigint = "0.4.4"
4545
python3-dll-a = "0.2.7"
4646
uuid = "1.7.0"
47-
jiter = {version = "0.0.7", features = ["python"]}
47+
jiter = { git = "https://github.com/pydantic/jiter", branch = "string-cow", features = ["python"] }
4848

4949
[lib]
5050
name = "_pydantic_core"

src/errors/line_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl ValLineError {
151151
#[derive(Clone)]
152152
pub enum InputValue {
153153
Python(PyObject),
154-
Json(JsonValue),
154+
Json(JsonValue<'static>),
155155
}
156156

157157
impl ToPyObject for InputValue {

src/errors/location.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use pyo3::exceptions::PyTypeError;
22
use pyo3::sync::GILOnceCell;
3+
use std::borrow::Cow;
34
use std::fmt;
45

56
use pyo3::prelude::*;
@@ -52,6 +53,12 @@ impl From<&str> for LocItem {
5253
}
5354
}
5455

56+
impl From<Cow<'_, str>> for LocItem {
57+
fn from(s: Cow<'_, str>) -> Self {
58+
Self::S(s.into_owned())
59+
}
60+
}
61+
5562
impl From<i64> for LocItem {
5663
fn from(i: i64) -> Self {
5764
Self::I(i)

src/input/input_json.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,26 @@ use super::{
2121
};
2222

2323
/// This is required but since JSON object keys are always strings, I don't think it can be called
24-
impl From<&JsonValue> for LocItem {
24+
impl From<&JsonValue<'_>> for LocItem {
2525
fn from(json_value: &JsonValue) -> Self {
2626
match json_value {
2727
JsonValue::Int(i) => (*i).into(),
28-
JsonValue::Str(s) => s.as_str().into(),
28+
JsonValue::Str(s) => s.clone().into(),
2929
v => format!("{v:?}").into(),
3030
}
3131
}
3232
}
3333

34-
impl From<JsonValue> for LocItem {
34+
impl From<JsonValue<'_>> for LocItem {
3535
fn from(json_value: JsonValue) -> Self {
3636
(&json_value).into()
3737
}
3838
}
3939

40-
impl<'py> Input<'py> for JsonValue {
40+
impl<'py> Input<'py> for JsonValue<'_> {
4141
fn as_error_value(&self) -> InputValue {
4242
// cloning JsonValue is cheap due to use of Arc
43-
InputValue::Json(self.clone())
43+
InputValue::Json(self.clone().into_static())
4444
}
4545

4646
fn is_none(&self) -> bool {
@@ -91,7 +91,7 @@ impl<'py> Input<'py> for JsonValue {
9191
// TODO: in V3 we may want to make JSON str always win if in union, for consistency,
9292
// see https://github.com/pydantic/pydantic-core/pull/867#discussion_r1386582501
9393
match self {
94-
JsonValue::Str(s) => Ok(ValidationMatch::strict(s.as_str().into())),
94+
JsonValue::Str(s) => Ok(ValidationMatch::strict(s.as_ref().into())),
9595
JsonValue::Int(i) if !strict && coerce_numbers_to_str => Ok(ValidationMatch::lax(i.to_string().into())),
9696
JsonValue::BigInt(b) if !strict && coerce_numbers_to_str => Ok(ValidationMatch::lax(b.to_string().into())),
9797
JsonValue::Float(f) if !strict && coerce_numbers_to_str => Ok(ValidationMatch::lax(f.to_string().into())),
@@ -135,7 +135,7 @@ impl<'py> Input<'py> for JsonValue {
135135

136136
fn exact_str(&self) -> ValResult<EitherString<'_>> {
137137
match self {
138-
JsonValue::Str(s) => Ok(s.as_str().into()),
138+
JsonValue::Str(s) => Ok(s.as_ref().into()),
139139
_ => Err(ValError::new(ErrorTypeDefaults::StringType, self)),
140140
}
141141
}
@@ -313,7 +313,7 @@ impl<'py> Input<'py> for str {
313313
fn as_error_value(&self) -> InputValue {
314314
// Justification for the clone: this is on the error pathway and we are generally ok
315315
// with errors having a performance penalty
316-
InputValue::Json(JsonValue::Str(self.to_owned()))
316+
InputValue::Json(JsonValue::Str(self.to_owned().into()))
317317
}
318318

319319
fn as_kwargs(&self, _py: Python<'py>) -> Option<Bound<'py, PyDict>> {
@@ -447,5 +447,5 @@ impl BorrowInput<'_> for String {
447447
}
448448

449449
fn string_to_vec(s: &str) -> JsonArray {
450-
JsonArray::new(s.chars().map(|c| JsonValue::Str(c.to_string())).collect())
450+
JsonArray::new(s.chars().map(|c| JsonValue::Str(c.to_string().into())).collect())
451451
}

src/input/return_enums.rs

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ pub enum GenericIterable<'a, 'py> {
8383
PyByteArray(&'a Bound<'py, PyByteArray>),
8484
Sequence(&'a Bound<'py, PySequence>),
8585
Iterator(Bound<'py, PyIterator>),
86-
JsonArray(&'a [JsonValue]),
87-
JsonObject(&'a JsonObject),
86+
JsonArray(&'a [JsonValue<'a>]),
87+
JsonObject(&'a JsonObject<'a>),
8888
JsonString(&'a str),
8989
}
9090

@@ -434,7 +434,7 @@ pub enum GenericMapping<'a, 'py> {
434434
PyMapping(&'a Bound<'py, PyMapping>),
435435
StringMapping(&'a Bound<'py, PyDict>),
436436
PyGetAttr(Bound<'py, PyAny>, Option<Bound<'py, PyDict>>),
437-
JsonObject(&'a JsonObject),
437+
JsonObject(&'a JsonObject<'a>),
438438
}
439439

440440
macro_rules! derive_from {
@@ -450,8 +450,8 @@ macro_rules! derive_from {
450450
derive_from!(GenericMapping, PyDict, PyDict);
451451
derive_from!(GenericMapping, PyMapping, PyMapping);
452452

453-
impl<'a> From<&'a JsonObject> for GenericMapping<'a, '_> {
454-
fn from(s: &'a JsonObject) -> Self {
453+
impl<'a> From<&'a JsonObject<'a>> for GenericMapping<'a, '_> {
454+
fn from(s: &'a JsonObject<'a>) -> Self {
455455
Self::JsonObject(s)
456456
}
457457
}
@@ -613,41 +613,50 @@ impl<'py> Iterator for AttributesGenericIterator<'py> {
613613
// size_hint is omitted as it isn't needed
614614
}
615615

616-
pub struct JsonObjectGenericIterator<'py> {
617-
object_iter: SliceIter<'py, (String, JsonValue)>,
616+
pub struct JsonObjectGenericIterator<'a> {
617+
object_iter: SliceIter<'a, (Cow<'a, str>, JsonValue<'a>)>,
618618
}
619619

620-
impl<'py> JsonObjectGenericIterator<'py> {
621-
pub fn new(json_object: &'py JsonObject) -> ValResult<Self> {
620+
impl<'a> JsonObjectGenericIterator<'a> {
621+
pub fn new(json_object: &'a JsonObject<'a>) -> ValResult<Self> {
622622
Ok(Self {
623623
object_iter: json_object.iter(),
624624
})
625625
}
626626
}
627627

628-
impl<'py> Iterator for JsonObjectGenericIterator<'py> {
629-
type Item = ValResult<(&'py String, &'py JsonValue)>;
628+
impl<'a> Iterator for JsonObjectGenericIterator<'a> {
629+
type Item = ValResult<(&'a str, &'a JsonValue<'a>)>;
630630

631631
fn next(&mut self) -> Option<Self::Item> {
632-
self.object_iter.next().map(|(key, value)| Ok((key, value)))
632+
self.object_iter.next().map(|(key, value)| Ok((key.as_ref(), value)))
633633
}
634634
// size_hint is omitted as it isn't needed
635635
}
636636

637-
#[derive(Debug, Clone)]
638-
pub enum GenericIterator {
637+
#[derive(Debug)]
638+
pub enum GenericIterator<'a> {
639639
PyIterator(GenericPyIterator),
640-
JsonArray(GenericJsonIterator),
640+
JsonArray(GenericJsonIterator<'a>),
641641
}
642642

643-
impl From<JsonArray> for GenericIterator {
644-
fn from(array: JsonArray) -> Self {
643+
impl GenericIterator<'_> {
644+
pub(crate) fn into_static(self) -> GenericIterator<'static> {
645+
match self {
646+
GenericIterator::PyIterator(iter) => GenericIterator::PyIterator(iter),
647+
GenericIterator::JsonArray(iter) => GenericIterator::JsonArray(iter.into_static()),
648+
}
649+
}
650+
}
651+
652+
impl<'a> From<JsonArray<'a>> for GenericIterator<'a> {
653+
fn from(array: JsonArray<'a>) -> Self {
645654
let json_iter = GenericJsonIterator { array, index: 0 };
646655
Self::JsonArray(json_iter)
647656
}
648657
}
649658

650-
impl From<&Bound<'_, PyAny>> for GenericIterator {
659+
impl From<&Bound<'_, PyAny>> for GenericIterator<'_> {
651660
fn from(obj: &Bound<'_, PyAny>) -> Self {
652661
let py_iter = GenericPyIterator {
653662
obj: obj.clone().into(),
@@ -690,13 +699,13 @@ impl GenericPyIterator {
690699
}
691700

692701
#[derive(Debug, Clone)]
693-
pub struct GenericJsonIterator {
694-
array: JsonArray,
702+
pub struct GenericJsonIterator<'a> {
703+
array: JsonArray<'a>,
695704
index: usize,
696705
}
697706

698-
impl GenericJsonIterator {
699-
pub fn next(&mut self, _py: Python) -> PyResult<Option<(&JsonValue, usize)>> {
707+
impl<'a> GenericJsonIterator<'a> {
708+
pub fn next(&mut self, _py: Python) -> PyResult<Option<(&JsonValue<'a>, usize)>> {
700709
if self.index < self.array.len() {
701710
// panic here is impossible due to bounds check above; compiler should be
702711
// able to optimize it away even
@@ -710,12 +719,19 @@ impl GenericJsonIterator {
710719
}
711720

712721
pub fn input_as_error_value(&self, _py: Python<'_>) -> InputValue {
713-
InputValue::Json(JsonValue::Array(self.array.clone()))
722+
InputValue::Json(JsonValue::Array(self.array.clone()).into_static())
714723
}
715724

716725
pub fn index(&self) -> usize {
717726
self.index
718727
}
728+
729+
pub fn into_static(self) -> GenericJsonIterator<'static> {
730+
GenericJsonIterator {
731+
array: JsonArray::new(self.array.iter().map(|v| v.clone().into_static()).collect()),
732+
index: self.index,
733+
}
734+
}
719735
}
720736

721737
#[cfg_attr(debug_assertions, derive(Debug))]
@@ -732,12 +748,12 @@ impl<'py> PyArgs<'py> {
732748

733749
#[cfg_attr(debug_assertions, derive(Debug))]
734750
pub struct JsonArgs<'a> {
735-
pub args: Option<&'a [JsonValue]>,
736-
pub kwargs: Option<&'a JsonObject>,
751+
pub args: Option<&'a [JsonValue<'a>]>,
752+
pub kwargs: Option<&'a JsonObject<'a>>,
737753
}
738754

739755
impl<'a> JsonArgs<'a> {
740-
pub fn new(args: Option<&'a [JsonValue]>, kwargs: Option<&'a JsonObject>) -> Self {
756+
pub fn new(args: Option<&'a [JsonValue<'a>]>, kwargs: Option<&'a JsonObject<'a>>) -> Self {
741757
Self { args, kwargs }
742758
}
743759
}

src/lookup_key.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,10 @@ impl LookupKey {
262262

263263
pub fn json_get<'data, 's>(
264264
&'s self,
265-
dict: &'data JsonObject,
266-
) -> ValResult<Option<(&'s LookupPath, &'data JsonValue)>> {
265+
dict: &'data JsonObject<'data>,
266+
) -> ValResult<Option<(&'s LookupPath, &'data JsonValue<'data>)>> {
267267
match self {
268-
Self::Simple { key, path, .. } => match dict.get(key) {
268+
Self::Simple { key, path, .. } => match dict.get(key.as_str()) {
269269
Some(value) => Ok(Some((path, value))),
270270
None => Ok(None),
271271
},
@@ -275,9 +275,9 @@ impl LookupKey {
275275
key2,
276276
path2,
277277
..
278-
} => match dict.get(key1) {
278+
} => match dict.get(key1.as_str()) {
279279
Some(value) => Ok(Some((path1, value))),
280-
None => match dict.get(key2) {
280+
None => match dict.get(key2.as_str()) {
281281
Some(value) => Ok(Some((path2, value))),
282282
None => Ok(None),
283283
},
@@ -475,7 +475,7 @@ impl PathItem {
475475
}
476476
}
477477

478-
pub fn json_get<'a>(&self, any_json: &'a JsonValue) -> Option<&'a JsonValue> {
478+
pub fn json_get<'a>(&self, any_json: &'a JsonValue<'a>) -> Option<&'a JsonValue<'a>> {
479479
match any_json {
480480
JsonValue::Object(v_obj) => self.json_obj_get(v_obj),
481481
JsonValue::Array(v_array) => match self {
@@ -493,9 +493,9 @@ impl PathItem {
493493
}
494494
}
495495

496-
pub fn json_obj_get<'a>(&self, json_obj: &'a JsonObject) -> Option<&'a JsonValue> {
496+
pub fn json_obj_get<'a>(&self, json_obj: &'a JsonObject<'a>) -> Option<&'a JsonValue<'a>> {
497497
match self {
498-
Self::S(key, _) => json_obj.get(key),
498+
Self::S(key, _) => json_obj.get(key.as_str()),
499499
_ => None,
500500
}
501501
}

src/validators/generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl Validator for GeneratorValidator {
6565
input: &(impl Input<'py> + ?Sized),
6666
state: &mut ValidationState<'_, 'py>,
6767
) -> ValResult<PyObject> {
68-
let iterator = input.validate_iter()?;
68+
let iterator = input.validate_iter()?.into_static();
6969
let validator = self.item_validator.as_ref().map(|v| {
7070
InternalValidator::new(
7171
py,
@@ -96,7 +96,7 @@ impl Validator for GeneratorValidator {
9696
#[pyclass(module = "pydantic_core._pydantic_core")]
9797
#[derive(Debug)]
9898
struct ValidatorIterator {
99-
iterator: GenericIterator,
99+
iterator: GenericIterator<'static>,
100100
validator: Option<InternalValidator>,
101101
min_length: Option<usize>,
102102
max_length: Option<usize>,

0 commit comments

Comments
 (0)