Skip to content

Commit a827216

Browse files
committed
refactor: cleary separate the public and private API
1 parent 6264760 commit a827216

18 files changed

+177
-171
lines changed

scim2_models/base.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
from scim2_models.annotations import Required
2424
from scim2_models.annotations import Returned
2525
from scim2_models.context import Context
26-
from scim2_models.utils import UNION_TYPES
27-
from scim2_models.utils import find_field_name
28-
from scim2_models.utils import normalize_attribute_name
29-
from scim2_models.utils import to_camel
26+
from scim2_models.utils import _UNION_TYPES
27+
from scim2_models.utils import _find_field_name
28+
from scim2_models.utils import _normalize_attribute_name
29+
from scim2_models.utils import _to_camel
3030

3131

32-
def contains_attribute_or_subattributes(
32+
def _contains_attribute_or_subattributes(
3333
attribute_urns: list[str], attribute_urn: str
3434
) -> bool:
3535
return attribute_urn in attribute_urns or any(
@@ -43,8 +43,8 @@ class BaseModel(PydanticBaseModel):
4343

4444
model_config = ConfigDict(
4545
alias_generator=AliasGenerator(
46-
validation_alias=normalize_attribute_name,
47-
serialization_alias=to_camel,
46+
validation_alias=_normalize_attribute_name,
47+
serialization_alias=_to_camel,
4848
),
4949
validate_assignment=True,
5050
populate_by_name=True,
@@ -77,7 +77,7 @@ def get_field_root_type(cls, attribute_name: str) -> Optional[type]:
7777
attribute_type = cls.model_fields[attribute_name].annotation
7878

7979
# extract 'x' from 'Optional[x]'
80-
if get_origin(attribute_type) in UNION_TYPES:
80+
if get_origin(attribute_type) in _UNION_TYPES:
8181
attribute_type = get_args(attribute_type)[0]
8282

8383
# extract 'x' from 'List[x]'
@@ -93,7 +93,7 @@ def get_field_multiplicity(cls, attribute_name: str) -> bool:
9393
attribute_type = cls.model_fields[attribute_name].annotation
9494

9595
# extract 'x' from 'Optional[x]'
96-
if get_origin(attribute_type) in UNION_TYPES:
96+
if get_origin(attribute_type) in _UNION_TYPES:
9797
attribute_type = get_args(attribute_type)[0]
9898

9999
origin = get_origin(attribute_type)
@@ -159,7 +159,7 @@ def normalize_dict_keys(
159159
result = {}
160160

161161
for key, val in input_dict.items():
162-
field_name = find_field_name(model_class, key)
162+
field_name = _find_field_name(model_class, key)
163163
field_type = (
164164
model_class.get_field_root_type(field_name) if field_name else None
165165
)
@@ -170,7 +170,7 @@ def normalize_dict_keys(
170170
if field_name and field_type == Any:
171171
result[key] = normalize_value(val)
172172
else:
173-
result[normalize_attribute_name(key)] = normalize_value(
173+
result[_normalize_attribute_name(key)] = normalize_value(
174174
val, field_type
175175
)
176176

@@ -285,11 +285,11 @@ def check_replacement_request_mutability(
285285
and issubclass(cls, Resource)
286286
and original is not None
287287
):
288-
cls.check_mutability_issues(original, obj)
288+
cls._check_mutability_issues(original, obj)
289289
return obj
290290

291291
@classmethod
292-
def check_mutability_issues(
292+
def _check_mutability_issues(
293293
cls, original: "BaseModel", replacement: "BaseModel"
294294
) -> None:
295295
"""Compare two instances, and check for differences of values on the fields marked as immutable."""
@@ -316,9 +316,9 @@ def check_mutability_issues(
316316
original_val = getattr(original, field_name)
317317
replacement_value = getattr(replacement, field_name)
318318
if original_val is not None and replacement_value is not None:
319-
cls.check_mutability_issues(original_val, replacement_value)
319+
cls._check_mutability_issues(original_val, replacement_value)
320320

321-
def set_complex_attribute_urns(self) -> None:
321+
def _set_complex_attribute_urns(self) -> None:
322322
"""Navigate through attributes and sub-attributes of type ComplexAttribute, and mark them with a '_attribute_urn' attribute.
323323
324324
'_attribute_urn' will later be used by 'get_attribute_urn'.
@@ -359,14 +359,14 @@ def scim_serializer(
359359
scim_ctx = info.context.get("scim") if info.context else None
360360

361361
if scim_ctx and Context.is_request(scim_ctx):
362-
value = self.scim_request_serializer(value, info)
362+
value = self._scim_request_serializer(value, info)
363363

364364
if scim_ctx and Context.is_response(scim_ctx):
365-
value = self.scim_response_serializer(value, info)
365+
value = self._scim_response_serializer(value, info)
366366

367367
return value
368368

369-
def scim_request_serializer(self, value: Any, info: FieldSerializationInfo) -> Any:
369+
def _scim_request_serializer(self, value: Any, info: FieldSerializationInfo) -> Any:
370370
"""Serialize the fields according to mutability indications passed in the serialization context."""
371371
mutability = self.get_field_annotation(info.field_name, Mutability)
372372
scim_ctx = info.context.get("scim") if info.context else None
@@ -390,7 +390,9 @@ def scim_request_serializer(self, value: Any, info: FieldSerializationInfo) -> A
390390

391391
return value
392392

393-
def scim_response_serializer(self, value: Any, info: FieldSerializationInfo) -> Any:
393+
def _scim_response_serializer(
394+
self, value: Any, info: FieldSerializationInfo
395+
) -> Any:
394396
"""Serialize the fields according to returnability indications passed in the serialization context."""
395397
returnability = self.get_field_annotation(info.field_name, Returned)
396398
attribute_urn = self.get_attribute_urn(info.field_name)
@@ -399,17 +401,17 @@ def scim_response_serializer(self, value: Any, info: FieldSerializationInfo) ->
399401
info.context.get("scim_excluded_attributes", []) if info.context else []
400402
)
401403

402-
attribute_urn = normalize_attribute_name(attribute_urn)
403-
included_urns = [normalize_attribute_name(urn) for urn in included_urns]
404-
excluded_urns = [normalize_attribute_name(urn) for urn in excluded_urns]
404+
attribute_urn = _normalize_attribute_name(attribute_urn)
405+
included_urns = [_normalize_attribute_name(urn) for urn in included_urns]
406+
excluded_urns = [_normalize_attribute_name(urn) for urn in excluded_urns]
405407

406408
if returnability == Returned.never:
407409
return None
408410

409411
if returnability == Returned.default and (
410412
(
411413
included_urns
412-
and not contains_attribute_or_subattributes(
414+
and not _contains_attribute_or_subattributes(
413415
included_urns, attribute_urn
414416
)
415417
)
@@ -427,7 +429,7 @@ def model_serializer_exclude_none(
427429
self, handler: SerializerFunctionWrapHandler, info: SerializationInfo
428430
) -> dict[str, Any]:
429431
"""Remove `None` values inserted by the :meth:`~scim2_models.base.BaseModel.scim_serializer`."""
430-
self.set_complex_attribute_urns()
432+
self._set_complex_attribute_urns()
431433
result = handler(self)
432434
return {key: value for key, value in result.items() if value is not None}
433435

scim2_models/messages/bulk.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from ..annotations import Required
1010
from ..attributes import ComplexAttribute
11-
from ..utils import int_to_str
11+
from ..utils import _int_to_str
1212
from .message import Message
1313

1414

@@ -42,7 +42,7 @@ class Method(str, Enum):
4242
response: Optional[Any] = None
4343
"""The HTTP response body for the specified request operation."""
4444

45-
status: Annotated[Optional[int], PlainSerializer(int_to_str)] = None
45+
status: Annotated[Optional[int], PlainSerializer(_int_to_str)] = None
4646
"""The HTTP response status code for the requested operation."""
4747

4848

scim2_models/messages/error.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pydantic import PlainSerializer
55

66
from ..annotations import Required
7-
from ..utils import int_to_str
7+
from ..utils import _int_to_str
88
from .message import Message
99

1010

@@ -15,7 +15,7 @@ class Error(Message):
1515
"urn:ietf:params:scim:api:messages:2.0:Error"
1616
]
1717

18-
status: Annotated[Optional[int], PlainSerializer(int_to_str)] = None
18+
status: Annotated[Optional[int], PlainSerializer(_int_to_str)] = None
1919
"""The HTTP status code (see Section 6 of [RFC7231]) expressed as a JSON
2020
string."""
2121

scim2_models/messages/list_response.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
from ..annotations import Required
1414
from ..context import Context
1515
from ..resources.resource import AnyResource
16-
from .message import GenericMessageMetaclass
1716
from .message import Message
17+
from .message import _GenericMessageMetaclass
1818

1919

20-
class ListResponse(Message, Generic[AnyResource], metaclass=GenericMessageMetaclass):
20+
class ListResponse(Message, Generic[AnyResource], metaclass=_GenericMessageMetaclass):
2121
schemas: Annotated[list[str], Required.true] = [
2222
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
2323
]

scim2_models/messages/message.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414

1515
from ..base import BaseModel
1616
from ..scim_object import ScimObject
17-
from ..utils import UNION_TYPES
17+
from ..utils import _UNION_TYPES
1818

1919

2020
class Message(ScimObject):
2121
"""SCIM protocol messages as defined by :rfc:`RFC7644 §3.1 <7644#section-3.1>`."""
2222

2323

24-
def create_schema_discriminator(
24+
def _create_schema_discriminator(
2525
resource_types_schemas: list[str],
2626
) -> Callable[[Any], Optional[str]]:
2727
"""Create a schema discriminator function for the given resource schemas.
@@ -51,7 +51,7 @@ def get_schema_from_payload(payload: Any) -> Optional[str]:
5151
return get_schema_from_payload
5252

5353

54-
def get_tag(resource_type: type[BaseModel]) -> Tag:
54+
def _get_tag(resource_type: type[BaseModel]) -> Tag:
5555
"""Create Pydantic tag from resource type schema.
5656
5757
:param resource_type: SCIM resource type
@@ -60,7 +60,7 @@ def get_tag(resource_type: type[BaseModel]) -> Tag:
6060
return Tag(resource_type.model_fields["schemas"].default[0])
6161

6262

63-
def create_tagged_resource_union(resource_union: Any) -> Any:
63+
def _create_tagged_resource_union(resource_union: Any) -> Any:
6464
"""Build Discriminated Unions for SCIM resources.
6565
6666
Creates discriminated unions so Pydantic can determine which class to instantiate
@@ -69,7 +69,7 @@ def create_tagged_resource_union(resource_union: Any) -> Any:
6969
:param resource_union: Union type of SCIM resources
7070
:return: Annotated discriminated union or original type
7171
"""
72-
if get_origin(resource_union) not in UNION_TYPES:
72+
if get_origin(resource_union) not in _UNION_TYPES:
7373
return resource_union
7474

7575
resource_types = get_args(resource_union)
@@ -81,19 +81,19 @@ def create_tagged_resource_union(resource_union: Any) -> Any:
8181
]
8282

8383
# Create discriminator function with schemas captured in closure
84-
schema_discriminator = create_schema_discriminator(resource_types_schemas)
84+
schema_discriminator = _create_schema_discriminator(resource_types_schemas)
8585
discriminator = Discriminator(schema_discriminator)
8686

8787
tagged_resources = [
88-
Annotated[resource_type, get_tag(resource_type)]
88+
Annotated[resource_type, _get_tag(resource_type)]
8989
for resource_type in resource_types
9090
]
9191
# Dynamic union construction from tuple - MyPy can't validate this at compile time
9292
union = Union[tuple(tagged_resources)] # type: ignore
9393
return Annotated[union, discriminator]
9494

9595

96-
class GenericMessageMetaclass(ModelMetaclass):
96+
class _GenericMessageMetaclass(ModelMetaclass):
9797
"""Metaclass for SCIM generic types with discriminated unions."""
9898

9999
def __new__(
@@ -103,7 +103,7 @@ def __new__(
103103
if kwargs.get("__pydantic_generic_metadata__") and kwargs[
104104
"__pydantic_generic_metadata__"
105105
].get("args"):
106-
tagged_union = create_tagged_resource_union(
106+
tagged_union = _create_tagged_resource_union(
107107
kwargs["__pydantic_generic_metadata__"]["args"][0]
108108
)
109109
kwargs["__pydantic_generic_metadata__"]["args"] = (tagged_union,)
@@ -112,7 +112,7 @@ def __new__(
112112
return klass
113113

114114

115-
def get_resource_class(obj) -> Optional[type[Resource]]:
115+
def _get_resource_class(obj) -> Optional[type[Resource]]:
116116
"""Extract the resource class from generic type parameter."""
117117
metadata = getattr(obj.__class__, "__pydantic_generic_metadata__", {"args": [None]})
118118
resource_class = metadata["args"][0]

0 commit comments

Comments
 (0)