modal 1.2.1.dev21__tar.gz → 1.2.5.dev4__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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/PKG-INFO +2 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_clustered_functions.py +1 -3
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_container_entrypoint.py +33 -207
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_functions.py +116 -101
- modal-1.2.5.dev4/modal/_grpc_client.py +171 -0
- modal-1.2.5.dev4/modal/_load_context.py +106 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_object.py +54 -19
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_output.py +3 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_partial_function.py +1 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_resolver.py +21 -35
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/container_io_manager.py +21 -22
- modal-1.2.5.dev4/modal/_runtime/user_code_event_loop.py +80 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/user_code_imports.py +209 -9
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_tunnel.py +11 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_tunnel.pyi +25 -3
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/async_utils.py +27 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/auth_token_manager.py +1 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/blob_utils.py +3 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/function_utils.py +1 -2
- modal-1.2.5.dev4/modal/_utils/grpc_utils.py +455 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/mount_utils.py +26 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/task_command_router_client.py +3 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/app.py +57 -25
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/app.pyi +11 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/config.py +3 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/container.py +1 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/dict.py +5 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/entry_point.py +2 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/network_file_system.py +1 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/queues.py +5 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/run.py +3 -199
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/secret.py +1 -2
- modal-1.2.5.dev4/modal/cli/shell.py +237 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/client.py +14 -124
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/client.pyi +8 -98
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cloud_bucket_mount.py +3 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cloud_bucket_mount.pyi +4 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cls.py +76 -36
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cls.pyi +3 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/config.py +6 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/container_process.py +4 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/dict.py +26 -17
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/dict.pyi +2 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/environments.py +16 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/environments.pyi +6 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/experimental/__init__.py +15 -3
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/experimental/flash.py +11 -14
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/experimental/flash.pyi +4 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/file_io.py +13 -27
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/functions.pyi +18 -12
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/image.py +48 -30
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/image.pyi +13 -9
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/io_streams.py +162 -123
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/io_streams.pyi +63 -100
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/mount.py +36 -26
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/mount.pyi +33 -7
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/network_file_system.py +19 -11
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/network_file_system.pyi +12 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/object.pyi +35 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/parallel_map.py +29 -31
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/parallel_map.pyi +3 -9
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/proxy.py +14 -6
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/proxy.pyi +10 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/queue.py +31 -23
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/queue.pyi +12 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/runner.py +62 -58
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/runner.pyi +4 -3
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/sandbox.py +56 -82
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/sandbox.pyi +0 -10
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/secret.py +61 -44
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/secret.pyi +21 -4
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/serving.py +7 -11
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/serving.pyi +7 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/snapshot.py +11 -8
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/token_flow.py +1 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/volume.py +45 -29
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/volume.pyi +2 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/PKG-INFO +2 -2
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/SOURCES.txt +4 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api.proto +55 -10
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api_grpc.py +1 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api_pb2.py +1064 -1017
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api_pb2.pyi +197 -30
- modal-1.2.5.dev4/modal_proto/modal_api_grpc.py +194 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_version/__init__.py +1 -1
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/pyproject.toml +1 -1
- modal-1.2.1.dev21/modal/_utils/grpc_utils.py +0 -293
- modal-1.2.1.dev21/modal_proto/modal_api_grpc.py +0 -194
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/LICENSE +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/README.md +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/__main__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_billing.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_ipython.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_location.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_pty.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_resources.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_serialization.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_traceback.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_type_manager.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/logger.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/_watcher.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/billing.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/README.md +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/builder/base-images.json +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/call_graph.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/_download.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/_traceback.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/app.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/cluster.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/environment.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/import_refs.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/launch.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/profile.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/token.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/utils.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/cli/volume.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/container_process.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/exception.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/experimental/ipython.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/file_io.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/functions.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/gpu.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/object.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/output.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/partial_function.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/partial_function.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/py.typed +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/retries.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/running_app.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/schedule.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/scheduler_placement.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/snapshot.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/stream_type.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal/token_flow.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/__init__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/py.typed +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/modal_version/__main__.py +0 -0
- {modal-1.2.1.dev21 → modal-1.2.5.dev4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modal
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5.dev4
|
|
4
4
|
Summary: Python client library for Modal
|
|
5
5
|
Author-email: Modal Labs <support@modal.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -13,7 +13,7 @@ Classifier: Topic :: System :: Distributed Computing
|
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
14
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Requires-Python:
|
|
16
|
+
Requires-Python: <3.14,>=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: aiohttp
|
|
@@ -5,7 +5,6 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
7
|
from modal._utils.async_utils import synchronize_api
|
|
8
|
-
from modal._utils.grpc_utils import retry_transient_errors
|
|
9
8
|
from modal.client import _Client
|
|
10
9
|
from modal.exception import InvalidError
|
|
11
10
|
from modal_proto import api_pb2
|
|
@@ -61,8 +60,7 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
|
|
|
61
60
|
os.environ["NCCL_NSOCKS_PERTHREAD"] = "1"
|
|
62
61
|
|
|
63
62
|
if world_size > 1:
|
|
64
|
-
resp
|
|
65
|
-
client.stub.TaskClusterHello,
|
|
63
|
+
resp = await client.stub.TaskClusterHello(
|
|
66
64
|
api_pb2.TaskClusterHelloRequest(
|
|
67
65
|
task_id=task_id,
|
|
68
66
|
container_ip=container_ip,
|
|
@@ -15,27 +15,19 @@ if telemetry_socket:
|
|
|
15
15
|
instrument_imports(telemetry_socket)
|
|
16
16
|
|
|
17
17
|
import asyncio
|
|
18
|
-
import inspect
|
|
19
18
|
import queue
|
|
20
19
|
import signal
|
|
21
|
-
import sys
|
|
22
20
|
import threading
|
|
23
21
|
import time
|
|
24
|
-
|
|
25
|
-
from typing import TYPE_CHECKING, Any,
|
|
22
|
+
import types
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Optional, cast
|
|
26
24
|
|
|
27
25
|
from google.protobuf.message import Message
|
|
28
26
|
|
|
29
27
|
from modal._clustered_functions import initialize_clustered_function
|
|
30
|
-
from modal.
|
|
31
|
-
_find_callables_for_obj,
|
|
32
|
-
_PartialFunctionFlags,
|
|
33
|
-
)
|
|
28
|
+
from modal._runtime.user_code_event_loop import UserCodeEventLoop
|
|
34
29
|
from modal._serialization import deserialize, deserialize_params
|
|
35
30
|
from modal._utils.async_utils import TaskContext, aclosing, synchronizer
|
|
36
|
-
from modal._utils.function_utils import (
|
|
37
|
-
callable_has_non_self_params,
|
|
38
|
-
)
|
|
39
31
|
from modal.app import App, _App
|
|
40
32
|
from modal.client import Client, _Client
|
|
41
33
|
from modal.config import logger
|
|
@@ -53,6 +45,7 @@ from ._runtime.container_io_manager import (
|
|
|
53
45
|
if TYPE_CHECKING:
|
|
54
46
|
import modal._object
|
|
55
47
|
import modal._runtime.container_io_manager
|
|
48
|
+
import modal._runtime.user_code_imports
|
|
56
49
|
|
|
57
50
|
|
|
58
51
|
class DaemonizedThreadPool:
|
|
@@ -101,81 +94,6 @@ class DaemonizedThreadPool:
|
|
|
101
94
|
self.inputs.put((func, args))
|
|
102
95
|
|
|
103
96
|
|
|
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
|
-
|
|
179
97
|
def call_function(
|
|
180
98
|
user_code_event_loop: UserCodeEventLoop,
|
|
181
99
|
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
|
|
@@ -331,23 +249,25 @@ def call_function(
|
|
|
331
249
|
signal.signal(signal.SIGUSR1, usr1_handler) # reset signal handler
|
|
332
250
|
|
|
333
251
|
|
|
334
|
-
def
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
252
|
+
def get_serialized_user_class_and_function(
|
|
253
|
+
function_def: api_pb2.Function, client: _Client
|
|
254
|
+
) -> tuple[Optional[type], Optional[types.FunctionType]]:
|
|
255
|
+
if function_def.definition_type == api_pb2.Function.DEFINITION_TYPE_SERIALIZED:
|
|
256
|
+
assert function_def.function_serialized or function_def.class_serialized
|
|
257
|
+
|
|
258
|
+
if function_def.function_serialized:
|
|
259
|
+
ser_fun = deserialize(function_def.function_serialized, client)
|
|
260
|
+
else:
|
|
261
|
+
ser_fun = None
|
|
262
|
+
|
|
263
|
+
if function_def.class_serialized:
|
|
264
|
+
ser_usr_cls = deserialize(function_def.class_serialized, client)
|
|
265
|
+
else:
|
|
266
|
+
ser_usr_cls = None
|
|
267
|
+
else:
|
|
268
|
+
ser_usr_cls, ser_fun = None, None
|
|
269
|
+
|
|
270
|
+
return ser_usr_cls, ser_fun
|
|
351
271
|
|
|
352
272
|
|
|
353
273
|
def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
@@ -357,34 +277,20 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
357
277
|
active_app: _App
|
|
358
278
|
service: Service
|
|
359
279
|
function_def = container_args.function_def
|
|
360
|
-
is_auto_snapshot: bool = function_def.is_auto_snapshot
|
|
361
280
|
# The worker sets this flag to "1" for snapshot and restore tasks. Otherwise, this flag is unset,
|
|
362
281
|
# in which case snapshots should be disabled.
|
|
363
282
|
is_snapshotting_function = (
|
|
364
283
|
function_def.is_checkpointing_function and os.environ.get("MODAL_ENABLE_SNAP_RESTORE") == "1"
|
|
365
284
|
)
|
|
366
285
|
|
|
367
|
-
_client: _Client = synchronizer._translate_in(client) # TODO(erikbern): ugly
|
|
286
|
+
_client: _Client = cast(_Client, synchronizer._translate_in(client)) # TODO(erikbern): ugly
|
|
368
287
|
|
|
369
288
|
# Call ContainerHello - currently a noop but might be used later for things
|
|
370
289
|
container_io_manager.hello()
|
|
371
290
|
|
|
372
291
|
with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
|
|
373
292
|
# If this is a serialized function, fetch the definition from the server
|
|
374
|
-
|
|
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
|
|
293
|
+
ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
|
|
388
294
|
|
|
389
295
|
# Initialize the function, importing user code.
|
|
390
296
|
with container_io_manager.handle_user_exception():
|
|
@@ -436,7 +342,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
436
342
|
|
|
437
343
|
# Initialize objects on the app.
|
|
438
344
|
# This is basically only functions and classes - anything else is deprecated and will be unsupported soon
|
|
439
|
-
app: App = synchronizer._translate_out(active_app)
|
|
345
|
+
app: App = cast(App, synchronizer._translate_out(active_app))
|
|
440
346
|
app._init_container(client, container_app)
|
|
441
347
|
|
|
442
348
|
# Hydrate all function dependencies.
|
|
@@ -450,10 +356,13 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
450
356
|
f"Function has {len(service.service_deps)} dependencies"
|
|
451
357
|
f" but container got {len(dep_object_ids)} object ids.\n"
|
|
452
358
|
f"Code deps: {service.service_deps}\n"
|
|
453
|
-
f"Object ids: {dep_object_ids}"
|
|
359
|
+
f"Object ids: {dep_object_ids}\n"
|
|
360
|
+
"\n"
|
|
361
|
+
"This can happen if you are defining Modal objects under a conditional statement "
|
|
362
|
+
"that evaluates differently in the local and remote environments."
|
|
454
363
|
)
|
|
455
364
|
for object_id, obj in zip(dep_object_ids, service.service_deps):
|
|
456
|
-
metadata: Message = container_app.object_handle_metadata[object_id]
|
|
365
|
+
metadata: Optional[Message] = container_app.object_handle_metadata[object_id]
|
|
457
366
|
obj._hydrate(object_id, _client, metadata)
|
|
458
367
|
|
|
459
368
|
# Initialize clustered functions.
|
|
@@ -464,91 +373,8 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
464
373
|
function_def._experimental_group_size,
|
|
465
374
|
)
|
|
466
375
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
pre_snapshot_methods = _find_callables_for_obj(
|
|
470
|
-
service.user_cls_instance, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
|
|
471
|
-
)
|
|
472
|
-
call_lifecycle_functions(event_loop, container_io_manager, list(pre_snapshot_methods.values()))
|
|
473
|
-
|
|
474
|
-
# If this container is being used to create a checkpoint, checkpoint the container after
|
|
475
|
-
# global imports and initialization. Checkpointed containers run from this point onwards.
|
|
476
|
-
if is_snapshotting_function:
|
|
477
|
-
container_io_manager.memory_snapshot()
|
|
478
|
-
|
|
479
|
-
# Install hooks for interactive functions.
|
|
480
|
-
def breakpoint_wrapper():
|
|
481
|
-
# note: it would be nice to not have breakpoint_wrapper() included in the backtrace
|
|
482
|
-
container_io_manager.interact(from_breakpoint=True)
|
|
483
|
-
import pdb
|
|
484
|
-
|
|
485
|
-
frame = inspect.currentframe().f_back
|
|
486
|
-
|
|
487
|
-
pdb.Pdb().set_trace(frame)
|
|
488
|
-
|
|
489
|
-
sys.breakpointhook = breakpoint_wrapper
|
|
490
|
-
|
|
491
|
-
# Identify the "enter" methods to run after resuming from a snapshot.
|
|
492
|
-
if service.user_cls_instance is not None and not is_auto_snapshot:
|
|
493
|
-
post_snapshot_methods = _find_callables_for_obj(
|
|
494
|
-
service.user_cls_instance, _PartialFunctionFlags.ENTER_POST_SNAPSHOT
|
|
495
|
-
)
|
|
496
|
-
call_lifecycle_functions(event_loop, container_io_manager, list(post_snapshot_methods.values()))
|
|
497
|
-
|
|
498
|
-
with container_io_manager.handle_user_exception():
|
|
499
|
-
finalized_functions = service.get_finalized_functions(function_def, container_io_manager)
|
|
500
|
-
# Execute the function.
|
|
501
|
-
lifespan_background_tasks = []
|
|
502
|
-
try:
|
|
503
|
-
for finalized_function in finalized_functions.values():
|
|
504
|
-
if finalized_function.lifespan_manager:
|
|
505
|
-
lifespan_background_tasks.append(
|
|
506
|
-
event_loop.create_task(finalized_function.lifespan_manager.background_task())
|
|
507
|
-
)
|
|
508
|
-
with container_io_manager.handle_user_exception():
|
|
509
|
-
event_loop.run(finalized_function.lifespan_manager.lifespan_startup())
|
|
510
|
-
call_function(
|
|
511
|
-
event_loop,
|
|
512
|
-
container_io_manager,
|
|
513
|
-
finalized_functions,
|
|
514
|
-
batch_max_size,
|
|
515
|
-
batch_wait_ms,
|
|
516
|
-
)
|
|
517
|
-
finally:
|
|
518
|
-
# Run exit handlers. From this point onward, ignore all SIGINT signals that come from
|
|
519
|
-
# graceful shutdowns originating on the worker, as well as stray SIGUSR1 signals that
|
|
520
|
-
# may have been sent to cancel inputs.
|
|
521
|
-
int_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
522
|
-
usr1_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
523
|
-
|
|
524
|
-
try:
|
|
525
|
-
try:
|
|
526
|
-
# run lifespan shutdown for asgi apps
|
|
527
|
-
for finalized_function in finalized_functions.values():
|
|
528
|
-
if finalized_function.lifespan_manager:
|
|
529
|
-
with container_io_manager.handle_user_exception():
|
|
530
|
-
event_loop.run(finalized_function.lifespan_manager.lifespan_shutdown())
|
|
531
|
-
finally:
|
|
532
|
-
# no need to keep the lifespan asgi call around - we send it no more messages
|
|
533
|
-
for lifespan_background_task in lifespan_background_tasks:
|
|
534
|
-
lifespan_background_task.cancel() # prevent dangling tasks
|
|
535
|
-
|
|
536
|
-
# Identify "exit" methods and run them.
|
|
537
|
-
# want to make sure this is called even if the lifespan manager fails
|
|
538
|
-
if service.user_cls_instance is not None and not is_auto_snapshot:
|
|
539
|
-
exit_methods = _find_callables_for_obj(service.user_cls_instance, _PartialFunctionFlags.EXIT)
|
|
540
|
-
call_lifecycle_functions(event_loop, container_io_manager, list(exit_methods.values()))
|
|
541
|
-
|
|
542
|
-
# Finally, commit on exit to catch uncommitted volume changes and surface background
|
|
543
|
-
# commit errors.
|
|
544
|
-
container_io_manager.volume_commit(
|
|
545
|
-
[v.volume_id for v in function_def.volume_mounts if v.allow_background_commits]
|
|
546
|
-
)
|
|
547
|
-
finally:
|
|
548
|
-
# Restore the original signal handler, needed for container_test hygiene since the
|
|
549
|
-
# test runs `main()` multiple times in the same process.
|
|
550
|
-
signal.signal(signal.SIGINT, int_handler)
|
|
551
|
-
signal.signal(signal.SIGUSR1, usr1_handler)
|
|
376
|
+
with service.execution_context(event_loop, container_io_manager) as finalized_functions:
|
|
377
|
+
call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
|
|
552
378
|
|
|
553
379
|
|
|
554
380
|
if __name__ == "__main__":
|