modal 0.62.16__py3-none-any.whl → 0.72.11__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 +17 -13
- modal/__main__.py +41 -3
- modal/_clustered_functions.py +80 -0
- modal/_clustered_functions.pyi +22 -0
- modal/_container_entrypoint.py +420 -937
- modal/_ipython.py +3 -13
- modal/_location.py +17 -10
- modal/_output.py +243 -99
- modal/_pty.py +2 -2
- modal/_resolver.py +55 -59
- modal/_resources.py +51 -0
- modal/_runtime/__init__.py +1 -0
- modal/_runtime/asgi.py +519 -0
- modal/_runtime/container_io_manager.py +1036 -0
- modal/_runtime/execution_context.py +89 -0
- modal/_runtime/telemetry.py +169 -0
- modal/_runtime/user_code_imports.py +356 -0
- modal/_serialization.py +134 -9
- modal/_traceback.py +47 -187
- modal/_tunnel.py +52 -16
- modal/_tunnel.pyi +19 -36
- modal/_utils/app_utils.py +3 -17
- modal/_utils/async_utils.py +479 -100
- modal/_utils/blob_utils.py +157 -186
- modal/_utils/bytes_io_segment_payload.py +97 -0
- modal/_utils/deprecation.py +89 -0
- modal/_utils/docker_utils.py +98 -0
- modal/_utils/function_utils.py +460 -171
- modal/_utils/grpc_testing.py +47 -31
- modal/_utils/grpc_utils.py +62 -109
- modal/_utils/hash_utils.py +61 -19
- modal/_utils/http_utils.py +39 -9
- modal/_utils/logger.py +2 -1
- modal/_utils/mount_utils.py +34 -16
- modal/_utils/name_utils.py +58 -0
- modal/_utils/package_utils.py +14 -1
- modal/_utils/pattern_utils.py +205 -0
- modal/_utils/rand_pb_testing.py +5 -7
- modal/_utils/shell_utils.py +15 -49
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +14 -12
- modal/app.py +1003 -314
- modal/app.pyi +540 -264
- modal/call_graph.py +7 -6
- modal/cli/_download.py +63 -53
- modal/cli/_traceback.py +200 -0
- modal/cli/app.py +205 -45
- modal/cli/config.py +12 -5
- modal/cli/container.py +62 -14
- modal/cli/dict.py +128 -0
- modal/cli/entry_point.py +26 -13
- modal/cli/environment.py +40 -9
- modal/cli/import_refs.py +64 -58
- modal/cli/launch.py +32 -18
- modal/cli/network_file_system.py +64 -83
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +35 -10
- modal/cli/programs/vscode.py +60 -10
- modal/cli/queues.py +131 -0
- modal/cli/run.py +234 -131
- modal/cli/secret.py +8 -7
- modal/cli/token.py +7 -2
- modal/cli/utils.py +79 -10
- modal/cli/volume.py +110 -109
- modal/client.py +250 -144
- modal/client.pyi +157 -118
- modal/cloud_bucket_mount.py +108 -34
- modal/cloud_bucket_mount.pyi +32 -38
- modal/cls.py +535 -148
- modal/cls.pyi +190 -146
- modal/config.py +41 -19
- modal/container_process.py +177 -0
- modal/container_process.pyi +82 -0
- modal/dict.py +111 -65
- modal/dict.pyi +136 -131
- modal/environments.py +106 -5
- modal/environments.pyi +77 -25
- modal/exception.py +34 -43
- modal/experimental.py +61 -2
- modal/extensions/ipython.py +5 -5
- modal/file_io.py +537 -0
- modal/file_io.pyi +235 -0
- modal/file_pattern_matcher.py +197 -0
- modal/functions.py +906 -911
- modal/functions.pyi +466 -430
- modal/gpu.py +57 -44
- modal/image.py +1089 -479
- modal/image.pyi +584 -228
- modal/io_streams.py +434 -0
- modal/io_streams.pyi +122 -0
- modal/mount.py +314 -101
- modal/mount.pyi +241 -235
- modal/network_file_system.py +92 -92
- modal/network_file_system.pyi +152 -110
- modal/object.py +67 -36
- modal/object.pyi +166 -143
- modal/output.py +63 -0
- modal/parallel_map.py +434 -0
- modal/parallel_map.pyi +75 -0
- modal/partial_function.py +282 -117
- modal/partial_function.pyi +222 -129
- modal/proxy.py +15 -12
- modal/proxy.pyi +3 -8
- modal/queue.py +182 -65
- modal/queue.pyi +218 -118
- modal/requirements/2024.04.txt +29 -0
- modal/requirements/2024.10.txt +16 -0
- modal/requirements/README.md +21 -0
- modal/requirements/base-images.json +22 -0
- modal/retries.py +48 -7
- modal/runner.py +459 -156
- modal/runner.pyi +135 -71
- modal/running_app.py +38 -0
- modal/sandbox.py +514 -236
- modal/sandbox.pyi +397 -169
- modal/schedule.py +4 -4
- modal/scheduler_placement.py +20 -3
- modal/secret.py +56 -31
- modal/secret.pyi +62 -42
- modal/serving.py +51 -56
- modal/serving.pyi +44 -36
- modal/stream_type.py +15 -0
- modal/token_flow.py +5 -3
- modal/token_flow.pyi +37 -32
- modal/volume.py +285 -157
- modal/volume.pyi +249 -184
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/METADATA +7 -7
- modal-0.72.11.dist-info/RECORD +174 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/top_level.txt +0 -1
- modal_docs/gen_reference_docs.py +3 -1
- modal_docs/mdmd/mdmd.py +0 -1
- modal_docs/mdmd/signatures.py +5 -2
- modal_global_objects/images/base_images.py +28 -0
- modal_global_objects/mounts/python_standalone.py +2 -2
- modal_proto/__init__.py +1 -1
- modal_proto/api.proto +1288 -533
- modal_proto/api_grpc.py +856 -456
- modal_proto/api_pb2.py +2165 -1157
- modal_proto/api_pb2.pyi +8859 -0
- modal_proto/api_pb2_grpc.py +1674 -855
- modal_proto/api_pb2_grpc.pyi +1416 -0
- modal_proto/modal_api_grpc.py +149 -0
- modal_proto/modal_options_grpc.py +3 -0
- modal_proto/options_pb2.pyi +20 -0
- modal_proto/options_pb2_grpc.pyi +7 -0
- modal_proto/py.typed +0 -0
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +2 -2
- modal/_asgi.py +0 -370
- modal/_container_entrypoint.pyi +0 -378
- modal/_container_exec.py +0 -128
- modal/_sandbox_shell.py +0 -49
- modal/shared_volume.py +0 -23
- modal/shared_volume.pyi +0 -24
- modal/stub.py +0 -783
- modal/stub.pyi +0 -332
- modal-0.62.16.dist-info/RECORD +0 -198
- modal_global_objects/images/conda.py +0 -15
- modal_global_objects/images/debian_slim.py +0 -15
- modal_global_objects/images/micromamba.py +0 -15
- test/__init__.py +0 -1
- test/aio_test.py +0 -12
- test/async_utils_test.py +0 -262
- test/blob_test.py +0 -67
- test/cli_imports_test.py +0 -149
- test/cli_test.py +0 -659
- test/client_test.py +0 -194
- test/cls_test.py +0 -630
- test/config_test.py +0 -137
- test/conftest.py +0 -1420
- test/container_app_test.py +0 -32
- test/container_test.py +0 -1389
- test/cpu_test.py +0 -23
- test/decorator_test.py +0 -85
- test/deprecation_test.py +0 -34
- test/dict_test.py +0 -33
- test/e2e_test.py +0 -68
- test/error_test.py +0 -7
- test/function_serialization_test.py +0 -32
- test/function_test.py +0 -653
- test/function_utils_test.py +0 -101
- test/gpu_test.py +0 -159
- test/grpc_utils_test.py +0 -141
- test/helpers.py +0 -42
- test/image_test.py +0 -669
- test/live_reload_test.py +0 -80
- test/lookup_test.py +0 -70
- test/mdmd_test.py +0 -329
- test/mount_test.py +0 -162
- test/mounted_files_test.py +0 -329
- test/network_file_system_test.py +0 -181
- test/notebook_test.py +0 -66
- test/object_test.py +0 -41
- test/package_utils_test.py +0 -25
- test/queue_test.py +0 -97
- test/resolver_test.py +0 -58
- test/retries_test.py +0 -67
- test/runner_test.py +0 -85
- test/sandbox_test.py +0 -191
- test/schedule_test.py +0 -15
- test/scheduler_placement_test.py +0 -29
- test/secret_test.py +0 -78
- test/serialization_test.py +0 -42
- test/stub_composition_test.py +0 -10
- test/stub_test.py +0 -360
- test/test_asgi_wrapper.py +0 -234
- test/token_flow_test.py +0 -18
- test/traceback_test.py +0 -135
- test/tunnel_test.py +0 -29
- test/utils_test.py +0 -88
- test/version_test.py +0 -14
- test/volume_test.py +0 -341
- test/watcher_test.py +0 -30
- test/webhook_test.py +0 -146
- /modal/{requirements.312.txt → requirements/2023.12.312.txt} +0 -0
- /modal/{requirements.txt → requirements/2023.12.txt} +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/LICENSE +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/WHEEL +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/entry_points.txt +0 -0
modal/exception.py
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
2
|
import random
|
3
3
|
import signal
|
4
|
-
import sys
|
5
|
-
import warnings
|
6
|
-
from datetime import date
|
7
|
-
from typing import Tuple
|
8
4
|
|
9
5
|
|
10
6
|
class Error(Exception):
|
@@ -58,6 +54,10 @@ class InteractiveTimeoutError(TimeoutError):
|
|
58
54
|
"""Raised when interactive frontends time out while trying to connect to a container."""
|
59
55
|
|
60
56
|
|
57
|
+
class OutputExpiredError(TimeoutError):
|
58
|
+
"""Raised when the Output exceeds expiration and times out."""
|
59
|
+
|
60
|
+
|
61
61
|
class AuthError(Error):
|
62
62
|
"""Raised when a client has missing or invalid authentication."""
|
63
63
|
|
@@ -82,6 +82,18 @@ class ExecutionError(Error):
|
|
82
82
|
"""Raised when something unexpected happened during runtime."""
|
83
83
|
|
84
84
|
|
85
|
+
class DeserializationError(Error):
|
86
|
+
"""Raised to provide more context when an error is encountered during deserialization."""
|
87
|
+
|
88
|
+
|
89
|
+
class SerializationError(Error):
|
90
|
+
"""Raised to provide more context when an error is encountered during serialization."""
|
91
|
+
|
92
|
+
|
93
|
+
class RequestSizeError(Error):
|
94
|
+
"""Raised when an operation produces a gRPC request that is rejected by the server for being too large."""
|
95
|
+
|
96
|
+
|
85
97
|
class DeprecationError(UserWarning):
|
86
98
|
"""UserWarning category emitted when a deprecated Modal feature or API is used."""
|
87
99
|
|
@@ -92,6 +104,16 @@ class PendingDeprecationError(UserWarning):
|
|
92
104
|
"""Soon to be deprecated feature. Only used intermittently because of multi-repo concerns."""
|
93
105
|
|
94
106
|
|
107
|
+
class ServerWarning(UserWarning):
|
108
|
+
"""Warning originating from the Modal server and re-issued in client code."""
|
109
|
+
|
110
|
+
|
111
|
+
class InternalFailure(Error):
|
112
|
+
"""
|
113
|
+
Retriable internal error.
|
114
|
+
"""
|
115
|
+
|
116
|
+
|
95
117
|
class _CliUserExecutionError(Exception):
|
96
118
|
"""mdmd:hidden
|
97
119
|
Private wrapper for exceptions during when importing or running stubs from the CLI.
|
@@ -107,45 +129,6 @@ class _CliUserExecutionError(Exception):
|
|
107
129
|
self.user_source = user_source
|
108
130
|
|
109
131
|
|
110
|
-
# TODO(erikbern): we have something similready in function_utils.py
|
111
|
-
_INTERNAL_MODULES = ["modal", "synchronicity"]
|
112
|
-
|
113
|
-
|
114
|
-
def _is_internal_frame(frame):
|
115
|
-
module = frame.f_globals["__name__"].split(".")[0]
|
116
|
-
return module in _INTERNAL_MODULES
|
117
|
-
|
118
|
-
|
119
|
-
def deprecation_error(deprecated_on: Tuple[int, int, int], msg: str):
|
120
|
-
raise DeprecationError(f"Deprecated on {date(*deprecated_on)}: {msg}")
|
121
|
-
|
122
|
-
|
123
|
-
def deprecation_warning(
|
124
|
-
deprecated_on: Tuple[int, int, int], msg: str, pending: bool = False, show_source: bool = True
|
125
|
-
) -> None:
|
126
|
-
"""Utility for getting the proper stack entry.
|
127
|
-
|
128
|
-
See the implementation of the built-in [warnings.warn](https://docs.python.org/3/library/warnings.html#available-functions).
|
129
|
-
"""
|
130
|
-
filename, lineno = "<unknown>", 0
|
131
|
-
if show_source:
|
132
|
-
# Find the last non-Modal line that triggered the warning
|
133
|
-
try:
|
134
|
-
frame = sys._getframe()
|
135
|
-
while frame is not None and _is_internal_frame(frame):
|
136
|
-
frame = frame.f_back
|
137
|
-
filename = frame.f_code.co_filename
|
138
|
-
lineno = frame.f_lineno
|
139
|
-
except ValueError:
|
140
|
-
# Use the defaults from above
|
141
|
-
pass
|
142
|
-
|
143
|
-
warning_cls: type = PendingDeprecationError if pending else DeprecationError
|
144
|
-
|
145
|
-
# This is a lower-level function that warnings.warn uses
|
146
|
-
warnings.warn_explicit(f"{date(*deprecated_on)}: {msg}", warning_cls, filename, lineno)
|
147
|
-
|
148
|
-
|
149
132
|
def _simulate_preemption_interrupt(signum, frame):
|
150
133
|
signal.alarm(30) # simulate a SIGKILL after 30s
|
151
134
|
raise KeyboardInterrupt("Simulated preemption interrupt from modal-client!")
|
@@ -194,3 +177,11 @@ class InputCancellation(BaseException):
|
|
194
177
|
|
195
178
|
class ModuleNotMountable(Exception):
|
196
179
|
pass
|
180
|
+
|
181
|
+
|
182
|
+
class ClientClosed(Error):
|
183
|
+
pass
|
184
|
+
|
185
|
+
|
186
|
+
class FilesystemExecutionError(Error):
|
187
|
+
"""Raised when an unknown error is thrown during a container filesystem operation."""
|
modal/experimental.py
CHANGED
@@ -1,10 +1,69 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
|
+
from typing import (
|
3
|
+
Any,
|
4
|
+
Callable,
|
5
|
+
)
|
6
|
+
|
7
|
+
import modal._clustered_functions
|
8
|
+
from modal.functions import _Function
|
9
|
+
|
10
|
+
from ._runtime.container_io_manager import _ContainerIOManager
|
11
|
+
from .exception import (
|
12
|
+
InvalidError,
|
13
|
+
)
|
14
|
+
from .partial_function import _PartialFunction, _PartialFunctionFlags
|
2
15
|
|
3
16
|
|
4
17
|
def stop_fetching_inputs():
|
5
18
|
"""Don't fetch any more inputs from the server, after the current one.
|
6
19
|
The container will exit gracefully after the current input is processed."""
|
20
|
+
_ContainerIOManager.stop_fetching_inputs()
|
21
|
+
|
22
|
+
|
23
|
+
def get_local_input_concurrency():
|
24
|
+
"""Get the container's local input concurrency.
|
25
|
+
If recently reduced to particular value, it can return a larger number than
|
26
|
+
set due to in-progress inputs."""
|
27
|
+
return _ContainerIOManager.get_input_concurrency()
|
28
|
+
|
29
|
+
|
30
|
+
def set_local_input_concurrency(concurrency: int):
|
31
|
+
"""Set the container's local input concurrency. Dynamic concurrency will be disabled.
|
32
|
+
When setting to a smaller value, this method will not interrupt in-progress inputs.
|
33
|
+
"""
|
34
|
+
_ContainerIOManager.set_input_concurrency(concurrency)
|
35
|
+
|
36
|
+
|
37
|
+
def clustered(size: int, broadcast: bool = True):
|
38
|
+
"""Provision clusters of colocated and networked containers for the Function.
|
39
|
+
|
40
|
+
Parameters:
|
41
|
+
size: int
|
42
|
+
Number of containers spun up to handle each input.
|
43
|
+
broadcast: bool = True
|
44
|
+
If True, inputs will be sent simultaneously to each container. Otherwise,
|
45
|
+
inputs will be sent only to the rank-0 container, which is responsible for
|
46
|
+
delegating to the workers.
|
47
|
+
"""
|
48
|
+
|
49
|
+
assert broadcast, "broadcast=False has not been implemented yet!"
|
50
|
+
|
51
|
+
if size <= 0:
|
52
|
+
raise ValueError("cluster size must be greater than 0")
|
53
|
+
|
54
|
+
def wrapper(raw_f: Callable[..., Any]) -> _PartialFunction:
|
55
|
+
if isinstance(raw_f, _Function):
|
56
|
+
raw_f = raw_f.get_raw_f()
|
57
|
+
raise InvalidError(
|
58
|
+
f"Applying decorators for {raw_f} in the wrong order!\nUsage:\n\n"
|
59
|
+
"@app.function()\n@modal.clustered()\ndef clustered_function():\n ..."
|
60
|
+
)
|
61
|
+
return _PartialFunction(
|
62
|
+
raw_f, _PartialFunctionFlags.FUNCTION | _PartialFunctionFlags.CLUSTERED, cluster_size=size
|
63
|
+
)
|
64
|
+
|
65
|
+
return wrapper
|
7
66
|
|
8
|
-
from .app import _container_app
|
9
67
|
|
10
|
-
|
68
|
+
def get_cluster_info() -> modal._clustered_functions.ClusterInfo:
|
69
|
+
return modal._clustered_functions.get_cluster_info()
|
modal/extensions/ipython.py
CHANGED
@@ -4,7 +4,7 @@ import logging
|
|
4
4
|
import sys
|
5
5
|
from typing import Any
|
6
6
|
|
7
|
-
from modal import
|
7
|
+
from modal import App
|
8
8
|
from modal._utils.async_utils import run_coro_blocking
|
9
9
|
from modal.config import config, logger
|
10
10
|
|
@@ -18,11 +18,11 @@ def load_ipython_extension(ipython):
|
|
18
18
|
logger.addHandler(logging.StreamHandler(stream=sys.stdout))
|
19
19
|
logger.setLevel(config["loglevel"])
|
20
20
|
|
21
|
-
# Create
|
22
|
-
|
23
|
-
ipython.push({"
|
21
|
+
# Create an app and provide it in the IPython app
|
22
|
+
app = App()
|
23
|
+
ipython.push({"app": app})
|
24
24
|
|
25
|
-
app_ctx =
|
25
|
+
app_ctx = app.run()
|
26
26
|
|
27
27
|
# Notebooks have an event loop present, but we want this function
|
28
28
|
# to be blocking. This is fairly hacky.
|