1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
import re
from typing import Any
import pytest
from pydantic_core import SchemaValidator, ValidationError
from pydantic_core import core_schema as cs
from ..conftest import Err, PyAndJson
def test_strict_bytes_validator():
v = SchemaValidator(cs.bytes_schema(strict=True))
assert v.validate_python(b'foo') == b'foo'
assert v.validate_json('"foo"') == b'foo'
with pytest.raises(ValidationError, match='Input should be a valid bytes'):
v.validate_python('foo')
with pytest.raises(ValidationError, match='Input should be a valid bytes'):
v.validate_python(bytearray(b'foo'))
def test_lax_bytes_validator():
v = SchemaValidator(cs.bytes_schema())
assert v.validate_python(b'foo') == b'foo'
assert v.validate_python('foo') == b'foo'
assert v.validate_python(bytearray(b'foo')) == b'foo'
assert v.validate_json('"foo"') == b'foo'
assert v.validate_python('🐈 Hello') == b'\xf0\x9f\x90\x88 Hello'
# `.to_str()` Returns a `UnicodeEncodeError` if the input is not valid unicode (containing unpaired surrogates).
# https://github.com/PyO3/pyo3/blob/6503128442b8f3e767c663a6a8d96376d7fb603d/src/types/string.rs#L477
with pytest.raises(ValidationError) as exc_info:
v.validate_python('🐈 Hello \ud800World')
assert exc_info.value.errors(include_url=False) == [
{
'type': 'string_unicode',
'loc': (),
'msg': 'Input should be a valid string, unable to parse raw data as a unicode string',
'input': '🐈 Hello \ud800World',
}
]
@pytest.mark.parametrize(
'opts,input,expected',
[
({}, b'foo', b'foo'),
({'max_length': 5}, b'foo', b'foo'),
({'max_length': 5}, b'foobar', Err('Data should have at most 5 bytes')),
({'min_length': 2}, b'foo', b'foo'),
({'min_length': 2}, b'f', Err('Data should have at least 2 bytes')),
({'min_length': 1, 'max_length': 6, 'strict': True}, b'bytes?', b'bytes?'),
],
)
def test_constrained_bytes_python_bytes(opts: dict[str, Any], input, expected):
v = SchemaValidator(cs.bytes_schema(**opts))
if isinstance(expected, Err):
with pytest.raises(ValidationError, match=re.escape(expected.message)):
v.validate_python(input)
else:
assert v.validate_python(input) == expected
@pytest.mark.parametrize(
'opts,input,expected',
[
({}, 'foo', b'foo'),
({'max_length': 5}, 'foo', b'foo'),
({'max_length': 5}, 'foobar', Err('Data should have at most 5 bytes')),
({'min_length': 2}, 'foo', b'foo'),
({'min_length': 2}, 'f', Err('Data should have at least 2 bytes')),
({}, 1, Err('Input should be a valid bytes')),
({}, 1.0, Err('Input should be a valid bytes')),
({}, [], Err('Input should be a valid bytes')),
({}, {}, Err('Input should be a valid bytes')),
],
)
def test_constrained_bytes(py_and_json: PyAndJson, opts: dict[str, Any], input, expected):
v = py_and_json({'type': 'bytes', **opts})
if isinstance(expected, Err):
with pytest.raises(ValidationError, match=re.escape(expected.message)):
v.validate_test(input)
assert v.isinstance_test(input) is False
else:
assert v.validate_test(input) == expected
assert v.isinstance_test(input) is True
def test_union():
v = SchemaValidator(cs.union_schema(choices=[cs.str_schema(strict=True), cs.bytes_schema(strict=True)]))
assert v.validate_python('oh, a string') == 'oh, a string'
assert v.validate_python(b'oh, bytes') == b'oh, bytes'
def test_length_ctx():
v = SchemaValidator(cs.bytes_schema(min_length=2, max_length=3))
with pytest.raises(ValidationError) as exc_info:
v.validate_python(b'1')
assert exc_info.value.errors(include_url=False) == [
{
'type': 'bytes_too_short',
'loc': (),
'msg': 'Data should have at least 2 bytes',
'input': b'1',
'ctx': {'min_length': 2},
}
]
with pytest.raises(ValidationError) as exc_info:
v.validate_python(b'1234')
assert exc_info.value.errors(include_url=False) == [
{
'type': 'bytes_too_long',
'loc': (),
'msg': 'Data should have at most 3 bytes',
'input': b'1234',
'ctx': {'max_length': 3},
}
]
|