|
5 | 5 | import pathlib |
6 | 6 | from typing import Any, Mapping, TypeVar, cast |
7 | 7 | from datetime import date, datetime |
8 | | -from typing_extensions import Literal, get_args, override, get_type_hints |
| 8 | +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints |
9 | 9 |
|
10 | 10 | import anyio |
11 | 11 | import pydantic |
12 | 12 |
|
13 | 13 | from ._utils import ( |
14 | 14 | is_list, |
| 15 | + is_given, |
| 16 | + lru_cache, |
15 | 17 | is_mapping, |
16 | 18 | is_iterable, |
17 | 19 | ) |
@@ -108,6 +110,7 @@ class Params(TypedDict, total=False): |
108 | 110 | return cast(_T, transformed) |
109 | 111 |
|
110 | 112 |
|
| 113 | +@lru_cache(maxsize=8096) |
111 | 114 | def _get_annotated_type(type_: type) -> type | None: |
112 | 115 | """If the given type is an `Annotated` type then it is returned, if not `None` is returned. |
113 | 116 |
|
@@ -142,6 +145,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: |
142 | 145 | return key |
143 | 146 |
|
144 | 147 |
|
| 148 | +def _no_transform_needed(annotation: type) -> bool: |
| 149 | + return annotation == float or annotation == int |
| 150 | + |
| 151 | + |
145 | 152 | def _transform_recursive( |
146 | 153 | data: object, |
147 | 154 | *, |
@@ -184,6 +191,15 @@ def _transform_recursive( |
184 | 191 | return cast(object, data) |
185 | 192 |
|
186 | 193 | inner_type = extract_type_arg(stripped_type, 0) |
| 194 | + if _no_transform_needed(inner_type): |
| 195 | + # for some types there is no need to transform anything, so we can get a small |
| 196 | + # perf boost from skipping that work. |
| 197 | + # |
| 198 | + # but we still need to convert to a list to ensure the data is json-serializable |
| 199 | + if is_list(data): |
| 200 | + return data |
| 201 | + return list(data) |
| 202 | + |
187 | 203 | return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] |
188 | 204 |
|
189 | 205 | if is_union_type(stripped_type): |
@@ -245,6 +261,11 @@ def _transform_typeddict( |
245 | 261 | result: dict[str, object] = {} |
246 | 262 | annotations = get_type_hints(expected_type, include_extras=True) |
247 | 263 | for key, value in data.items(): |
| 264 | + if not is_given(value): |
| 265 | + # we don't need to include `NotGiven` values here as they'll |
| 266 | + # be stripped out before the request is sent anyway |
| 267 | + continue |
| 268 | + |
248 | 269 | type_ = annotations.get(key) |
249 | 270 | if type_ is None: |
250 | 271 | # we do not have a type annotation for this field, leave it as is |
@@ -332,6 +353,15 @@ async def _async_transform_recursive( |
332 | 353 | return cast(object, data) |
333 | 354 |
|
334 | 355 | inner_type = extract_type_arg(stripped_type, 0) |
| 356 | + if _no_transform_needed(inner_type): |
| 357 | + # for some types there is no need to transform anything, so we can get a small |
| 358 | + # perf boost from skipping that work. |
| 359 | + # |
| 360 | + # but we still need to convert to a list to ensure the data is json-serializable |
| 361 | + if is_list(data): |
| 362 | + return data |
| 363 | + return list(data) |
| 364 | + |
335 | 365 | return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] |
336 | 366 |
|
337 | 367 | if is_union_type(stripped_type): |
@@ -393,10 +423,25 @@ async def _async_transform_typeddict( |
393 | 423 | result: dict[str, object] = {} |
394 | 424 | annotations = get_type_hints(expected_type, include_extras=True) |
395 | 425 | for key, value in data.items(): |
| 426 | + if not is_given(value): |
| 427 | + # we don't need to include `NotGiven` values here as they'll |
| 428 | + # be stripped out before the request is sent anyway |
| 429 | + continue |
| 430 | + |
396 | 431 | type_ = annotations.get(key) |
397 | 432 | if type_ is None: |
398 | 433 | # we do not have a type annotation for this field, leave it as is |
399 | 434 | result[key] = value |
400 | 435 | else: |
401 | 436 | result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) |
402 | 437 | return result |
| 438 | + |
| 439 | + |
| 440 | +@lru_cache(maxsize=8096) |
| 441 | +def get_type_hints( |
| 442 | + obj: Any, |
| 443 | + globalns: dict[str, Any] | None = None, |
| 444 | + localns: Mapping[str, Any] | None = None, |
| 445 | + include_extras: bool = False, |
| 446 | +) -> dict[str, Any]: |
| 447 | + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) |
0 commit comments