modal 1.0.6.dev56__py3-none-any.whl → 1.0.6.dev59__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/_functions.py CHANGED
@@ -1245,14 +1245,12 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1245
1245
  )
1246
1246
  try:
1247
1247
  response = await retry_transient_errors(resolver.client.stub.FunctionGet, request)
1248
- except GRPCError as exc:
1249
- if exc.status == Status.NOT_FOUND:
1250
- env_context = f" (in the '{environment_name}' environment)" if environment_name else ""
1251
- raise NotFoundError(
1252
- f"Lookup failed for Function '{name}' from the '{app_name}' app{env_context}: {exc.message}."
1253
- )
1254
- else:
1255
- raise
1248
+ except NotFoundError as exc:
1249
+ # refine the error message
1250
+ env_context = f" (in the '{environment_name}' environment)" if environment_name else ""
1251
+ raise NotFoundError(
1252
+ f"Lookup failed for Function '{name}' from the '{app_name}' app{env_context}: {exc}."
1253
+ ) from None
1256
1254
 
1257
1255
  print_server_warnings(response.server_warnings)
1258
1256
 
modal/_object.py CHANGED
@@ -8,6 +8,8 @@ from typing import Callable, ClassVar, Optional
8
8
  from google.protobuf.message import Message
9
9
  from typing_extensions import Self
10
10
 
11
+ from modal._traceback import suppress_tb_frames
12
+
11
13
  from ._resolver import Resolver
12
14
  from ._utils.async_utils import aclosing
13
15
  from ._utils.deprecation import deprecation_warning
@@ -271,7 +273,8 @@ class _Object:
271
273
  else:
272
274
  c = client if client is not None else await _Client.from_env()
273
275
  resolver = Resolver(c)
274
- await resolver.load(self)
276
+ with suppress_tb_frames(1): # skip this frame by default
277
+ await resolver.load(self)
275
278
  return self
276
279
 
277
280
 
modal/_resolver.py CHANGED
@@ -8,13 +8,11 @@ from asyncio import Future
8
8
  from collections.abc import Hashable
9
9
  from typing import TYPE_CHECKING, Optional
10
10
 
11
- from grpclib import GRPCError, Status
12
-
11
+ from modal._traceback import suppress_tb_frames
13
12
  from modal_proto import api_pb2
14
13
 
15
14
  from ._utils.async_utils import TaskContext
16
15
  from .client import _Client
17
- from .exception import NotFoundError
18
16
 
19
17
  if TYPE_CHECKING:
20
18
  from rich.tree import Tree
@@ -144,12 +142,8 @@ class Resolver:
144
142
  # Load the object itself
145
143
  if not obj._load:
146
144
  raise Exception(f"Object {obj} has no loader function")
147
- try:
148
- await obj._load(obj, self, existing_object_id)
149
- except GRPCError as exc:
150
- if exc.status == Status.NOT_FOUND:
151
- raise NotFoundError(exc.message)
152
- raise
145
+
146
+ await obj._load(obj, self, existing_object_id)
153
147
 
154
148
  # Check that the id of functions didn't change
155
149
  # Persisted refs are ignored because their life cycle is managed independently.
@@ -169,9 +163,9 @@ class Resolver:
169
163
  self._local_uuid_to_future[obj.local_uuid] = cached_future
170
164
  if deduplication_key is not None:
171
165
  self._deduplication_cache[deduplication_key] = cached_future
172
-
173
- # TODO(elias): print original exception/trace rather than the Resolver-internal trace
174
- return await cached_future
166
+ with suppress_tb_frames(2):
167
+ # skip current frame + `loader()` closure frame from above
168
+ return await cached_future
175
169
 
176
170
  def objects(self) -> list["modal._object._Object"]:
177
171
  unique_objects: dict[str, "modal._object._Object"] = {}
modal/_traceback.py CHANGED
@@ -8,10 +8,12 @@ so that Rich is not a dependency of the container Client.
8
8
  import re
9
9
  import sys
10
10
  import traceback
11
+ import typing
11
12
  import warnings
12
13
  from types import TracebackType
13
14
  from typing import Any, Iterable, Optional
14
15
 
16
+ from modal.config import config, logger
15
17
  from modal_proto import api_pb2
