modal 1.3.1.dev1__tar.gz → 1.3.1.dev3__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.3.1.dev1 → modal-1.3.1.dev3}/PKG-INFO +1 -1
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/blob_utils.py +3 -9
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/task_command_router_client.py +25 -38
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/client.pyi +2 -2
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/functions.pyi +6 -6
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_version/__init__.py +1 -1
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/LICENSE +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/README.md +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/__main__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_billing.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_clustered_functions.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_clustered_functions.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_container_entrypoint.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_functions.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_grpc_client.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_ipython.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_load_context.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_location.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_object.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_output.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_partial_function.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_pty.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_resolver.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_resources.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/asgi.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/execution_context.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/telemetry.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_serialization.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_traceback.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_tunnel.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_tunnel.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_type_manager.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/app_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/async_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/deprecation.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/docker_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/function_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/git_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/hash_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/http_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/logger.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/mount_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/name_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/package_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/shell_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_utils/time_utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_vendor/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_vendor/tblib.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/_watcher.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/app.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/app.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/billing.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/2023.12.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/2024.04.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/2024.10.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/2025.06.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/README.md +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/builder/base-images.json +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/call_graph.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/_download.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/_traceback.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/app.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/cluster.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/config.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/container.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/dict.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/entry_point.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/environment.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/import_refs.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/launch.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/network_file_system.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/profile.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/programs/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/programs/vscode.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/queues.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/run.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/secret.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/shell.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/token.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/utils.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cli/volume.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/client.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cls.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/cls.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/config.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/container_process.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/container_process.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/dict.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/dict.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/environments.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/environments.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/exception.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/experimental/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/experimental/flash.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/experimental/flash.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/experimental/ipython.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/file_io.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/file_io.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/file_pattern_matcher.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/functions.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/gpu.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/image.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/image.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/io_streams.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/io_streams.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/mount.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/mount.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/network_file_system.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/network_file_system.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/object.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/object.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/output.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/parallel_map.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/parallel_map.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/partial_function.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/partial_function.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/proxy.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/proxy.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/py.typed +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/queue.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/queue.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/retries.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/runner.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/runner.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/running_app.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/sandbox.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/sandbox.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/schedule.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/scheduler_placement.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/secret.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/secret.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/serving.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/serving.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/snapshot.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/snapshot.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/stream_type.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/token_flow.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/token_flow.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/volume.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal/volume.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/requires.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal.egg-info/top_level.txt +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/__init__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api.proto +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api_grpc.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api_pb2.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/py.typed +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router.proto +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/modal_version/__main__.py +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/pyproject.toml +0 -0
- {modal-1.3.1.dev1 → modal-1.3.1.dev3}/setup.cfg +0 -0
|
@@ -4,7 +4,6 @@ import dataclasses
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import os
|
|
6
6
|
import platform
|
|
7
|
-
import random
|
|
8
7
|
import time
|
|
9
8
|
from collections.abc import AsyncIterator
|
|
10
9
|
from contextlib import AbstractContextManager, contextmanager
|
|
@@ -58,10 +57,8 @@ MULTIPART_UPLOAD_THRESHOLD = 1024**3
|
|
|
58
57
|
# For block based storage like volumefs2: the size of a block
|
|
59
58
|
BLOCK_SIZE: int = 8 * 1024 * 1024
|
|
60
59
|
|
|
61
|
-
HEALTHY_R2_UPLOAD_PERCENTAGE = 0.95
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
@retry(n_attempts=5, base_delay=0.5, timeout=None)
|
|
61
|
+
@retry(n_attempts=3, base_delay=0.3, timeout=None)
|
|
65
62
|
async def _upload_to_s3_url(
|
|
66
63
|
upload_url,
|
|
67
64
|
payload: "BytesIOSegmentPayload",
|
|
@@ -191,13 +188,10 @@ def get_content_length(data: BinaryIO) -> int:
|
|
|
191
188
|
async def _blob_upload_with_fallback(
|
|
192
189
|
items, blob_ids: list[str], callback, content_length: int
|
|
193
190
|
) -> tuple[str, bool, int]:
|
|
191
|
+
"""Try uploading to each provider in order, with fallback on failure."""
|
|
194
192
|
r2_throughput_bytes_s = 0
|
|
195
193
|
r2_failed = False
|
|
196
194
|
for idx, (item, blob_id) in enumerate(zip(items, blob_ids)):
|
|
197
|
-
# We want to default to R2 95% of the time and S3 5% of the time.
|
|
198
|
-
# To ensure the failure path is continuously exercised.
|
|
199
|
-
if idx == 0 and len(items) > 1 and random.random() > HEALTHY_R2_UPLOAD_PERCENTAGE:
|
|
200
|
-
continue
|
|
201
195
|
try:
|
|
202
196
|
if blob_id.endswith(":r2"):
|
|
203
197
|
t0 = time.monotonic_ns()
|
|
@@ -207,7 +201,7 @@ async def _blob_upload_with_fallback(
|
|
|
207
201
|
else:
|
|
208
202
|
await callback(item)
|
|
209
203
|
return blob_id, r2_failed, r2_throughput_bytes_s
|
|
210
|
-
except Exception
|
|
204
|
+
except Exception:
|
|
211
205
|
if blob_id.endswith(":r2"):
|
|
212
206
|
r2_failed = True
|
|
213
207
|
# Ignore all errors except the last one, since we're out of fallback options.
|
|
@@ -5,6 +5,8 @@ import json
|
|
|
5
5
|
import ssl
|
|
6
6
|
import time
|
|
7
7
|
import urllib.parse
|
|
8
|
+
import weakref
|
|
9
|
+
from contextlib import suppress
|
|
8
10
|
from typing import AsyncGenerator, Optional
|
|
9
11
|
|
|
10
12
|
import grpclib.client
|
|
@@ -106,6 +108,15 @@ async def fetch_command_router_access(server_client, task_id: str) -> api_pb2.Ta
|
|
|
106
108
|
)
|
|
107
109
|
|
|
108
110
|
|
|
111
|
+
def _finalize_channel(loop, channel):
|
|
112
|
+
if not loop.is_closed():
|
|
113
|
+
# only run if loop has not shut down
|
|
114
|
+
# call_soon_threadsafe could throw if the loop is torn down after calling
|
|
115
|
+
# is_closed. This is safe to ignore.
|
|
116
|
+
with suppress(Exception):
|
|
117
|
+
loop.call_soon_threadsafe(channel.close)
|
|
118
|
+
|
|
119
|
+
|
|
109
120
|
class TaskCommandRouterClient:
|
|
110
121
|
"""
|
|
111
122
|
Client used to talk directly to TaskCommandRouter service on worker hosts.
|
|
@@ -199,44 +210,17 @@ class TaskCommandRouterClient:
|
|
|
199
210
|
|
|
200
211
|
self._closed = False
|
|
201
212
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
grpclib.events.listen(self._channel, grpclib.events.SendRequest, send_request)
|
|
213
|
+
self._channel_finalizer = weakref.finalize(
|
|
214
|
+
self,
|
|
215
|
+
_finalize_channel,
|
|
216
|
+
loop,
|
|
217
|
+
channel,
|
|
218
|
+
)
|
|
209
219
|
|
|
210
220
|
self._stub = TaskCommandRouterStub(self._channel)
|
|
211
221
|
|
|
212
|
-
def
|
|
213
|
-
"""
|
|
214
|
-
|
|
215
|
-
This object is typically used through synchronicity wrappers, which means this finalizer
|
|
216
|
-
may run on a different thread than the event loop that owns the channel. Closing the
|
|
217
|
-
channel is therefore scheduled onto the owning loop using call_soon_threadsafe.
|
|
218
|
-
|
|
219
|
-
Use getattr in the event that attributes are not yet initialized or the
|
|
220
|
-
object is in a half-torn-down state.
|
|
221
|
-
"""
|
|
222
|
-
if getattr(self, "_closed", False):
|
|
223
|
-
return
|
|
224
|
-
self._closed = True
|
|
225
|
-
|
|
226
|
-
channel = getattr(self, "_channel", None)
|
|
227
|
-
if channel is None:
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
loop = getattr(self, "_loop", None)
|
|
231
|
-
|
|
232
|
-
if loop is not None and not loop.is_closed():
|
|
233
|
-
try:
|
|
234
|
-
loop.call_soon_threadsafe(channel.close)
|
|
235
|
-
except Exception:
|
|
236
|
-
# call_soon_threadsafe could throw if the loop is torn down
|
|
237
|
-
# after calling is_closed. This is safe to ignore, and we don't
|
|
238
|
-
# want to raise an exception from a destructor.
|
|
239
|
-
pass
|
|
222
|
+
def _get_metadata(self):
|
|
223
|
+
return {"authorization": f"Bearer {self._jwt}"}
|
|
240
224
|
|
|
241
225
|
async def close(self) -> None:
|
|
242
226
|
"""Close the client."""
|
|
@@ -245,6 +229,9 @@ class TaskCommandRouterClient:
|
|
|
245
229
|
|
|
246
230
|
self._closed = True
|
|
247
231
|
self._channel.close()
|
|
232
|
+
if self._channel_finalizer.alive:
|
|
233
|
+
# skip the finalizer if we've closed the channel anyway
|
|
234
|
+
self._channel_finalizer.detach()
|
|
248
235
|
|
|
249
236
|
async def exec_start(self, request: sr_pb2.TaskExecStartRequest) -> sr_pb2.TaskExecStartResponse:
|
|
250
237
|
"""Start an exec'd command, properly retrying on transient errors."""
|
|
@@ -420,12 +407,12 @@ class TaskCommandRouterClient:
|
|
|
420
407
|
|
|
421
408
|
async def _call_with_auth_retry(self, func, *args, **kwargs):
|
|
422
409
|
try:
|
|
423
|
-
return await func(*args, **kwargs)
|
|
410
|
+
return await func(*args, **kwargs, metadata=self._get_metadata())
|
|
424
411
|
except GRPCError as exc:
|
|
425
412
|
if exc.status == Status.UNAUTHENTICATED:
|
|
426
413
|
await self._refresh_jwt()
|
|
427
414
|
# Retry with the original arguments preserved
|
|
428
|
-
return await func(*args, **kwargs)
|
|
415
|
+
return await func(*args, **kwargs, metadata=self._get_metadata())
|
|
429
416
|
raise
|
|
430
417
|
|
|
431
418
|
async def _stream_stdio(
|
|
@@ -460,7 +447,7 @@ class TaskCommandRouterClient:
|
|
|
460
447
|
while True:
|
|
461
448
|
timeout = max(0, deadline - time.monotonic()) if deadline is not None else None
|
|
462
449
|
try:
|
|
463
|
-
stream = self._stub.TaskExecStdioRead.open(timeout=timeout)
|
|
450
|
+
stream = self._stub.TaskExecStdioRead.open(timeout=timeout, metadata=self._get_metadata())
|
|
464
451
|
async with stream as s:
|
|
465
452
|
req = sr_pb2.TaskExecStdioReadRequest(
|
|
466
453
|
task_id=task_id,
|
|
@@ -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.3.1.
|
|
36
|
+
version: str = "1.3.1.dev3",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -163,7 +163,7 @@ class Client:
|
|
|
163
163
|
server_url: str,
|
|
164
164
|
client_type: int,
|
|
165
165
|
credentials: typing.Optional[tuple[str, str]],
|
|
166
|
-
version: str = "1.3.1.
|
|
166
|
+
version: str = "1.3.1.dev3",
|
|
167
167
|
):
|
|
168
168
|
"""mdmd:hidden
|
|
169
169
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -407,7 +407,7 @@ class Function(
|
|
|
407
407
|
|
|
408
408
|
_call_generator: ___call_generator_spec
|
|
409
409
|
|
|
410
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
410
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
411
411
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
412
412
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
413
413
|
...
|
|
@@ -416,7 +416,7 @@ class Function(
|
|
|
416
416
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
417
417
|
...
|
|
418
418
|
|
|
419
|
-
remote: __remote_spec[modal._functions.
|
|
419
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
|
|
420
420
|
|
|
421
421
|
class __remote_gen_spec(typing_extensions.Protocol):
|
|
422
422
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -443,7 +443,7 @@ class Function(
|
|
|
443
443
|
"""
|
|
444
444
|
...
|
|
445
445
|
|
|
446
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
446
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
447
447
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
448
448
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
449
449
|
|
|
@@ -466,7 +466,7 @@ class Function(
|
|
|
466
466
|
"""
|
|
467
467
|
...
|
|
468
468
|
|
|
469
|
-
_experimental_spawn: ___experimental_spawn_spec[modal._functions.
|
|
469
|
+
_experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
470
470
|
|
|
471
471
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
|
|
472
472
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
|
|
@@ -474,7 +474,7 @@ class Function(
|
|
|
474
474
|
|
|
475
475
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
|
|
476
476
|
|
|
477
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
477
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
|
|
478
478
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
479
479
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
480
480
|
|
|
@@ -495,7 +495,7 @@ class Function(
|
|
|
495
495
|
"""
|
|
496
496
|
...
|
|
497
497
|
|
|
498
|
-
spawn: __spawn_spec[modal._functions.
|
|
498
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
|
|
499
499
|
|
|
500
500
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
501
501
|
"""Return the inner Python object wrapped by this Modal Function."""
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|