Skip to content

Commit 1f18da2

Browse files
authored
jiter (#974)
1 parent 9d07a8c commit 1f18da2

30 files changed

+385
-489
lines changed

Cargo.lock

Lines changed: 85 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ base64 = "0.21.5"
4343
num-bigint = "0.4.4"
4444
python3-dll-a = "0.2.7"
4545
uuid = "1.5.0"
46+
jiter = {version = "0.0.4", features = ["python"]}
47+
#jiter = {path = "../jiter", features = ["python"]}
4648

4749
[lib]
4850
name = "_pydantic_core"

python/pydantic_core/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Url,
2323
ValidationError,
2424
__version__,
25+
from_json,
2526
to_json,
2627
to_jsonable_python,
2728
validate_core_schema,
@@ -63,6 +64,7 @@
6364
'PydanticSerializationUnexpectedValue',
6465
'TzInfo',
6566
'to_json',
67+
'from_json',
6668
'to_jsonable_python',
6769
'validate_core_schema',
6870
]

python/pydantic_core/_pydantic_core.pyi

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ __all__ = [
4141
'PydanticUndefinedType',
4242
'Some',
4343
'to_json',
44+
'from_json',
4445
'to_jsonable_python',
4546
'list_all_errors',
4647
'TzInfo',
@@ -384,6 +385,23 @@ def to_json(
384385
JSON bytes.
385386
"""
386387

388+
def from_json(data: str | bytes | bytearray, *, allow_inf_nan: bool = True) -> Any:
389+
"""
390+
Deserialize JSON data to a Python object.
391+
392+
This is effectively a faster version of [`json.loads()`][json.loads].
393+
394+
Arguments:
395+
data: The JSON data to deserialize.
396+
allow_inf_nan: Whether to allow `Infinity`, `-Infinity` and `NaN` values as `json.loads()` does by default.
397+
398+
Raises:
399+
ValueError: If deserialization fails.
400+
401+
Returns:
402+
The deserialized Python object.
403+
"""
404+
387405
def to_jsonable_python(
388406
value: Any,
389407
*,

src/errors/line_error.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use pyo3::exceptions::PyTypeError;
22
use pyo3::prelude::*;
33
use pyo3::PyDowncastError;
44

5-
use crate::input::{Input, JsonInput};
5+
use jiter::JsonValue;
6+
7+
use crate::input::Input;
68

79
use super::location::{LocItem, Location};
810
use super::types::ErrorType;
@@ -147,7 +149,7 @@ impl<'a> ValLineError<'a> {
147149
#[derive(Clone)]
148150
pub enum InputValue<'a> {
149151
PyAny(&'a PyAny),
150-
JsonInput(JsonInput),
152+
JsonInput(JsonValue),
151153
String(&'a str),
152154
}
153155

src/errors/location.rs

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ use pyo3::once_cell::GILOnceCell;
33
use std::fmt;
44

55
use pyo3::prelude::*;
6-
use pyo3::types::{PyList, PyString, PyTuple};
6+
use pyo3::types::{PyList, PyTuple};
77
use serde::ser::SerializeSeq;
88
use serde::{Serialize, Serializer};
99

1010
use crate::lookup_key::{LookupPath, PathItem};
11-
use crate::tools::extract_i64;
1211

1312
/// Used to store individual items of the error location, e.g. a string for key/field names
1413
/// or a number for array indices.
@@ -35,6 +34,12 @@ impl fmt::Display for LocItem {
3534
}
3635
}
3736

37+
// TODO rename to ToLocItem
38+
pub trait AsLocItem {
39+
// TODO rename to to_loc_item
40+
fn as_loc_item(&self) -> LocItem;
41+
}
42+
3843
impl From<String> for LocItem {
3944
fn from(s: String) -> Self {
4045
Self::S(s)
@@ -82,21 +87,6 @@ impl ToPyObject for LocItem {
8287
}
8388
}
8489

85-
impl TryFrom<&PyAny> for LocItem {
86-
type Error = PyErr;
87-
88-
fn try_from(loc_item: &PyAny) -> PyResult<Self> {
89-
if let Ok(py_str) = loc_item.downcast::<PyString>() {
90-
let str = py_str.to_str()?.to_string();
91-
Ok(Self::S(str))
92-
} else if let Ok(int) = extract_i64(loc_item) {
93-
Ok(Self::I(int))
94-
} else {
95-
Err(PyTypeError::new_err("Item in a location must be a string or int"))
96-
}
97-
}
98-
}
99-
10090
impl Serialize for LocItem {
10191
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
10292
where
@@ -211,9 +201,9 @@ impl TryFrom<Option<&PyAny>> for Location {
211201
fn try_from(location: Option<&PyAny>) -> PyResult<Self> {
212202
if let Some(location) = location {
213203
let mut loc_vec: Vec<LocItem> = if let Ok(tuple) = location.downcast::<PyTuple>() {
214-
tuple.iter().map(LocItem::try_from).collect::<PyResult<_>>()?
204+
tuple.iter().map(AsLocItem::as_loc_item).collect()
215205
} else if let Ok(list) = location.downcast::<PyList>() {
216-
list.iter().map(LocItem::try_from).collect::<PyResult<_>>()?
206+
list.iter().map(AsLocItem::as_loc_item).collect()
217207
} else {
218208
return Err(PyTypeError::new_err(
219209
"Location must be a list or tuple of strings and ints",

src/errors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod validation_exception;
77
mod value_exception;
88

99
pub use self::line_error::{InputValue, ValError, ValLineError, ValResult};
10-
pub use self::location::LocItem;
10+
pub use self::location::{AsLocItem, LocItem};
1111
pub use self::types::{list_all_errors, ErrorType, ErrorTypeDefaults, Number};
1212
pub use self::validation_exception::ValidationError;
1313
pub use self::value_exception::{PydanticCustomError, PydanticKnownError, PydanticOmit, PydanticUseDefault};

src/input/input_abstract.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ use pyo3::exceptions::PyValueError;
44
use pyo3::types::{PyDict, PyType};
55
use pyo3::{intern, prelude::*};
66

7-
use crate::errors::{InputValue, LocItem, ValResult};
7+
use jiter::JsonValue;
8+
9+
use crate::errors::{AsLocItem, InputValue, ValResult};
810
use crate::tools::py_err;
911
use crate::{PyMultiHostUrl, PyUrl};
1012

1113
use super::datetime::{EitherDate, EitherDateTime, EitherTime, EitherTimedelta};
1214
use super::return_enums::{EitherBytes, EitherInt, EitherString};
13-
use super::{EitherFloat, GenericArguments, GenericIterable, GenericIterator, GenericMapping, JsonInput};
15+
use super::{EitherFloat, GenericArguments, GenericIterable, GenericIterator, GenericMapping};
1416

1517
#[derive(Debug, Clone, Copy)]
1618
pub enum InputType {
@@ -46,9 +48,7 @@ impl TryFrom<&str> for InputType {
4648
/// the convention is to either implement:
4749
/// * `strict_*` & `lax_*` if they have different behavior
4850
/// * or, `validate_*` and `strict_*` to just call `validate_*` if the behavior for strict and lax is the same
49-
pub trait Input<'a>: fmt::Debug + ToPyObject {
50-
fn as_loc_item(&self) -> LocItem;
51-
51+
pub trait Input<'a>: fmt::Debug + ToPyObject + AsLocItem {
5252
fn as_error_value(&'a self) -> InputValue<'a>;
5353

5454
fn identity(&self) -> Option<usize> {
@@ -89,7 +89,7 @@ pub trait Input<'a>: fmt::Debug + ToPyObject {
8989

9090
fn validate_dataclass_args(&'a self, dataclass_name: &str) -> ValResult<'a, GenericArguments<'a>>;
9191

92-
fn parse_json(&'a self) -> ValResult<'a, JsonInput>;
92+
fn parse_json(&'a self) -> ValResult<'a, JsonValue>;
9393

9494
fn validate_str(&'a self, strict: bool, coerce_numbers_to_str: bool) -> ValResult<EitherString<'a>> {
9595
if strict {

0 commit comments

Comments
 (0)