@@ -54,7 +54,35 @@ class BaseModel(PydanticBaseModel):
54
54
55
55
@classmethod
56
56
def get_field_annotation (cls , field_name : str , annotation_type : type ) -> Any :
57
- """Return the annotation of type 'annotation_type' of the field 'field_name'."""
57
+ """Return the annotation of type 'annotation_type' of the field 'field_name'.
58
+
59
+ This method extracts SCIM-specific annotations from a field's metadata,
60
+ such as :class:`~scim2_models.Mutability`, :class:`~scim2_models.Required`,
61
+ or :class:`~scim2_models.Returned` annotations.
62
+
63
+ :return: The annotation instance if found, otherwise the annotation type's default value
64
+
65
+ >>> from scim2_models.resources.user import User
66
+ >>> from scim2_models.annotations import Mutability, Required
67
+
68
+ Get the mutability annotation of the 'id' field:
69
+
70
+ >>> mutability = User.get_field_annotation("id", Mutability)
71
+ >>> mutability
72
+ <Mutability.read_only: 'readOnly'>
73
+
74
+ Get the required annotation of the 'user_name' field:
75
+
76
+ >>> required = User.get_field_annotation("user_name", Required)
77
+ >>> required
78
+ <Required.true: True>
79
+
80
+ If no annotation is found, returns the default value:
81
+
82
+ >>> missing = User.get_field_annotation("display_name", Required)
83
+ >>> missing
84
+ <Required.false: False>
85
+ """
58
86
field_metadata = cls .model_fields [field_name ].metadata
59
87
60
88
default_value = getattr (annotation_type , "_default" , None )
@@ -71,8 +99,34 @@ def annotation_type_filter(item: Any) -> bool:
71
99
def get_field_root_type (cls , attribute_name : str ) -> Optional [type ]:
72
100
"""Extract the root type from a model field.
73
101
74
- For example, return 'GroupMember' for
75
- 'Optional[List[GroupMember]]'
102
+ This method unwraps complex type annotations to find the underlying
103
+ type, removing Optional and List wrappers to get to the actual type
104
+ of the field's content.
105
+
106
+ :return: The root type of the field, or None if not found
107
+
108
+ >>> from scim2_models.resources.user import User
109
+ >>> from scim2_models.resources.group import Group
110
+
111
+ Simple type:
112
+
113
+ >>> User.get_field_root_type("user_name")
114
+ <class 'str'>
115
+
116
+ ``Optional`` type unwraps to the underlying type:
117
+
118
+ >>> User.get_field_root_type("display_name")
119
+ <class 'str'>
120
+
121
+ ``List`` type unwraps to the element type:
122
+
123
+ >>> User.get_field_root_type("emails") # doctest: +ELLIPSIS
124
+ <class 'scim2_models.resources.user.Email'>
125
+
126
+ ``Optional[List[T]]`` unwraps to ``T``:
127
+
128
+ >>> Group.get_field_root_type("members") # doctest: +ELLIPSIS
129
+ <class 'scim2_models.resources.group.GroupMember'>
76
130
"""
77
131
attribute_type = cls .model_fields [attribute_name ].annotation
78
132
@@ -89,7 +143,20 @@ def get_field_root_type(cls, attribute_name: str) -> Optional[type]:
89
143
90
144
@classmethod
91
145
def get_field_multiplicity (cls , attribute_name : str ) -> bool :
92
- """Indicate whether a field holds multiple values."""
146
+ """Indicate whether a field holds multiple values.
147
+
148
+ This method determines if a field is defined as a list type,
149
+ which indicates it can contain multiple values. It handles
150
+ Optional wrappers correctly.
151
+
152
+ :return: True if the field holds multiple values (is a list), False otherwise
153
+
154
+ >>> from scim2_models.resources.user import User
155
+ >>> User.get_field_multiplicity("user_name")
156
+ False
157
+ >>> User.get_field_multiplicity("emails")
158
+ True
159
+ """
93
160
attribute_type = cls .model_fields [attribute_name ].annotation
94
161
95
162
# extract 'x' from 'Optional[x]'
0 commit comments