modal 1.4.4.dev22__tar.gz → 1.5.0__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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- {modal-1.4.4.dev22 → modal-1.5.0}/PKG-INFO +1 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_environments.py +1 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/image.py +7 -7
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/utils.py +8 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/client.pyi +2 -10
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/functions.pyi +6 -6
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/sandbox.py +54 -11
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/sandbox.pyi +12 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/api_pb2.py +421 -406
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/api_pb2.pyi +21 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_version/__init__.py +1 -1
- {modal-1.4.4.dev22 → modal-1.5.0}/LICENSE +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/README.md +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/__main__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_billing.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_clustered_functions.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_container_entrypoint.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_function_variants.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_functions.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_grpc_client.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_image.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_ipython.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_load_context.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_location.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_logs.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_object.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_output/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_output/manager.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_output/pty.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_output/rich.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_output/status.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_partial_function.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_resolver.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_resources.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/execution_context.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_serialization.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_server.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_traceback.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_tunnel.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_tunnel.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_type_manager.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/logger.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_vendor/version.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_watcher.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/_workspace.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/app.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/app.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/billing.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/README.md +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/builder/base-images.json +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/call_graph.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/_download.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/_help.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/_traceback.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/app.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/billing.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/changelog.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/cluster.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/config.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/container.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/dashboard.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/dict.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/entry_point.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/environment.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/import_refs.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/launch.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/logo.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/profile.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/queues.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/run.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/secret.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/selector.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/shell.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/skills.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/token.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cli/volume.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/client.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cls.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/cls.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/config.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/container_process.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/container_process.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/dict.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/dict.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/environments.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/environments.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/exception.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/experimental/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/experimental/flash.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/experimental/flash.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/experimental/ipython.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/file_io.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/file_io.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/functions.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/image.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/image.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/io_streams.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/io_streams.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/mount.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/mount.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/network_file_system.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/network_file_system.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/object.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/object.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/output.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/parallel_map.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/parallel_map.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/partial_function.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/partial_function.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/proxy.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/proxy.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/py.typed +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/queue.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/queue.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/retries.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/runner.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/runner.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/running_app.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/sandbox_fs.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/sandbox_fs.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/schedule.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/scheduler_placement.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/secret.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/secret.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/server.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/server.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/serving.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/serving.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/snapshot.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/snapshot.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/stream_type.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/token_flow.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/token_flow.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/volume.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/volume.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/workspace.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal/workspace.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_docs/mdmd/types.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/__init__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/api_grpc.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/py.typed +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/modal_version/__main__.py +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/pyproject.toml +0 -0
- {modal-1.4.4.dev22 → modal-1.5.0}/setup.cfg +0 -0
|
@@ -104,7 +104,7 @@ class _EnvironmentManager:
|
|
|
104
104
|
MemberRole = Literal["viewer", "contributor"]
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
def _role_to_proto(role: str) -> api_pb2.EnvironmentRole.ValueType:
|
|
107
|
+
def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
|
|
108
108
|
match role:
|
|
109
109
|
case "viewer":
|
|
110
110
|
return api_pb2.ENVIRONMENT_ROLE_VIEWER
|
|
@@ -19,12 +19,12 @@ from modal_proto import api_pb2
|
|
|
19
19
|
|
|
20
20
|
from ._help import ModalGroup
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
IMAGE_NAMES_LIST_PAGE_SIZE = 100
|
|
23
|
+
IMAGE_NAMES_LIST_PAGE_DELAY_SECONDS = 1.0
|
|
24
24
|
|
|
25
25
|
image_cli = ModalGroup(name="image", help="Manage Images.")
|
|
26
|
-
|
|
27
|
-
image_cli.add_command(
|
|
26
|
+
image_names_cli = ModalGroup(name="names", help="Manage Modal Image names.")
|
|
27
|
+
image_cli.add_command(image_names_cli)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def _print_result_summary(count: int) -> None:
|
|
@@ -60,7 +60,7 @@ async def _iter_tag_pages(
|
|
|
60
60
|
api_pb2.ImageListTagsRequest(
|
|
61
61
|
environment_name=env,
|
|
62
62
|
tag_prefix=prefix,
|
|
63
|
-
max_objects=
|
|
63
|
+
max_objects=IMAGE_NAMES_LIST_PAGE_SIZE,
|
|
64
64
|
page_token=page_token,
|
|
65
65
|
)
|
|
66
66
|
)
|
|
@@ -70,10 +70,10 @@ async def _iter_tag_pages(
|
|
|
70
70
|
page_token = response.next_page_token
|
|
71
71
|
if not page_token:
|
|
72
72
|
return
|
|
73
|
-
await asyncio.sleep(
|
|
73
|
+
await asyncio.sleep(IMAGE_NAMES_LIST_PAGE_DELAY_SECONDS)
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
@
|
|
76
|
+
@image_names_cli.command("list", help="List named Images.")
|
|
77
77
|
@env_option
|
|
78
78
|
@click.option("--prefix", default="", help="Only include named image tags that start with this prefix.")
|
|
79
79
|
@click.option("--json", is_flag=True, default=False)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
2
|
import asyncio
|
|
3
3
|
import io
|
|
4
|
+
import re
|
|
4
5
|
import sys
|
|
5
6
|
from collections.abc import Sequence
|
|
6
7
|
from contextlib import nullcontext
|
|
@@ -126,6 +127,11 @@ def _plain(text: Text | str) -> str:
|
|
|
126
127
|
return text.plain if isinstance(text, Text) else text
|
|
127
128
|
|
|
128
129
|
|
|
130
|
+
def _col_name_to_json_key(name: str) -> str:
|
|
131
|
+
"""Convert a display column name like "App ID" to a snake_case JSON key like "app_id"."""
|
|
132
|
+
return re.sub(r"[^a-zA-Z0-9]+", "_", name).lower().strip("_")
|
|
133
|
+
|
|
134
|
+
|
|
129
135
|
def is_tty() -> bool:
|
|
130
136
|
return OutputManager.get().is_terminal
|
|
131
137
|
|
|
@@ -145,7 +151,8 @@ def display_table(
|
|
|
145
151
|
|
|
146
152
|
output = OutputManager.get()
|
|
147
153
|
if json:
|
|
148
|
-
|
|
154
|
+
json_keys = [_col_name_to_json_key(col_to_str(col)) for col in columns]
|
|
155
|
+
json_data = [{json_keys[i]: _plain(row[i]) for i in range(len(columns))} for row in rows]
|
|
149
156
|
output.print_json(dumps(json_data))
|
|
150
157
|
elif csv:
|
|
151
158
|
csv_buffer = io.StringIO()
|
|
@@ -31,11 +31,7 @@ class _Client:
|
|
|
31
31
|
client_type: int
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
34
|
-
self,
|
|
35
|
-
server_url: str,
|
|
36
|
-
client_type: int,
|
|
37
|
-
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.4.4.dev22",
|
|
34
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.5.0"
|
|
39
35
|
):
|
|
40
36
|
"""mdmd:hidden
|
|
41
37
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -201,11 +197,7 @@ class Client:
|
|
|
201
197
|
client_type: int
|
|
202
198
|
|
|
203
199
|
def __init__(
|
|
204
|
-
self,
|
|
205
|
-
server_url: str,
|
|
206
|
-
client_type: int,
|
|
207
|
-
credentials: typing.Optional[tuple[str, str]],
|
|
208
|
-
version: str = "1.4.4.dev22",
|
|
200
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.5.0"
|
|
209
201
|
):
|
|
210
202
|
"""mdmd:hidden
|
|
211
203
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -487,7 +487,7 @@ class Function(
|
|
|
487
487
|
|
|
488
488
|
_call_generator: ___call_generator_spec
|
|
489
489
|
|
|
490
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
490
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
491
491
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
492
492
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result.
|
|
493
493
|
|
|
@@ -512,7 +512,7 @@ class Function(
|
|
|
512
512
|
"""
|
|
513
513
|
...
|
|
514
514
|
|
|
515
|
-
remote: __remote_spec[modal._functions.
|
|
515
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
|
|
516
516
|
|
|
517
517
|
class __remote_gen_spec(typing_extensions.Protocol):
|
|
518
518
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -562,7 +562,7 @@ class Function(
|
|
|
562
562
|
"""
|
|
563
563
|
...
|
|
564
564
|
|
|
565
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
565
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
566
566
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
567
567
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
568
568
|
|
|
@@ -595,7 +595,7 @@ class Function(
|
|
|
595
595
|
"""
|
|
596
596
|
...
|
|
597
597
|
|
|
598
|
-
_experimental_spawn: ___experimental_spawn_spec[modal._functions.
|
|
598
|
+
_experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
599
599
|
|
|
600
600
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
|
|
601
601
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
|
|
@@ -603,7 +603,7 @@ class Function(
|
|
|
603
603
|
|
|
604
604
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
|
|
605
605
|
|
|
606
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
606
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
607
607
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
608
608
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
609
609
|
|
|
@@ -636,7 +636,7 @@ class Function(
|
|
|
636
636
|
"""
|
|
637
637
|
...
|
|
638
638
|
|
|
639
|
-
spawn: __spawn_spec[modal._functions.
|
|
639
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
640
640
|
|
|
641
641
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
642
642
|
"""Return the inner Python object wrapped by this Modal Function.
|
|
@@ -3,12 +3,13 @@ import asyncio
|
|
|
3
3
|
import builtins
|
|
4
4
|
import enum
|
|
5
5
|
import json
|
|
6
|
+
import logging
|
|
6
7
|
import os
|
|
7
8
|
import time
|
|
8
9
|
import typing
|
|
9
10
|
import uuid
|
|
10
11
|
import weakref
|
|
11
|
-
from collections.abc import AsyncGenerator, AsyncIterator, Collection, Sequence
|
|
12
|
+
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Collection, Sequence
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from pathlib import PurePosixPath
|
|
14
15
|
from typing import TYPE_CHECKING, Any, Literal, Union, overload
|
|
@@ -71,6 +72,42 @@ from .stream_type import StreamType
|
|
|
71
72
|
_default_image: _Image = _Image.debian_slim()
|
|
72
73
|
|
|
73
74
|
|
|
75
|
+
async def _gather_load_with_timings(
|
|
76
|
+
load_coros: Sequence[Awaitable[Any]],
|
|
77
|
+
) -> list[tuple[str, float]]:
|
|
78
|
+
"""Await all loader coroutines concurrently and return [(object_id, elapsed_seconds)] per load."""
|
|
79
|
+
timings: list[tuple[str, float]] = []
|
|
80
|
+
|
|
81
|
+
async def timed(coro: Awaitable[Any]) -> None:
|
|
82
|
+
start = time.monotonic()
|
|
83
|
+
obj = await coro
|
|
84
|
+
timings.append((obj.object_id, time.monotonic() - start))
|
|
85
|
+
|
|
86
|
+
await asyncio.gather(*(timed(c) for c in load_coros))
|
|
87
|
+
return timings
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _format_sandbox_create_timing_log(
|
|
91
|
+
sandbox_id: str,
|
|
92
|
+
total_seconds: float,
|
|
93
|
+
rpc_seconds: float,
|
|
94
|
+
dep_timings: Sequence[tuple[str, float]],
|
|
95
|
+
) -> str:
|
|
96
|
+
"""Format the Sandbox create debug log line, listing the slowest deps first."""
|
|
97
|
+
if dep_timings:
|
|
98
|
+
deps_sorted = sorted(dep_timings, key=lambda t: t[1], reverse=True)
|
|
99
|
+
shown = deps_sorted[:10]
|
|
100
|
+
dep_summary = ", ".join(f"{label}: {elapsed:.2f}s" for label, elapsed in shown)
|
|
101
|
+
if len(deps_sorted) > 10:
|
|
102
|
+
dep_summary += f", +{len(deps_sorted) - 10} more"
|
|
103
|
+
else:
|
|
104
|
+
dep_summary = "none"
|
|
105
|
+
return (
|
|
106
|
+
f"Sandbox {sandbox_id} created in {total_seconds:.2f}s "
|
|
107
|
+
f"(create rpc: {rpc_seconds:.2f}s; dependencies: {dep_summary})"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
74
111
|
# The maximum number of bytes that can be passed to an exec on Linux.
|
|
75
112
|
# Though this is technically a 'server side' limit, it is unlikely to change.
|
|
76
113
|
# getconf ARG_MAX will show this value on a host.
|
|
@@ -349,6 +386,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
349
386
|
pty_info = _Sandbox._default_pty_info()
|
|
350
387
|
|
|
351
388
|
async def _load(self: _Sandbox, resolver: Resolver, load_context: LoadContext, _existing_object_id: str | None):
|
|
389
|
+
load_start = time.monotonic()
|
|
352
390
|
# An already-hydrated image (e.g. one returned by
|
|
353
391
|
# `Sandbox.snapshot_directory`) is skipped — there's nothing to load.
|
|
354
392
|
dep_tasks: list = []
|
|
@@ -365,8 +403,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
365
403
|
dep_tasks.append(resolver.load(cloud_bucket_mount.secret, load_context))
|
|
366
404
|
if proxy:
|
|
367
405
|
dep_tasks.append(resolver.load(proxy, load_context))
|
|
368
|
-
if dep_tasks
|
|
369
|
-
await asyncio.gather(*dep_tasks)
|
|
406
|
+
dep_timings = await _gather_load_with_timings(dep_tasks) if dep_tasks else []
|
|
370
407
|
|
|
371
408
|
# Validate that the same volume (by object_id) isn't mounted at multiple paths
|
|
372
409
|
validate_volumes_by_object_id(validated_volumes)
|
|
@@ -444,10 +481,16 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
444
481
|
create_req = api_pb2.SandboxCreateRequest(
|
|
445
482
|
app_id=load_context.app_id, definition=definition, tags=tag_protos
|
|
446
483
|
)
|
|
484
|
+
rpc_start = time.monotonic()
|
|
447
485
|
create_resp = await load_context.client.stub.SandboxCreate(create_req)
|
|
486
|
+
rpc_elapsed = time.monotonic() - rpc_start
|
|
448
487
|
sandbox_id = create_resp.sandbox_id
|
|
449
488
|
self._hydrate(sandbox_id, load_context.client, None)
|
|
450
489
|
|
|
490
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
491
|
+
total_elapsed = time.monotonic() - load_start
|
|
492
|
+
logger.debug(_format_sandbox_create_timing_log(sandbox_id, total_elapsed, rpc_elapsed, dep_timings))
|
|
493
|
+
|
|
451
494
|
return _Sandbox._from_loader(_load, "Sandbox()", load_context_overrides=LoadContext.empty())
|
|
452
495
|
|
|
453
496
|
@staticmethod
|
|
@@ -864,6 +907,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
864
907
|
)
|
|
865
908
|
|
|
866
909
|
async def _load(self: _Sandbox, resolver: Resolver, load_context: LoadContext, _existing_object_id: str | None):
|
|
910
|
+
load_start = time.monotonic()
|
|
867
911
|
dep_tasks: list = []
|
|
868
912
|
if not image._is_hydrated:
|
|
869
913
|
dep_tasks.append(resolver.load(image, load_context))
|
|
@@ -874,8 +918,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
874
918
|
for _, cloud_bucket_mount in cloud_bucket_mounts:
|
|
875
919
|
if cloud_bucket_mount.secret:
|
|
876
920
|
dep_tasks.append(resolver.load(cloud_bucket_mount.secret, load_context))
|
|
877
|
-
if dep_tasks
|
|
878
|
-
await asyncio.gather(*dep_tasks)
|
|
921
|
+
dep_timings = await _gather_load_with_timings(dep_tasks) if dep_tasks else []
|
|
879
922
|
|
|
880
923
|
validate_volumes_by_object_id(validated_volumes)
|
|
881
924
|
|
|
@@ -909,9 +952,11 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
909
952
|
create_req = api_pb2.SandboxCreateV2Request(app_id=load_context.app_id, definition=definition)
|
|
910
953
|
assert load_context.client._auth_token_manager
|
|
911
954
|
auth_token = await load_context.client._auth_token_manager.get_token()
|
|
955
|
+
rpc_start = time.monotonic()
|
|
912
956
|
create_resp = await load_context.client.stub.SandboxCreateV2(
|
|
913
957
|
create_req, metadata=[("x-modal-auth-token", auth_token)]
|
|
914
958
|
)
|
|
959
|
+
rpc_elapsed = time.monotonic() - rpc_start
|
|
915
960
|
sandbox_id = create_resp.sandbox_id
|
|
916
961
|
self._hydrate(sandbox_id, load_context.client, None)
|
|
917
962
|
self._is_v2 = True
|
|
@@ -922,6 +967,10 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
922
967
|
for t in create_resp.tunnels
|
|
923
968
|
}
|
|
924
969
|
|
|
970
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
971
|
+
total_elapsed = time.monotonic() - load_start
|
|
972
|
+
logger.debug(_format_sandbox_create_timing_log(sandbox_id, total_elapsed, rpc_elapsed, dep_timings))
|
|
973
|
+
|
|
925
974
|
obj = _Sandbox._from_loader(_load, "Sandbox()", load_context_overrides=LoadContext.empty())
|
|
926
975
|
|
|
927
976
|
app_id: str | None = None
|
|
@@ -1247,8 +1296,6 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1247
1296
|
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1248
1297
|
```
|
|
1249
1298
|
"""
|
|
1250
|
-
self._ensure_v1("mount_image")
|
|
1251
|
-
|
|
1252
1299
|
if not isinstance(image, _Image):
|
|
1253
1300
|
raise TypeError(f"Sandbox.mount_image(image=...) expects an Image object, got {image!r}")
|
|
1254
1301
|
|
|
@@ -1299,8 +1346,6 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1299
1346
|
path: Absolute mount point directory to unmount.
|
|
1300
1347
|
|
|
1301
1348
|
"""
|
|
1302
|
-
self._ensure_v1("unmount_image")
|
|
1303
|
-
|
|
1304
1349
|
task_id = await self._get_task_id()
|
|
1305
1350
|
command_router_client = await self._get_command_router_client(task_id)
|
|
1306
1351
|
|
|
@@ -1344,8 +1389,6 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
1344
1389
|
sandbox_session_2.filesystem.list_files("/user_project")
|
|
1345
1390
|
```
|
|
1346
1391
|
"""
|
|
1347
|
-
self._ensure_v1("snapshot_directory")
|
|
1348
|
-
|
|
1349
1392
|
wire_ttl_seconds = _ttl_to_wire_ttl(ttl)
|
|
1350
1393
|
|
|
1351
1394
|
task_id = await self._get_task_id()
|
|
@@ -29,6 +29,18 @@ import pathlib
|
|
|
29
29
|
import typing
|
|
30
30
|
import typing_extensions
|
|
31
31
|
|
|
32
|
+
async def _gather_load_with_timings(
|
|
33
|
+
load_coros: collections.abc.Sequence[collections.abc.Awaitable[typing.Any]],
|
|
34
|
+
) -> list[tuple[str, float]]:
|
|
35
|
+
"""Await all loader coroutines concurrently and return [(object_id, elapsed_seconds)] per load."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
def _format_sandbox_create_timing_log(
|
|
39
|
+
sandbox_id: str, total_seconds: float, rpc_seconds: float, dep_timings: collections.abc.Sequence[tuple[str, float]]
|
|
40
|
+
) -> str:
|
|
41
|
+
"""Format the Sandbox create debug log line, listing the slowest deps first."""
|
|
42
|
+
...
|
|
43
|
+
|
|
32
44
|
def _ttl_to_wire_ttl(ttl: typing.Optional[int]) -> int:
|
|
33
45
|
"""Convert a TTL value to the wire format, validating the input."""
|
|
34
46
|
...
|