modAL 1.2.5__tar.gz → 1.2.5.dev1__tar.gz
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-1.2.5 → modal-1.2.5.dev1}/PKG-INFO +1 -1
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_container_entrypoint.py +206 -29
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_functions.py +0 -5
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_partial_function.py +0 -2
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/user_code_imports.py +10 -236
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/blob_utils.py +5 -18
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/function_utils.py +12 -9
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/grpc_utils.py +1 -3
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/task_command_router_client.py +73 -46
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/app.py +3 -28
- modal-1.2.5.dev1/modal/cli/shell.py +237 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/client.pyi +10 -2
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/dict.py +6 -31
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/dict.pyi +0 -2
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/experimental/__init__.py +2 -8
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/experimental/flash.py +16 -132
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/experimental/flash.pyi +0 -60
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/functions.pyi +6 -7
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/io_streams.py +99 -78
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/io_streams.pyi +26 -28
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/mount.py +8 -14
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/runner.py +1 -1
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/volume.py +1 -5
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/SOURCES.txt +6 -1
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api.proto +4 -20
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api_pb2.py +874 -880
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api_pb2.pyi +9 -34
- modal-1.2.5.dev1/modal_proto/sandbox_router.proto +145 -0
- modal-1.2.5.dev1/modal_proto/sandbox_router_grpc.py +105 -0
- modal-1.2.5.dev1/modal_proto/sandbox_router_pb2.py +149 -0
- modal-1.2.5.dev1/modal_proto/sandbox_router_pb2.pyi +333 -0
- modal-1.2.5.dev1/modal_proto/sandbox_router_pb2_grpc.py +203 -0
- modal-1.2.5.dev1/modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_version/__init__.py +1 -1
- {modal-1.2.5 → modal-1.2.5.dev1}/pyproject.toml +4 -1
- modal-1.2.5/modal/_runtime/user_code_event_loop.py +0 -80
- modal-1.2.5/modal/cli/shell.py +0 -375
- {modal-1.2.5 → modal-1.2.5.dev1}/LICENSE +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/README.md +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/__main__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_billing.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_clustered_functions.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_grpc_client.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_ipython.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_load_context.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_location.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_object.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_output.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_pty.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_resolver.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_resources.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_serialization.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_traceback.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_tunnel.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_tunnel.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_type_manager.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/logger.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/_watcher.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/app.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/billing.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/README.md +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/builder/base-images.json +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/call_graph.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/_download.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/_traceback.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/app.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/cluster.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/config.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/container.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/dict.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/entry_point.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/environment.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/import_refs.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/launch.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/profile.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/queues.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/run.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/secret.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/token.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/utils.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cli/volume.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/client.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cls.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/cls.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/config.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/container_process.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/container_process.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/environments.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/environments.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/exception.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/experimental/ipython.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/file_io.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/file_io.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/functions.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/gpu.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/image.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/image.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/mount.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/network_file_system.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/network_file_system.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/object.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/object.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/output.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/parallel_map.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/parallel_map.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/partial_function.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/partial_function.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/proxy.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/proxy.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/py.typed +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/queue.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/queue.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/retries.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/runner.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/running_app.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/sandbox.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/sandbox.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/schedule.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/scheduler_placement.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/secret.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/secret.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/serving.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/serving.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/snapshot.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/snapshot.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/stream_type.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/token_flow.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/token_flow.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal/volume.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/__init__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/py.typed +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/modal_version/__main__.py +0 -0
- {modal-1.2.5 → modal-1.2.5.dev1}/setup.cfg +0 -0
|
@@ -15,19 +15,27 @@ if telemetry_socket:
|
|
|
15
15
|
instrument_imports(telemetry_socket)
|
|
16
16
|
|
|
17
17
|
import asyncio
|
|
18
|
+
import inspect
|
|
18
19
|
import queue
|
|
19
20
|
import signal
|
|
21
|
+
import sys
|
|
20
22
|
import threading
|
|
21
23
|
import time
|
|
22
|
-
import
|
|
23
|
-
from typing import TYPE_CHECKING, Any,
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
24
26
|
|
|
25
27
|
from google.protobuf.message import Message
|
|
26
28
|
|
|
27
29
|
from modal._clustered_functions import initialize_clustered_function
|
|
28
|
-
from modal.
|
|
30
|
+
from modal._partial_function import (
|
|
31
|
+
_find_callables_for_obj,
|
|
32
|
+
_PartialFunctionFlags,
|
|
33
|
+
)
|
|
29
34
|
from modal._serialization import deserialize, deserialize_params
|
|
30
35
|
from modal._utils.async_utils import TaskContext, aclosing, synchronizer
|
|
36
|
+
from modal._utils.function_utils import (
|
|
37
|
+
callable_has_non_self_params,
|
|
38
|
+
)
|
|
31
39
|
from modal.app import App, _App
|
|
32
40
|
from modal.client import Client, _Client
|
|
33
41
|
from modal.config import logger
|
|
@@ -45,7 +53,6 @@ from ._runtime.container_io_manager import (
|
|
|
45
53
|
if TYPE_CHECKING:
|
|
46
54
|
import modal._object
|
|
47
55
|
import modal._runtime.container_io_manager
|
|
48
|
-
import modal._runtime.user_code_imports
|
|
49
56
|
|
|
50
57
|
|
|
51
58
|
class DaemonizedThreadPool:
|
|
@@ -94,6 +101,81 @@ class DaemonizedThreadPool:
|
|
|
94
101
|
self.inputs.put((func, args))
|
|
95
102
|
|
|
96
103
|
|
|
104
|
+
class UserCodeEventLoop:
|
|
105
|
+
"""Run an async event loop as a context manager and handle signals.
|
|
106
|
+
|
|
107
|
+
This will run all *user supplied* async code, i.e. async functions, as well as async enter/exit managers
|
|
108
|
+
|
|
109
|
+
The following signals are handled while a coroutine is running on the event loop until
|
|
110
|
+
completion (and then handlers are deregistered):
|
|
111
|
+
|
|
112
|
+
- `SIGUSR1`: converted to an async task cancellation. Note that this only affects the event
|
|
113
|
+
loop, and the signal handler defined here doesn't run for sync functions.
|
|
114
|
+
- `SIGINT`: Unless the global signal handler has been set to SIGIGN, the loop's signal handler
|
|
115
|
+
is set to cancel the current task and raise KeyboardInterrupt to the caller.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __enter__(self):
|
|
119
|
+
self.loop = asyncio.new_event_loop()
|
|
120
|
+
self.tasks = set()
|
|
121
|
+
return self
|
|
122
|
+
|
|
123
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
124
|
+
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
|
|
125
|
+
if sys.version_info[:2] >= (3, 9):
|
|
126
|
+
self.loop.run_until_complete(self.loop.shutdown_default_executor()) # Introduced in Python 3.9
|
|
127
|
+
|
|
128
|
+
for task in self.tasks:
|
|
129
|
+
task.cancel()
|
|
130
|
+
|
|
131
|
+
self.loop.close()
|
|
132
|
+
|
|
133
|
+
def create_task(self, coro):
|
|
134
|
+
task = self.loop.create_task(coro)
|
|
135
|
+
self.tasks.add(task)
|
|
136
|
+
task.add_done_callback(self.tasks.discard)
|
|
137
|
+
return task
|
|
138
|
+
|
|
139
|
+
def run(self, coro):
|
|
140
|
+
task = asyncio.ensure_future(coro, loop=self.loop)
|
|
141
|
+
self._sigints = 0
|
|
142
|
+
|
|
143
|
+
def _sigint_handler():
|
|
144
|
+
# cancel the task in order to have run_until_complete return soon and
|
|
145
|
+
# prevent a bunch of unwanted tracebacks when shutting down the
|
|
146
|
+
# event loop.
|
|
147
|
+
|
|
148
|
+
# this basically replicates the sigint handler installed by asyncio.run()
|
|
149
|
+
self._sigints += 1
|
|
150
|
+
if self._sigints == 1:
|
|
151
|
+
# first sigint is graceful
|
|
152
|
+
task.cancel()
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# this should normally not happen, but the second sigint would "hard kill" the event loop!
|
|
156
|
+
raise KeyboardInterrupt()
|
|
157
|
+
|
|
158
|
+
ignore_sigint = signal.getsignal(signal.SIGINT) == signal.SIG_IGN
|
|
159
|
+
if not ignore_sigint:
|
|
160
|
+
self.loop.add_signal_handler(signal.SIGINT, _sigint_handler)
|
|
161
|
+
|
|
162
|
+
# Before Python 3.9 there is no argument to Task.cancel
|
|
163
|
+
if sys.version_info[:2] >= (3, 9):
|
|
164
|
+
self.loop.add_signal_handler(signal.SIGUSR1, task.cancel, "Input was cancelled by user")
|
|
165
|
+
else:
|
|
166
|
+
self.loop.add_signal_handler(signal.SIGUSR1, task.cancel)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
return self.loop.run_until_complete(task)
|
|
170
|
+
except asyncio.CancelledError:
|
|
171
|
+
if self._sigints > 0:
|
|
172
|
+
raise KeyboardInterrupt()
|
|
173
|
+
finally:
|
|
174
|
+
self.loop.remove_signal_handler(signal.SIGUSR1)
|
|
175
|
+
if not ignore_sigint:
|
|
176
|
+
self.loop.remove_signal_handler(signal.SIGINT)
|
|
177
|
+
|
|
178
|
+
|
|
97
179
|
def call_function(
|
|
98
180
|
user_code_event_loop: UserCodeEventLoop,
|
|
99
181
|
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
|
|
@@ -249,25 +331,23 @@ def call_function(
|
|
|
249
331
|
signal.signal(signal.SIGUSR1, usr1_handler) # reset signal handler
|
|
250
332
|
|
|
251
333
|
|
|
252
|
-
def
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return ser_usr_cls, ser_fun
|
|
334
|
+
def call_lifecycle_functions(
|
|
335
|
+
event_loop: UserCodeEventLoop,
|
|
336
|
+
container_io_manager, #: ContainerIOManager, TODO: this type is generated at runtime
|
|
337
|
+
funcs: Sequence[Callable[..., Any]],
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Call function(s), can be sync or async, but any return values are ignored."""
|
|
340
|
+
with container_io_manager.handle_user_exception():
|
|
341
|
+
for func in funcs:
|
|
342
|
+
# We are deprecating parametrized exit methods but want to gracefully handle old code.
|
|
343
|
+
# We can remove this once the deprecation in the actual @exit decorator is enforced.
|
|
344
|
+
args = (None, None, None) if callable_has_non_self_params(func) else ()
|
|
345
|
+
# in case func is non-async, it's executed here and sigint will by default
|
|
346
|
+
# interrupt it using a KeyboardInterrupt exception
|
|
347
|
+
res = func(*args)
|
|
348
|
+
if inspect.iscoroutine(res):
|
|
349
|
+
# if however func is async, we have to jump through some hoops
|
|
350
|
+
event_loop.run(res)
|
|
271
351
|
|
|
272
352
|
|
|
273
353
|
def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
@@ -277,20 +357,34 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
277
357
|
active_app: _App
|
|
278
358
|
service: Service
|
|
279
359
|
function_def = container_args.function_def
|
|
360
|
+
is_auto_snapshot: bool = function_def.is_auto_snapshot
|
|
280
361
|
# The worker sets this flag to "1" for snapshot and restore tasks. Otherwise, this flag is unset,
|
|
281
362
|
# in which case snapshots should be disabled.
|
|
282
363
|
is_snapshotting_function = (
|
|
283
364
|
function_def.is_checkpointing_function and os.environ.get("MODAL_ENABLE_SNAP_RESTORE") == "1"
|
|
284
365
|
)
|
|
285
366
|
|
|
286
|
-
_client: _Client =
|
|
367
|
+
_client: _Client = synchronizer._translate_in(client) # TODO(erikbern): ugly
|
|
287
368
|
|
|
288
369
|
# Call ContainerHello - currently a noop but might be used later for things
|
|
289
370
|
container_io_manager.hello()
|
|
290
371
|
|
|
291
372
|
with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
|
|
292
373
|
# If this is a serialized function, fetch the definition from the server
|
|
293
|
-
|
|
374
|
+
if function_def.definition_type == api_pb2.Function.DEFINITION_TYPE_SERIALIZED:
|
|
375
|
+
assert function_def.function_serialized or function_def.class_serialized
|
|
376
|
+
|
|
377
|
+
if function_def.function_serialized:
|
|
378
|
+
ser_fun = deserialize(function_def.function_serialized, _client)
|
|
379
|
+
else:
|
|
380
|
+
ser_fun = None
|
|
381
|
+
|
|
382
|
+
if function_def.class_serialized:
|
|
383
|
+
ser_usr_cls = deserialize(function_def.class_serialized, _client)
|
|
384
|
+
else:
|
|
385
|
+
ser_usr_cls = None
|
|
386
|
+
else:
|
|
387
|
+
ser_usr_cls, ser_fun = None, None
|
|
294
388
|
|
|
295
389
|
# Initialize the function, importing user code.
|
|
296
390
|
with container_io_manager.handle_user_exception():
|
|
@@ -342,7 +436,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
342
436
|
|
|
343
437
|
# Initialize objects on the app.
|
|
344
438
|
# This is basically only functions and classes - anything else is deprecated and will be unsupported soon
|
|
345
|
-
app: App =
|
|
439
|
+
app: App = synchronizer._translate_out(active_app)
|
|
346
440
|
app._init_container(client, container_app)
|
|
347
441
|
|
|
348
442
|
# Hydrate all function dependencies.
|
|
@@ -362,7 +456,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
362
456
|
"that evaluates differently in the local and remote environments."
|
|
363
457
|
)
|
|
364
458
|
for object_id, obj in zip(dep_object_ids, service.service_deps):
|
|
365
|
-
metadata:
|
|
459
|
+
metadata: Message = container_app.object_handle_metadata[object_id]
|
|
366
460
|
obj._hydrate(object_id, _client, metadata)
|
|
367
461
|
|
|
368
462
|
# Initialize clustered functions.
|
|
@@ -373,8 +467,91 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
373
467
|
function_def._experimental_group_size,
|
|
374
468
|
)
|
|
375
469
|
|
|
376
|
-
|
|
377
|
-
|
|
470
|
+
# Identify all "enter" methods that need to run before we snapshot.
|
|
471
|
+
if service.user_cls_instance is not None and not is_auto_snapshot:
|
|
472
|
+
pre_snapshot_methods = _find_callables_for_obj(
|
|
473
|
+
service.user_cls_instance, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
|
|
474
|
+
)
|
|
475
|
+
call_lifecycle_functions(event_loop, container_io_manager, list(pre_snapshot_methods.values()))
|
|
476
|
+
|
|
477
|
+
# If this container is being used to create a checkpoint, checkpoint the container after
|
|
478
|
+
# global imports and initialization. Checkpointed containers run from this point onwards.
|
|
479
|
+
if is_snapshotting_function:
|
|
480
|
+
container_io_manager.memory_snapshot()
|
|
481
|
+
|
|
482
|
+
# Install hooks for interactive functions.
|
|
483
|
+
def breakpoint_wrapper():
|
|
484
|
+
# note: it would be nice to not have breakpoint_wrapper() included in the backtrace
|
|
485
|
+
container_io_manager.interact(from_breakpoint=True)
|
|
486
|
+
import pdb
|
|
487
|
+
|
|
488
|
+
frame = inspect.currentframe().f_back
|
|
489
|
+
|
|
490
|
+
pdb.Pdb().set_trace(frame)
|
|
491
|
+
|
|
492
|
+
sys.breakpointhook = breakpoint_wrapper
|
|
493
|
+
|
|
494
|
+
# Identify the "enter" methods to run after resuming from a snapshot.
|
|
495
|
+
if service.user_cls_instance is not None and not is_auto_snapshot:
|
|
496
|
+
post_snapshot_methods = _find_callables_for_obj(
|
|
497
|
+
service.user_cls_instance, _PartialFunctionFlags.ENTER_POST_SNAPSHOT
|
|
498
|
+
)
|
|
499
|
+
call_lifecycle_functions(event_loop, container_io_manager, list(post_snapshot_methods.values()))
|
|
500
|
+
|
|
501
|
+
with container_io_manager.handle_user_exception():
|
|
502
|
+
finalized_functions = service.get_finalized_functions(function_def, container_io_manager)
|
|
503
|
+
# Execute the function.
|
|
504
|
+
lifespan_background_tasks = []
|
|
505
|
+
try:
|
|
506
|
+
for finalized_function in finalized_functions.values():
|
|
507
|
+
if finalized_function.lifespan_manager:
|
|
508
|
+
lifespan_background_tasks.append(
|
|
509
|
+
event_loop.create_task(finalized_function.lifespan_manager.background_task())
|
|
510
|
+
)
|
|
511
|
+
with container_io_manager.handle_user_exception():
|
|
512
|
+
event_loop.run(finalized_function.lifespan_manager.lifespan_startup())
|
|
513
|
+
call_function(
|
|
514
|
+
event_loop,
|
|
515
|
+
container_io_manager,
|
|
516
|
+
finalized_functions,
|
|
517
|
+
batch_max_size,
|
|
518
|
+
batch_wait_ms,
|
|
519
|
+
)
|
|
520
|
+
finally:
|
|
521
|
+
# Run exit handlers. From this point onward, ignore all SIGINT signals that come from
|
|
522
|
+
# graceful shutdowns originating on the worker, as well as stray SIGUSR1 signals that
|
|
523
|
+
# may have been sent to cancel inputs.
|
|
524
|
+
int_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
525
|
+
usr1_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
526
|
+
|
|
527
|
+
try:
|
|
528
|
+
try:
|
|
529
|
+
# run lifespan shutdown for asgi apps
|
|
530
|
+
for finalized_function in finalized_functions.values():
|
|
531
|
+
if finalized_function.lifespan_manager:
|
|
532
|
+
with container_io_manager.handle_user_exception():
|
|
533
|
+
event_loop.run(finalized_function.lifespan_manager.lifespan_shutdown())
|
|
534
|
+
finally:
|
|
535
|
+
# no need to keep the lifespan asgi call around - we send it no more messages
|
|
536
|
+
for lifespan_background_task in lifespan_background_tasks:
|
|
537
|
+
lifespan_background_task.cancel() # prevent dangling tasks
|
|
538
|
+
|
|
539
|
+
# Identify "exit" methods and run them.
|
|
540
|
+
# want to make sure this is called even if the lifespan manager fails
|
|
541
|
+
if service.user_cls_instance is not None and not is_auto_snapshot:
|
|
542
|
+
exit_methods = _find_callables_for_obj(service.user_cls_instance, _PartialFunctionFlags.EXIT)
|
|
543
|
+
call_lifecycle_functions(event_loop, container_io_manager, list(exit_methods.values()))
|
|
544
|
+
|
|
545
|
+
# Finally, commit on exit to catch uncommitted volume changes and surface background
|
|
546
|
+
# commit errors.
|
|
547
|
+
container_io_manager.volume_commit(
|
|
548
|
+
[v.volume_id for v in function_def.volume_mounts if v.allow_background_commits]
|
|
549
|
+
)
|
|
550
|
+
finally:
|
|
551
|
+
# Restore the original signal handler, needed for container_test hygiene since the
|
|
552
|
+
# test runs `main()` multiple times in the same process.
|
|
553
|
+
signal.signal(signal.SIGINT, int_handler)
|
|
554
|
+
signal.signal(signal.SIGUSR1, usr1_handler)
|
|
378
555
|
|
|
379
556
|
|
|
380
557
|
if __name__ == "__main__":
|
|
@@ -701,7 +701,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
701
701
|
_experimental_proxy_ip: Optional[str] = None,
|
|
702
702
|
_experimental_custom_scaling_factor: Optional[float] = None,
|
|
703
703
|
restrict_output: bool = False,
|
|
704
|
-
http_config: Optional[api_pb2.HTTPConfig] = None,
|
|
705
704
|
) -> "_Function":
|
|
706
705
|
"""mdmd:hidden
|
|
707
706
|
|
|
@@ -988,7 +987,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
988
987
|
function_definition = api_pb2.Function(
|
|
989
988
|
module_name=info.module_name or "",
|
|
990
989
|
function_name=info.function_name,
|
|
991
|
-
implementation_name=info.implementation_name,
|
|
992
990
|
mount_ids=loaded_mount_ids,
|
|
993
991
|
secret_ids=[secret.object_id for secret in secrets],
|
|
994
992
|
image_id=(image.object_id if image else ""),
|
|
@@ -1048,14 +1046,12 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1048
1046
|
function_schema=function_schema,
|
|
1049
1047
|
supported_input_formats=supported_input_formats,
|
|
1050
1048
|
supported_output_formats=supported_output_formats,
|
|
1051
|
-
http_config=http_config,
|
|
1052
1049
|
)
|
|
1053
1050
|
|
|
1054
1051
|
if isinstance(gpu, list):
|
|
1055
1052
|
function_data = api_pb2.FunctionData(
|
|
1056
1053
|
module_name=function_definition.module_name,
|
|
1057
1054
|
function_name=function_definition.function_name,
|
|
1058
|
-
implementation_name=function_definition.implementation_name,
|
|
1059
1055
|
function_type=function_definition.function_type,
|
|
1060
1056
|
warm_pool_size=function_definition.warm_pool_size,
|
|
1061
1057
|
concurrency_limit=function_definition.concurrency_limit,
|
|
@@ -1087,7 +1083,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1087
1083
|
untrusted=function_definition.untrusted,
|
|
1088
1084
|
supported_input_formats=supported_input_formats,
|
|
1089
1085
|
supported_output_formats=supported_output_formats,
|
|
1090
|
-
http_config=http_config,
|
|
1091
1086
|
)
|
|
1092
1087
|
|
|
1093
1088
|
ranked_functions = []
|
|
@@ -46,7 +46,6 @@ class _PartialFunctionFlags(enum.IntFlag):
|
|
|
46
46
|
BATCHED = 64
|
|
47
47
|
CONCURRENT = 128
|
|
48
48
|
CLUSTERED = 256 # Experimental: Clustered functions
|
|
49
|
-
HTTP_WEB_INTERFACE = 512 # Experimental: HTTP server
|
|
50
49
|
|
|
51
50
|
@staticmethod
|
|
52
51
|
def all() -> int:
|
|
@@ -77,7 +76,6 @@ class _PartialFunctionParams:
|
|
|
77
76
|
target_concurrent_inputs: Optional[int] = None
|
|
78
77
|
build_timeout: Optional[int] = None
|
|
79
78
|
rdma: Optional[bool] = None
|
|
80
|
-
http_config: Optional[api_pb2.HTTPConfig] = None
|
|
81
79
|
|
|
82
80
|
def update(self, other: "_PartialFunctionParams") -> None:
|
|
83
81
|
"""Update self with params set in other."""
|