Skip to content

Commit 34f3b37

Browse files
committed
fix: Base64Bytes compatibility with all supported pydantic versions
The behavior changed with Pydantic 2.10, which broke some unit tests. https://pydantic.dev/articles/pydantic-v2-10-release#use-b64decode-and-b64encode-for-base64bytes-and-base64str-types
1 parent bcf08ef commit 34f3b37

File tree

5 files changed

+71
-4
lines changed

5 files changed

+71
-4
lines changed

doc/changelog.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Changelog
22
=========
33

4+
[0.2.10] - 2024-12-02
5+
---------------------
6+
7+
Changed
8+
^^^^^^^
9+
- The ``schema`` attribute is annotated with :attr:`~scim2_models.Required.true`.
10+
11+
Fixed
12+
^^^^^
13+
- ``Base64Bytes`` compatibility between pydantic 2.10+ and <2.10
14+
415
[0.2.9] - 2024-12-02
516
--------------------
617

scim2_models/rfc7643/schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from typing import Union
1010
from typing import get_origin
1111

12-
from pydantic import Base64Bytes
1312
from pydantic import Field
1413
from pydantic import create_model
1514
from pydantic import field_validator
@@ -30,6 +29,7 @@
3029
from ..base import URIReference
3130
from ..base import is_complex_attribute
3231
from ..constants import RESERVED_WORDS
32+
from ..utils import Base64Bytes
3333
from ..utils import normalize_attribute_name
3434
from .resource import Extension
3535
from .resource import Resource

scim2_models/rfc7643/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import Optional
55
from typing import Union
66

7-
from pydantic import Base64Bytes
87
from pydantic import EmailStr
98
from pydantic import Field
109

@@ -17,6 +16,7 @@
1716
from ..base import Required
1817
from ..base import Returned
1918
from ..base import Uniqueness
19+
from ..utils import Base64Bytes
2020
from .resource import Resource
2121

2222

scim2_models/utils.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import base64
12
import re
3+
from typing import Annotated
4+
from typing import Literal
25
from typing import Optional
36
from typing import Union
47

8+
from pydantic import EncodedBytes
9+
from pydantic import EncoderProtocol
510
from pydantic.alias_generators import to_snake
11+
from pydantic_core import PydanticCustomError
612

713
try:
814
from types import UnionType # type: ignore
@@ -17,6 +23,57 @@ def int_to_str(status: Optional[int]) -> Optional[str]:
1723
return None if status is None else str(status)
1824

1925

26+
# Copied from Pydantic 2.10 repository
27+
class Base64Encoder(EncoderProtocol): # pragma: no cover
28+
"""Standard (non-URL-safe) Base64 encoder."""
29+
30+
@classmethod
31+
def decode(cls, data: bytes) -> bytes:
32+
"""Decode the data from base64 encoded bytes to original bytes data.
33+
34+
Args:
35+
data: The data to decode.
36+
37+
Returns:
38+
The decoded data.
39+
40+
"""
41+
try:
42+
return base64.b64decode(data)
43+
except ValueError as e:
44+
raise PydanticCustomError(
45+
"base64_decode", "Base64 decoding error: '{error}'", {"error": str(e)}
46+
) from e
47+
48+
@classmethod
49+
def encode(cls, value: bytes) -> bytes:
50+
"""Encode the data from bytes to a base64 encoded bytes.
51+
52+
Args:
53+
value: The data to encode.
54+
55+
Returns:
56+
The encoded data.
57+
58+
"""
59+
return base64.b64encode(value)
60+
61+
@classmethod
62+
def get_json_format(cls) -> Literal["base64"]:
63+
"""Get the JSON format for the encoded data.
64+
65+
Returns:
66+
The JSON format for the encoded data.
67+
68+
"""
69+
return "base64"
70+
71+
72+
# Compatibility with Pydantic <2.10
73+
# https://pydantic.dev/articles/pydantic-v2-10-release#use-b64decode-and-b64encode-for-base64bytes-and-base64str-types
74+
Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)]
75+
76+
2077
def to_camel(string: str) -> str:
2178
"""Transform strings to camelCase.
2279

tests/test_dynamic_resources.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
from typing import Literal
33
from typing import Union
44

5-
from pydantic import Base64Bytes
6-
75
from scim2_models.base import CaseExact
86
from scim2_models.base import ComplexAttribute
97
from scim2_models.base import ExternalReference
@@ -18,6 +16,7 @@
1816
from scim2_models.rfc7643.resource import Resource
1917
from scim2_models.rfc7643.schema import Attribute
2018
from scim2_models.rfc7643.schema import Schema
19+
from scim2_models.utils import Base64Bytes
2120

2221

2322
def test_make_group_model_from_schema(load_sample):

0 commit comments

Comments
 (0)