|
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 (
|
@@ -359,6 +359,7 @@ def __init__(
|
359 | 359 | self._custom_query = custom_query or {}
|
360 | 360 | self._strict_response_validation = _strict_response_validation
|
361 | 361 | self._idempotency_header = None
|
| 362 | + self._platform: Platform | None = None |
362 | 363 |
|
363 | 364 | if max_retries is None: # pyright: ignore[reportUnnecessaryComparison]
|
364 | 365 | raise TypeError(
|
@@ -623,7 +624,10 @@ def base_url(self, url: URL | str) -> None:
|
623 | 624 | self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url))
|
624 | 625 |
|
625 | 626 | def platform_headers(self) -> Dict[str, str]:
|
626 |
| - return platform_headers(self._version) |
| 627 | + # the actual implementation is in a separate `lru_cache` decorated |
| 628 | + # function because adding `lru_cache` to methods will leak memory |
| 629 | + # https://github.com/python/cpython/issues/88476 |
| 630 | + return platform_headers(self._version, platform=self._platform) |
627 | 631 |
|
628 | 632 | def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None:
|
629 | 633 | """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified.
|
@@ -1513,6 +1517,11 @@ async def _request(
|
1513 | 1517 | stream_cls: type[_AsyncStreamT] | None,
|
1514 | 1518 | remaining_retries: int | None,
|
1515 | 1519 | ) -> ResponseT | _AsyncStreamT:
|
| 1520 | + if self._platform is None: |
| 1521 | + # `get_platform` can make blocking IO calls so we |
| 1522 | + # execute it earlier while we are in an async context |
| 1523 | + self._platform = await asyncify(get_platform)() |
| 1524 | + |
1516 | 1525 | cast_to = self._maybe_override_cast_to(cast_to, options)
|
1517 | 1526 | await self._prepare_options(options)
|
1518 | 1527 |
|
@@ -1949,11 +1958,11 @@ def get_platform() -> Platform:
|
1949 | 1958 |
|
1950 | 1959 |
|
1951 | 1960 | @lru_cache(maxsize=None)
|
1952 |
| -def platform_headers(version: str) -> Dict[str, str]: |
| 1961 | +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: |
1953 | 1962 | return {
|
1954 | 1963 | "X-Stainless-Lang": "python",
|
1955 | 1964 | "X-Stainless-Package-Version": version,
|
1956 |
| - "X-Stainless-OS": str(get_platform()), |
| 1965 | + "X-Stainless-OS": str(platform or get_platform()), |
1957 | 1966 | "X-Stainless-Arch": str(get_architecture()),
|
1958 | 1967 | "X-Stainless-Runtime": get_python_runtime(),
|
1959 | 1968 | "X-Stainless-Runtime-Version": get_python_version(),
|
|
0 commit comments