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 (
@@ -456,7 +457,7 @@ def _build_request(
456
457
raise RuntimeError (f"Unexpected JSON data type, { type (json_data )} , cannot merge with `extra_body`" )
457
458
458
459
headers = self ._build_headers (options )
459
- params = _merge_mappings (self ._custom_query , options .params )
460
+ params = _merge_mappings (self .default_query , options .params )
460
461
content_type = headers .get ("Content-Type" )
461
462
462
463
# If the given Content-Type header is multipart/form-data then it
@@ -592,6 +593,12 @@ def default_headers(self) -> dict[str, str | Omit]:
592
593
** self ._custom_headers ,
593
594
}
594
595
596
+ @property
597
+ def default_query (self ) -> dict [str , object ]:
598
+ return {
599
+ ** self ._custom_query ,
600
+ }
601
+
595
602
def _validate_headers (
596
603
self ,
597
604
headers : Headers , # noqa: ARG002
@@ -616,7 +623,10 @@ def base_url(self, url: URL | str) -> None:
616
623
self ._base_url = self ._enforce_trailing_slash (url if isinstance (url , URL ) else URL (url ))
617
624
618
625
def platform_headers (self ) -> Dict [str , str ]:
619
- 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 )
620
630
621
631
def _parse_retry_after_header (self , response_headers : Optional [httpx .Headers ] = None ) -> float | None :
622
632
"""Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified.
@@ -1492,6 +1502,11 @@ async def _request(
1492
1502
stream_cls : type [_AsyncStreamT ] | None ,
1493
1503
remaining_retries : int | None ,
1494
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
+
1495
1510
cast_to = self ._maybe_override_cast_to (cast_to , options )
1496
1511
await self ._prepare_options (options )
1497
1512
@@ -1915,11 +1930,11 @@ def get_platform() -> Platform:
1915
1930
1916
1931
1917
1932
@lru_cache (maxsize = None )
1918
- def platform_headers (version : str ) -> Dict [str , str ]:
1933
+ def platform_headers (version : str , * , platform : Platform | None ) -> Dict [str , str ]:
1919
1934
return {
1920
1935
"X-Stainless-Lang" : "python" ,
1921
1936
"X-Stainless-Package-Version" : version ,
1922
- "X-Stainless-OS" : str (get_platform ()),
1937
+ "X-Stainless-OS" : str (platform or get_platform ()),
1923
1938
"X-Stainless-Arch" : str (get_architecture ()),
1924
1939
"X-Stainless-Runtime" : get_python_runtime (),
1925
1940
"X-Stainless-Runtime-Version" : get_python_version (),
0 commit comments