Skip to content

Commit 8063ad2

Browse files
committed
fix: dynamic schema genaration for user defined classes with inheritence
1 parent 52297da commit 8063ad2

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

doc/changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Changelog
22
=========
33

4+
[0.3.5] - Unreleased
5+
--------------------
6+
7+
Added
8+
^^^^^
9+
- Fix dynamic schema generation for user defined classes with inheritance.
10+
411
[0.3.4] - 2025-06-05
512
--------------------
613

scim2_models/rfc7643/resource.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from ..base import CaseExact
1818
from ..base import ComplexAttribute
1919
from ..base import ExternalReference
20+
from ..base import MultiValuedComplexAttribute
2021
from ..base import Mutability
2122
from ..base import Required
2223
from ..base import Returned
@@ -253,8 +254,8 @@ def from_schema(cls, schema) -> "Resource":
253254
AnyResource = TypeVar("AnyResource", bound="Resource")
254255

255256

256-
def dedicated_attributes(model):
257-
"""Return attributes that are not members of parent classes."""
257+
def dedicated_attributes(model, excluded_models):
258+
"""Return attributes that are not members the parent 'excluded_models'."""
258259

259260
def compare_field_infos(fi1, fi2):
260261
return (
@@ -268,8 +269,8 @@ def compare_field_infos(fi1, fi2):
268269

269270
parent_field_infos = {
270271
field_name: field_info
271-
for parent in model.__bases__
272-
for field_name, field_info in parent.model_fields.items()
272+
for excluded_model in excluded_models
273+
for field_name, field_info in excluded_model.model_fields.items()
273274
}
274275
field_infos = {
275276
field_name: field_info
@@ -283,7 +284,7 @@ def model_to_schema(model: type[BaseModel]):
283284
from scim2_models.rfc7643.schema import Schema
284285

285286
schema_urn = model.model_fields["schemas"].default[0]
286-
field_infos = dedicated_attributes(model)
287+
field_infos = dedicated_attributes(model, [Resource])
287288
attributes = [
288289
model_attribute_to_attribute(model, attribute_name)
289290
for attribute_name in field_infos
@@ -323,7 +324,9 @@ def model_attribute_to_attribute(model, attribute_name):
323324
sub_attributes = (
324325
[
325326
model_attribute_to_attribute(root_type, sub_attribute_name)
326-
for sub_attribute_name in dedicated_attributes(root_type)
327+
for sub_attribute_name in dedicated_attributes(
328+
root_type, [MultiValuedComplexAttribute]
329+
)
327330
if (
328331
attribute_name != "sub_attributes"
329332
or sub_attribute_name != "sub_attributes"

tests/test_dynamic_schemas.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import operator
2+
from typing import Annotated
3+
from typing import Optional
24

35
from scim2_models.base import Context
6+
from scim2_models.base import Required
47
from scim2_models.rfc7643.enterprise_user import EnterpriseUser
58
from scim2_models.rfc7643.group import Group
9+
from scim2_models.rfc7643.resource import Resource
610
from scim2_models.rfc7643.resource_type import ResourceType
711
from scim2_models.rfc7643.schema import Schema
812
from scim2_models.rfc7643.service_provider_config import ServiceProviderConfig
@@ -127,3 +131,49 @@ def test_dump_with_context():
127131
models = [User, EnterpriseUser, Group, ResourceType, Schema, ServiceProviderConfig]
128132
for model in models:
129133
model.to_schema().model_dump(scim_ctx=Context.RESOURCE_QUERY_RESPONSE)
134+
135+
136+
def test_inheritance():
137+
"""Check that parent attributes are included in the schema."""
138+
139+
class Foo(Resource):
140+
schemas: Annotated[list[str], Required.true] = [
141+
"urn:ietf:params:scim:schemas:core:2.0:Foo"
142+
]
143+
144+
foo: Optional[str] = None
145+
146+
class Bar(Foo):
147+
bar: Optional[str] = None
148+
149+
schema = Bar.to_schema()
150+
assert schema.model_dump() == {
151+
"attributes": [
152+
{
153+
"caseExact": False,
154+
"multiValued": False,
155+
"mutability": "readWrite",
156+
"name": "foo",
157+
"required": False,
158+
"returned": "default",
159+
"type": "string",
160+
"uniqueness": "none",
161+
},
162+
{
163+
"caseExact": False,
164+
"multiValued": False,
165+
"mutability": "readWrite",
166+
"name": "bar",
167+
"required": False,
168+
"returned": "default",
169+
"type": "string",
170+
"uniqueness": "none",
171+
},
172+
],
173+
"description": "Bar",
174+
"id": "urn:ietf:params:scim:schemas:core:2.0:Foo",
175+
"name": "Bar",
176+
"schemas": [
177+
"urn:ietf:params:scim:schemas:core:2.0:Schema",
178+
],
179+
}

0 commit comments

Comments
 (0)