From d261826b67e0a2f783167e6daef59987ad46defa Mon Sep 17 00:00:00 2001 From: Naq Date: Fri, 6 Mar 2026 17:57:11 +0200 Subject: [PATCH 1/2] fix: Check for NoneType extension in serialization --- scim2_models/resources/resource.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scim2_models/resources/resource.py b/scim2_models/resources/resource.py index 254f314..59990a5 100644 --- a/scim2_models/resources/resource.py +++ b/scim2_models/resources/resource.py @@ -118,6 +118,10 @@ def _extension_serializer( dumped when the model is used as an extension for another model. """ partial_result = handler(value) + + if partial_result is None: + return None + result = { attr_name: value for attr_name, value in partial_result.items() From 636455e2229b3f45e92adb26055ccc636e475988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Tue, 10 Mar 2026 08:54:05 +0100 Subject: [PATCH 2/2] tests: add unit tests --- doc/changelog.rst | 7 +++++++ scim2_models/resources/resource.py | 6 +++--- tests/test_resource_extension.py | 9 +++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 136b503..5914862 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,6 +1,13 @@ Changelog ========= +[0.6.5] - 2026-03-10 +-------------------- + +Fixed +^^^^^ +- Fix extension serialization crash when an extension is declared but not populated on a resource serialized outside of SCIM context (e.g. FastAPI ``response_model``). :pr:`131` + [0.6.4] - 2026-02-05 -------------------- diff --git a/scim2_models/resources/resource.py b/scim2_models/resources/resource.py index 59990a5..3e50ccb 100644 --- a/scim2_models/resources/resource.py +++ b/scim2_models/resources/resource.py @@ -117,11 +117,11 @@ def _extension_serializer( For instance, attributes 'meta', 'id' or 'schemas' should not be dumped when the model is used as an extension for another model. """ - partial_result = handler(value) - - if partial_result is None: + if value is None: return None + partial_result = handler(value) + result = { attr_name: value for attr_name, value in partial_result.items() diff --git a/tests/test_resource_extension.py b/tests/test_resource_extension.py index 01983f5..a27d998 100644 --- a/tests/test_resource_extension.py +++ b/tests/test_resource_extension.py @@ -1,6 +1,7 @@ import datetime import pytest +from pydantic import TypeAdapter from scim2_models import URN from scim2_models import Context @@ -370,6 +371,14 @@ def test_class_getitem(): User[int] +def test_dump_resource_with_unset_extension(): + """Serialize a resource whose extension is declared but not populated.""" + user = User[EnterpriseUser](user_name="bjensen") + ta = TypeAdapter(User[EnterpriseUser]) + payload = ta.dump_python(user) + assert "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" not in payload + + def test_model_attribute_to_scim_attribute_error(): """Test error case where get_field_root_type returns None.""" from pydantic import Field