modal 1.5.6.dev30__tar.gz → 1.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modal-1.5.6.dev30 → modal-1.6.0}/PKG-INFO +1 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_functions.py +9 -5
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_grpc_client.py +9 -4
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_image.py +16 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_partial_function.py +3 -3
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_server.py +16 -11
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_server.pyi +23 -17
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/grpc_utils.py +43 -8
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/app.py +310 -308
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/entry_point.py +8 -8
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/environment.py +134 -132
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/function.py +788 -784
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/image.py +4 -2
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/queues.py +42 -42
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/server.py +727 -736
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/client.py +10 -5
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/client.pyi +11 -11
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/config.py +1 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/mount.py +9 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/mount.pyi +5 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/partial_function.pyi +3 -3
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/queue.py +16 -2
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox.py +85 -72
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox.pyi +31 -8
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/secret.py +12 -9
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/secret.pyi +5 -5
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/server.pyi +1 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/types.py +94 -67
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_grpc.py +16 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2.py +1106 -1061
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2.pyi +118 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2_grpc.py +33 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2_grpc.pyi +10 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_version/__init__.py +1 -1
- {modal-1.5.6.dev30 → modal-1.6.0}/LICENSE +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/README.md +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/__main__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_billing.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_cluster.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_cluster.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_environments.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_function_variants.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_ipython.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_load_context.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_location.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs_manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_object.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_outbound_policy.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_outbound_policy.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/pty.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/rich.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/status.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_resolver.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_resources.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_serialization.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_supports_logs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_traceback.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_tunnel.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_tunnel.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_type_manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/logger.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/version.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_watcher.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/_workspace.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/app.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/app.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/billing.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/billing.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/README.md +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/base-images.json +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/call_graph.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_download.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_help.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_logs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_stats.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_traceback.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/billing.py +85 -85
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/changelog.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/config.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/container.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/curl.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/dashboard.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/dict.py +16 -16
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/endpoint.py +67 -67
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/import_refs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/logo.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/network_file_system.py +64 -64
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/profile.py +19 -19
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/run.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/secret.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/selector.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/shell.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/skills.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/token.py +50 -50
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/utils.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/volume.py +121 -121
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/workspace.py +34 -34
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cls.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cls.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cluster.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/cluster.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/container_process.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/container_process.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/dict.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/dict.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/environments.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/environments.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/exception.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/_flash_container_entry.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash_server.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash_server.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/ipython.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/functions.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/functions.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/image.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/image.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/io_streams.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/io_streams.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/network_file_system.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/network_file_system.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/object.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/object.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/output.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/parallel_map.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/parallel_map.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/partial_function.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/proxy.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/proxy.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/py.typed +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/queue.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/retries.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/runner.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/runner.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/running_app.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox_fs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/schedule.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/scheduler_placement.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/server.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/serving.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/serving.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/snapshot.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/snapshot.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/stream_type.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/token_flow.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/token_flow.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/volume.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/volume.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/workspace.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal/workspace.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/__init__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/py.typed +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/modal_version/__main__.py +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/pyproject.toml +0 -0
- {modal-1.5.6.dev30 → modal-1.6.0}/setup.cfg +0 -0
|
@@ -1262,15 +1262,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1262
1262
|
},
|
|
1263
1263
|
# We don't have any secret IDs yet as we are dehydrated, so we return the reprs instead
|
|
1264
1264
|
secrets=[repr(s) for s in secrets],
|
|
1265
|
-
|
|
1265
|
+
startup_timeout=startup_timeout,
|
|
1266
|
+
_inner_server_info=FunctionInfo._InnerServerInfo(
|
|
1267
|
+
server_url="",
|
|
1266
1268
|
port=http_config.port,
|
|
1267
1269
|
unauthenticated=http_config.unauthenticated,
|
|
1268
1270
|
h2_enabled=http_config.h2_enabled,
|
|
1269
|
-
|
|
1271
|
+
routing_region=http_config.proxy_regions[0] if http_config.proxy_regions else None,
|
|
1272
|
+
sessioned=is_sessioned,
|
|
1273
|
+
startup_timeout=http_config.startup_timeout,
|
|
1274
|
+
exit_grace_period=http_config.exit_grace_period,
|
|
1270
1275
|
)
|
|
1271
1276
|
if http_config
|
|
1272
1277
|
else None,
|
|
1273
|
-
_sessioned=is_sessioned,
|
|
1274
1278
|
web_info=FunctionInfo.WebInfo._from_proto("", webhook_config) if webhook_config else None,
|
|
1275
1279
|
method_names=list(method_definitions.keys()) if method_definitions is not None else None,
|
|
1276
1280
|
method_details={
|
|
@@ -1745,14 +1749,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1745
1749
|
return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
|
|
1746
1750
|
|
|
1747
1751
|
async def _fetch_flash_auth_token(self, retry: Retry) -> str:
|
|
1748
|
-
resp = await self.client.
|
|
1752
|
+
resp = await self.client._stub.FunctionGetFlashAuthToken(
|
|
1749
1753
|
api_pb2.FunctionGetFlashAuthTokenRequest(function_id=self.object_id), retry=retry
|
|
1750
1754
|
)
|
|
1751
1755
|
return resp.token
|
|
1752
1756
|
|
|
1753
1757
|
async def _get_flash_auth_token(self) -> str:
|
|
1754
1758
|
if self._flash_token_manager is None:
|
|
1755
|
-
self._flash_token_manager = _AuthTokenManager(self.client.
|
|
1759
|
+
self._flash_token_manager = _AuthTokenManager(self.client._stub, fetch=self._fetch_flash_auth_token)
|
|
1756
1760
|
return await self._flash_token_manager.get_token()
|
|
1757
1761
|
|
|
1758
1762
|
def _apply_dynamic_config(
|
|
@@ -44,6 +44,14 @@ _STATUS_TO_EXCEPTION: dict[Status, type[exception._GRPCErrorWrapper]] = {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def grpc_error_to_modal_exception(exc: GRPCError) -> exception._GRPCErrorWrapper:
|
|
48
|
+
modal_exc = _STATUS_TO_EXCEPTION[exc.status](exc.message)
|
|
49
|
+
modal_exc._grpc_message = exc.message or ""
|
|
50
|
+
modal_exc._grpc_status = exc.status
|
|
51
|
+
modal_exc._grpc_details = exc.details
|
|
52
|
+
return modal_exc
|
|
53
|
+
|
|
54
|
+
|
|
47
55
|
class grpc_error_converter:
|
|
48
56
|
def __enter__(self):
|
|
49
57
|
pass
|
|
@@ -53,10 +61,7 @@ class grpc_error_converter:
|
|
|
53
61
|
use_full_traceback = config.get("traceback")
|
|
54
62
|
with suppress_tb_frame():
|
|
55
63
|
if isinstance(exc, GRPCError):
|
|
56
|
-
modal_exc =
|
|
57
|
-
modal_exc._grpc_message = exc.message or ""
|
|
58
|
-
modal_exc._grpc_status = exc.status
|
|
59
|
-
modal_exc._grpc_details = exc.details
|
|
64
|
+
modal_exc = grpc_error_to_modal_exception(exc)
|
|
60
65
|
if use_full_traceback:
|
|
61
66
|
raise modal_exc
|
|
62
67
|
else:
|
|
@@ -354,6 +354,16 @@ def _create_context_mount(
|
|
|
354
354
|
copy_patterns = extract_copy_command_patterns(docker_commands)
|
|
355
355
|
if not copy_patterns:
|
|
356
356
|
return None # no mount needed
|
|
357
|
+
|
|
358
|
+
resolved_context_dir = context_dir.resolve()
|
|
359
|
+
for copy_pattern in copy_patterns:
|
|
360
|
+
# Docker treats leading slashes as relative to the build context root.
|
|
361
|
+
resolved_source = (resolved_context_dir / copy_pattern.lstrip("/")).resolve()
|
|
362
|
+
if not resolved_source.is_relative_to(resolved_context_dir):
|
|
363
|
+
raise InvalidError(
|
|
364
|
+
f"COPY source {copy_pattern!r} is outside the build context directory ({str(context_dir)!r})."
|
|
365
|
+
)
|
|
366
|
+
|
|
357
367
|
include_fn = FilePatternMatcher(*copy_patterns)
|
|
358
368
|
|
|
359
369
|
def ignore_with_include(source: Path) -> bool:
|
|
@@ -364,7 +374,12 @@ def _create_context_mount(
|
|
|
364
374
|
|
|
365
375
|
return False
|
|
366
376
|
|
|
367
|
-
return _Mount._add_local_dir(
|
|
377
|
+
return _Mount._add_local_dir(
|
|
378
|
+
context_dir,
|
|
379
|
+
PurePosixPath("/"),
|
|
380
|
+
ignore=ignore_with_include,
|
|
381
|
+
filter_external_links=True,
|
|
382
|
+
)
|
|
368
383
|
|
|
369
384
|
|
|
370
385
|
def _create_context_mount_function(
|
|
@@ -732,7 +732,7 @@ def _sessioned() -> Callable[
|
|
|
732
732
|
[Callable[P, ReturnType] | _PartialFunction[P, ReturnType, ReturnType]],
|
|
733
733
|
_PartialFunction[P, ReturnType, ReturnType],
|
|
734
734
|
]:
|
|
735
|
-
"""Decorator that enables sessions on a Server.
|
|
735
|
+
"""Decorator that enables sticky sessions on a Server.
|
|
736
736
|
|
|
737
737
|
Every request must carry a session token obtained from a session start request; requests with the same token are
|
|
738
738
|
routed to the same container until the session is idle for `idle_timeout` seconds or explicitly terminated. A
|
|
@@ -741,7 +741,7 @@ def _sessioned() -> Callable[
|
|
|
741
741
|
Only valid with `@app.server()`.
|
|
742
742
|
|
|
743
743
|
Examples:
|
|
744
|
-
Define a sessioned
|
|
744
|
+
Define a Server with the `@modal.sessioned()` decorator:
|
|
745
745
|
|
|
746
746
|
```python
|
|
747
747
|
app = modal.App("my-app")
|
|
@@ -828,7 +828,7 @@ def _clustered(*, size: int, rdma: bool = False) -> _ClusteredDecorator:
|
|
|
828
828
|
containers and must be multiples of `size`. For example, `size=4` with
|
|
829
829
|
`min_containers=8` keeps two clusters warm.
|
|
830
830
|
|
|
831
|
-
See the [multi-node clusters guide](https://modal.com/docs/guide/multi-node-
|
|
831
|
+
See the [multi-node clusters guide](https://modal.com/docs/guide/multi-node-clusters)
|
|
832
832
|
for hardware requirements and networking details.
|
|
833
833
|
|
|
834
834
|
Parameters:
|
|
@@ -113,7 +113,7 @@ class _Server:
|
|
|
113
113
|
|
|
114
114
|
@property
|
|
115
115
|
def sessions(self) -> "_ServerSessionsManager":
|
|
116
|
-
"""Start and terminate sessions on a Server decorated with `@modal.sessioned()`."""
|
|
116
|
+
"""Start and terminate sticky sessions on a Server decorated with `@modal.sessioned()`."""
|
|
117
117
|
return _ServerSessionsManager(self)
|
|
118
118
|
|
|
119
119
|
async def info(self, *, refresh: bool = False) -> ServerInfo:
|
|
@@ -160,7 +160,7 @@ class _Server:
|
|
|
160
160
|
This interface is experimental and may change or be removed without warning.
|
|
161
161
|
"""
|
|
162
162
|
service_function = self._get_service_function()
|
|
163
|
-
response = await service_function.client.
|
|
163
|
+
response = await service_function.client._stub.FlashContainerList(
|
|
164
164
|
api_pb2.FlashContainerListRequest(function_id=service_function.object_id)
|
|
165
165
|
)
|
|
166
166
|
return [
|
|
@@ -250,8 +250,11 @@ class _Server:
|
|
|
250
250
|
# This is required since we want to support @livemethod() decorated methods
|
|
251
251
|
service_function = self._get_service_function()
|
|
252
252
|
await service_function.hydrate(client)
|
|
253
|
-
if
|
|
254
|
-
|
|
253
|
+
if (
|
|
254
|
+
service_function._function_info is not None
|
|
255
|
+
and service_function._function_info._inner_server_info is not None
|
|
256
|
+
):
|
|
257
|
+
self._is_sessioned = service_function._function_info._inner_server_info.sessioned
|
|
255
258
|
return self
|
|
256
259
|
|
|
257
260
|
@classmethod
|
|
@@ -470,13 +473,13 @@ class _Server:
|
|
|
470
473
|
request.until.FromDatetime(until)
|
|
471
474
|
if container:
|
|
472
475
|
request.container_id = container
|
|
473
|
-
stats = await self._get_service_function().client.
|
|
476
|
+
stats = await self._get_service_function().client._stub.ServerGetTimeRangeStats(request)
|
|
474
477
|
return ServerStats._from_proto(stats)
|
|
475
478
|
|
|
476
479
|
|
|
477
480
|
@retry(n_attempts=5, base_delay=0.5, attempt_timeout=65, total_timeout=200)
|
|
478
481
|
async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
|
|
479
|
-
"""POST to a session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
|
|
482
|
+
"""POST to a sticky session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
|
|
480
483
|
async with ClientSessionRegistry.get_session().post(url, headers=headers) as resp:
|
|
481
484
|
body = await resp.text()
|
|
482
485
|
if resp.status >= 400:
|
|
@@ -498,11 +501,11 @@ class _ServerSessionsManager:
|
|
|
498
501
|
raise InvalidError("`sessions` requires `@modal.sessioned()` on the Server.")
|
|
499
502
|
|
|
500
503
|
async def start(self, idle_timeout: int = 600) -> ServerSessionCredentials:
|
|
501
|
-
"""Start a session and return its ID and token.
|
|
504
|
+
"""Start a sticky session and return its ID and token.
|
|
502
505
|
|
|
503
|
-
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
504
|
-
has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
505
|
-
as it holds a live session.
|
|
506
|
+
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
507
|
+
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
508
|
+
for as long as it holds a live session.
|
|
506
509
|
|
|
507
510
|
Args:
|
|
508
511
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
@@ -547,7 +550,9 @@ class _ServerSessionsManager:
|
|
|
547
550
|
raise ExecutionError("Failed to start session: unexpected response from server") from None
|
|
548
551
|
|
|
549
552
|
async def terminate(self, token: str) -> None:
|
|
550
|
-
"""Terminate a session.
|
|
553
|
+
"""Terminate a sticky session.
|
|
554
|
+
|
|
555
|
+
New requests to it will be rejected. The container continues serving other sessions.
|
|
551
556
|
|
|
552
557
|
Args:
|
|
553
558
|
token: The `token` of the `ServerSessionCredentials` to terminate.
|
|
@@ -67,7 +67,7 @@ class _Server:
|
|
|
67
67
|
|
|
68
68
|
@property
|
|
69
69
|
def sessions(self) -> _ServerSessionsManager:
|
|
70
|
-
"""Start and terminate sessions on a Server decorated with `@modal.sessioned()`."""
|
|
70
|
+
"""Start and terminate sticky sessions on a Server decorated with `@modal.sessioned()`."""
|
|
71
71
|
...
|
|
72
72
|
|
|
73
73
|
async def info(self, *, refresh: bool = False) -> modal.types.ServerInfo:
|
|
@@ -272,7 +272,7 @@ class _Server:
|
|
|
272
272
|
...
|
|
273
273
|
|
|
274
274
|
async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
|
|
275
|
-
"""POST to a session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
|
|
275
|
+
"""POST to a sticky session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
|
|
276
276
|
...
|
|
277
277
|
|
|
278
278
|
class _ServerSessionsManager:
|
|
@@ -283,11 +283,11 @@ class _ServerSessionsManager:
|
|
|
283
283
|
|
|
284
284
|
def _validate(self) -> None: ...
|
|
285
285
|
async def start(self, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
|
|
286
|
-
"""Start a session and return its ID and token.
|
|
286
|
+
"""Start a sticky session and return its ID and token.
|
|
287
287
|
|
|
288
|
-
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
289
|
-
has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
290
|
-
as it holds a live session.
|
|
288
|
+
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
289
|
+
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
290
|
+
for as long as it holds a live session.
|
|
291
291
|
|
|
292
292
|
Args:
|
|
293
293
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
@@ -308,7 +308,9 @@ class _ServerSessionsManager:
|
|
|
308
308
|
...
|
|
309
309
|
|
|
310
310
|
async def terminate(self, token: str) -> None:
|
|
311
|
-
"""Terminate a session.
|
|
311
|
+
"""Terminate a sticky session.
|
|
312
|
+
|
|
313
|
+
New requests to it will be rejected. The container continues serving other sessions.
|
|
312
314
|
|
|
313
315
|
Args:
|
|
314
316
|
token: The `token` of the `ServerSessionCredentials` to terminate.
|
|
@@ -334,11 +336,11 @@ class ServerSessionsManager:
|
|
|
334
336
|
|
|
335
337
|
class __start_spec(typing_extensions.Protocol):
|
|
336
338
|
def __call__(self, /, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
|
|
337
|
-
"""Start a session and return its ID and token.
|
|
339
|
+
"""Start a sticky session and return its ID and token.
|
|
338
340
|
|
|
339
|
-
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
340
|
-
has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
341
|
-
as it holds a live session.
|
|
341
|
+
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
342
|
+
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
343
|
+
for as long as it holds a live session.
|
|
342
344
|
|
|
343
345
|
Args:
|
|
344
346
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
@@ -359,11 +361,11 @@ class ServerSessionsManager:
|
|
|
359
361
|
...
|
|
360
362
|
|
|
361
363
|
async def aio(self, /, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
|
|
362
|
-
"""Start a session and return its ID and token.
|
|
364
|
+
"""Start a sticky session and return its ID and token.
|
|
363
365
|
|
|
364
|
-
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
365
|
-
has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
366
|
-
as it holds a live session.
|
|
366
|
+
Requests to the server URL that carry the returned token are routed to the same container until the
|
|
367
|
+
session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
|
|
368
|
+
for as long as it holds a live session.
|
|
367
369
|
|
|
368
370
|
Args:
|
|
369
371
|
idle_timeout: Seconds without an in-flight request before the session ends.
|
|
@@ -387,7 +389,9 @@ class ServerSessionsManager:
|
|
|
387
389
|
|
|
388
390
|
class __terminate_spec(typing_extensions.Protocol):
|
|
389
391
|
def __call__(self, /, token: str) -> None:
|
|
390
|
-
"""Terminate a session.
|
|
392
|
+
"""Terminate a sticky session.
|
|
393
|
+
|
|
394
|
+
New requests to it will be rejected. The container continues serving other sessions.
|
|
391
395
|
|
|
392
396
|
Args:
|
|
393
397
|
token: The `token` of the `ServerSessionCredentials` to terminate.
|
|
@@ -404,7 +408,9 @@ class ServerSessionsManager:
|
|
|
404
408
|
...
|
|
405
409
|
|
|
406
410
|
async def aio(self, /, token: str) -> None:
|
|
407
|
-
"""Terminate a session.
|
|
411
|
+
"""Terminate a sticky session.
|
|
412
|
+
|
|
413
|
+
New requests to it will be rejected. The container continues serving other sessions.
|
|
408
414
|
|
|
409
415
|
Args:
|
|
410
416
|
token: The `token` of the `ServerSessionCredentials` to terminate.
|
|
@@ -26,7 +26,12 @@ from grpclib.exceptions import StreamTerminatedError
|
|
|
26
26
|
from grpclib.protocol import H2Protocol
|
|
27
27
|
from h2.exceptions import ProtocolError as H2ProtocolError
|
|
28
28
|
|
|
29
|
-
from modal.exception import
|
|
29
|
+
from modal.exception import (
|
|
30
|
+
ClientClosed,
|
|
31
|
+
ConnectionError,
|
|
32
|
+
NotFoundError,
|
|
33
|
+
TimeoutError as ModalTimeoutError,
|
|
34
|
+
)
|
|
30
35
|
from modal_proto import api_pb2
|
|
31
36
|
from modal_version import __version__
|
|
32
37
|
|
|
@@ -486,6 +491,26 @@ class Retry:
|
|
|
486
491
|
warning_message: RetryWarningMessage | None = None
|
|
487
492
|
|
|
488
493
|
|
|
494
|
+
class RetryTimeoutError(ModalTimeoutError):
|
|
495
|
+
"""Raised by `_retry_transient_errors` when `Retry.total_timeout` is spent before an attempt succeeds.
|
|
496
|
+
|
|
497
|
+
Internal: callers with a user-supplied timeout should convert this into a plain `modal.exception.TimeoutError`
|
|
498
|
+
with a message referring to that timeout. `final_exception` is the failure of the last attempt (server errors
|
|
499
|
+
already converted to their `modal.exception` type), which the caller can inspect to decide how to report the
|
|
500
|
+
timeout.
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
final_exception: Exception
|
|
504
|
+
|
|
505
|
+
def __init__(self, final_exception: Exception):
|
|
506
|
+
super().__init__(
|
|
507
|
+
"The operation timed out after repeated failures "
|
|
508
|
+
f"(last error: {type(final_exception).__name__}: {final_exception}). "
|
|
509
|
+
"Please try again, and contact Modal support if the problem persists."
|
|
510
|
+
)
|
|
511
|
+
self.final_exception = final_exception
|
|
512
|
+
|
|
513
|
+
|
|
489
514
|
async def retry_transient_errors(
|
|
490
515
|
fn: "grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]",
|
|
491
516
|
req: RequestType,
|
|
@@ -527,14 +552,22 @@ def process_exception_before_retry(
|
|
|
527
552
|
delay: float,
|
|
528
553
|
idempotency_key: str,
|
|
529
554
|
rpc_elapsed: float,
|
|
555
|
+
total_deadline_reached: bool = False,
|
|
530
556
|
):
|
|
531
557
|
"""Process exception before retry, used by `_retry_transient_errors`."""
|
|
532
558
|
with suppress_tb_frame():
|
|
533
559
|
if final_attempt:
|
|
560
|
+
reason = "total deadline consumed" if total_deadline_reached else "max retries reached"
|
|
534
561
|
logger.debug(
|
|
535
|
-
f"Final attempt failed with {repr(exc)} {n_retries=} {delay=} {rpc_elapsed=:0.2f}s "
|
|
562
|
+
f"Final attempt failed ({reason}) with {repr(exc)} {n_retries=} {delay=} {rpc_elapsed=:0.2f}s "
|
|
536
563
|
f"for {fn_name} ({idempotency_key[:8]})"
|
|
537
564
|
)
|
|
565
|
+
if total_deadline_reached:
|
|
566
|
+
import modal._grpc_client
|
|
567
|
+
|
|
568
|
+
if isinstance(exc, GRPCError):
|
|
569
|
+
exc = modal._grpc_client.grpc_error_to_modal_exception(exc)
|
|
570
|
+
raise RetryTimeoutError(exc) from exc
|
|
538
571
|
if isinstance(exc, OSError):
|
|
539
572
|
raise ConnectionError(str(exc))
|
|
540
573
|
elif isinstance(exc, asyncio.TimeoutError):
|
|
@@ -664,6 +697,7 @@ async def _retry_transient_errors(
|
|
|
664
697
|
server_delay,
|
|
665
698
|
idempotency_key,
|
|
666
699
|
elapsed_time,
|
|
700
|
+
total_deadline_reached=total_timeout_will_be_reached,
|
|
667
701
|
)
|
|
668
702
|
|
|
669
703
|
if last_server_retry_warning_time is None or (
|
|
@@ -687,12 +721,12 @@ async def _retry_transient_errors(
|
|
|
687
721
|
# Client handles retry
|
|
688
722
|
if isinstance(exc, GRPCError) and exc.status not in status_codes:
|
|
689
723
|
raise exc
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
724
|
+
max_retries_reached = retry.max_retries is not None and n_retries >= retry.max_retries
|
|
725
|
+
total_deadline_reached = total_deadline is not None and (
|
|
726
|
+
time.time() >= total_deadline
|
|
727
|
+
or (not max_retries_reached and time.time() + delay + retry.attempt_timeout_floor >= total_deadline)
|
|
728
|
+
)
|
|
729
|
+
final_attempt = max_retries_reached or total_deadline_reached
|
|
696
730
|
|
|
697
731
|
with suppress_tb_frame():
|
|
698
732
|
process_exception_before_retry(
|
|
@@ -703,6 +737,7 @@ async def _retry_transient_errors(
|
|
|
703
737
|
delay,
|
|
704
738
|
idempotency_key,
|
|
705
739
|
time.monotonic() - attempt_started_at,
|
|
740
|
+
total_deadline_reached=total_deadline_reached,
|
|
706
741
|
)
|
|
707
742
|
|
|
708
743
|
n_retries += 1
|