modal 1.5.3.dev2__tar.gz → 1.5.3.dev4__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.3.dev2 → modal-1.5.3.dev4}/PKG-INFO +1 -1
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_container_entrypoint.py +2 -4
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_functions.py +26 -3
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/container_io_manager.py +6 -30
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/container_io_manager.pyi +5 -16
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/grpc_utils.py +1 -1
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/client.py +13 -7
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/client.pyi +15 -13
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cls.py +3 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/config.py +30 -1
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/functions.pyi +16 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/sandbox.py +40 -7
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/sandbox.pyi +95 -20
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/api_grpc.py +16 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/api_pb2.py +1170 -1137
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/api_pb2.pyi +83 -22
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/api_pb2_grpc.py +33 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/api_pb2_grpc.pyi +10 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/task_command_router_grpc.py +16 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/task_command_router_pb2.py +33 -13
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/task_command_router_pb2.pyi +31 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/task_command_router_pb2_grpc.py +37 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/task_command_router_pb2_grpc.pyi +20 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_version/__init__.py +1 -1
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/LICENSE +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/README.md +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/__main__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_billing.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_clustered_functions.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_environments.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_function_variants.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_grpc_client.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_image.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_ipython.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_load_context.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_location.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_logs.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_object.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_output/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_output/manager.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_output/pty.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_output/rich.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_output/status.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_partial_function.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_resolver.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_resources.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_serialization.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_server.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_traceback.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_tunnel.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_tunnel.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_type_manager.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/logger.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_vendor/version.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_watcher.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/_workspace.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/app.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/app.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/billing.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/billing.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/README.md +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/builder/base-images.json +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/call_graph.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/_download.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/_help.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/_traceback.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/app.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/billing.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/changelog.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/cluster.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/config.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/container.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/curl.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/dashboard.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/dict.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/endpoint.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/entry_point.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/environment.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/image.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/import_refs.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/launch.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/logo.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/profile.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/queues.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/run.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/secret.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/selector.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/shell.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/skills.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/token.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/utils.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/volume.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cli/workspace.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/cls.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/container_process.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/container_process.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/dict.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/dict.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/environments.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/environments.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/exception.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/experimental/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/experimental/flash.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/experimental/ipython.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/file_io.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/file_io.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/functions.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/image.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/image.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/io_streams.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/io_streams.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/mount.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/mount.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/network_file_system.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/network_file_system.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/object.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/object.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/output.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/parallel_map.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/parallel_map.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/partial_function.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/partial_function.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/proxy.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/proxy.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/py.typed +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/queue.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/queue.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/retries.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/runner.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/runner.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/running_app.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/sandbox_fs.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/schedule.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/scheduler_placement.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/secret.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/secret.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/server.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/server.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/serving.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/serving.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/snapshot.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/snapshot.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/stream_type.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/token_flow.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/token_flow.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/types.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/volume.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/volume.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/workspace.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal/workspace.pyi +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/__init__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_proto/py.typed +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/modal_version/__main__.py +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/pyproject.toml +0 -0
- {modal-1.5.3.dev2 → modal-1.5.3.dev4}/setup.cfg +0 -0
|
@@ -145,7 +145,7 @@ def call_function(
|
|
|
145
145
|
item_count += 1
|
|
146
146
|
|
|
147
147
|
await container_io_manager._send_outputs.aio(
|
|
148
|
-
|
|
148
|
+
io_context.output_items_generator_done(started_at, item_count)
|
|
149
149
|
)
|
|
150
150
|
else:
|
|
151
151
|
value = await io_context.call_function_async()
|
|
@@ -182,9 +182,7 @@ def call_function(
|
|
|
182
182
|
container_io_manager._queue_put(generator_queue, value)
|
|
183
183
|
item_count += 1
|
|
184
184
|
|
|
185
|
-
container_io_manager._send_outputs(
|
|
186
|
-
started_at, io_context.output_items_generator_done(started_at, item_count)
|
|
187
|
-
)
|
|
185
|
+
container_io_manager._send_outputs(io_context.output_items_generator_done(started_at, item_count))
|
|
188
186
|
else:
|
|
189
187
|
values = io_context.call_function_sync()
|
|
190
188
|
container_io_manager.push_outputs(io_context, started_at, values)
|
|
@@ -627,6 +627,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
627
627
|
_metadata: api_pb2.FunctionHandleMetadata | None = None
|
|
628
628
|
_options: _FunctionOptions
|
|
629
629
|
_base_function: "_Function | None" = None
|
|
630
|
+
_app_id: str | None = None
|
|
630
631
|
|
|
631
632
|
@staticmethod
|
|
632
633
|
def from_local(
|
|
@@ -1403,6 +1404,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1403
1404
|
metadata.max_object_size_bytes if metadata.HasField("max_object_size_bytes") else MAX_OBJECT_SIZE_BYTES
|
|
1404
1405
|
)
|
|
1405
1406
|
self._experimental_flash_urls = metadata._experimental_flash_urls
|
|
1407
|
+
self._app_id = metadata.app_id or None
|
|
1406
1408
|
|
|
1407
1409
|
# Invalidate the Function variant cache when we load new metadata, since the base Function handle
|
|
1408
1410
|
# is now in sync with the server but any previously-cached variants may be stale.
|
|
@@ -1412,6 +1414,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1412
1414
|
# Overridden concrete implementation of base class method
|
|
1413
1415
|
assert self._function_name, f"Function name must be set before metadata can be retrieved for {self}"
|
|
1414
1416
|
return api_pb2.FunctionHandleMetadata(
|
|
1417
|
+
app_id=self._app_id or "",
|
|
1415
1418
|
function_name=self._function_name,
|
|
1416
1419
|
function_type=get_function_type(self._is_generator),
|
|
1417
1420
|
web_url=self._web_url or "",
|
|
@@ -1673,6 +1676,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1673
1676
|
)
|
|
1674
1677
|
fc: _FunctionCall[ReturnType] = _FunctionCall._new_hydrated(function_call_id, self.client, None)
|
|
1675
1678
|
fc._num_inputs = num_inputs # set the cached value of num_inputs
|
|
1679
|
+
fc._app_id = self._app_id
|
|
1680
|
+
fc._function_id = self.object_id
|
|
1676
1681
|
return fc
|
|
1677
1682
|
|
|
1678
1683
|
async def _call_function(self, args, kwargs) -> ReturnType:
|
|
@@ -1884,6 +1889,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1884
1889
|
invocation.function_call_id, invocation.client, None
|
|
1885
1890
|
)
|
|
1886
1891
|
fc._is_generator = self._is_generator if self._is_generator else False
|
|
1892
|
+
fc._app_id = self._app_id
|
|
1893
|
+
fc._function_id = self.object_id
|
|
1887
1894
|
return fc
|
|
1888
1895
|
|
|
1889
1896
|
@synchronizer.no_input_translation
|
|
@@ -1920,6 +1927,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1920
1927
|
fc: _FunctionCall[ReturnType] = _FunctionCall._new_hydrated(
|
|
1921
1928
|
invocation.function_call_id, invocation.client, None
|
|
1922
1929
|
)
|
|
1930
|
+
fc._app_id = self._app_id
|
|
1931
|
+
fc._function_id = self.object_id
|
|
1923
1932
|
return fc
|
|
1924
1933
|
|
|
1925
1934
|
def get_raw_f(self) -> Callable[..., Any]:
|
|
@@ -1979,10 +1988,18 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1979
1988
|
|
|
1980
1989
|
_is_generator: bool = False
|
|
1981
1990
|
_num_inputs: int | None = None
|
|
1991
|
+
_app_id: str | None = None
|
|
1992
|
+
_function_id: str | None = None
|
|
1982
1993
|
|
|
1983
1994
|
def _invocation(self):
|
|
1984
1995
|
return _Invocation(self.client.stub, self.object_id, self.client)
|
|
1985
1996
|
|
|
1997
|
+
async def _hydrate_from_id_metadata(self) -> None:
|
|
1998
|
+
"""Hydrate metadata only when needed for FunctionCall fields."""
|
|
1999
|
+
request = api_pb2.FunctionCallFromIdRequest(function_call_id=self.object_id)
|
|
2000
|
+
resp = await self.client.stub.FunctionCallFromId(request)
|
|
2001
|
+
self._hydrate_metadata(resp)
|
|
2002
|
+
|
|
1986
2003
|
@live_method
|
|
1987
2004
|
async def num_inputs(self) -> int:
|
|
1988
2005
|
"""Get the number of inputs in the function call.
|
|
@@ -1991,9 +2008,8 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1991
2008
|
How many inputs this function call includes (e.g. `1` for `.spawn()`, more for `.spawn_map()`).
|
|
1992
2009
|
"""
|
|
1993
2010
|
if self._num_inputs is None:
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
self._num_inputs = resp.num_inputs # cached
|
|
2011
|
+
await self._hydrate_from_id_metadata()
|
|
2012
|
+
assert self._num_inputs is not None
|
|
1997
2013
|
return self._num_inputs
|
|
1998
2014
|
|
|
1999
2015
|
@live_method
|
|
@@ -2135,6 +2151,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
2135
2151
|
)
|
|
2136
2152
|
return typing.cast("modal.functions.FunctionCall[Any]", synchronizer._translate_out(impl_instance))
|
|
2137
2153
|
|
|
2154
|
+
def _hydrate_metadata(self, metadata: Message | None):
|
|
2155
|
+
# Overridden concrete implementation of base class method
|
|
2156
|
+
if isinstance(metadata, api_pb2.FunctionCallFromIdResponse):
|
|
2157
|
+
self._num_inputs = metadata.num_inputs
|
|
2158
|
+
self._app_id = metadata.metadata.app_id or None
|
|
2159
|
+
self._function_id = metadata.metadata.function_id or None
|
|
2160
|
+
|
|
2138
2161
|
@staticmethod
|
|
2139
2162
|
async def gather(*function_calls: "_FunctionCall[T]") -> typing.Sequence[T]:
|
|
2140
2163
|
"""Wait until all Modal FunctionCall objects have results before returning.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2024
|
|
2
2
|
import asyncio
|
|
3
3
|
import inspect
|
|
4
|
-
import math
|
|
5
4
|
import sys
|
|
6
5
|
import time
|
|
7
6
|
import traceback
|
|
@@ -48,8 +47,6 @@ DYNAMIC_CONCURRENCY_INTERVAL_SECS = 3
|
|
|
48
47
|
DYNAMIC_CONCURRENCY_TIMEOUT_SECS = 10
|
|
49
48
|
MAX_OUTPUT_BATCH_SIZE: int = 49
|
|
50
49
|
|
|
51
|
-
RTT_S: float = 0.5 # conservative estimate of RTT in seconds.
|
|
52
|
-
|
|
53
50
|
|
|
54
51
|
class Sentinel:
|
|
55
52
|
"""Used to get type-stubs to work with this object."""
|
|
@@ -474,8 +471,6 @@ class _ContainerIOManager:
|
|
|
474
471
|
_task_lifecycle_manager: _TaskLifecycleManager
|
|
475
472
|
input_plane_server_url: str | None
|
|
476
473
|
|
|
477
|
-
calls_completed: int
|
|
478
|
-
total_user_time: float
|
|
479
474
|
current_input_id: str | None
|
|
480
475
|
current_inputs: dict[str, IOContext] # input_id -> IOContext
|
|
481
476
|
current_input_started_at: float | None
|
|
@@ -510,8 +505,6 @@ class _ContainerIOManager:
|
|
|
510
505
|
|
|
511
506
|
self.input_plane_server_url = container_args.input_plane_server_url
|
|
512
507
|
|
|
513
|
-
self.calls_completed = 0
|
|
514
|
-
self.total_user_time = 0.0
|
|
515
508
|
self.current_input_id = None
|
|
516
509
|
self.current_inputs = {}
|
|
517
510
|
self.current_input_started_at = None
|
|
@@ -791,18 +784,6 @@ class _ContainerIOManager:
|
|
|
791
784
|
"""Put a value onto a queue, using the synchronicity event loop."""
|
|
792
785
|
await queue.put(value)
|
|
793
786
|
|
|
794
|
-
def get_average_call_time(self) -> float:
|
|
795
|
-
if self.calls_completed == 0:
|
|
796
|
-
return 0
|
|
797
|
-
|
|
798
|
-
return self.total_user_time / self.calls_completed
|
|
799
|
-
|
|
800
|
-
def get_max_inputs_to_fetch(self):
|
|
801
|
-
if self.calls_completed == 0:
|
|
802
|
-
return 1
|
|
803
|
-
|
|
804
|
-
return math.ceil(RTT_S / max(self.get_average_call_time(), 1e-6))
|
|
805
|
-
|
|
806
787
|
@synchronizer.no_io_translation
|
|
807
788
|
async def _generate_inputs(
|
|
808
789
|
self,
|
|
@@ -814,8 +795,6 @@ class _ContainerIOManager:
|
|
|
814
795
|
while self._fetching_inputs:
|
|
815
796
|
await self._input_slots.acquire()
|
|
816
797
|
|
|
817
|
-
request.average_call_time = self.get_average_call_time()
|
|
818
|
-
request.max_values = self.get_max_inputs_to_fetch() # Deprecated; remove.
|
|
819
798
|
request.input_concurrency = self.get_input_concurrency()
|
|
820
799
|
request.batch_max_size, request.batch_linger_ms = batch_max_size, batch_wait_ms
|
|
821
800
|
|
|
@@ -888,7 +867,7 @@ class _ContainerIOManager:
|
|
|
888
867
|
# collect all active input slots, meaning all inputs have wrapped up.
|
|
889
868
|
await self._input_slots.close()
|
|
890
869
|
|
|
891
|
-
async def _send_outputs(self,
|
|
870
|
+
async def _send_outputs(self, outputs: list[api_pb2.FunctionPutOutputsItem]) -> None:
|
|
892
871
|
"""Send pre-built output items with retry and chunking."""
|
|
893
872
|
# There are multiple outputs for a single IOContext in the case of @modal.batched.
|
|
894
873
|
# Limit the batch size to 20 to stay within message size limits and buffer size limits.
|
|
@@ -902,7 +881,7 @@ class _ContainerIOManager:
|
|
|
902
881
|
),
|
|
903
882
|
)
|
|
904
883
|
input_ids = [output.input_id for output in outputs]
|
|
905
|
-
self.exit_context(
|
|
884
|
+
self.exit_context(input_ids)
|
|
906
885
|
|
|
907
886
|
@asynccontextmanager
|
|
908
887
|
async def handle_input_exception(
|
|
@@ -922,7 +901,7 @@ class _ContainerIOManager:
|
|
|
922
901
|
raise
|
|
923
902
|
except (InputCancellation, asyncio.CancelledError):
|
|
924
903
|
outputs = await io_context.output_items_cancellation(started_at)
|
|
925
|
-
await self._send_outputs(
|
|
904
|
+
await self._send_outputs(outputs)
|
|
926
905
|
logger.warning(f"Successfully canceled input {io_context.input_ids}")
|
|
927
906
|
return
|
|
928
907
|
except BaseException as exc:
|
|
@@ -933,12 +912,9 @@ class _ContainerIOManager:
|
|
|
933
912
|
# print exception so it's logged
|
|
934
913
|
print_exception(*sys.exc_info())
|
|
935
914
|
outputs = await io_context.output_items_exception(started_at, self.task_id, exc)
|
|
936
|
-
await self._send_outputs(
|
|
937
|
-
|
|
938
|
-
def exit_context(self, started_at, input_ids: list[str]):
|
|
939
|
-
self.total_user_time += time.time() - started_at
|
|
940
|
-
self.calls_completed += 1
|
|
915
|
+
await self._send_outputs(outputs)
|
|
941
916
|
|
|
917
|
+
def exit_context(self, input_ids: list[str]):
|
|
942
918
|
for input_id in input_ids:
|
|
943
919
|
self.current_inputs.pop(input_id)
|
|
944
920
|
|
|
@@ -954,7 +930,7 @@ class _ContainerIOManager:
|
|
|
954
930
|
) -> None:
|
|
955
931
|
# The standard output encoding+sending method for successful function outputs
|
|
956
932
|
outputs = await io_context.output_items(started_at, output_data)
|
|
957
|
-
await self._send_outputs(
|
|
933
|
+
await self._send_outputs(outputs)
|
|
958
934
|
|
|
959
935
|
@asynccontextmanager
|
|
960
936
|
async def snapshot_context_manager(self) -> AsyncGenerator[None, None]:
|
|
@@ -100,8 +100,6 @@ class _ContainerIOManager:
|
|
|
100
100
|
app_id: str
|
|
101
101
|
_task_lifecycle_manager: modal._runtime.task_lifecycle_manager._TaskLifecycleManager
|
|
102
102
|
input_plane_server_url: typing.Optional[str]
|
|
103
|
-
calls_completed: int
|
|
104
|
-
total_user_time: float
|
|
105
103
|
current_input_id: typing.Optional[str]
|
|
106
104
|
current_inputs: dict[str, IOContext]
|
|
107
105
|
current_input_started_at: typing.Optional[float]
|
|
@@ -185,8 +183,6 @@ class _ContainerIOManager:
|
|
|
185
183
|
"""Put a value onto a queue, using the synchronicity event loop."""
|
|
186
184
|
...
|
|
187
185
|
|
|
188
|
-
def get_average_call_time(self) -> float: ...
|
|
189
|
-
def get_max_inputs_to_fetch(self): ...
|
|
190
186
|
def _generate_inputs(
|
|
191
187
|
self, batch_max_size: int, batch_wait_ms: int
|
|
192
188
|
) -> collections.abc.AsyncIterator[list[tuple[str, int, str, str, modal_proto.api_pb2.FunctionInput]]]: ...
|
|
@@ -196,7 +192,7 @@ class _ContainerIOManager:
|
|
|
196
192
|
batch_max_size: int = 0,
|
|
197
193
|
batch_wait_ms: int = 0,
|
|
198
194
|
) -> collections.abc.AsyncIterator[IOContext]: ...
|
|
199
|
-
async def _send_outputs(self,
|
|
195
|
+
async def _send_outputs(self, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
|
|
200
196
|
"""Send pre-built output items with retry and chunking."""
|
|
201
197
|
...
|
|
202
198
|
|
|
@@ -204,7 +200,7 @@ class _ContainerIOManager:
|
|
|
204
200
|
"""Handle an exception while processing a function input."""
|
|
205
201
|
...
|
|
206
202
|
|
|
207
|
-
def exit_context(self,
|
|
203
|
+
def exit_context(self, input_ids: list[str]): ...
|
|
208
204
|
async def push_outputs(self, io_context: IOContext, started_at: float, output_data: list[typing.Any]) -> None: ...
|
|
209
205
|
def snapshot_context_manager(self) -> typing.AsyncContextManager[None]: ...
|
|
210
206
|
async def interact(self, from_breakpoint: bool = False): ...
|
|
@@ -246,8 +242,6 @@ class ContainerIOManager:
|
|
|
246
242
|
app_id: str
|
|
247
243
|
_task_lifecycle_manager: modal._runtime.task_lifecycle_manager.TaskLifecycleManager
|
|
248
244
|
input_plane_server_url: typing.Optional[str]
|
|
249
|
-
calls_completed: int
|
|
250
|
-
total_user_time: float
|
|
251
245
|
current_input_id: typing.Optional[str]
|
|
252
246
|
current_inputs: dict[str, IOContext]
|
|
253
247
|
current_input_started_at: typing.Optional[float]
|
|
@@ -413,9 +407,6 @@ class ContainerIOManager:
|
|
|
413
407
|
|
|
414
408
|
_queue_put: ___queue_put_spec
|
|
415
409
|
|
|
416
|
-
def get_average_call_time(self) -> float: ...
|
|
417
|
-
def get_max_inputs_to_fetch(self): ...
|
|
418
|
-
|
|
419
410
|
class ___generate_inputs_spec(typing_extensions.Protocol):
|
|
420
411
|
def __call__(
|
|
421
412
|
self, /, batch_max_size: int, batch_wait_ms: int
|
|
@@ -445,11 +436,11 @@ class ContainerIOManager:
|
|
|
445
436
|
run_inputs_outputs: __run_inputs_outputs_spec
|
|
446
437
|
|
|
447
438
|
class ___send_outputs_spec(typing_extensions.Protocol):
|
|
448
|
-
def __call__(self, /,
|
|
439
|
+
def __call__(self, /, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
|
|
449
440
|
"""Send pre-built output items with retry and chunking."""
|
|
450
441
|
...
|
|
451
442
|
|
|
452
|
-
async def aio(self, /,
|
|
443
|
+
async def aio(self, /, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
|
|
453
444
|
"""Send pre-built output items with retry and chunking."""
|
|
454
445
|
...
|
|
455
446
|
|
|
@@ -468,7 +459,7 @@ class ContainerIOManager:
|
|
|
468
459
|
|
|
469
460
|
handle_input_exception: __handle_input_exception_spec
|
|
470
461
|
|
|
471
|
-
def exit_context(self,
|
|
462
|
+
def exit_context(self, input_ids: list[str]): ...
|
|
472
463
|
|
|
473
464
|
class __push_outputs_spec(typing_extensions.Protocol):
|
|
474
465
|
def __call__(self, /, io_context: IOContext, started_at: float, output_data: list[typing.Any]) -> None: ...
|
|
@@ -517,5 +508,3 @@ class ContainerIOManager:
|
|
|
517
508
|
def stop_fetching_inputs(cls): ...
|
|
518
509
|
|
|
519
510
|
MAX_OUTPUT_BATCH_SIZE: int
|
|
520
|
-
|
|
521
|
-
RTT_S: float
|
|
@@ -325,7 +325,7 @@ def create_channel(
|
|
|
325
325
|
async def create_channel_with_fallbacks(
|
|
326
326
|
server_url: str,
|
|
327
327
|
metadata: dict[str, str] = {},
|
|
328
|
-
stagger_delay: float =
|
|
328
|
+
stagger_delay: float = 3.0,
|
|
329
329
|
) -> grpclib.client.Channel:
|
|
330
330
|
"""Create a connected channel, supporting comma-separated fallback URLs.
|
|
331
331
|
|
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
import urllib.parse
|
|
7
7
|
import warnings
|
|
8
8
|
from collections.abc import AsyncGenerator, Collection, Mapping
|
|
9
|
-
from typing import Any, ClassVar, TypeVar
|
|
9
|
+
from typing import Any, ClassVar, Coroutine, TypeVar
|
|
10
10
|
|
|
11
11
|
import grpclib.client
|
|
12
12
|
from google.protobuf import empty_pb2
|
|
@@ -41,7 +41,7 @@ def _get_metadata(client_type: int, credentials: tuple[str, str] | None, version
|
|
|
41
41
|
# sys.version_info is structured unlike sys.version or platform.python_version()
|
|
42
42
|
python_version = "%d.%d.%d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro)
|
|
43
43
|
|
|
44
|
-
metadata = {
|
|
44
|
+
metadata: dict[str, str] = {
|
|
45
45
|
"x-modal-client-version": version,
|
|
46
46
|
"x-modal-client-type": str(client_type),
|
|
47
47
|
"x-modal-python-version": python_version,
|
|
@@ -58,9 +58,15 @@ def _get_metadata(client_type: int, credentials: tuple[str, str] | None, version
|
|
|
58
58
|
)
|
|
59
59
|
if agent_env := _agent_environment():
|
|
60
60
|
metadata["x-modal-agent-harness"] = urllib.parse.quote(agent_env)
|
|
61
|
+
|
|
62
|
+
override_headers = config.get("override_headers")
|
|
63
|
+
if override_headers:
|
|
64
|
+
metadata.update(override_headers)
|
|
65
|
+
|
|
61
66
|
return metadata
|
|
62
67
|
|
|
63
68
|
|
|
69
|
+
T = TypeVar("T")
|
|
64
70
|
ReturnType = TypeVar("ReturnType")
|
|
65
71
|
_Value = str | bytes
|
|
66
72
|
_MetadataLike = Mapping[str, _Value] | Collection[tuple[str, _Value]]
|
|
@@ -308,7 +314,7 @@ class _Client:
|
|
|
308
314
|
("x-modal-auth-token", token),
|
|
309
315
|
]
|
|
310
316
|
|
|
311
|
-
async def _call_safely(self, coro, readable_method: str):
|
|
317
|
+
async def _call_safely(self, coro: Coroutine[Any, Any, T], readable_method: str) -> T:
|
|
312
318
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
313
319
|
|
|
314
320
|
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
@@ -364,11 +370,11 @@ class _Client:
|
|
|
364
370
|
async def _call_unary(
|
|
365
371
|
self,
|
|
366
372
|
grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
367
|
-
request:
|
|
373
|
+
request: RequestType,
|
|
368
374
|
*,
|
|
369
375
|
timeout: float | None = None,
|
|
370
376
|
metadata: _MetadataLike | None = None,
|
|
371
|
-
) ->
|
|
377
|
+
) -> ResponseType:
|
|
372
378
|
coro = grpclib_method(request, timeout=timeout, metadata=metadata)
|
|
373
379
|
return await self._call_safely(coro, grpclib_method.name)
|
|
374
380
|
|
|
@@ -376,10 +382,10 @@ class _Client:
|
|
|
376
382
|
async def _call_stream(
|
|
377
383
|
self,
|
|
378
384
|
grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
379
|
-
request:
|
|
385
|
+
request: RequestType,
|
|
380
386
|
*,
|
|
381
387
|
metadata: _MetadataLike | None,
|
|
382
|
-
) -> AsyncGenerator[
|
|
388
|
+
) -> AsyncGenerator[ResponseType, None]:
|
|
383
389
|
stream_context = grpclib_method.open(metadata=metadata)
|
|
384
390
|
stream = await self._call_safely(stream_context.__aenter__(), f"{grpclib_method.name}.open")
|
|
385
391
|
try:
|
|
@@ -13,6 +13,8 @@ import typing_extensions
|
|
|
13
13
|
|
|
14
14
|
def _get_metadata(client_type: int, credentials: typing.Optional[tuple[str, str]], version: str) -> dict[str, str]: ...
|
|
15
15
|
|
|
16
|
+
T = typing.TypeVar("T")
|
|
17
|
+
|
|
16
18
|
ReturnType = typing.TypeVar("ReturnType")
|
|
17
19
|
|
|
18
20
|
RequestType = typing.TypeVar("RequestType", bound="google.protobuf.message.Message")
|
|
@@ -35,7 +37,7 @@ class _Client:
|
|
|
35
37
|
server_url: str,
|
|
36
38
|
client_type: int,
|
|
37
39
|
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.5.3.
|
|
40
|
+
version: str = "1.5.3.dev4",
|
|
39
41
|
):
|
|
40
42
|
"""mdmd:hidden
|
|
41
43
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -154,7 +156,7 @@ class _Client:
|
|
|
154
156
|
"""
|
|
155
157
|
...
|
|
156
158
|
|
|
157
|
-
async def _call_safely(self, coro, readable_method: str):
|
|
159
|
+
async def _call_safely(self, coro: typing.Coroutine[typing.Any, typing.Any, T], readable_method: str) -> T:
|
|
158
160
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
159
161
|
|
|
160
162
|
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
@@ -168,7 +170,7 @@ class _Client:
|
|
|
168
170
|
async def _call_unary(
|
|
169
171
|
self,
|
|
170
172
|
grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
171
|
-
request:
|
|
173
|
+
request: RequestType,
|
|
172
174
|
*,
|
|
173
175
|
timeout: typing.Optional[float] = None,
|
|
174
176
|
metadata: typing.Union[
|
|
@@ -176,18 +178,18 @@ class _Client:
|
|
|
176
178
|
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
177
179
|
None,
|
|
178
180
|
] = None,
|
|
179
|
-
) ->
|
|
181
|
+
) -> ResponseType: ...
|
|
180
182
|
def _call_stream(
|
|
181
183
|
self,
|
|
182
184
|
grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
183
|
-
request:
|
|
185
|
+
request: RequestType,
|
|
184
186
|
*,
|
|
185
187
|
metadata: typing.Union[
|
|
186
188
|
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
|
187
189
|
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
188
190
|
None,
|
|
189
191
|
],
|
|
190
|
-
) -> collections.abc.AsyncGenerator[
|
|
192
|
+
) -> collections.abc.AsyncGenerator[ResponseType, None]: ...
|
|
191
193
|
|
|
192
194
|
class Client:
|
|
193
195
|
_client_from_env: typing.ClassVar[typing.Optional[Client]]
|
|
@@ -205,7 +207,7 @@ class Client:
|
|
|
205
207
|
server_url: str,
|
|
206
208
|
client_type: int,
|
|
207
209
|
credentials: typing.Optional[tuple[str, str]],
|
|
208
|
-
version: str = "1.5.3.
|
|
210
|
+
version: str = "1.5.3.dev4",
|
|
209
211
|
):
|
|
210
212
|
"""mdmd:hidden
|
|
211
213
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -429,7 +431,7 @@ class Client:
|
|
|
429
431
|
get_input_plane_metadata: __get_input_plane_metadata_spec
|
|
430
432
|
|
|
431
433
|
class ___call_safely_spec(typing_extensions.Protocol):
|
|
432
|
-
def __call__(self, /, coro, readable_method: str):
|
|
434
|
+
def __call__(self, /, coro: T, readable_method: str) -> T:
|
|
433
435
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
434
436
|
|
|
435
437
|
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
@@ -438,7 +440,7 @@ class Client:
|
|
|
438
440
|
"""
|
|
439
441
|
...
|
|
440
442
|
|
|
441
|
-
async def aio(self, /, coro, readable_method: str):
|
|
443
|
+
async def aio(self, /, coro: typing.Coroutine[typing.Any, typing.Any, T], readable_method: str) -> T:
|
|
442
444
|
"""Runs coroutine wrapped in a task that's part of the client's task context
|
|
443
445
|
|
|
444
446
|
* Raises ClientClosed in case the client is closed while the coroutine is executed
|
|
@@ -464,7 +466,7 @@ class Client:
|
|
|
464
466
|
async def _call_unary(
|
|
465
467
|
self,
|
|
466
468
|
grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
|
|
467
|
-
request:
|
|
469
|
+
request: RequestType,
|
|
468
470
|
*,
|
|
469
471
|
timeout: typing.Optional[float] = None,
|
|
470
472
|
metadata: typing.Union[
|
|
@@ -472,18 +474,18 @@ class Client:
|
|
|
472
474
|
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
473
475
|
None,
|
|
474
476
|
] = None,
|
|
475
|
-
) ->
|
|
477
|
+
) -> ResponseType: ...
|
|
476
478
|
def _call_stream(
|
|
477
479
|
self,
|
|
478
480
|
grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
|
|
479
|
-
request:
|
|
481
|
+
request: RequestType,
|
|
480
482
|
*,
|
|
481
483
|
metadata: typing.Union[
|
|
482
484
|
collections.abc.Mapping[str, typing.Union[str, bytes]],
|
|
483
485
|
collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
|
|
484
486
|
None,
|
|
485
487
|
],
|
|
486
|
-
) -> collections.abc.AsyncGenerator[
|
|
488
|
+
) -> collections.abc.AsyncGenerator[ResponseType, None]: ...
|
|
487
489
|
|
|
488
490
|
HEARTBEAT_INTERVAL: float
|
|
489
491
|
|
|
@@ -86,6 +86,8 @@ def _bind_instance_method(cls: "_Cls", service_function: _Function, method_name:
|
|
|
86
86
|
|
|
87
87
|
method_metadata = service_function._method_handle_metadata[method_name]
|
|
88
88
|
new_function._hydrate(service_function.object_id, service_function.client, method_metadata)
|
|
89
|
+
if not new_function._app_id:
|
|
90
|
+
new_function._app_id = service_function._app_id
|
|
89
91
|
|
|
90
92
|
async def _load(fun: "_Function", resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
|
|
91
93
|
# there is currently no actual loading logic executed to create each method on
|
|
@@ -426,6 +428,7 @@ class _Obj:
|
|
|
426
428
|
)
|
|
427
429
|
await resolver.load(method_function, load_context) # get the appropriate method handle (lazy)
|
|
428
430
|
fun._hydrate_from_other(method_function)
|
|
431
|
+
fun._app_id = method_function._app_id
|
|
429
432
|
|
|
430
433
|
# The reason we don't *always* use this lazy loader is because it precludes attribute access
|
|
431
434
|
# on local classes.
|
|
@@ -106,7 +106,8 @@ from modal_proto import api_pb2
|
|
|
106
106
|
from ._utils.logger import configure_logger
|
|
107
107
|
from .exception import InvalidError, NotFoundError
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
# By default, try api.modal.com and fail over to api.modal2.com
|
|
110
|
+
DEFAULT_SERVER_URL = "https://api.modal.com,https://api.modal2.com"
|
|
110
111
|
|
|
111
112
|
|
|
112
113
|
# Locate config file and read it
|
|
@@ -263,6 +264,33 @@ def _enforce_suffix_rules(x: str) -> str:
|
|
|
263
264
|
return x
|
|
264
265
|
|
|
265
266
|
|
|
267
|
+
def _transform_headers(s: str) -> dict[str, str]:
|
|
268
|
+
error_msg = (
|
|
269
|
+
"Must be a comma-separated list of `<header>:<value>` pairs (whitespace is ignored). "
|
|
270
|
+
"<header> and <value> must contain at least one non-whitespace character, and may "
|
|
271
|
+
"not contain commas."
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
if s == "":
|
|
275
|
+
return {}
|
|
276
|
+
|
|
277
|
+
pairs = s.split(",")
|
|
278
|
+
|
|
279
|
+
headers: dict[str, str] = {}
|
|
280
|
+
for pair in pairs:
|
|
281
|
+
res = pair.split(":", maxsplit=1)
|
|
282
|
+
if len(res) != 2:
|
|
283
|
+
raise ValueError(error_msg)
|
|
284
|
+
|
|
285
|
+
header, value = res[0].strip(), res[1].strip()
|
|
286
|
+
if len(header) == 0 or len(value) == 0:
|
|
287
|
+
raise ValueError(error_msg)
|
|
288
|
+
|
|
289
|
+
headers[header] = value
|
|
290
|
+
|
|
291
|
+
return headers
|
|
292
|
+
|
|
293
|
+
|
|
266
294
|
class _Setting(typing.NamedTuple):
|
|
267
295
|
default: typing.Any = None
|
|
268
296
|
transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
|
|
@@ -305,6 +333,7 @@ _SETTINGS = {
|
|
|
305
333
|
"max_throttle_wait": _Setting(None, transform=lambda x: int(x) if x else None),
|
|
306
334
|
"async_warnings": _Setting(True, transform=_to_boolean), # Activate synchronicity usage warnings
|
|
307
335
|
"disable_api_proxy": _Setting(False, transform=_to_boolean),
|
|
336
|
+
"override_headers": _Setting(None, transform=_transform_headers),
|
|
308
337
|
}
|
|
309
338
|
|
|
310
339
|
|
|
@@ -58,6 +58,7 @@ class Function(
|
|
|
58
58
|
_metadata: typing.Optional[modal_proto.api_pb2.FunctionHandleMetadata]
|
|
59
59
|
_options: modal._function_variants._FunctionOptions
|
|
60
60
|
_base_function: typing.Optional[Function]
|
|
61
|
+
_app_id: typing.Optional[str]
|
|
61
62
|
|
|
62
63
|
def __init__(self, *args, **kwargs):
|
|
63
64
|
"""mdmd:hidden"""
|
|
@@ -892,6 +893,8 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
892
893
|
|
|
893
894
|
_is_generator: bool
|
|
894
895
|
_num_inputs: typing.Optional[int]
|
|
896
|
+
_app_id: typing.Optional[str]
|
|
897
|
+
_function_id: typing.Optional[str]
|
|
895
898
|
|
|
896
899
|
def __init__(self, *args, **kwargs):
|
|
897
900
|
"""mdmd:hidden"""
|
|
@@ -899,6 +902,17 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
899
902
|
|
|
900
903
|
def _invocation(self): ...
|
|
901
904
|
|
|
905
|
+
class ___hydrate_from_id_metadata_spec(typing_extensions.Protocol):
|
|
906
|
+
def __call__(self, /) -> None:
|
|
907
|
+
"""Hydrate metadata only when needed for FunctionCall fields."""
|
|
908
|
+
...
|
|
909
|
+
|
|
910
|
+
async def aio(self, /) -> None:
|
|
911
|
+
"""Hydrate metadata only when needed for FunctionCall fields."""
|
|
912
|
+
...
|
|
913
|
+
|
|
914
|
+
_hydrate_from_id_metadata: ___hydrate_from_id_metadata_spec
|
|
915
|
+
|
|
902
916
|
class __num_inputs_spec(typing_extensions.Protocol):
|
|
903
917
|
def __call__(self, /) -> int:
|
|
904
918
|
"""Get the number of inputs in the function call.
|
|
@@ -1115,6 +1129,8 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
|
|
|
1115
1129
|
|
|
1116
1130
|
from_id: typing.ClassVar[__from_id_spec]
|
|
1117
1131
|
|
|
1132
|
+
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
|
1133
|
+
|
|
1118
1134
|
class __gather_spec(typing_extensions.Protocol):
|
|
1119
1135
|
def __call__(self, /, *function_calls: FunctionCall[modal._functions.T]) -> typing.Sequence[modal._functions.T]:
|
|
1120
1136
|
"""Wait until all Modal FunctionCall objects have results before returning.
|