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,
)
| 318 | |
| 319 | |
| 320 | async def _async_transform_recursive( |
| 321 | data: object, |
| 322 | *, |
| 323 | annotation: type, |
| 324 | inner_type: type | None = None, |
| 325 | ) -> object: |
| 326 | """Transform the given data against the expected type. |
| 327 | |
| 328 | Args: |
| 329 | annotation: The direct type annotation given to the particular piece of data. |
| 330 | This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc |
| 331 | |
| 332 | inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type |
| 333 | is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in |
| 334 | the list can be transformed using the metadata from the container type. |
| 335 | |
| 336 | Defaults to the same value as the `annotation` argument. |
| 337 | """ |
| 338 | from .._compat import model_dump |
| 339 | |
| 340 | if inner_type is None: |
| 341 | inner_type = annotation |
| 342 | |
| 343 | stripped_type = strip_annotated_type(inner_type) |
| 344 | origin = get_origin(stripped_type) or stripped_type |
| 345 | if is_typeddict(stripped_type) and is_mapping(data): |
| 346 | return await _async_transform_typeddict(data, stripped_type) |
| 347 | |
| 348 | if origin == dict and is_mapping(data): |
| 349 | items_type = get_args(stripped_type)[1] |
| 350 | return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} |
| 351 | |
| 352 | if ( |
| 353 | # List[T] |
| 354 | (is_list_type(stripped_type) and is_list(data)) |
| 355 | # Iterable[T] |
| 356 | or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) |
| 357 | # Sequence[T] |
| 358 | or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) |
| 359 | ): |
| 360 | # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually |
| 361 | # intended as an iterable, so we don't transform it. |
| 362 | if isinstance(data, dict): |
| 363 | return cast(object, data) |
| 364 | |
| 365 | inner_type = extract_type_arg(stripped_type, 0) |
| 366 | if _no_transform_needed(inner_type): |
| 367 | # for some types there is no need to transform anything, so we can get a small |
| 368 | # perf boost from skipping that work. |
| 369 | # |
| 370 | # but we still need to convert to a list to ensure the data is json-serializable |
| 371 | if is_list(data): |
| 372 | return data |
| 373 | return list(data) |
| 374 | |
| 375 | return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] |
| 376 | |
| 377 | if is_union_type(stripped_type): |
no test coverage detected