modAL 1.2.6.dev4__py3-none-any.whl → 1.2.7.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.
modal/cls.py CHANGED
@@ -7,7 +7,6 @@ from pathlib import PurePosixPath
7
7
  from typing import Any, Callable, Optional, Sequence, TypeVar, Union
8
8
 
9
9
  from google.protobuf.message import Message
10
- from grpclib import GRPCError, Status
11
10
 
12
11
  from modal_proto import api_pb2
13
12
 
@@ -659,11 +658,6 @@ More information on class parameterization can be found here: https://modal.com/
659
658
  raise NotFoundError(
660
659
  f"Lookup failed for Cls '{name}' from the '{app_name}' app{env_context}: {exc}."
661
660
  ) from None
662
- except GRPCError as exc:
663
- if exc.status == Status.FAILED_PRECONDITION:
664
- raise InvalidError(exc.message) from None
665
- else:
666
- raise
667
661
 
668
662
  print_server_warnings(response.server_warnings)
669
663
  await resolver.load(self._class_service_function, load_context)
modal/dict.py CHANGED
@@ -5,7 +5,7 @@ from datetime import datetime
5
5
  from typing import Any, Optional, Union
6
6
 
7
7
  from google.protobuf.message import Message
8
- from grpclib import GRPCError, Status
8
+ from grpclib import GRPCError
9
9
  from synchronicity import classproperty
10
10
  from synchronicity.async_wrap import asynccontextmanager
11
11
 
@@ -126,10 +126,8 @@ class _DictManager:
126
126
  )
127
127
  try:
128
128
  await client.stub.DictGetOrCreate(req)
129
- except GRPCError as exc:
130
- if exc.status == Status.ALREADY_EXISTS and not allow_existing:
131
- raise AlreadyExistsError(exc.message)
132
- else:
129
+ except AlreadyExistsError:
130
+ if not allow_existing:
133
131
  raise
134
132
 
135
133
  @staticmethod
modal/exception.py CHANGED
@@ -1,7 +1,45 @@
1
1
  # Copyright Modal Labs 2022
2
+ """
3
+ Modal-specific exception types.
4
+
5
+ ## Notes on `grpclib.GRPCError` migration
6
+
7
+ Historically, the Modal SDK could propagate `grpclib.GRPCError` exceptions out
8
+ to user code. As of v1.3, we are in the process of gracefully migrating to
9
+ always raising a Modal exception type in these cases. To avoid breaking user
10
+ code that relies on catching `grpclib.GRPCError`, a subset of Modal exception
11
+ types temporarily inherit from `grpclib.GRPCError`.
12
+
13
+ We encourage users to migrate any code that currently catches `grpclib.GRPCError`
14
+ to instead catch the appropriate Modal exception type. The following mapping
15
+ between GRPCError status codes and Modal exception types is currently in use:
16
+
17
+ ```
18
+ CANCELLED -> ServiceError
19
+ UNKNOWN -> ServiceError
20
+ INVALID_ARGUMENT -> InvalidError
21
+ DEADLINE_EXCEEDED -> ServiceError
22
+ NOT_FOUND -> NotFoundError
23
+ ALREADY_EXISTS -> AlreadyExistsError
24
+ PERMISSION_DENIED -> PermissionDeniedError
25
+ RESOURCE_EXHAUSTED -> ResourceExhaustedError
26
+ FAILED_PRECONDITION -> ConflictError
27
+ ABORTED -> ConflictError
28
+ OUT_OF_RANGE -> InvalidError
29
+ UNIMPLEMENTED -> UnimplementedError
30
+ INTERNAL -> InternalError
31
+ UNAVAILABLE -> ServiceError
32
+ DATA_LOSS -> ServiceError
33
+ UNAUTHENTICATED -> AuthError
34
+ ```
35
+
36
+ """
37
+
2
38
  import random
3
39
  import signal
40
+ from typing import Any, Optional
4
41
 
