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/__init__.py +2 -2
- modal/__main__.py +4 -29
- modal/_functions.py +3 -7
- modal/_grpc_client.py +48 -28
- modal/_tunnel.py +5 -9
- modal/_utils/grpc_utils.py +2 -5
- modal/_utils/task_command_router_client.py +52 -46
- modal/cli/environment.py +2 -16
- modal/cli/launch.py +0 -74
- modal/cli/network_file_system.py +3 -15
- modal/cli/utils.py +1 -13
- modal/cli/volume.py +3 -15
- modal/client.pyi +2 -2
- modal/cls.py +0 -6
- modal/dict.py +3 -5
- modal/exception.py +142 -16
- modal/functions.pyi +6 -6
- modal/image.py +5 -8
- modal/io_streams.py +3 -6
- modal/mount.py +0 -1
- modal/mount.pyi +3 -3
- modal/queue.py +10 -18
- modal/runner.py +1 -8
- modal/sandbox.py +6 -27
- modal/secret.py +4 -20
- modal/volume.py +14 -17
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/METADATA +3 -3
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/RECORD +33 -35
- modal_version/__init__.py +1 -1
- modal/cli/programs/launch_instance_ssh.py +0 -94
- modal/cli/programs/run_marimo.py +0 -95
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/WHEEL +0 -0
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/entry_points.txt +0 -0
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/licenses/LICENSE +0 -0
- {modal-1.2.6.dev4.dist-info → modal-1.2.7.dev2.dist-info}/top_level.txt +0 -0
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
|
|
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
|
|
130
|
-
if
|
|
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
|
|
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[
|
|
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.
|
|
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[
|
|
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.
|
|
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[
|
|
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.
|
|
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.
|
|
69
|
-
"2025.06": ["3.
|
|
70
|
-
"2024.10": ["3.
|
|
71
|
-
"2024.04": ["3.
|
|
72
|
-
"2023.12": ["3.
|
|
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
|
|
701
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
101
|
-
if
|
|
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
|
|
601
|
-
if
|
|
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
|
-
|
|
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
|
|
620
|
-
if
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
96
|
-
if
|
|
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
|
-
|
|
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
|
-
|
|
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)
|