modal 1.4.4.dev1__tar.gz → 1.4.4.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/PKG-INFO +1 -1
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/client.pyi +2 -2
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/experimental/flash.py +14 -5
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/experimental/flash.pyi +14 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/sandbox.py +53 -3
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/sandbox.pyi +29 -6
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/api_grpc.py +16 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/api_pb2.py +825 -785
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/api_pb2.pyi +87 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/api_pb2_grpc.py +33 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/api_pb2_grpc.pyi +10 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_version/__init__.py +1 -1
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/LICENSE +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/README.md +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/__main__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_billing.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_clustered_functions.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_container_entrypoint.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_environments.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_function_variants.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_functions.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_grpc_client.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_ipython.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_load_context.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_location.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_logs.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_object.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_output/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_output/manager.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_output/pty.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_output/rich.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_output/status.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_partial_function.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_resolver.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_resources.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/execution_context.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_serialization.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_server.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_traceback.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_tunnel.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_tunnel.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_type_manager.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/logger.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_vendor/version.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/_watcher.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/app.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/app.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/billing.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/README.md +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/builder/base-images.json +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/call_graph.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/_download.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/_help.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/_traceback.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/app.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/billing.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/changelog.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/cluster.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/config.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/container.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/dashboard.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/dict.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/entry_point.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/environment.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/import_refs.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/launch.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/logo.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/profile.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/queues.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/run.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/secret.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/selector.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/shell.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/token.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/utils.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cli/volume.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/client.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cls.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/cls.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/config.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/container_process.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/container_process.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/dict.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/dict.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/environments.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/environments.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/exception.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/experimental/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/experimental/ipython.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/file_io.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/file_io.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/functions.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/functions.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/image.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/image.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/io_streams.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/io_streams.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/mount.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/mount.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/network_file_system.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/network_file_system.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/object.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/object.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/output.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/parallel_map.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/parallel_map.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/partial_function.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/partial_function.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/proxy.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/proxy.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/py.typed +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/queue.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/queue.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/retries.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/runner.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/runner.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/running_app.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/sandbox_fs.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/sandbox_fs.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/schedule.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/scheduler_placement.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/secret.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/secret.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/server.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/server.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/serving.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/serving.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/snapshot.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/snapshot.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/stream_type.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/token_flow.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/token_flow.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/volume.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal/volume.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/__init__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/py.typed +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/modal_version/__main__.py +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/pyproject.toml +0 -0
- {modal-1.4.4.dev1 → modal-1.4.4.dev2}/setup.cfg +0 -0
|
@@ -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.4.4.
|
|
38
|
+
version: str = "1.4.4.dev2",
|
|
39
39
|
):
|
|
40
40
|
"""mdmd:hidden
|
|
41
41
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -175,7 +175,7 @@ class Client:
|
|
|
175
175
|
server_url: str,
|
|
176
176
|
client_type: int,
|
|
177
177
|
credentials: typing.Optional[tuple[str, str]],
|
|
178
|
-
version: str = "1.4.4.
|
|
178
|
+
version: str = "1.4.4.dev2",
|
|
179
179
|
):
|
|
180
180
|
"""mdmd:hidden
|
|
181
181
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -51,6 +51,7 @@ class _FlashManager:
|
|
|
51
51
|
self.stopped = False
|
|
52
52
|
self.num_heartbeat_failures = 0
|
|
53
53
|
self.task_id = os.environ["MODAL_TASK_ID"]
|
|
54
|
+
self.heartbeat_task = None
|
|
54
55
|
|
|
55
56
|
async def is_port_connection_healthy(
|
|
56
57
|
self, process: Optional[subprocess.Popen], timeout: float = 0.5
|
|
@@ -86,13 +87,22 @@ class _FlashManager:
|
|
|
86
87
|
host = parsed_url.hostname
|
|
87
88
|
port = parsed_url.port or 443
|
|
88
89
|
|
|
90
|
+
if self.is_server:
|
|
91
|
+
await self._start_server_tunnel()
|
|
92
|
+
return
|
|
93
|
+
await self._start_flash_registration(host, port)
|
|
94
|
+
|
|
95
|
+
async def _start_server_tunnel(self) -> None:
|
|
96
|
+
# Worker-side HTTP relay owns Flash registration and drain for server tasks.
|
|
97
|
+
logger.warning(f"[Modal Flash] Server tunnel opened at {self.tunnel.url}.")
|
|
98
|
+
|
|
99
|
+
async def _start_flash_registration(self, host: str, port: int) -> None:
|
|
89
100
|
try:
|
|
90
101
|
await self._wait_for_port_success(host, port)
|
|
91
102
|
except (Exception, KeyboardInterrupt, asyncio.CancelledError):
|
|
92
103
|
await self._deregister()
|
|
93
104
|
await self.tunnel_manager.__aexit__(*sys.exc_info())
|
|
94
105
|
raise
|
|
95
|
-
|
|
96
106
|
self.heartbeat_task = asyncio.create_task(self._run_heartbeat(host, port))
|
|
97
107
|
self.drain_task = asyncio.create_task(self._drain_container())
|
|
98
108
|
|
|
@@ -209,16 +219,15 @@ class _FlashManager:
|
|
|
209
219
|
if self.heartbeat_task:
|
|
210
220
|
self.heartbeat_task.cancel()
|
|
211
221
|
try:
|
|
222
|
+
# NOTE(gongy): We skip calling TunnelStop to avoid interrupting in-flight requests.
|
|
223
|
+
# It is up to the user to wait after calling .stop() to drain in-flight requests.
|
|
212
224
|
await asyncio.wait_for(self.heartbeat_task, timeout=5)
|
|
225
|
+
logger.warning(f"[Modal Flash] Stopping heartbeat task on {self.tunnel.url}.")
|
|
213
226
|
except (asyncio.TimeoutError, asyncio.CancelledError):
|
|
214
227
|
logger.warning("[Modal Flash] Heartbeat task did not stop within 5s.")
|
|
215
228
|
except Exception as e:
|
|
216
229
|
logger.error(f"[Modal Flash] Error stopping: {e}")
|
|
217
230
|
self.stopped = True
|
|
218
|
-
logger.warning(f"[Modal Flash] No longer accepting new requests on {self.tunnel.url}.")
|
|
219
|
-
|
|
220
|
-
# NOTE(gongy): We skip calling TunnelStop to avoid interrupting in-flight requests.
|
|
221
|
-
# It is up to the user to wait after calling .stop() to drain in-flight requests.
|
|
222
231
|
|
|
223
232
|
async def close(self):
|
|
224
233
|
if not self.stopped:
|
|
@@ -23,6 +23,8 @@ class _FlashManager:
|
|
|
23
23
|
self, process: typing.Optional[subprocess.Popen], timeout: float = 0.5
|
|
24
24
|
) -> tuple[bool, typing.Optional[Exception]]: ...
|
|
25
25
|
async def _start(self): ...
|
|
26
|
+
async def _start_server_tunnel(self) -> None: ...
|
|
27
|
+
async def _start_flash_registration(self, host: str, port: int) -> None: ...
|
|
26
28
|
async def _deregister(self): ...
|
|
27
29
|
async def _drain_container(self):
|
|
28
30
|
"""Background task that checks if we've encountered too many failures and drains the container if so."""
|
|
@@ -63,6 +65,18 @@ class FlashManager:
|
|
|
63
65
|
|
|
64
66
|
_start: ___start_spec
|
|
65
67
|
|
|
68
|
+
class ___start_server_tunnel_spec(typing_extensions.Protocol):
|
|
69
|
+
def __call__(self, /) -> None: ...
|
|
70
|
+
async def aio(self, /) -> None: ...
|
|
71
|
+
|
|
72
|
+
_start_server_tunnel: ___start_server_tunnel_spec
|
|
73
|
+
|
|
74
|
+
class ___start_flash_registration_spec(typing_extensions.Protocol):
|
|
75
|
+
def __call__(self, /, host: str, port: int) -> None: ...
|
|
76
|
+
async def aio(self, /, host: str, port: int) -> None: ...
|
|
77
|
+
|
|
78
|
+
_start_flash_registration: ___start_flash_registration_spec
|
|
79
|
+
|
|
66
80
|
class ___deregister_spec(typing_extensions.Protocol):
|
|
67
81
|
def __call__(self, /): ...
|
|
68
82
|
async def aio(self, /): ...
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
2
|
import asyncio
|
|
3
3
|
import builtins
|
|
4
|
+
import enum
|
|
4
5
|
import json
|
|
5
6
|
import os
|
|
6
7
|
import time
|
|
@@ -75,11 +76,47 @@ _default_image: _Image = _Image.debian_slim()
|
|
|
75
76
|
# e.g. 'runsc exec ...'. So we use 2**16 as the limit.
|
|
76
77
|
ARG_MAX_BYTES = 2**16
|
|
77
78
|
TTL_NO_EXPIRY_SENTINEL = -1
|
|
79
|
+
_V1_SANDBOX_ID_ALPHABET = frozenset("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
|
80
|
+
_ULID_ALPHABET = frozenset("0123456789ABCDEFGHJKMNPQRSTVWXYZ")
|
|
78
81
|
|
|
79
82
|
if TYPE_CHECKING:
|
|
80
83
|
import modal.app
|
|
81
84
|
|
|
82
85
|
|
|
86
|
+
class SandboxVersion(enum.Enum):
|
|
87
|
+
V1 = 1
|
|
88
|
+
V2 = 2
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _is_v1_sandbox_id(sandbox_id: str) -> bool:
|
|
92
|
+
prefix, separator, suffix = sandbox_id.partition("-")
|
|
93
|
+
return (
|
|
94
|
+
prefix == "sb"
|
|
95
|
+
and separator == "-"
|
|
96
|
+
and len(suffix) == 22
|
|
97
|
+
and all(ch in _V1_SANDBOX_ID_ALPHABET for ch in suffix)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _is_v2_sandbox_id(sandbox_id: str) -> bool:
|
|
102
|
+
prefix, separator, suffix = sandbox_id.partition("-")
|
|
103
|
+
return (
|
|
104
|
+
prefix == "sb"
|
|
105
|
+
and separator == "-"
|
|
106
|
+
and len(suffix) == 26
|
|
107
|
+
and suffix[0] in "01234567"
|
|
108
|
+
and all(ch in _ULID_ALPHABET for ch in suffix)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _get_sandbox_version(sandbox_id: str) -> SandboxVersion:
|
|
113
|
+
if _is_v2_sandbox_id(sandbox_id):
|
|
114
|
+
return SandboxVersion.V2
|
|
115
|
+
if _is_v1_sandbox_id(sandbox_id):
|
|
116
|
+
return SandboxVersion.V1
|
|
117
|
+
raise InvalidError(f"Invalid Sandbox ID: {sandbox_id!r}")
|
|
118
|
+
|
|
119
|
+
|
|
83
120
|
def _result_returncode(result: Optional[api_pb2.GenericResult]) -> Optional[int]:
|
|
84
121
|
if result is None or result.status == api_pb2.GenericResult.GENERIC_STATUS_UNSPECIFIED:
|
|
85
122
|
return None
|
|
@@ -675,6 +712,11 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
675
712
|
|
|
676
713
|
Features like tags, memory snapshots, volumes, network file systems, GPUs,
|
|
677
714
|
custom domains, OIDC identity tokens, and proxies are not supported.
|
|
715
|
+
|
|
716
|
+
V2 sandboxes created with this method are not currently returned by
|
|
717
|
+
`Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
|
|
718
|
+
Store `sandbox.object_id` if you need to retrieve the sandbox later, and
|
|
719
|
+
use `Sandbox.from_id(sandbox.object_id)` to reattach.
|
|
678
720
|
"""
|
|
679
721
|
from .app import _App
|
|
680
722
|
|
|
@@ -908,10 +950,18 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
908
950
|
if client is None:
|
|
909
951
|
client = await _Client.from_env()
|
|
910
952
|
|
|
953
|
+
sandbox_version = _get_sandbox_version(sandbox_id)
|
|
954
|
+
is_v2 = sandbox_version == SandboxVersion.V2
|
|
911
955
|
req = api_pb2.SandboxWaitRequest(sandbox_id=sandbox_id, timeout=0)
|
|
912
|
-
|
|
956
|
+
if is_v2:
|
|
957
|
+
assert client._auth_token_manager
|
|
958
|
+
auth_token = await client._auth_token_manager.get_token()
|
|
959
|
+
resp = await client.stub.SandboxWaitV2(req, metadata=[("x-modal-auth-token", auth_token)])
|
|
960
|
+
else:
|
|
961
|
+
resp = await client.stub.SandboxWait(req)
|
|
913
962
|
|
|
914
963
|
obj = _Sandbox._new_hydrated(sandbox_id, client, None)
|
|
964
|
+
obj._is_v2 = is_v2
|
|
915
965
|
|
|
916
966
|
if resp.result.status:
|
|
917
967
|
obj._result = resp.result
|
|
@@ -1000,7 +1050,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1000
1050
|
# You can later mount this snapshot to another Sandbox:
|
|
1001
1051
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1002
1052
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1003
|
-
sandbox_session_2.
|
|
1053
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1004
1054
|
```
|
|
1005
1055
|
"""
|
|
1006
1056
|
self._ensure_v1("mount_image")
|
|
@@ -1071,7 +1121,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1071
1121
|
# You can later mount this snapshot to another Sandbox:
|
|
1072
1122
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1073
1123
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1074
|
-
sandbox_session_2.
|
|
1124
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1075
1125
|
```
|
|
1076
1126
|
"""
|
|
1077
1127
|
self._ensure_v1("snapshot_directory")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _typeshed
|
|
2
2
|
import collections.abc
|
|
3
|
+
import enum
|
|
3
4
|
import google.protobuf.message
|
|
4
5
|
import modal._object
|
|
5
6
|
import modal._tunnel
|
|
@@ -27,6 +28,13 @@ import pathlib
|
|
|
27
28
|
import typing
|
|
28
29
|
import typing_extensions
|
|
29
30
|
|
|
31
|
+
class SandboxVersion(enum.Enum):
|
|
32
|
+
V1 = 1
|
|
33
|
+
V2 = 2
|
|
34
|
+
|
|
35
|
+
def _is_v1_sandbox_id(sandbox_id: str) -> bool: ...
|
|
36
|
+
def _is_v2_sandbox_id(sandbox_id: str) -> bool: ...
|
|
37
|
+
def _get_sandbox_version(sandbox_id: str) -> SandboxVersion: ...
|
|
30
38
|
def _result_returncode(result: typing.Optional[modal_proto.api_pb2.GenericResult]) -> typing.Optional[int]: ...
|
|
31
39
|
def _validate_exec_args(args: collections.abc.Sequence[str]) -> None: ...
|
|
32
40
|
|
|
@@ -333,6 +341,11 @@ class _Sandbox(modal._object._Object):
|
|
|
333
341
|
|
|
334
342
|
Features like tags, memory snapshots, volumes, network file systems, GPUs,
|
|
335
343
|
custom domains, OIDC identity tokens, and proxies are not supported.
|
|
344
|
+
|
|
345
|
+
V2 sandboxes created with this method are not currently returned by
|
|
346
|
+
`Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
|
|
347
|
+
Store `sandbox.object_id` if you need to retrieve the sandbox later, and
|
|
348
|
+
use `Sandbox.from_id(sandbox.object_id)` to reattach.
|
|
336
349
|
"""
|
|
337
350
|
...
|
|
338
351
|
|
|
@@ -417,7 +430,7 @@ class _Sandbox(modal._object._Object):
|
|
|
417
430
|
# You can later mount this snapshot to another Sandbox:
|
|
418
431
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
419
432
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
420
|
-
sandbox_session_2.
|
|
433
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
421
434
|
```
|
|
422
435
|
"""
|
|
423
436
|
...
|
|
@@ -443,7 +456,7 @@ class _Sandbox(modal._object._Object):
|
|
|
443
456
|
# You can later mount this snapshot to another Sandbox:
|
|
444
457
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
445
458
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
446
|
-
sandbox_session_2.
|
|
459
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
447
460
|
```
|
|
448
461
|
"""
|
|
449
462
|
...
|
|
@@ -1219,6 +1232,11 @@ class Sandbox(modal.object.Object):
|
|
|
1219
1232
|
|
|
1220
1233
|
Features like tags, memory snapshots, volumes, network file systems, GPUs,
|
|
1221
1234
|
custom domains, OIDC identity tokens, and proxies are not supported.
|
|
1235
|
+
|
|
1236
|
+
V2 sandboxes created with this method are not currently returned by
|
|
1237
|
+
`Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
|
|
1238
|
+
Store `sandbox.object_id` if you need to retrieve the sandbox later, and
|
|
1239
|
+
use `Sandbox.from_id(sandbox.object_id)` to reattach.
|
|
1222
1240
|
"""
|
|
1223
1241
|
...
|
|
1224
1242
|
|
|
@@ -1257,6 +1275,11 @@ class Sandbox(modal.object.Object):
|
|
|
1257
1275
|
|
|
1258
1276
|
Features like tags, memory snapshots, volumes, network file systems, GPUs,
|
|
1259
1277
|
custom domains, OIDC identity tokens, and proxies are not supported.
|
|
1278
|
+
|
|
1279
|
+
V2 sandboxes created with this method are not currently returned by
|
|
1280
|
+
`Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
|
|
1281
|
+
Store `sandbox.object_id` if you need to retrieve the sandbox later, and
|
|
1282
|
+
use `Sandbox.from_id(sandbox.object_id)` to reattach.
|
|
1260
1283
|
"""
|
|
1261
1284
|
...
|
|
1262
1285
|
|
|
@@ -1420,7 +1443,7 @@ class Sandbox(modal.object.Object):
|
|
|
1420
1443
|
# You can later mount this snapshot to another Sandbox:
|
|
1421
1444
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1422
1445
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1423
|
-
sandbox_session_2.
|
|
1446
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1424
1447
|
```
|
|
1425
1448
|
"""
|
|
1426
1449
|
...
|
|
@@ -1445,7 +1468,7 @@ class Sandbox(modal.object.Object):
|
|
|
1445
1468
|
# You can later mount this snapshot to another Sandbox:
|
|
1446
1469
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1447
1470
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1448
|
-
sandbox_session_2.
|
|
1471
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1449
1472
|
```
|
|
1450
1473
|
"""
|
|
1451
1474
|
...
|
|
@@ -1486,7 +1509,7 @@ class Sandbox(modal.object.Object):
|
|
|
1486
1509
|
# You can later mount this snapshot to another Sandbox:
|
|
1487
1510
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1488
1511
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1489
|
-
sandbox_session_2.
|
|
1512
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1490
1513
|
```
|
|
1491
1514
|
"""
|
|
1492
1515
|
...
|
|
@@ -1503,7 +1526,7 @@ class Sandbox(modal.object.Object):
|
|
|
1503
1526
|
# You can later mount this snapshot to another Sandbox:
|
|
1504
1527
|
sandbox_session_2 = modal.Sandbox.create(...)
|
|
1505
1528
|
sandbox_session_2.mount_image("/user_project", user_project_snapshot)
|
|
1506
|
-
sandbox_session_2.
|
|
1529
|
+
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1507
1530
|
```
|
|
1508
1531
|
"""
|
|
1509
1532
|
...
|
|
@@ -263,6 +263,10 @@ class ModalClientBase(abc.ABC):
|
|
|
263
263
|
async def EndpointCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointCreateRequest, modal_proto.api_pb2.EndpointCreateResponse]') -> None:
|
|
264
264
|
pass
|
|
265
265
|
|
|
266
|
+
@abc.abstractmethod
|
|
267
|
+
async def EndpointList(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointListRequest, modal_proto.api_pb2.EndpointListResponse]') -> None:
|
|
268
|
+
pass
|
|
269
|
+
|
|
266
270
|
@abc.abstractmethod
|
|
267
271
|
async def EnvironmentCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EnvironmentCreateRequest, google.protobuf.empty_pb2.Empty]') -> None:
|
|
268
272
|
pass
|
|
@@ -1183,6 +1187,12 @@ class ModalClientBase(abc.ABC):
|
|
|
1183
1187
|
modal_proto.api_pb2.EndpointCreateRequest,
|
|
1184
1188
|
modal_proto.api_pb2.EndpointCreateResponse,
|
|
1185
1189
|
),
|
|
1190
|
+
'/modal.client.ModalClient/EndpointList': grpclib.const.Handler(
|
|
1191
|
+
self.EndpointList,
|
|
1192
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1193
|
+
modal_proto.api_pb2.EndpointListRequest,
|
|
1194
|
+
modal_proto.api_pb2.EndpointListResponse,
|
|
1195
|
+
),
|
|
1186
1196
|
'/modal.client.ModalClient/EnvironmentCreate': grpclib.const.Handler(
|
|
1187
1197
|
self.EnvironmentCreate,
|
|
1188
1198
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -2383,6 +2393,12 @@ class ModalClientStub:
|
|
|
2383
2393
|
modal_proto.api_pb2.EndpointCreateRequest,
|
|
2384
2394
|
modal_proto.api_pb2.EndpointCreateResponse,
|
|
2385
2395
|
)
|
|
2396
|
+
self.EndpointList = grpclib.client.UnaryUnaryMethod(
|
|
2397
|
+
channel,
|
|
2398
|
+
'/modal.client.ModalClient/EndpointList',
|
|
2399
|
+
modal_proto.api_pb2.EndpointListRequest,
|
|
2400
|
+
modal_proto.api_pb2.EndpointListResponse,
|
|
2401
|
+
)
|
|
2386
2402
|
self.EnvironmentCreate = grpclib.client.UnaryUnaryMethod(
|
|
2387
2403
|
channel,
|
|
2388
2404
|
'/modal.client.ModalClient/EnvironmentCreate',
|