42
+ import grpclib
5
43
  import synchronicity.exceptions
6
44
 
7
45
  UserCodeException = synchronicity.exceptions.UserCodeException # Deprecated type used for return_exception wrapping
@@ -26,10 +64,112 @@ class Error(Exception):
26
64
  """
27
65
 
28
66
 
29
- class AlreadyExistsError(Error):
67
+ class _GRPCErrorWrapper(grpclib.GRPCError):
68
+ """This transitional class helps us migrate away from propagating `grpclib.GRPCError` to users.
69
+
70
+ It serves two purposes:
71
+ - It avoids abruptly breaking user code that catches `grpclib.GRPCError`
72
+ - It actively warns when users access attributes defined by `grpclib.GRPCError`
73
+
74
+ This won't catch all cases (users might react indiscriminately to GRPCError without checking the status).
75
+
76
+ The mapping between GRPCError status codes and our error types is defined in `modal._grpc_client`.
77
+
78
+ """
79
+
80
+ # These will be set on the instance in our error handling middleware
81
+ _grpc_message: str
82
+ _grpc_status: grpclib.Status
83
+ _grpc_details: Any
84
+
85
+ def __init__(self, message: Optional[str] = None):
86
+ # Override GRPCError's init and repr to behave more like a regular Exception
87
+ # (We don't customize these anywhere in our custom error types currently).
88
+ self._message = message or ""
89
+
90
+ def __repr__(self) -> str:
91
+ return f"{type(self).__name__}({self._message!r})"
92
+
93
+ def _warn_on_grpc_error_attribute_access(self) -> None:
94
+ from ._utils.deprecation import deprecation_warning # Avoid circular import
95
+
96
+ exc_type = type(self).__name__
97
+ deprecation_warning(
98
+ (2025, 12, 9),
99
+ "Modal will stop propagating the `grpclib.GRPCError` type in the future. "
100
+ f"Update your code so that it catches `modal.exception.{exc_type}` directly "
101
+ "to avoid changes to error handling behavior in the future.",
102
+ pending=True,
103
+ )
104
+
105
+ @property
106
+ def message(self) -> str:
107
+ self._warn_on_grpc_error_attribute_access()
108
+ return self._grpc_message
109
+
110
+ @message.setter
111
+ def message(self, value: str) -> None:
112
+ self._grpc_message = value
113
+
114
+ @property
115
+ def status(self) -> grpclib.Status:
116
+ self._warn_on_grpc_error_attribute_access()
117
+ return self._grpc_status
118
+
119
+ @status.setter
120
+ def status(self, value: grpclib.Status) -> None:
121
+ self._grpc_status = value
122
+
123
+ @property
124
+ def details(self) -> Any:
125
+ self._warn_on_grpc_error_attribute_access()
126
+ return self._grpc_details
127
+
128
+ @details.setter
129
+ def details(self, value: Any) -> None:
130
+ self._grpc_details = value
131
+
132
+
133
+ class AlreadyExistsError(Error, _GRPCErrorWrapper):
30
134
  """Raised when a resource creation conflicts with an existing resource."""
31
135
 
32
136
 
137
+ class AuthError(Error, _GRPCErrorWrapper):
138
+ """Raised when a client has missing or invalid authentication."""
139
+
140
+
141
+ class InternalError(Error, _GRPCErrorWrapper):
142
+ """Raised when an internal error occurs in the Modal system."""
143
+
144
+
145
+ class InvalidError(Error, _GRPCErrorWrapper):
146
+ """Raised when user does something invalid."""
147
+
148
+
149
+ class ConflictError(InvalidError, _GRPCErrorWrapper):
150
+ """Raised when a resource conflict occurs between the request and current system state."""
151
+
152
+
153
+ class NotFoundError(Error, _GRPCErrorWrapper):
154
+ """Raised when a requested resource was not found."""
155
+
156
+
157
+ class PermissionDeniedError(Error, _GRPCErrorWrapper):
158
+ """Raised when a user does not have permission to perform the requested operation."""
159
+
160
+
161
+ class ResourceExhaustedError(Error, _GRPCErrorWrapper):
162
+ """Raised when a server-side resource has been exhausted, e.g. a quota or rate limit."""
163
+
164
+
165
+ class ServiceError(Error, _GRPCErrorWrapper):
166
+ """Raised when an error occurs in basic client<>server communication."""
167
+
168
+
169
+ class UnimplementedError(Error, _GRPCErrorWrapper):
170
+ """Raised when a requested operation is not implemented or not supported."""
171
+
172
+
33
173
  class RemoteError(Error):
34
174
  """Raised when an error occurs on the Modal server."""
35
175
 
@@ -70,26 +210,14 @@ class OutputExpiredError(TimeoutError):
70
210
  """Raised when the Output exceeds expiration and times out."""
71
211
 
72
212
 
73
- class AuthError(Error):
74
- """Raised when a client has missing or invalid authentication."""
75
-
76
-
77
213
  class ConnectionError(Error):
78
214
  """Raised when an issue occurs while connecting to the Modal servers."""
79
215
 
80
216
 
81
- class InvalidError(Error):
82
- """Raised when user does something invalid."""
83
-
84
-
85
217
  class VersionError(Error):
86
218
  """Raised when the current client version of Modal is unsupported."""
87
219
 
88
220
 
89
- class NotFoundError(Error):
90
- """Raised when a requested resource was not found."""
91
-
92
-
93
221
  class ExecutionError(Error):
94
222
  """Raised when something unexpected happened during runtime."""
95
223
 
@@ -121,9 +249,7 @@ class ServerWarning(UserWarning):
121
249
 
122
250
 
123
251
  class InternalFailure(Error):
124
- """
125
- Retriable internal error.
126
- """
252
+ """Retriable internal error."""
127
253
 
128
254
 
129
255
  class _CliUserExecutionError(Exception):
modal/functions.pyi CHANGED
@@ -409,7 +409,7 @@ class Function(
409
409
 
410
410
  _call_generator: ___call_generator_spec[typing_extensions.Self]
411
411
 
412
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
412
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
413
413
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
414
414
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
415
415
  ...
@@ -418,7 +418,7 @@ class Function(
418
418
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
419
419
  ...
420
420
 
421
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
421
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
422
422
 
423
423
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
424
424
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -445,7 +445,7 @@ class Function(
445
445
  """
