Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ Here is an example of usage:
```python
import datetime
from httpx2 import Client
from scim2_client import SCIMResponseErrorObject
from scim2_client.engines.httpx2 import SyncSCIMClient
from scim2_models import SCIMException

client = Client(
base_url="https://auth.example/scim/v2", headers={"Authorization": "Bearer foobar"}
Expand Down Expand Up @@ -70,7 +70,7 @@ assert user.meta.last_modified == datetime.datetime(
user = User(user_name="bjensen@example.com")
try:
scim.create(user)
except SCIMResponseErrorObject as exc:
except SCIMException as exc:
error = exc.to_error()
assert (
error.detail
Expand Down
31 changes: 22 additions & 9 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Added
Changed
^^^^^^^
- scim2-models 0.8 is not supported yet, and 0.7.0 is now the minimum supported version.
- **Breaking:** invalid requests and server :class:`~scim2_models.Error` objects now raise
:class:`~scim2_models.SCIMException` subclasses from scim2-models instead of scim2-client
custom exceptions. :issue:`39`

Deprecated
^^^^^^^^^^
Expand All @@ -22,6 +25,16 @@ Deprecated
Will be removed in 0.9.
- Passing a :code:`httpx.Client` or a :code:`httpx.AsyncClient` to the request engines,
in favor of their httpx2 counterparts. Will be removed in 0.9.
- The exceptions with a ``*Error`` suffix, in favor of their ``*Exception`` counterparts.
The old names still point at the renamed classes, so ``except`` blocks written against
them keep working. Will be removed in 0.9.

Removed
^^^^^^^
- **Breaking:** ``SCIMRequestError``, ``RequestPayloadValidationError`` and
``SCIMResponseErrorObject``, which have no counterpart among the scim2-models exceptions.
Code catching them must catch :class:`~scim2_models.SCIMException` instead, which is also
what invalid request payloads and server errors now raise.

Fixed
^^^^^
Expand Down Expand Up @@ -50,7 +63,7 @@ Changed

Changed
^^^^^^^
- :class:`~scim2_client.SCIMResponseErrorObject` now exposes a :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method
- ``SCIMResponseErrorObject`` now exposes a ``to_error()`` method
returning the :class:`~scim2_models.Error` object from the server. :issue:`37`

[0.7.2] - 2026-02-03
Expand All @@ -60,14 +73,14 @@ Fixed
^^^^^
- Skip ``Content-Type`` header validation for 204 responses. :issue:`34`

[0.7.1] - 2025-01-25
[0.7.1] - 2026-01-25
--------------------

Fixed
^^^^^
- ``schemas`` is no longer included in GET query parameters per RFC 7644 §3.4.2.

[0.7.0] - 2025-01-25
[0.7.0] - 2026-01-25
--------------------

Added
Expand Down Expand Up @@ -168,7 +181,7 @@ Added
Added
^^^^^
- :class:`~scim2_client.engines.werkzeug.TestSCIMClient` raise a
:class:`~scim2_client.UnexpectedContentFormat` exception when response is not JSON.
``UnexpectedContentFormat`` exception when response is not JSON.

[0.3.2] - 2024-11-29
--------------------
Expand Down Expand Up @@ -224,7 +237,7 @@ Added

Fixed
^^^^^
- :class:`~scim2_client.RequestPayloadValidationError` error message.
- ``RequestPayloadValidationError`` error message.
- Don't crash when servers don't return content type headers. :pr:`22,24`

[0.2.0] - 2024-09-01
Expand Down Expand Up @@ -278,7 +291,7 @@ Fixed

Added
^^^^^
- :class:`~scim2_client.SCIMResponseErrorObject` implementation.
- ``SCIMResponseErrorObject`` implementation.

[0.1.5] - 2024-06-05
--------------------
Expand All @@ -293,9 +306,9 @@ Added
- :class:`~scim2_models.ServiceProviderConfig`, :class:`~scim2_models.ResourceType`
and :class:`~scim2_models.Schema` are added to the default resource types list.
- Any custom URL can be used with all the :class:`~scim2_client.SCIMClient` methods.
- :class:`~scim2_client.ResponsePayloadValidationError` implementation.
- :class:`~scim2_client.RequestPayloadValidationError` implementation.
- :class:`~scim2_client.RequestNetworkError` implementation.
- ``ResponsePayloadValidationError`` implementation.
- ``RequestPayloadValidationError`` implementation.
- ``RequestNetworkError`` implementation.

Fixed
^^^^^
Expand Down
27 changes: 16 additions & 11 deletions doc/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,9 @@ Bulk
Error handling
==============

By default, if the server returns an error, a :class:`~scim2_client.SCIMResponseErrorObject` exception is raised.
The :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method gives access to the :class:`~scim2_models.Error` object:
By default, if the request payload is invalid or if the server returns an error,
a :class:`~scim2_models.SCIMException` exception is raised.
The :meth:`~scim2_models.SCIMException.to_error` method gives access to the :class:`~scim2_models.Error` object:

.. tab-set::
:class: outline
Expand All @@ -345,11 +346,11 @@ The :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method gives access t

.. code-block:: python

from scim2_client import SCIMResponseErrorObject
from scim2_models import SCIMException

try:
response = scim.create(request)
except SCIMResponseErrorObject as exc:
except SCIMException as exc:
error = exc.to_error()
print(f"SCIM error [{error.status}] {error.scim_type}: {error.detail}")

Expand All @@ -358,14 +359,18 @@ The :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method gives access t

.. code-block:: python

from scim2_client import SCIMResponseErrorObject
from scim2_models import SCIMException

try:
response = await scim.create(request)
except SCIMResponseErrorObject as exc:
except SCIMException as exc:
error = exc.to_error()
print(f"SCIM error [{error.status}] {error.scim_type}: {error.detail}")

Exceptions raised while validating the request payload happen before anything is
sent. They keep the original :class:`~pydantic.ValidationError`, which lists every
invalid attribute, in :attr:`~BaseException.__cause__`.

Request and response validation
===============================

Expand All @@ -374,16 +379,16 @@ However sometimes you want to accept invalid inputs and outputs.
To achieve this, all the methods provide the following parameters, all are :data:`True` by default:

- :paramref:`~scim2_client.SCIMClient.check_request_payload`:
If :data:`True` (the default) a :class:`~pydantic.ValidationError` will be raised if the input does not respect the SCIM standard.
If :data:`True` (the default) a :class:`~scim2_models.SCIMException` will be raised if the input does not respect the SCIM standard.
If :data:`False`, input is expected to be a :data:`dict` that will be passed as-is in the request.
- :paramref:`~scim2_client.SCIMClient.check_response_payload`:
If :data:`True` (the default) a :class:`~pydantic.ValidationError` will be raised if the server response does not respect the SCIM standard.
If :data:`True` (the default) a :class:`~scim2_client.ResponsePayloadValidationException` will be raised if the server response does not respect the SCIM standard.
If :data:`False` the server response is returned as-is.
- :code:`expected_status_codes`: The list of expected status codes in the response.
If :data:`None` any status code is accepted.
If an unexpected status code is returned, a :class:`~scim2_client.errors.UnexpectedStatusCode` exception is raised.
- :paramref:`~scim2_client.SCIMClient.raise_scim_errors`: If :data:`True` (the default) and the server returned an :class:`~scim2_models.Error` object, a :class:`~scim2_client.SCIMResponseErrorObject` exception will be raised.
The :meth:`~scim2_client.SCIMResponseErrorObject.to_error` method gives access to the :class:`~scim2_models.Error` object.
If an unexpected status code is returned, a :class:`~scim2_client.errors.UnexpectedStatusCodeException` exception is raised.
- :paramref:`~scim2_client.SCIMClient.raise_scim_errors`: If :data:`True` (the default) and the server returned an :class:`~scim2_models.Error` object, a :class:`~scim2_models.SCIMException` exception will be raised.
The :meth:`~scim2_models.SCIMException.to_error` method gives access to the :class:`~scim2_models.Error` object.
If :data:`False` the error object is returned directly.


Expand Down
42 changes: 22 additions & 20 deletions scim2_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
from . import errors
from .client import BaseSyncSCIMClient
from .client import SCIMClient
from .errors import RequestNetworkError
from .errors import RequestPayloadValidationError
from .errors import ResponsePayloadValidationError
from .errors import SCIMClientError
from .errors import SCIMRequestError
from .errors import SCIMResponseError
from .errors import SCIMResponseErrorObject
from .errors import UnexpectedContentFormat
from .errors import UnexpectedContentType
from .errors import UnexpectedStatusCode
from .errors import RequestNetworkException
from .errors import ResponsePayloadValidationException
from .errors import SCIMClientException
from .errors import SCIMResponseException
from .errors import UnexpectedContentFormatException
from .errors import UnexpectedContentTypeException
from .errors import UnexpectedStatusCodeException

__all__ = [
"SCIMClient",
"BaseSyncSCIMClient",
"SCIMClientError",
"SCIMRequestError",
"SCIMResponseError",
"SCIMResponseErrorObject",
"UnexpectedContentFormat",
"UnexpectedContentType",
"UnexpectedStatusCode",
"RequestPayloadValidationError",
"RequestNetworkError",
"ResponsePayloadValidationError",
"SCIMClientException",
"SCIMResponseException",
"RequestNetworkException",
"UnexpectedStatusCodeException",
"UnexpectedContentTypeException",
"UnexpectedContentFormatException",
"ResponsePayloadValidationException",
]


def __getattr__(name: str) -> type[SCIMClientException]:
if name not in errors._DEPRECATED_ALIASES:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

return errors.deprecated_alias(name)
Loading
Loading