Transform the given data against the expected type. Args: annotation: The direct type annotation given to the particular piece of data. This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc inner_type: If applicable, this is t
(
data: object,
*,
annotation: type,
inner_type: type | None = None,
)
| 152 | |
| 153 | |
| 154 | def _transform_recursive( |
| 155 | data: object, |
| 156 | *, |
| 157 | annotation: type, |
| 158 | inner_type: type | None = None, |
| 159 | ) -> object: |
| 160 | class="st">"""Transform the given data against the expected type. |
| 161 | |
| 162 | Args: |
| 163 | annotation: The direct type annotation given to the particular piece of data. |
| 164 | This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc |
| 165 | |
| 166 | inner_type: If applicable, this is the class="st">"inside" type. This is useful in certain cases where the outside type |
| 167 | is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in |
| 168 | the list can be transformed using the metadata from the container type. |
| 169 | |
| 170 | Defaults to the same value as the `annotation` argument. |
| 171 | class="st">""" |
| 172 | from .._compat import model_dump |
| 173 | |
| 174 | if inner_type is None: |
| 175 | inner_type = annotation |
| 176 | |
| 177 | stripped_type = strip_annotated_type(inner_type) |
| 178 | origin = get_origin(stripped_type) or stripped_type |
| 179 | if is_typeddict(stripped_type) and is_mapping(data): |
| 180 | return _transform_typeddict(data, stripped_type) |
| 181 | |
| 182 | if origin == dict and is_mapping(data): |
| 183 | items_type = get_args(stripped_type)[1] |
| 184 | return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} |
| 185 | |
| 186 | if ( |
| 187 | class="cm"># List[T] |
| 188 | (is_list_type(stripped_type) and is_list(data)) |
| 189 | class="cm"># Iterable[T] |
| 190 | or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) |
| 191 | class="cm"># Sequence[T] |
| 192 | or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) |
| 193 | ): |
| 194 | class="cm"># dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually |
| 195 | class="cm"># intended as an iterable, so we don't transform it. |
| 196 | if isinstance(data, dict): |
| 197 | return cast(object, data) |
| 198 | |
| 199 | inner_type = extract_type_arg(stripped_type, 0) |
| 200 | if _no_transform_needed(inner_type): |
| 201 | class="cm"># for some types there is no need to transform anything, so we can get a small |
| 202 | class="cm"># perf boost from skipping that work. |
| 203 | class="cm"># |
| 204 | class="cm"># but we still need to convert to a list to ensure the data is json-serializable |
| 205 | if is_list(data): |
| 206 | return data |
| 207 | return list(data) |
| 208 | |
| 209 | return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] |
| 210 | |
| 211 | if is_union_type(stripped_type): |
no test coverage detected