446
446
  ...
447
447
 
448
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
448
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
450
450
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
451
451
 
@@ -469,7 +469,7 @@ class Function(
469
469
  ...
470
470
 
471
471
  _experimental_spawn: ___experimental_spawn_spec[
472
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
472
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
473
473
  ]
474
474
 
475
475
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -478,7 +478,7 @@ class Function(
478
478
 
479
479
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
480
480
 
481
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
481
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
482
482
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
483
483
  """Calls the function with the given arguments, without waiting for the results.
484
484
 
@@ -499,7 +499,7 @@ class Function(
499
499
  """
500
500
  ...
501
501
 
502
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
502
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
503
503
 
504
504
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
505
505
  """Return the inner Python object wrapped by this Modal Function."""
modal/image.py CHANGED
@@ -65,11 +65,11 @@ ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVI
65
65
  # Python versions in mount.py where we specify the "standalone Python versions" we create mounts for.
66
66
  # Consider consolidating these multiple sources of truth?
67
67
  SUPPORTED_PYTHON_SERIES: dict[ImageBuilderVersion, list[str]] = {
68
- "PREVIEW": ["3.9", "3.10", "3.11", "3.12", "3.13"],
69
- "2025.06": ["3.9", "3.10", "3.11", "3.12", "3.13"],
70
- "2024.10": ["3.9", "3.10", "3.11", "3.12", "3.13"],
71
- "2024.04": ["3.9", "3.10", "3.11", "3.12"],
72
- "2023.12": ["3.9", "3.10", "3.11", "3.12"],
68
+ "PREVIEW": ["3.10", "3.11", "3.12", "3.13"],
69
+ "2025.06": ["3.10", "3.11", "3.12", "3.13"],
70
+ "2024.10": ["3.10", "3.11", "3.12", "3.13"],
71
+ "2024.04": ["3.10", "3.11", "3.12"],
72
+ "2023.12": ["3.10", "3.11", "3.12"],
73
73
  }
74
74
 
75
75
  LOCAL_REQUIREMENTS_DIR = Path(__file__).parent / "builder"
@@ -1742,9 +1742,6 @@ class _Image(_Object, type_prefix="im"):
1742
1742
  """A Micromamba base image. Micromamba allows for fast building of small Conda-based containers."""
1743
1743
 
1744
1744
  def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
1745
- nonlocal python_version
1746
- if version == "2023.12" and python_version is None:
1747
- python_version = "3.9" # Backcompat for old hardcoded default param
1748
1745
  validated_python_version = _validate_python_version(python_version, version)
1749
1746
  micromamba_version = _base_image_config("micromamba", version)
1750
1747
  tag = f"mambaorg/micromamba:{micromamba_version}"
modal/io_streams.py CHANGED
@@ -18,7 +18,6 @@ from typing import (
18
18
  cast,
19
19
  )
20
20
 
21
- from grpclib import Status
22
21
  from grpclib.exceptions import GRPCError, StreamTerminatedError
23
22
 
24
23
  from modal.exception import ClientClosed, ExecTimeoutError, InvalidError
@@ -29,6 +28,7 @@ from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES
29
28
  from ._utils.task_command_router_client import TaskCommandRouterClient
30
29
  from .client import _Client
31
30
  from .config import logger
31
+ from .exception import ConflictError
32
32
  from .stream_type import StreamType
33
33
 
34
34
  if TYPE_CHECKING:
@@ -697,11 +697,8 @@ class _StreamWriterThroughServer:
697
697
  input=api_pb2.RuntimeInputMessage(message=data, message_index=index, eof=self._is_closed),
698
698
  ),
699
699
  )
