|
60 | 60 | RequestOptions,
|
61 | 61 | ModelBuilderProtocol,
|
62 | 62 | )
|
63 |
| -from ._utils import is_dict, is_list, is_given, lru_cache, is_mapping |
| 63 | +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping |
64 | 64 | from ._compat import model_copy, model_dump
|
65 | 65 | from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
|
66 | 66 | from ._response import (
|
@@ -358,6 +358,7 @@ def __init__(
|
358 | 358 | self._custom_query = custom_query or {}
|
359 | 359 | self._strict_response_validation = _strict_response_validation
|
360 | 360 | self._idempotency_header = None
|
| 361 | + self._platform: Platform | None = None |
361 | 362 |
|
362 | 363 | if max_retries is None: # pyright: ignore[reportUnnecessaryComparison]
|
363 | 364 | raise TypeError(
|
@@ -622,7 +623,10 @@ def base_url(self, url: URL | str) -> None:
|
622 | 623 | self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url))
|
623 | 624 |
|
624 | 625 | def platform_headers(self) -> Dict[str, str]:
|
625 |
| - return platform_headers(self._version) |
| 626 | + # the actual implementation is in a separate `lru_cache` decorated |
| 627 | + # function because adding `lru_cache` to methods will leak memory |
| 628 | + # https://github.com/python/cpython/issues/88476 |
| 629 | + return platform_headers(self._version, platform=self._platform) |
626 | 630 |
|
627 | 631 | def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None:
|
628 | 632 | """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified.
|
@@ -1498,6 +1502,11 @@ async def _request(
|
1498 | 1502 | stream_cls: type[_AsyncStreamT] | None,
|
1499 | 1503 | remaining_retries: int | None,
|
1500 | 1504 | ) -> ResponseT | _AsyncStreamT:
|
| 1505 | + if self._platform is None: |
| 1506 | + # `get_platform` can make blocking IO calls so we |
| 1507 | + # execute it earlier while we are in an async context |
| 1508 | + self._platform = await asyncify(get_platform)() |
| 1509 | + |
1501 | 1510 | cast_to = self._maybe_override_cast_to(cast_to, options)
|
1502 | 1511 | await self._prepare_options(options)
|
1503 | 1512 |
|
@@ -1921,11 +1930,11 @@ def get_platform() -> Platform:
|
1921 | 1930 |
|
1922 | 1931 |
|
1923 | 1932 | @lru_cache(maxsize=None)
|
1924 |
| -def platform_headers(version: str) -> Dict[str, str]: |
| 1933 | +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: |
1925 | 1934 | return {
|
1926 | 1935 | "X-Stainless-Lang": "python",
|
1927 | 1936 | "X-Stainless-Package-Version": version,
|
1928 |
| - "X-Stainless-OS": str(get_platform()), |
| 1937 | + "X-Stainless-OS": str(platform or get_platform()), |
1929 | 1938 | "X-Stainless-Arch": str(get_architecture()),
|
1930 | 1939 | "X-Stainless-Runtime": get_python_runtime(),
|
1931 | 1940 | "X-Stainless-Runtime-Version": get_python_version(),
|
|
0 commit comments