modAL 1.2.3.dev9__tar.gz → 1.2.3.dev11__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.2.3.dev9 → modal-1.2.3.dev11}/PKG-INFO +1 -1
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/grpc_utils.py +57 -4
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/client.pyi +2 -2
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/io_streams.py +101 -32
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/io_streams.pyi +30 -5
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/sandbox.py +2 -6
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api.proto +13 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api_grpc.py +1 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api_pb2.py +1032 -1021
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api_pb2.pyi +29 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_version/__init__.py +1 -1
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/LICENSE +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/README.md +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/__main__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_billing.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_clustered_functions.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_functions.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_grpc_client.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_ipython.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_load_context.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_location.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_object.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_output.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_partial_function.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_pty.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_resolver.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_resources.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_serialization.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_traceback.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_tunnel.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_tunnel.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_type_manager.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/blob_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/logger.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/_watcher.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/app.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/app.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/billing.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/README.md +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/builder/base-images.json +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/call_graph.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/_download.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/_traceback.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/app.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/cluster.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/config.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/container.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/dict.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/entry_point.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/environment.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/import_refs.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/launch.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/profile.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/queues.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/run.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/secret.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/token.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/utils.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cli/volume.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/client.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cls.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/cls.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/config.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/container_process.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/container_process.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/dict.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/dict.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/environments.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/environments.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/exception.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/experimental/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/experimental/flash.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/experimental/flash.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/experimental/ipython.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/file_io.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/file_io.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/functions.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/functions.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/gpu.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/image.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/image.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/mount.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/mount.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/network_file_system.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/network_file_system.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/object.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/object.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/output.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/parallel_map.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/parallel_map.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/partial_function.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/partial_function.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/proxy.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/proxy.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/py.typed +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/queue.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/queue.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/retries.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/runner.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/runner.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/running_app.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/sandbox.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/schedule.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/scheduler_placement.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/secret.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/secret.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/serving.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/serving.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/snapshot.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/snapshot.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/stream_type.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/token_flow.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/token_flow.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/volume.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal/volume.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/__init__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/py.typed +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/modal_version/__main__.py +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/pyproject.toml +0 -0
- {modal-1.2.3.dev9 → modal-1.2.3.dev11}/setup.cfg +0 -0
|
@@ -9,7 +9,8 @@ import urllib.parse
|
|
|
9
9
|
import uuid
|
|
10
10
|
from collections.abc import AsyncIterator
|
|
11
11
|
from dataclasses import dataclass, field
|
|
12
|
-
from
|
|
12
|
+
from functools import cache
|
|
13
|
+
from typing import Any, Optional, Sequence, TypeVar
|
|
13
14
|
|
|
14
15
|
import grpclib.client
|
|
15
16
|
import grpclib.config
|
|
@@ -17,11 +18,14 @@ import grpclib.events
|
|
|
17
18
|
import grpclib.protocol
|
|
18
19
|
import grpclib.stream
|
|
19
20
|
from google.protobuf.message import Message
|
|
21
|
+
from google.protobuf.symbol_database import SymbolDatabase
|
|
20
22
|
from grpclib import GRPCError, Status
|
|
23
|
+
from grpclib.encoding.base import StatusDetailsCodecBase
|
|
21
24
|
from grpclib.exceptions import StreamTerminatedError
|
|
22
25
|
from grpclib.protocol import H2Protocol
|
|
23
26
|
|
|
24
27
|
from modal.exception import AuthError, ConnectionError
|
|
28
|
+
from modal_proto import api_pb2
|
|
25
29
|
from modal_version import __version__
|
|
26
30
|
|
|
27
31
|
from .._traceback import suppress_tb_frames
|
|
@@ -107,6 +111,56 @@ class ConnectionManager:
|
|
|
107
111
|
self._channels.clear()
|
|
108
112
|
|
|
109
113
|
|
|
114
|
+
@cache
|
|
115
|
+
def _sym_db() -> SymbolDatabase:
|
|
116
|
+
from google.protobuf.symbol_database import Default
|
|
117
|
+
|
|
118
|
+
return Default()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CustomProtoStatusDetailsCodec(StatusDetailsCodecBase):
|
|
122
|
+
"""grpclib compatible details codec.
|
|
123
|
+
|
|
124
|
+
The server can encode the details using `google.rpc.Status` using grpclib's default codec and this custom codec
|
|
125
|
+
can decode it into a `api_pb2.RPCStatus`.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def encode(
|
|
129
|
+
self,
|
|
130
|
+
status: Status,
|
|
131
|
+
message: Optional[str],
|
|
132
|
+
details: Optional[Sequence[Message]],
|
|
133
|
+
) -> bytes:
|
|
134
|
+
details_proto = api_pb2.RPCStatus(code=status.value, message=message or "")
|
|
135
|
+
if details is not None:
|
|
136
|
+
for detail in details:
|
|
137
|
+
detail_container = details_proto.details.add()
|
|
138
|
+
detail_container.Pack(detail)
|
|
139
|
+
return details_proto.SerializeToString()
|
|
140
|
+
|
|
141
|
+
def decode(
|
|
142
|
+
self,
|
|
143
|
+
status: Status,
|
|
144
|
+
message: Optional[str],
|
|
145
|
+
data: bytes,
|
|
146
|
+
) -> Any:
|
|
147
|
+
sym_db = _sym_db()
|
|
148
|
+
details_proto = api_pb2.RPCStatus.FromString(data)
|
|
149
|
+
|
|
150
|
+
details = []
|
|
151
|
+
for detail_container in details_proto.details:
|
|
152
|
+
# If we do not know how to decode an emssage, we'll ignore it.
|
|
153
|
+
with contextlib.suppress(Exception):
|
|
154
|
+
msg_type = sym_db.GetSymbol(detail_container.TypeName())
|
|
155
|
+
detail = msg_type()
|
|
156
|
+
detail_container.Unpack(detail)
|
|
157
|
+
details.append(detail)
|
|
158
|
+
return details
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
custom_detail_codec = CustomProtoStatusDetailsCodec()
|
|
162
|
+
|
|
163
|
+
|
|
110
164
|
def create_channel(
|
|
111
165
|
server_url: str,
|
|
112
166
|
metadata: dict[str, str] = {},
|
|
@@ -125,7 +179,7 @@ def create_channel(
|
|
|
125
179
|
)
|
|
126
180
|
|
|
127
181
|
if o.scheme == "unix":
|
|
128
|
-
channel = grpclib.client.Channel(path=o.path, config=config)
|
|
182
|
+
channel = grpclib.client.Channel(path=o.path, config=config, status_details_codec=custom_detail_codec)
|
|
129
183
|
elif o.scheme in ("http", "https"):
|
|
130
184
|
target = o.netloc
|
|
131
185
|
parts = target.split(":")
|
|
@@ -133,7 +187,7 @@ def create_channel(
|
|
|
133
187
|
ssl = o.scheme.endswith("s")
|
|
134
188
|
host = parts[0]
|
|
135
189
|
port = int(parts[1]) if len(parts) == 2 else 443 if ssl else 80
|
|
136
|
-
channel = grpclib.client.Channel(host, port, ssl=ssl, config=config)
|
|
190
|
+
channel = grpclib.client.Channel(host, port, ssl=ssl, config=config, status_details_codec=custom_detail_codec)
|
|
137
191
|
else:
|
|
138
192
|
raise Exception(f"Unknown scheme: {o.scheme}")
|
|
139
193
|
|
|
@@ -258,7 +312,6 @@ async def _retry_transient_errors(
|
|
|
258
312
|
raise AuthError(exc.message)
|
|
259
313
|
else:
|
|
260
314
|
raise exc
|
|
261
|
-
|
|
262
315
|
if retry.max_retries is not None and n_retries >= retry.max_retries:
|
|
263
316
|
final_attempt = True
|
|
264
317
|
elif total_deadline is not None and time.time() + delay + retry.attempt_timeout_floor >= total_deadline:
|
|
@@ -33,7 +33,7 @@ class _Client:
|
|
|
33
33
|
server_url: str,
|
|
34
34
|
client_type: int,
|
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.2.3.
|
|
36
|
+
version: str = "1.2.3.dev11",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -164,7 +164,7 @@ class Client:
|
|
|
164
164
|
server_url: str,
|
|
165
165
|
client_type: int,
|
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.2.3.
|
|
167
|
+
version: str = "1.2.3.dev11",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
2
|
import asyncio
|
|
3
3
|
import codecs
|
|
4
|
+
import contextlib
|
|
5
|
+
import sys
|
|
4
6
|
import time
|
|
5
7
|
from collections.abc import AsyncGenerator, AsyncIterator
|
|
6
8
|
from dataclasses import dataclass
|
|
@@ -377,7 +379,7 @@ async def _stdio_stream_from_command_router(
|
|
|
377
379
|
return
|
|
378
380
|
|
|
379
381
|
|
|
380
|
-
class _BytesStreamReaderThroughCommandRouter
|
|
382
|
+
class _BytesStreamReaderThroughCommandRouter:
|
|
381
383
|
"""
|
|
382
384
|
StreamReader implementation that will read directly from the worker that
|
|
383
385
|
hosts the sandbox.
|
|
@@ -396,27 +398,27 @@ class _BytesStreamReaderThroughCommandRouter(Generic[T]):
|
|
|
396
398
|
def file_descriptor(self) -> int:
|
|
397
399
|
return self._params.file_descriptor
|
|
398
400
|
|
|
399
|
-
async def read(self) ->
|
|
401
|
+
async def read(self) -> bytes:
|
|
400
402
|
data_bytes = b""
|
|
401
403
|
async for part in self:
|
|
402
404
|
data_bytes += cast(bytes, part)
|
|
403
|
-
return
|
|
405
|
+
return data_bytes
|
|
404
406
|
|
|
405
|
-
def __aiter__(self) -> AsyncIterator[
|
|
407
|
+
def __aiter__(self) -> AsyncIterator[bytes]:
|
|
406
408
|
return self
|
|
407
409
|
|
|
408
|
-
async def __anext__(self) ->
|
|
410
|
+
async def __anext__(self) -> bytes:
|
|
409
411
|
if self._stream is None:
|
|
410
412
|
self._stream = _stdio_stream_from_command_router(self._params)
|
|
411
413
|
# This raises StopAsyncIteration if the stream is at EOF.
|
|
412
|
-
return
|
|
414
|
+
return await self._stream.__anext__()
|
|
413
415
|
|
|
414
416
|
async def aclose(self):
|
|
415
417
|
if self._stream:
|
|
416
418
|
await self._stream.aclose()
|
|
417
419
|
|
|
418
420
|
|
|
419
|
-
class _TextStreamReaderThroughCommandRouter
|
|
421
|
+
class _TextStreamReaderThroughCommandRouter:
|
|
420
422
|
"""
|
|
421
423
|
StreamReader implementation that will read directly from the worker
|
|
422
424
|
that hosts the sandbox.
|
|
@@ -437,16 +439,16 @@ class _TextStreamReaderThroughCommandRouter(Generic[T]):
|
|
|
437
439
|
def file_descriptor(self) -> int:
|
|
438
440
|
return self._params.file_descriptor
|
|
439
441
|
|
|
440
|
-
async def read(self) ->
|
|
442
|
+
async def read(self) -> str:
|
|
441
443
|
data_str = ""
|
|
442
444
|
async for part in self:
|
|
443
445
|
data_str += cast(str, part)
|
|
444
|
-
return
|
|
446
|
+
return data_str
|
|
445
447
|
|
|
446
|
-
def __aiter__(self) -> AsyncIterator[
|
|
448
|
+
def __aiter__(self) -> AsyncIterator[str]:
|
|
447
449
|
return self
|
|
448
450
|
|
|
449
|
-
async def __anext__(self) ->
|
|
451
|
+
async def __anext__(self) -> str:
|
|
450
452
|
if self._stream is None:
|
|
451
453
|
bytes_stream = _stdio_stream_from_command_router(self._params)
|
|
452
454
|
if self._by_line:
|
|
@@ -454,13 +456,88 @@ class _TextStreamReaderThroughCommandRouter(Generic[T]):
|
|
|
454
456
|
else:
|
|
455
457
|
self._stream = _decode_bytes_stream_to_str(bytes_stream)
|
|
456
458
|
# This raises StopAsyncIteration if the stream is at EOF.
|
|
457
|
-
return
|
|
459
|
+
return await self._stream.__anext__()
|
|
458
460
|
|
|
459
461
|
async def aclose(self):
|
|
460
462
|
if self._stream:
|
|
461
463
|
await self._stream.aclose()
|
|
462
464
|
|
|
463
465
|
|
|
466
|
+
class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
|
|
467
|
+
"""
|
|
468
|
+
StreamReader implementation for StreamType.STDOUT when using the task command router.
|
|
469
|
+
|
|
470
|
+
This mirrors the behavior from the server-backed implementation: the stream is printed to
|
|
471
|
+
the local stdout immediately and is not readable via StreamReader methods.
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
_reader: Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
|
|
475
|
+
|
|
476
|
+
def __init__(
|
|
477
|
+
self,
|
|
478
|
+
reader: Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter],
|
|
479
|
+
) -> None:
|
|
480
|
+
self._reader = reader
|
|
481
|
+
self._task: Optional[asyncio.Task[None]] = None
|
|
482
|
+
self._closed = False
|
|
483
|
+
# Kick off a background task that reads from the underlying text stream and prints to stdout.
|
|
484
|
+
self._start_printing_task()
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def file_descriptor(self) -> int:
|
|
488
|
+
return self._reader.file_descriptor
|
|
489
|
+
|
|
490
|
+
def _start_printing_task(self) -> None:
|
|
491
|
+
async def _run():
|
|
492
|
+
try:
|
|
493
|
+
|
|
494
|
+
def print_text_part(part: Union[str, bytes]) -> None:
|
|
495
|
+
assert isinstance(part, str)
|
|
496
|
+
print(cast(str, part), end="")
|
|
497
|
+
|
|
498
|
+
def print_bytes_part(part: Union[str, bytes]) -> None:
|
|
499
|
+
assert isinstance(part, bytes)
|
|
500
|
+
sys.stdout.buffer.write(cast(bytes, part))
|
|
501
|
+
sys.stdout.buffer.flush()
|
|
502
|
+
|
|
503
|
+
if isinstance(self._reader, _BytesStreamReaderThroughCommandRouter):
|
|
504
|
+
print_part = print_bytes_part
|
|
505
|
+
elif isinstance(self._reader, _TextStreamReaderThroughCommandRouter):
|
|
506
|
+
print_part = print_text_part
|
|
507
|
+
else:
|
|
508
|
+
raise ValueError("Unsupported reader type")
|
|
509
|
+
|
|
510
|
+
async for part in self._reader:
|
|
511
|
+
print_part(part)
|
|
512
|
+
except Exception as e:
|
|
513
|
+
logger.exception(f"Error printing stream: {e}")
|
|
514
|
+
finally:
|
|
515
|
+
closed, self._closed = self._closed, True
|
|
516
|
+
if not closed:
|
|
517
|
+
await self._reader.aclose()
|
|
518
|
+
|
|
519
|
+
self._task = asyncio.create_task(_run())
|
|
520
|
+
|
|
521
|
+
async def read(self) -> T:
|
|
522
|
+
raise InvalidError("Output can only be retrieved using the PIPE stream type.")
|
|
523
|
+
|
|
524
|
+
def __aiter__(self) -> AsyncIterator[T]:
|
|
525
|
+
raise InvalidError("Output can only be retrieved using the PIPE stream type.")
|
|
526
|
+
|
|
527
|
+
async def __anext__(self) -> T:
|
|
528
|
+
raise InvalidError("Output can only be retrieved using the PIPE stream type.")
|
|
529
|
+
|
|
530
|
+
async def aclose(self):
|
|
531
|
+
if self._task is not None:
|
|
532
|
+
self._task.cancel()
|
|
533
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
534
|
+
await self._task
|
|
535
|
+
self._task = None
|
|
536
|
+
closed, self._closed = self._closed, True
|
|
537
|
+
if not closed:
|
|
538
|
+
await self._reader.aclose()
|
|
539
|
+
|
|
540
|
+
|
|
464
541
|
class _DevnullStreamReader(Generic[T]):
|
|
465
542
|
"""StreamReader implementation for a stream configured with
|
|
466
543
|
StreamType.DEVNULL. Throws an error if read or any other method is
|
|
@@ -499,6 +576,7 @@ class _StreamReader(Generic[T]):
|
|
|
499
576
|
_DevnullStreamReader,
|
|
500
577
|
_TextStreamReaderThroughCommandRouter,
|
|
501
578
|
_BytesStreamReaderThroughCommandRouter,
|
|
579
|
+
_StdoutPrintingStreamReaderThroughCommandRouter,
|
|
502
580
|
]
|
|
503
581
|
|
|
504
582
|
def __init__(
|
|
@@ -523,35 +601,26 @@ class _StreamReader(Generic[T]):
|
|
|
523
601
|
file_descriptor, object_id, object_type, client, stream_type, text, by_line, deadline
|
|
524
602
|
)
|
|
525
603
|
else:
|
|
526
|
-
# The only reason task_id is optional is because StreamReader is
|
|
527
|
-
#
|
|
528
|
-
# when the StreamReader is created.
|
|
604
|
+
# The only reason task_id is optional is because StreamReader is also used for sandbox
|
|
605
|
+
# logs, which don't have a task ID available when the StreamReader is created.
|
|
529
606
|
assert task_id is not None
|
|
530
607
|
assert object_type == "container_process"
|
|
531
608
|
if stream_type == StreamType.DEVNULL:
|
|
532
609
|
self._impl = _DevnullStreamReader(file_descriptor)
|
|
533
610
|
else:
|
|
534
611
|
assert stream_type == StreamType.PIPE or stream_type == StreamType.STDOUT
|
|
535
|
-
# TODO(saltzm): The original implementation of STDOUT StreamType in
|
|
536
|
-
# _StreamReaderThroughServer prints to stdout immediately. This doesn't match
|
|
537
|
-
# python subprocess.run, which uses None to print to stdout immediately, and uses
|
|
538
|
-
# STDOUT as an argument to stderr to redirect stderr to the stdout stream. We should
|
|
539
|
-
# implement the old behavior here before moving out of beta, but after that
|
|
540
|
-
# we should consider changing the API to match python subprocess.run. I don't expect
|
|
541
|
-
# many customers are using this in any case, so I think it's fine to leave this
|
|
542
|
-
# unimplemented for now.
|
|
543
|
-
if stream_type == StreamType.STDOUT:
|
|
544
|
-
raise NotImplementedError(
|
|
545
|
-
"Currently the STDOUT stream type is not supported when using exec "
|
|
546
|
-
"through a task command router, which is currently in beta."
|
|
547
|
-
)
|
|
548
612
|
params = _StreamReaderThroughCommandRouterParams(
|
|
549
613
|
file_descriptor, task_id, object_id, command_router_client, deadline
|
|
550
614
|
)
|
|
551
615
|
if text:
|
|
552
|
-
|
|
616
|
+
reader = _TextStreamReaderThroughCommandRouter(params, by_line)
|
|
617
|
+
else:
|
|
618
|
+
reader = _BytesStreamReaderThroughCommandRouter(params)
|
|
619
|
+
|
|
620
|
+
if stream_type == StreamType.STDOUT:
|
|
621
|
+
self._impl = _StdoutPrintingStreamReaderThroughCommandRouter(reader)
|
|
553
622
|
else:
|
|
554
|
-
self._impl =
|
|
623
|
+
self._impl = reader
|
|
555
624
|
|
|
556
625
|
@property
|
|
557
626
|
def file_descriptor(self) -> int:
|
|
@@ -560,7 +629,7 @@ class _StreamReader(Generic[T]):
|
|
|
560
629
|
|
|
561
630
|
async def read(self) -> T:
|
|
562
631
|
"""Fetch the entire contents of the stream until EOF."""
|
|
563
|
-
return await self._impl.read()
|
|
632
|
+
return cast(T, await self._impl.read())
|
|
564
633
|
|
|
565
634
|
# TODO(saltzm): I'd prefer to have the implementation classes only implement __aiter__
|
|
566
635
|
# and have them return generator functions directly, but synchronicity doesn't let us
|
|
@@ -572,7 +641,7 @@ class _StreamReader(Generic[T]):
|
|
|
572
641
|
|
|
573
642
|
async def __anext__(self) -> T:
|
|
574
643
|
"""mdmd:hidden"""
|
|
575
|
-
return await self._impl.__anext__()
|
|
644
|
+
return cast(T, await self._impl.__anext__())
|
|
576
645
|
|
|
577
646
|
async def aclose(self):
|
|
578
647
|
"""mdmd:hidden"""
|
|
@@ -127,7 +127,7 @@ def _stdio_stream_from_command_router(
|
|
|
127
127
|
"""Stream raw bytes from the router client."""
|
|
128
128
|
...
|
|
129
129
|
|
|
130
|
-
class _BytesStreamReaderThroughCommandRouter
|
|
130
|
+
class _BytesStreamReaderThroughCommandRouter:
|
|
131
131
|
"""StreamReader implementation that will read directly from the worker that
|
|
132
132
|
hosts the sandbox.
|
|
133
133
|
|
|
@@ -139,12 +139,12 @@ class _BytesStreamReaderThroughCommandRouter(typing.Generic[T]):
|
|
|
139
139
|
|
|
140
140
|
@property
|
|
141
141
|
def file_descriptor(self) -> int: ...
|
|
142
|
-
async def read(self) ->
|
|
143
|
-
def __aiter__(self) -> collections.abc.AsyncIterator[
|
|
144
|
-
async def __anext__(self) ->
|
|
142
|
+
async def read(self) -> bytes: ...
|
|
143
|
+
def __aiter__(self) -> collections.abc.AsyncIterator[bytes]: ...
|
|
144
|
+
async def __anext__(self) -> bytes: ...
|
|
145
145
|
async def aclose(self): ...
|
|
146
146
|
|
|
147
|
-
class _TextStreamReaderThroughCommandRouter
|
|
147
|
+
class _TextStreamReaderThroughCommandRouter:
|
|
148
148
|
"""StreamReader implementation that will read directly from the worker
|
|
149
149
|
that hosts the sandbox.
|
|
150
150
|
|
|
@@ -156,6 +156,29 @@ class _TextStreamReaderThroughCommandRouter(typing.Generic[T]):
|
|
|
156
156
|
|
|
157
157
|
@property
|
|
158
158
|
def file_descriptor(self) -> int: ...
|
|
159
|
+
async def read(self) -> str: ...
|
|
160
|
+
def __aiter__(self) -> collections.abc.AsyncIterator[str]: ...
|
|
161
|
+
async def __anext__(self) -> str: ...
|
|
162
|
+
async def aclose(self): ...
|
|
163
|
+
|
|
164
|
+
class _StdoutPrintingStreamReaderThroughCommandRouter(typing.Generic[T]):
|
|
165
|
+
"""StreamReader implementation for StreamType.STDOUT when using the task command router.
|
|
166
|
+
|
|
167
|
+
This mirrors the behavior from the server-backed implementation: the stream is printed to
|
|
168
|
+
the local stdout immediately and is not readable via StreamReader methods.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
_reader: typing.Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self, reader: typing.Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def file_descriptor(self) -> int: ...
|
|
181
|
+
def _start_printing_task(self) -> None: ...
|
|
159
182
|
async def read(self) -> T: ...
|
|
160
183
|
def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
|
|
161
184
|
async def __anext__(self) -> T: ...
|
|
@@ -189,6 +212,7 @@ class _StreamReader(typing.Generic[T]):
|
|
|
189
212
|
_DevnullStreamReader,
|
|
190
213
|
_TextStreamReaderThroughCommandRouter,
|
|
191
214
|
_BytesStreamReaderThroughCommandRouter,
|
|
215
|
+
_StdoutPrintingStreamReaderThroughCommandRouter,
|
|
192
216
|
]
|
|
193
217
|
|
|
194
218
|
def __init__(
|
|
@@ -357,6 +381,7 @@ class StreamReader(typing.Generic[T]):
|
|
|
357
381
|
_DevnullStreamReader,
|
|
358
382
|
_TextStreamReaderThroughCommandRouter,
|
|
359
383
|
_BytesStreamReaderThroughCommandRouter,
|
|
384
|
+
_StdoutPrintingStreamReaderThroughCommandRouter,
|
|
360
385
|
]
|
|
361
386
|
|
|
362
387
|
def __init__(
|
|
@@ -928,12 +928,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
928
928
|
elif stdout == StreamType.DEVNULL:
|
|
929
929
|
stdout_config = sr_pb2.TaskExecStdoutConfig.TASK_EXEC_STDOUT_CONFIG_DEVNULL
|
|
930
930
|
elif stdout == StreamType.STDOUT:
|
|
931
|
-
#
|
|
932
|
-
|
|
933
|
-
raise NotImplementedError(
|
|
934
|
-
"Currently the STDOUT stream type is not supported when using exec "
|
|
935
|
-
"through a task command router, which is currently in beta."
|
|
936
|
-
)
|
|
931
|
+
# Stream stdout to the client so that it can be printed locally in the reader.
|
|
932
|
+
stdout_config = sr_pb2.TaskExecStdoutConfig.TASK_EXEC_STDOUT_CONFIG_PIPE
|
|
937
933
|
else:
|
|
938
934
|
raise ValueError("Unsupported StreamType for stdout")
|
|
939
935
|
|
|
@@ -4,6 +4,7 @@ option go_package = "github.com/modal-labs/modal/go/proto";
|
|
|
4
4
|
|
|
5
5
|
package modal.client;
|
|
6
6
|
|
|
7
|
+
import "google/protobuf/any.proto";
|
|
7
8
|
import "google/protobuf/empty.proto";
|
|
8
9
|
import "google/protobuf/struct.proto";
|
|
9
10
|
import "google/protobuf/timestamp.proto";
|
|
@@ -2045,6 +2046,7 @@ message GPUConfig {
|
|
|
2045
2046
|
string gpu_type = 4;
|
|
2046
2047
|
}
|
|
2047
2048
|
|
|
2049
|
+
|
|
2048
2050
|
message GeneratorDone { // Sent as the output when a generator finishes running.
|
|
2049
2051
|
uint64 items_total = 1;
|
|
2050
2052
|
}
|
|
@@ -2086,6 +2088,7 @@ message GenericResult { // Used for both tasks and function outputs
|
|
|
2086
2088
|
string propagation_reason = 13; // (?)
|
|
2087
2089
|
}
|
|
2088
2090
|
|
|
2091
|
+
|
|
2089
2092
|
message Image {
|
|
2090
2093
|
repeated BaseImage base_images = 5;
|
|
2091
2094
|
repeated string dockerfile_commands = 6;
|
|
@@ -2631,6 +2634,16 @@ message QueuePutRequest {
|
|
|
2631
2634
|
int32 partition_ttl_seconds = 6;
|
|
2632
2635
|
}
|
|
2633
2636
|
|
|
2637
|
+
// A copy google.rpc.Status for GRPCError.details:
|
|
2638
|
+
// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto
|
|
2639
|
+
// RPCStatus is compatible with google.rpc.Status, so one can encode messages using
|
|
2640
|
+
// google.rpc.Status. The `details` field can be a list of any message, but for client
|
|
2641
|
+
// to decode it, the messages should be defined here (`modal_proto`).
|
|
2642
|
+
message RPCStatus {
|
|
2643
|
+
int32 code = 1;
|
|
2644
|
+
string message = 2;
|
|
2645
|
+
repeated google.protobuf.Any details = 3;
|
|
2646
|
+
}
|
|
2634
2647
|
|
|
2635
2648
|
message RateLimit {
|
|
2636
2649
|
int32 limit = 1;
|