modal 1.2.1.dev22__tar.gz → 1.2.2.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/PKG-INFO +1 -1
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/mount_utils.py +26 -1
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/client.pyi +2 -2
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/container_process.py +2 -3
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/functions.pyi +6 -6
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/image.py +21 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/image.pyi +4 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/io_streams.py +51 -47
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/io_streams.pyi +20 -6
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/sandbox.py +2 -2
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/volume.py +1 -1
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_version/__init__.py +1 -1
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/LICENSE +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/README.md +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/__main__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_billing.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_clustered_functions.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_functions.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_ipython.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_location.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_object.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_output.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_partial_function.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_pty.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_resolver.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_resources.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_serialization.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_traceback.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_tunnel.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_tunnel.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_type_manager.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/blob_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/logger.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/_watcher.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/app.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/app.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/billing.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/README.md +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/builder/base-images.json +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/call_graph.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/_download.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/_traceback.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/app.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/cluster.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/config.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/container.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/dict.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/entry_point.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/environment.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/import_refs.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/launch.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/profile.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/queues.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/run.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/secret.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/token.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/utils.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cli/volume.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/client.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cls.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/cls.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/config.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/container_process.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/dict.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/dict.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/environments.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/environments.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/exception.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/experimental/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/experimental/flash.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/experimental/flash.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/experimental/ipython.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/file_io.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/file_io.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/functions.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/gpu.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/mount.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/mount.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/network_file_system.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/network_file_system.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/object.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/object.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/output.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/parallel_map.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/parallel_map.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/partial_function.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/partial_function.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/proxy.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/proxy.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/py.typed +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/queue.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/queue.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/retries.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/runner.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/runner.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/running_app.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/sandbox.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/schedule.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/scheduler_placement.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/secret.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/secret.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/serving.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/serving.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/snapshot.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/snapshot.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/stream_type.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/token_flow.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/token_flow.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal/volume.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/__init__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api.proto +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api_pb2.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/py.typed +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/modal_version/__main__.py +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/pyproject.toml +0 -0
- {modal-1.2.1.dev22 → modal-1.2.2.dev2}/setup.cfg +0 -0
|
@@ -3,7 +3,9 @@ import posixpath
|
|
|
3
3
|
import typing
|
|
4
4
|
from collections.abc import Mapping, Sequence
|
|
5
5
|
from pathlib import PurePath, PurePosixPath
|
|
6
|
-
from typing import Union
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
from typing_extensions import TypeGuard
|
|
7
9
|
|
|
8
10
|
from ..cloud_bucket_mount import _CloudBucketMount
|
|
9
11
|
from ..exception import InvalidError
|
|
@@ -76,3 +78,26 @@ def validate_volumes(
|
|
|
76
78
|
)
|
|
77
79
|
|
|
78
80
|
return validated_volumes
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_only_modal_volumes(
|
|
84
|
+
volumes: Optional[Optional[dict[Union[str, PurePosixPath], _Volume]]],
|
|
85
|
+
caller_name: str,
|
|
86
|
+
) -> Sequence[tuple[str, _Volume]]:
|
|
87
|
+
"""Validate all volumes are `modal.Volume`."""
|
|
88
|
+
if volumes is None:
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
validated_volumes = validate_volumes(volumes)
|
|
92
|
+
|
|
93
|
+
# Although the typing forbids `_CloudBucketMount` for type checking, one can still pass a `_CloudBucketMount`
|
|
94
|
+
# during runtime, so we'll check the type here.
|
|
95
|
+
def all_modal_volumes(
|
|
96
|
+
vols: Sequence[tuple[str, Union[_Volume, _CloudBucketMount]]],
|
|
97
|
+
) -> TypeGuard[Sequence[tuple[str, _Volume]]]:
|
|
98
|
+
return all(isinstance(v, _Volume) for _, v in vols)
|
|
99
|
+
|
|
100
|
+
if not all_modal_volumes(validated_volumes):
|
|
101
|
+
raise InvalidError(f"{caller_name} only supports volumes that are modal.Volume")
|
|
102
|
+
|
|
103
|
+
return validated_volumes
|
|
@@ -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.
|
|
36
|
+
version: str = "1.2.2.dev2",
|
|
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.
|
|
167
|
+
version: str = "1.2.2.dev2",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -101,6 +101,7 @@ class _ContainerProcessThroughServer(Generic[T]):
|
|
|
101
101
|
|
|
102
102
|
Returns `None` if the process is still running, else returns the exit code.
|
|
103
103
|
"""
|
|
104
|
+
assert self._process_id
|
|
104
105
|
if self._returncode is not None:
|
|
105
106
|
return self._returncode
|
|
106
107
|
if self._exec_deadline and time.monotonic() >= self._exec_deadline:
|
|
@@ -119,6 +120,7 @@ class _ContainerProcessThroughServer(Generic[T]):
|
|
|
119
120
|
return None
|
|
120
121
|
|
|
121
122
|
async def _wait_for_completion(self) -> int:
|
|
123
|
+
assert self._process_id
|
|
122
124
|
while True:
|
|
123
125
|
req = api_pb2.ContainerExecWaitRequest(exec_id=self._process_id, timeout=10)
|
|
124
126
|
resp: api_pb2.ContainerExecWaitResponse = await retry_transient_errors(
|
|
@@ -169,9 +171,6 @@ class _ContainerProcessThroughServer(Generic[T]):
|
|
|
169
171
|
stream_impl = stream._impl
|
|
170
172
|
# Don't skip empty messages so we can detect when the process has booted.
|
|
171
173
|
async for chunk in stream_impl._get_logs(skip_empty_messages=False):
|
|
172
|
-
if chunk is None:
|
|
173
|
-
break
|
|
174
|
-
|
|
175
174
|
if not on_connect.is_set():
|
|
176
175
|
connecting_status.stop()
|
|
177
176
|
on_connect.set()
|
|
@@ -401,7 +401,7 @@ class Function(
|
|
|
401
401
|
|
|
402
402
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
|
403
403
|
|
|
404
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
404
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
405
405
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
406
406
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
407
407
|
...
|
|
@@ -410,7 +410,7 @@ class Function(
|
|
|
410
410
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
411
411
|
...
|
|
412
412
|
|
|
413
|
-
remote: __remote_spec[modal._functions.
|
|
413
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
414
414
|
|
|
415
415
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
416
416
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -437,7 +437,7 @@ class Function(
|
|
|
437
437
|
"""
|
|
438
438
|
...
|
|
439
439
|
|
|
440
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
440
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
441
441
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
442
442
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
443
443
|
|
|
@@ -461,7 +461,7 @@ class Function(
|
|
|
461
461
|
...
|
|
462
462
|
|
|
463
463
|
_experimental_spawn: ___experimental_spawn_spec[
|
|
464
|
-
modal._functions.
|
|
464
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
|
465
465
|
]
|
|
466
466
|
|
|
467
467
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
|
@@ -470,7 +470,7 @@ class Function(
|
|
|
470
470
|
|
|
471
471
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
|
472
472
|
|
|
473
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
473
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
474
474
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
475
475
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
476
476
|
|
|
@@ -491,7 +491,7 @@ class Function(
|
|
|
491
491
|
"""
|
|
492
492
|
...
|
|
493
493
|
|
|
494
|
-
spawn: __spawn_spec[modal._functions.
|
|
494
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
495
495
|
|
|
496
496
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
497
497
|
"""Return the inner Python object wrapped by this Modal Function."""
|
|
@@ -39,6 +39,7 @@ from ._utils.docker_utils import (
|
|
|
39
39
|
)
|
|
40
40
|
from ._utils.function_utils import FunctionInfo
|
|
41
41
|
from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES, retry_transient_errors
|
|
42
|
+
from ._utils.mount_utils import validate_only_modal_volumes
|
|
42
43
|
from .client import _Client
|
|
43
44
|
from .cloud_bucket_mount import _CloudBucketMount
|
|
44
45
|
from .config import config, logger, user_config_path
|
|
@@ -487,6 +488,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
487
488
|
context_mount_function: Optional[Callable[[], Optional[_Mount]]] = None,
|
|
488
489
|
force_build: bool = False,
|
|
489
490
|
build_args: dict[str, str] = {},
|
|
491
|
+
validated_volumes: Optional[Sequence[tuple[str, _Volume]]] = None,
|
|
490
492
|
# For internal use only.
|
|
491
493
|
_namespace: "api_pb2.DeploymentNamespace.ValueType" = api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
|
|
492
494
|
_do_assert_no_mount_layers: bool = True,
|
|
@@ -494,6 +496,9 @@ class _Image(_Object, type_prefix="im"):
|
|
|
494
496
|
if base_images is None:
|
|
495
497
|
base_images = {}
|
|
496
498
|
|
|
499
|
+
if validated_volumes is None:
|
|
500
|
+
validated_volumes = []
|
|
501
|
+
|
|
497
502
|
if secrets is None:
|
|
498
503
|
secrets = []
|
|
499
504
|
if gpu_config is None:
|
|
@@ -514,6 +519,8 @@ class _Image(_Object, type_prefix="im"):
|
|
|
514
519
|
deps += (build_function,)
|
|
515
520
|
if image_registry_config and image_registry_config.secret:
|
|
516
521
|
deps += (image_registry_config.secret,)
|
|
522
|
+
for _, vol in validated_volumes:
|
|
523
|
+
deps += (vol,)
|
|
517
524
|
return deps
|
|
518
525
|
|
|
519
526
|
async def _load(self: _Image, resolver: Resolver, existing_object_id: Optional[str]):
|
|
@@ -592,6 +599,17 @@ class _Image(_Object, type_prefix="im"):
|
|
|
592
599
|
build_function_id = ""
|
|
593
600
|
_build_function = None
|
|
594
601
|
|
|
602
|
+
# Relies on dicts being ordered (true as of Python 3.6).
|
|
603
|
+
volume_mounts = [
|
|
604
|
+
api_pb2.VolumeMount(
|
|
605
|
+
mount_path=path,
|
|
606
|
+
volume_id=volume.object_id,
|
|
607
|
+
allow_background_commits=True,
|
|
608
|
+
read_only=volume._read_only,
|
|
609
|
+
)
|
|
610
|
+
for path, volume in validated_volumes
|
|
611
|
+
]
|
|
612
|
+
|
|
595
613
|
image_definition = api_pb2.Image(
|
|
596
614
|
base_images=base_images_pb2s,
|
|
597
615
|
dockerfile_commands=dockerfile.commands,
|
|
@@ -604,6 +622,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
604
622
|
runtime_debug=config.get("function_runtime_debug"),
|
|
605
623
|
build_function=_build_function,
|
|
606
624
|
build_args=build_args,
|
|
625
|
+
volume_mounts=volume_mounts,
|
|
607
626
|
)
|
|
608
627
|
|
|
609
628
|
req = api_pb2.ImageGetOrCreateRequest(
|
|
@@ -1690,6 +1709,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
1690
1709
|
*commands: Union[str, list[str]],
|
|
1691
1710
|
env: Optional[dict[str, Optional[str]]] = None,
|
|
1692
1711
|
secrets: Optional[Collection[_Secret]] = None,
|
|
1712
|
+
volumes: Optional[dict[Union[str, PurePosixPath], _Volume]] = None,
|
|
1693
1713
|
gpu: GPU_T = None,
|
|
1694
1714
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
|
1695
1715
|
) -> "_Image":
|
|
@@ -1712,6 +1732,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
1712
1732
|
secrets=secrets,
|
|
1713
1733
|
gpu_config=parse_gpu_config(gpu),
|
|
1714
1734
|
force_build=self.force_build or force_build,
|
|
1735
|
+
validated_volumes=validate_only_modal_volumes(volumes, "Image.run_commands"),
|
|
1715
1736
|
)
|
|
1716
1737
|
|
|
1717
1738
|
@staticmethod
|
|
@@ -176,6 +176,7 @@ class _Image(modal._object._Object):
|
|
|
176
176
|
] = None,
|
|
177
177
|
force_build: bool = False,
|
|
178
178
|
build_args: dict[str, str] = {},
|
|
179
|
+
validated_volumes: typing.Optional[collections.abc.Sequence[tuple[str, modal.volume._Volume]]] = None,
|
|
179
180
|
_namespace: int = 1,
|
|
180
181
|
_do_assert_no_mount_layers: bool = True,
|
|
181
182
|
): ...
|
|
@@ -668,6 +669,7 @@ class _Image(modal._object._Object):
|
|
|
668
669
|
*commands: typing.Union[str, list[str]],
|
|
669
670
|
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
670
671
|
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
672
|
+
volumes: typing.Optional[dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]] = None,
|
|
671
673
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig] = None,
|
|
672
674
|
force_build: bool = False,
|
|
673
675
|
) -> _Image:
|
|
@@ -1091,6 +1093,7 @@ class Image(modal.object.Object):
|
|
|
1091
1093
|
] = None,
|
|
1092
1094
|
force_build: bool = False,
|
|
1093
1095
|
build_args: dict[str, str] = {},
|
|
1096
|
+
validated_volumes: typing.Optional[collections.abc.Sequence[tuple[str, modal.volume.Volume]]] = None,
|
|
1094
1097
|
_namespace: int = 1,
|
|
1095
1098
|
_do_assert_no_mount_layers: bool = True,
|
|
1096
1099
|
): ...
|
|
@@ -1648,6 +1651,7 @@ class Image(modal.object.Object):
|
|
|
1648
1651
|
*commands: typing.Union[str, list[str]],
|
|
1649
1652
|
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1650
1653
|
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
1654
|
+
volumes: typing.Optional[dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume]] = None,
|
|
1651
1655
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig] = None,
|
|
1652
1656
|
force_build: bool = False,
|
|
1653
1657
|
) -> Image:
|
|
@@ -64,7 +64,6 @@ async def _container_process_logs_iterator(
|
|
|
64
64
|
get_raw_bytes=True,
|
|
65
65
|
last_batch_index=last_index,
|
|
66
66
|
)
|
|
67
|
-
|
|
68
67
|
stream = client.stub.ContainerExecGetOutput.unary_stream(req)
|
|
69
68
|
while True:
|
|
70
69
|
# Check deadline before attempting to receive the next batch
|
|
@@ -76,11 +75,13 @@ async def _container_process_logs_iterator(
|
|
|
76
75
|
break
|
|
77
76
|
except StopAsyncIteration:
|
|
78
77
|
break
|
|
78
|
+
|
|
79
|
+
for item in batch.items:
|
|
80
|
+
yield item.message_bytes, batch.batch_index
|
|
81
|
+
|
|
79
82
|
if batch.HasField("exit_code"):
|
|
80
83
|
yield None, batch.batch_index
|
|
81
84
|
break
|
|
82
|
-
for item in batch.items:
|
|
83
|
-
yield item.message_bytes, batch.batch_index
|
|
84
85
|
|
|
85
86
|
|
|
86
87
|
T = TypeVar("T", str, bytes)
|
|
@@ -89,7 +90,7 @@ T = TypeVar("T", str, bytes)
|
|
|
89
90
|
class _StreamReaderThroughServer(Generic[T]):
|
|
90
91
|
"""A StreamReader implementation that reads from the server."""
|
|
91
92
|
|
|
92
|
-
_stream: Optional[AsyncGenerator[
|
|
93
|
+
_stream: Optional[AsyncGenerator[T, None]]
|
|
93
94
|
|
|
94
95
|
def __init__(
|
|
95
96
|
self,
|
|
@@ -134,10 +135,9 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
134
135
|
self._stream_type = stream_type
|
|
135
136
|
|
|
136
137
|
if self._object_type == "container_process":
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
self._container_process_buffer: list[Optional[bytes]] = []
|
|
138
|
+
# TODO: we should not have this async code in constructors!
|
|
139
|
+
# it only works as long as all the construction happens inside of synchronicity code
|
|
140
|
+
self._container_process_buffer: list[Optional[bytes]] = [] # TODO: change this to an asyncio.Queue
|
|
141
141
|
self._consume_container_process_task = asyncio.create_task(self._consume_container_process_stream())
|
|
142
142
|
|
|
143
143
|
@property
|
|
@@ -147,21 +147,18 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
147
147
|
|
|
148
148
|
async def read(self) -> T:
|
|
149
149
|
"""Fetch the entire contents of the stream until EOF."""
|
|
150
|
-
data_str = ""
|
|
151
|
-
data_bytes = b""
|
|
152
150
|
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read starting")
|
|
153
|
-
async for message in self._get_logs():
|
|
154
|
-
if message is None:
|
|
155
|
-
break
|
|
156
|
-
if self._text:
|
|
157
|
-
data_str += message.decode("utf-8")
|
|
158
|
-
else:
|
|
159
|
-
data_bytes += message
|
|
160
|
-
|
|
161
|
-
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
|
162
151
|
if self._text:
|
|
152
|
+
data_str = ""
|
|
153
|
+
async for message in _decode_bytes_stream_to_str(self._get_logs()):
|
|
154
|
+
data_str += message
|
|
155
|
+
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
|
163
156
|
return cast(T, data_str)
|
|
164
157
|
else:
|
|
158
|
+
data_bytes = b""
|
|
159
|
+
async for message in self._get_logs():
|
|
160
|
+
data_bytes += message
|
|
161
|
+
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
|
165
162
|
return cast(T, data_bytes)
|
|
166
163
|
|
|
167
164
|
async def _consume_container_process_stream(self):
|
|
@@ -181,6 +178,7 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
181
178
|
)
|
|
182
179
|
async for message, batch_index in iterator:
|
|
183
180
|
if self._stream_type == StreamType.STDOUT and message:
|
|
181
|
+
# TODO: rearchitect this, since these bytes aren't necessarily decodable
|
|
184
182
|
print(message.decode("utf-8"), end="")
|
|
185
183
|
elif self._stream_type == StreamType.PIPE:
|
|
186
184
|
self._container_process_buffer.append(message)
|
|
@@ -208,6 +206,9 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
208
206
|
|
|
209
207
|
async def _stream_container_process(self) -> AsyncGenerator[tuple[Optional[bytes], str], None]:
|
|
210
208
|
"""Streams the container process buffer to the reader."""
|
|
209
|
+
# Container process streams need to be consumed as they are produced,
|
|
210
|
+
# otherwise the process will block. Use a buffer to store the stream
|
|
211
|
+
# until the client consumes it.
|
|
211
212
|
entry_id = 0
|
|
212
213
|
if self._last_entry_id:
|
|
213
214
|
entry_id = int(self._last_entry_id) + 1
|
|
@@ -225,7 +226,7 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
225
226
|
|
|
226
227
|
entry_id += 1
|
|
227
228
|
|
|
228
|
-
async def _get_logs(self, skip_empty_messages: bool = True) -> AsyncGenerator[
|
|
229
|
+
async def _get_logs(self, skip_empty_messages: bool = True) -> AsyncGenerator[bytes, None]:
|
|
229
230
|
"""Streams sandbox or process logs from the server to the reader.
|
|
230
231
|
|
|
231
232
|
Logs returned by this method may contain partial or multiple lines at a time.
|
|
@@ -237,7 +238,6 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
237
238
|
raise InvalidError("Logs can only be retrieved using the PIPE stream type.")
|
|
238
239
|
|
|
239
240
|
if self.eof:
|
|
240
|
-
yield None
|
|
241
241
|
return
|
|
242
242
|
|
|
243
243
|
completed = False
|
|
@@ -262,6 +262,8 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
262
262
|
if message is None:
|
|
263
263
|
completed = True
|
|
264
264
|
self.eof = True
|
|
265
|
+
return
|
|
266
|
+
|
|
265
267
|
yield message
|
|
266
268
|
|
|
267
269
|
except (GRPCError, StreamTerminatedError) as exc:
|
|
@@ -275,43 +277,37 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
275
277
|
continue
|
|
276
278
|
raise
|
|
277
279
|
|
|
278
|
-
async def _get_logs_by_line(self) -> AsyncGenerator[
|
|
280
|
+
async def _get_logs_by_line(self) -> AsyncGenerator[bytes, None]:
|
|
279
281
|
"""Process logs from the server and yield complete lines only."""
|
|
280
282
|
async for message in self._get_logs():
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
yield
|
|
286
|
-
else:
|
|
287
|
-
assert isinstance(message, bytes)
|
|
288
|
-
self._line_buffer += message
|
|
289
|
-
while b"\n" in self._line_buffer:
|
|
290
|
-
line, self._line_buffer = self._line_buffer.split(b"\n", 1)
|
|
291
|
-
yield line + b"\n"
|
|
283
|
+
assert isinstance(message, bytes)
|
|
284
|
+
self._line_buffer += message
|
|
285
|
+
while b"\n" in self._line_buffer:
|
|
286
|
+
line, self._line_buffer = self._line_buffer.split(b"\n", 1)
|
|
287
|
+
yield line + b"\n"
|
|
292
288
|
|
|
293
|
-
|
|
289
|
+
if self._line_buffer:
|
|
290
|
+
yield self._line_buffer
|
|
291
|
+
self._line_buffer = b""
|
|
292
|
+
|
|
293
|
+
def _ensure_stream(self) -> AsyncGenerator[T, None]:
|
|
294
294
|
if not self._stream:
|
|
295
295
|
if self._by_line:
|
|
296
|
-
|
|
296
|
+
# TODO: This is quite odd - it does line buffering in binary mode
|
|
297
|
+
# but we then always add the buffered text decoding on top of that.
|
|
298
|
+
# feels a bit upside down...
|
|
299
|
+
stream = self._get_logs_by_line()
|
|
297
300
|
else:
|
|
298
|
-
|
|
301
|
+
stream = self._get_logs()
|
|
302
|
+
if self._text:
|
|
303
|
+
stream = _decode_bytes_stream_to_str(stream)
|
|
304
|
+
self._stream = cast(AsyncGenerator[T, None], stream)
|
|
299
305
|
return self._stream
|
|
300
306
|
|
|
301
307
|
async def __anext__(self) -> T:
|
|
302
308
|
"""mdmd:hidden"""
|
|
303
309
|
stream = self._ensure_stream()
|
|
304
|
-
|
|
305
|
-
value = await stream.__anext__()
|
|
306
|
-
|
|
307
|
-
# The stream yields None if it receives an EOF batch.
|
|
308
|
-
if value is None:
|
|
309
|
-
raise StopAsyncIteration
|
|
310
|
-
|
|
311
|
-
if self._text:
|
|
312
|
-
return cast(T, value.decode("utf-8"))
|
|
313
|
-
else:
|
|
314
|
-
return cast(T, value)
|
|
310
|
+
return cast(T, await stream.__anext__())
|
|
315
311
|
|
|
316
312
|
async def aclose(self):
|
|
317
313
|
"""mdmd:hidden"""
|
|
@@ -330,6 +326,7 @@ async def _decode_bytes_stream_to_str(stream: AsyncGenerator[bytes, None]) -> As
|
|
|
330
326
|
text = decoder.decode(item, final=False)
|
|
331
327
|
if text:
|
|
332
328
|
yield text
|
|
329
|
+
|
|
333
330
|
# Flush any buffered partial character at end-of-stream
|
|
334
331
|
tail = decoder.decode(b"", final=True)
|
|
335
332
|
if tail:
|
|
@@ -512,6 +509,13 @@ class _StreamReader(Generic[T]):
|
|
|
512
509
|
```
|
|
513
510
|
"""
|
|
514
511
|
|
|
512
|
+
_impl: Union[
|
|
513
|
+
_StreamReaderThroughServer,
|
|
514
|
+
_DevnullStreamReader,
|
|
515
|
+
_TextStreamReaderThroughCommandRouter,
|
|
516
|
+
_BytesStreamReaderThroughCommandRouter,
|
|
517
|
+
]
|
|
518
|
+
|
|
515
519
|
def __init__(
|
|
516
520
|
self,
|
|
517
521
|
file_descriptor: "api_pb2.FileDescriptor.ValueType",
|
|
@@ -21,7 +21,7 @@ T = typing.TypeVar("T")
|
|
|
21
21
|
class _StreamReaderThroughServer(typing.Generic[T]):
|
|
22
22
|
"""A StreamReader implementation that reads from the server."""
|
|
23
23
|
|
|
24
|
-
_stream: typing.Optional[collections.abc.AsyncGenerator[
|
|
24
|
+
_stream: typing.Optional[collections.abc.AsyncGenerator[T, None]]
|
|
25
25
|
|
|
26
26
|
def __init__(
|
|
27
27
|
self,
|
|
@@ -54,9 +54,7 @@ class _StreamReaderThroughServer(typing.Generic[T]):
|
|
|
54
54
|
"""Streams the container process buffer to the reader."""
|
|
55
55
|
...
|
|
56
56
|
|
|
57
|
-
def _get_logs(
|
|
58
|
-
self, skip_empty_messages: bool = True
|
|
59
|
-
) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]:
|
|
57
|
+
def _get_logs(self, skip_empty_messages: bool = True) -> collections.abc.AsyncGenerator[bytes, None]:
|
|
60
58
|
"""Streams sandbox or process logs from the server to the reader.
|
|
61
59
|
|
|
62
60
|
Logs returned by this method may contain partial or multiple lines at a time.
|
|
@@ -66,11 +64,11 @@ class _StreamReaderThroughServer(typing.Generic[T]):
|
|
|
66
64
|
"""
|
|
67
65
|
...
|
|
68
66
|
|
|
69
|
-
def _get_logs_by_line(self) -> collections.abc.AsyncGenerator[
|
|
67
|
+
def _get_logs_by_line(self) -> collections.abc.AsyncGenerator[bytes, None]:
|
|
70
68
|
"""Process logs from the server and yield complete lines only."""
|
|
71
69
|
...
|
|
72
70
|
|
|
73
|
-
def _ensure_stream(self) -> collections.abc.AsyncGenerator[
|
|
71
|
+
def _ensure_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
|
|
74
72
|
async def __anext__(self) -> T:
|
|
75
73
|
"""mdmd:hidden"""
|
|
76
74
|
...
|
|
@@ -200,6 +198,14 @@ class _StreamReader(typing.Generic[T]):
|
|
|
200
198
|
print(f"Message: {message}")
|
|
201
199
|
```
|
|
202
200
|
"""
|
|
201
|
+
|
|
202
|
+
_impl: typing.Union[
|
|
203
|
+
_StreamReaderThroughServer,
|
|
204
|
+
_DevnullStreamReader,
|
|
205
|
+
_TextStreamReaderThroughCommandRouter,
|
|
206
|
+
_BytesStreamReaderThroughCommandRouter,
|
|
207
|
+
]
|
|
208
|
+
|
|
203
209
|
def __init__(
|
|
204
210
|
self,
|
|
205
211
|
file_descriptor: int,
|
|
@@ -392,6 +398,14 @@ class StreamReader(typing.Generic[T]):
|
|
|
392
398
|
print(f"Message: {message}")
|
|
393
399
|
```
|
|
394
400
|
"""
|
|
401
|
+
|
|
402
|
+
_impl: typing.Union[
|
|
403
|
+
_StreamReaderThroughServer,
|
|
404
|
+
_DevnullStreamReader,
|
|
405
|
+
_TextStreamReaderThroughCommandRouter,
|
|
406
|
+
_BytesStreamReaderThroughCommandRouter,
|
|
407
|
+
]
|
|
408
|
+
|
|
395
409
|
def __init__(
|
|
396
410
|
self,
|
|
397
411
|
file_descriptor: int,
|
|
@@ -516,10 +516,10 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
516
516
|
return obj
|
|
517
517
|
|
|
518
518
|
def _hydrate_metadata(self, handle_metadata: Optional[Message]):
|
|
519
|
-
self._stdout
|
|
519
|
+
self._stdout = StreamReader(
|
|
520
520
|
api_pb2.FILE_DESCRIPTOR_STDOUT, self.object_id, "sandbox", self._client, by_line=True
|
|
521
521
|
)
|
|
522
|
-
self._stderr
|
|
522
|
+
self._stderr = StreamReader(
|
|
523
523
|
api_pb2.FILE_DESCRIPTOR_STDERR, self.object_id, "sandbox", self._client, by_line=True
|
|
524
524
|
)
|
|
525
525
|
self._stdin = StreamWriter(self.object_id, "sandbox", self._client)
|
|
@@ -1265,7 +1265,7 @@ async def _put_missing_blocks(
|
|
|
1265
1265
|
file_progress.pending_blocks.add(missing_block.block_index)
|
|
1266
1266
|
task_progress_cb = functools.partial(progress_cb, task_id=file_progress.task_id)
|
|
1267
1267
|
|
|
1268
|
-
@retry(n_attempts=
|
|
1268
|
+
@retry(n_attempts=11, base_delay=0.5, timeout=None)
|
|
1269
1269
|
async def put_missing_block_attempt(payload: BytesIOSegmentPayload) -> bytes:
|
|
1270
1270
|
with payload.reset_on_error(subtract_progress=True):
|
|
1271
1271
|
async with ClientSessionRegistry.get_session().put(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|