modAL 1.2.3.dev12__tar.gz → 1.2.3.dev14__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.dev12 → modal-1.2.3.dev14}/PKG-INFO +1 -1
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_functions.py +1 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_load_context.py +2 -1
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_object.py +12 -6
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/grpc_utils.py +90 -20
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/client.py +9 -9
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/client.pyi +8 -9
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/config.py +8 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/functions.pyi +6 -6
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api.proto +6 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api_pb2.py +363 -353
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api_pb2.pyi +18 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_version/__init__.py +1 -1
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/LICENSE +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/README.md +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/__main__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_billing.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_clustered_functions.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_grpc_client.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_ipython.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_location.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_output.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_partial_function.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_pty.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_resolver.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_resources.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_serialization.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_traceback.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_tunnel.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_tunnel.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_type_manager.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/blob_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/logger.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/_watcher.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/app.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/app.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/billing.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/README.md +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/builder/base-images.json +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/call_graph.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/_download.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/_traceback.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/app.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/cluster.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/config.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/container.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/dict.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/entry_point.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/environment.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/import_refs.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/launch.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/profile.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/queues.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/run.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/secret.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/token.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/utils.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cli/volume.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cls.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/cls.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/container_process.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/container_process.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/dict.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/dict.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/environments.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/environments.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/exception.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/experimental/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/experimental/flash.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/experimental/flash.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/experimental/ipython.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/file_io.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/file_io.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/functions.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/gpu.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/image.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/image.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/io_streams.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/io_streams.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/mount.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/mount.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/network_file_system.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/network_file_system.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/object.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/object.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/output.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/parallel_map.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/parallel_map.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/partial_function.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/partial_function.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/proxy.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/proxy.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/py.typed +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/queue.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/queue.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/retries.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/runner.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/runner.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/running_app.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/sandbox.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/sandbox.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/schedule.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/scheduler_placement.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/secret.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/secret.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/serving.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/serving.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/snapshot.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/snapshot.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/stream_type.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/token_flow.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/token_flow.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/volume.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal/volume.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/__init__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/py.typed +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/modal_version/__main__.py +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/pyproject.toml +0 -0
- {modal-1.2.3.dev12 → modal-1.2.3.dev14}/setup.cfg +0 -0
|
@@ -536,6 +536,7 @@ class _InputPlaneInvocation:
|
|
|
536
536
|
async def _get_metadata(input_plane_region: str, client: _Client) -> list[tuple[str, str]]:
|
|
537
537
|
if not input_plane_region:
|
|
538
538
|
return []
|
|
539
|
+
assert client._auth_token_manager, "Client is not open"
|
|
539
540
|
token = await client._auth_token_manager.get_token()
|
|
540
541
|
return [("x-modal-input-plane-region", input_plane_region), ("x-modal-auth-token", token)]
|
|
541
542
|
|
|
@@ -66,8 +66,9 @@ class LoadContext:
|
|
|
66
66
|
|
|
67
67
|
Returns a new instance (no in place mutation)"""
|
|
68
68
|
|
|
69
|
+
is_valid_client = self._client is not None and not self._client._snapshotted
|
|
69
70
|
return LoadContext(
|
|
70
|
-
client=
|
|
71
|
+
client=self.client if is_valid_client else await _Client.from_env(),
|
|
71
72
|
environment_name=self._environment_name or config.get("environment") or "",
|
|
72
73
|
app_id=self._app_id,
|
|
73
74
|
)
|
|
@@ -43,7 +43,7 @@ class _Object:
|
|
|
43
43
|
_prefix_to_type: ClassVar[dict[str, type]] = {}
|
|
44
44
|
|
|
45
45
|
# For constructors
|
|
46
|
-
_load: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]]
|
|
46
|
+
_load: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]] = None
|
|
47
47
|
_preload: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]]
|
|
48
48
|
_rep: str
|
|
49
49
|
_is_another_app: bool
|
|
@@ -308,11 +308,17 @@ class _Object:
|
|
|
308
308
|
# memory snapshots capture references which must be rehydrated
|
|
309
309
|
# on restore to handle staleness.
|
|
310
310
|
logger.debug(f"rehydrating {self} after snapshot")
|
|
311
|
-
self.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
311
|
+
if self._hydrate_lazily:
|
|
312
|
+
logger.debug(f"reloading lazy {self} from server")
|
|
313
|
+
self._is_hydrated = False # un-hydrate and re-resolve
|
|
314
|
+
# we don't set an explicit Client here, relying on the default
|
|
315
|
+
# env client to be applied by LoadContext.apply_default
|
|
316
|
+
root_load_context = LoadContext.empty()
|
|
317
|
+
resolver = Resolver()
|
|
318
|
+
await resolver.load(typing.cast(_Object, self), root_load_context)
|
|
319
|
+
else:
|
|
320
|
+
logger.debug(f"reloading non-lazy {self} by replacing client")
|
|
321
|
+
self._client = client or await _Client.from_env()
|
|
316
322
|
self._is_rehydrated = True
|
|
317
323
|
logger.debug(f"rehydrated {self} with client {id(self.client)}")
|
|
318
324
|
elif not self._hydrate_lazily:
|
|
@@ -29,6 +29,7 @@ from modal_proto import api_pb2
|
|
|
29
29
|
from modal_version import __version__
|
|
30
30
|
|
|
31
31
|
from .._traceback import suppress_tb_frames
|
|
32
|
+
from ..config import config
|
|
32
33
|
from .async_utils import retry
|
|
33
34
|
from .logger import logger
|
|
34
35
|
|
|
@@ -72,6 +73,7 @@ RETRYABLE_GRPC_STATUS_CODES = [
|
|
|
72
73
|
Status.INTERNAL,
|
|
73
74
|
Status.UNKNOWN,
|
|
74
75
|
]
|
|
76
|
+
SERVER_RETRY_WARNING_TIME_INTERVAL = 30.0
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
@dataclass
|
|
@@ -251,6 +253,50 @@ async def retry_transient_errors(
|
|
|
251
253
|
return await _retry_transient_errors(fn, req, retry=Retry(max_retries=max_retries))
|
|
252
254
|
|
|
253
255
|
|
|
256
|
+
def get_server_retry_policy(exc: Exception) -> Optional[api_pb2.RPCRetryPolicy]:
|
|
257
|
+
"""Get server retry policy."""
|
|
258
|
+
if not isinstance(exc, GRPCError) or not exc.details:
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
# Server should not set multiple retry instructions, but if there is more than one, pick the first one
|
|
262
|
+
for entry in exc.details:
|
|
263
|
+
if isinstance(entry, api_pb2.RPCRetryPolicy):
|
|
264
|
+
return entry
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def process_exception_before_retry(
|
|
269
|
+
exc: Exception,
|
|
270
|
+
final_attempt: bool,
|
|
271
|
+
fn_name: str,
|
|
272
|
+
n_retries: int,
|
|
273
|
+
delay: float,
|
|
274
|
+
total_deadline: Optional[float],
|
|
275
|
+
idempotency_key: str,
|
|
276
|
+
):
|
|
277
|
+
"""Process exception before retry, used by `_retry_transient_errors`."""
|
|
278
|
+
with suppress_tb_frames(1):
|
|
279
|
+
if final_attempt:
|
|
280
|
+
logger.debug(
|
|
281
|
+
f"Final attempt failed with {repr(exc)} {n_retries=} {delay=} "
|
|
282
|
+
f"{total_deadline=} for {fn_name} ({idempotency_key[:8]})"
|
|
283
|
+
)
|
|
284
|
+
if isinstance(exc, OSError):
|
|
285
|
+
raise ConnectionError(str(exc))
|
|
286
|
+
elif isinstance(exc, asyncio.TimeoutError):
|
|
287
|
+
raise ConnectionError(str(exc))
|
|
288
|
+
else:
|
|
289
|
+
raise exc
|
|
290
|
+
|
|
291
|
+
if isinstance(exc, AttributeError) and "_write_appdata" not in str(exc):
|
|
292
|
+
# StreamTerminatedError are not properly raised in grpclib<=0.4.7
|
|
293
|
+
# fixed in https://github.com/vmagamedov/grpclib/issues/185
|
|
294
|
+
# TODO: update to newer version (>=0.4.8) once stable
|
|
295
|
+
raise exc
|
|
296
|
+
|
|
297
|
+
logger.debug(f"Retryable failure {repr(exc)} {n_retries=} {delay=} for {fn_name} ({idempotency_key[:8]})")
|
|
298
|
+
|
|
299
|
+
|
|
254
300
|
async def _retry_transient_errors(
|
|
255
301
|
fn: typing.Union[
|
|
256
302
|
"modal._grpc_client.UnaryUnaryWrapper[RequestType, ResponseType]",
|
|
@@ -273,12 +319,15 @@ async def _retry_transient_errors(
|
|
|
273
319
|
|
|
274
320
|
delay = retry.base_delay
|
|
275
321
|
n_retries = 0
|
|
322
|
+
n_throttled_retries = 0
|
|
276
323
|
|
|
277
324
|
status_codes = [*RETRYABLE_GRPC_STATUS_CODES, *retry.additional_status_codes]
|
|
278
325
|
|
|
279
326
|
idempotency_key = str(uuid.uuid4())
|
|
280
327
|
|
|
281
328
|
t0 = time.time()
|
|
329
|
+
last_server_retry_warning_time = None
|
|
330
|
+
|
|
282
331
|
if retry.total_timeout is not None:
|
|
283
332
|
total_deadline = t0 + retry.total_timeout
|
|
284
333
|
else:
|
|
@@ -290,14 +339,18 @@ async def _retry_transient_errors(
|
|
|
290
339
|
attempt_metadata = [
|
|
291
340
|
("x-idempotency-key", idempotency_key),
|
|
292
341
|
("x-retry-attempt", str(n_retries)),
|
|
342
|
+
("x-throttle-retry-attempt", str(n_throttled_retries)),
|
|
293
343
|
*metadata,
|
|
294
344
|
]
|
|
295
345
|
if n_retries > 0:
|
|
296
346
|
attempt_metadata.append(("x-retry-delay", str(time.time() - t0)))
|
|
347
|
+
if n_throttled_retries > 0:
|
|
348
|
+
attempt_metadata.append(("x-throttle-retry-delay", str(time.time() - t0)))
|
|
349
|
+
|
|
297
350
|
timeouts = []
|
|
298
351
|
if retry.attempt_timeout is not None:
|
|
299
352
|
timeouts.append(retry.attempt_timeout)
|
|
300
|
-
if
|
|
353
|
+
if total_deadline is not None:
|
|
301
354
|
timeouts.append(max(total_deadline - time.time(), retry.attempt_timeout_floor))
|
|
302
355
|
if timeouts:
|
|
303
356
|
timeout = min(timeouts) # In case the function provided both types of timeouts
|
|
@@ -307,6 +360,39 @@ async def _retry_transient_errors(
|
|
|
307
360
|
with suppress_tb_frames(1):
|
|
308
361
|
return await fn_callable(req, metadata=attempt_metadata, timeout=timeout)
|
|
309
362
|
except (StreamTerminatedError, GRPCError, OSError, asyncio.TimeoutError, AttributeError) as exc:
|
|
363
|
+
# Server side instruction for retries
|
|
364
|
+
if isinstance(exc, GRPCError) and (server_retry_policy := get_server_retry_policy(exc)):
|
|
365
|
+
server_delay = server_retry_policy.retry_after_secs
|
|
366
|
+
|
|
367
|
+
# When `total_deadline` is not defined and server gives instruction for retries, then use
|
|
368
|
+
# `max_throttle_wait`.
|
|
369
|
+
if total_deadline is None and (max_throttle_wait := config.get("max_throttle_wait")):
|
|
370
|
+
total_deadline = t0 + max_throttle_wait
|
|
371
|
+
|
|
372
|
+
final_attempt = (
|
|
373
|
+
total_deadline is not None
|
|
374
|
+
and time.time() + server_delay + retry.attempt_timeout_floor >= total_deadline
|
|
375
|
+
)
|
|
376
|
+
with suppress_tb_frames(1):
|
|
377
|
+
process_exception_before_retry(
|
|
378
|
+
exc, final_attempt, fn.name, n_retries, server_delay, total_deadline, idempotency_key
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
now = time.time()
|
|
382
|
+
if last_server_retry_warning_time is None or (
|
|
383
|
+
now - last_server_retry_warning_time >= SERVER_RETRY_WARNING_TIME_INTERVAL
|
|
384
|
+
):
|
|
385
|
+
last_server_retry_warning_time = now
|
|
386
|
+
logger.warning(
|
|
387
|
+
f"Warning: Received failure {exc.status}: {exc.message}. "
|
|
388
|
+
f"Will retry in {server_delay:0.2f} seconds."
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
n_throttled_retries += 1
|
|
392
|
+
await asyncio.sleep(server_delay)
|
|
393
|
+
continue
|
|
394
|
+
|
|
395
|
+
# Client handles retry
|
|
310
396
|
if isinstance(exc, GRPCError) and exc.status not in status_codes:
|
|
311
397
|
if exc.status == Status.UNAUTHENTICATED:
|
|
312
398
|
raise AuthError(exc.message)
|
|
@@ -320,25 +406,9 @@ async def _retry_transient_errors(
|
|
|
320
406
|
final_attempt = False
|
|
321
407
|
|
|
322
408
|
with suppress_tb_frames(1):
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
f"{total_deadline=} for {fn.name} ({idempotency_key[:8]})"
|
|
327
|
-
)
|
|
328
|
-
if isinstance(exc, OSError):
|
|
329
|
-
raise ConnectionError(str(exc))
|
|
330
|
-
elif isinstance(exc, asyncio.TimeoutError):
|
|
331
|
-
raise ConnectionError(str(exc))
|
|
332
|
-
else:
|
|
333
|
-
raise exc
|
|
334
|
-
|
|
335
|
-
if isinstance(exc, AttributeError) and "_write_appdata" not in str(exc):
|
|
336
|
-
# StreamTerminatedError are not properly raised in grpclib<=0.4.7
|
|
337
|
-
# fixed in https://github.com/vmagamedov/grpclib/issues/185
|
|
338
|
-
# TODO: update to newer version (>=0.4.8) once stable
|
|
339
|
-
raise exc
|
|
340
|
-
|
|
341
|
-
logger.debug(f"Retryable failure {repr(exc)} {n_retries=} {delay=} for {fn.name} ({idempotency_key[:8]})")
|
|
409
|
+
process_exception_before_retry(
|
|
410
|
+
exc, final_attempt, fn.name, n_retries, delay, total_deadline, idempotency_key
|
|
411
|
+
)
|
|
342
412
|
|
|
343
413
|
n_retries += 1
|
|
344
414
|
|
|
@@ -5,7 +5,7 @@ import platform
|
|
|
5
5
|
import sys
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import warnings
|
|
8
|
-
from collections.abc import AsyncGenerator,
|
|
8
|
+
from collections.abc import AsyncGenerator, Collection, Mapping
|
|
9
9
|
from typing import Any, ClassVar, Optional, TypeVar, Union
|
|
10
10
|
|
|
11
11
|
import grpclib.client
|
|
@@ -14,7 +14,7 @@ from google.protobuf.message import Message
|
|
|
14
14
|
from synchronicity.async_wrap import asynccontextmanager
|
|
15
15
|
|
|
16
16
|
from modal._utils.async_utils import synchronizer
|
|
17
|
-
from modal_proto import
|
|
17
|
+
from modal_proto import api_pb2, modal_api_grpc
|
|
18
18
|
from modal_version import __version__
|
|
19
19
|
|
|
20
20
|
from ._traceback import print_server_warnings
|
|
@@ -70,10 +70,10 @@ class _Client:
|
|
|
70
70
|
_client_from_env: ClassVar[Optional["_Client"]] = None
|
|
71
71
|
_client_from_env_lock: ClassVar[Optional[asyncio.Lock]] = None
|
|
72
72
|
_cancellation_context: TaskContext
|
|
73
|
-
_cancellation_context_event_loop: asyncio.AbstractEventLoop = None
|
|
74
|
-
_stub: Optional[
|
|
75
|
-
_auth_token_manager: _AuthTokenManager = None
|
|
76
|
-
_snapshotted: bool
|
|
73
|
+
_cancellation_context_event_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
74
|
+
_stub: Optional[modal_api_grpc.ModalClientModal]
|
|
75
|
+
_auth_token_manager: Optional[_AuthTokenManager] = None
|
|
76
|
+
_snapshotted: bool = False
|
|
77
77
|
|
|
78
78
|
def __init__(
|
|
79
79
|
self,
|
|
@@ -90,8 +90,8 @@ class _Client:
|
|
|
90
90
|
self._credentials = credentials
|
|
91
91
|
self.version = version
|
|
92
92
|
self._closed = False
|
|
93
|
-
self._stub
|
|
94
|
-
self._auth_token_manager
|
|
93
|
+
self._stub = None
|
|
94
|
+
self._auth_token_manager = None
|
|
95
95
|
self._snapshotted = False
|
|
96
96
|
self._owner_pid = None
|
|
97
97
|
|
|
@@ -163,7 +163,7 @@ class _Client:
|
|
|
163
163
|
|
|
164
164
|
@classmethod
|
|
165
165
|
@asynccontextmanager
|
|
166
|
-
async def anonymous(cls, server_url: str) ->
|
|
166
|
+
async def anonymous(cls, server_url: str) -> AsyncGenerator["_Client", None]:
|
|
167
167
|
"""mdmd:hidden
|
|
168
168
|
Create a connection with no credentials; to be used for token creation.
|
|
169
169
|
"""
|
|
@@ -5,7 +5,6 @@ import google.protobuf.message
|
|
|
5
5
|
import grpclib.client
|
|
6
6
|
import modal._utils.async_utils
|
|
7
7
|
import modal._utils.auth_token_manager
|
|
8
|
-
import modal_proto.api_grpc
|
|
9
8
|
import modal_proto.modal_api_grpc
|
|
10
9
|
import synchronicity.combined_types
|
|
11
10
|
import typing
|
|
@@ -23,9 +22,9 @@ class _Client:
|
|
|
23
22
|
_client_from_env: typing.ClassVar[typing.Optional[_Client]]
|
|
24
23
|
_client_from_env_lock: typing.ClassVar[typing.Optional[asyncio.locks.Lock]]
|
|
25
24
|
_cancellation_context: modal._utils.async_utils.TaskContext
|
|
26
|
-
_cancellation_context_event_loop: asyncio.events.AbstractEventLoop
|
|
27
|
-
_stub: typing.Optional[modal_proto.
|
|
28
|
-
_auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
|
|
25
|
+
_cancellation_context_event_loop: typing.Optional[asyncio.events.AbstractEventLoop]
|
|
26
|
+
_stub: typing.Optional[modal_proto.modal_api_grpc.ModalClientModal]
|
|
27
|
+
_auth_token_manager: typing.Optional[modal._utils.auth_token_manager._AuthTokenManager]
|
|
29
28
|
_snapshotted: bool
|
|
30
29
|
|
|
31
30
|
def __init__(
|
|
@@ -33,7 +32,7 @@ class _Client:
|
|
|
33
32
|
server_url: str,
|
|
34
33
|
client_type: int,
|
|
35
34
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.2.3.
|
|
35
|
+
version: str = "1.2.3.dev14",
|
|
37
36
|
):
|
|
38
37
|
"""mdmd:hidden
|
|
39
38
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -154,9 +153,9 @@ class Client:
|
|
|
154
153
|
_client_from_env: typing.ClassVar[typing.Optional[Client]]
|
|
155
154
|
_client_from_env_lock: typing.ClassVar[typing.Optional[asyncio.locks.Lock]]
|
|
156
155
|
_cancellation_context: modal._utils.async_utils.TaskContext
|
|
157
|
-
_cancellation_context_event_loop: asyncio.events.AbstractEventLoop
|
|
158
|
-
_stub: typing.Optional[modal_proto.
|
|
159
|
-
_auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
|
|
156
|
+
_cancellation_context_event_loop: typing.Optional[asyncio.events.AbstractEventLoop]
|
|
157
|
+
_stub: typing.Optional[modal_proto.modal_api_grpc.ModalClientModal]
|
|
158
|
+
_auth_token_manager: typing.Optional[modal._utils.auth_token_manager._AuthTokenManager]
|
|
160
159
|
_snapshotted: bool
|
|
161
160
|
|
|
162
161
|
def __init__(
|
|
@@ -164,7 +163,7 @@ class Client:
|
|
|
164
163
|
server_url: str,
|
|
165
164
|
client_type: int,
|
|
166
165
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.2.3.
|
|
166
|
+
version: str = "1.2.3.dev14",
|
|
168
167
|
):
|
|
169
168
|
"""mdmd:hidden
|
|
170
169
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -217,6 +217,13 @@ def _enforce_suffix_rules(x: str) -> str:
|
|
|
217
217
|
return x
|
|
218
218
|
|
|
219
219
|
|
|
220
|
+
def _int_or_none(x: str) -> Optional[int]:
|
|
221
|
+
if not x:
|
|
222
|
+
return None
|
|
223
|
+
x_int = int(x)
|
|
224
|
+
return x_int if x_int > 0 else None
|
|
225
|
+
|
|
226
|
+
|
|
220
227
|
class _Setting(typing.NamedTuple):
|
|
221
228
|
default: typing.Any = None
|
|
222
229
|
transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
|
|
@@ -258,6 +265,7 @@ _SETTINGS = {
|
|
|
258
265
|
transform=lambda s: _check_value(["pickle", "cbor"])(s.lower()),
|
|
259
266
|
),
|
|
260
267
|
"dev_suffix": _Setting("", transform=_enforce_suffix_rules),
|
|
268
|
+
"max_throttle_wait": _Setting(None, transform=_int_or_none),
|
|
261
269
|
}
|
|
262
270
|
|
|
263
271
|
|
|
@@ -408,7 +408,7 @@ class Function(
|
|
|
408
408
|
|
|
409
409
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
|
410
410
|
|
|
411
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
411
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
412
412
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
413
413
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
414
414
|
...
|
|
@@ -417,7 +417,7 @@ class Function(
|
|
|
417
417
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
418
418
|
...
|
|
419
419
|
|
|
420
|
-
remote: __remote_spec[modal._functions.
|
|
420
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
421
421
|
|
|
422
422
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
423
423
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -444,7 +444,7 @@ class Function(
|
|
|
444
444
|
"""
|
|
445
445
|
...
|
|
446
446
|
|
|
447
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
447
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
448
448
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
449
449
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
450
450
|
|
|
@@ -468,7 +468,7 @@ class Function(
|
|
|
468
468
|
...
|
|
469
469
|
|
|
470
470
|
_experimental_spawn: ___experimental_spawn_spec[
|
|
471
|
-
modal._functions.
|
|
471
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
|
472
472
|
]
|
|
473
473
|
|
|
474
474
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
|
@@ -477,7 +477,7 @@ class Function(
|
|
|
477
477
|
|
|
478
478
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
|
479
479
|
|
|
480
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
480
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
481
481
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
482
482
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
483
483
|
|
|
@@ -498,7 +498,7 @@ class Function(
|
|
|
498
498
|
"""
|
|
499
499
|
...
|
|
500
500
|
|
|
501
|
-
spawn: __spawn_spec[modal._functions.
|
|
501
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
502
502
|
|
|
503
503
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
504
504
|
"""Return the inner Python object wrapped by this Modal Function."""
|
|
@@ -2642,6 +2642,12 @@ message QueuePutRequest {
|
|
|
2642
2642
|
int32 partition_ttl_seconds = 6;
|
|
2643
2643
|
}
|
|
2644
2644
|
|
|
2645
|
+
// Retry repolicy used by GRPCError.details for the server to give instructions
|
|
2646
|
+
// for the client to retry.
|
|
2647
|
+
message RPCRetryPolicy {
|
|
2648
|
+
float retry_after_secs = 1;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2645
2651
|
// A copy google.rpc.Status for GRPCError.details:
|
|
2646
2652
|
// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto
|
|
2647
2653
|
// RPCStatus is compatible with google.rpc.Status, so one can encode messages using
|