-
Notifications
You must be signed in to change notification settings - Fork 980
Expand file tree
/
Copy path__init__.py
More file actions
100 lines (88 loc) · 3.85 KB
/
Copy path__init__.py
File metadata and controls
100 lines (88 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import threading
from typing import Dict, Optional
import httpx
from pyqwest import HTTPTransport
from e2b.api import (
ProxyConfig,
connection_retries,
make_async_logging_event_hooks,
pool_idle_timeout,
pool_max_idle_per_host,
proxy_to_config,
)
from e2b.api.client_async import AsyncApiPyqwestTransport, ConnectionRetryTransport
from e2b.api.metadata import default_headers
from e2b.exceptions import AuthenticationException
from e2b.volume.client.client import AuthenticatedClient as AsyncVolumeApiClient
from e2b.volume.connection_config import READ_TIMEOUT, VolumeConnectionConfig
def get_api_client(
config: VolumeConnectionConfig, *, for_streaming: bool = False, **kwargs
) -> AsyncVolumeApiClient:
if config.access_token is None:
raise AuthenticationException(
"Volume token is required for volume content operations. "
"Use `AsyncVolume.create`/`AsyncVolume.connect` to obtain it "
"or pass `token` in options.",
)
headers = {
**default_headers,
**(config.headers or {}),
}
request_timeout = config.request_timeout
return AsyncVolumeApiClient(
base_url=config.api_url,
token=config.access_token,
auth_header_name="Authorization",
prefix="Bearer",
headers=headers,
timeout=(
httpx.Timeout(request_timeout) if request_timeout is not None else None
),
httpx_args={
# The proxy lives in the cached transport; passing `proxy` here too
# would mount a fresh, never-closed proxy transport per client.
"transport": get_transport(config, for_streaming=for_streaming),
"event_hooks": make_async_logging_event_hooks(config.logger),
},
**kwargs,
)
_transport_lock = threading.Lock()
# One transport (= one connection pool) per (proxy, streaming) pair; None is
# the direct pool. pyqwest's I/O runs on its own Rust runtime, so unlike the
# httpx transport this replaced, the transport is not bound to an event loop
# and the cache is process-global rather than per-loop.
_transports: Dict[tuple[Optional[ProxyConfig], bool], AsyncApiPyqwestTransport] = {}
def get_transport(
config: VolumeConnectionConfig, *, for_streaming: bool = False
) -> AsyncApiPyqwestTransport:
"""The shared pyqwest-backed httpx transports for volume content API calls.
The streaming transport carries ``read_timeout``, the idle bound on every
read: it resets after each successful read, so it caps how long a
streamed download may stall without limiting total transfer time. It is
fixed per transport — the adapter's per-request timeouts are
whole-request deadlines, and the sync adapter does not bound body reads
at all. Only streamed downloads use it: reqwest's read timer keeps
running while a request body is sent and while waiting for the response
head, so on the regular transport it would cut off uploads and slow
unary responses longer than the idle bound (those stay bounded by their
whole-request deadlines instead).
"""
proxy = proxy_to_config(config.proxy)
key = (proxy, for_streaming)
with _transport_lock:
transport = _transports.get(key)
if transport is None:
transport = AsyncApiPyqwestTransport(
ConnectionRetryTransport(
HTTPTransport(
tls_include_system_certs=True,
proxy=proxy.to_pyqwest() if proxy is not None else None,
pool_idle_timeout=pool_idle_timeout,
pool_max_idle_per_host=pool_max_idle_per_host,
read_timeout=READ_TIMEOUT if for_streaming else None,
),
max_retries=connection_retries,
)
)
_transports[key] = transport
return transport