modAL 1.3.1.dev0__py3-none-any.whl → 1.3.1.dev2__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.
@@ -5,6 +5,8 @@ import json
5
5
  import ssl
6
6
  import time
7
7
  import urllib.parse
8
+ import weakref
9
+ from contextlib import suppress
8
10
  from typing import AsyncGenerator, Optional
9
11
 
10
12
  import grpclib.client
@@ -106,6 +108,15 @@ async def fetch_command_router_access(server_client, task_id: str) -> api_pb2.Ta
106
108
  )
107
109
 
108
110
 
111
+ def _finalize_channel(loop, channel):
112
+ if not loop.is_closed():
113
+ # only run if loop has not shut down
114
+ # call_soon_threadsafe could throw if the loop is torn down after calling
115
+ # is_closed. This is safe to ignore.
116
+ with suppress(Exception):
117
+ loop.call_soon_threadsafe(channel.close)
118
+
119
+
109
120
  class TaskCommandRouterClient:
110
121
  """
111
122
  Client used to talk directly to TaskCommandRouter service on worker hosts.
@@ -199,44 +210,17 @@ class TaskCommandRouterClient:
199
210
 
200
211
  self._closed = False
201
212
 
202
- async def send_request(event: grpclib.events.SendRequest) -> None:
203
- # This will get the most recent JWT for every request. No need to
204
- # lock _jwt_refresh_lock: reads and writes happen on the
205
- # single-threaded event loop and variable assignment is atomic.
206
- event.metadata["authorization"] = f"Bearer {self._jwt}"
207
-
208
- grpclib.events.listen(self._channel, grpclib.events.SendRequest, send_request)
213
+ self._channel_finalizer = weakref.finalize(
214
+ self,
215
+ _finalize_channel,
216
+ loop,
217
+ channel,
218
+ )
209
219
 
210
220
  self._stub = TaskCommandRouterStub(self._channel)
211
221
 
212
- def __del__(self) -> None:
213
- """Best-effort cleanup if the caller forgot to close().
214
-
215
- This object is typically used through synchronicity wrappers, which means this finalizer
216
- may run on a different thread than the event loop that owns the channel. Closing the
217
- channel is therefore scheduled onto the owning loop using call_soon_threadsafe.
218
-
219
- Use getattr in the event that attributes are not yet initialized or the
220
- object is in a half-torn-down state.
221
- """
222
- if getattr(self, "_closed", False):
223
- return
224
- self._closed = True
225
-
226
- channel = getattr(self, "_channel", None)
227
- if channel is None:
228
- return
229
-
230
- loop = getattr(self, "_loop", None)
231
-
232
- if loop is not None and not loop.is_closed():
233
- try:
234
- loop.call_soon_threadsafe(channel.close)
235
- except Exception:
236
- # call_soon_threadsafe could throw if the loop is torn down
237
- # after calling is_closed. This is safe to ignore, and we don't
238
- # want to raise an exception from a destructor.
239
- pass
222
+ def _get_metadata(self):
223
+ return {"authorization": f"Bearer {self._jwt}"}
240
224
 
241
225
  async def close(self) -> None:
242
226
  """Close the client."""
@@ -245,6 +229,9 @@ class TaskCommandRouterClient:
245
229
 
246
230
  self._closed = True
247
231
  self._channel.close()
232
+ if self._channel_finalizer.alive:
233
+ # skip the finalizer if we've closed the channel anyway
234
+ self._channel_finalizer.detach()
248
235
 
249
236
  async def exec_start(self, request: sr_pb2.TaskExecStartRequest) -> sr_pb2.TaskExecStartResponse:
250
237
  """Start an exec'd command, properly retrying on transient errors."""
@@ -420,12 +407,12 @@ class TaskCommandRouterClient:
420
407
 
421
408
  async def _call_with_auth_retry(self, func, *args, **kwargs):
422
409
  try:
423
- return await func(*args, **kwargs)
410
+ return await func(*args, **kwargs, metadata=self._get_metadata())
424
411
  except GRPCError as exc:
425
412
  if exc.status == Status.UNAUTHENTICATED:
