Skip to content

Commit 66dd593

Browse files
committed
chore: remove code planed for 0.8 removal
1 parent ba1df0b commit 66dd593

6 files changed

Lines changed: 15 additions & 236 deletions

File tree

‎doc/changelog.rst‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ Changed
6060

6161
Removed
6262
^^^^^^^
63+
- ``Resource.get_by_schema`` and ``Resource.get_by_payload``, deprecated in 0.6.13. Use
64+
:func:`~scim2_models.get_model_by_schema` and :func:`~scim2_models.get_model_by_payload`, whose
65+
``resource_types`` parameter is named ``models``.
66+
- The ``original`` parameter of :meth:`~scim2_models.BaseModel.model_validate`, deprecated in
67+
0.6.7. Validate the payload, then call :meth:`~scim2_models.Resource.replace` on the result to
68+
compare it against the stored resource. A replacement request is no longer checked against an
69+
original at validation time, so an immutable attribute that changed raises
70+
:exc:`~scim2_models.MutabilityException` from ``replace`` instead of a
71+
:exc:`~pydantic.ValidationError` from ``model_validate``.
6372
- ``Path.field_name``, ``Path.field_type``, ``Path.is_multivalued``, ``Path.get_annotation`` and
6473
``Path.urn``. Use :meth:`~scim2_models.Path.resolve`, whose
6574
:class:`~scim2_models.AttributeBinding` carries them.

‎scim2_models/base.py‎

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import warnings
21
from collections.abc import Mapping
32
from inspect import isclass
43
from types import MappingProxyType
@@ -7,7 +6,6 @@
76
from typing import ClassVar
87
from typing import NamedTuple
98
from typing import NoReturn
10-
from typing import Optional
119
from typing import cast
1210
from typing import get_args
1311
from typing import get_origin
@@ -432,13 +430,10 @@ def enforce_scim_context(self, info: ValidationInfo) -> Self:
432430
if not scim_context or scim_context == Context.DEFAULT:
433431
return self
434432

435-
from scim2_models.resources.resource import Resource
436-
437433
is_create_or_replace = scim_context in (
438434
Context.RESOURCE_CREATION_REQUEST,
439435
Context.RESOURCE_REPLACEMENT_REQUEST,
440436
)
441-
original = info.context.get("original") if info.context else None
442437
fields_set = self.model_fields_set
443438

444439
for field_name in self.__class__.model_fields:
@@ -456,14 +451,6 @@ def enforce_scim_context(self, info: ValidationInfo) -> Self:
456451
if self.get_field_multiplicity(field_name) and value is not None:
457452
self._check_primary_uniqueness(field_name, value)
458453

459-
# DEPRECATED: Remove when original is not used in validation
460-
if (
461-
scim_context == Context.RESOURCE_REPLACEMENT_REQUEST
462-
and original is not None
463-
and issubclass(type(self), Resource)
464-
):
465-
self._check_replacement_mutability(original)
466-
467454
return self
468455

469456
def _raise_field_error(
@@ -550,13 +537,6 @@ def _check_returnability(self, field_name: str, value: Any) -> None:
550537
},
551538
)
552539