16
18
 
17
19
  from ._vendor.tblib import Traceback as TBLibTraceback
@@ -115,7 +117,7 @@ def traceback_contains_remote_call(tb: Optional[TracebackType]) -> bool:
115
117
  def print_exception(exc: Optional[type[BaseException]], value: Optional[BaseException], tb: Optional[TracebackType]):
116
118
  """Add backwards compatibility for printing exceptions with "notes" for Python<3.11."""
117
119
  traceback.print_exception(exc, value, tb)
118
- if sys.version_info < (3, 11) and value is not None:
120
+ if sys.version_info < (3, 11) and value is not None: # type: ignore
119
121
  notes = getattr(value, "__notes__", [])
120
122
  print(*notes, sep="\n", file=sys.stderr)
121
123
 
@@ -127,3 +129,42 @@ def print_server_warnings(server_warnings: Iterable[api_pb2.Warning]):
127
129
  """
128
130
  for warning in server_warnings:
129
131
  warnings.warn_explicit(warning.message, ServerWarning, "<modal-server>", 0)
132
+
133
+
134
+ # for some reason, the traceback cleanup here can't be moved into a context manager :(
135
+ traceback_suppression_note = (
136
+ "Internal Modal traceback frames are suppressed for readability. Use MODAL_TRACEBACK=1 to show a full traceback."
137
+ )
138
+
139
+
140
+ class suppress_tb_frames:
141
+ def __init__(self, n: int):
142
+ self.n = n
143
+
144
+ def __enter__(self):
145
+ pass
146
+
147
+ def __exit__(
148
+ self, exc_type: Optional[type[BaseException]], exc: Optional[BaseException], tb: Optional[TracebackType]
149
+ ) -> typing.Literal[False]:
150
+ # *base* exceptions like CancelledError, SystemExit etc. can come from random places,
151
+ # so we don't suppress tracebacks for those
152
+ is_base_exception = not isinstance(exc, Exception)
153
+ if config.get("traceback") or exc_type is None or is_base_exception:
154
+ return False
155
+
156
+ # modify traceback on exception object
157
+ try:
158
+ final_tb = tb
159
+ for _ in range(self.n):
160
+ final_tb = final_tb.tb_next
161
+ except AttributeError:
162
+ logger.debug(f"Failed to suppress {self.n} traceback frames from {str(exc_type)} {str(exc)}")
163
+ raise
164
+
165
+ exc.with_traceback(final_tb)
166
+ notes = getattr(exc, "__notes__", [])
167
+ if traceback_suppression_note not in notes:
168
+ # .add_note was added in Python 3.11
169
+ notes.append(traceback_suppression_note)
170
+ return False
@@ -4,6 +4,7 @@ import concurrent.futures
4
4
  import functools
5
5
  import inspect
6
6
  import itertools
7
+ import sys
7
8
  import time
8
9
  import typing
9
10
  from collections.abc import AsyncGenerator, AsyncIterable, Awaitable, Iterable, Iterator
@@ -31,6 +32,10 @@ T = TypeVar("T")
31
32
  P = ParamSpec("P")
32
33
  V = TypeVar("V")
33
34
 
35
+ if sys.platform == "win32":
36
+ # quick workaround for deadlocks on shutdown - need to investigate further
37
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
38
+
34
39
  synchronizer = synchronicity.Synchronizer()
35
40
 
36
41
 
modal/client.py CHANGED
@@ -18,19 +18,20 @@ from typing import (
18
18
  import grpclib.client
19
19
  from google.protobuf import empty_pb2
20
20
  from google.protobuf.message import Message
21
+ from grpclib import GRPCError, Status
21
22
  from synchronicity.async_wrap import asynccontextmanager
22
23
 
23
24
  from modal._utils.async_utils import synchronizer
24
25
  from modal_proto import api_grpc, api_pb2, modal_api_grpc
25
26
  from modal_version import __version__
26
27
 
27
- from ._traceback import print_server_warnings
28
+ from ._traceback import print_server_warnings, suppress_tb_frames
28
29
  from ._utils import async_utils
29
30
  from ._utils.async_utils import TaskContext, synchronize_api
30
31
  from ._utils.auth_token_manager import _AuthTokenManager
31
32
  from ._utils.grpc_utils import ConnectionManager, retry_transient_errors
32
33
  from .config import _check_config, _is_remote, config, logger
33
- from .exception import AuthError, ClientClosed
34
+ from .exception import AuthError, ClientClosed, NotFoundError
34
35
 
35
36
  HEARTBEAT_INTERVAL: float = config.get("heartbeat_interval")
36
37
  HEARTBEAT_TIMEOUT: float = HEARTBEAT_INTERVAL + 0.1
@@ -355,6 +356,33 @@ class _Client:
355
356
  Client = synchronize_api(_Client)
356
357
 
357
358
 
359
+ class grpc_error_converter:
360
+ def __enter__(self):
361
+ pass
362
+
363
+ def __exit__(self, exc_type, exc, traceback) -> bool:
364
+ # skip all internal frames from grpclib
365
+ use_full_traceback = config.get("traceback")
366
+ with suppress_tb_frames(1):
367
+ if isinstance(exc, GRPCError):
368
+ if exc.status == Status.NOT_FOUND:
369
+ if use_full_traceback:
370
+ raise NotFoundError(exc.message)
371
+ else:
372
+ raise NotFoundError(exc.message) from None # from None to skip the grpc-internal cause
373
+
374
+ if not use_full_traceback:
375
+ # just include the frame in grpclib that actually raises the GRPCError
376
+ tb = exc.__traceback__
377
+ while tb.tb_next:
378
+ tb = tb.tb_next
379
+ exc.with_traceback(tb)
380
+ raise exc from None # from None to skip the grpc-internal cause
381
+ raise exc
382
+
383
+ return False
384
+
385
+
358
386
  class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
359
387
  # Calls a grpclib.UnaryUnaryMethod using a specific Client instance, respecting
360
388
  # if that client is closed etc. and possibly introducing Modal-specific retry logic
@@ -396,7 +424,8 @@ class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
396
424
  #
397
425
  # [1]: https://github.com/vmagamedov/grpclib/blob/62f968a4c84e3f64e6966097574ff0a59969ea9b/grpclib/client.py#L844
398
426
  self.wrapped_method.channel = await self.client._get_channel(self.server_url)
399
- return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
427
+ with suppress_tb_frames(1), grpc_error_converter():
428
+ return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
400
429
 
401
430
 
402
431
  class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
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.0.6.dev56",
36
+ version: str = "1.0.6.dev59",
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.0.6.dev56",
166
+ version: str = "1.0.6.dev59",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -332,6 +332,10 @@ class Client:
332
332
  ],
333
333
  ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
334
334
 
335
+ class grpc_error_converter:
336
+ def __enter__(self): ...
337
+ def __exit__(self, exc_type, exc, traceback) -> bool: ...
338
+
335
339
  class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
336
340
  """Abstract base class for generic types.
