Skip to content

Commit 59f3a8e

Browse files
committed
allow bytes and bytearray to json
1 parent 9899af5 commit 59f3a8e

File tree

3 files changed

+34
-11
lines changed

3 files changed

+34
-11
lines changed

pydantic_core/_pydantic_core.pyi

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, List
1+
from typing import Any, Dict, List, Union
22

33
from pydantic_core._types import Schema
44

@@ -8,8 +8,8 @@ class SchemaValidator:
88
def __init__(self, schema: Schema) -> None: ...
99
def validate_python(self, input: Any) -> Any: ...
1010
def isinstance_python(self, input: Any) -> bool: ...
11-
def validate_json(self, input: str) -> Any: ...
12-
def isinstance_json(self, input: str) -> bool: ...
11+
def validate_json(self, input: Union[str, bytes]) -> Any: ...
12+
def isinstance_json(self, input: Union[str, bytes]) -> bool: ...
1313
def validate_assignment(self, field: str, input: Any, data: Dict[str, Any]) -> Dict[str, Any]: ...
1414

1515
class SchemaError(ValueError):

src/validators/mod.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ use std::fmt::Debug;
22

33
use enum_dispatch::enum_dispatch;
44

5-
use pyo3::exceptions::PyRecursionError;
5+
use pyo3::exceptions::{PyRecursionError, PyTypeError};
66
use pyo3::prelude::*;
7-
use pyo3::types::{PyAny, PyDict};
8-
use serde_json::from_str as parse_json;
7+
use pyo3::types::{PyAny, PyByteArray, PyBytes, PyDict, PyString};
98

109
use crate::build_tools::{py_error, SchemaDict, SchemaError};
1110
use crate::errors::{ErrorKind, ValError, ValLineError, ValResult, ValidationError};
@@ -99,8 +98,8 @@ impl SchemaValidator {
9998
}
10099
}
101100

102-
pub fn validate_json(&self, py: Python, input: String) -> PyResult<PyObject> {
103-
match parse_json::<JsonInput>(&input) {
101+
pub fn validate_json(&self, py: Python, input: &PyAny) -> PyResult<PyObject> {
102+
match parse_json(input)? {
104103
Ok(input) => {
105104
let r = self.validator.validate(
106105
py,
@@ -112,15 +111,15 @@ impl SchemaValidator {
112111
r.map_err(|e| self.prepare_validation_err(py, e))
113112
}
114113
Err(e) => {
115-
let line_err = ValLineError::new(ErrorKind::InvalidJson { error: e.to_string() }, &input);
114+
let line_err = ValLineError::new(ErrorKind::InvalidJson { error: e.to_string() }, input);
116115
let err = ValError::LineErrors(vec![line_err]);
117116
Err(self.prepare_validation_err(py, err))
118117
}
119118
}
120119
}
121120

122-
pub fn isinstance_json(&self, py: Python, input: String) -> PyResult<bool> {
123-
match parse_json::<JsonInput>(&input) {
121+
pub fn isinstance_json(&self, py: Python, input: &PyAny) -> PyResult<bool> {
122+
match parse_json(input)? {
124123
Ok(input) => {
125124
match self.validator.validate(
126125
py,
@@ -164,6 +163,18 @@ impl SchemaValidator {
164163
}
165164
}
166165

166+
fn parse_json(input: &PyAny) -> PyResult<serde_json::Result<JsonInput>> {
167+
if let Ok(py_bytes) = input.cast_as::<PyBytes>() {
168+
Ok(serde_json::from_slice(py_bytes.as_bytes()))
169+
} else if let Ok(py_str) = input.cast_as::<PyString>() {
170+
Ok(serde_json::from_str(&py_str.to_string_lossy()))
171+
} else if let Ok(py_byte_array) = input.cast_as::<PyByteArray>() {
172+
Ok(serde_json::from_slice(unsafe { py_byte_array.as_bytes() }))
173+
} else {
174+
Err(PyTypeError::new_err("JSON input must be str, bytes or bytearray"))
175+
}
176+
}
177+
167178
pub trait BuildValidator: Sized {
168179
const EXPECTED_TYPE: &'static str;
169180

tests/test_json.py

+12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ def test_bool(input_value, output_value):
1212
assert v.validate_json(input_value) == output_value
1313

1414

15+
@pytest.mark.parametrize('input_value', ['[1, 2, 3]', b'[1, 2, 3]', bytearray(b'[1, 2, 3]')])
16+
def test_input_types(input_value):
17+
v = SchemaValidator({'type': 'list', 'items_schema': {'type': 'int'}})
18+
assert v.validate_json(input_value) == [1, 2, 3]
19+
20+
21+
def test_input_type_invalid():
22+
v = SchemaValidator({'type': 'list', 'items_schema': {'type': 'int'}})
23+
with pytest.raises(TypeError, match='^JSON input must be str, bytes or bytearray$'):
24+
v.validate_json([])
25+
26+
1527
def test_null():
1628
assert SchemaValidator({'type': 'none'}).validate_json('null') is None
1729

0 commit comments

Comments
 (0)