modal 1.4.4.dev13__tar.gz → 1.4.4.dev14__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.4.4.dev13 → modal-1.4.4.dev14}/PKG-INFO +1 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_container_entrypoint.py +1 -2
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_functions.py +152 -60
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_partial_function.py +66 -71
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/container_io_manager.py +28 -26
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/container_io_manager.pyi +7 -13
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/task_lifecycle_manager.py +22 -4
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/task_lifecycle_manager.pyi +14 -2
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/user_code_imports.py +14 -38
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_server.py +7 -7
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_tunnel.py +76 -77
- modal-1.4.4.dev14/modal/_tunnel.pyi +424 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_vendor/cloudpickle.py +9 -9
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/app.py +408 -220
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/app.pyi +811 -296
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/app.py +5 -5
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/billing.py +8 -8
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/changelog.py +7 -7
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/container.py +1 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/run.py +6 -6
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/shell.py +1 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/volume.py +9 -9
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/client.py +32 -6
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/client.pyi +107 -25
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cloud_bucket_mount.py +81 -80
- modal-1.4.4.dev14/modal/cloud_bucket_mount.pyi +248 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cls.py +96 -45
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cls.pyi +196 -89
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/config.py +24 -7
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/dict.py +145 -104
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/dict.pyi +356 -240
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/exception.py +23 -25
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/experimental/ipython.py +7 -7
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/file_io.py +9 -10
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/file_io.pyi +18 -20
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/file_pattern_matcher.py +16 -16
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/functions.pyi +350 -161
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/image.py +716 -422
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/image.pyi +1213 -596
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/io_streams.py +22 -23
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/io_streams.pyi +54 -56
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/mount.py +14 -15
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/mount.pyi +26 -28
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/network_file_system.py +56 -45
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/network_file_system.pyi +126 -100
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/output.py +7 -7
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/parallel_map.py +88 -68
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/parallel_map.pyi +80 -58
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/partial_function.pyi +66 -71
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/proxy.py +7 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/proxy.pyi +16 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/queue.py +225 -148
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/queue.pyi +554 -331
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/retries.py +37 -37
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/runner.py +10 -11
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/runner.pyi +30 -33
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/sandbox.py +348 -145
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/sandbox.pyi +683 -198
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/sandbox_fs.py +133 -114
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/sandbox_fs.pyi +392 -335
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/schedule.py +41 -39
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/secret.py +153 -106
- modal-1.4.4.dev14/modal/secret.pyi +789 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/server.pyi +14 -14
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/serving.py +12 -0
- modal-1.4.4.dev14/modal/serving.pyi +99 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/snapshot.py +8 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/snapshot.pyi +18 -2
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/volume.py +204 -153
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/volume.pyi +499 -356
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/SOURCES.txt +1 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/gen_reference_docs.py +2 -0
- modal-1.4.4.dev14/modal_docs/mdmd/mdmd.py +550 -0
- modal-1.4.4.dev14/modal_docs/mdmd/signatures.py +289 -0
- modal-1.4.4.dev14/modal_docs/mdmd/types.py +33 -0
- modal-1.4.4.dev14/modal_proto/__init__.py +4 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_version/__init__.py +1 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/pyproject.toml +4 -0
- modal-1.4.4.dev13/modal/_tunnel.pyi +0 -427
- modal-1.4.4.dev13/modal/cloud_bucket_mount.pyi +0 -246
- modal-1.4.4.dev13/modal/secret.pyi +0 -662
- modal-1.4.4.dev13/modal/serving.pyi +0 -59
- modal-1.4.4.dev13/modal_docs/mdmd/mdmd.py +0 -301
- modal-1.4.4.dev13/modal_docs/mdmd/signatures.py +0 -76
- modal-1.4.4.dev13/modal_proto/__init__.py +0 -1
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/LICENSE +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/README.md +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/__main__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_billing.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_clustered_functions.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_environments.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_function_variants.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_grpc_client.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_ipython.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_load_context.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_location.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_logs.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_object.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_output/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_output/manager.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_output/pty.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_output/rich.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_output/status.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_resolver.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_resources.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/execution_context.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_serialization.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_traceback.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_type_manager.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/logger.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_vendor/version.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_watcher.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/_workspace.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/billing.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/README.md +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/builder/base-images.json +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/call_graph.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/_download.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/_help.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/_traceback.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/cluster.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/config.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/dashboard.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/dict.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/entry_point.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/environment.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/import_refs.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/launch.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/logo.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/profile.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/queues.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/secret.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/selector.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/token.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/cli/utils.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/container_process.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/container_process.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/environments.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/environments.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/experimental/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/experimental/flash.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/experimental/flash.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/functions.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/object.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/object.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/partial_function.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/py.typed +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/running_app.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/scheduler_placement.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/server.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/stream_type.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/token_flow.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/token_flow.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/workspace.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal/workspace.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/api_grpc.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/api_pb2.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/py.typed +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/modal_version/__main__.py +0 -0
- {modal-1.4.4.dev13 → modal-1.4.4.dev14}/setup.cfg +0 -0
|
@@ -401,9 +401,8 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
401
401
|
function_def._experimental_group_size,
|
|
402
402
|
)
|
|
403
403
|
|
|
404
|
-
with service.
|
|
404
|
+
with service.function_execution_context(
|
|
405
405
|
event_loop=event_loop,
|
|
406
|
-
snapshot=container_io_manager.memory_snapshot,
|
|
407
406
|
container_io_manager=container_io_manager,
|
|
408
407
|
) as finalized_functions:
|
|
409
408
|
# This context managers handles pre/post snapshot lifecycle of the user code,
|
|
@@ -1165,21 +1165,26 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1165
1165
|
Subsequent deployments of the App containing this Function will reset the autoscaler back to
|
|
1166
1166
|
its static configuration.
|
|
1167
1167
|
|
|
1168
|
-
|
|
1168
|
+
Args:
|
|
1169
|
+
min_containers: Minimum number of containers to keep running, or `None` to leave unchanged.
|
|
1170
|
+
max_containers: Maximum concurrent containers, or `None` to leave unchanged.
|
|
1171
|
+
buffer_containers: Extra containers to keep warm beyond demand, or `None` to leave unchanged.
|
|
1172
|
+
scaledown_window: Seconds idle containers wait before scaling down, or `None` to leave unchanged.
|
|
1169
1173
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1174
|
+
Examples:
|
|
1175
|
+
```python notest
|
|
1176
|
+
f = modal.Function.from_name("my-app", "function")
|
|
1172
1177
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1178
|
+
# Always have at least 2 containers running, with an extra buffer when the Function is active
|
|
1179
|
+
f.update_autoscaler(min_containers=2, buffer_containers=1)
|
|
1175
1180
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1181
|
+
# Limit this Function to avoid spinning up more than 5 containers
|
|
1182
|
+
f.update_autoscaler(max_containers=5)
|
|
1178
1183
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1184
|
+
# Extend the scaledown window to increase the amount of time that idle containers stay alive
|
|
1185
|
+
f.update_autoscaler(scaledown_window=300)
|
|
1181
1186
|
|
|
1182
|
-
|
|
1187
|
+
```
|
|
1183
1188
|
|
|
1184
1189
|
"""
|
|
1185
1190
|
if self._is_method:
|
|
@@ -1255,9 +1260,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1255
1260
|
object with metadata from Modal servers until the first
|
|
1256
1261
|
time it is actually used.
|
|
1257
1262
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1263
|
+
Args:
|
|
1264
|
+
app_name: Name of the deployed App.
|
|
1265
|
+
name: Name of the Function within that App. For class methods, use `Cls.from_name` instead.
|
|
1266
|
+
environment_name: Environment to look up the App in; defaults to the active environment.
|
|
1267
|
+
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
1268
|
+
|
|
1269
|
+
Returns:
|
|
1270
|
+
A lazy `Function` handle.
|
|
1271
|
+
|
|
1272
|
+
Examples:
|
|
1273
|
+
```python
|
|
1274
|
+
f = modal.Function.from_name("other-app", "function")
|
|
1275
|
+
```
|
|
1261
1276
|
"""
|
|
1262
1277
|
if "." in name:
|
|
1263
1278
|
raise InvalidError(
|
|
@@ -1388,13 +1403,18 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1388
1403
|
async def get_web_url(self) -> str | None:
|
|
1389
1404
|
"""URL for addressing a Web Function via HTTP.
|
|
1390
1405
|
|
|
1391
|
-
Returns
|
|
1406
|
+
Returns:
|
|
1407
|
+
The HTTPS URL for the web endpoint, or `None` if this Function is not a web endpoint.
|
|
1392
1408
|
"""
|
|
1393
1409
|
return self._web_url
|
|
1394
1410
|
|
|
1395
1411
|
@live_method
|
|
1396
1412
|
async def _experimental_get_flash_urls(self) -> list[str] | None:
|
|
1397
|
-
"""URL of the flash service for the function.
|
|
1413
|
+
"""URL of the flash service for the function.
|
|
1414
|
+
|
|
1415
|
+
Returns:
|
|
1416
|
+
Flash service URLs when configured, or `None`.
|
|
1417
|
+
"""
|
|
1398
1418
|
return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
|
|
1399
1419
|
|
|
1400
1420
|
def _apply_dynamic_config(
|
|
@@ -1681,8 +1701,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1681
1701
|
@synchronizer.no_io_translation
|
|
1682
1702
|
@live_method
|
|
1683
1703
|
async def remote(self, *args: P.args, **kwargs: P.kwargs) -> ReturnType:
|
|
1684
|
-
"""
|
|
1685
|
-
|
|
1704
|
+
"""Calls the function remotely, executing it with the given arguments and returning the execution's result.
|
|
1705
|
+
|
|
1706
|
+
Args:
|
|
1707
|
+
*args: Positional arguments forwarded to the deployed function.
|
|
1708
|
+
**kwargs: Keyword arguments forwarded to the deployed function.
|
|
1709
|
+
|
|
1710
|
+
Returns:
|
|
1711
|
+
The value returned by the remote function.
|
|
1686
1712
|
"""
|
|
1687
1713
|
# TODO: Generics/TypeVars
|
|
1688
1714
|
self._check_no_web_url("remote")
|
|
@@ -1696,8 +1722,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1696
1722
|
@synchronizer.no_io_translation
|
|
1697
1723
|
@live_method_gen
|
|
1698
1724
|
async def remote_gen(self, *args, **kwargs) -> AsyncGenerator[Any, None]:
|
|
1699
|
-
"""
|
|
1700
|
-
|
|
1725
|
+
"""Calls the generator remotely, executing it with the given arguments.
|
|
1726
|
+
|
|
1727
|
+
Args:
|
|
1728
|
+
*args: Positional arguments forwarded to the deployed generator function.
|
|
1729
|
+
**kwargs: Keyword arguments forwarded to the deployed generator function.
|
|
1730
|
+
|
|
1731
|
+
Yields:
|
|
1732
|
+
Values produced by the remote generator.
|
|
1701
1733
|
"""
|
|
1702
1734
|
# TODO: Generics/TypeVars
|
|
1703
1735
|
self._check_no_web_url("remote_gen")
|
|
@@ -1727,12 +1759,18 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1727
1759
|
|
|
1728
1760
|
@synchronizer.nowrap
|
|
1729
1761
|
def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType:
|
|
1730
|
-
"""
|
|
1731
|
-
Calls the function locally, executing it with the given arguments and returning the execution's result.
|
|
1762
|
+
"""Calls the function locally, executing it with the given arguments and returning the execution's result.
|
|
1732
1763
|
|
|
1733
1764
|
The function will execute in the same environment as the caller, just like calling the underlying function
|
|
1734
1765
|
directly in Python. In particular, only secrets available in the caller environment will be available
|
|
1735
1766
|
through environment variables.
|
|
1767
|
+
|
|
1768
|
+
Args:
|
|
1769
|
+
*args: Positional arguments passed to the underlying Python callable.
|
|
1770
|
+
**kwargs: Keyword arguments passed to the underlying Python callable.
|
|
1771
|
+
|
|
1772
|
+
Returns:
|
|
1773
|
+
The return value of the local call (or a coroutine for async functions).
|
|
1736
1774
|
"""
|
|
1737
1775
|
# TODO(erikbern): it would be nice to remove the nowrap thing, but right now that would cause
|
|
1738
1776
|
# "user code" to run on the synchronicity thread, which seems bad
|
|
@@ -1785,9 +1823,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1785
1823
|
|
|
1786
1824
|
This experimental version of the spawn method allows up to 1 million inputs to be spawned.
|
|
1787
1825
|
|
|
1788
|
-
Returns a `modal.FunctionCall` object, that can later be polled or
|
|
1789
|
-
waited for using `.get(timeout=...)`.
|
|
1790
1826
|
Conceptually similar to `multiprocessing.pool.apply_async`, or a Future/Promise in other contexts.
|
|
1827
|
+
|
|
1828
|
+
Args:
|
|
1829
|
+
*args: Positional arguments forwarded to the remote function.
|
|
1830
|
+
**kwargs: Keyword arguments forwarded to the remote function.
|
|
1831
|
+
|
|
1832
|
+
Returns:
|
|
1833
|
+
A `modal.FunctionCall` handle; poll or await results with `.get(timeout=...)`.
|
|
1791
1834
|
"""
|
|
1792
1835
|
self._check_no_web_url("_experimental_spawn")
|
|
1793
1836
|
if self._is_generator:
|
|
@@ -1817,10 +1860,16 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1817
1860
|
async def spawn(self, *args: P.args, **kwargs: P.kwargs) -> "_FunctionCall[ReturnType]":
|
|
1818
1861
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
1819
1862
|
|
|
1820
|
-
Returns a [`modal.FunctionCall`](https://modal.com/docs/reference/modal.FunctionCall) object
|
|
1821
|
-
that can later be polled or waited for using
|
|
1822
|
-
[`.get(timeout=...)`](https://modal.com/docs/reference/modal.FunctionCall#get).
|
|
1823
1863
|
Conceptually similar to `multiprocessing.pool.apply_async`, or a Future/Promise in other contexts.
|
|
1864
|
+
|
|
1865
|
+
Args:
|
|
1866
|
+
*args: Positional arguments forwarded to the remote function.
|
|
1867
|
+
**kwargs: Keyword arguments forwarded to the remote function.
|
|
1868
|
+
|
|
1869
|
+
Returns:
|
|
1870
|
+
A [`modal.FunctionCall`](https://modal.com/docs/reference/modal.FunctionCall) object
|
|
1871
|
+
that can later be polled or waited for using
|
|
1872
|
+
[`.get(timeout=...)`](https://modal.com/docs/reference/modal.FunctionCall#get).
|
|
1824
1873
|
"""
|
|
1825
1874
|
self._check_no_web_url("spawn")
|
|
1826
1875
|
if self._is_generator:
|
|
@@ -1834,13 +1883,21 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1834
1883
|
return fc
|
|
1835
1884
|
|
|
1836
1885
|
def get_raw_f(self) -> Callable[..., Any]:
|
|
1837
|
-
"""Return the inner Python object wrapped by this Modal Function.
|
|
1886
|
+
"""Return the inner Python object wrapped by this Modal Function.
|
|
1887
|
+
|
|
1888
|
+
Returns:
|
|
1889
|
+
The original function object registered with Modal.
|
|
1890
|
+
"""
|
|
1838
1891
|
assert self._raw_f is not None
|
|
1839
1892
|
return self._raw_f
|
|
1840
1893
|
|
|
1841
1894
|
@live_method
|
|
1842
1895
|
async def get_current_stats(self) -> FunctionStats:
|
|
1843
|
-
"""Return a `FunctionStats` object describing the current function's queue and runner counts.
|
|
1896
|
+
"""Return a `FunctionStats` object describing the current function's queue and runner counts.
|
|
1897
|
+
|
|
1898
|
+
Returns:
|
|
1899
|
+
Snapshot counts for backlog, runners, and running inputs.
|
|
1900
|
+
"""
|
|
1844
1901
|
resp = await self.client.stub.FunctionGetCurrentStats(
|
|
1845
1902
|
api_pb2.FunctionGetCurrentStatsRequest(function_id=self.object_id),
|
|
1846
1903
|
retry=Retry(total_timeout=10.0),
|
|
@@ -1888,7 +1945,11 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1888
1945
|
|
|
1889
1946
|
@live_method
|
|
1890
1947
|
async def num_inputs(self) -> int:
|
|
1891
|
-
"""Get the number of inputs in the function call.
|
|
1948
|
+
"""Get the number of inputs in the function call.
|
|
1949
|
+
|
|
1950
|
+
Returns:
|
|
1951
|
+
How many inputs this function call includes (e.g. `1` for `.spawn()`, more for `.spawn_map()`).
|
|
1952
|
+
"""
|
|
1892
1953
|
if self._num_inputs is None:
|
|
1893
1954
|
request = api_pb2.FunctionCallFromIdRequest(function_call_id=self.object_id)
|
|
1894
1955
|
resp = await self.client.stub.FunctionCallFromId(request)
|
|
@@ -1898,6 +1959,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1898
1959
|
@live_method
|
|
1899
1960
|
async def get(self, timeout: float | None = None, *, index: int = 0) -> ReturnType:
|
|
1900
1961
|
"""Get the result of the index-th input of the function call.
|
|
1962
|
+
|
|
1901
1963
|
`.spawn()` calls have a single output, so only specifying `index=0` is valid.
|
|
1902
1964
|
A non-zero index is useful when your function has multiple outputs, like via `.spawn_map()`.
|
|
1903
1965
|
|
|
@@ -1906,6 +1968,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1906
1968
|
which can be set to `0` to poll for an output immediately.
|
|
1907
1969
|
|
|
1908
1970
|
The returned coroutine is not cancellation-safe.
|
|
1971
|
+
|
|
1972
|
+
Args:
|
|
1973
|
+
timeout: Maximum seconds to wait for a result, or `None` to wait indefinitely.
|
|
1974
|
+
index: Which input's result to retrieve (typically `0` for `.spawn()`).
|
|
1975
|
+
|
|
1976
|
+
Returns:
|
|
1977
|
+
The deserialized return value from that input.
|
|
1909
1978
|
"""
|
|
1910
1979
|
return await self._invocation().poll_function(timeout=timeout, index=index)
|
|
1911
1980
|
|
|
@@ -1915,21 +1984,28 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1915
1984
|
|
|
1916
1985
|
Optionally, specify a range [start, end) to iterate over.
|
|
1917
1986
|
|
|
1918
|
-
|
|
1919
|
-
```python
|
|
1920
|
-
@app.function()
|
|
1921
|
-
def my_func(a):
|
|
1922
|
-
return a ** 2
|
|
1987
|
+
If `end` is not provided, it will iterate over all results.
|
|
1923
1988
|
|
|
1989
|
+
Args:
|
|
1990
|
+
start: First input index to include (inclusive).
|
|
1991
|
+
end: One past the last index to include, or `None` for all remaining inputs.
|
|
1924
1992
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
fc = my_func.spawn_map([1, 2, 3, 4])
|
|
1928
|
-
assert list(fc.iter()) == [1, 4, 9, 16]
|
|
1929
|
-
assert list(fc.iter(start=1, end=3)) == [4, 9]
|
|
1930
|
-
```
|
|
1993
|
+
Yields:
|
|
1994
|
+
Each result value in index order.
|
|
1931
1995
|
|
|
1932
|
-
|
|
1996
|
+
Examples:
|
|
1997
|
+
```python
|
|
1998
|
+
@app.function()
|
|
1999
|
+
def my_func(a):
|
|
2000
|
+
return a ** 2
|
|
2001
|
+
|
|
2002
|
+
|
|
2003
|
+
@app.local_entrypoint()
|
|
2004
|
+
def main():
|
|
2005
|
+
fc = my_func.spawn_map([1, 2, 3, 4])
|
|
2006
|
+
assert list(fc.iter()) == [1, 4, 9, 16]
|
|
2007
|
+
assert list(fc.iter(start=1, end=3)) == [4, 9]
|
|
2008
|
+
```
|
|
1933
2009
|
"""
|
|
1934
2010
|
num_inputs = await self.num_inputs()
|
|
1935
2011
|
if end is None:
|
|
@@ -1946,6 +2022,9 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1946
2022
|
|
|
1947
2023
|
See [`modal.call_graph`](https://modal.com/docs/reference/modal.call_graph) reference page
|
|
1948
2024
|
for documentation on the structure of the returned `InputInfo` items.
|
|
2025
|
+
|
|
2026
|
+
Returns:
|
|
2027
|
+
A list of `InputInfo` nodes describing the call graph.
|
|
1949
2028
|
"""
|
|
1950
2029
|
assert self._client and self._client.stub
|
|
1951
2030
|
request = api_pb2.FunctionGetCallGraphRequest(function_call_id=self.object_id)
|
|
@@ -1963,6 +2042,9 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1963
2042
|
|
|
1964
2043
|
If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
|
|
1965
2044
|
causing any non-cancelled inputs on those containers to be rescheduled in new containers.
|
|
2045
|
+
|
|
2046
|
+
Args:
|
|
2047
|
+
terminate_containers: If True, forcibly terminate workers running cancelled inputs.
|
|
1966
2048
|
"""
|
|
1967
2049
|
request = api_pb2.FunctionCallCancelRequest(
|
|
1968
2050
|
function_call_id=self.object_id, terminate_containers=terminate_containers
|
|
@@ -1977,20 +2059,26 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1977
2059
|
) -> "modal.functions.FunctionCall[Any]":
|
|
1978
2060
|
"""Instantiate a FunctionCall object from an existing ID.
|
|
1979
2061
|
|
|
1980
|
-
|
|
2062
|
+
Note that it's only necessary to re-instantiate the `FunctionCall` with this method
|
|
2063
|
+
if you no longer have access to the original object returned from `Function.spawn`.
|
|
1981
2064
|
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
fc_id = fc.object_id
|
|
2065
|
+
Args:
|
|
2066
|
+
function_call_id: Object ID of an existing function call (e.g. from `FunctionCall.object_id`).
|
|
2067
|
+
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
1986
2068
|
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
result = fc.get()
|
|
1990
|
-
```
|
|
2069
|
+
Returns:
|
|
2070
|
+
A `FunctionCall` handle for the given ID.
|
|
1991
2071
|
|
|
1992
|
-
|
|
1993
|
-
|
|
2072
|
+
Examples:
|
|
2073
|
+
```python notest
|
|
2074
|
+
# Spawn a FunctionCall and keep track of its object ID
|
|
2075
|
+
fc = my_func.spawn()
|
|
2076
|
+
fc_id = fc.object_id
|
|
2077
|
+
|
|
2078
|
+
# Later, use the ID to re-instantiate the FunctionCall object
|
|
2079
|
+
fc = FunctionCall.from_id(fc_id)
|
|
2080
|
+
result = fc.get()
|
|
2081
|
+
```
|
|
1994
2082
|
|
|
1995
2083
|
"""
|
|
1996
2084
|
_client = typing.cast(_Client, synchronizer._translate_in(client))
|
|
@@ -2013,17 +2101,21 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
2013
2101
|
|
|
2014
2102
|
Accepts a variable number of `FunctionCall` objects, as returned by `Function.spawn()`.
|
|
2015
2103
|
|
|
2016
|
-
|
|
2017
|
-
from the first failing function call.
|
|
2104
|
+
Raises an exception from the first failing function call.
|
|
2018
2105
|
|
|
2019
|
-
|
|
2106
|
+
Args:
|
|
2107
|
+
*function_calls: `FunctionCall` instances to wait on (same order as the returned sequence).
|
|
2020
2108
|
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
fc2 = slow_func_2.spawn()
|
|
2109
|
+
Returns:
|
|
2110
|
+
Results in the same order as `function_calls` (like `asyncio.gather`).
|
|
2024
2111
|
|
|
2025
|
-
|
|
2026
|
-
|
|
2112
|
+
Examples:
|
|
2113
|
+
```python notest
|
|
2114
|
+
fc1 = slow_func_1.spawn()
|
|
2115
|
+
fc2 = slow_func_2.spawn()
|
|
2116
|
+
|
|
2117
|
+
result_1, result_2 = modal.FunctionCall.gather(fc1, fc2)
|
|
2118
|
+
```
|
|
2027
2119
|
|
|
2028
2120
|
*Added in v0.73.69*: This method replaces the deprecated `modal.functions.gather` function.
|
|
2029
2121
|
"""
|
|
@@ -288,16 +288,15 @@ def _method(
|
|
|
288
288
|
) -> _MethodDecoratorType:
|
|
289
289
|
"""Decorator for methods that should be transformed into a Modal Function registered against this class's App.
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
```
|
|
291
|
+
Examples:
|
|
292
|
+
```python
|
|
293
|
+
@app.cls(cpu=8)
|
|
294
|
+
class MyCls:
|
|
295
|
+
|
|
296
|
+
@modal.method()
|
|
297
|
+
def f(self):
|
|
298
|
+
...
|
|
299
|
+
```
|
|
301
300
|
"""
|
|
302
301
|
if _warn_parentheses_missing is not None:
|
|
303
302
|
raise InvalidError(
|
|
@@ -423,16 +422,15 @@ def _asgi_app(
|
|
|
423
422
|
Asynchronous Server Gateway Interface (ASGI) is a standard for Python
|
|
424
423
|
web apps, supported by all popular Python web libraries.
|
|
425
424
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
from typing import Callable
|
|
425
|
+
Examples:
|
|
426
|
+
```python
|
|
427
|
+
from typing import Callable
|
|
430
428
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
429
|
+
@app.function()
|
|
430
|
+
@modal.asgi_app()
|
|
431
|
+
def create_asgi() -> Callable:
|
|
432
|
+
...
|
|
433
|
+
```
|
|
436
434
|
|
|
437
435
|
To learn how to use Modal with popular web frameworks, see the
|
|
438
436
|
[guide on Web Functions](https://modal.com/docs/guide/webhooks).
|
|
@@ -481,16 +479,15 @@ def _wsgi_app(
|
|
|
481
479
|
which is compatible with ASGI and supports additional functionality such as web sockets.
|
|
482
480
|
Modal supports ASGI via [`asgi_app`](https://modal.com/docs/reference/modal.asgi_app).
|
|
483
481
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
from typing import Callable
|
|
482
|
+
Examples:
|
|
483
|
+
```python
|
|
484
|
+
from typing import Callable
|
|
488
485
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
486
|
+
@app.function()
|
|
487
|
+
@modal.wsgi_app()
|
|
488
|
+
def create_wsgi() -> Callable:
|
|
489
|
+
...
|
|
490
|
+
```
|
|
494
491
|
|
|
495
492
|
To learn how to use this decorator with popular web frameworks, see the
|
|
496
493
|
[guide on Web Functions](https://modal.com/docs/guide/webhooks).
|
|
@@ -539,16 +536,15 @@ def _web_server(
|
|
|
539
536
|
HTTP server listening on a container port. This is useful for servers written in other languages
|
|
540
537
|
like Rust, as well as integrating with non-ASGI frameworks like aiohttp and Tornado.
|
|
541
538
|
|
|
542
|
-
|
|
539
|
+
Examples:
|
|
540
|
+
```python
|
|
541
|
+
import subprocess
|
|
543
542
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
def my_file_server():
|
|
550
|
-
subprocess.Popen("python -m http.server -d / 8000", shell=True)
|
|
551
|
-
```
|
|
543
|
+
@app.function()
|
|
544
|
+
@modal.web_server(8000)
|
|
545
|
+
def my_file_server():
|
|
546
|
+
subprocess.Popen("python -m http.server -d / 8000", shell=True)
|
|
547
|
+
```
|
|
552
548
|
|
|
553
549
|
The above example starts a simple file server, displaying the contents of the root directory.
|
|
554
550
|
Here, requests to the URL will go to external port 8000 on the container. The
|
|
@@ -651,25 +647,24 @@ def _batched(
|
|
|
651
647
|
]:
|
|
652
648
|
"""Decorator for functions or class methods that should be batched.
|
|
653
649
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
@app.function()
|
|
659
|
-
@modal.batched(max_batch_size=4, wait_ms=1000)
|
|
660
|
-
async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]:
|
|
661
|
-
return [x * y for x, y in zip(xs, ys)]
|
|
662
|
-
|
|
663
|
-
# call batched_multiply with individual inputs
|
|
664
|
-
# batched_multiply.remote.aio(2, 100)
|
|
665
|
-
|
|
666
|
-
# With `@app.cls()`, apply the decorator to a method (this may change in the future)
|
|
667
|
-
@app.cls()
|
|
668
|
-
class BatchedClass:
|
|
650
|
+
Examples:
|
|
651
|
+
```python
|
|
652
|
+
# Stack the decorator under `@app.function()` to enable dynamic batching
|
|
653
|
+
@app.function()
|
|
669
654
|
@modal.batched(max_batch_size=4, wait_ms=1000)
|
|
670
|
-
def batched_multiply(
|
|
655
|
+
async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]:
|
|
671
656
|
return [x * y for x, y in zip(xs, ys)]
|
|
672
|
-
|
|
657
|
+
|
|
658
|
+
# call batched_multiply with individual inputs
|
|
659
|
+
# batched_multiply.remote.aio(2, 100)
|
|
660
|
+
|
|
661
|
+
# With `@app.cls()`, apply the decorator to a method (this may change in the future)
|
|
662
|
+
@app.cls()
|
|
663
|
+
class BatchedClass:
|
|
664
|
+
@modal.batched(max_batch_size=4, wait_ms=1000)
|
|
665
|
+
def batched_multiply(self, xs: list[int], ys: list[int]) -> list[int]:
|
|
666
|
+
return [x * y for x, y in zip(xs, ys)]
|
|
667
|
+
```
|
|
673
668
|
|
|
674
669
|
See the [dynamic batching guide](https://modal.com/docs/guide/dynamic-batching) for more information.
|
|
675
670
|
"""
|
|
@@ -728,25 +723,25 @@ def _concurrent(
|
|
|
728
723
|
arrival rate of inputs increases. This can trade-off a small increase in average
|
|
729
724
|
latency to avoid larger tail latencies from input queuing.
|
|
730
725
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
...
|
|
739
|
-
|
|
740
|
-
# With `@app.cls()`, apply the decorator at the class level, not on individual methods
|
|
741
|
-
@app.cls()
|
|
742
|
-
@modal.concurrent(max_inputs=100, target_inputs=80)
|
|
743
|
-
class C:
|
|
744
|
-
@modal.method()
|
|
745
|
-
def f(self, data):
|
|
746
|
-
# Sync function; must be thread-safe
|
|
726
|
+
Examples:
|
|
727
|
+
```python
|
|
728
|
+
# Stack the decorator under `@app.function()` to enable input concurrency
|
|
729
|
+
@app.function()
|
|
730
|
+
@modal.concurrent(max_inputs=100)
|
|
731
|
+
async def f(data):
|
|
732
|
+
# Async function; will be scheduled as asyncio task
|
|
747
733
|
...
|
|
748
734
|
|
|
749
|
-
|
|
735
|
+
# With `@app.cls()`, apply the decorator at the class level, not on individual methods
|
|
736
|
+
@app.cls()
|
|
737
|
+
@modal.concurrent(max_inputs=100, target_inputs=80)
|
|
738
|
+
class C:
|
|
739
|
+
@modal.method()
|
|
740
|
+
def f(self, data):
|
|
741
|
+
# Sync function; must be thread-safe
|
|
742
|
+
...
|
|
743
|
+
|
|
744
|
+
```
|
|
750
745
|
|
|
751
746
|
*Added in v0.73.148:* This decorator replaces the `allow_concurrent_inputs` parameter
|
|
752
747
|
in `@app.function()` and `@app.cls()`.
|
|
@@ -956,42 +956,25 @@ class _ContainerIOManager:
|
|
|
956
956
|
outputs = await io_context.output_items(started_at, output_data)
|
|
957
957
|
await self._send_outputs(started_at, outputs)
|
|
958
958
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
if self.checkpoint_id:
|
|
962
|
-
logger.debug(f"Checkpoint ID: {self.checkpoint_id} (Memory Snapshot ID)")
|
|
963
|
-
else:
|
|
964
|
-
raise ValueError("No checkpoint ID provided for memory snapshot")
|
|
965
|
-
|
|
959
|
+
@asynccontextmanager
|
|
960
|
+
async def snapshot_context_manager(self) -> AsyncGenerator[None, None]:
|
|
966
961
|
# Pause heartbeats since they keep the client connection open which causes the snapshotter to crash
|
|
967
962
|
async with self.heartbeat_condition:
|
|
968
|
-
# Snapshot GPU memory.
|
|
969
|
-
if self.function_def._experimental_enable_gpu_snapshot and self.function_def.resources.gpu_config.gpu_type:
|
|
970
|
-
logger.debug("GPU memory snapshot enabled. Attempting to snapshot GPU memory.")
|
|
971
|
-
|
|
972
|
-
self._task_lifecycle_manager.create_cuda_checkpoint()
|
|
973
|
-
|
|
974
963
|
# Notify the heartbeat loop that the snapshot phase has begun in order to
|
|
975
964
|
# prevent it from sending heartbeat RPCs
|
|
976
965
|
self._waiting_for_memory_snapshot = True
|
|
977
966
|
self.heartbeat_condition.notify_all()
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
logger.debug("Memory snapshot request sent. Connection closed.")
|
|
986
|
-
await self._task_lifecycle_manager.memory_restore()
|
|
967
|
+
try:
|
|
968
|
+
yield
|
|
969
|
+
finally:
|
|
970
|
+
# Turn heartbeats back on. This is safe since the snapshot RPC
|
|
971
|
+
# and the restore phase has finished.
|
|
972
|
+
self._waiting_for_memory_snapshot = False
|
|
973
|
+
self.heartbeat_condition.notify_all()
|
|
987
974
|
# Reset current input state
|
|
988
975
|
self.current_input_id = None
|
|
989
976
|
self.current_inputs = {}
|
|
990
977
|
self.current_input_started_at = None
|
|
991
|
-
# Turn heartbeats back on. This is safe since the snapshot RPC
|
|
992
|
-
# and the restore phase has finished.
|
|
993
|
-
self._waiting_for_memory_snapshot = False
|
|
994
|
-
self.heartbeat_condition.notify_all()
|
|
995
978
|
|
|
996
979
|
async def interact(self, from_breakpoint: bool = False):
|
|
997
980
|
if self._is_interactivity_enabled:
|
|
@@ -1009,6 +992,25 @@ class _ContainerIOManager:
|
|
|
1009
992
|
logger.error("Failed to start PTY shell.")
|
|
1010
993
|
raise e
|
|
1011
994
|
|
|
995
|
+
def _install_breakpoint_hook(self) -> None:
|
|
996
|
+
# sys.breakpointhook is synchronous, so call the blocking wrapper for async interact().
|
|
997
|
+
container_io_manager: Any = synchronizer._translate_out(self)
|
|
998
|
+
|
|
999
|
+
# Install hooks for interactive functions.
|
|
1000
|
+
def breakpoint_wrapper(*args: Any, **kwargs: Any) -> None:
|
|
1001
|
+
# note: it would be nice to not have breakpoint_wrapper() included in the backtrace
|
|
1002
|
+
container_io_manager.interact(from_breakpoint=True)
|
|
1003
|
+
import pdb # noqa: T100
|
|
1004
|
+
|
|
1005
|
+
current_frame = inspect.currentframe()
|
|
1006
|
+
if current_frame is not None:
|
|
1007
|
+
frame = current_frame.f_back
|
|
1008
|
+
pdb.Pdb().set_trace(frame)
|
|
1009
|
+
else:
|
|
1010
|
+
raise RuntimeError("No current frame found")
|
|
1011
|
+
|
|
1012
|
+
sys.breakpointhook = breakpoint_wrapper
|
|
1013
|
+
|
|
1012
1014
|
@property
|
|
1013
1015
|
def target_concurrency(self) -> int:
|
|
1014
1016
|
return self._target_concurrency
|