337
341
 
modal/cls.py CHANGED
@@ -642,14 +642,14 @@ More information on class parameterization can be found here: https://modal.com/
642
642
  )
643
643
  try:
644
644
  response = await retry_transient_errors(resolver.client.stub.ClassGet, request)
645
+ except NotFoundError as exc:
646
+ env_context = f" (in the '{environment_name}' environment)" if environment_name else ""
647
+ raise NotFoundError(
648
+ f"Lookup failed for Cls '{name}' from the '{app_name}' app{env_context}: {exc}."
649
+ ) from None
645
650
  except GRPCError as exc:
646
- if exc.status == Status.NOT_FOUND:
647
- env_context = f" (in the '{environment_name}' environment)" if environment_name else ""
648
- raise NotFoundError(
649
- f"Lookup failed for Cls '{name}' from the '{app_name}' app{env_context}: {exc.message}."
650
- )
651
- elif exc.status == Status.FAILED_PRECONDITION:
652
- raise InvalidError(exc.message)
651
+ if exc.status == Status.FAILED_PRECONDITION:
652
+ raise InvalidError(exc.message) from None
653
653
  else:
654
654
  raise
655
655
 
modal/functions.pyi CHANGED
@@ -428,7 +428,7 @@ class Function(
428
428
 
429
429
  _call_generator: ___call_generator_spec[typing_extensions.Self]
430
430
 
431
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
431
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
432
432
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
433
433
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
434
434
  ...
@@ -437,7 +437,7 @@ class Function(
437
437
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
438
438
  ...
439
439
 
440
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
440
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
441
441
 
442
442
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
443
443
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -464,7 +464,7 @@ class Function(
464
464
  """
465
465
  ...
466
466
 
467
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
467
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
468
468
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
469
469
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
470
470
 
@@ -488,7 +488,7 @@ class Function(
488
488
  ...
489
489
 
490
490
  _experimental_spawn: ___experimental_spawn_spec[
491
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
491
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
492
492
  ]
493
493
 
494
494
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -497,7 +497,7 @@ class Function(
497
497
 
498
498
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
499
499
 
500
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
500
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
501
501
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
502
502
  """Calls the function with the given arguments, without waiting for the results.
503
503
 
@@ -518,7 +518,7 @@ class Function(
518
518
  """
519
519
  ...
520
520
 
521
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
521
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
522
522
 
523
523
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
524
524
  """Return the inner Python object wrapped by this Modal Function."""
@@ -6,7 +6,6 @@ from collections.abc import AsyncIterator
6
6
  from pathlib import Path, PurePosixPath
7
7
  from typing import Any, BinaryIO, Callable, Optional, Union
8
8
 
9
- from grpclib import GRPCError, Status
10
9
  from synchronicity.async_wrap import asynccontextmanager
11
10
 
12
11
  import modal
@@ -122,8 +121,8 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
122
121
  try:
123
122
  response = await resolver.client.stub.SharedVolumeGetOrCreate(req)
124
123
  self._hydrate(response.shared_volume_id, resolver.client, None)
125
- except GRPCError as exc:
126
- if exc.status == Status.NOT_FOUND and exc.message == "App has wrong entity vo":
124
+ except modal.exception.NotFoundError as exc:
125
+ if exc.args[0] == "App has wrong entity vo":
127
126
  raise InvalidError(
128
127
  f"Attempted to mount: `{name}` as a NetworkFileSystem " + "which already exists as a Volume"
129
128
  )
@@ -281,8 +280,9 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
281
280
  req = api_pb2.SharedVolumeGetFileRequest(shared_volume_id=self.object_id, path=path)
282
281
  try:
283
282
  response = await retry_transient_errors(self._client.stub.SharedVolumeGetFile, req)
284
- except GRPCError as exc:
285
- raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
283
+ except modal.exception.NotFoundError as exc:
284
+ raise FileNotFoundError(exc.args[0])
285
+
286
286
  if response.WhichOneof("data_oneof") == "data":
287
287
  yield response.data
288
288
  else:
@@ -363,7 +363,10 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
363
363
  async def remove_file(self, path: str, recursive=False):
364
364
  """Remove a file in a network file system."""
365
365
  req = api_pb2.SharedVolumeRemoveFileRequest(shared_volume_id=self.object_id, path=path, recursive=recursive)
366
- await retry_transient_errors(self._client.stub.SharedVolumeRemoveFile, req)
366
+ try:
367
+ await retry_transient_errors(self._client.stub.SharedVolumeRemoveFile, req)
368
+ except modal.exception.NotFoundError as exc:
369
+ raise FileNotFoundError(exc.args[0])
367
370
 
368
371
 
369
372
  NetworkFileSystem = synchronize_api(_NetworkFileSystem)
modal/volume.py CHANGED
@@ -26,6 +26,7 @@ from google.protobuf.message import Message
26
26
  from grpclib import GRPCError, Status
27
27
  from synchronicity.async_wrap import asynccontextmanager
28
28
 
29
+ import modal.exception
29
30
  import modal_proto.api_pb2
30
31
  from modal.exception import InvalidError, VolumeUploadTimeoutError
31
32
  from modal_proto import api_pb2
@@ -449,8 +450,8 @@ class _Volume(_Object, type_prefix="vo"):
449
450
 
450
451
  try:
451
452
  response = await retry_transient_errors(self._client.stub.VolumeGetFile2, req)
452
- except GRPCError as exc:
453
- raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
453
+ except modal.exception.NotFoundError as exc:
454
+ raise FileNotFoundError(exc.args[0])
454
455
 
455
456
  async def read_block(block_url: str) -> bytes:
456
457
  async with ClientSessionRegistry.get_session().get(block_url) as get_response:
@@ -483,8 +484,8 @@ class _Volume(_Object, type_prefix="vo"):
483
484
 
484
485
  try:
485
486
  response = await retry_transient_errors(self._client.stub.VolumeGetFile2, req)
486
- except GRPCError as exc:
487
- raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
487
+ except modal.exception.NotFoundError as exc:
488
+ raise FileNotFoundError(exc.args[0])
488
489
 
489
490
  # TODO(dflemstr): Sane default limit? Make configurable?
490
491
  download_semaphore = asyncio.Semaphore(multiprocessing.cpu_count())
@@ -526,12 +527,15 @@ class _Volume(_Object, type_prefix="vo"):
526
527
  if self._read_only:
527
528
  raise InvalidError("Read-only Volume can not be written to")
528
529
 
529
- if self._is_v1:
530
- req = api_pb2.VolumeRemoveFileRequest(volume_id=self.object_id, path=path, recursive=recursive)
531
- await retry_transient_errors(self._client.stub.VolumeRemoveFile, req)
532
- else:
533
- req = api_pb2.VolumeRemoveFile2Request(volume_id=self.object_id, path=path, recursive=recursive)
534
- await retry_transient_errors(self._client.stub.VolumeRemoveFile2, req)
530
+ try:
531
+ if self._is_v1:
532
+ req = api_pb2.VolumeRemoveFileRequest(volume_id=self.object_id, path=path, recursive=recursive)
533
+ await retry_transient_errors(self._client.stub.VolumeRemoveFile, req)
534
+ else:
535
+ req = api_pb2.VolumeRemoveFile2Request(volume_id=self.object_id, path=path, recursive=recursive)
536
+ await retry_transient_errors(self._client.stub.VolumeRemoveFile2, req)
537
+ except modal.exception.NotFoundError as exc:
538
+ raise FileNotFoundError(exc.args[0])
535
539
 
536
540
  @live_method
537
541
  async def copy_files(self, src_paths: Sequence[str], dst_path: str, recursive: bool = False) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.6.dev56
3
+ Version: 1.0.6.dev59
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -22,7 +22,7 @@ Requires-Dist: click~=8.1.0
22
22
  Requires-Dist: grpclib<0.4.9,>=0.4.7
23
23
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
24
24
  Requires-Dist: rich>=12.0.0
25
- Requires-Dist: synchronicity~=0.9.15
25
+ Requires-Dist: synchronicity~=0.10.1
26
26
  Requires-Dist: toml
27
27
  Requires-Dist: typer>=0.9
28
28
  Requires-Dist: types-certifi
@@ -3,17 +3,17 @@ modal/__main__.py,sha256=sTJcc9EbDuCKSwg3tL6ZckFw9WWdlkXW8mId1IvJCNc,2846
3
3
  modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
4
4
  modal/_clustered_functions.pyi,sha256=_QKM87tdYwcALSGth8a0-9qXl02fZK6zMfEGEoYz7eA,1007
5
5
  modal/_container_entrypoint.py,sha256=1qBMNY_E9ICC_sRCtillMxmKPsmxJl1J0_qOAG8rH-0,28288
6
- modal/_functions.py,sha256=9Dcw1JXABE78UuWj46AxxAS3iaVptJwlAyBcdqkicXs,81235
6
+ modal/_functions.py,sha256=zjtdBd41AzxshemfMrohAlfsMlyhPorjeyNde9gwF0k,81169
7
7
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
8
8
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
9
- modal/_object.py,sha256=QWyUGjrGLupITkyvJru2cekizsaVdteAhwMQlw_tE4k,11172
9
+ modal/_object.py,sha256=vzRWhAcFJDM8ZmiUSDgSj9gJhLZJA-wjLVvTl984N4A,11295
10
10
  modal/_output.py,sha256=LRM9KroHuR7t5pq8iLYjpFz1sQrHYan2kvRDjT6KAw4,26082
11
11
  modal/_partial_function.py,sha256=B1J4S9W-La0NHaVmY1aCuH0E3QxJHIX6ZWY5eNTQ7io,37142
12
12
  modal/_pty.py,sha256=JZfPDDpzqICZqtyPI_oMJf_9w-p_lLNuzHhwhodUXio,1329
13
- modal/_resolver.py,sha256=-nolqj_p_mx5czVYj1Mazh2IQWpSMrTOGughVJqYfo8,7579
13
+ modal/_resolver.py,sha256=2RWvm34cNSnbv1v7izJMNZgfvpLDD6LzaBlr0lIrLnY,7364
14
14
  modal/_resources.py,sha256=NMAp0GCLutiZI4GuKSIVnRHVlstoD3hNGUabjTUtzf4,1794
15
15
  modal/_serialization.py,sha256=wt09fOwo-E9ATlOG91O9RXCTPRNtsm97v4Unk8Bzd-8,24145
16
- modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
16
+ modal/_traceback.py,sha256=muKP7RbAXq74UGwkaVIdOxDdfDug0UcDKr9fe4LL3m8,6503
17
17
  modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320
18
18
  modal/_tunnel.pyi,sha256=rvC7USR2BcKkbZIeCJXwf7-UfGE-LPLjKsGNiK7Lxa4,13366
19
19
  modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
@@ -21,11 +21,11 @@ modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
21
21
  modal/app.py,sha256=cCgX6rYbEB__q2XoAF2OAeH--L0EM4XWZ820YWglKSQ,48134
22
22
  modal/app.pyi,sha256=cXiSTu2bwu6csAUdkOlh7mr9tPvtaS2qWSEhlC1UxAg,43787
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
- modal/client.py,sha256=5QyM7VJjsFbHf6E91ar3A2KY9mx03wdtGlNJvfTKUVs,17087
25
- modal/client.pyi,sha256=NyX4kOlZwtbv_RzPFy978DiD6bMHtEtnSG50THzR5DU,15270
24
+ modal/client.py,sha256=pBSZ7lv5dezIL9U9H4tpE0Yz6qA1n0NoNbnJ3KCQMMA,18252
25
+ modal/client.pyi,sha256=TAsUVP2Tsp7ip2tk01xZOoukvAkw7gyRV1ePqBIfPJU,15390
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=-qSfYAQvIoO_l2wsCCGTG5ZUwQieNKXdAO00yP1-LYU,7394
28
- modal/cls.py,sha256=B5EtzpBXemH718YvgXaYjuTKvairvqfXJ7IwLZ_6vVA,40034
28
+ modal/cls.py,sha256=7A0xGnugQzm8dOfnKMjLjtqekRlRtQ0jPFRYgq6xdUM,40018
29
29
  modal/cls.pyi,sha256=_tZ5qrlL-ZDEcD-mf9BZkkNH5XPr4SmGTEQ-RVmqF3I,27772
30
30
  modal/config.py,sha256=FqVewLPVVR4feq_46JBENiCzqTuXKpnvQZxaeWbS39g,12009
31
31
  modal/container_process.py,sha256=1m4NPg0lgZmlG9OTkhHbjafqlqoo4lLv-A-X5lV21yo,6852
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=BVqAJ0sgPUfN8QsYztWiGB4j56he60TncM02KsylnCw,21449
39
39
  modal/file_io.pyi,sha256=cPT_hsplE5iLCXhYOLn1Sp9eDdk7DxdFmicQHanJZyg,15918
40
40
  modal/file_pattern_matcher.py,sha256=urAue8es8jxqX94k9EYoZxxhtfgOlsEES8lbFHOorzc,7734
41
41
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
42
- modal/functions.pyi,sha256=ffW_kkU8AxMuV77ltmjK3nslXW_2iwEjKsT-Cgd4Trs,34840
42
+ modal/functions.pyi,sha256=FJe_91dSrMCRNVT-YV1UhtxFKzIvL_C5q8xdk08-wT8,34840
43
43
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
44
44
  modal/image.py,sha256=qTJ6pTcLfYRh112wId7CCNWWmm077w6JoIqxE8BiCoo,102261
45
45
  modal/image.pyi,sha256=TVy-rnSAP2WgQ5zf_sQLFzb-99Qg9LiQNGXR9psFA_o,68107
@@ -47,7 +47,7 @@ modal/io_streams.py,sha256=ut9tY_yEtiBsgQ40u_72Ns87IZHfbMxfnh8t6U9RSGA,16204
47
47
  modal/io_streams.pyi,sha256=aOun_jUFKHSJyUY6-7gKvNoxzcULsa8_hxdtEO7v-gk,13980
48
48
  modal/mount.py,sha256=q-pPeVxAmte-G_LDpbFwaNs2Rb2MIpscfnCXzkhxrOI,36734
49
49
  modal/mount.pyi,sha256=n6AuS8J3bTCQj750nVZZdVBvzCAlSM2fyxAt_5LLFik,20264
50
- modal/network_file_system.py,sha256=92U94Wk2fP40LlgLDIHkTqQ-zc21YxaG6SdFQy8SudU,14731
50
+ modal/network_file_system.py,sha256=AdjxI_hCYaDZz60gOuJeig8yourfWhHmEpn13C_fnMA,14775
51
51
  modal/network_file_system.pyi,sha256=Td_IobHr84iLo_9LZKQ4tNdUB60yjX8QWBaFiUvhfi8,17685
52
52
  modal/object.py,sha256=bTeskuY8JFrESjU4_UL_nTwYlBQdOLmVaOX3X6EMxsg,164
53
53
  modal/object.pyi,sha256=751TV6BntarPsErf0HDQPsvePjWFf0JZK8ZAiRpM1yg,6627
@@ -78,7 +78,7 @@ modal/snapshot.pyi,sha256=0q83hlmWxAhDu8xwZyL5VmYh0i8Tigf7S60or2k30L8,1682
78
78
  modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
79
79
  modal/token_flow.py,sha256=0_4KabXKsuE4OXTJ1OuLOtA-b1sesShztMZkkRFK7tA,7605
80
80
  modal/token_flow.pyi,sha256=eirYjyqbRiT3GCKMIPHJPpkvBTu8WxDKqSHehWaJI_4,2533
81
- modal/volume.py,sha256=7-nLtHhIY18qPJo0W23rBc2p4chf-t4Se3uJPzTSzoA,44333
81
+ modal/volume.py,sha256=4AcgWhqq-zgSbmX-Tx7u2oo1gPLW5mxfb_oZfVSzpQs,44449
82
82
  modal/volume.pyi,sha256=sjr67f0npiRzl2j3blrcMA_QSoogJAS0xLqWI06xWXQ,40727
83
83
  modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
84
84
  modal/_runtime/asgi.py,sha256=_2xSTsDD27Cit7xnMs4lzkJA2wzer2_N4Oa3BkXFzVA,22521
@@ -91,7 +91,7 @@ modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5
91
91
  modal/_runtime/user_code_imports.py,sha256=78wJyleqY2RVibqcpbDQyfWVBVT9BjyHPeoV9WdwV5Y,17720
92
92
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
93
93
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
94
- modal/_utils/async_utils.py,sha256=MhSCsCL8GqIVFWoHubU_899IH-JBZAiiqadG9Wri2l4,29361
94
+ modal/_utils/async_utils.py,sha256=ot8NiPGZ5bRJhY5ilZyjNgx24VI-1BIpCu054oLHDf0,29556
95
95
  modal/_utils/auth_token_manager.py,sha256=4YS0pBfwbuNFy5DoAIOnBNCcYjS9rNCMv4zSVGybiOw,5245
96
96
  modal/_utils/blob_utils.py,sha256=v2NAQVVGx1AQjHQ7-2T64x5rYtwjFFykxDXb-0grrzA,21022
97
97
  modal/_utils/bytes_io_segment_payload.py,sha256=vaXPq8b52-x6G2hwE7SrjS58pg_aRm7gV3bn3yjmTzQ,4261
@@ -151,7 +151,7 @@ modal/requirements/2025.06.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqr
151
151
  modal/requirements/PREVIEW.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqrs,312
152
152
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
153
153
  modal/requirements/base-images.json,sha256=JYSDAgHTl-WrV_TZW5icY-IJEnbe2eQ4CZ_KN6EOZKU,1304
154
- modal-1.0.6.dev56.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
154
+ modal-1.0.6.dev59.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
155
155
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
156
156
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
157
157
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -159,10 +159,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
159
159
  modal_docs/mdmd/mdmd.py,sha256=eW5MzrEl7mSclDo4Uv64sQ1-4IyLggldbgUJdBVLDdI,6449
160
160
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
161
161
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
162
- modal_proto/api.proto,sha256=ZtaJsKz1QBSoLPFfQlm4wWfzA4nwfmLrBX5JUoJaGoI,99686
162
+ modal_proto/api.proto,sha256=idhfshJXfQ79fmRsGdJuBu9m6Q7UDOSHKAxJlvqR20g,99923
163
163
  modal_proto/api_grpc.py,sha256=F7Hu-1Yg7p5a2SbKw9yR4AgpyU0ntvgZTaVbIJMR0DE,122366
164
- modal_proto/api_pb2.py,sha256=55fetV2PE1yQ2eChjhd9ACXqApBR-gYfkzFx4z9tjr8,349839
165
- modal_proto/api_pb2.pyi,sha256=cNkDtLG_wv-Etkr_hJAwl1zqCPEmuOZPI3xdVNP3k6U,477167
164
+ modal_proto/api_pb2.py,sha256=l8mSgayid4o2z62KDprp3XsoDS7HNydohaUJKh7xoSo,350428
165
+ modal_proto/api_pb2.pyi,sha256=JTACUgjpwAqk8I2982mXEG3Y_8DfKim5LP9WGayqlAg,478686
166
166
  modal_proto/api_pb2_grpc.py,sha256=pIFrNmCOgRRcIW8A1Ekja9Po6fHcsj54ExDZFzTpYe4,264347
167
167
  modal_proto/api_pb2_grpc.pyi,sha256=vtxrQ9xnQG6ZRXjp2uk43Mb7wV7F4qGYuVl5JUBc8jI,61968
168
168
  modal_proto/modal_api_grpc.py,sha256=Yl_fGbSIuX2FAEnURkYpKqshs7kbNqtz5HlTJEXkbhE,18487
@@ -174,10 +174,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
174
174
  modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
175
175
  modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
176
176
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
- modal_version/__init__.py,sha256=iXcV8S4jG6TbTxVUhJoMC3q-sJlAh0KPHbCypNhxGPI,121
177
+ modal_version/__init__.py,sha256=jYBjFPQBTRA_WaNo9NhlTwo_VSPBb15phQiBWkU1X6E,121
178
178
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
179
- modal-1.0.6.dev56.dist-info/METADATA,sha256=hE2AZx6ifwO9aTeTe5SHTjn3iVym0NQ0z2-ZfUoBaAw,2462
180
- modal-1.0.6.dev56.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
181
- modal-1.0.6.dev56.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
182
- modal-1.0.6.dev56.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
183
- modal-1.0.6.dev56.dist-info/RECORD,,
179
+ modal-1.0.6.dev59.dist-info/METADATA,sha256=L-giTWZRwgZjEmVrGqeamN00vK008XA90y2gKoBX1vw,2462
180
+ modal-1.0.6.dev59.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
181
+ modal-1.0.6.dev59.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
182
+ modal-1.0.6.dev59.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
183
+ modal-1.0.6.dev59.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -2609,8 +2609,15 @@ message SandboxListResponse {
2609
2609
  }
2610
2610
 
2611
2611
  message SandboxRestoreRequest {
2612
+ enum SandboxNameOverrideType {
2613
+ SANDBOX_NAME_OVERRIDE_TYPE_UNSPECIFIED = 0;
2614
+ SANDBOX_NAME_OVERRIDE_TYPE_NONE = 1;
2615
+ SANDBOX_NAME_OVERRIDE_TYPE_STRING = 2;
2616
+ }
2617
+
2612
2618
  string snapshot_id = 1;
2613
- string sandbox_name = 2;
2619
+ string sandbox_name_override = 2;
2620
+ SandboxNameOverrideType sandbox_name_override_type = 3;
2614
2621
  }
2615
2622
 
2616
2623
  message SandboxRestoreResponse {