modal 1.4.3.dev23__tar.gz → 1.4.3.dev25__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.4.3.dev23 → modal-1.4.3.dev25}/PKG-INFO +1 -1
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_functions.py +5 -1
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/grpc_utils.py +10 -6
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/task_command_router_client.py +5 -14
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/cluster.py +1 -4
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/container.py +1 -3
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/client.pyi +2 -2
- modal-1.4.3.dev25/modal/container_process.py +224 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/container_process.pyi +11 -120
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/io_streams.py +69 -199
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/io_streams.pyi +125 -64
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/sandbox.py +74 -94
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/sandbox.pyi +28 -76
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/sandbox_fs.py +29 -18
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/sandbox_fs.pyi +6 -6
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/api_grpc.py +16 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/api_pb2.py +874 -777
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/api_pb2.pyi +222 -5
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/api_pb2_grpc.py +34 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/api_pb2_grpc.pyi +12 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/task_command_router_grpc.py +16 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/task_command_router_pb2.py +33 -13
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/task_command_router_pb2.pyi +45 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/task_command_router_pb2_grpc.py +34 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/task_command_router_pb2_grpc.pyi +12 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_version/__init__.py +1 -1
- modal-1.4.3.dev23/modal/container_process.py +0 -470
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/LICENSE +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/README.md +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/__main__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_billing.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_clustered_functions.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_container_entrypoint.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_environments.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_grpc_client.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_ipython.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_load_context.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_location.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_logs.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_object.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_output/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_output/manager.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_output/pty.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_output/rich.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_output/status.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_partial_function.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_resolver.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_resources.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/execution_context.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_serialization.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_server.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_traceback.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_tunnel.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_tunnel.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_type_manager.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/logger.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_vendor/version.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/_watcher.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/app.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/app.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/billing.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/README.md +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/builder/base-images.json +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/call_graph.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/_download.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/_help.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/_traceback.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/app.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/billing.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/changelog.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/config.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/dashboard.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/dict.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/entry_point.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/environment.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/import_refs.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/launch.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/logo.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/profile.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/queues.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/run.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/secret.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/selector.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/shell.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/token.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/utils.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cli/volume.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/client.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cls.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/cls.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/config.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/dict.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/dict.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/environments.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/environments.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/exception.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/experimental/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/experimental/flash.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/experimental/flash.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/experimental/ipython.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/file_io.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/file_io.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/functions.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/functions.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/image.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/image.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/mount.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/mount.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/network_file_system.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/network_file_system.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/object.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/object.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/output.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/parallel_map.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/parallel_map.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/partial_function.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/partial_function.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/proxy.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/proxy.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/py.typed +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/queue.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/queue.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/retries.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/runner.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/runner.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/running_app.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/schedule.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/scheduler_placement.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/secret.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/secret.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/server.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/server.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/serving.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/serving.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/snapshot.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/snapshot.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/stream_type.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/token_flow.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/token_flow.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/volume.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal/volume.pyi +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/__init__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_proto/py.typed +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/modal_version/__main__.py +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/pyproject.toml +0 -0
- {modal-1.4.3.dev23 → modal-1.4.3.dev25}/setup.cfg +0 -0
|
@@ -549,6 +549,7 @@ class FunctionStats:
|
|
|
549
549
|
backlog: int
|
|
550
550
|
num_total_runners: int
|
|
551
551
|
num_running_inputs: int
|
|
552
|
+
input_headroom: int
|
|
552
553
|
|
|
553
554
|
|
|
554
555
|
def _parse_retries(
|
|
@@ -1851,7 +1852,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1851
1852
|
retry=Retry(total_timeout=10.0),
|
|
1852
1853
|
)
|
|
1853
1854
|
return FunctionStats(
|
|
1854
|
-
backlog=resp.backlog,
|
|
1855
|
+
backlog=resp.backlog,
|
|
1856
|
+
num_total_runners=resp.num_total_tasks,
|
|
1857
|
+
num_running_inputs=resp.num_running_inputs,
|
|
1858
|
+
input_headroom=resp.input_headroom,
|
|
1855
1859
|
)
|
|
1856
1860
|
|
|
1857
1861
|
@live_method
|
|
@@ -403,7 +403,7 @@ async def _retry_transient_errors(
|
|
|
403
403
|
and isinstance(exc, GRPCError)
|
|
404
404
|
and (server_retry_policy := get_server_retry_policy(exc))
|
|
405
405
|
):
|
|
406
|
-
server_delay = server_retry_policy.retry_after_secs
|
|
406
|
+
server_delay = max(server_retry_policy.retry_after_secs, 0.1)
|
|
407
407
|
|
|
408
408
|
now = time.time()
|
|
409
409
|
|
|
@@ -417,6 +417,7 @@ async def _retry_transient_errors(
|
|
|
417
417
|
)
|
|
418
418
|
final_attempt = total_timeout_will_be_reached or max_throttle_will_be_reached
|
|
419
419
|
|
|
420
|
+
elapsed_time = time.monotonic() - attempt_started_at
|
|
420
421
|
with suppress_tb_frame():
|
|
421
422
|
process_exception_before_retry(
|
|
422
423
|
exc,
|
|
@@ -425,20 +426,23 @@ async def _retry_transient_errors(
|
|
|
425
426
|
n_retries,
|
|
426
427
|
server_delay,
|
|
427
428
|
idempotency_key,
|
|
428
|
-
|
|
429
|
+
elapsed_time,
|
|
429
430
|
)
|
|
430
431
|
|
|
431
|
-
now = time.time()
|
|
432
432
|
if last_server_retry_warning_time is None or (
|
|
433
433
|
now - last_server_retry_warning_time >= SERVER_RETRY_WARNING_TIME_INTERVAL
|
|
434
434
|
):
|
|
435
435
|
last_server_retry_warning_time = now
|
|
436
436
|
logger.warning(
|
|
437
|
-
f"Warning: Received {exc.status}{os.linesep}"
|
|
438
|
-
f"{
|
|
439
|
-
f"Will retry in {server_delay:0.2f} seconds."
|
|
437
|
+
f"Warning: Received {exc.status}{os.linesep}{exc.message}{os.linesep}"
|
|
438
|
+
f"for {fn.name} ({idempotency_key[:8]}). Retrying..."
|
|
440
439
|
)
|
|
441
440
|
|
|
441
|
+
logger.debug(
|
|
442
|
+
f"Server-side retry triggered with {repr(exc)} {n_throttled_retries=} {server_delay=} "
|
|
443
|
+
f"{elapsed_time=} for {fn.name} ({idempotency_key[:8]})."
|
|
444
|
+
)
|
|
445
|
+
|
|
442
446
|
n_throttled_retries += 1
|
|
443
447
|
await asyncio.sleep(server_delay)
|
|
444
448
|
continue
|
|
@@ -16,7 +16,7 @@ from grpclib import GRPCError, Status
|
|
|
16
16
|
from grpclib.exceptions import StreamTerminatedError
|
|
17
17
|
|
|
18
18
|
from modal.config import logger
|
|
19
|
-
from modal.exception import
|
|
19
|
+
from modal.exception import ExecTimeoutError
|
|
20
20
|
from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
|
|
21
21
|
from modal_proto.task_command_router_grpc import TaskCommandRouterStub
|
|
22
22
|
|
|
@@ -196,21 +196,12 @@ class TaskCommandRouterClient:
|
|
|
196
196
|
return cls(server_client, task_id, url, jwt, channel, loop, jwt_refresh_lock, sandbox_id=sandbox_id)
|
|
197
197
|
|
|
198
198
|
@classmethod
|
|
199
|
-
async def
|
|
199
|
+
async def init(
|
|
200
200
|
cls,
|
|
201
201
|
server_client,
|
|
202
202
|
task_id: str,
|
|
203
|
-
) ->
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Returns None if command router access is not enabled (FAILED_PRECONDITION).
|
|
207
|
-
"""
|
|
208
|
-
try:
|
|
209
|
-
resp = await fetch_command_router_access(server_client, task_id)
|
|
210
|
-
except ConflictError:
|
|
211
|
-
logger.debug(f"Command router access is not enabled for task {task_id}")
|
|
212
|
-
return None
|
|
213
|
-
|
|
203
|
+
) -> "TaskCommandRouterClient":
|
|
204
|
+
resp = await fetch_command_router_access(server_client, task_id)
|
|
214
205
|
logger.debug(f"Using command router access for task {task_id}")
|
|
215
206
|
return await cls._connect(server_client, task_id, resp.url, resp.jwt)
|
|
216
207
|
|
|
@@ -241,7 +232,7 @@ class TaskCommandRouterClient:
|
|
|
241
232
|
stream_stdio_retry_delay_factor: float = 2,
|
|
242
233
|
stream_stdio_max_retries: int = 10,
|
|
243
234
|
) -> None:
|
|
244
|
-
"""Callers should not use this directly. Use TaskCommandRouterClient.
|
|
235
|
+
"""Callers should not use this directly. Use TaskCommandRouterClient.init() instead."""
|
|
245
236
|
# Record the loop this instance is bound to so __del__ can safely schedule cleanup
|
|
246
237
|
# even if finalization happens from a different thread (e.g. via synchronicity).
|
|
247
238
|
self._loop = loop
|
|
@@ -16,7 +16,6 @@ from modal.cli.utils import display_table, env_option, is_tty
|
|
|
16
16
|
from modal.client import _Client
|
|
17
17
|
from modal.config import config
|
|
18
18
|
from modal.container_process import _ContainerProcess
|
|
19
|
-
from modal.exception import InvalidError
|
|
20
19
|
from modal.output import OutputManager
|
|
21
20
|
from modal.stream_type import StreamType
|
|
22
21
|
from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
|
|
@@ -79,9 +78,7 @@ async def shell(cluster_id: str, rank: int = 0):
|
|
|
79
78
|
|
|
80
79
|
pty = is_tty()
|
|
81
80
|
|
|
82
|
-
command_router_client = await TaskCommandRouterClient.
|
|
83
|
-
if command_router_client is None:
|
|
84
|
-
raise InvalidError(f"Command router access is not available for container {task_id}")
|
|
81
|
+
command_router_client = await TaskCommandRouterClient.init(client, task_id)
|
|
85
82
|
|
|
86
83
|
process_id = str(uuid.uuid4())
|
|
87
84
|
|
|
@@ -278,9 +278,7 @@ async def _exec_impl(
|
|
|
278
278
|
|
|
279
279
|
client = await _Client.from_env()
|
|
280
280
|
|
|
281
|
-
command_router_client = await TaskCommandRouterClient.
|
|
282
|
-
if command_router_client is None:
|
|
283
|
-
raise InvalidError(f"Command router access is not available for container {container_id}")
|
|
281
|
+
command_router_client = await TaskCommandRouterClient.init(client, container_id)
|
|
284
282
|
|
|
285
283
|
process_id = str(uuid.uuid4())
|
|
286
284
|
|
|
@@ -35,7 +35,7 @@ class _Client:
|
|
|
35
35
|
server_url: str,
|
|
36
36
|
client_type: int,
|
|
37
37
|
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.4.3.
|
|
38
|
+
version: str = "1.4.3.dev25",
|
|
39
39
|
):
|
|
40
40
|
"""mdmd:hidden
|
|
41
41
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -175,7 +175,7 @@ class Client:
|
|
|
175
175
|
server_url: str,
|
|
176
176
|
client_type: int,
|
|
177
177
|
credentials: typing.Optional[tuple[str, str]],
|
|
178
|
-
version: str = "1.4.3.
|
|
178
|
+
version: str = "1.4.3.dev25",
|
|
179
179
|
):
|
|
180
180
|
"""mdmd:hidden
|
|
181
181
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Copyright Modal Labs 2024
|
|
2
|
+
import asyncio
|
|
3
|
+
import platform
|
|
4
|
+
from typing import Generic, Optional, TypeVar
|
|
5
|
+
|
|
6
|
+
from modal_proto import api_pb2
|
|
7
|
+
|
|
8
|
+
from ._utils.async_utils import TaskContext, synchronize_api
|
|
9
|
+
from ._utils.shell_utils import stream_from_stdin, write_to_fd
|
|
10
|
+
from ._utils.task_command_router_client import TaskCommandRouterClient
|
|
11
|
+
from .client import _Client
|
|
12
|
+
from .config import logger
|
|
13
|
+
from .exception import ExecTimeoutError, InteractiveTimeoutError, InvalidError
|
|
14
|
+
from .io_streams import (
|
|
15
|
+
_StreamReader,
|
|
16
|
+
_StreamReaderThroughCommandRouterParams,
|
|
17
|
+
_StreamWriter,
|
|
18
|
+
_StreamWriterThroughCommandRouterParams,
|
|
19
|
+
)
|
|
20
|
+
from .stream_type import StreamType
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T", str, bytes)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def _iter_stream_as_bytes(stream: _StreamReader[T]):
|
|
26
|
+
"""Yield raw bytes from a StreamReader regardless of text mode/backend."""
|
|
27
|
+
async for part in stream:
|
|
28
|
+
if isinstance(part, str):
|
|
29
|
+
yield part.encode("utf-8")
|
|
30
|
+
else:
|
|
31
|
+
yield part
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _ContainerProcess(Generic[T]):
|
|
35
|
+
"""Represents a running process in a container.
|
|
36
|
+
|
|
37
|
+
Container processes communicate via direct communication with
|
|
38
|
+
the Modal worker where the container is running.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
process_id: str,
|
|
44
|
+
task_id: str,
|
|
45
|
+
client: _Client,
|
|
46
|
+
command_router_client: TaskCommandRouterClient,
|
|
47
|
+
stdout: StreamType = StreamType.PIPE,
|
|
48
|
+
stderr: StreamType = StreamType.PIPE,
|
|
49
|
+
exec_deadline: Optional[float] = None,
|
|
50
|
+
text: bool = True,
|
|
51
|
+
by_line: bool = False,
|
|
52
|
+
) -> None:
|
|
53
|
+
self._client = client
|
|
54
|
+
self._command_router_client = command_router_client
|
|
55
|
+
self._process_id = process_id
|
|
56
|
+
self._exec_deadline = exec_deadline
|
|
57
|
+
self._text = text
|
|
58
|
+
self._by_line = by_line
|
|
59
|
+
self._task_id = task_id
|
|
60
|
+
self._stdout = _StreamReader[T](
|
|
61
|
+
_StreamReaderThroughCommandRouterParams(
|
|
62
|
+
file_descriptor=api_pb2.FILE_DESCRIPTOR_STDOUT,
|
|
63
|
+
task_id=self._task_id,
|
|
64
|
+
object_id=process_id,
|
|
65
|
+
command_router_client=self._command_router_client,
|
|
66
|
+
deadline=exec_deadline,
|
|
67
|
+
),
|
|
68
|
+
stream_type=stdout,
|
|
69
|
+
text=text,
|
|
70
|
+
by_line=by_line,
|
|
71
|
+
)
|
|
72
|
+
self._stderr = _StreamReader[T](
|
|
73
|
+
_StreamReaderThroughCommandRouterParams(
|
|
74
|
+
file_descriptor=api_pb2.FILE_DESCRIPTOR_STDERR,
|
|
75
|
+
task_id=self._task_id,
|
|
76
|
+
object_id=process_id,
|
|
77
|
+
command_router_client=self._command_router_client,
|
|
78
|
+
deadline=exec_deadline,
|
|
79
|
+
),
|
|
80
|
+
stream_type=stderr,
|
|
81
|
+
text=text,
|
|
82
|
+
by_line=by_line,
|
|
83
|
+
)
|
|
84
|
+
self._stdin = _StreamWriter(
|
|
85
|
+
_StreamWriterThroughCommandRouterParams(
|
|
86
|
+
task_id=self._task_id,
|
|
87
|
+
object_id=process_id,
|
|
88
|
+
command_router_client=self._command_router_client,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
self._returncode = None
|
|
92
|
+
|
|
93
|
+
def __repr__(self) -> str:
|
|
94
|
+
return f"ContainerProcess(process_id={self._process_id!r})"
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def stdout(self) -> _StreamReader[T]:
|
|
98
|
+
"""StreamReader for the container process's stdout stream."""
|
|
99
|
+
return self._stdout
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def stderr(self) -> _StreamReader[T]:
|
|
103
|
+
"""StreamReader for the container process's stderr stream."""
|
|
104
|
+
return self._stderr
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def stdin(self) -> _StreamWriter:
|
|
108
|
+
"""StreamWriter for the container process's stdin stream."""
|
|
109
|
+
return self._stdin
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def returncode(self) -> int:
|
|
113
|
+
if self._returncode is None:
|
|
114
|
+
raise InvalidError(
|
|
115
|
+
"You must call wait() before accessing the returncode. "
|
|
116
|
+
"To poll for the status of a running process, use poll() instead."
|
|
117
|
+
)
|
|
118
|
+
return self._returncode
|
|
119
|
+
|
|
120
|
+
async def poll(self) -> Optional[int]:
|
|
121
|
+
"""Check if the container process has finished running.
|
|
122
|
+
|
|
123
|
+
Returns `None` if the process is still running, else returns the exit code.
|
|
124
|
+
"""
|
|
125
|
+
if self._returncode is not None:
|
|
126
|
+
return self._returncode
|
|
127
|
+
try:
|
|
128
|
+
resp = await self._command_router_client.exec_poll(self._task_id, self._process_id, self._exec_deadline)
|
|
129
|
+
which = resp.WhichOneof("exit_status")
|
|
130
|
+
if which is None:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
if which == "code":
|
|
134
|
+
self._returncode = int(resp.code)
|
|
135
|
+
return self._returncode
|
|
136
|
+
elif which == "signal":
|
|
137
|
+
self._returncode = 128 + int(resp.signal)
|
|
138
|
+
return self._returncode
|
|
139
|
+
else:
|
|
140
|
+
logger.debug(f"ContainerProcess {self._process_id} exited with unexpected status: {which}")
|
|
141
|
+
raise InvalidError("Unexpected exit status")
|
|
142
|
+
except ExecTimeoutError:
|
|
143
|
+
logger.debug(f"ContainerProcess poll for {self._process_id} did not complete within deadline")
|
|
144
|
+
# TODO(saltzm): This is a weird API, but customers currently may rely on it. This
|
|
145
|
+
# should probably raise an ExecTimeoutError instead.
|
|
146
|
+
self._returncode = -1
|
|
147
|
+
return self._returncode
|
|
148
|
+
except Exception as e:
|
|
149
|
+
# Re-raise non-transient errors or errors resulting from exceeding retries on transient errors.
|
|
150
|
+
logger.warning(f"ContainerProcess poll for {self._process_id} failed: {e}")
|
|
151
|
+
raise
|
|
152
|
+
|
|
153
|
+
async def wait(self) -> int:
|
|
154
|
+
"""Wait for the container process to finish running. Returns the exit code."""
|
|
155
|
+
if self._returncode is not None:
|
|
156
|
+
return self._returncode
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
resp = await self._command_router_client.exec_wait(self._task_id, self._process_id, self._exec_deadline)
|
|
160
|
+
which = resp.WhichOneof("exit_status")
|
|
161
|
+
if which == "code":
|
|
162
|
+
self._returncode = int(resp.code)
|
|
163
|
+
elif which == "signal":
|
|
164
|
+
self._returncode = 128 + int(resp.signal)
|
|
165
|
+
else:
|
|
166
|
+
logger.debug(f"ContainerProcess {self._process_id} exited with unexpected status: {which}")
|
|
167
|
+
self._returncode = -1
|
|
168
|
+
raise InvalidError("Unexpected exit status")
|
|
169
|
+
except ExecTimeoutError:
|
|
170
|
+
logger.debug(f"ContainerProcess {self._process_id} did not complete within deadline")
|
|
171
|
+
# TODO(saltzm): This is a weird API, but customers currently may rely on it. This
|
|
172
|
+
# should be a ExecTimeoutError.
|
|
173
|
+
self._returncode = -1
|
|
174
|
+
|
|
175
|
+
return self._returncode
|
|
176
|
+
|
|
177
|
+
async def attach(self):
|
|
178
|
+
"""mdmd:hidden"""
|
|
179
|
+
if platform.system() == "Windows":
|
|
180
|
+
print("interactive exec is not currently supported on Windows.") # noqa: T201
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
from .output import OutputManager
|
|
184
|
+
|
|
185
|
+
output = OutputManager.get()
|
|
186
|
+
connecting_status = output.status("Connecting...")
|
|
187
|
+
connecting_status.start()
|
|
188
|
+
on_connect = asyncio.Event()
|
|
189
|
+
|
|
190
|
+
async def _write_to_fd_loop(stream: _StreamReader[T]):
|
|
191
|
+
async for chunk in _iter_stream_as_bytes(stream):
|
|
192
|
+
if chunk is None:
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
if not on_connect.is_set():
|
|
196
|
+
connecting_status.stop()
|
|
197
|
+
on_connect.set()
|
|
198
|
+
|
|
199
|
+
await write_to_fd(stream.file_descriptor, chunk)
|
|
200
|
+
|
|
201
|
+
async def _handle_input(data: bytes, message_index: int):
|
|
202
|
+
self.stdin.write(data)
|
|
203
|
+
await self.stdin.drain()
|
|
204
|
+
|
|
205
|
+
async with TaskContext() as tc:
|
|
206
|
+
stdout_task = tc.create_task(_write_to_fd_loop(self.stdout))
|
|
207
|
+
stderr_task = tc.create_task(_write_to_fd_loop(self.stderr))
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
# Time out if we can't connect fast enough.
|
|
211
|
+
await asyncio.wait_for(on_connect.wait(), timeout=60)
|
|
212
|
+
|
|
213
|
+
async with stream_from_stdin(_handle_input, use_raw_terminal=True):
|
|
214
|
+
await stdout_task
|
|
215
|
+
await stderr_task
|
|
216
|
+
|
|
217
|
+
except (asyncio.TimeoutError, TimeoutError):
|
|
218
|
+
connecting_status.stop()
|
|
219
|
+
stdout_task.cancel()
|
|
220
|
+
stderr_task.cancel()
|
|
221
|
+
raise InteractiveTimeoutError("Failed to establish connection to container. Please try again.")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
ContainerProcess = synchronize_api(_ContainerProcess)
|
|
@@ -7,140 +7,27 @@ import typing_extensions
|
|
|
7
7
|
|
|
8
8
|
T = typing.TypeVar("T")
|
|
9
9
|
|
|
10
|
-
class _ContainerProcessThroughServer(typing.Generic[T]):
|
|
11
|
-
"""Abstract base class for generic types.
|
|
12
|
-
|
|
13
|
-
A generic type is typically declared by inheriting from
|
|
14
|
-
this class parameterized with one or more type variables.
|
|
15
|
-
For example, a generic mapping type might be defined as::
|
|
16
|
-
|
|
17
|
-
class Mapping(Generic[KT, VT]):
|
|
18
|
-
def __getitem__(self, key: KT) -> VT:
|
|
19
|
-
...
|
|
20
|
-
# Etc.
|
|
21
|
-
|
|
22
|
-
This class can then be used as follows::
|
|
23
|
-
|
|
24
|
-
def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
|
|
25
|
-
try:
|
|
26
|
-
return mapping[key]
|
|
27
|
-
except KeyError:
|
|
28
|
-
return default
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
_process_id: typing.Optional[str]
|
|
32
|
-
_stdout: modal.io_streams._StreamReader[T]
|
|
33
|
-
_stderr: modal.io_streams._StreamReader[T]
|
|
34
|
-
_stdin: modal.io_streams._StreamWriter
|
|
35
|
-
_exec_deadline: typing.Optional[float]
|
|
36
|
-
_text: bool
|
|
37
|
-
_by_line: bool
|
|
38
|
-
_returncode: typing.Optional[int]
|
|
39
|
-
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
process_id: str,
|
|
43
|
-
task_id: str,
|
|
44
|
-
client: modal.client._Client,
|
|
45
|
-
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
46
|
-
stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
47
|
-
exec_deadline: typing.Optional[float] = None,
|
|
48
|
-
text: bool = True,
|
|
49
|
-
by_line: bool = False,
|
|
50
|
-
) -> None:
|
|
51
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
52
|
-
...
|
|
53
|
-
|
|
54
|
-
def __repr__(self) -> str:
|
|
55
|
-
"""Return repr(self)."""
|
|
56
|
-
...
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def stdout(self) -> modal.io_streams._StreamReader[T]:
|
|
60
|
-
"""StreamReader for the container process's stdout stream."""
|
|
61
|
-
...
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def stderr(self) -> modal.io_streams._StreamReader[T]:
|
|
65
|
-
"""StreamReader for the container process's stderr stream."""
|
|
66
|
-
...
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def stdin(self) -> modal.io_streams._StreamWriter:
|
|
70
|
-
"""StreamWriter for the container process's stdin stream."""
|
|
71
|
-
...
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def returncode(self) -> int: ...
|
|
75
|
-
async def poll(self) -> typing.Optional[int]:
|
|
76
|
-
"""Check if the container process has finished running.
|
|
77
|
-
|
|
78
|
-
Returns `None` if the process is still running, else returns the exit code.
|
|
79
|
-
"""
|
|
80
|
-
...
|
|
81
|
-
|
|
82
|
-
async def _wait_for_completion(self) -> int: ...
|
|
83
|
-
async def wait(self) -> int:
|
|
84
|
-
"""Wait for the container process to finish running. Returns the exit code."""
|
|
85
|
-
...
|
|
86
|
-
|
|
87
|
-
async def attach(self):
|
|
88
|
-
"""mdmd:hidden"""
|
|
89
|
-
...
|
|
90
|
-
|
|
91
10
|
def _iter_stream_as_bytes(stream: modal.io_streams._StreamReader[T]):
|
|
92
11
|
"""Yield raw bytes from a StreamReader regardless of text mode/backend."""
|
|
93
12
|
...
|
|
94
13
|
|
|
95
|
-
class
|
|
96
|
-
"""
|
|
14
|
+
class _ContainerProcess(typing.Generic[T]):
|
|
15
|
+
"""Represents a running process in a container.
|
|
16
|
+
|
|
17
|
+
Container processes communicate via direct communication with
|
|
97
18
|
the Modal worker where the container is running.
|
|
98
19
|
"""
|
|
99
|
-
def __init__(
|
|
100
|
-
self,
|
|
101
|
-
process_id: str,
|
|
102
|
-
client: modal.client._Client,
|
|
103
|
-
command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
|
|
104
|
-
task_id: str,
|
|
105
|
-
*,
|
|
106
|
-
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
107
|
-
stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
108
|
-
exec_deadline: typing.Optional[float] = None,
|
|
109
|
-
text: bool = True,
|
|
110
|
-
by_line: bool = False,
|
|
111
|
-
) -> None:
|
|
112
|
-
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
113
|
-
...
|
|
114
|
-
|
|
115
|
-
def __repr__(self) -> str:
|
|
116
|
-
"""Return repr(self)."""
|
|
117
|
-
...
|
|
118
|
-
|
|
119
|
-
@property
|
|
120
|
-
def stdout(self) -> modal.io_streams._StreamReader[T]: ...
|
|
121
|
-
@property
|
|
122
|
-
def stderr(self) -> modal.io_streams._StreamReader[T]: ...
|
|
123
|
-
@property
|
|
124
|
-
def stdin(self) -> modal.io_streams._StreamWriter: ...
|
|
125
|
-
@property
|
|
126
|
-
def returncode(self) -> int: ...
|
|
127
|
-
async def poll(self) -> typing.Optional[int]: ...
|
|
128
|
-
async def wait(self) -> int: ...
|
|
129
|
-
async def attach(self): ...
|
|
130
|
-
|
|
131
|
-
class _ContainerProcess(typing.Generic[T]):
|
|
132
|
-
"""Represents a running process in a container."""
|
|
133
20
|
def __init__(
|
|
134
21
|
self,
|
|
135
22
|
process_id: str,
|
|
136
23
|
task_id: str,
|
|
137
24
|
client: modal.client._Client,
|
|
25
|
+
command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
|
|
138
26
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
139
27
|
stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
140
28
|
exec_deadline: typing.Optional[float] = None,
|
|
141
29
|
text: bool = True,
|
|
142
30
|
by_line: bool = False,
|
|
143
|
-
command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
|
|
144
31
|
) -> None:
|
|
145
32
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
146
33
|
...
|
|
@@ -182,18 +69,22 @@ class _ContainerProcess(typing.Generic[T]):
|
|
|
182
69
|
...
|
|
183
70
|
|
|
184
71
|
class ContainerProcess(typing.Generic[T]):
|
|
185
|
-
"""Represents a running process in a container.
|
|
72
|
+
"""Represents a running process in a container.
|
|
73
|
+
|
|
74
|
+
Container processes communicate via direct communication with
|
|
75
|
+
the Modal worker where the container is running.
|
|
76
|
+
"""
|
|
186
77
|
def __init__(
|
|
187
78
|
self,
|
|
188
79
|
process_id: str,
|
|
189
80
|
task_id: str,
|
|
190
81
|
client: modal.client.Client,
|
|
82
|
+
command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
|
|
191
83
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
192
84
|
stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
|
193
85
|
exec_deadline: typing.Optional[float] = None,
|
|
194
86
|
text: bool = True,
|
|
195
87
|
by_line: bool = False,
|
|
196
|
-
command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
|
|
197
88
|
) -> None: ...
|
|
198
89
|
def __repr__(self) -> str: ...
|
|
199
90
|
@property
|