|
6 | 6 | from typing import get_origin |
7 | 7 |
|
8 | 8 | from pydantic import AliasGenerator |
| 9 | +from pydantic import Base64Bytes |
9 | 10 | from pydantic import BaseModel as PydanticBaseModel |
10 | 11 | from pydantic import ConfigDict |
11 | 12 | from pydantic import FieldSerializationInfo |
|
20 | 21 | from pydantic_core import PydanticCustomError |
21 | 22 | from typing_extensions import Self |
22 | 23 |
|
| 24 | +from scim2_models.annotations import CaseExact |
23 | 25 | from scim2_models.annotations import Mutability |
24 | 26 | from scim2_models.annotations import Required |
25 | 27 | from scim2_models.annotations import Returned |
26 | 28 | from scim2_models.context import Context |
27 | 29 | from scim2_models.exceptions import MutabilityException |
| 30 | +from scim2_models.reference import Reference |
28 | 31 | from scim2_models.utils import UNION_TYPES |
29 | 32 | from scim2_models.utils import _find_field_name |
30 | 33 | from scim2_models.utils import _normalize_attribute_name |
@@ -145,15 +148,35 @@ def get_field_annotation(cls, field_name: str, annotation_type: type) -> Any: |
145 | 148 | """ |
146 | 149 | field_metadata = cls.model_fields[field_name].metadata |
147 | 150 |
|
148 | | - default_value = getattr(annotation_type, "_default", None) |
149 | | - |
150 | 151 | def annotation_type_filter(item: Any) -> bool: |
151 | 152 | return isinstance(item, annotation_type) |
152 | 153 |
|
153 | | - field_annotation = next( |
154 | | - filter(annotation_type_filter, field_metadata), default_value |
155 | | - ) |
156 | | - return field_annotation |
| 154 | + field_annotation = next(filter(annotation_type_filter, field_metadata), None) |
| 155 | + if field_annotation is not None: |
| 156 | + return field_annotation |
| 157 | + |
| 158 | + if annotation_type is CaseExact: |
| 159 | + return cls._default_case_exact(field_name) |
| 160 | + |
| 161 | + return getattr(annotation_type, "_default", None) |
| 162 | + |
| 163 | + @classmethod |
| 164 | + def _default_case_exact(cls, field_name: str) -> CaseExact: |
| 165 | + """Return the implicit case sensitivity of a field, based on its type. |
| 166 | +
|
| 167 | + :rfc:`RFC7643 §2.3.6 <7643#section-2.3.6>` and |
| 168 | + :rfc:`§2.3.7 <7643#section-2.3.7>` state that binary and reference |
| 169 | + values are case exact, whatever the schema representations of |
| 170 | + :rfc:`§8.7 <7643#section-8.7>` say. |
| 171 | + """ |
| 172 | + root_type = cls.get_field_root_type(field_name) |
| 173 | + if root_type == Base64Bytes: |
| 174 | + return CaseExact.true |
| 175 | + |
| 176 | + if isclass(root_type) and issubclass(root_type, Reference): |
| 177 | + return CaseExact.true |
| 178 | + |
| 179 | + return CaseExact.false |
157 | 180 |
|
158 | 181 | @classmethod |
159 | 182 | def get_field_root_type(cls, attribute_name: str) -> type | None: |
|
0 commit comments