700
- except GRPCError as exc:
701
- if exc.status == Status.FAILED_PRECONDITION:
702
- raise ValueError(exc.message)
703
- else:
704
- raise exc
700
+ except ConflictError as exc:
701
+ raise ValueError(str(exc))
705
702
 
706
703
 
707
704
  class _StreamWriterThroughCommandRouter:
modal/mount.py CHANGED
@@ -41,7 +41,6 @@ MOUNT_PUT_FILE_CLIENT_TIMEOUT = 10 * 60 # 10 min max for transferring files
41
41
  # These can be updated safely, but changes will trigger a rebuild for all images
42
42
  # that rely on `add_python()` in their constructor.
43
43
  PYTHON_STANDALONE_VERSIONS: dict[str, tuple[str, str]] = {
44
- "3.9": ("20230826", "3.9.18"),
45
44
  "3.10": ("20230826", "3.10.13"),
46
45
  "3.11": ("20230826", "3.11.5"),
47
46
  "3.12": ("20240107", "3.12.1"),
modal/mount.pyi CHANGED
@@ -428,7 +428,7 @@ async def _create_single_client_dependency_mount(
428
428
  ): ...
429
429
  async def _create_client_dependency_mounts(
430
430
  client=None,
431
- python_versions: list[str] = ["3.9", "3.10", "3.11", "3.12", "3.13"],
431
+ python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13"],
432
432
  builder_versions: list[str] = ["2025.06"],
433
433
  check_if_exists=True,
434
434
  dry_run=False,
@@ -439,7 +439,7 @@ class __create_client_dependency_mounts_spec(typing_extensions.Protocol):
439
439
  self,
440
440
  /,
441
441
  client=None,
442
- python_versions: list[str] = ["3.9", "3.10", "3.11", "3.12", "3.13"],
442
+ python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13"],
443
443
  builder_versions: list[str] = ["2025.06"],
444
444
  check_if_exists=True,
445
445
  dry_run=False,
@@ -448,7 +448,7 @@ class __create_client_dependency_mounts_spec(typing_extensions.Protocol):
448
448
  self,
449
449
  /,
450
450
  client=None,
451
- python_versions: list[str] = ["3.9", "3.10", "3.11", "3.12", "3.13"],
451
+ python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13"],
452
452
  builder_versions: list[str] = ["2025.06"],
453
453
  check_if_exists=True,
454
454
  dry_run=False,
modal/queue.py CHANGED
@@ -8,7 +8,7 @@ from datetime import datetime
8
8
  from typing import Any, Optional, Union
9
9
 
10
10
  from google.protobuf.message import Message
11
- from grpclib import GRPCError, Status
11
+ from grpclib import Status
12
12
  from synchronicity import classproperty
13
13
  from synchronicity.async_wrap import asynccontextmanager
14
14
 
@@ -30,7 +30,7 @@ from ._utils.grpc_utils import Retry
30
30
  from ._utils.name_utils import check_object_name
31
31
  from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
32
32
  from .client import _Client
33
- from .exception import AlreadyExistsError, InvalidError, NotFoundError, RequestSizeError
33
+ from .exception import AlreadyExistsError, InvalidError, NotFoundError, RequestSizeError, ResourceExhaustedError
34
34
 
35
35
 
36
36
  @dataclass
@@ -97,10 +97,8 @@ class _QueueManager:
97
97
  )
98
98
  try:
99
99
  await client.stub.QueueGetOrCreate(req)
100
- except GRPCError as exc:
101
- if exc.status == Status.ALREADY_EXISTS and not allow_existing:
102
- raise AlreadyExistsError(exc.message)
103
- else:
100
+ except AlreadyExistsError:
101
+ if not allow_existing:
104
102
  raise
105
103
 
106
104
  @staticmethod
@@ -597,14 +595,11 @@ class _Queue(_Object, type_prefix="qu"):
597
595
  total_timeout=timeout,
598
596
  ),
