modal 1.5.3.dev5__tar.gz → 1.5.3.dev7__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.5.3.dev5 → modal-1.5.3.dev7}/PKG-INFO +1 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_environments.py +7 -8
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_functions.py +11 -15
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/task_command_router_client.py +127 -3
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_workspace.py +11 -14
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/shell.py +20 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/client.pyi +2 -2
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/container_process.py +13 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/container_process.pyi +27 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/environments.pyi +12 -10
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/functions.pyi +20 -20
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/runner.py +31 -9
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/runner.pyi +8 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/sandbox.py +31 -16
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/sandbox.pyi +10 -14
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/sandbox_fs.py +33 -82
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/sandbox_fs.pyi +26 -6
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/workspace.pyi +22 -18
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/gen_reference_docs.py +0 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/mdmd/mdmd.py +21 -4
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/mdmd/types.py +1 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/api_pb2.py +117 -104
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/api_pb2.pyi +22 -14
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_version/__init__.py +1 -1
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/LICENSE +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/README.md +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/__main__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_billing.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_clustered_functions.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_function_variants.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_grpc_client.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_image.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_ipython.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_load_context.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_location.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_logs.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_object.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_output/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_output/manager.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_output/pty.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_output/rich.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_output/status.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_partial_function.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_resolver.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_resources.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_serialization.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_server.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_traceback.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_tunnel.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_tunnel.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_type_manager.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/logger.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_vendor/version.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/_watcher.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/app.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/app.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/billing.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/billing.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/README.md +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/builder/base-images.json +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/call_graph.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/_download.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/_help.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/_traceback.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/app.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/billing.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/changelog.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/cluster.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/config.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/container.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/curl.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/dashboard.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/dict.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/endpoint.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/entry_point.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/environment.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/image.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/import_refs.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/launch.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/logo.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/profile.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/queues.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/run.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/secret.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/selector.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/skills.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/token.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/utils.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/volume.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cli/workspace.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/client.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cls.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/cls.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/config.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/dict.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/dict.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/environments.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/exception.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/experimental/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/experimental/flash.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/experimental/ipython.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/file_io.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/file_io.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/functions.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/image.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/image.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/io_streams.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/io_streams.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/mount.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/mount.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/network_file_system.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/network_file_system.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/object.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/object.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/output.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/parallel_map.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/parallel_map.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/partial_function.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/partial_function.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/proxy.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/proxy.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/py.typed +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/queue.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/queue.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/retries.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/running_app.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/schedule.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/scheduler_placement.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/secret.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/secret.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/server.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/server.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/serving.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/serving.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/snapshot.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/snapshot.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/stream_type.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/token_flow.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/token_flow.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/types.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/volume.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/volume.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal/workspace.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/__init__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/py.typed +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/modal_version/__main__.py +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/pyproject.toml +0 -0
- {modal-1.5.3.dev5 → modal-1.5.3.dev7}/setup.cfg +0 -0
|
@@ -104,11 +104,7 @@ class _EnvironmentManager:
|
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
class _EnvironmentMembersManager:
|
|
107
|
-
"""mdmd:namespace
|
|
108
|
-
Namespace with methods for managing the membership of a restricted Environment.
|
|
109
|
-
|
|
110
|
-
See https://modal.com/docs/guide/rbac for more information on restricted Environments.
|
|
111
|
-
"""
|
|
107
|
+
"""mdmd:namespace"""
|
|
112
108
|
|
|
113
109
|
def __init__(self, environment: "_Environment"):
|
|
114
110
|
"""mdmd:hidden"""
|
|
@@ -288,6 +284,10 @@ class _Environment(_Object, type_prefix="en"):
|
|
|
288
284
|
|
|
289
285
|
@property
|
|
290
286
|
def members(self) -> "_EnvironmentMembersManager":
|
|
287
|
+
"""Namespace with methods for managing the membership of a restricted Environment.
|
|
288
|
+
|
|
289
|
+
See https://modal.com/docs/guide/rbac for more information on restricted Environments.
|
|
290
|
+
"""
|
|
291
291
|
return _EnvironmentMembersManager(self)
|
|
292
292
|
|
|
293
293
|
# TODO(michael) Keeping this private for now until we decide what else should be in it
|
|
@@ -372,13 +372,12 @@ class _Environment(_Object, type_prefix="en"):
|
|
|
372
372
|
|
|
373
373
|
@property
|
|
374
374
|
def billing(self) -> "_EnvironmentBillingManager":
|
|
375
|
+
"""Namespace for Environment billing APIs."""
|
|
375
376
|
return _EnvironmentBillingManager(self)
|
|
376
377
|
|
|
377
378
|
|
|
378
379
|
class _EnvironmentBillingManager:
|
|
379
|
-
"""mdmd:namespace
|
|
380
|
-
Namespace for Environment billing APIs
|
|
381
|
-
"""
|
|
380
|
+
"""mdmd:namespace"""
|
|
382
381
|
|
|
383
382
|
def __init__(self, environment: _Environment):
|
|
384
383
|
"""mdmd:ignore"""
|
|
@@ -2073,11 +2073,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
2073
2073
|
|
|
2074
2074
|
@live_method
|
|
2075
2075
|
async def get_call_graph(self) -> list[InputInfo]:
|
|
2076
|
-
"""
|
|
2077
|
-
call ID, along with the status of execution for each node.
|
|
2076
|
+
"""Fetch information about the graph of Inputs this FunctionCall is part of.
|
|
2078
2077
|
|
|
2079
|
-
|
|
2080
|
-
|
|
2078
|
+
Note: the call graph data is not populated in real-time, and its capture is best-effort.
|
|
2079
|
+
We do not recommend relying on this method for critical use cases.
|
|
2080
|
+
|
|
2081
|
+
See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
|
|
2082
|
+
on the return values.
|
|
2081
2083
|
|
|
2082
2084
|
Returns:
|
|
2083
2085
|
A list of `InputInfo` nodes describing the call graph.
|
|
@@ -2088,19 +2090,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
2088
2090
|
return _reconstruct_call_graph(response)
|
|
2089
2091
|
|
|
2090
2092
|
@live_method
|
|
2091
|
-
async def cancel(
|
|
2092
|
-
|
|
2093
|
-
# if true, containers running the inputs are forcibly terminated
|
|
2094
|
-
terminate_containers: bool = False,
|
|
2095
|
-
):
|
|
2096
|
-
"""Cancels the function call, which will stop its execution and mark its inputs as
|
|
2097
|
-
[`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
|
|
2098
|
-
|
|
2099
|
-
If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
|
|
2100
|
-
causing any non-cancelled inputs on those containers to be rescheduled in new containers.
|
|
2093
|
+
async def cancel(self, terminate_containers: bool = False):
|
|
2094
|
+
"""Cancel the FunctionCall and terminate its inputs without retrying.
|
|
2101
2095
|
|
|
2102
2096
|
Args:
|
|
2103
|
-
terminate_containers: If True,
|
|
2097
|
+
terminate_containers: If True, terminate the containers running the cancelled
|
|
2098
|
+
inputs. Any other inputs running concurrently on those containers will be
|
|
2099
|
+
rescheduled.
|
|
2104
2100
|
"""
|
|
2105
2101
|
request = api_pb2.FunctionCallCancelRequest(
|
|
2106
2102
|
function_call_id=self.object_id, terminate_containers=terminate_containers
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright Modal Labs 2025
|
|
2
2
|
import asyncio
|
|
3
3
|
import base64
|
|
4
|
+
import io
|
|
4
5
|
import json
|
|
5
6
|
import socket
|
|
6
7
|
import ssl
|
|
@@ -8,16 +9,16 @@ import time
|
|
|
8
9
|
import typing
|
|
9
10
|
import urllib.parse
|
|
10
11
|
import weakref
|
|
11
|
-
from collections.abc import AsyncGenerator, Callable
|
|
12
|
+
from collections.abc import AsyncGenerator, AsyncIterable, Callable
|
|
12
13
|
from contextlib import suppress
|
|
13
|
-
from typing import TypeVar
|
|
14
|
+
from typing import BinaryIO, TypeVar
|
|
14
15
|
|
|
15
16
|
import grpclib.client
|
|
16
17
|
from grpclib import GRPCError, Status
|
|
17
18
|
from grpclib.exceptions import StreamTerminatedError
|
|
18
19
|
|
|
19
20
|
from modal.config import logger
|
|
20
|
-
from modal.exception import ExecTimeoutError, TimeoutError as ModalTimeoutError
|
|
21
|
+
from modal.exception import AuthError, ExecTimeoutError, InternalError, ServiceError, TimeoutError as ModalTimeoutError
|
|
21
22
|
from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
|
|
22
23
|
from modal_proto.task_command_router_grpc import TaskCommandRouterStub
|
|
23
24
|
|
|
@@ -26,6 +27,17 @@ from .._utils.grpc_utils import ModalChannel, create_channel_config
|
|
|
26
27
|
from .async_utils import aclosing, retry
|
|
27
28
|
from .grpc_utils import RETRYABLE_GRPC_STATUS_CODES
|
|
28
29
|
|
|
30
|
+
STREAMING_STDIN_CHUNK_SIZE = 256 * 1024
|
|
31
|
+
|
|
32
|
+
_STREAMING_STDIN_RESUMABLE_EXCEPTIONS = (
|
|
33
|
+
AuthError,
|
|
34
|
+
InternalError,
|
|
35
|
+
ServiceError,
|
|
36
|
+
StreamTerminatedError,
|
|
37
|
+
OSError,
|
|
38
|
+
asyncio.TimeoutError,
|
|
39
|
+
)
|
|
40
|
+
|
|
29
41
|
|
|
30
42
|
@retry(n_attempts=34, base_delay=1, max_delay=10, attempt_timeout=10, total_timeout=310)
|
|
31
43
|
async def _connect_channel(channel: grpclib.client.Channel):
|
|
@@ -468,6 +480,118 @@ class TaskCommandRouterClient:
|
|
|
468
480
|
lambda: self._call_with_auth_retry(self._stub.TaskExecStdinWrite, request)
|
|
469
481
|
)
|
|
470
482
|
|
|
483
|
+
async def exec_stdin_write_stream(
|
|
484
|
+
self,
|
|
485
|
+
task_id: str,
|
|
486
|
+
exec_id: str,
|
|
487
|
+
source: BinaryIO,
|
|
488
|
+
chunk_size: int = STREAMING_STDIN_CHUNK_SIZE,
|
|
489
|
+
max_resume_attempts: int = 9,
|
|
490
|
+
) -> int:
|
|
491
|
+
"""Stream `source` into the exec's stdin, with bounded resume on transient failures.
|
|
492
|
+
|
|
493
|
+
Streams the full contents of `source` in one client-streaming RPC and
|
|
494
|
+
closes stdin (EOF) on success. On a resumable error, queries
|
|
495
|
+
`exec_stdin_status` for the server's canonical offset, seeks `source`
|
|
496
|
+
to that point, and reopens the stream.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
task_id: The task ID of the task running the exec'd command.
|
|
500
|
+
exec_id: The execution ID of the command to write to.
|
|
501
|
+
source: A seekable byte source (file, BytesIO, etc.).
|
|
502
|
+
chunk_size: Bytes per outbound message.
|
|
503
|
+
max_resume_attempts: Upper bound on resume retries before giving up.
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
The total number of bytes streamed into the exec's stdin.
|
|
507
|
+
"""
|
|
508
|
+
offset = 0
|
|
509
|
+
attempt = 0
|
|
510
|
+
while True:
|
|
511
|
+
|
|
512
|
+
async def _chunks():
|
|
513
|
+
while True:
|
|
514
|
+
chunk = source.read(chunk_size)
|
|
515
|
+
if not chunk:
|
|
516
|
+
return
|
|
517
|
+
yield chunk
|
|
518
|
+
|
|
519
|
+
try:
|
|
520
|
+
source.seek(offset)
|
|
521
|
+
await self._exec_stdin_write_stream(task_id, exec_id, offset, _chunks())
|
|
522
|
+
return source.tell()
|
|
523
|
+
except (*_STREAMING_STDIN_RESUMABLE_EXCEPTIONS, AttributeError) as e:
|
|
524
|
+
# StreamTerminatedError surfaces as AttributeError in grpclib<=0.4.7;
|
|
525
|
+
# see call_with_retries_on_transient_errors.
|
|
526
|
+
if isinstance(e, AttributeError) and "_write_appdata" not in str(e):
|
|
527
|
+
raise
|
|
528
|
+
attempt += 1
|
|
529
|
+
if attempt > max_resume_attempts:
|
|
530
|
+
raise
|
|
531
|
+
status = await self.exec_stdin_status(task_id=task_id, exec_id=exec_id)
|
|
532
|
+
if status.closed:
|
|
533
|
+
pos = source.tell()
|
|
534
|
+
if status.num_bytes_written == pos and source.seek(0, io.SEEK_END) == pos:
|
|
535
|
+
logger.debug(f"exec_stdin_write_stream completed but response was lost: {e}")
|
|
536
|
+
return pos
|
|
537
|
+
raise
|
|
538
|
+
offset = status.num_bytes_written
|
|
539
|
+
logger.debug(f"exec_stdin_write_stream resuming from offset {offset} after error: {e}")
|
|
540
|
+
|
|
541
|
+
async def _exec_stdin_write_stream(
|
|
542
|
+
self,
|
|
543
|
+
task_id: str,
|
|
544
|
+
exec_id: str,
|
|
545
|
+
start_offset: int,
|
|
546
|
+
chunks: AsyncIterable[bytes],
|
|
547
|
+
) -> sr_pb2.TaskExecStdinWriteStreamResponse | None:
|
|
548
|
+
"""Single client-streaming attempt: Start, Data chunks, then End (EOF).
|
|
549
|
+
|
|
550
|
+
Does not retry; `exec_stdin_write_stream` owns resume.
|
|
551
|
+
"""
|
|
552
|
+
with grpc_error_converter():
|
|
553
|
+
stream = self._stub.TaskExecStdinWriteStream.open(metadata=self._get_metadata())
|
|
554
|
+
async with stream as s:
|
|
555
|
+
start = sr_pb2.TaskExecStdinWriteStreamRequest(
|
|
556
|
+
start=sr_pb2.TaskExecStdinWriteStreamStart(
|
|
557
|
+
task_id=task_id,
|
|
558
|
+
exec_id=exec_id,
|
|
559
|
+
offset=start_offset,
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
await s.send_message(start)
|
|
563
|
+
async for data in chunks:
|
|
564
|
+
if not data:
|
|
565
|
+
continue
|
|
566
|
+
await s.send_message(sr_pb2.TaskExecStdinWriteStreamRequest(data=data))
|
|
567
|
+
# The server closes stdin only on this explicit End message. A
|
|
568
|
+
# stream that breaks before it leaves stdin open for resume.
|
|
569
|
+
await s.send_message(
|
|
570
|
+
sr_pb2.TaskExecStdinWriteStreamRequest(end=sr_pb2.TaskExecStdinWriteStreamEnd()),
|
|
571
|
+
end=True,
|
|
572
|
+
)
|
|
573
|
+
return await s.recv_message()
|
|
574
|
+
|
|
575
|
+
async def exec_stdin_status(self, task_id: str, exec_id: str) -> sr_pb2.TaskExecStdinStatusResponse:
|
|
576
|
+
"""Read the current stdin write status for an exec'd command, to support retries from the right offset.
|
|
577
|
+
|
|
578
|
+
Used by streaming clients to find the resume offset after a stream
|
|
579
|
+
failure. Evicts any in-flight stdin stream for the exec, so it is not
|
|
580
|
+
safe to call for read-only observability.
|
|
581
|
+
|
|
582
|
+
Args:
|
|
583
|
+
task_id: The task ID of the task running the exec'd command.
|
|
584
|
+
exec_id: The execution ID of the command to query.
|
|
585
|
+
Raises:
|
|
586
|
+
Other errors: If retries are exhausted on transient errors or if there's an error
|
|
587
|
+
from the RPC itself.
|
|
588
|
+
"""
|
|
589
|
+
request = sr_pb2.TaskExecStdinStatusRequest(task_id=task_id, exec_id=exec_id)
|
|
590
|
+
with grpc_error_converter():
|
|
591
|
+
return await call_with_retries_on_transient_errors(
|
|
592
|
+
lambda: self._call_with_auth_retry(self._stub.TaskExecStdinStatus, request)
|
|
593
|
+
)
|
|
594
|
+
|
|
471
595
|
async def sandbox_stdin_write_v2(
|
|
472
596
|
self, task_id: str, offset: int, data: bytes, eof: bool
|
|
473
597
|
) -> sr_pb2.SandboxStdinWriteV2Response:
|
|
@@ -35,9 +35,7 @@ def _member_role_from_proto(proto_value: int) -> str:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class _WorkspaceMembersManager:
|
|
38
|
-
"""mdmd:namespace
|
|
39
|
-
Namespace with methods for managing the membership of a Workspace.
|
|
40
|
-
"""
|
|
38
|
+
"""mdmd:namespace"""
|
|
41
39
|
|
|
42
40
|
def __init__(self, workspace: "_Workspace"):
|
|
43
41
|
"""mdmd:hidden"""
|
|
@@ -81,6 +79,7 @@ class _Workspace(_Object, type_prefix="ac"):
|
|
|
81
79
|
|
|
82
80
|
@property
|
|
83
81
|
def members(self) -> "_WorkspaceMembersManager":
|
|
82
|
+
"""Namespace with methods for managing the membership of a Workspace."""
|
|
84
83
|
return _WorkspaceMembersManager(self)
|
|
85
84
|
|
|
86
85
|
@staticmethod
|
|
@@ -110,23 +109,25 @@ class _Workspace(_Object, type_prefix="ac"):
|
|
|
110
109
|
|
|
111
110
|
@property
|
|
112
111
|
def billing(self) -> "_WorkspaceBillingManager":
|
|
112
|
+
"""Namespace for Workspace billing APIs."""
|
|
113
113
|
return _WorkspaceBillingManager(self)
|
|
114
114
|
|
|
115
115
|
@property
|
|
116
116
|
def proxy_tokens(self) -> "_WorkspaceProxyTokenManager":
|
|
117
|
+
"""Namespace with methods for managing the proxy tokens in a Workspace.
|
|
118
|
+
|
|
119
|
+
See [the guide](https://modal.com/docs/guide/webhook-proxy-auth) for more information on proxy tokens.
|
|
120
|
+
"""
|
|
117
121
|
return _WorkspaceProxyTokenManager(self)
|
|
118
122
|
|
|
119
123
|
@property
|
|
120
124
|
def settings(self) -> "_WorkspaceSettingsManager":
|
|
125
|
+
"""Namespace for Workspace settings APIs."""
|
|
121
126
|
return _WorkspaceSettingsManager(self)
|
|
122
127
|
|
|
123
128
|
|
|
124
129
|
class _WorkspaceProxyTokenManager:
|
|
125
|
-
"""mdmd:namespace
|
|
126
|
-
Namespace with methods for managing the proxy tokens in a Workspace.
|
|
127
|
-
|
|
128
|
-
See [the guide](https://modal.com/docs/guide/webhook-proxy-auth) for more information on proxy tokens.
|
|
129
|
-
"""
|
|
130
|
+
"""mdmd:namespace"""
|
|
130
131
|
|
|
131
132
|
def __init__(self, workspace: "_Workspace"):
|
|
132
133
|
"""mdmd:hidden"""
|
|
@@ -245,9 +246,7 @@ class _WorkspaceProxyTokenManager:
|
|
|
245
246
|
|
|
246
247
|
|
|
247
248
|
class _WorkspaceBillingManager:
|
|
248
|
-
"""mdmd:namespace
|
|
249
|
-
Namespace for Workspace billing APIs.
|
|
250
|
-
"""
|
|
249
|
+
"""mdmd:namespace"""
|
|
251
250
|
|
|
252
251
|
def __init__(self, workspace: _Workspace):
|
|
253
252
|
"""mdmd:hidden"""
|
|
@@ -321,9 +320,7 @@ class _WorkspaceBillingManager:
|
|
|
321
320
|
|
|
322
321
|
|
|
323
322
|
class _WorkspaceSettingsManager:
|
|
324
|
-
"""mdmd:namespace
|
|
325
|
-
Namespace for Workspace settings APIs.
|
|
326
|
-
"""
|
|
323
|
+
"""mdmd:namespace"""
|
|
327
324
|
|
|
328
325
|
@classmethod
|
|
329
326
|
def valid_settings(cls):
|
|
@@ -236,6 +236,7 @@ def _start_shell_from_image(
|
|
|
236
236
|
region: str | None,
|
|
237
237
|
pty: bool,
|
|
238
238
|
experimental_options: dict[str, str],
|
|
239
|
+
v2: bool,
|
|
239
240
|
) -> None:
|
|
240
241
|
volumes = {f"/mnt/{vol}": Volume.from_name(vol) for vol in volume}
|
|
241
242
|
secrets = [Secret.from_name(s) for s in secret]
|
|
@@ -267,6 +268,7 @@ def _start_shell_from_image(
|
|
|
267
268
|
region=region.split(",") if region else [],
|
|
268
269
|
pty=pty,
|
|
269
270
|
experimental_options=experimental_options,
|
|
271
|
+
v2=v2,
|
|
270
272
|
)
|
|
271
273
|
|
|
272
274
|
|
|
@@ -327,6 +329,14 @@ def _start_shell_from_image(
|
|
|
327
329
|
hidden=True,
|
|
328
330
|
metavar="KEY=VALUE",
|
|
329
331
|
)
|
|
332
|
+
@click.option(
|
|
333
|
+
"--experimental-v2",
|
|
334
|
+
"v2",
|
|
335
|
+
is_flag=True,
|
|
336
|
+
default=False,
|
|
337
|
+
hidden=True,
|
|
338
|
+
help="Create the shell using the V2 sandbox backend.",
|
|
339
|
+
)
|
|
330
340
|
def shell(
|
|
331
341
|
ref: str | None = None,
|
|
332
342
|
cmd: str = "/bin/bash",
|
|
@@ -344,6 +354,7 @@ def shell(
|
|
|
344
354
|
pty: bool | None = None,
|
|
345
355
|
use_module_mode: bool = False,
|
|
346
356
|
experimental_options: tuple[str, ...] = (),
|
|
357
|
+
v2: bool = False,
|
|
347
358
|
):
|
|
348
359
|
"""Run a command or interactive shell inside a Modal container.
|
|
349
360
|
|
|
@@ -423,13 +434,20 @@ def shell(
|
|
|
423
434
|
shell.params,
|
|
424
435
|
ctx,
|
|
425
436
|
locals(),
|
|
426
|
-
allowed=lambda p: p in {"cmd", "env", "pty", "ref", "use_module_mode", "experimental_options"},
|
|
437
|
+
allowed=lambda p: p in {"cmd", "env", "pty", "ref", "use_module_mode", "experimental_options", "v2"},
|
|
427
438
|
):
|
|
428
439
|
raise ClickException(
|
|
429
440
|
f"Cannot specify container configuration arguments ({', '.join(passed_forbidden)}) "
|
|
430
441
|
f"when starting a new container from a function reference ('{ref}')."
|
|
431
442
|
)
|
|
432
443
|
|
|
444
|
+
if v2:
|
|
445
|
+
# Function specs always carry mounts (e.g. the client mount), which the
|
|
446
|
+
# V2 backend does not support.
|
|
447
|
+
raise ClickException(
|
|
448
|
+
"--experimental-v2 is not supported when starting a shell from a function reference. "
|
|
449
|
+
"Use an image instead (e.g. `modal shell --experimental-v2 --image ...`)."
|
|
450
|
+
)
|
|
433
451
|
function_spec = _function_spec_from_ref(ref, use_module_mode)
|
|
434
452
|
_start_shell_from_function_spec(app, cmds, env, timeout, function_spec, pty, parsed_experimental_options)
|
|
435
453
|
return
|
|
@@ -463,4 +481,5 @@ def shell(
|
|
|
463
481
|
region,
|
|
464
482
|
pty,
|
|
465
483
|
parsed_experimental_options,
|
|
484
|
+
v2,
|
|
466
485
|
)
|
|
@@ -37,7 +37,7 @@ class _Client:
|
|
|
37
37
|
server_url: str,
|
|
38
38
|
client_type: int,
|
|
39
39
|
credentials: typing.Optional[tuple[str, str]],
|
|
40
|
-
version: str = "1.5.3.
|
|
40
|
+
version: str = "1.5.3.dev7",
|
|
41
41
|
):
|
|
42
42
|
"""mdmd:hidden
|
|
43
43
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -207,7 +207,7 @@ class Client:
|
|
|
207
207
|
server_url: str,
|
|
208
208
|
client_type: int,
|
|
209
209
|
credentials: typing.Optional[tuple[str, str]],
|
|
210
|
-
version: str = "1.5.3.
|
|
210
|
+
version: str = "1.5.3.dev7",
|
|
211
211
|
):
|
|
212
212
|
"""mdmd:hidden
|
|
213
213
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Copyright Modal Labs 2024
|
|
2
2
|
import asyncio
|
|
3
3
|
import platform
|
|
4
|
-
from typing import Generic, TypeVar
|
|
4
|
+
from typing import BinaryIO, Generic, TypeVar
|
|
5
5
|
|
|
6
6
|
from modal_proto import api_pb2
|
|
7
7
|
|
|
@@ -117,6 +117,18 @@ class _ContainerProcess(Generic[T]):
|
|
|
117
117
|
)
|
|
118
118
|
return self._returncode
|
|
119
119
|
|
|
120
|
+
async def _stdin_write_stream(self, source: BinaryIO) -> int:
|
|
121
|
+
"""mdmd:hidden
|
|
122
|
+
Stream `source` into the process's stdin and close it (EOF) on success.
|
|
123
|
+
|
|
124
|
+
Returns the total bytes streamed.
|
|
125
|
+
"""
|
|
126
|
+
return await self._command_router_client.exec_stdin_write_stream(
|
|
127
|
+
task_id=self._task_id,
|
|
128
|
+
exec_id=self._process_id,
|
|
129
|
+
source=source,
|
|
130
|
+
)
|
|
131
|
+
|
|
120
132
|
async def poll(self) -> int | None:
|
|
121
133
|
"""Check if the container process has finished running.
|
|
122
134
|
|
|
@@ -53,6 +53,14 @@ class _ContainerProcess(typing.Generic[T]):
|
|
|
53
53
|
|
|
54
54
|
@property
|
|
55
55
|
def returncode(self) -> int: ...
|
|
56
|
+
async def _stdin_write_stream(self, source: typing.BinaryIO) -> int:
|
|
57
|
+
"""mdmd:hidden
|
|
58
|
+
Stream `source` into the process's stdin and close it (EOF) on success.
|
|
59
|
+
|
|
60
|
+
Returns the total bytes streamed.
|
|
61
|
+
"""
|
|
62
|
+
...
|
|
63
|
+
|
|
56
64
|
async def poll(self) -> typing.Optional[int]:
|
|
57
65
|
"""Check if the container process has finished running.
|
|
58
66
|
|
|
@@ -105,6 +113,25 @@ class ContainerProcess(typing.Generic[T]):
|
|
|
105
113
|
@property
|
|
106
114
|
def returncode(self) -> int: ...
|
|
107
115
|
|
|
116
|
+
class ___stdin_write_stream_spec(typing_extensions.Protocol):
|
|
117
|
+
def __call__(self, /, source: typing.BinaryIO) -> int:
|
|
118
|
+
"""mdmd:hidden
|
|
119
|
+
Stream `source` into the process's stdin and close it (EOF) on success.
|
|
120
|
+
|
|
121
|
+
Returns the total bytes streamed.
|
|
122
|
+
"""
|
|
123
|
+
...
|
|
124
|
+
|
|
125
|
+
async def aio(self, /, source: typing.BinaryIO) -> int:
|
|
126
|
+
"""mdmd:hidden
|
|
127
|
+
Stream `source` into the process's stdin and close it (EOF) on success.
|
|
128
|
+
|
|
129
|
+
Returns the total bytes streamed.
|
|
130
|
+
"""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
_stdin_write_stream: ___stdin_write_stream_spec
|
|
134
|
+
|
|
108
135
|
class __poll_spec(typing_extensions.Protocol):
|
|
109
136
|
def __call__(self, /) -> typing.Optional[int]:
|
|
110
137
|
"""Check if the container process has finished running.
|
|
@@ -102,11 +102,7 @@ class EnvironmentManager:
|
|
|
102
102
|
delete: __delete_spec
|
|
103
103
|
|
|
104
104
|
class EnvironmentMembersManager:
|
|
105
|
-
"""mdmd:namespace
|
|
106
|
-
Namespace with methods for managing the membership of a restricted Environment.
|
|
107
|
-
|
|
108
|
-
See https://modal.com/docs/guide/rbac for more information on restricted Environments.
|
|
109
|
-
"""
|
|
105
|
+
"""mdmd:namespace"""
|
|
110
106
|
def __init__(self, environment: Environment):
|
|
111
107
|
"""mdmd:hidden"""
|
|
112
108
|
...
|
|
@@ -254,9 +250,7 @@ class EnvironmentMembersManager:
|
|
|
254
250
|
_dispatch_role_updates: ___dispatch_role_updates_spec
|
|
255
251
|
|
|
256
252
|
class EnvironmentBillingManager:
|
|
257
|
-
"""mdmd:namespace
|
|
258
|
-
Namespace for Environment billing APIs
|
|
259
|
-
"""
|
|
253
|
+
"""mdmd:namespace"""
|
|
260
254
|
def __init__(self, environment: Environment):
|
|
261
255
|
"""mdmd:ignore"""
|
|
262
256
|
...
|
|
@@ -352,7 +346,13 @@ class Environment(modal.object.Object):
|
|
|
352
346
|
@classmethod
|
|
353
347
|
def objects(cls) -> EnvironmentManager: ...
|
|
354
348
|
@property
|
|
355
|
-
def members(self) -> EnvironmentMembersManager:
|
|
349
|
+
def members(self) -> EnvironmentMembersManager:
|
|
350
|
+
"""Namespace with methods for managing the membership of a restricted Environment.
|
|
351
|
+
|
|
352
|
+
See https://modal.com/docs/guide/rbac for more information on restricted Environments.
|
|
353
|
+
"""
|
|
354
|
+
...
|
|
355
|
+
|
|
356
356
|
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
|
357
357
|
@staticmethod
|
|
358
358
|
def _get_or_create(
|
|
@@ -378,7 +378,9 @@ class Environment(modal.object.Object):
|
|
|
378
378
|
...
|
|
379
379
|
|
|
380
380
|
@property
|
|
381
|
-
def billing(self) -> EnvironmentBillingManager:
|
|
381
|
+
def billing(self) -> EnvironmentBillingManager:
|
|
382
|
+
"""Namespace for Environment billing APIs."""
|
|
383
|
+
...
|
|
382
384
|
|
|
383
385
|
class __create_environment_spec(typing_extensions.Protocol):
|
|
384
386
|
def __call__(self, /, name: str, client: typing.Optional[modal.client.Client] = None): ...
|
|
@@ -1044,11 +1044,13 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
1044
1044
|
|
|
1045
1045
|
class __get_call_graph_spec(typing_extensions.Protocol):
|
|
1046
1046
|
def __call__(self, /) -> list[modal.types.InputInfo]:
|
|
1047
|
-
"""
|
|
1048
|
-
call ID, along with the status of execution for each node.
|
|
1047
|
+
"""Fetch information about the graph of Inputs this FunctionCall is part of.
|
|
1049
1048
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1049
|
+
Note: the call graph data is not populated in real-time, and its capture is best-effort.
|
|
1050
|
+
We do not recommend relying on this method for critical use cases.
|
|
1051
|
+
|
|
1052
|
+
See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
|
|
1053
|
+
on the return values.
|
|
1052
1054
|
|
|
1053
1055
|
Returns:
|
|
1054
1056
|
A list of `InputInfo` nodes describing the call graph.
|
|
@@ -1056,11 +1058,13 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
1056
1058
|
...
|
|
1057
1059
|
|
|
1058
1060
|
async def aio(self, /) -> list[modal.types.InputInfo]:
|
|
1059
|
-
"""
|
|
1060
|
-
|
|
1061
|
+
"""Fetch information about the graph of Inputs this FunctionCall is part of.
|
|
1062
|
+
|
|
1063
|
+
Note: the call graph data is not populated in real-time, and its capture is best-effort.
|
|
1064
|
+
We do not recommend relying on this method for critical use cases.
|
|
1061
1065
|
|
|
1062
|
-
See [`modal.
|
|
1063
|
-
|
|
1066
|
+
See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
|
|
1067
|
+
on the return values.
|
|
1064
1068
|
|
|
1065
1069
|
Returns:
|
|
1066
1070
|
A list of `InputInfo` nodes describing the call graph.
|
|
@@ -1071,26 +1075,22 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
1071
1075
|
|
|
1072
1076
|
class __cancel_spec(typing_extensions.Protocol):
|
|
1073
1077
|
def __call__(self, /, terminate_containers: bool = False):
|
|
1074
|
-
"""
|
|
1075
|
-
[`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
|
|
1076
|
-
|
|
1077
|
-
If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
|
|
1078
|
-
causing any non-cancelled inputs on those containers to be rescheduled in new containers.
|
|
1078
|
+
"""Cancel the FunctionCall and terminate its inputs without retrying.
|
|
1079
1079
|
|
|
1080
1080
|
Args:
|
|
1081
|
-
terminate_containers: If True,
|
|
1081
|
+
terminate_containers: If True, terminate the containers running the cancelled
|
|
1082
|
+
inputs. Any other inputs running concurrently on those containers will be
|
|
1083
|
+
rescheduled.
|
|
1082
1084
|
"""
|
|
1083
1085
|
...
|
|
1084
1086
|
|
|
1085
1087
|
async def aio(self, /, terminate_containers: bool = False):
|
|
1086
|
-
"""
|
|
1087
|
-
[`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
|
|
1088
|
-
|
|
1089
|
-
If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
|
|
1090
|
-
causing any non-cancelled inputs on those containers to be rescheduled in new containers.
|
|
1088
|
+
"""Cancel the FunctionCall and terminate its inputs without retrying.
|
|
1091
1089
|
|
|
1092
1090
|
Args:
|
|
1093
|
-
terminate_containers: If True,
|
|
1091
|
+
terminate_containers: If True, terminate the containers running the cancelled
|
|
1092
|
+
inputs. Any other inputs running concurrently on those containers will be
|
|
1093
|
+
rescheduled.
|
|
1094
1094
|
"""
|
|
1095
1095
|
...
|
|
1096
1096
|
|
|
@@ -690,7 +690,12 @@ async def _deploy_app(
|
|
|
690
690
|
|
|
691
691
|
|
|
692
692
|
async def _interactive_shell(
|
|
693
|
-
_app: "modal.app._App",
|
|
693
|
+
_app: "modal.app._App",
|
|
694
|
+
cmds: list[str],
|
|
695
|
+
environment_name: str = "",
|
|
696
|
+
pty: bool = True,
|
|
697
|
+
v2: bool = False,
|
|
698
|
+
**kwargs: Any,
|
|
694
699
|
) -> None:
|
|
695
700
|
"""Run an interactive shell (like `bash`) within the image for this app.
|
|
696
701
|
|
|
@@ -732,13 +737,25 @@ async def _interactive_shell(
|
|
|
732
737
|
|
|
733
738
|
# Temporarily enable output to show image build logs during sandbox creation
|
|
734
739
|
output_mgr.set_quiet_mode(False)
|
|
735
|
-
|
|
736
|
-
"
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
740
|
+
if v2:
|
|
741
|
+
for option in ("gpu", "mounts", "network_file_systems"):
|
|
742
|
+
if kwargs.pop(option, None):
|
|
743
|
+
raise InvalidError(f"`{option}` is not supported for V2 sandboxes")
|
|
744
|
+
sandbox = await _Sandbox._experimental_create(
|
|
745
|
+
"sleep",
|
|
746
|
+
"100000",
|
|
747
|
+
app=_app,
|
|
748
|
+
secrets=secrets,
|
|
749
|
+
**kwargs,
|
|
750
|
+
)
|
|
751
|
+
else:
|
|
752
|
+
sandbox = await _Sandbox._create(
|
|
753
|
+
"sleep",
|
|
754
|
+
"100000",
|
|
755
|
+
app=_app,
|
|
756
|
+
secrets=secrets,
|
|
757
|
+
**kwargs,
|
|
758
|
+
)
|
|
742
759
|
|
|
743
760
|
# Re-enable quiet mode before starting the interactive session
|
|
744
761
|
output_mgr.set_quiet_mode(True)
|
|
@@ -757,7 +774,12 @@ async def _interactive_shell(
|
|
|
757
774
|
except InteractiveTimeoutError:
|
|
758
775
|
# Check on status of Sandbox. It may have crashed, causing connection failure.
|
|
759
776
|
req = api_pb2.SandboxWaitRequest(sandbox_id=sandbox._object_id, timeout=0)
|
|
760
|
-
|
|
777
|
+
if v2:
|
|
778
|
+
assert sandbox._client._auth_token_manager
|
|
779
|
+
auth_token = await sandbox._client._auth_token_manager.get_token()
|
|
780
|
+
resp = await sandbox._client.stub.SandboxWaitV2(req, metadata=[("x-modal-auth-token", auth_token)])
|
|
781
|
+
else:
|
|
782
|
+
resp = await sandbox._client.stub.SandboxWait(req)
|
|
761
783
|
if resp.result.exception:
|
|
762
784
|
raise RemoteError(resp.result.exception)
|
|
763
785
|
else:
|