modAL 1.2.7.dev7__tar.gz → 1.2.7.dev9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/PKG-INFO +1 -1
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/blob_utils.py +5 -4
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/client.pyi +2 -2
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/file_io.py +22 -10
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/io_streams.py +10 -7
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_version/__init__.py +1 -1
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/LICENSE +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/README.md +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/__main__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_billing.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_clustered_functions.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_functions.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_grpc_client.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_ipython.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_load_context.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_location.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_object.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_output.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_partial_function.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_pty.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_resolver.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_resources.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_serialization.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_traceback.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_tunnel.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_tunnel.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_type_manager.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/logger.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/_watcher.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/app.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/app.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/billing.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/README.md +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/builder/base-images.json +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/call_graph.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/_download.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/_traceback.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/app.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/cluster.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/config.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/container.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/dict.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/entry_point.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/environment.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/import_refs.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/launch.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/profile.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/queues.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/run.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/secret.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/shell.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/token.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/utils.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cli/volume.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/client.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cls.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/cls.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/config.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/container_process.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/container_process.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/dict.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/dict.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/environments.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/environments.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/exception.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/experimental/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/experimental/flash.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/experimental/flash.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/experimental/ipython.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/file_io.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/functions.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/functions.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/gpu.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/image.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/image.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/io_streams.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/mount.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/mount.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/network_file_system.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/network_file_system.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/object.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/object.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/output.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/parallel_map.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/parallel_map.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/partial_function.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/partial_function.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/proxy.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/proxy.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/py.typed +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/queue.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/queue.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/retries.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/runner.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/runner.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/running_app.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/sandbox.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/sandbox.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/schedule.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/scheduler_placement.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/secret.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/secret.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/serving.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/serving.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/snapshot.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/snapshot.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/stream_type.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/token_flow.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/token_flow.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/volume.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal/volume.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/__init__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api.proto +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api_pb2.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/py.typed +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/modal_version/__main__.py +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/pyproject.toml +0 -0
- {modal-1.2.7.dev7 → modal-1.2.7.dev9}/setup.cfg +0 -0
|
@@ -152,12 +152,13 @@ async def perform_multipart_upload(
|
|
|
152
152
|
part_etags = await TaskContext.gather(*upload_coros)
|
|
153
153
|
|
|
154
154
|
# The body of the complete_multipart_upload command needs some data in xml format:
|
|
155
|
-
|
|
155
|
+
completion_parts = ["<CompleteMultipartUpload>"]
|
|
156
156
|
for part_number, etag in enumerate(part_etags, 1):
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
completion_parts.append(f"""<Part>\n<PartNumber>{part_number}</PartNumber>\n<ETag>"{etag}"</ETag>\n</Part>""")
|
|
158
|
+
completion_parts.append("</CompleteMultipartUpload>")
|
|
159
|
+
completion_body = "\n".join(completion_parts)
|
|
159
160
|
|
|
160
|
-
# etag of combined object should be md5 hex of
|
|
161
|
+
# etag of combined object should be md5 hex of concatenated md5 *bytes* from parts + `-{num_parts}`
|
|
161
162
|
bin_hash_parts = [bytes.fromhex(etag) for etag in part_etags]
|
|
162
163
|
|
|
163
164
|
expected_multipart_etag = hashlib.md5(b"".join(bin_hash_parts)).hexdigest() + f"-{len(part_etags)}"
|
|
@@ -32,7 +32,7 @@ class _Client:
|
|
|
32
32
|
server_url: str,
|
|
33
33
|
client_type: int,
|
|
34
34
|
credentials: typing.Optional[tuple[str, str]],
|
|
35
|
-
version: str = "1.2.7.
|
|
35
|
+
version: str = "1.2.7.dev9",
|
|
36
36
|
):
|
|
37
37
|
"""mdmd:hidden
|
|
38
38
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -163,7 +163,7 @@ class Client:
|
|
|
163
163
|
server_url: str,
|
|
164
164
|
client_type: int,
|
|
165
165
|
credentials: typing.Optional[tuple[str, str]],
|
|
166
|
-
version: str = "1.2.7.
|
|
166
|
+
version: str = "1.2.7.dev9",
|
|
167
167
|
):
|
|
168
168
|
"""mdmd:hidden
|
|
169
169
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -190,7 +190,7 @@ class _FileIO(Generic[T]):
|
|
|
190
190
|
|
|
191
191
|
async def _wait(self, exec_id: str) -> bytes:
|
|
192
192
|
# The logic here is similar to how output is read from `exec`
|
|
193
|
-
|
|
193
|
+
output_buffer = io.BytesIO()
|
|
194
194
|
completed = False
|
|
195
195
|
retries_remaining = 10
|
|
196
196
|
while not completed:
|
|
@@ -201,7 +201,7 @@ class _FileIO(Generic[T]):
|
|
|
201
201
|
break
|
|
202
202
|
if isinstance(data, Exception):
|
|
203
203
|
raise data
|
|
204
|
-
|
|
204
|
+
output_buffer.write(data)
|
|
205
205
|
except (GRPCError, StreamTerminatedError) as exc:
|
|
206
206
|
if retries_remaining > 0:
|
|
207
207
|
retries_remaining -= 1
|
|
@@ -212,7 +212,7 @@ class _FileIO(Generic[T]):
|
|
|
212
212
|
elif isinstance(exc, StreamTerminatedError):
|
|
213
213
|
continue
|
|
214
214
|
raise
|
|
215
|
-
return
|
|
215
|
+
return output_buffer.getvalue()
|
|
216
216
|
|
|
217
217
|
def _validate_type(self, data: Union[bytes, str]) -> None:
|
|
218
218
|
if self._binary and isinstance(data, str):
|
|
@@ -421,10 +421,22 @@ class _FileIO(Generic[T]):
|
|
|
421
421
|
task_id=self._task_id,
|
|
422
422
|
),
|
|
423
423
|
)
|
|
424
|
+
|
|
425
|
+
def end_of_event(item: bytes, buffer: io.BytesIO, boundary_token: bytes) -> bool:
|
|
426
|
+
if not item.endswith(b"\n"):
|
|
427
|
+
return False
|
|
428
|
+
boundary_token_size = len(boundary_token)
|
|
429
|
+
if buffer.tell() < boundary_token_size:
|
|
430
|
+
return False
|
|
431
|
+
buffer.seek(-boundary_token_size, io.SEEK_END)
|
|
432
|
+
if buffer.read(boundary_token_size) == boundary_token:
|
|
433
|
+
return True
|
|
434
|
+
return False
|
|
435
|
+
|
|
424
436
|
async with TaskContext() as tc:
|
|
425
437
|
tc.create_task(self._consume_watch_output(resp.exec_id))
|
|
426
438
|
|
|
427
|
-
|
|
439
|
+
item_buffer = io.BytesIO()
|
|
428
440
|
while True:
|
|
429
441
|
if len(self._watch_output_buffer) > 0:
|
|
430
442
|
item = self._watch_output_buffer.pop(0)
|
|
@@ -432,12 +444,12 @@ class _FileIO(Generic[T]):
|
|
|
432
444
|
break
|
|
433
445
|
if isinstance(item, Exception):
|
|
434
446
|
raise item
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
#
|
|
438
|
-
if
|
|
447
|
+
item_buffer.write(item)
|
|
448
|
+
assert isinstance(item, bytes)
|
|
449
|
+
# Single events may span multiple messages so we need to check for a special event boundary token
|
|
450
|
+
if end_of_event(item, item_buffer, boundary_token=b"\n\n"):
|
|
439
451
|
try:
|
|
440
|
-
event_json = json.loads(
|
|
452
|
+
event_json = json.loads(item_buffer.getvalue().strip().decode())
|
|
441
453
|
event = FileWatchEvent(
|
|
442
454
|
type=FileWatchEventType(event_json["event_type"]),
|
|
443
455
|
paths=event_json["paths"],
|
|
@@ -447,7 +459,7 @@ class _FileIO(Generic[T]):
|
|
|
447
459
|
except (json.JSONDecodeError, KeyError, ValueError):
|
|
448
460
|
# skip invalid events
|
|
449
461
|
pass
|
|
450
|
-
|
|
462
|
+
item_buffer = io.BytesIO()
|
|
451
463
|
else:
|
|
452
464
|
await asyncio.sleep(0.1)
|
|
453
465
|
|
|
@@ -23,7 +23,7 @@ from grpclib.exceptions import GRPCError, StreamTerminatedError
|
|
|
23
23
|
from modal.exception import ClientClosed, ExecTimeoutError, InvalidError
|
|
24
24
|
from modal_proto import api_pb2
|
|
25
25
|
|
|
26
|
-
from ._utils.async_utils import aclosing, synchronize_api
|
|
26
|
+
from ._utils.async_utils import aclosing, synchronize_api, synchronizer
|
|
27
27
|
from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES
|
|
28
28
|
from ._utils.task_command_router_client import TaskCommandRouterClient
|
|
29
29
|
from .client import _Client
|
|
@@ -153,17 +153,17 @@ class _StreamReaderThroughServer(Generic[T]):
|
|
|
153
153
|
"""Fetch the entire contents of the stream until EOF."""
|
|
154
154
|
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read starting")
|
|
155
155
|
if self._text:
|
|
156
|
-
|
|
156
|
+
buffer = io.StringIO()
|
|
157
157
|
async for message in _decode_bytes_stream_to_str(self._get_logs()):
|
|
158
|
-
|
|
158
|
+
buffer.write(message)
|
|
159
159
|
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
|
160
|
-
return cast(T,
|
|
160
|
+
return cast(T, buffer.getvalue())
|
|
161
161
|
else:
|
|
162
|
-
|
|
162
|
+
buffer = io.BytesIO()
|
|
163
163
|
async for message in self._get_logs():
|
|
164
|
-
|
|
164
|
+
buffer.write(message)
|
|
165
165
|
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
|
166
|
-
return cast(T,
|
|
166
|
+
return cast(T, buffer.getvalue())
|
|
167
167
|
|
|
168
168
|
async def _consume_container_process_stream(self):
|
|
169
169
|
"""Consume the container process stream and store messages in the buffer."""
|
|
@@ -566,6 +566,9 @@ class _StreamReader(Generic[T]):
|
|
|
566
566
|
task_id: Optional[str] = None,
|
|
567
567
|
) -> None:
|
|
568
568
|
"""mdmd:hidden"""
|
|
569
|
+
# we can remove this once we ensure no constructors use async code
|
|
570
|
+
assert asyncio.get_running_loop() == synchronizer._get_loop(start=False)
|
|
571
|
+
|
|
569
572
|
if by_line and not text:
|
|
570
573
|
raise ValueError("line-buffering is only supported when text=True")
|
|
571
574
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|