599
597
  )
600
- except GRPCError as exc:
601
- if exc.status == Status.RESOURCE_EXHAUSTED:
602
- raise queue.Full(str(exc))
603
- elif "status = '413'" in exc.message:
598
+ except ResourceExhaustedError as exc:
599
+ if "status = '413'" in str(exc):
604
600
  method = "put_many" if len(vs) > 1 else "put"
605
601
  raise RequestSizeError(f"Queue.{method} request is too large") from exc
606
- else:
607
- raise exc
602
+ raise queue.Full(str(exc))
608
603
 
609
604
  async def _put_many_nonblocking(self, partition: Optional[str], partition_ttl: int, vs: list[Any]):
610
605
  vs_encoded = [serialize(v) for v in vs]
@@ -616,14 +611,11 @@ class _Queue(_Object, type_prefix="qu"):
616
611
  )
617
612
  try:
618
613
  await self._client.stub.QueuePut(request)
619
- except GRPCError as exc:
620
- if exc.status == Status.RESOURCE_EXHAUSTED:
621
- raise queue.Full(exc.message)
622
- elif "status = '413'" in exc.message:
614
+ except ResourceExhaustedError as exc:
615
+ if "status = '413'" in str(exc):
623
616
  method = "put_many" if len(vs) > 1 else "put"
624
617
  raise RequestSizeError(f"Queue.{method} request is too large") from exc
625
- else:
626
- raise exc
618
+ raise queue.Full(str(exc)) from exc
627
619
 
628
620
  @live_method
629
621
  async def len(self, *, partition: Optional[str] = None, total: bool = False) -> int:
modal/runner.py CHANGED
@@ -13,7 +13,6 @@ from contextlib import nullcontext
13
13
  from multiprocessing.synchronize import Event
