Skip to content

Commit

Permalink
Add tests anticipating pydantic/pydantic-core#1308
Browse files Browse the repository at this point in the history
  • Loading branch information
josh-newman committed Jun 26, 2024
1 parent f024d03 commit 259b0b8
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
5 changes: 4 additions & 1 deletion pydantic/_internal/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class ConfigWrapper:
# whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never'
revalidate_instances: Literal['always', 'never', 'subclass-instances']
ser_json_timedelta: Literal['iso8601', 'float']
ser_json_bytes: Literal['utf8', 'base64']
ser_json_bytes: Literal['utf8', 'base64', 'hex']
val_json_bytes: Literal['utf8', 'base64', 'hex']
ser_json_inf_nan: Literal['null', 'constants']
# whether to validate default values during validation, default False
validate_default: bool
Expand Down Expand Up @@ -182,6 +183,7 @@ def dict_not_none(**kwargs: Any) -> Any:
strict=self.config_dict.get('strict'),
ser_json_timedelta=self.config_dict.get('ser_json_timedelta'),
ser_json_bytes=self.config_dict.get('ser_json_bytes'),
val_json_bytes=self.config_dict.get('val_json_bytes'),
ser_json_inf_nan=self.config_dict.get('ser_json_inf_nan'),
from_attributes=self.config_dict.get('from_attributes'),
loc_by_alias=self.config_dict.get('loc_by_alias'),
Expand Down Expand Up @@ -255,6 +257,7 @@ def push(self, config_wrapper: ConfigWrapper | ConfigDict | None):
revalidate_instances='never',
ser_json_timedelta='iso8601',
ser_json_bytes='utf8',
val_json_bytes='utf8',
ser_json_inf_nan='null',
validate_default=False,
validate_return=False,
Expand Down
17 changes: 14 additions & 3 deletions pydantic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,13 +572,24 @@ class Transaction(BaseModel):
- `'float'` will serialize timedeltas to the total number of seconds.
"""

ser_json_bytes: Literal['utf8', 'base64']
ser_json_bytes: Literal['utf8', 'base64', 'hex']
"""
The encoding of JSON serialized bytes. Accepts the string values of `'utf8'` and `'base64'`.
Defaults to `'utf8'`.
The encoding of JSON serialized bytes. Defaults to `'utf8'`.
Set equal to `val_json_bytes` to get back an equal value after serialization round trip.
- `'utf8'` will serialize bytes to UTF-8 strings.
- `'base64'` will serialize bytes to URL safe base64 strings.
- `'hex'` will serialize bytes to hexadecimal strings.
"""

val_json_bytes: Literal['utf8', 'base64', 'hex']
"""
The encoding of JSON serialized bytes to decode. Defaults to `'utf8'`.
Set equal to `ser_json_bytes` to get back an equal value after serialization round trip.
- `'utf8'` will deserialize UTF-8 strings to bytes.
- `'base64'` will deserialize URL safe base64 strings to bytes.
- `'hex'` will deserialize hexadecimal strings to bytes.
"""

ser_json_inf_nan: Literal['null', 'constants']
Expand Down
43 changes: 43 additions & 0 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
GetJsonSchemaHandler,
NameEmail,
PlainSerializer,
RootModel,
)
from pydantic._internal._config import ConfigWrapper
from pydantic._internal._generate_schema import GenerateSchema
Expand Down Expand Up @@ -500,3 +501,45 @@ class A(BaseModel):
m = A(a=MyEnum.A, b=[1, 2, 3], c=Decimal('0'))
assert m.model_dump_json() == '{"a":"A","b":"list!","c":"decimal!"}'
assert m.model_dump() == {'a': MyEnum.A, 'b': [1, 2, 3], 'c': Decimal('0')}


# Expect failure until pydantic-core is upgraded to include pydantic/pydantic-core#1308.
@pytest.mark.xfail
def test_json_bytes_base64_round_trip():
class R(RootModel[bytes]):
model_config = ConfigDict(ser_json_bytes='base64', val_json_bytes='base64')

r = R(b'hello')
r_encoded = '"aGVsbG8="'
assert r.model_dump_json() == r_encoded
assert R.model_validate_json(r_encoded) == r

class M(BaseModel):
key: bytes
model_config = R.model_config

m = M(key=b'hello')
m_encoded = f'{{"key":{r_encoded}}}'
assert m.model_dump_json() == m_encoded
assert M.model_validate_json(m_encoded) == m


# Expect failure until pydantic-core is upgraded to include pydantic/pydantic-core#1308.
@pytest.mark.xfail
def test_json_bytes_hex_round_trip():
class R(RootModel[bytes]):
model_config = ConfigDict(ser_json_bytes='hex', val_json_bytes='hex')

r = R(b'hello')
r_encoded = '"68656c6c6f"'
assert r.model_dump_json() == r_encoded
assert R.model_validate_json(r_encoded) == r

class M(BaseModel):
key: bytes
model_config = R.model_config

m = M(key=b'hello')
m_encoded = f'{{"key":{r_encoded}}}'
assert m.model_dump_json() == m_encoded
assert M.model_validate_json(m_encoded) == m

0 comments on commit 259b0b8

Please sign in to comment.