modAL 1.5.4.dev9__py3-none-any.whl → 1.5.4.dev11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modal/_utils/auth_token_manager.py +84 -60
- modal/_utils/task_command_router_client.py +4 -3
- modal/client.pyi +2 -2
- modal/sandbox.py +3 -0
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/METADATA +1 -1
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/RECORD +18 -18
- modal_proto/__init__.py +7 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +1185 -1155
- modal_proto/api_pb2.pyi +67 -0
- modal_proto/api_pb2_grpc.py +34 -0
- modal_proto/api_pb2_grpc.pyi +17 -0
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/__init__.py +1 -1
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/WHEEL +0 -0
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/entry_points.txt +0 -0
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/licenses/LICENSE +0 -0
- {modal-1.5.4.dev9.dist-info → modal-1.5.4.dev11.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import base64
|
|
4
4
|
import json
|
|
5
|
+
import random
|
|
5
6
|
import time
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -17,8 +18,14 @@ AUTH_DENIED_EXCEPTIONS = (AuthError, PermissionDeniedError)
|
|
|
17
18
|
class _AuthTokenManager:
|
|
18
19
|
"""Handles fetching and refreshing of the input plane auth token."""
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
-
|
|
21
|
+
# Fraction of the token's lifetime to use before refreshing.
|
|
22
|
+
REFRESH_FRACTION = 0.5
|
|
23
|
+
# Width of the random band added on top of REFRESH_FRACTION, so that clients issued
|
|
24
|
+
# same-lifetime tokens at the same moment (a fan-out of containers starting together)
|
|
25
|
+
# spread their refreshes out instead of refreshing in lockstep every cycle. The band
|
|
26
|
+
# only ever delays a refresh, so the margin before expiry stays at no less than
|
|
27
|
+
# 1 - REFRESH_FRACTION - REFRESH_JITTER of the lifetime.
|
|
28
|
+
REFRESH_JITTER = 0.1
|
|
22
29
|
# Bound each AuthTokenGet attempt so a refresh can't hang.
|
|
23
30
|
AUTH_TOKEN_TIMEOUT = 5.0
|
|
24
31
|
AUTH_TOKEN_RETRY_TOTAL_TIMEOUT = 3 * AUTH_TOKEN_TIMEOUT
|
|
@@ -33,9 +40,10 @@ class _AuthTokenManager:
|
|
|
33
40
|
self._stub = stub
|
|
34
41
|
self._token = ""
|
|
35
42
|
self._expiry = 0.0
|
|
43
|
+
self._refresh_at = 0.0
|
|
36
44
|
self._retry_after = 0.0
|
|
37
45
|
self._backoff = self.FAILURE_BACKOFF_BASE
|
|
38
|
-
self.
|
|
46
|
+
self._refresh_task: asyncio.Task | None = None
|
|
39
47
|
|
|
40
48
|
async def get_token(self) -> str:
|
|
41
49
|
"""
|
|
@@ -45,21 +53,20 @@ class _AuthTokenManager:
|
|
|
45
53
|
concurrently by multiple coroutines, all requests will block until the token has been fetched. But only one
|
|
46
54
|
coroutine will actually make a request to the control plane to fetch the new token. This ensures we do not hit
|
|
47
55
|
the control plane with more requests than needed.
|
|
48
|
-
3. Has a valid cached token
|
|
49
|
-
and cache it. If `get_token` is called concurrently, only one request will fetch the new token, and the
|
|
50
|
-
will be given the old (but still valid) token - i.e. they will not block.
|
|
56
|
+
3. Has a valid cached token that has used up its refresh fraction of its lifetime. In this case we fetch a new
|
|
57
|
+
token and cache it. If `get_token` is called concurrently, only one request will fetch the new token, and the
|
|
58
|
+
others will be given the old (but still valid) token - i.e. they will not block.
|
|
51
59
|
"""
|
|
52
60
|
if not self._token or self._is_expired():
|
|
53
61
|
# We either have no token or it is expired - block everyone until we get a new token
|
|
54
62
|
await self._try_refresh_token()
|
|
55
63
|
elif self._should_refresh():
|
|
56
|
-
# The token
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# The lock is taken, so someone else is refreshing. Continue to use the old token.
|
|
64
|
+
# The token is still valid, but has used up its refresh fraction of its lifetime.
|
|
65
|
+
if self._refresh_task is not None:
|
|
66
|
+
# A refresh is in flight, so someone else is refreshing. Continue to use the old token.
|
|
60
67
|
return self._token
|
|
61
68
|
else:
|
|
62
|
-
#
|
|
69
|
+
# Nobody is refreshing, so we need to fetch a new token.
|
|
63
70
|
await self._try_refresh_token()
|
|
64
71
|
|
|
65
72
|
return self._token
|
|
@@ -76,58 +83,63 @@ class _AuthTokenManager:
|
|
|
76
83
|
|
|
77
84
|
async def _refresh_token(self):
|
|
78
85
|
"""
|
|
79
|
-
Fetch a new token from the control plane.
|
|
80
|
-
|
|
86
|
+
Fetch a new token from the control plane. Concurrent callers share a single request: the first caller starts it
|
|
87
|
+
and the others await the same task, so a burst of callers results in one RPC whether it succeeds or fails. The
|
|
88
|
+
task is owned by the event loop rather than by the caller that started it, so it runs to completion even if that
|
|
89
|
+
caller goes away.
|
|
81
90
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# Double check inside lock - maybe another coroutine refreshed already. This happens the first time we fetch
|
|
85
|
-
# the token. The first coroutine will fetch the token, while the others block on the lock, waiting for the
|
|
86
|
-
# new token. Once we have a new token, the other coroutines will unblock and return from here.
|
|
91
|
+
if self._refresh_task is None:
|
|
92
|
+
# Maybe another coroutine refreshed already, or we're cooling down after a failure.
|
|
87
93
|
if self._token and not self._should_refresh():
|
|
88
94
|
return
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
# Note: the task is created in async code so it binds to the synchronicity event loop.
|
|
96
|
+
self._refresh_task = asyncio.create_task(self._fetch_token())
|
|
97
|
+
self._refresh_task.add_done_callback(self._refresh_task_done)
|
|
98
|
+
# Shielded so a cancelled caller doesn't cancel the refresh for everyone else.
|
|
99
|
+
await asyncio.shield(self._refresh_task)
|
|
100
|
+
|
|
101
|
+
def _refresh_task_done(self, task: "asyncio.Task"):
|
|
102
|
+
if self._refresh_task is task:
|
|
103
|
+
self._refresh_task = None
|
|
104
|
+
if not task.cancelled():
|
|
105
|
+
# Retrieve the exception, if any, so a failure isn't logged as unhandled when no caller is waiting.
|
|
106
|
+
task.exception()
|
|
107
|
+
|
|
108
|
+
async def _fetch_token(self):
|
|
109
|
+
"""Make the AuthTokenGet request and cache its token, or arm the failure backoff."""
|
|
110
|
+
try:
|
|
111
|
+
resp: api_pb2.AuthTokenGetResponse = await self._stub.AuthTokenGet(
|
|
112
|
+
api_pb2.AuthTokenGetRequest(),
|
|
113
|
+
# No cached token to fall back on, so a failure is user-visible: retry transient errors.
|
|
114
|
+
# Otherwise one attempt, and _retry_after handles the cooldown.
|
|
115
|
+
retry=Retry(
|
|
116
|
+
attempt_timeout=self.AUTH_TOKEN_TIMEOUT,
|
|
117
|
+
total_timeout=self.AUTH_TOKEN_RETRY_TOTAL_TIMEOUT,
|
|
118
|
+
max_retries=0 if self._token else DEFAULT_MAX_RETRIES,
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
if not resp.token:
|
|
122
|
+
# Not expected
|
|
123
|
+
raise ExecutionError(
|
|
124
|
+
"Internal error: Did not receive auth token from server. Please contact Modal support."
|
|
99
125
|
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
except Exception as e:
|
|
118
|
-
# Back off (exponentially on consecutive failures) so we don't hammer a struggling server.
|
|
119
|
-
self._retry_after = time.time() + self._backoff
|
|
120
|
-
self._backoff = min(self._backoff * 2, self.FAILURE_BACKOFF_MAX)
|
|
121
|
-
raise
|
|
122
|
-
|
|
123
|
-
async def _get_lock(self) -> asyncio.Lock:
|
|
124
|
-
# Note: this function runs no async code but is marked as async to ensure it's
|
|
125
|
-
# being run inside the synchronicity event loop and binds the lock to the
|
|
126
|
-
# correct event loop on Python 3.9 which eagerly assigns event loops on
|
|
127
|
-
# constructions of locks
|
|
128
|
-
if self._lock is None:
|
|
129
|
-
self._lock = asyncio.Lock()
|
|
130
|
-
return self._lock
|
|
126
|
+
if exp := self._decode_jwt(resp.token).get("exp"):
|
|
127
|
+
expiry = float(exp)
|
|
128
|
+
else:
|
|
129
|
+
# This should never happen.
|
|
130
|
+
logger.warning("x-modal-auth-token does not contain exp field")
|
|
131
|
+
expiry = time.time() + self.DEFAULT_EXPIRY_OFFSET
|
|
132
|
+
self._token = resp.token
|
|
133
|
+
self._set_expiry(expiry)
|
|
134
|
+
self._retry_after = 0.0
|
|
135
|
+
self._backoff = self.FAILURE_BACKOFF_BASE
|
|
136
|
+
except AUTH_DENIED_EXCEPTIONS:
|
|
137
|
+
raise
|
|
138
|
+
except Exception as e:
|
|
139
|
+
# Back off (exponentially on consecutive failures) so we don't hammer a struggling server.
|
|
140
|
+
self._retry_after = time.time() + self._backoff
|
|
141
|
+
self._backoff = min(self._backoff * 2, self.FAILURE_BACKOFF_MAX)
|
|
142
|
+
raise
|
|
131
143
|
|
|
132
144
|
@staticmethod
|
|
133
145
|
def _decode_jwt(token: str) -> dict[str, Any]:
|
|
@@ -150,8 +162,20 @@ class _AuthTokenManager:
|
|
|
150
162
|
# Fetch only when the token is stale/expiring and we're not in a post-failure backoff.
|
|
151
163
|
return self._needs_refresh() and not self._in_backoff()
|
|
152
164
|
|
|
165
|
+
def _set_expiry(self, expiry: float) -> None:
|
|
166
|
+
"""Record a new expiry and schedule the proactive refresh a jittered fraction of the way through.
|
|
167
|
+
|
|
168
|
+
The delay is measured from the remaining lifetime as this client sees it, so a client whose clock is
|
|
169
|
+
skewed relative to the issuer still keeps a margin proportional to the lifetime it observes.
|
|
170
|
+
"""
|
|
171
|
+
now = time.time()
|
|
172
|
+
self._expiry = expiry
|
|
173
|
+
remaining = max(expiry - now, 0.0)
|
|
174
|
+
fraction = self.REFRESH_FRACTION + random.random() * self.REFRESH_JITTER
|
|
175
|
+
self._refresh_at = now + remaining * fraction
|
|
176
|
+
|
|
153
177
|
def _needs_refresh(self):
|
|
154
|
-
return time.time() >=
|
|
178
|
+
return time.time() >= self._refresh_at
|
|
155
179
|
|
|
156
180
|
def _is_expired(self):
|
|
157
181
|
return time.time() >= self._expiry
|
|
@@ -292,9 +292,10 @@ class TaskCommandRouterClient:
|
|
|
292
292
|
) -> "TaskCommandRouterClient":
|
|
293
293
|
"""Initialize a TaskCommandRouterClient for a V2 sandbox.
|
|
294
294
|
|
|
295
|
-
`access` is the router access returned by SandboxCreateV2,
|
|
296
|
-
freshly
|
|
297
|
-
a re-attached sandbox, or the server could not mint a token — we
|
|
295
|
+
`access` is the router access returned by SandboxCreateV2/SandboxRestoreV2,
|
|
296
|
+
which lets a freshly scheduled sandbox connect without a round-trip. When it
|
|
297
|
+
is absent — a re-attached sandbox, or the server could not mint a token — we
|
|
298
|
+
ask for it.
|
|
298
299
|
"""
|
|
299
300
|
if access is not None:
|
|
300
301
|
url, jwt = access.url, access.jwt
|
modal/client.pyi
CHANGED
|
@@ -37,7 +37,7 @@ class _Client:
|
|
|
37
37
|
server_url: str,
|
|
38
38
|
client_type: int,
|
|
39
39
|
credentials: typing.Optional[tuple[str, str]],
|
|
40
|
-
version: str = "1.5.4.
|
|
40
|
+
version: str = "1.5.4.dev11",
|
|
41
41
|
):
|
|
42
42
|
"""mdmd:hidden
|
|
43
43
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -207,7 +207,7 @@ class Client:
|
|
|
207
207
|
server_url: str,
|
|
208
208
|
client_type: int,
|
|
209
209
|
credentials: typing.Optional[tuple[str, str]],
|
|
210
|
-
version: str = "1.5.4.
|
|
210
|
+
version: str = "1.5.4.dev11",
|
|
211
211
|
):
|
|
212
212
|
"""mdmd:hidden
|
|
213
213
|
The Modal client object is not intended to be instantiated directly by users.
|
modal/sandbox.py
CHANGED
|
@@ -2288,6 +2288,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
2288
2288
|
|
|
2289
2289
|
sandbox = await _Sandbox.from_id(restore_resp.sandbox_id, client)
|
|
2290
2290
|
sandbox._task_id = restore_resp.task_id
|
|
2291
|
+
# Seeded after from_id, whose hydration resets the field.
|
|
2292
|
+
if restore_resp.HasField("command_router_access"):
|
|
2293
|
+
sandbox._init_command_router_access = restore_resp.command_router_access
|
|
2291
2294
|
return sandbox
|
|
2292
2295
|
|
|
2293
2296
|
@property
|
|
@@ -34,7 +34,7 @@ modal/billing.py,sha256=E1-P_P8bJFLp-bTaggYd4BaaViImPB5UdtXiY8VeAZQ,390
|
|
|
34
34
|
modal/billing.pyi,sha256=4m7VLYNS8IiJ2I3CJx0awvrOM6BILWRVLfdeqtAYr8M,7189
|
|
35
35
|
modal/call_graph.py,sha256=XGALIDn1bLchWo_goyX23bcPjzCZcfqKUo8INIfDapo,1775
|
|
36
36
|
modal/client.py,sha256=NjpAL0fIKabEaN0uXJPEoI1OTjFtXSp4tSck-M-WF3k,16360
|
|
37
|
-
modal/client.pyi,sha256=
|
|
37
|
+
modal/client.pyi,sha256=QFNaYNlNXJCAo7pTbX4AgqHCxU5VOx_qpUcuCBHYI3E,17999
|
|
38
38
|
modal/cloud_bucket_mount.py,sha256=NrdNV9r9aL3QFrlt-Dhtd7_K3f8v1igrHf2FRwgJ1CI,6827
|
|
39
39
|
modal/cloud_bucket_mount.pyi,sha256=0E8d53pMh2KsFa-rMh_i6LxlzW4cMV8drVmmkPHSqZk,8149
|
|
40
40
|
modal/cls.py,sha256=TxRxHdY4L6AZ6I16DNPgDryAkGtlFadD-Mm0ovtfN58,41206
|
|
@@ -76,7 +76,7 @@ modal/retries.py,sha256=tSGB5bGM5xZZUDXMkuelIEoQJY0vOukOssZhwc6tXAo,5014
|
|
|
76
76
|
modal/runner.py,sha256=oqKyahvUEDpbGcNkxoCtKqFUShKWhoYEPecn7pk-fc4,31090
|
|
77
77
|
modal/runner.pyi,sha256=HjkJBkv3GPIe9occmuQUBRgVuw82vrTL0urM8zV4TaY,9760
|
|
78
78
|
modal/running_app.py,sha256=xSR1VVlhxEFytmjx2x697_0GpD25Vc9crhp6r117_QY,1145
|
|
79
|
-
modal/sandbox.py,sha256=
|
|
79
|
+
modal/sandbox.py,sha256=p-QgzNALkRb0q_qRc-K28HpgAe1LRyomc3URd3QsRBY,126078
|
|
80
80
|
modal/sandbox.pyi,sha256=PXlInwHCuZYNXkRO0AKMpX3yKTAmhliN-ajiXltcsb0,146622
|
|
81
81
|
modal/sandbox_fs.py,sha256=GSuPlrLSdQ7fG7c_TGMd-VWs_s6RqQxMvLivdvC6s4s,27002
|
|
82
82
|
modal/sandbox_fs.pyi,sha256=TJWx8h7x3yRC7KUxeF7q62_0a7w1IHufnJL7AYEojAg,45285
|
|
@@ -118,7 +118,7 @@ modal/_runtime/user_code_imports.py,sha256=5MgewJzAeZqop47FQyB-lEpHBfC50ApHgSb86
|
|
|
118
118
|
modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
|
|
119
119
|
modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
|
|
120
120
|
modal/_utils/async_utils.py,sha256=2pBzV_ErMH055TGxT0re2szYUN6k8X94RkLwIp_JbZw,46393
|
|
121
|
-
modal/_utils/auth_token_manager.py,sha256=
|
|
121
|
+
modal/_utils/auth_token_manager.py,sha256=qhnbTv6EO5_BxvUUlyCrNt8sQaUIZ77CdSrzBH9sLaU,8563
|
|
122
122
|
modal/_utils/blob_utils.py,sha256=KyqXWF82PdfA6pvEu99QwGfSqyHQxPUvjyi760GXkdY,26242
|
|
123
123
|
modal/_utils/browser_utils.py,sha256=Yqq0qU4Id6oaDOdeT1JEmbCN7vj4baNJJraJhRWU8ZI,1085
|
|
124
124
|
modal/_utils/bytes_io_segment_payload.py,sha256=2OrIrjrsJR38a9D7JCITLqGvIPaiWViTQhxtcZlFn_M,4328
|
|
@@ -142,7 +142,7 @@ modal/_utils/proxy_support.py,sha256=hLA_XojYxzPysOiLqyUbB-SgMLmn_X8WtXTk92xh7g4
|
|
|
142
142
|
modal/_utils/rand_pb_testing.py,sha256=kwAKIros_g6XPXluiJAktQ79YPkD9SGopKGl83GQ1l0,4361
|
|
143
143
|
modal/_utils/sandbox_fs_utils.py,sha256=rsfo1uDbVSoiY21XERDzsmp-DALcNmlPfJ6jn_2p5T0,15680
|
|
144
144
|
modal/_utils/shell_utils.py,sha256=_2yHrlx4Crx5JWQXECLPQ51kclu8H-U3N7jOkBN7q5U,3390
|
|
145
|
-
modal/_utils/task_command_router_client.py,sha256=
|
|
145
|
+
modal/_utils/task_command_router_client.py,sha256=QJxxzceZDrr9BEDVs524tZ17tgO3wEVDw-4gF8-P96U,46073
|
|
146
146
|
modal/_utils/time_utils.py,sha256=JaFyW8y4iimzhrGT5KlQmlXH4oMZrZOGCtUBZAV-gfU,10965
|
|
147
147
|
modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
|
148
148
|
modal/_vendor/a2wsgi_wsgi.py,sha256=RVLIYVJA8a8o96QxEXYT5tuog2zev2S24XOuAVTXZAs,21872
|
|
@@ -198,7 +198,7 @@ modal/experimental/flash.py,sha256=GAJ1c-EGL8M3_loAkqr5gdUfySMHY3U_oc6RywfZ8-0,3
|
|
|
198
198
|
modal/experimental/flash.pyi,sha256=lpTqJFZRMGWscw0xpYZVdBMnAYjj16asXE4_64A6itQ,17774
|
|
199
199
|
modal/experimental/ipython.py,sha256=kJ6o9wkAY_MiSb7paewI7f1MJAeqdi8TX5pBdhr_mUI,2925
|
|
200
200
|
modal/skills/modal/SKILL.md,sha256=WzLjYJ1utjc_ZOf6qZTPwB6wxxwRYB5PibdvIf_FLy0,5316
|
|
201
|
-
modal-1.5.4.
|
|
201
|
+
modal-1.5.4.dev11.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
202
202
|
modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
|
203
203
|
modal_docs/gen_cli_docs.py,sha256=9-b8vl2EsLWD9zJqpCK8IUi5h44Bjn3i3rSdedZV4bA,6415
|
|
204
204
|
modal_docs/gen_cli_docs_main.py,sha256=UPGr7O1DYTIRLwfINe2rbdh89J3cPVTU1OWL-SjxnOQ,198
|
|
@@ -209,23 +209,23 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
|
|
|
209
209
|
modal_docs/mdmd/mdmd.py,sha256=J49V8ak-hnF7SyBJDc-p3a_aEZpaAImkz2Rf74mDvA4,25070
|
|
210
210
|
modal_docs/mdmd/signatures.py,sha256=OASroKle763Kfumn4Ek4PABJXEGP56JMCTug44QoBx4,11327
|
|
211
211
|
modal_docs/mdmd/types.py,sha256=tv7fvziPr-MeDzOp4e2pK5h3d1DXTUOO74rPqDoBn6g,736
|
|
212
|
-
modal_proto/__init__.py,sha256=
|
|
213
|
-
modal_proto/api_grpc.py,sha256=
|
|
214
|
-
modal_proto/api_pb2.py,sha256=
|
|
215
|
-
modal_proto/api_pb2.pyi,sha256=
|
|
216
|
-
modal_proto/api_pb2_grpc.py,sha256=
|
|
217
|
-
modal_proto/api_pb2_grpc.pyi,sha256=
|
|
218
|
-
modal_proto/modal_api_grpc.py,sha256=
|
|
212
|
+
modal_proto/__init__.py,sha256=Hkou4lcD5tArEkW_9v9KZhLY7fl0fA9-O28beKU55pg,396
|
|
213
|
+
modal_proto/api_grpc.py,sha256=T7G1w30VHACC466T9WgEuAUKzk1T55K4xhh9yrx30jE,184199
|
|
214
|
+
modal_proto/api_pb2.py,sha256=Cv-bAV11PEAgWXYeQHcLBgmjvgjDvwR1MrrdT7hNXr0,503011
|
|
215
|
+
modal_proto/api_pb2.pyi,sha256=RrSAYQpmuc2pCz3sF3zbTcmC7R1oOppakVUdwm-IKOs,695248
|
|
216
|
+
modal_proto/api_pb2_grpc.py,sha256=iVzwMoa9O8vZKvPX4gCFq3f5QYTMpHFsLa-mEc-Zwis,395203
|
|
217
|
+
modal_proto/api_pb2_grpc.pyi,sha256=Clzhm_sI17aGhz31Rtl2MpCdfeM_8PlGu2O4aH92fbk,92749
|
|
218
|
+
modal_proto/modal_api_grpc.py,sha256=36yBgbLRHAKnchTZlRHVQyYWbFTTc_xrx6j8JHz1Ozo,29059
|
|
219
219
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
220
220
|
modal_proto/task_command_router_grpc.py,sha256=UurJEgLeAc2CCdEsCOFE9DgIQy1ra6bXHXJLNBK81Mg,21284
|
|
221
221
|
modal_proto/task_command_router_pb2.py,sha256=4cyDT_uPGi54blsO663bnSzUe7aBFqi9F4iM8N1JPaU,41130
|
|
222
222
|
modal_proto/task_command_router_pb2.pyi,sha256=5xfhnwtTDTmJo1Nv0tPBJrYzb0aeilY2Lm92wWT_6yw,46211
|
|
223
223
|
modal_proto/task_command_router_pb2_grpc.py,sha256=zuFkJgKcswB2ftjnyEgs70dp61VMA7gK0h0uzR3gYFw,42563
|
|
224
224
|
modal_proto/task_command_router_pb2_grpc.pyi,sha256=UjAJFqz9DpOFXP04fVm5lB2eAyTtjOg3oKjfWei8yLU,14262
|
|
225
|
-
modal_version/__init__.py,sha256=
|
|
225
|
+
modal_version/__init__.py,sha256=f197D89MvvrXc5P_dA9HTULCibAz73m15IoOoWlbd3A,121
|
|
226
226
|
modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
|
|
227
|
-
modal-1.5.4.
|
|
228
|
-
modal-1.5.4.
|
|
229
|
-
modal-1.5.4.
|
|
230
|
-
modal-1.5.4.
|
|
231
|
-
modal-1.5.4.
|
|
227
|
+
modal-1.5.4.dev11.dist-info/METADATA,sha256=Zebs41aU_dhmL3bh94SKRb1jqPY9pnIwnlZiXsJkf88,2513
|
|
228
|
+
modal-1.5.4.dev11.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
229
|
+
modal-1.5.4.dev11.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
|
230
|
+
modal-1.5.4.dev11.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
|
231
|
+
modal-1.5.4.dev11.dist-info/RECORD,,
|
modal_proto/__init__.py
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
# Copyright Modal Labs 2024
|
|
2
|
+
"""Modal gRPC types for Python.
|
|
3
|
+
|
|
4
|
+
Warning: direct usage of Modal's gRPC API is discouraged,
|
|
5
|
+
and no support or compatibility guarantees are provided.
|
|
6
|
+
Message fields or entire RPCs may be changed or removed
|
|
7
|
+
without notice. We recommend using official SDKs instead.
|
|
8
|
+
"""
|
|
2
9
|
from pkgutil import extend_path
|
|
3
10
|
|
|
4
11
|
__path__ = extend_path(__path__, __name__) # type: ignore[has-type]
|
modal_proto/api_grpc.py
CHANGED
|
@@ -123,6 +123,10 @@ class ModalClientBase(abc.ABC):
|
|
|
123
123
|
async def AuthTokenGet(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.AuthTokenGetRequest, modal_proto.api_pb2.AuthTokenGetResponse]') -> None:
|
|
124
124
|
pass
|
|
125
125
|
|
|
126
|
+
@abc.abstractmethod
|
|
127
|
+
async def AutoscalerOverrideHistory(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.AutoscalerOverrideHistoryRequest, modal_proto.api_pb2.AutoscalerOverrideHistoryResponse]') -> None:
|
|
128
|
+
pass
|
|
129
|
+
|
|
126
130
|
@abc.abstractmethod
|
|
127
131
|
async def BlobCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.BlobCreateRequest, modal_proto.api_pb2.BlobCreateResponse]') -> None:
|
|
128
132
|
pass
|
|
@@ -1117,6 +1121,12 @@ class ModalClientBase(abc.ABC):
|
|
|
1117
1121
|
modal_proto.api_pb2.AuthTokenGetRequest,
|
|
1118
1122
|
modal_proto.api_pb2.AuthTokenGetResponse,
|
|
1119
1123
|
),
|
|
1124
|
+
'/modal.client.ModalClient/AutoscalerOverrideHistory': grpclib.const.Handler(
|
|
1125
|
+
self.AutoscalerOverrideHistory,
|
|
1126
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1127
|
+
modal_proto.api_pb2.AutoscalerOverrideHistoryRequest,
|
|
1128
|
+
modal_proto.api_pb2.AutoscalerOverrideHistoryResponse,
|
|
1129
|
+
),
|
|
1120
1130
|
'/modal.client.ModalClient/BlobCreate': grpclib.const.Handler(
|
|
1121
1131
|
self.BlobCreate,
|
|
1122
1132
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -2533,6 +2543,12 @@ class ModalClientStub:
|
|
|
2533
2543
|
modal_proto.api_pb2.AuthTokenGetRequest,
|
|
2534
2544
|
modal_proto.api_pb2.AuthTokenGetResponse,
|
|
2535
2545
|
)
|
|
2546
|
+
self.AutoscalerOverrideHistory = grpclib.client.UnaryUnaryMethod(
|
|
2547
|
+
channel,
|
|
2548
|
+
'/modal.client.ModalClient/AutoscalerOverrideHistory',
|
|
2549
|
+
modal_proto.api_pb2.AutoscalerOverrideHistoryRequest,
|
|
2550
|
+
modal_proto.api_pb2.AutoscalerOverrideHistoryResponse,
|
|
2551
|
+
)
|
|
2536
2552
|
self.BlobCreate = grpclib.client.UnaryUnaryMethod(
|
|
2537
2553
|
channel,
|
|
2538
2554
|
'/modal.client.ModalClient/BlobCreate',
|