-
Notifications
You must be signed in to change notification settings - Fork 980
Expand file tree
/
Copy path__init__.py
More file actions
99 lines (87 loc) · 3.75 KB
/
Copy path__init__.py
File metadata and controls
99 lines (87 loc) · 3.75 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
import threading
from typing import Dict, Optional
import httpx
from pyqwest import SyncHTTPTransport
from e2b.api import (
ProxyConfig,
connection_retries,
make_logging_event_hooks,
pool_idle_timeout,
pool_max_idle_per_host,
proxy_to_config,
)
from e2b.api.client_sync import ApiPyqwestTransport, ConnectionRetryTransport
from e2b.api.metadata import default_headers
from e2b.exceptions import AuthenticationException
from e2b.volume.client.client import AuthenticatedClient as VolumeApiClient
from e2b.volume.connection_config import READ_TIMEOUT, VolumeConnectionConfig
def get_api_client(
config: VolumeConnectionConfig, *, for_streaming: bool = False, **kwargs
) -> VolumeApiClient:
if config.access_token is None:
raise AuthenticationException(
"Volume token is required for volume content operations. "
"Use `Volume.create`/`Volume.connect` to obtain it "
"or pass `token` in options.",
)
headers = {
**default_headers,
**(config.headers or {}),
}
request_timeout = config.request_timeout
return VolumeApiClient(
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_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 transports are thread-safe, so unlike the httpx
# transport this replaced, the cache is process-global rather than per-thread.
_transports: Dict[tuple[Optional[ProxyConfig], bool], ApiPyqwestTransport] = {}
def get_transport(
config: VolumeConnectionConfig, *, for_streaming: bool = False
) -> ApiPyqwestTransport:
"""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 = ApiPyqwestTransport(
ConnectionRetryTransport(
SyncHTTPTransport(
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