modal 1.5.6.dev18__tar.gz → 1.5.6.dev19__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.6.dev18 → modal-1.5.6.dev19}/PKG-INFO +1 -1
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/app.py +76 -19
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/entry_point.py +1 -3
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/client.pyi +2 -2
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/config.py +1 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/functions.pyi +6 -6
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/sandbox.py +57 -16
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/sandbox.pyi +8 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/SOURCES.txt +0 -1
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/api_pb2.py +528 -533
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/api_pb2.pyi +9 -3
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_version/__init__.py +1 -1
- modal-1.5.6.dev18/modal/cli/bootstrap.py +0 -139
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/LICENSE +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/README.md +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/__main__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_billing.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_clustered_functions.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_environments.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_function_variants.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_functions.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_grpc_client.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_image.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_ipython.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_load_context.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_location.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_logs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_logs_manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_object.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_output/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_output/manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_output/pty.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_output/rich.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_output/status.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_partial_function.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_resolver.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_resources.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_serialization.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_server.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_supports_logs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_traceback.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_tunnel.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_tunnel.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_type_manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/logger.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_vendor/version.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_watcher.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/_workspace.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/app.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/app.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/billing.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/billing.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/README.md +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/builder/base-images.json +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/call_graph.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/_download.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/_help.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/_logs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/_stats.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/_traceback.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/billing.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/changelog.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/cluster.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/config.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/container.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/curl.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/dashboard.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/dict.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/endpoint.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/environment.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/function.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/image.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/import_refs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/launch.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/logo.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/profile.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/queues.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/run.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/secret.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/selector.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/server.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/shell.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/skills.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/token.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/utils.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/volume.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cli/workspace.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/client.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cls.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/cls.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/container_process.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/container_process.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/dict.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/dict.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/environments.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/environments.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/exception.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/experimental/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/experimental/flash.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/experimental/ipython.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/functions.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/image.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/image.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/io_streams.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/io_streams.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/mount.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/mount.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/network_file_system.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/network_file_system.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/object.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/object.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/output.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/parallel_map.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/parallel_map.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/partial_function.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/partial_function.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/proxy.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/proxy.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/py.typed +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/queue.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/queue.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/retries.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/runner.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/runner.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/running_app.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/sandbox_fs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/schedule.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/scheduler_placement.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/secret.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/secret.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/server.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/server.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/serving.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/serving.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/snapshot.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/snapshot.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/stream_type.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/token_flow.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/token_flow.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/types.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/volume.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/volume.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/workspace.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal/workspace.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/__init__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/py.typed +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/modal_version/__main__.py +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/pyproject.toml +0 -0
- {modal-1.5.6.dev18 → modal-1.5.6.dev19}/setup.cfg +0 -0
|
@@ -3,13 +3,15 @@ import re
|
|
|
3
3
|
import sys
|
|
4
4
|
import time
|
|
5
5
|
import warnings
|
|
6
|
+
from collections.abc import Mapping
|
|
6
7
|
from json import dumps
|
|
7
8
|
from typing import Literal, get_args
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
import rich
|
|
11
12
|
from click import UsageError
|
|
12
|
-
from
|
|
13
|
+
from google.protobuf.json_format import MessageToDict
|
|
14
|
+
from rich.table import Column, Table
|
|
13
15
|
from rich.text import Text
|
|
14
16
|
|
|
15
17
|
from modal._environments import ensure_env
|
|
@@ -620,8 +622,9 @@ async def dashboard(
|
|
|
620
622
|
@click.argument("app_identifier")
|
|
621
623
|
@env_option
|
|
622
624
|
@click.option("--json", is_flag=True, default=False)
|
|
625
|
+
@click.option("--no-color", is_flag=True, default=False, help="Disable colors in the output.")
|
|
623
626
|
@synchronizer.create_blocking
|
|
624
|
-
async def info(app_identifier: str, *, env: str | None = None, json: bool = False):
|
|
627
|
+
async def info(app_identifier: str, *, env: str | None = None, json: bool = False, no_color: bool = False):
|
|
625
628
|
"""Show an App's lifecycle, Functions, and Servers.
|
|
626
629
|
|
|
627
630
|
Examples:
|
|
@@ -650,6 +653,17 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
|
|
|
650
653
|
state = APP_STATE_TO_MESSAGE.get(lifecycle.app_state, Text("unknown", style="gray"))
|
|
651
654
|
|
|
652
655
|
if json:
|
|
656
|
+
summaries = {
|
|
657
|
+
function_id: MessageToDict(summary, preserving_proto_field_name=True)
|
|
658
|
+
for function_id, summary in resp.function_info_summaries.items()
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
def entries_with_info(entries: Mapping[str, str]) -> dict[str, dict]:
|
|
662
|
+
return {
|
|
663
|
+
name: {"id": function_id, "summary": summaries.get(function_id, {})}
|
|
664
|
+
for name, function_id in sorted(entries.items())
|
|
665
|
+
}
|
|
666
|
+
|
|
653
667
|
output.print_json(
|
|
654
668
|
dumps(
|
|
655
669
|
{
|
|
@@ -665,28 +679,71 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
|
|
|
665
679
|
"stopped_at": timestamp_to_localized_str(lifecycle.stopped_at, json),
|
|
666
680
|
"stopped_by": lifecycle.stopped_by or None,
|
|
667
681
|
},
|
|
668
|
-
"functions":
|
|
669
|
-
"servers":
|
|
682
|
+
"functions": entries_with_info(app_info.functions),
|
|
683
|
+
"servers": entries_with_info(app_info.servers),
|
|
670
684
|
}
|
|
671
685
|
)
|
|
672
686
|
)
|
|
673
687
|
return
|
|
674
688
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
)
|
|
689
|
+
header = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
|
|
690
|
+
header.add_row(Text("App:"), Text(app_info.description))
|
|
691
|
+
header.add_row(Text("App ID:"), Text(app_info.app_id))
|
|
692
|
+
header.add_row(Text("State:"), Text(state.plain))
|
|
678
693
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
694
|
+
def event(label: str, timestamp: float, actor: str, version: int = 0) -> None:
|
|
695
|
+
parts = [timestamp_to_localized_str(timestamp, isotz=False), actor]
|
|
696
|
+
if version:
|
|
697
|
+
parts.insert(0, f"v{version}")
|
|
698
|
+
header.add_row(Text(label + ":"), Text(" · ".join(p for p in parts if p)))
|
|
683
699
|
|
|
684
|
-
events: list[list[Text | str | None]] = [
|
|
685
|
-
["Created", timestamp_to_localized_str(lifecycle.created_at, json), lifecycle.created_by]
|
|
686
|
-
]
|
|
687
|
-
if lifecycle.deployed_at:
|
|
688
|
-
label = f"Deployed (v{lifecycle.version})" if lifecycle.version else "Deployed"
|
|
689
|
-
events.append([label, timestamp_to_localized_str(lifecycle.deployed_at, json), lifecycle.deployed_by])
|
|
690
700
|
if lifecycle.stopped_at:
|
|
691
|
-
|
|
692
|
-
|
|
701
|
+
event("Stopped", lifecycle.stopped_at, lifecycle.stopped_by)
|
|
702
|
+
if lifecycle.deployed_at:
|
|
703
|
+
event("Deployment", lifecycle.deployed_at, lifecycle.deployed_by, lifecycle.version)
|
|
704
|
+
event("Created", lifecycle.created_at, lifecycle.created_by)
|
|
705
|
+
output.print(header)
|
|
706
|
+
|
|
707
|
+
name_width = max((len(name) for name in [*app_info.functions, *app_info.servers]), default=0)
|
|
708
|
+
for title, entries in (("Functions", app_info.functions), ("Servers", app_info.servers)):
|
|
709
|
+
if not entries:
|
|
710
|
+
continue
|
|
711
|
+
output.print("")
|
|
712
|
+
output.print(Text(f"{title} ({len(entries)}):"))
|
|
713
|
+
for index, (name, function_id) in enumerate(sorted(entries.items())):
|
|
714
|
+
if index:
|
|
715
|
+
output.print("")
|
|
716
|
+
identity = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
|
|
717
|
+
identity.add_column(width=name_width + 2)
|
|
718
|
+
identity.add_column(no_wrap=True)
|
|
719
|
+
identity.add_row(Text(f" {name}"), Text(function_id))
|
|
720
|
+
output.print(identity)
|
|
721
|
+
if function_id not in resp.function_info_summaries:
|
|
722
|
+
continue
|
|
723
|
+
summary = resp.function_info_summaries[function_id]
|
|
724
|
+
hardware = [f"{gpu.count} × {gpu.gpu_type} GPU" for gpu in summary.gpu_config]
|
|
725
|
+
metadata = Text(f" {hardware[0] if hardware else 'CPU'}")
|
|
726
|
+
if len(hardware) > 1:
|
|
727
|
+
metadata.append(f" ({', '.join(hardware[1:])})", style=None if no_color else "bright_black")
|
|
728
|
+
if summary.web_function:
|
|
729
|
+
metadata.append(" · Web function")
|
|
730
|
+
if schedule := _app_function_schedule(summary.schedule):
|
|
731
|
+
metadata.append(" · ")
|
|
732
|
+
metadata.append(schedule)
|
|
733
|
+
if summary.HasField("requires_proxy_auth") and not summary.requires_proxy_auth:
|
|
734
|
+
metadata.append(" · ")
|
|
735
|
+
metadata.append("Unauthenticated", style=None if no_color else "yellow")
|
|
736
|
+
output.print(metadata)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def _app_function_schedule(schedule: api_pb2.Schedule) -> str | None:
|
|
740
|
+
if schedule.HasField("cron"):
|
|
741
|
+
return f"{schedule.cron.cron_string} ({schedule.cron.timezone or 'UTC'})"
|
|
742
|
+
if schedule.HasField("period"):
|
|
743
|
+
parts = [
|
|
744
|
+
f"{value:g} {field.name.removesuffix('s') if value == 1 else field.name}"
|
|
745
|
+
for field, value in schedule.period.ListFields()
|
|
746
|
+
if value
|
|
747
|
+
]
|
|
748
|
+
return "Every " + ", ".join(parts)
|
|
749
|
+
return None
|
|
@@ -11,7 +11,6 @@ from . import run, shell as shell_module
|
|
|
11
11
|
from ._help import ModalCommand, ModalGroup, ModalProfileOption
|
|
12
12
|
from .app import app_cli
|
|
13
13
|
from .billing import billing_cli
|
|
14
|
-
from .bootstrap import bootstrap
|
|
15
14
|
from .changelog import changelog
|
|
16
15
|
from .cluster import cluster_cli
|
|
17
16
|
from .config import config_cli
|
|
@@ -131,8 +130,6 @@ entrypoint_cli.add_command(secret_cli, panel="Storage")
|
|
|
131
130
|
entrypoint_cli.add_command(queue_cli, panel="Storage")
|
|
132
131
|
entrypoint_cli.add_command(volume_cli, panel="Storage")
|
|
133
132
|
|
|
134
|
-
entrypoint_cli.add_command(setup, panel="Onboarding")
|
|
135
|
-
entrypoint_cli.add_command(bootstrap, panel="Onboarding")
|
|
136
133
|
|
|
137
134
|
entrypoint_cli.add_command(workspace_cli, panel="Configuration")
|
|
138
135
|
entrypoint_cli.add_command(environment_cli, panel="Configuration")
|
|
@@ -140,6 +137,7 @@ entrypoint_cli.add_command(profile_cli, panel="Configuration")
|
|
|
140
137
|
entrypoint_cli.add_command(config_cli, panel="Configuration")
|
|
141
138
|
entrypoint_cli.add_command(token_cli, panel="Configuration")
|
|
142
139
|
entrypoint_cli.add_command(skills_cli, panel="Configuration")
|
|
140
|
+
entrypoint_cli.add_command(setup, panel="Configuration")
|
|
143
141
|
|
|
144
142
|
entrypoint_cli.add_command(billing_cli, panel="Observability")
|
|
145
143
|
entrypoint_cli.add_command(changelog, panel="Observability")
|
|
@@ -77,7 +77,7 @@ class _Client:
|
|
|
77
77
|
server_url: str,
|
|
78
78
|
client_type: int,
|
|
79
79
|
credentials: typing.Optional[tuple[str, str]],
|
|
80
|
-
version: str = "1.5.6.
|
|
80
|
+
version: str = "1.5.6.dev19",
|
|
81
81
|
*,
|
|
82
82
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
83
83
|
):
|
|
@@ -277,7 +277,7 @@ class Client:
|
|
|
277
277
|
server_url: str,
|
|
278
278
|
client_type: int,
|
|
279
279
|
credentials: typing.Optional[tuple[str, str]],
|
|
280
|
-
version: str = "1.5.6.
|
|
280
|
+
version: str = "1.5.6.dev19",
|
|
281
281
|
*,
|
|
282
282
|
oauth_credentials: typing.Optional[_OAuthCredentials] = None,
|
|
283
283
|
):
|
|
@@ -377,6 +377,7 @@ _SETTINGS = {
|
|
|
377
377
|
"image_builder_version": _Setting(),
|
|
378
378
|
"strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
379
379
|
"sandbox_v2": _Setting(None, transform=_to_boolean),
|
|
380
|
+
"use_control_plane_sidecar_create": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
380
381
|
"snapshot_debug": _Setting(False, transform=_to_boolean),
|
|
381
382
|
"cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
|
|
382
383
|
# Used to indicate gVisor cuda-checkpoint automation.
|
|
@@ -653,7 +653,7 @@ class Function(
|
|
|
653
653
|
|
|
654
654
|
_call_generator: ___call_generator_spec
|
|
655
655
|
|
|
656
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
656
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
|
657
657
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
658
658
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result.
|
|
659
659
|
|
|
@@ -678,7 +678,7 @@ class Function(
|
|
|
678
678
|
"""
|
|
679
679
|
...
|
|
680
680
|
|
|
681
|
-
remote: __remote_spec[modal._functions.
|
|
681
|
+
remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
|
|
682
682
|
|
|
683
683
|
class __remote_gen_spec(typing_extensions.Protocol):
|
|
684
684
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -728,7 +728,7 @@ class Function(
|
|
|
728
728
|
"""
|
|
729
729
|
...
|
|
730
730
|
|
|
731
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
731
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
|
732
732
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
733
733
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
734
734
|
|
|
@@ -761,7 +761,7 @@ class Function(
|
|
|
761
761
|
"""
|
|
762
762
|
...
|
|
763
763
|
|
|
764
|
-
_experimental_spawn: ___experimental_spawn_spec[modal._functions.
|
|
764
|
+
_experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
|
|
765
765
|
|
|
766
766
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
|
|
767
767
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
|
|
@@ -769,7 +769,7 @@ class Function(
|
|
|
769
769
|
|
|
770
770
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
|
|
771
771
|
|
|
772
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
772
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
|
773
773
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
774
774
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
775
775
|
|
|
@@ -802,7 +802,7 @@ class Function(
|
|
|
802
802
|
"""
|
|
803
803
|
...
|
|
804
804
|
|
|
805
|
-
spawn: __spawn_spec[modal._functions.
|
|
805
|
+
spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
|
|
806
806
|
|
|
807
807
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
808
808
|
"""Return the inner Python object wrapped by this Modal Function.
|
|
@@ -2979,6 +2979,15 @@ class _SidecarContainer:
|
|
|
2979
2979
|
_MAIN_CONTAINER_NAME: str = "main"
|
|
2980
2980
|
|
|
2981
2981
|
|
|
2982
|
+
def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
|
|
2983
|
+
"""Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
|
|
2984
|
+
|
|
2985
|
+
Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
|
|
2986
|
+
Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
|
|
2987
|
+
"""
|
|
2988
|
+
return is_v2 and config.get("use_control_plane_sidecar_create")
|
|
2989
|
+
|
|
2990
|
+
|
|
2982
2991
|
class _SidecarManager:
|
|
2983
2992
|
"""Creates and manages sidecar containers in a Sandbox."""
|
|
2984
2993
|
|
|
@@ -3060,8 +3069,13 @@ class _SidecarManager:
|
|
|
3060
3069
|
"or `.snapshot_filesystem()`\n"
|
|
3061
3070
|
)
|
|
3062
3071
|
|
|
3063
|
-
|
|
3064
|
-
|
|
3072
|
+
env_dict, resolvable_secrets = _split_env_dict_and_resolvable_secrets(list(secrets or []))
|
|
3073
|
+
if env:
|
|
3074
|
+
# `env` takes precedence over environment variables from secrets
|
|
3075
|
+
env_dict |= env
|
|
3076
|
+
_validate_sandbox_env(env_dict)
|
|
3077
|
+
|
|
3078
|
+
hydrate_coros = [secret.hydrate(client=self._sandbox._client) for secret in resolvable_secrets] + [
|
|
3065
3079
|
volume.hydrate(client=self._sandbox._client) for _, volume in validated_volumes
|
|
3066
3080
|
]
|
|
3067
3081
|
await TaskContext.gather(*hydrate_coros)
|
|
@@ -3073,21 +3087,48 @@ class _SidecarManager:
|
|
|
3073
3087
|
# Relies on dicts being ordered (true as of Python 3.6).
|
|
3074
3088
|
volume_mounts = [_volume_to_mount_proto(path, volume) for path, volume in validated_volumes]
|
|
3075
3089
|
|
|
3076
|
-
|
|
3090
|
+
network_access = _build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist)
|
|
3091
|
+
pty_info = _Sandbox._default_pty_info() if pty else None
|
|
3092
|
+
|
|
3093
|
+
if _use_control_plane_sidecar_create(self._sandbox._is_v2):
|
|
3094
|
+
definition = api_pb2.Sandbox(
|
|
3095
|
+
entrypoint_args=list(args),
|
|
3096
|
+
image_id=image.object_id,
|
|
3097
|
+
secret_ids=[secret.object_id for secret in resolvable_secrets],
|
|
3098
|
+
workdir=workdir,
|
|
3099
|
+
volume_mounts=volume_mounts,
|
|
3100
|
+
network_access=network_access,
|
|
3101
|
+
pty_info=pty_info,
|
|
3102
|
+
)
|
|
3103
|
+
create_req = api_pb2.SandboxContainerCreateV2Request(
|
|
3104
|
+
sandbox_id=self._sandbox.object_id,
|
|
3105
|
+
container_name=name,
|
|
3106
|
+
definition=definition,
|
|
3107
|
+
ephemeral_secrets=api_pb2.StringMap(contents=env_dict) if env_dict else None,
|
|
3108
|
+
)
|
|
3109
|
+
client = self._sandbox._client
|
|
3110
|
+
assert client._auth_token_manager
|
|
3111
|
+
auth_token = await client._auth_token_manager.get_token()
|
|
3112
|
+
create_resp = await client.stub.SandboxContainerCreateV2(
|
|
3113
|
+
create_req, metadata=[("x-modal-auth-token", auth_token)]
|
|
3114
|
+
)
|
|
3115
|
+
else:
|
|
3116
|
+
task_id, command_router_client = await self._get_command_router()
|
|
3117
|
+
create_resp = await command_router_client.container_create(
|
|
3118
|
+
sr_pb2.TaskContainerCreateRequest(
|
|
3119
|
+
task_id=task_id,
|
|
3120
|
+
container_name=name,
|
|
3121
|
+
image_id=image.object_id,
|
|
3122
|
+
args=list(args),
|
|
3123
|
+
env=env_dict,
|
|
3124
|
+
workdir=workdir or "",
|
|
3125
|
+
secret_ids=[secret.object_id for secret in resolvable_secrets],
|
|
3126
|
+
volume_mounts=volume_mounts,
|
|
3127
|
+
network_access=network_access,
|
|
3128
|
+
pty_info=pty_info,
|
|
3129
|
+
)
|
|
3130
|
+
)
|
|
3077
3131
|
|
|
3078
|
-
create_req = sr_pb2.TaskContainerCreateRequest(
|
|
3079
|
-
task_id=task_id,
|
|
3080
|
-
container_name=name,
|
|
3081
|
-
image_id=image.object_id,
|
|
3082
|
-
args=list(args),
|
|
3083
|
-
env=env or {},
|
|
3084
|
-
workdir=workdir or "",
|
|
3085
|
-
secret_ids=[secret.object_id for secret in secrets],
|
|
3086
|
-
volume_mounts=volume_mounts,
|
|
3087
|
-
network_access=_build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist),
|
|
3088
|
-
pty_info=_Sandbox._default_pty_info() if pty else None,
|
|
3089
|
-
)
|
|
3090
|
-
create_resp = await command_router_client.container_create(create_req)
|
|
3091
3132
|
container_id = create_resp.container_id
|
|
3092
3133
|
container_name = create_resp.container_name or name
|
|
3093
3134
|
return _SidecarContainer(self._sandbox, container_id, container_name)
|
|
@@ -1222,6 +1222,14 @@ class _SidecarContainer:
|
|
|
1222
1222
|
"""
|
|
1223
1223
|
...
|
|
1224
1224
|
|
|
1225
|
+
def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
|
|
1226
|
+
"""Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
|
|
1227
|
+
|
|
1228
|
+
Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
|
|
1229
|
+
Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
|
|
1230
|
+
"""
|
|
1231
|
+
...
|
|
1232
|
+
|
|
1225
1233
|
class _SidecarManager:
|
|
1226
1234
|
"""Creates and manages sidecar containers in a Sandbox."""
|
|
1227
1235
|
def __init__(self, sandbox: _Sandbox) -> None:
|