553-
def _check_replacement_mutability(self, original: "BaseModel") -> None:
554-
"""Check if 'immutable' attributes have been mutated in replacement requests."""
555-
try:
556-
self._apply_replace_constraints(original)
557-
except MutabilityException as exc:
558-
raise exc.as_pydantic_error() from exc
559-
560540
def _check_primary_uniqueness(self, field_name: str, value: Any) -> None:
561541
"""Validate that only one attribute can be marked as primary in multi-valued lists, per :rfc:`RFC7643 §2.4 <7643#section-2.4>`."""
562542
element_type = self.get_field_root_type(field_name)
@@ -794,13 +774,11 @@ def _scim_response_serializer(
794774
def _prepare_model_validate(
795775
cls,
796776
scim_ctx: Context | None = Context.DEFAULT,
797-
original: Optional["BaseModel"] = None,
798777
scim_policy: ScimPolicy | None = None,
799778
**kwargs: Any,
800779
) -> dict[str, Any]:
801780
context = kwargs.setdefault("context", {})
802781
context.setdefault("scim", scim_ctx)
803-
context.setdefault("original", original)
804782
context.setdefault("scim_policy", scim_policy)
805783
return kwargs
806784

@@ -809,7 +787,6 @@ def model_validate(
809787
cls,
810788
*args: Any,
811789
scim_ctx: Context | None = Context.DEFAULT,
812-
original: Optional["BaseModel"] = None,
813790
scim_policy: ScimPolicy | None = None,
814791
**kwargs: Any,
815792
) -> Self:
@@ -818,26 +795,8 @@ def model_validate(
818795
:param scim_ctx: The SCIM :class:`~scim2_models.Context` in which the validation happens.
819796
:param scim_policy: The :class:`~scim2_models.ScimPolicy` the validation
820797
runs under. Defaults to the strict reading of the specification.
821-
:param original: If this parameter is set during :attr:`~Context.RESOURCE_REPLACEMENT_REQUEST`,
822-
:attr:`~scim2_models.Mutability.immutable` parameters will be compared against the *original* model value.
823-
An exception is raised if values are different.
824-
825-
.. deprecated:: 0.6.7
826-
Use :meth:`~scim2_models.Resource.replace` on the validated instance instead.
827-
Will be removed in 0.8.0.
828798
"""
829-
if original is not None:
830-
warnings.warn(
831-
"The 'original' parameter is deprecated, "
832-
"use the 'replace' method on the validated instance instead. "
833-
"Will be removed in 0.8.0.",
834-
DeprecationWarning,
835-
stacklevel=2,
836-
)
837-
838-
validate_kwargs = cls._prepare_model_validate(
839-
scim_ctx, original, scim_policy, **kwargs
840-
)
799+
validate_kwargs = cls._prepare_model_validate(scim_ctx, scim_policy, **kwargs)
841800
return super().model_validate(*args, **validate_kwargs)
842801

843802
@classmethod

‎scim2_models/context.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ class Context(Enum):
8383
and servers validating resource replacement request payloads.
8484
8585
- When used for serialization, it will not dump attributes annotated with :attr:`~scim2_models.Mutability.read_only`.
86-
- When used for validation, it will ignore attributes annotated with :attr:`scim2_models.Mutability.read_only` and raise a :class:`~pydantic_core.ValidationError`:
87-
- when finding attributes annotated with :attr:`~scim2_models.Mutability.immutable` different than the ``original`` parameter passed to :meth:`~scim2_models.BaseModel.model_validate`;
88-
- when attributes annotated with :attr:`Required.true <scim2_models.Required.true>` are missing on null.
86+
- When used for validation, it will ignore attributes annotated with :attr:`scim2_models.Mutability.read_only` and raise a :class:`~pydantic_core.ValidationError` when attributes annotated with :attr:`Required.true <scim2_models.Required.true>` are missing or null. Comparing the payload against the stored resource is :meth:`~scim2_models.Resource.replace`'s business.
8987
"""
9088

9189
RESOURCE_REPLACEMENT_RESPONSE = auto()

‎scim2_models/resources/resource.py‎

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import copyreg
2-
import warnings
32
from collections.abc import MutableMapping
4-
from collections.abc import Sequence
53
from datetime import datetime
64
from enum import Enum
75
from typing import TYPE_CHECKING
@@ -34,12 +32,9 @@
3432
from ..base import BaseModel
3533
from ..context import Context
3634
from ..exceptions import InvalidPathException
37-
from ..lookup import get_model_by_payload
38-
from ..lookup import get_model_by_schema
3935
from ..path import Path
4036
from ..policy import ScimPolicy
4137
from ..policy import _policy
42-
from ..scim_object import AnyScimObject
4338
from ..scim_object import ScimObject
4439
from ..utils import UNION_TYPES
4540
from ..utils import _normalize_attribute_name
@@ -366,44 +361,6 @@ def get_extension_model(
366361
return extension
367362
return None
368363

369-
@staticmethod
370-
def get_by_schema(
371-
resource_types: Sequence[type[AnyScimObject]],
372-
schema: str,
373-
with_extensions: bool = True,
374-
) -> type[AnyScimObject] | type["Extension"] | None:
375-
"""Given a resource type list and a schema, find the matching resource type.
376-
377-
.. deprecated:: 0.6.13
378-
Use :func:`~scim2_models.get_model_by_schema` instead.
379-
"""
380-
warnings.warn(
381-
"Resource.get_by_schema is deprecated, use "
382-
"scim2_models.get_model_by_schema instead. Will be removed in 0.8.0.",
383-
DeprecationWarning,
384-
stacklevel=2,
385-
)
386-
return get_model_by_schema(resource_types, schema, with_extensions)
387-
388-
@staticmethod
389-
def get_by_payload(
390-
resource_types: Sequence[type[AnyScimObject]],
391-
payload: dict[str, Any],
392-
**kwargs: Any,
393-
) -> type[AnyScimObject] | type["Extension"] | None:
394-
"""Given a resource type list and a payload, find the matching resource type.
395-
396-
.. deprecated:: 0.6.13
397-
Use :func:`~scim2_models.get_model_by_payload` instead.
398-
"""
399-
warnings.warn(
400-
"Resource.get_by_payload is deprecated, use "
401-
"scim2_models.get_model_by_payload instead. Will be removed in 0.8.0.",
402-
DeprecationWarning,
403-
stacklevel=2,
404-
)
405-
return get_model_by_payload(resource_types, payload, **kwargs)
406-
407364
def _model_schemas(self) -> list[str]:
408365
"""List the base schema and the schemas of the declared extensions."""
409366
return super()._model_schemas() + list(self.get_extension_models())

‎tests/test_model_validation.py‎

Lines changed: 4 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -184,120 +184,6 @@ class SubResource(Resource):
184184
]
185185

186186

187-
def test_validate_replacement_request_mutability():
188-
"""Test query validation for resource model replacement requests.
189-
190-
Attributes marked as:
191-
- Mutability.immutable raise a ValidationError if different than the 'original' item.
192-
- Mutability.read_only are copied from the original
193-
"""
194-
original = MutResource(read_only="y", read_write="y", write_only="y", immutable="y")
195-
with pytest.warns(DeprecationWarning, match="original"):
196-
assert MutResource.model_validate(
197-
{
198-
"schemas": ["urn:example:MutResource"],
199-
"readOnly": "x",
200-
"readWrite": "x",
201-
"writeOnly": "x",
202-
"immutable": "y",
203-
},
204-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
205-
original=original,
206-
) == MutResource(
207-
schemas=["urn:example:MutResource"],
208-
read_only="y",
209-
readWrite="x",
210-
writeOnly="x",
211-
immutable="y",
212-
)
213-
214-
with pytest.warns(DeprecationWarning, match="original"):
215-
MutResource.model_validate(
216-
{
217-
"schemas": ["urn:example:MutResource"],
218-
"immutable": "y",
219-
},
220-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
221-
original=original,
222-
)
223-
224-
with pytest.warns(DeprecationWarning, match="original"):
225-
with pytest.raises(
226-
ValidationError,
227-
match="mutability",
228-
):
229-
MutResource.model_validate(
230-
{
231-
"schemas": ["urn:example:MutResource"],
232-
"immutable": "x",
233-
},
234-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
235-
original=original,
236-
)
237-
238-
239-
def test_validate_replacement_request_mutability_sub_attributes():
240-
"""Test query validation for resource model replacement requests.
241-
242-
Sub-attributes marked as:
243-
- Mutability.immutable raise a ValidationError if different than the 'original' item.
244-
- Mutability.read_only are ignored
245-
"""
246-
247-
class Sub(ComplexAttribute):
248-
immutable: Annotated[str | None, Mutability.immutable] = None
249-
250-
class Super(Resource):
251-
__schema__ = URN("urn:example:Super")
252-
sub: Sub | None = None
253-
254-
original = Super(sub=Sub(immutable="y"))
255-
with pytest.warns(DeprecationWarning, match="original"):
256-
assert Super.model_validate(
257-
{
258-
"schemas": ["urn:example:Super"],
259-
"sub": {
260-
"immutable": "y",
261-
},
262-
},
263-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
264-
original=original,
265-
) == Super(
266-
schemas=["urn:example:Super"],
267-
sub=Sub(
268-
immutable="y",
269-
),
270-
)
271-
272-
with pytest.warns(DeprecationWarning, match="original"):
273-
Super.model_validate(
274-
{
275-
"schemas": ["urn:example:Super"],
276-
"sub": {
277-
"immutable": "y",
278-
},
279-
},
280-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
281-
original=original,
282-
)
283-
284-
with pytest.warns(DeprecationWarning, match="original"):
285-
with pytest.raises(
286-
ValidationError,
287-
match="mutability",
288-
):
289-
Super.model_validate(
290-
{
291-
"schemas": ["urn:example:Super"],
292-
"sub": {
293-
"immutable": "x",
294-
},
295-
},
296-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
297-
original=original,
298-
)
299-
300-
301187
def test_replace_detects_changed_field():
302188
"""Replace raises MutabilityException when an immutable field differs."""
303189
from scim2_models.exceptions import MutabilityException
@@ -597,19 +483,8 @@ class MyResource(Resource):
597483
assert replacement[MyExt].read_write == "new"
598484

599485

600-
def test_original_parameter_emits_deprecation_warning():
601-
"""Passing 'original' to model_validate emits a DeprecationWarning."""
602-
original = MutResource(immutable="y")
603-
with pytest.warns(DeprecationWarning, match="original"):
604-
MutResource.model_validate(
605-
{"schemas": ["urn:example:MutResource"], "immutable": "y"},
606-
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST,
607-
original=original,
608-
)
609-
610-
611-
def test_replacement_request_without_original_parameter():
612-
"""Replacement requests work without 'original' when using replace manually."""
486+
def test_replace_after_validating_a_replacement_request():
487+
"""A replacement request is validated on its own, then compared to the stored resource."""
613488
from scim2_models.exceptions import MutabilityException
614489

615490
original = MutResource(immutable="y")
@@ -621,8 +496,8 @@ def test_replacement_request_without_original_parameter():
621496
replacement.replace(original)
622497

623498

624-
def test_replacement_request_without_original_allows_matching_values():
625-
"""Replacement requests validate and replace succeeds with identical immutable values."""
499+
def test_replace_after_validating_a_replacement_request_that_changes_nothing():
500+
"""A replacement request repeating the immutable values it was given is accepted."""
626501
original = MutResource(immutable="y")
627502
replacement = MutResource.model_validate(
628503
{"schemas": ["urn:example:MutResource"], "immutable": "y"},

‎tests/test_models.py‎

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from scim2_models import Group
1313
from scim2_models import ListResponse
1414
from scim2_models import PatchOp
15-
from scim2_models import Resource
1615
from scim2_models import ResourceType
1716
from scim2_models import Schema
1817
from scim2_models import SearchRequest
@@ -175,24 +174,6 @@ def test_get_model_by_schema():
175174
)
176175

177176

178-
def test_get_by_schema_deprecation():
179-
"""Resource.get_by_schema is kept as a deprecated alias."""
180-
with pytest.warns(DeprecationWarning, match="get_model_by_schema"):
181-
assert (
182-
Resource.get_by_schema(
183-
[Group], "urn:ietf:params:scim:schemas:core:2.0:Group"
184-
)
185-
== Group
186-
)
187-
188-
189-
def test_get_by_payload_deprecation():
190-
"""Resource.get_by_payload is kept as a deprecated alias."""
191-
payload = {"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"]}
192-
with pytest.warns(DeprecationWarning, match="get_model_by_payload"):
193-
assert Resource.get_by_payload([Group], payload) == Group
194-
195-
196177
def test_get_message_by_schema_along_resources():
197178
"""Messages such as ListResponse can be looked up next to resources carrying extensions."""
198179
resource_types = [ListResponse[User], User[EnterpriseUser]]

0 commit comments

Comments
 (0)