426
413
  await self._refresh_jwt()
427
414
  # Retry with the original arguments preserved
428
- return await func(*args, **kwargs)
415
+ return await func(*args, **kwargs, metadata=self._get_metadata())
429
416
  raise
430
417
 
431
418
  async def _stream_stdio(
@@ -460,7 +447,7 @@ class TaskCommandRouterClient:
460
447
  while True:
461
448
  timeout = max(0, deadline - time.monotonic()) if deadline is not None else None
462
449
  try:
463
- stream = self._stub.TaskExecStdioRead.open(timeout=timeout)
450
+ stream = self._stub.TaskExecStdioRead.open(timeout=timeout, metadata=self._get_metadata())
464
451
  async with stream as s:
465
452
  req = sr_pb2.TaskExecStdioReadRequest(
466
453
  task_id=task_id,
modal/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.3.1.dev0",
36
+ version: str = "1.3.1.dev2",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.3.1.dev0",
166
+ version: str = "1.3.1.dev2",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev0
3
+ Version: 1.3.1.dev2
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -26,7 +26,7 @@ modal/app.pyi,sha256=WR0nGSUh8-8OYRHFqEnijGoPp_IbawNY1JW_0pozXsE,50857
26
26
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
27
27
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
28
  modal/client.py,sha256=CXnyJYLhY--xaCkjv3tpjYDca8AuXGQG1IiaXQVX4y4,14436
29
- modal/client.pyi,sha256=VfRy7DXu5vNO5JP0T8YHiiNqgE8yYhCsyyUMmwsA2fc,14471
29
+ modal/client.pyi,sha256=zCGUF3RioFCBkCy14L6a3gZ8kvoucamb3bKgVijBo98,14471
30
30
  modal/cloud_bucket_mount.py,sha256=cvVj5Rt4nDHoQbTneoJPTaAcnl_Ab_mrsjnnzd0v9OI,6013
31
31
  modal/cloud_bucket_mount.pyi,sha256=vXusGMOUk9HjCs4lhxteuCK4xLQclPfugGWKo7LECEA,7539
32
32
  modal/cls.py,sha256=Cmqt1vOw0-01ktjJBOrdzvdyDbHpjKMTaZ2eEy_8YUE,41694
@@ -116,7 +116,7 @@ modal/_utils/package_utils.py,sha256=jYE7WFVzYWIfO0ZJhno_BYHb6Skp48uupOiw1a3yWzw
116
116
  modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
117
117
  modal/_utils/rand_pb_testing.py,sha256=oNtQB1BY9CDh7vDXEkJitCuCu9a2ufk0KRcEuZ15d0k,4229
118
118
  modal/_utils/shell_utils.py,sha256=hWHzv730Br2Xyj6cGPiMZ-198Z3RZuOu3pDXhFSZ22c,2157
119
- modal/_utils/task_command_router_client.py,sha256=Ua1WoNEH0Tdi1skLFZ8dJ5I2aq95zaup9MLlL0hsIdw,23961
119
+ modal/_utils/task_command_router_client.py,sha256=-5ZYVBZj0mQLA2Nul2o_XSEM5xV0hrzLziP4v6SZPiI,23219
120
120
  modal/_utils/time_utils.py,sha256=43tpFVwT7ykOjlETIFLVt9auMsRZqYYRYBEKxGCrRSA,1212
121
121
  modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
122
122
  modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,21878
@@ -158,7 +158,7 @@ modal/experimental/__init__.py,sha256=GOuo3t4-Le0ZVddiHIOw0MQxkdTJge-bVbhDvgnblm
158
158
  modal/experimental/flash.py,sha256=GJZwSr-4OHlP6gCIZdcbsSb8D8WX9xJwMphjwcfsRoU,31262
159
159
  modal/experimental/flash.pyi,sha256=W62a0AhmLUuH9a0n8Y3-DYhyEEFH3-bRUQFphOQfKHg,16418
160
160
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
161
- modal-1.3.1.dev0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
161
+ modal-1.3.1.dev2.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
162
162
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
163
163
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
164
164
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -166,10 +166,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
166
166
  modal_docs/mdmd/mdmd.py,sha256=tUTImNd4UMFk1opkaw8J672gX8AkBO5gbY2S_NMxsxs,7140
167
167
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
168
168
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
169
- modal_proto/api.proto,sha256=bs2s1cWI6iciKdq-eFfki4vtYS3Q2XBrbxbbtLJuRCE,111143
169
+ modal_proto/api.proto,sha256=rCVrHKcB6QWjaALTGasjPr6En10CCWTWHSEYAMbkslk,111165
170
170
  modal_proto/api_grpc.py,sha256=-8SHvPtalTAjQtRv7zzU7Z1VoDupoJs2ifG4gl_mF0s,134442
171
- modal_proto/api_pb2.py,sha256=WtQuOdWlkcFgehpcn27nZmdQ5uMQPvluzElkHfdQlhM,384541
172
- modal_proto/api_pb2.pyi,sha256=3MsY1f4n7LfMiy-J5zA-cKHPci3ejSMb3JaISxkbHAU,542926
171
+ modal_proto/api_pb2.py,sha256=DfSnuy7cKJ6e6udu22xKoVQyoF0d0aod0U-_za9j7ZE,384579
172
+ modal_proto/api_pb2.pyi,sha256=4jfkVV0jZDsIVkoEaffdkRnrjwy_rxiYsy1LKAIKzzQ,543056
173
173
  modal_proto/api_pb2_grpc.py,sha256=Hqw9jcbhpr-W6jsfog_tGU55ouZjITxGvA-DGNBqOLA,289714
174
174
  modal_proto/api_pb2_grpc.pyi,sha256=QLJ58ANCx147HeGJva58h0MTCLIDs9JmVjrx8bDdwlg,67776
175
175
  modal_proto/modal_api_grpc.py,sha256=MqaBZB2ZqYj6XTIgI_p5dOPjt9gKT4pAJNd_WXR3W84,21295
@@ -180,10 +180,10 @@ modal_proto/task_command_router_pb2.py,sha256=2Y8wRvnq4_7RiQ-5ZAPIgXaxrHv_MXzgS5
180
180
  modal_proto/task_command_router_pb2.pyi,sha256=yI4tFteErLlonFA2HyaWtONfhY_VGSe7KCWFIspvmkE,16323
181
181
  modal_proto/task_command_router_pb2_grpc.py,sha256=q2VWfK2-4uDcPaPLhAqPZkgxhuQRYnWmwUcnEbPbGuk,13895
182
182
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=e1MM10VGUgDkm041v53QeTglGQPzTpA0FUsFtezjp74,4536
183
- modal_version/__init__.py,sha256=BBWANGgdOqoz_AAvW4L3wSTTNVQgOzT37GoLzforXbI,120
183
+ modal_version/__init__.py,sha256=0AlO5a1cQL-5pwDpK4XlTfcYD1BbP5voNFZ8DMSnYK0,120
184
184
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
185
- modal-1.3.1.dev0.dist-info/METADATA,sha256=eo2Z8mnKT0nQdP5bdUFlkwdEVZOHJE2dh3Y0dOUiE2I,2492
186
- modal-1.3.1.dev0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
187
- modal-1.3.1.dev0.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
188
- modal-1.3.1.dev0.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
189
- modal-1.3.1.dev0.dist-info/RECORD,,
185
+ modal-1.3.1.dev2.dist-info/METADATA,sha256=z3ClS3lEdrOLP9XxGEtKPgQGakMVOWEp2S1YcG9uCv8,2492
186
+ modal-1.3.1.dev2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
187
+ modal-1.3.1.dev2.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
188
+ modal-1.3.1.dev2.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
189
+ modal-1.3.1.dev2.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -2886,7 +2886,7 @@ message SandboxInfo {
2886
2886
  string app_id = 5;
2887
2887
  repeated SandboxTag tags = 6; // TODO: Not yet exposed in client library.
2888
2888
  string name = 7;
2889
-
2889
+ string image_id = 8;
2890
2890
  reserved 2; // modal.client.Sandbox definition
2891
2891
  }
2892
2892