14
14
  from typing import TYPE_CHECKING, Any, Optional, TypeVar
15
15
 
16
- from grpclib import GRPCError, Status
17
16
  from synchronicity.async_wrap import asynccontextmanager
18
17
 
19
18
  import modal._runtime.execution_context
@@ -200,13 +199,7 @@ async def _publish_app(
200
199
  definition_ids=definition_ids,
201
200
  )
202
201
 
203
- try:
204
- response = await client.stub.AppPublish(request)
205
- except GRPCError as exc:
206
- if exc.status == Status.INVALID_ARGUMENT or exc.status == Status.FAILED_PRECONDITION:
207
- raise InvalidError(exc.message)
208
- raise
209
-
202
+ response = await client.stub.AppPublish(request)
210
203
  print_server_warnings(response.server_warnings)
211
204
  return response.url, response.server_warnings
212
205
 
modal/sandbox.py CHANGED
@@ -15,7 +15,6 @@ if TYPE_CHECKING:
15
15
  import _typeshed
16
16
 
17
17
  from google.protobuf.message import Message
18
- from grpclib import GRPCError, Status
19
18
 
20
19
  from modal._tunnel import Tunnel
21
20
  from modal.cloud_bucket_mount import _CloudBucketMount, cloud_bucket_mounts_to_proto
@@ -34,7 +33,7 @@ from ._utils.name_utils import check_object_name
34
33
  from ._utils.task_command_router_client import TaskCommandRouterClient
35
34
  from .client import _Client
36
35
  from .container_process import _ContainerProcess
37
- from .exception import AlreadyExistsError, ExecutionError, InvalidError, SandboxTerminatedError, SandboxTimeoutError
36
+ from .exception import ExecutionError, InvalidError, SandboxTerminatedError, SandboxTimeoutError
38
37
  from .file_io import FileWatchEvent, FileWatchEventType, _FileIO
39
38
  from .gpu import GPU_T
40
39
  from .image import _Image
@@ -261,13 +260,7 @@ class _Sandbox(_Object, type_prefix="sb"):
261
260
  )
262
261
 
263
262
  create_req = api_pb2.SandboxCreateRequest(app_id=load_context.app_id, definition=definition)
264
- try:
265
- create_resp = await load_context.client.stub.SandboxCreate(create_req)
266
- except GRPCError as exc:
267
- if exc.status == Status.ALREADY_EXISTS:
268
- raise AlreadyExistsError(exc.message)
269
- raise exc
270
-
263
+ create_resp = await load_context.client.stub.SandboxCreate(create_req)
271
264
  sandbox_id = create_resp.sandbox_id
272
265
  self._hydrate(sandbox_id, load_context.client, None)
273
266
 
@@ -557,10 +550,7 @@ class _Sandbox(_Object, type_prefix="sb"):
557
550
  async def get_tags(self) -> dict[str, str]:
558
551
  """Fetches any tags (key-value pairs) currently attached to this Sandbox from the server."""
559
552
  req = api_pb2.SandboxTagsGetRequest(sandbox_id=self.object_id)
560
- try:
561
- resp = await self._client.stub.SandboxTagsGet(req)
562
- except GRPCError as exc:
563
- raise InvalidError(exc.message) if exc.status == Status.INVALID_ARGUMENT else exc
553
+ resp = await self._client.stub.SandboxTagsGet(req)
564
554
 
565
555
  return {tag.tag_name: tag.tag_value for tag in resp.tags}
566
556
 
@@ -581,10 +571,7 @@ class _Sandbox(_Object, type_prefix="sb"):
581
571
  sandbox_id=self.object_id,
582
572
  tags=tags_list,
583
573
  )
584
- try:
585
- await self._client.stub.SandboxTagsSet(req)
586
- except GRPCError as exc:
587
- raise InvalidError(exc.message) if exc.status == Status.INVALID_ARGUMENT else exc
574
+ await self._client.stub.SandboxTagsSet(req)
588
575
 
