modal 1.5.2.dev20__tar.gz → 1.5.2.dev21__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.2.dev20 → modal-1.5.2.dev21}/PKG-INFO +1 -1
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/container.py +10 -2
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/curl.py +6 -4
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/shell.py +6 -2
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/client.pyi +2 -2
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox.py +47 -5
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox.pyi +114 -15
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/types.py +7 -1
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/volume.py +26 -1
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/volume.pyi +26 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/mdmd.py +34 -11
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_grpc.py +32 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2.py +800 -767
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2.pyi +69 -4
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2_grpc.py +66 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2_grpc.pyi +20 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/modal_api_grpc.py +2 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_version/__init__.py +1 -1
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/LICENSE +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/README.md +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/__main__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_billing.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_clustered_functions.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_environments.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_function_variants.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_functions.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_grpc_client.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_image.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_ipython.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_load_context.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_location.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_logs.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_object.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/manager.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/pty.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/rich.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/status.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_partial_function.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_resolver.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_resources.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_serialization.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_server.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_traceback.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_tunnel.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_tunnel.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_type_manager.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/logger.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/version.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_watcher.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_workspace.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/app.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/app.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/billing.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/billing.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/README.md +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/base-images.json +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/call_graph.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_download.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_help.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_traceback.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/app.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/billing.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/changelog.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/cluster.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/config.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/dashboard.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/dict.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/endpoint.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/entry_point.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/environment.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/image.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/import_refs.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/launch.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/logo.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/profile.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/queues.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/run.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/secret.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/selector.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/skills.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/token.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/utils.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/volume.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/workspace.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/client.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cls.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cls.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/config.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/container_process.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/container_process.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/dict.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/dict.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/environments.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/environments.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/exception.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/flash.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/ipython.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_io.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_io.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/functions.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/functions.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/image.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/image.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/io_streams.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/io_streams.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/mount.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/mount.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/network_file_system.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/network_file_system.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/object.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/object.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/output.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/parallel_map.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/parallel_map.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/partial_function.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/partial_function.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/proxy.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/proxy.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/py.typed +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/queue.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/queue.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/retries.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/runner.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/runner.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/running_app.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox_fs.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/schedule.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/scheduler_placement.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/secret.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/secret.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/server.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/server.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/serving.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/serving.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/snapshot.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/snapshot.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/stream_type.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/token_flow.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/token_flow.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/workspace.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/workspace.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/__init__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/py.typed +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_version/__main__.py +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/pyproject.toml +0 -0
- {modal-1.5.2.dev20 → modal-1.5.2.dev21}/setup.cfg +0 -0
|
@@ -269,15 +269,23 @@ async def _exec_impl(
|
|
|
269
269
|
pty: bool | None = None,
|
|
270
270
|
container_id: str = "",
|
|
271
271
|
command: tuple[str, ...] = (),
|
|
272
|
+
object_id_for_v2: str | None = None,
|
|
272
273
|
):
|
|
273
|
-
"""Execute a command in a container (implementation).
|
|
274
|
+
"""Execute a command in a container (implementation).
|
|
275
|
+
|
|
276
|
+
For tasks belonging to V2 sandboxes, `object_id_for_v2` must be set to the
|
|
277
|
+
sandbox ID.
|
|
278
|
+
"""
|
|
274
279
|
|
|
275
280
|
if pty is None:
|
|
276
281
|
pty = is_tty()
|
|
277
282
|
|
|
278
283
|
client = await _Client.from_env()
|
|
279
284
|
|
|
280
|
-
|
|
285
|
+
if object_id_for_v2 is not None:
|
|
286
|
+
command_router_client = await TaskCommandRouterClient.init_v2(client, object_id_for_v2, container_id)
|
|
287
|
+
else:
|
|
288
|
+
command_router_client = await TaskCommandRouterClient.init(client, container_id)
|
|
281
289
|
|
|
282
290
|
process_id = str(uuid.uuid4())
|
|
283
291
|
|
|
@@ -117,10 +117,12 @@ async def curl(curl_args: tuple[str, ...]):
|
|
|
117
117
|
All arguments are passed directly to `curl`, which must be installed locally.
|
|
118
118
|
|
|
119
119
|
Examples:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
modal curl https://user--my-app.us-west.modal.direct
|
|
123
|
+
modal curl -X GET https://user--my-app.us-west.modal.direct
|
|
124
|
+
```
|
|
125
|
+
|
|
124
126
|
"""
|
|
125
127
|
url = find_url(curl_args)
|
|
126
128
|
if url is None:
|
|
@@ -17,7 +17,7 @@ from ..functions import Function
|
|
|
17
17
|
from ..image import Image
|
|
18
18
|
from ..mount import _Mount
|
|
19
19
|
from ..runner import interactive_shell
|
|
20
|
-
from ..sandbox import _MAIN_CONTAINER_NAME, Sandbox
|
|
20
|
+
from ..sandbox import _MAIN_CONTAINER_NAME, Sandbox, _is_v2_sandbox_id
|
|
21
21
|
from ..secret import Secret
|
|
22
22
|
from ..stream_type import StreamType
|
|
23
23
|
from ..volume import Volume
|
|
@@ -135,6 +135,8 @@ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
|
|
|
135
135
|
if container_name is not None and container_name != _MAIN_CONTAINER_NAME:
|
|
136
136
|
return _start_shell_in_sidecar_container(sandbox_id, container_name, cmd, pty)
|
|
137
137
|
|
|
138
|
+
object_id_for_v2: str | None = None
|
|
139
|
+
|
|
138
140
|
if _is_valid_modal_id(sandbox_id, "sb-"):
|
|
139
141
|
try:
|
|
140
142
|
sandbox = Sandbox.from_id(sandbox_id)
|
|
@@ -143,10 +145,12 @@ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
|
|
|
143
145
|
raise ClickException(f"Sandbox '{sandbox_id}' not found (is it still running?)")
|
|
144
146
|
except Exception as e:
|
|
145
147
|
raise ClickException(f"Error connecting to Sandbox '{sandbox_id}': {str(e)}")
|
|
148
|
+
if _is_v2_sandbox_id(sandbox_id):
|
|
149
|
+
object_id_for_v2 = sandbox_id
|
|
146
150
|
|
|
147
151
|
assert _is_valid_modal_id(ref, "ta-")
|
|
148
152
|
try:
|
|
149
|
-
_exec_impl(container_id=ref, command=shlex.split(cmd), pty=pty)
|
|
153
|
+
_exec_impl(container_id=ref, command=shlex.split(cmd), pty=pty, object_id_for_v2=object_id_for_v2)
|
|
150
154
|
except NotFoundError:
|
|
151
155
|
raise ClickException(f"Container '{ref}' not found (is it still running?)")
|
|
152
156
|
except Exception as e:
|
|
@@ -35,7 +35,7 @@ class _Client:
|
|
|
35
35
|
server_url: str,
|
|
36
36
|
client_type: int,
|
|
37
37
|
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.5.2.
|
|
38
|
+
version: str = "1.5.2.dev21",
|
|
39
39
|
):
|
|
40
40
|
"""mdmd:hidden
|
|
41
41
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -205,7 +205,7 @@ class Client:
|
|
|
205
205
|
server_url: str,
|
|
206
206
|
client_type: int,
|
|
207
207
|
credentials: typing.Optional[tuple[str, str]],
|
|
208
|
-
version: str = "1.5.2.
|
|
208
|
+
version: str = "1.5.2.dev21",
|
|
209
209
|
):
|
|
210
210
|
"""mdmd:hidden
|
|
211
211
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -861,9 +861,10 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
861
861
|
(e.g. `region="us-east-1"`).
|
|
862
862
|
|
|
863
863
|
V2 sandboxes created with this method are not currently returned by
|
|
864
|
-
`Sandbox.list()
|
|
865
|
-
|
|
866
|
-
use `Sandbox.from_id(sandbox.object_id)` to
|
|
864
|
+
`Sandbox.list()`. A named sandbox can be looked up with
|
|
865
|
+
`Sandbox._experimental_from_name(app_name, name)`; otherwise store
|
|
866
|
+
`sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
|
|
867
|
+
reattach.
|
|
867
868
|
"""
|
|
868
869
|
from .app import _App
|
|
869
870
|
|
|
@@ -1186,6 +1187,44 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1186
1187
|
resp = await client.stub.SandboxGetFromName(req)
|
|
1187
1188
|
return _Sandbox._new_hydrated(resp.sandbox_id, client, None)
|
|
1188
1189
|
|
|
1190
|
+
@staticmethod
|
|
1191
|
+
async def _experimental_from_name(
|
|
1192
|
+
app_name: str,
|
|
1193
|
+
name: str,
|
|
1194
|
+
*,
|
|
1195
|
+
environment_name: str | None = None,
|
|
1196
|
+
client: _Client | None = None,
|
|
1197
|
+
) -> "_Sandbox":
|
|
1198
|
+
"""Get a running V2 Sandbox by name from a deployed App.
|
|
1199
|
+
|
|
1200
|
+
This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
|
|
1201
|
+
|
|
1202
|
+
Args:
|
|
1203
|
+
app_name: Name of the deployed app to look up the sandbox under.
|
|
1204
|
+
name: Sandbox name to resolve.
|
|
1205
|
+
environment_name: Optional environment name for the lookup; defaults to the configured environment.
|
|
1206
|
+
client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
|
|
1207
|
+
|
|
1208
|
+
Returns:
|
|
1209
|
+
A `Sandbox` handle for the running sandbox.
|
|
1210
|
+
|
|
1211
|
+
Raises:
|
|
1212
|
+
NotFoundError: If no running sandbox exists with the given name.
|
|
1213
|
+
"""
|
|
1214
|
+
if client is None:
|
|
1215
|
+
client = await _Client.from_env()
|
|
1216
|
+
env_name = _get_environment_name(environment_name)
|
|
1217
|
+
|
|
1218
|
+
req = api_pb2.SandboxGetFromNameRequest(sandbox_name=name, app_name=app_name, environment_name=env_name)
|
|
1219
|
+
assert client._auth_token_manager
|
|
1220
|
+
auth_token = await client._auth_token_manager.get_token()
|
|
1221
|
+
resp = await client.stub.SandboxGetFromNameV2(req, metadata=[("x-modal-auth-token", auth_token)])
|
|
1222
|
+
|
|
1223
|
+
obj = _Sandbox._new_hydrated(resp.sandbox_id, client, None)
|
|
1224
|
+
obj._is_v2 = True
|
|
1225
|
+
obj._hydrate_metadata_v2()
|
|
1226
|
+
return obj
|
|
1227
|
+
|
|
1189
1228
|
@staticmethod
|
|
1190
1229
|
async def from_id(sandbox_id: str, client: _Client | None = None) -> "_Sandbox":
|
|
1191
1230
|
"""Construct a Sandbox from an id and look up the Sandbox result.
|
|
@@ -2279,15 +2318,15 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
2279
2318
|
|
|
2280
2319
|
@staticmethod
|
|
2281
2320
|
async def _experimental_list(
|
|
2282
|
-
*, app_id: str | None = None, client: _Client | None = None
|
|
2321
|
+
*, app_id: str | None = None, tags: dict[str, str] | None = None, client: _Client | None = None
|
|
2283
2322
|
) -> AsyncGenerator["_Sandbox", None]:
|
|
2284
2323
|
"""List v2 Sandboxes in an App.
|
|
2285
2324
|
|
|
2286
2325
|
This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
|
|
2287
|
-
Filtering based on tags is not yet supported.
|
|
2288
2326
|
|
|
2289
2327
|
Args:
|
|
2290
2328
|
app_id: The App to list Sandboxes under.
|
|
2329
|
+
tags: If set, only sandboxes containing at least these tags are returned.
|
|
2291
2330
|
client: Optional client to use for the session.
|
|
2292
2331
|
|
|
2293
2332
|
Yields:
|
|
@@ -2304,12 +2343,15 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
2304
2343
|
if client is None:
|
|
2305
2344
|
client = await _Client.from_env()
|
|
2306
2345
|
|
|
2346
|
+
tags_list = [api_pb2.SandboxTag(tag_name=name, tag_value=value) for name, value in tags.items()] if tags else []
|
|
2347
|
+
|
|
2307
2348
|
assert client._auth_token_manager
|
|
2308
2349
|
while True:
|
|
2309
2350
|
req = api_pb2.SandboxListRequest(
|
|
2310
2351
|
app_id=app_id,
|
|
2311
2352
|
before_timestamp=before_timestamp,
|
|
2312
2353
|
include_finished=False,
|
|
2354
|
+
tags=tags_list,
|
|
2313
2355
|
)
|
|
2314
2356
|
|
|
2315
2357
|
# Fetches a batch of sandboxes. SandboxListV2 authenticates via the
|
|
@@ -409,9 +409,10 @@ class _Sandbox(modal._object._Object):
|
|
|
409
409
|
(e.g. `region="us-east-1"`).
|
|
410
410
|
|
|
411
411
|
V2 sandboxes created with this method are not currently returned by
|
|
412
|
-
`Sandbox.list()
|
|
413
|
-
|
|
414
|
-
use `Sandbox.from_id(sandbox.object_id)` to
|
|
412
|
+
`Sandbox.list()`. A named sandbox can be looked up with
|
|
413
|
+
`Sandbox._experimental_from_name(app_name, name)`; otherwise store
|
|
414
|
+
`sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
|
|
415
|
+
reattach.
|
|
415
416
|
"""
|
|
416
417
|
...
|
|
417
418
|
|
|
@@ -465,6 +466,32 @@ class _Sandbox(modal._object._Object):
|
|
|
465
466
|
"""
|
|
466
467
|
...
|
|
467
468
|
|
|
469
|
+
@staticmethod
|
|
470
|
+
async def _experimental_from_name(
|
|
471
|
+
app_name: str,
|
|
472
|
+
name: str,
|
|
473
|
+
*,
|
|
474
|
+
environment_name: typing.Optional[str] = None,
|
|
475
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
476
|
+
) -> _Sandbox:
|
|
477
|
+
"""Get a running V2 Sandbox by name from a deployed App.
|
|
478
|
+
|
|
479
|
+
This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
app_name: Name of the deployed app to look up the sandbox under.
|
|
483
|
+
name: Sandbox name to resolve.
|
|
484
|
+
environment_name: Optional environment name for the lookup; defaults to the configured environment.
|
|
485
|
+
client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
A `Sandbox` handle for the running sandbox.
|
|
489
|
+
|
|
490
|
+
Raises:
|
|
491
|
+
NotFoundError: If no running sandbox exists with the given name.
|
|
492
|
+
"""
|
|
493
|
+
...
|
|
494
|
+
|
|
468
495
|
@staticmethod
|
|
469
496
|
async def from_id(sandbox_id: str, client: typing.Optional[modal.client._Client] = None) -> _Sandbox:
|
|
470
497
|
"""Construct a Sandbox from an id and look up the Sandbox result.
|
|
@@ -922,15 +949,18 @@ class _Sandbox(modal._object._Object):
|
|
|
922
949
|
|
|
923
950
|
@staticmethod
|
|
924
951
|
def _experimental_list(
|
|
925
|
-
*,
|
|
952
|
+
*,
|
|
953
|
+
app_id: typing.Optional[str] = None,
|
|
954
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
955
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
926
956
|
) -> collections.abc.AsyncGenerator[_Sandbox, None]:
|
|
927
957
|
"""List v2 Sandboxes in an App.
|
|
928
958
|
|
|
929
959
|
This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
|
|
930
|
-
Filtering based on tags is not yet supported.
|
|
931
960
|
|
|
932
961
|
Args:
|
|
933
962
|
app_id: The App to list Sandboxes under.
|
|
963
|
+
tags: If set, only sandboxes containing at least these tags are returned.
|
|
934
964
|
client: Optional client to use for the session.
|
|
935
965
|
|
|
936
966
|
Yields:
|
|
@@ -1729,9 +1759,10 @@ class Sandbox(modal.object.Object):
|
|
|
1729
1759
|
(e.g. `region="us-east-1"`).
|
|
1730
1760
|
|
|
1731
1761
|
V2 sandboxes created with this method are not currently returned by
|
|
1732
|
-
`Sandbox.list()
|
|
1733
|
-
|
|
1734
|
-
use `Sandbox.from_id(sandbox.object_id)` to
|
|
1762
|
+
`Sandbox.list()`. A named sandbox can be looked up with
|
|
1763
|
+
`Sandbox._experimental_from_name(app_name, name)`; otherwise store
|
|
1764
|
+
`sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
|
|
1765
|
+
reattach.
|
|
1735
1766
|
"""
|
|
1736
1767
|
...
|
|
1737
1768
|
|
|
@@ -1789,9 +1820,10 @@ class Sandbox(modal.object.Object):
|
|
|
1789
1820
|
(e.g. `region="us-east-1"`).
|
|
1790
1821
|
|
|
1791
1822
|
V2 sandboxes created with this method are not currently returned by
|
|
1792
|
-
`Sandbox.list()
|
|
1793
|
-
|
|
1794
|
-
use `Sandbox.from_id(sandbox.object_id)` to
|
|
1823
|
+
`Sandbox.list()`. A named sandbox can be looked up with
|
|
1824
|
+
`Sandbox._experimental_from_name(app_name, name)`; otherwise store
|
|
1825
|
+
`sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
|
|
1826
|
+
reattach.
|
|
1795
1827
|
"""
|
|
1796
1828
|
...
|
|
1797
1829
|
|
|
@@ -1892,6 +1924,63 @@ class Sandbox(modal.object.Object):
|
|
|
1892
1924
|
|
|
1893
1925
|
from_name: typing.ClassVar[__from_name_spec]
|
|
1894
1926
|
|
|
1927
|
+
class ___experimental_from_name_spec(typing_extensions.Protocol):
|
|
1928
|
+
def __call__(
|
|
1929
|
+
self,
|
|
1930
|
+
/,
|
|
1931
|
+
app_name: str,
|
|
1932
|
+
name: str,
|
|
1933
|
+
*,
|
|
1934
|
+
environment_name: typing.Optional[str] = None,
|
|
1935
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
1936
|
+
) -> Sandbox:
|
|
1937
|
+
"""Get a running V2 Sandbox by name from a deployed App.
|
|
1938
|
+
|
|
1939
|
+
This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
|
|
1940
|
+
|
|
1941
|
+
Args:
|
|
1942
|
+
app_name: Name of the deployed app to look up the sandbox under.
|
|
1943
|
+
name: Sandbox name to resolve.
|
|
1944
|
+
environment_name: Optional environment name for the lookup; defaults to the configured environment.
|
|
1945
|
+
client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
|
|
1946
|
+
|
|
1947
|
+
Returns:
|
|
1948
|
+
A `Sandbox` handle for the running sandbox.
|
|
1949
|
+
|
|
1950
|
+
Raises:
|
|
1951
|
+
NotFoundError: If no running sandbox exists with the given name.
|
|
1952
|
+
"""
|
|
1953
|
+
...
|
|
1954
|
+
|
|
1955
|
+
async def aio(
|
|
1956
|
+
self,
|
|
1957
|
+
/,
|
|
1958
|
+
app_name: str,
|
|
1959
|
+
name: str,
|
|
1960
|
+
*,
|
|
1961
|
+
environment_name: typing.Optional[str] = None,
|
|
1962
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
1963
|
+
) -> Sandbox:
|
|
1964
|
+
"""Get a running V2 Sandbox by name from a deployed App.
|
|
1965
|
+
|
|
1966
|
+
This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
|
|
1967
|
+
|
|
1968
|
+
Args:
|
|
1969
|
+
app_name: Name of the deployed app to look up the sandbox under.
|
|
1970
|
+
name: Sandbox name to resolve.
|
|
1971
|
+
environment_name: Optional environment name for the lookup; defaults to the configured environment.
|
|
1972
|
+
client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
|
|
1973
|
+
|
|
1974
|
+
Returns:
|
|
1975
|
+
A `Sandbox` handle for the running sandbox.
|
|
1976
|
+
|
|
1977
|
+
Raises:
|
|
1978
|
+
NotFoundError: If no running sandbox exists with the given name.
|
|
1979
|
+
"""
|
|
1980
|
+
...
|
|
1981
|
+
|
|
1982
|
+
_experimental_from_name: typing.ClassVar[___experimental_from_name_spec]
|
|
1983
|
+
|
|
1895
1984
|
class __from_id_spec(typing_extensions.Protocol):
|
|
1896
1985
|
def __call__(self, /, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox:
|
|
1897
1986
|
"""Construct a Sandbox from an id and look up the Sandbox result.
|
|
@@ -2864,15 +2953,20 @@ class Sandbox(modal.object.Object):
|
|
|
2864
2953
|
|
|
2865
2954
|
class ___experimental_list_spec(typing_extensions.Protocol):
|
|
2866
2955
|
def __call__(
|
|
2867
|
-
self,
|
|
2956
|
+
self,
|
|
2957
|
+
/,
|
|
2958
|
+
*,
|
|
2959
|
+
app_id: typing.Optional[str] = None,
|
|
2960
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
2961
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
2868
2962
|
) -> typing.Generator[Sandbox, None, None]:
|
|
2869
2963
|
"""List v2 Sandboxes in an App.
|
|
2870
2964
|
|
|
2871
2965
|
This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
|
|
2872
|
-
Filtering based on tags is not yet supported.
|
|
2873
2966
|
|
|
2874
2967
|
Args:
|
|
2875
2968
|
app_id: The App to list Sandboxes under.
|
|
2969
|
+
tags: If set, only sandboxes containing at least these tags are returned.
|
|
2876
2970
|
client: Optional client to use for the session.
|
|
2877
2971
|
|
|
2878
2972
|
Yields:
|
|
@@ -2881,15 +2975,20 @@ class Sandbox(modal.object.Object):
|
|
|
2881
2975
|
...
|
|
2882
2976
|
|
|
2883
2977
|
def aio(
|
|
2884
|
-
self,
|
|
2978
|
+
self,
|
|
2979
|
+
/,
|
|
2980
|
+
*,
|
|
2981
|
+
app_id: typing.Optional[str] = None,
|
|
2982
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
2983
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
2885
2984
|
) -> collections.abc.AsyncGenerator[Sandbox, None]:
|
|
2886
2985
|
"""List v2 Sandboxes in an App.
|
|
2887
2986
|
|
|
2888
2987
|
This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
|
|
2889
|
-
Filtering based on tags is not yet supported.
|
|
2890
2988
|
|
|
2891
2989
|
Args:
|
|
2892
2990
|
app_id: The App to list Sandboxes under.
|
|
2991
|
+
tags: If set, only sandboxes containing at least these tags are returned.
|
|
2893
2992
|
client: Optional client to use for the session.
|
|
2894
2993
|
|
|
2895
2994
|
Yields:
|
|
@@ -5,7 +5,7 @@ import enum
|
|
|
5
5
|
from dataclasses import FrozenInstanceError, dataclass
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
from decimal import Decimal
|
|
8
|
-
from typing import Any, Iterable, Optional
|
|
8
|
+
from typing import Any, Iterable, Optional, TypedDict
|
|
9
9
|
|
|
10
10
|
from modal_proto import api_pb2
|
|
11
11
|
|
|
@@ -90,6 +90,12 @@ class SecretInfo:
|
|
|
90
90
|
created_by: str | None
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
class VolumeCreateOptions(TypedDict, total=False):
|
|
94
|
+
"""Options used when creating a Volume."""
|
|
95
|
+
|
|
96
|
+
experimental_options: dict[str, Any]
|
|
97
|
+
|
|
98
|
+
|
|
93
99
|
@dataclass
|
|
94
100
|
class VolumeInfo:
|
|
95
101
|
"""Information about a Volume object."""
|
|
@@ -71,7 +71,7 @@ from ._utils.name_utils import check_object_name
|
|
|
71
71
|
from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
|
|
72
72
|
from .client import _Client
|
|
73
73
|
from .config import logger
|
|
74
|
-
from .types import FileEntry, FileEntryType as FileEntryType, VolumeInfo
|
|
74
|
+
from .types import FileEntry, FileEntryType as FileEntryType, VolumeCreateOptions, VolumeInfo
|
|
75
75
|
|
|
76
76
|
# Max duration for uploading to volumes files
|
|
77
77
|
# As a guide, files >40GiB will take >10 minutes to upload.
|
|
@@ -120,6 +120,20 @@ def _validate_volume_version(
|
|
|
120
120
|
)
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
def _experimental_options_to_string_map(experimental_options: dict[str, Any]) -> dict[str, str]:
|
|
124
|
+
"""Coerce experimental option values to strings for the proto's string map."""
|
|
125
|
+
return {str(key): str(value) for key, value in experimental_options.items()}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _volume_create_options_to_proto(create_options: "VolumeCreateOptions | None") -> api_pb2.VolumeCreateOptions:
|
|
129
|
+
"""Build a VolumeCreateOptions proto message from the public TypedDict."""
|
|
130
|
+
if not create_options:
|
|
131
|
+
return api_pb2.VolumeCreateOptions()
|
|
132
|
+
return api_pb2.VolumeCreateOptions(
|
|
133
|
+
experimental_options=_experimental_options_to_string_map(create_options.get("experimental_options", {})),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
123
137
|
class _VolumeManager:
|
|
124
138
|
"""Namespace with methods for managing named Volume objects."""
|
|
125
139
|
|
|
@@ -131,6 +145,7 @@ class _VolumeManager:
|
|
|
131
145
|
allow_existing: bool = False,
|
|
132
146
|
environment_name: str | None = None,
|
|
133
147
|
client: _Client | None = None,
|
|
148
|
+
experimental_options: dict[str, Any] | None = None,
|
|
134
149
|
) -> None:
|
|
135
150
|
"""Create a new named Volume in the workspace environment.
|
|
136
151
|
|
|
@@ -144,6 +159,7 @@ class _VolumeManager:
|
|
|
144
159
|
allow_existing: If True, do nothing when a Volume with this name already exists.
|
|
145
160
|
environment_name: Environment to create in; defaults to the active environment.
|
|
146
161
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
162
|
+
experimental_options: Experimental options to create Volume with.
|
|
147
163
|
|
|
148
164
|
Examples:
|
|
149
165
|
```python notest
|
|
@@ -181,6 +197,9 @@ class _VolumeManager:
|
|
|
181
197
|
environment_name=_get_environment_name(environment_name),
|
|
182
198
|
object_creation_type=object_creation_type,
|
|
183
199
|
version=version,
|
|
200
|
+
create_options=_volume_create_options_to_proto(
|
|
201
|
+
VolumeCreateOptions(experimental_options=experimental_options or {})
|
|
202
|
+
),
|
|
184
203
|
)
|
|
185
204
|
try:
|
|
186
205
|
response = await client.stub.VolumeGetOrCreate(req)
|
|
@@ -545,6 +564,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
545
564
|
environment_name: str | None = None,
|
|
546
565
|
create_if_missing: bool = False,
|
|
547
566
|
version: "modal_proto.api_pb2.VolumeFsVersion.ValueType | None" = None,
|
|
567
|
+
create_options: "VolumeCreateOptions | None" = None,
|
|
548
568
|
client: _Client | None = None,
|
|
549
569
|
) -> "_Volume":
|
|
550
570
|
"""Reference a Volume by name, optionally creating it on the server first.
|
|
@@ -556,6 +576,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
556
576
|
environment_name: Environment to resolve the name in; defaults to the active environment.
|
|
557
577
|
create_if_missing: If True, create the Volume when it does not already exist.
|
|
558
578
|
version: Optional VolumeFS backend version; must match an existing Volume when set.
|
|
579
|
+
create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
|
|
559
580
|
client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
|
|
560
581
|
|
|
561
582
|
Returns:
|
|
@@ -580,6 +601,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
580
601
|
environment_name=load_context.environment_name,
|
|
581
602
|
object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
|
|
582
603
|
version=version,
|
|
604
|
+
create_options=_volume_create_options_to_proto(create_options),
|
|
583
605
|
)
|
|
584
606
|
response = await load_context.client.stub.VolumeGetOrCreate(req)
|
|
585
607
|
if version is not None:
|
|
@@ -650,6 +672,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
650
672
|
client: _Client | None = None,
|
|
651
673
|
environment_name: str | None = None,
|
|
652
674
|
version: "modal_proto.api_pb2.VolumeFsVersion.ValueType | None" = None,
|
|
675
|
+
create_options: "VolumeCreateOptions | None" = None,
|
|
653
676
|
_heartbeat_sleep: float = EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, # mdmd:line-hidden
|
|
654
677
|
) -> AsyncGenerator["_Volume", None]:
|
|
655
678
|
"""Create an anonymous Volume that exists for the duration of the context manager.
|
|
@@ -658,6 +681,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
658
681
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
659
682
|
environment_name: Environment for the ephemeral Volume; defaults to the active environment.
|
|
660
683
|
version: Optional VolumeFS backend version for the ephemeral Volume.
|
|
684
|
+
create_options: Options applied when creating the ephemeral Volume.
|
|
661
685
|
|
|
662
686
|
Examples:
|
|
663
687
|
```python
|
|
@@ -678,6 +702,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
|
678
702
|
object_creation_type=api_pb2.OBJECT_CREATION_TYPE_EPHEMERAL,
|
|
679
703
|
environment_name=_get_environment_name(environment_name),
|
|
680
704
|
version=version,
|
|
705
|
+
create_options=_volume_create_options_to_proto(create_options),
|
|
681
706
|
)
|
|
682
707
|
response = await client.stub.VolumeGetOrCreate(request)
|
|
683
708
|
async with TaskContext() as tc:
|
|
@@ -28,6 +28,16 @@ def _validate_volume_version(requested_version: int, actual_version: int, volume
|
|
|
28
28
|
"""Validate that the returned volume version matches the requested version."""
|
|
29
29
|
...
|
|
30
30
|
|
|
31
|
+
def _experimental_options_to_string_map(experimental_options: dict[str, typing.Any]) -> dict[str, str]:
|
|
32
|
+
"""Coerce experimental option values to strings for the proto's string map."""
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
def _volume_create_options_to_proto(
|
|
36
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions],
|
|
37
|
+
) -> modal_proto.api_pb2.VolumeCreateOptions:
|
|
38
|
+
"""Build a VolumeCreateOptions proto message from the public TypedDict."""
|
|
39
|
+
...
|
|
40
|
+
|
|
31
41
|
class _VolumeManager:
|
|
32
42
|
"""Namespace with methods for managing named Volume objects."""
|
|
33
43
|
async def create(
|
|
@@ -38,6 +48,7 @@ class _VolumeManager:
|
|
|
38
48
|
allow_existing: bool = False,
|
|
39
49
|
environment_name: typing.Optional[str] = None,
|
|
40
50
|
client: typing.Optional[modal.client._Client] = None,
|
|
51
|
+
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
41
52
|
) -> None:
|
|
42
53
|
"""Create a new named Volume in the workspace environment.
|
|
43
54
|
|
|
@@ -51,6 +62,7 @@ class _VolumeManager:
|
|
|
51
62
|
allow_existing: If True, do nothing when a Volume with this name already exists.
|
|
52
63
|
environment_name: Environment to create in; defaults to the active environment.
|
|
53
64
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
65
|
+
experimental_options: Experimental options to create Volume with.
|
|
54
66
|
|
|
55
67
|
Examples:
|
|
56
68
|
```python notest
|
|
@@ -167,6 +179,7 @@ class VolumeManager:
|
|
|
167
179
|
allow_existing: bool = False,
|
|
168
180
|
environment_name: typing.Optional[str] = None,
|
|
169
181
|
client: typing.Optional[modal.client.Client] = None,
|
|
182
|
+
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
170
183
|
) -> None:
|
|
171
184
|
"""Create a new named Volume in the workspace environment.
|
|
172
185
|
|
|
@@ -180,6 +193,7 @@ class VolumeManager:
|
|
|
180
193
|
allow_existing: If True, do nothing when a Volume with this name already exists.
|
|
181
194
|
environment_name: Environment to create in; defaults to the active environment.
|
|
182
195
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
196
|
+
experimental_options: Experimental options to create Volume with.
|
|
183
197
|
|
|
184
198
|
Examples:
|
|
185
199
|
```python notest
|
|
@@ -212,6 +226,7 @@ class VolumeManager:
|
|
|
212
226
|
allow_existing: bool = False,
|
|
213
227
|
environment_name: typing.Optional[str] = None,
|
|
214
228
|
client: typing.Optional[modal.client.Client] = None,
|
|
229
|
+
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
215
230
|
) -> None:
|
|
216
231
|
"""Create a new named Volume in the workspace environment.
|
|
217
232
|
|
|
@@ -225,6 +240,7 @@ class VolumeManager:
|
|
|
225
240
|
allow_existing: If True, do nothing when a Volume with this name already exists.
|
|
226
241
|
environment_name: Environment to create in; defaults to the active environment.
|
|
227
242
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
243
|
+
experimental_options: Experimental options to create Volume with.
|
|
228
244
|
|
|
229
245
|
Examples:
|
|
230
246
|
```python notest
|
|
@@ -564,6 +580,7 @@ class _Volume(modal._object._Object):
|
|
|
564
580
|
environment_name: typing.Optional[str] = None,
|
|
565
581
|
create_if_missing: bool = False,
|
|
566
582
|
version: typing.Optional[int] = None,
|
|
583
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
|
|
567
584
|
client: typing.Optional[modal.client._Client] = None,
|
|
568
585
|
) -> _Volume:
|
|
569
586
|
"""Reference a Volume by name, optionally creating it on the server first.
|
|
@@ -575,6 +592,7 @@ class _Volume(modal._object._Object):
|
|
|
575
592
|
environment_name: Environment to resolve the name in; defaults to the active environment.
|
|
576
593
|
create_if_missing: If True, create the Volume when it does not already exist.
|
|
577
594
|
version: Optional VolumeFS backend version; must match an existing Volume when set.
|
|
595
|
+
create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
|
|
578
596
|
client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
|
|
579
597
|
|
|
580
598
|
Returns:
|
|
@@ -630,6 +648,7 @@ class _Volume(modal._object._Object):
|
|
|
630
648
|
client: typing.Optional[modal.client._Client] = None,
|
|
631
649
|
environment_name: typing.Optional[str] = None,
|
|
632
650
|
version: typing.Optional[int] = None,
|
|
651
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
|
|
633
652
|
_heartbeat_sleep: float = 300,
|
|
634
653
|
) -> typing.AsyncContextManager[_Volume]:
|
|
635
654
|
"""Create an anonymous Volume that exists for the duration of the context manager.
|
|
@@ -638,6 +657,7 @@ class _Volume(modal._object._Object):
|
|
|
638
657
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
639
658
|
environment_name: Environment for the ephemeral Volume; defaults to the active environment.
|
|
640
659
|
version: Optional VolumeFS backend version for the ephemeral Volume.
|
|
660
|
+
create_options: Options applied when creating the ephemeral Volume.
|
|
641
661
|
|
|
642
662
|
Examples:
|
|
643
663
|
```python
|
|
@@ -928,6 +948,7 @@ class Volume(modal.object.Object):
|
|
|
928
948
|
environment_name: typing.Optional[str] = None,
|
|
929
949
|
create_if_missing: bool = False,
|
|
930
950
|
version: typing.Optional[int] = None,
|
|
951
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
|
|
931
952
|
client: typing.Optional[modal.client.Client] = None,
|
|
932
953
|
) -> Volume:
|
|
933
954
|
"""Reference a Volume by name, optionally creating it on the server first.
|
|
@@ -939,6 +960,7 @@ class Volume(modal.object.Object):
|
|
|
939
960
|
environment_name: Environment to resolve the name in; defaults to the active environment.
|
|
940
961
|
create_if_missing: If True, create the Volume when it does not already exist.
|
|
941
962
|
version: Optional VolumeFS backend version; must match an existing Volume when set.
|
|
963
|
+
create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
|
|
942
964
|
client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
|
|
943
965
|
|
|
944
966
|
Returns:
|
|
@@ -995,6 +1017,7 @@ class Volume(modal.object.Object):
|
|
|
995
1017
|
client: typing.Optional[modal.client.Client] = None,
|
|
996
1018
|
environment_name: typing.Optional[str] = None,
|
|
997
1019
|
version: typing.Optional[int] = None,
|
|
1020
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
|
|
998
1021
|
_heartbeat_sleep: float = 300,
|
|
999
1022
|
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
|
|
1000
1023
|
"""Create an anonymous Volume that exists for the duration of the context manager.
|
|
@@ -1003,6 +1026,7 @@ class Volume(modal.object.Object):
|
|
|
1003
1026
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
1004
1027
|
environment_name: Environment for the ephemeral Volume; defaults to the active environment.
|
|
1005
1028
|
version: Optional VolumeFS backend version for the ephemeral Volume.
|
|
1029
|
+
create_options: Options applied when creating the ephemeral Volume.
|
|
1006
1030
|
|
|
1007
1031
|
Examples:
|
|
1008
1032
|
```python
|
|
@@ -1025,6 +1049,7 @@ class Volume(modal.object.Object):
|
|
|
1025
1049
|
client: typing.Optional[modal.client.Client] = None,
|
|
1026
1050
|
environment_name: typing.Optional[str] = None,
|
|
1027
1051
|
version: typing.Optional[int] = None,
|
|
1052
|
+
create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
|
|
1028
1053
|
_heartbeat_sleep: float = 300,
|
|
1029
1054
|
) -> typing.AsyncContextManager[Volume]:
|
|
1030
1055
|
"""Create an anonymous Volume that exists for the duration of the context manager.
|
|
@@ -1033,6 +1058,7 @@ class Volume(modal.object.Object):
|
|
|
1033
1058
|
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
1034
1059
|
environment_name: Environment for the ephemeral Volume; defaults to the active environment.
|
|
1035
1060
|
version: Optional VolumeFS backend version for the ephemeral Volume.
|
|
1061
|
+
create_options: Options applied when creating the ephemeral Volume.
|
|
1036
1062
|
|
|
1037
1063
|
Examples:
|
|
1038
1064
|
```python
|