589
576
  async def snapshot_filesystem(self, timeout: int = 55) -> _Image:
590
577
  """Snapshot the filesystem of the Sandbox.
@@ -1025,12 +1012,7 @@ class _Sandbox(_Object, type_prefix="sb"):
1025
1012
  sandbox_name_override=name,
1026
1013
  sandbox_name_override_type=api_pb2.SandboxRestoreRequest.SANDBOX_NAME_OVERRIDE_TYPE_STRING,
1027
1014
  )
1028
- try:
1029
- restore_resp: api_pb2.SandboxRestoreResponse = await client.stub.SandboxRestore(restore_req)
1030
- except GRPCError as exc:
1031
- if exc.status == Status.ALREADY_EXISTS:
1032
- raise AlreadyExistsError(exc.message)
1033
- raise exc
1015
+ restore_resp: api_pb2.SandboxRestoreResponse = await client.stub.SandboxRestore(restore_req)
1034
1016
 
1035
1017
  sandbox = await _Sandbox.from_id(restore_resp.sandbox_id, client)
1036
1018
 
@@ -1171,10 +1153,7 @@ class _Sandbox(_Object, type_prefix="sb"):
1171
1153
  )
1172
1154
 
1173
1155
  # Fetches a batch of sandboxes.
1174
- try:
1175
- resp = await client.stub.SandboxList(req)
1176
- except GRPCError as exc:
1177
- raise InvalidError(exc.message) if exc.status == Status.INVALID_ARGUMENT else exc
1156
+ resp = await client.stub.SandboxList(req)
1178
1157
 
1179
1158
  if not resp.sandboxes:
1180
1159
  return
modal/secret.py CHANGED
@@ -5,7 +5,6 @@ from datetime import datetime
5
5
  from typing import Optional, Union
6
6
 
7
7
  from google.protobuf.message import Message
8
- from grpclib import GRPCError, Status
9
8
  from synchronicity import classproperty
10
9
 
11
10
  from modal_proto import api_pb2
@@ -92,10 +91,8 @@ class _SecretManager:
92
91
  )
93
92
  try:
94
93
  await client.stub.SecretGetOrCreate(req)
95
- except GRPCError as exc:
96
- if exc.status == Status.ALREADY_EXISTS and not allow_existing:
97
- raise AlreadyExistsError(exc.message)
98
- else:
94
+ except AlreadyExistsError:
95
+ if not allow_existing:
99
96
  raise
100
97
 
101
98
  @staticmethod
@@ -222,14 +219,7 @@ async def _load_from_env_dict(instance: "_Secret", load_context: LoadContext, en
222
219
  environment_name=load_context.environment_name,
223
220
  )
224
221
 
225
- try:
226
- resp = await load_context.client.stub.SecretGetOrCreate(req)
227
- except GRPCError as exc:
228
- if exc.status == Status.INVALID_ARGUMENT:
229
- raise InvalidError(exc.message)
230
- if exc.status == Status.FAILED_PRECONDITION:
231
- raise InvalidError(exc.message)
232
- raise
222
+ resp = await load_context.client.stub.SecretGetOrCreate(req)
233
223
  instance._hydrate(resp.secret_id, load_context.client, resp.metadata)
234
224
 
235
225
 
@@ -411,13 +401,7 @@ class _Secret(_Object, type_prefix="st"):
411
401
  environment_name=load_context.environment_name,
412
402
  required_keys=required_keys,
413
403
  )
414
- try:
415
- response = await load_context.client.stub.SecretGetOrCreate(req)
416
- except GRPCError as exc:
417
- if exc.status == Status.NOT_FOUND:
418
- raise NotFoundError(exc.message)
419
- else:
420
- raise
404
+ response = await load_context.client.stub.SecretGetOrCreate(req)
421
405
  self._hydrate(response.secret_id, load_context.client, response.metadata)
422
406
 
423
407
  rep = _Secret._repr(name, environment_name)