modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__py3-none-any.whl

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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

Files changed (147) hide show
  1. modal/__main__.py +3 -4
  2. modal/_billing.py +80 -0
  3. modal/_clustered_functions.py +7 -3
  4. modal/_clustered_functions.pyi +4 -2
  5. modal/_container_entrypoint.py +41 -49
  6. modal/_functions.py +424 -195
  7. modal/_grpc_client.py +171 -0
  8. modal/_load_context.py +105 -0
  9. modal/_object.py +68 -20
  10. modal/_output.py +58 -45
  11. modal/_partial_function.py +36 -11
  12. modal/_pty.py +7 -3
  13. modal/_resolver.py +21 -35
  14. modal/_runtime/asgi.py +4 -3
  15. modal/_runtime/container_io_manager.py +301 -186
  16. modal/_runtime/container_io_manager.pyi +70 -61
  17. modal/_runtime/execution_context.py +18 -2
  18. modal/_runtime/execution_context.pyi +4 -1
  19. modal/_runtime/gpu_memory_snapshot.py +170 -63
  20. modal/_runtime/user_code_imports.py +28 -58
  21. modal/_serialization.py +57 -1
  22. modal/_utils/async_utils.py +33 -12
  23. modal/_utils/auth_token_manager.py +2 -5
  24. modal/_utils/blob_utils.py +110 -53
  25. modal/_utils/function_utils.py +49 -42
  26. modal/_utils/grpc_utils.py +80 -50
  27. modal/_utils/mount_utils.py +26 -1
  28. modal/_utils/name_utils.py +17 -3
  29. modal/_utils/task_command_router_client.py +536 -0
  30. modal/_utils/time_utils.py +34 -6
  31. modal/app.py +219 -83
  32. modal/app.pyi +229 -56
  33. modal/billing.py +5 -0
  34. modal/{requirements → builder}/2025.06.txt +1 -0
  35. modal/{requirements → builder}/PREVIEW.txt +1 -0
  36. modal/cli/_download.py +19 -3
  37. modal/cli/_traceback.py +3 -2
  38. modal/cli/app.py +4 -4
  39. modal/cli/cluster.py +15 -7
  40. modal/cli/config.py +5 -3
  41. modal/cli/container.py +7 -6
  42. modal/cli/dict.py +22 -16
  43. modal/cli/entry_point.py +12 -5
  44. modal/cli/environment.py +5 -4
  45. modal/cli/import_refs.py +3 -3
  46. modal/cli/launch.py +102 -5
  47. modal/cli/network_file_system.py +9 -13
  48. modal/cli/profile.py +3 -2
  49. modal/cli/programs/launch_instance_ssh.py +94 -0
  50. modal/cli/programs/run_jupyter.py +1 -1
  51. modal/cli/programs/run_marimo.py +95 -0
  52. modal/cli/programs/vscode.py +1 -1
  53. modal/cli/queues.py +57 -26
  54. modal/cli/run.py +58 -16
  55. modal/cli/secret.py +48 -22
  56. modal/cli/utils.py +3 -4
  57. modal/cli/volume.py +28 -25
  58. modal/client.py +13 -116
  59. modal/client.pyi +9 -91
  60. modal/cloud_bucket_mount.py +5 -3
  61. modal/cloud_bucket_mount.pyi +5 -1
  62. modal/cls.py +130 -102
  63. modal/cls.pyi +45 -85
  64. modal/config.py +29 -10
  65. modal/container_process.py +291 -13
  66. modal/container_process.pyi +95 -32
  67. modal/dict.py +282 -63
  68. modal/dict.pyi +423 -73
  69. modal/environments.py +15 -27
  70. modal/environments.pyi +5 -15
  71. modal/exception.py +8 -0
  72. modal/experimental/__init__.py +143 -38
  73. modal/experimental/flash.py +247 -78
  74. modal/experimental/flash.pyi +137 -9
  75. modal/file_io.py +14 -28
  76. modal/file_io.pyi +2 -2
  77. modal/file_pattern_matcher.py +25 -16
  78. modal/functions.pyi +134 -61
  79. modal/image.py +255 -86
  80. modal/image.pyi +300 -62
  81. modal/io_streams.py +436 -126
  82. modal/io_streams.pyi +236 -171
  83. modal/mount.py +62 -157
  84. modal/mount.pyi +45 -172
  85. modal/network_file_system.py +30 -53
  86. modal/network_file_system.pyi +16 -76
  87. modal/object.pyi +42 -8
  88. modal/parallel_map.py +821 -113
  89. modal/parallel_map.pyi +134 -0
  90. modal/partial_function.pyi +4 -1
  91. modal/proxy.py +16 -7
  92. modal/proxy.pyi +10 -2
  93. modal/queue.py +263 -61
  94. modal/queue.pyi +409 -66
  95. modal/runner.py +112 -92
  96. modal/runner.pyi +45 -27
  97. modal/sandbox.py +451 -124
  98. modal/sandbox.pyi +513 -67
  99. modal/secret.py +291 -67
  100. modal/secret.pyi +425 -19
  101. modal/serving.py +7 -11
  102. modal/serving.pyi +7 -8
  103. modal/snapshot.py +11 -8
  104. modal/token_flow.py +4 -4
  105. modal/volume.py +344 -98
  106. modal/volume.pyi +464 -68
  107. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
  108. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  109. modal_docs/mdmd/mdmd.py +11 -1
  110. modal_proto/api.proto +399 -67
  111. modal_proto/api_grpc.py +241 -1
  112. modal_proto/api_pb2.py +1395 -1000
  113. modal_proto/api_pb2.pyi +1239 -79
  114. modal_proto/api_pb2_grpc.py +499 -4
  115. modal_proto/api_pb2_grpc.pyi +162 -14
  116. modal_proto/modal_api_grpc.py +175 -160
  117. modal_proto/sandbox_router.proto +145 -0
  118. modal_proto/sandbox_router_grpc.py +105 -0
  119. modal_proto/sandbox_router_pb2.py +149 -0
  120. modal_proto/sandbox_router_pb2.pyi +333 -0
  121. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  122. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  123. modal_proto/task_command_router.proto +144 -0
  124. modal_proto/task_command_router_grpc.py +105 -0
  125. modal_proto/task_command_router_pb2.py +149 -0
  126. modal_proto/task_command_router_pb2.pyi +333 -0
  127. modal_proto/task_command_router_pb2_grpc.py +203 -0
  128. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  129. modal_version/__init__.py +1 -1
  130. modal-1.0.6.dev58.dist-info/RECORD +0 -183
  131. modal_proto/modal_options_grpc.py +0 -3
  132. modal_proto/options.proto +0 -19
  133. modal_proto/options_grpc.py +0 -3
  134. modal_proto/options_pb2.py +0 -35
  135. modal_proto/options_pb2.pyi +0 -20
  136. modal_proto/options_pb2_grpc.py +0 -4
  137. modal_proto/options_pb2_grpc.pyi +0 -7
  138. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  139. /modal/{requirements → builder}/2023.12.txt +0 -0
  140. /modal/{requirements → builder}/2024.04.txt +0 -0
  141. /modal/{requirements → builder}/2024.10.txt +0 -0
  142. /modal/{requirements → builder}/README.md +0 -0
  143. /modal/{requirements → builder}/base-images.json +0 -0
  144. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  145. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  146. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  147. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/io_streams.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  import collections.abc
2
+ import modal._utils.task_command_router_client
2
3
  import modal.client
3
4
  import modal.stream_type
4
5
  import typing
@@ -17,29 +18,10 @@ def _container_process_logs_iterator(
17
18
 
18
19
  T = typing.TypeVar("T")
19
20
 
20
- class _StreamReader(typing.Generic[T]):
21
- """Retrieve logs from a stream (`stdout` or `stderr`).
22
-
23
- As an asynchronous iterable, the object supports the `for` and `async for`
24
- statements. Just loop over the object to read in chunks.
25
-
26
- **Usage**
21
+ class _StreamReaderThroughServer(typing.Generic[T]):
22
+ """A StreamReader implementation that reads from the server."""
27
23
 
28
- ```python fixture:running_app
29
- from modal import Sandbox
30
-
31
- sandbox = Sandbox.create(
32
- "bash",
33
- "-c",
34
- "for i in $(seq 1 10); do echo foo; sleep 0.1; done",
35
- app=running_app,
36
- )
37
- for message in sandbox.stdout:
38
- print(f"Message: {message}")
39
- ```
40
- """
41
-
42
- _stream: typing.Optional[collections.abc.AsyncGenerator[typing.Optional[bytes], None]]
24
+ _stream: typing.Optional[collections.abc.AsyncGenerator[T, None]]
43
25
 
44
26
  def __init__(
45
27
  self,
@@ -61,19 +43,7 @@ class _StreamReader(typing.Generic[T]):
61
43
  ...
62
44
 
63
45
  async def read(self) -> T:
64
- """Fetch the entire contents of the stream until EOF.
65
-
66
- **Usage**
67
-
68
- ```python fixture:running_app
69
- from modal import Sandbox
70
-
71
- sandbox = Sandbox.create("echo", "hello", app=running_app)
72
- sandbox.wait()
73
-
74
- print(sandbox.stdout.read())
75
- ```
76
- """
46
+ """Fetch the entire contents of the stream until EOF."""
77
47
  ...
78
48
 
79
49
  async def _consume_container_process_stream(self):
@@ -84,9 +54,7 @@ class _StreamReader(typing.Generic[T]):
84
54
  """Streams the container process buffer to the reader."""
85
55
  ...
86
56
 
87
- def _get_logs(
88
- self, skip_empty_messages: bool = True
89
- ) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]:
57
+ def _get_logs(self, skip_empty_messages: bool = True) -> collections.abc.AsyncGenerator[bytes, None]:
90
58
  """Streams sandbox or process logs from the server to the reader.
91
59
 
92
60
  Logs returned by this method may contain partial or multiple lines at a time.
@@ -96,11 +64,158 @@ class _StreamReader(typing.Generic[T]):
96
64
  """
97
65
  ...
98
66
 
99
- def _get_logs_by_line(self) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]:
67
+ def _get_logs_by_line(self) -> collections.abc.AsyncGenerator[bytes, None]:
100
68
  """Process logs from the server and yield complete lines only."""
101
69
  ...
102
70
 
103
- def _ensure_stream(self) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]: ...
71
+ def _ensure_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
72
+ async def __anext__(self) -> T:
73
+ """mdmd:hidden"""
74
+ ...
75
+
76
+ async def aclose(self):
77
+ """mdmd:hidden"""
78
+ ...
79
+
80
+ def _decode_bytes_stream_to_str(
81
+ stream: collections.abc.AsyncGenerator[bytes, None],
82
+ ) -> collections.abc.AsyncGenerator[str, None]:
83
+ """Incrementally decode a bytes async generator as UTF-8 without breaking on chunk boundaries.
84
+
85
+ This function uses a streaming UTF-8 decoder so that multi-byte characters split across
86
+ chunks are handled correctly instead of raising ``UnicodeDecodeError``.
87
+ """
88
+ ...
89
+
90
+ def _stream_by_line(stream: collections.abc.AsyncGenerator[bytes, None]) -> collections.abc.AsyncGenerator[bytes, None]:
91
+ """Yield complete lines only (ending with
92
+ ), buffering partial lines until complete.
93
+ """
94
+ ...
95
+
96
+ class _StreamReaderThroughCommandRouterParams:
97
+ """_StreamReaderThroughCommandRouterParams(file_descriptor: 'api_pb2.FileDescriptor.ValueType', task_id: str, object_id: str, command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient, deadline: Optional[float])"""
98
+
99
+ file_descriptor: int
100
+ task_id: str
101
+ object_id: str
102
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient
103
+ deadline: typing.Optional[float]
104
+
105
+ def __init__(
106
+ self,
107
+ file_descriptor: int,
108
+ task_id: str,
109
+ object_id: str,
110
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
111
+ deadline: typing.Optional[float],
112
+ ) -> None:
113
+ """Initialize self. See help(type(self)) for accurate signature."""
114
+ ...
115
+
116
+ def __repr__(self):
117
+ """Return repr(self)."""
118
+ ...
119
+
120
+ def __eq__(self, other):
121
+ """Return self==value."""
122
+ ...
123
+
124
+ def _stdio_stream_from_command_router(
125
+ params: _StreamReaderThroughCommandRouterParams,
126
+ ) -> collections.abc.AsyncGenerator[bytes, None]:
127
+ """Stream raw bytes from the router client."""
128
+ ...
129
+
130
+ class _BytesStreamReaderThroughCommandRouter(typing.Generic[T]):
131
+ """StreamReader implementation that will read directly from the worker that
132
+ hosts the sandbox.
133
+
134
+ This implementation is used for non-text streams.
135
+ """
136
+ def __init__(self, params: _StreamReaderThroughCommandRouterParams) -> None:
137
+ """Initialize self. See help(type(self)) for accurate signature."""
138
+ ...
139
+
140
+ @property
141
+ def file_descriptor(self) -> int: ...
142
+ async def read(self) -> T: ...
143
+ def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
144
+ async def __anext__(self) -> T: ...
145
+ async def aclose(self): ...
146
+
147
+ class _TextStreamReaderThroughCommandRouter(typing.Generic[T]):
148
+ """StreamReader implementation that will read directly from the worker
149
+ that hosts the sandbox.
150
+
151
+ This implementation is used for text streams.
152
+ """
153
+ def __init__(self, params: _StreamReaderThroughCommandRouterParams, by_line: bool) -> None:
154
+ """Initialize self. See help(type(self)) for accurate signature."""
155
+ ...
156
+
157
+ @property
158
+ def file_descriptor(self) -> int: ...
159
+ async def read(self) -> T: ...
160
+ def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
161
+ async def __anext__(self) -> T: ...
162
+ async def aclose(self): ...
163
+
164
+ class _DevnullStreamReader(typing.Generic[T]):
165
+ """StreamReader implementation for a stream configured with
166
+ StreamType.DEVNULL. Throws an error if read or any other method is
167
+ called.
168
+ """
169
+ def __init__(self, file_descriptor: int) -> None:
170
+ """Initialize self. See help(type(self)) for accurate signature."""
171
+ ...
172
+
173
+ @property
174
+ def file_descriptor(self) -> int: ...
175
+ async def read(self) -> T: ...
176
+ def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
177
+ async def __anext__(self) -> T: ...
178
+ async def aclose(self): ...
179
+
180
+ class _StreamReader(typing.Generic[T]):
181
+ """Retrieve logs from a stream (`stdout` or `stderr`).
182
+
183
+ As an asynchronous iterable, the object supports the `for` and `async for`
184
+ statements. Just loop over the object to read in chunks.
185
+ """
186
+
187
+ _impl: typing.Union[
188
+ _StreamReaderThroughServer,
189
+ _DevnullStreamReader,
190
+ _TextStreamReaderThroughCommandRouter,
191
+ _BytesStreamReaderThroughCommandRouter,
192
+ ]
193
+
194
+ def __init__(
195
+ self,
196
+ file_descriptor: int,
197
+ object_id: str,
198
+ object_type: typing.Literal["sandbox", "container_process"],
199
+ client: modal.client._Client,
200
+ stream_type: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
201
+ text: bool = True,
202
+ by_line: bool = False,
203
+ deadline: typing.Optional[float] = None,
204
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
205
+ task_id: typing.Optional[str] = None,
206
+ ) -> None:
207
+ """mdmd:hidden"""
208
+ ...
209
+
210
+ @property
211
+ def file_descriptor(self) -> int:
212
+ """Possible values are `1` for stdout and `2` for stderr."""
213
+ ...
214
+
215
+ async def read(self) -> T:
216
+ """Fetch the entire contents of the stream until EOF."""
217
+ ...
218
+
104
219
  def __aiter__(self) -> collections.abc.AsyncIterator[T]:
105
220
  """mdmd:hidden"""
106
221
  ...
@@ -113,7 +228,7 @@ class _StreamReader(typing.Generic[T]):
113
228
  """mdmd:hidden"""
114
229
  ...
115
230
 
116
- class _StreamWriter:
231
+ class _StreamWriterThroughServer:
117
232
  """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
118
233
  def __init__(
119
234
  self, object_id: str, object_type: typing.Literal["sandbox", "container_process"], client: modal.client._Client
@@ -127,24 +242,71 @@ class _StreamWriter:
127
242
 
128
243
  This is non-blocking and queues the data to an internal buffer. Must be
129
244
  used along with the `drain()` method, which flushes the buffer.
245
+ """
246
+ ...
130
247
 
131
- **Usage**
248
+ def write_eof(self) -> None:
249
+ """Close the write end of the stream after the buffered data is drained.
250
+
251
+ If the process was blocked on input, it will become unblocked after
252
+ `write_eof()`. This method needs to be used along with the `drain()`
253
+ method, which flushes the EOF to the process.
254
+ """
255
+ ...
256
+
257
+ async def drain(self) -> None:
258
+ """Flush the write buffer and send data to the running process.
259
+
260
+ This is a flow control method that blocks until data is sent. It returns
261
+ when it is appropriate to continue writing data to the stream.
262
+ """
263
+ ...
264
+
265
+ class _StreamWriterThroughCommandRouter:
266
+ def __init__(
267
+ self,
268
+ object_id: str,
269
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
270
+ task_id: str,
271
+ ) -> None:
272
+ """Initialize self. See help(type(self)) for accurate signature."""
273
+ ...
132
274
 
133
- ```python fixture:running_app
134
- from modal import Sandbox
275
+ def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None: ...
276
+ def write_eof(self) -> None: ...
277
+ async def drain(self) -> None: ...
135
278
 
136
- sandbox = Sandbox.create(
279
+ class _StreamWriter:
280
+ """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
281
+ def __init__(
282
+ self,
283
+ object_id: str,
284
+ object_type: typing.Literal["sandbox", "container_process"],
285
+ client: modal.client._Client,
286
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
287
+ task_id: typing.Optional[str] = None,
288
+ ) -> None:
289
+ """mdmd:hidden"""
290
+ ...
291
+
292
+ def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None:
293
+ """Write data to the stream but does not send it immediately.
294
+
295
+ This is non-blocking and queues the data to an internal buffer. Must be
296
+ used along with the `drain()` method, which flushes the buffer.
297
+
298
+ **Usage**
299
+
300
+ ```python fixture:sandbox
301
+ proc = sandbox.exec(
137
302
  "bash",
138
303
  "-c",
139
304
  "while read line; do echo $line; done",
140
- app=running_app,
141
305
  )
142
- sandbox.stdin.write(b"foo\n")
143
- sandbox.stdin.write(b"bar\n")
144
- sandbox.stdin.write_eof()
145
-
146
- sandbox.stdin.drain()
147
- sandbox.wait()
306
+ proc.stdin.write(b"foo\n")
307
+ proc.stdin.write(b"bar\n")
308
+ proc.stdin.write_eof()
309
+ proc.stdin.drain()
148
310
  ```
149
311
  """
150
312
  ...
@@ -188,24 +350,14 @@ class StreamReader(typing.Generic[T]):
188
350
 
189
351
  As an asynchronous iterable, the object supports the `for` and `async for`
190
352
  statements. Just loop over the object to read in chunks.
191
-
192
- **Usage**
193
-
194
- ```python fixture:running_app
195
- from modal import Sandbox
196
-
197
- sandbox = Sandbox.create(
198
- "bash",
199
- "-c",
200
- "for i in $(seq 1 10); do echo foo; sleep 0.1; done",
201
- app=running_app,
202
- )
203
- for message in sandbox.stdout:
204
- print(f"Message: {message}")
205
- ```
206
353
  """
207
354
 
208
- _stream: typing.Optional[collections.abc.AsyncGenerator[typing.Optional[bytes], None]]
355
+ _impl: typing.Union[
356
+ _StreamReaderThroughServer,
357
+ _DevnullStreamReader,
358
+ _TextStreamReaderThroughCommandRouter,
359
+ _BytesStreamReaderThroughCommandRouter,
360
+ ]
209
361
 
210
362
  def __init__(
211
363
  self,
@@ -217,6 +369,8 @@ class StreamReader(typing.Generic[T]):
217
369
  text: bool = True,
218
370
  by_line: bool = False,
219
371
  deadline: typing.Optional[float] = None,
372
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
373
+ task_id: typing.Optional[str] = None,
220
374
  ) -> None:
221
375
  """mdmd:hidden"""
222
376
  ...
@@ -228,103 +382,15 @@ class StreamReader(typing.Generic[T]):
228
382
 
229
383
  class __read_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
230
384
  def __call__(self, /) -> T_INNER:
231
- """Fetch the entire contents of the stream until EOF.
232
-
233
- **Usage**
234
-
235
- ```python fixture:running_app
236
- from modal import Sandbox
237
-
238
- sandbox = Sandbox.create("echo", "hello", app=running_app)
239
- sandbox.wait()
240
-
241
- print(sandbox.stdout.read())
242
- ```
243
- """
385
+ """Fetch the entire contents of the stream until EOF."""
244
386
  ...
245
387
 
246
388
  async def aio(self, /) -> T_INNER:
247
- """Fetch the entire contents of the stream until EOF.
248
-
249
- **Usage**
250
-
251
- ```python fixture:running_app
252
- from modal import Sandbox
253
-
254
- sandbox = Sandbox.create("echo", "hello", app=running_app)
255
- sandbox.wait()
256
-
257
- print(sandbox.stdout.read())
258
- ```
259
- """
389
+ """Fetch the entire contents of the stream until EOF."""
260
390
  ...
261
391
 
262
392
  read: __read_spec[T, typing_extensions.Self]
263
393
 
264
- class ___consume_container_process_stream_spec(typing_extensions.Protocol[SUPERSELF]):
265
- def __call__(self, /):
266
- """Consume the container process stream and store messages in the buffer."""
267
- ...
268
-
269
- async def aio(self, /):
270
- """Consume the container process stream and store messages in the buffer."""
271
- ...
272
-
273
- _consume_container_process_stream: ___consume_container_process_stream_spec[typing_extensions.Self]
274
-
275
- class ___stream_container_process_spec(typing_extensions.Protocol[SUPERSELF]):
276
- def __call__(self, /) -> typing.Generator[tuple[typing.Optional[bytes], str], None, None]:
277
- """Streams the container process buffer to the reader."""
278
- ...
279
-
280
- def aio(self, /) -> collections.abc.AsyncGenerator[tuple[typing.Optional[bytes], str], None]:
281
- """Streams the container process buffer to the reader."""
282
- ...
283
-
284
- _stream_container_process: ___stream_container_process_spec[typing_extensions.Self]
285
-
286
- class ___get_logs_spec(typing_extensions.Protocol[SUPERSELF]):
287
- def __call__(self, /, skip_empty_messages: bool = True) -> typing.Generator[typing.Optional[bytes], None, None]:
288
- """Streams sandbox or process logs from the server to the reader.
289
-
290
- Logs returned by this method may contain partial or multiple lines at a time.
291
-
292
- When the stream receives an EOF, it yields None. Once an EOF is received,
293
- subsequent invocations will not yield logs.
294
- """
295
- ...
296
-
297
- def aio(
298
- self, /, skip_empty_messages: bool = True
299
- ) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]:
300
- """Streams sandbox or process logs from the server to the reader.
301
-
302
- Logs returned by this method may contain partial or multiple lines at a time.
303
-
304
- When the stream receives an EOF, it yields None. Once an EOF is received,
305
- subsequent invocations will not yield logs.
306
- """
307
- ...
308
-
309
- _get_logs: ___get_logs_spec[typing_extensions.Self]
310
-
311
- class ___get_logs_by_line_spec(typing_extensions.Protocol[SUPERSELF]):
312
- def __call__(self, /) -> typing.Generator[typing.Optional[bytes], None, None]:
313
- """Process logs from the server and yield complete lines only."""
314
- ...
315
-
316
- def aio(self, /) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]:
317
- """Process logs from the server and yield complete lines only."""
318
- ...
319
-
320
- _get_logs_by_line: ___get_logs_by_line_spec[typing_extensions.Self]
321
-
322
- class ___ensure_stream_spec(typing_extensions.Protocol[SUPERSELF]):
323
- def __call__(self, /) -> typing.Generator[typing.Optional[bytes], None, None]: ...
324
- def aio(self, /) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]: ...
325
-
326
- _ensure_stream: ___ensure_stream_spec[typing_extensions.Self]
327
-
328
394
  def __iter__(self) -> typing.Iterator[T]:
329
395
  """mdmd:hidden"""
330
396
  ...
@@ -352,12 +418,16 @@ class StreamReader(typing.Generic[T]):
352
418
  class StreamWriter:
353
419
  """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
354
420
  def __init__(
355
- self, object_id: str, object_type: typing.Literal["sandbox", "container_process"], client: modal.client.Client
421
+ self,
422
+ object_id: str,
423
+ object_type: typing.Literal["sandbox", "container_process"],
424
+ client: modal.client.Client,
425
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
426
+ task_id: typing.Optional[str] = None,
356
427
  ) -> None:
357
428
  """mdmd:hidden"""
358
429
  ...
359
430
 
360
- def _get_next_index(self) -> int: ...
361
431
  def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None:
362
432
  """Write data to the stream but does not send it immediately.
363
433
 
@@ -366,21 +436,16 @@ class StreamWriter:
366
436
 
367
437
  **Usage**
368
438
 
369
- ```python fixture:running_app
370
- from modal import Sandbox
371
-
372
- sandbox = Sandbox.create(
439
+ ```python fixture:sandbox
440
+ proc = sandbox.exec(
373
441
  "bash",
374
442
  "-c",
375
443
  "while read line; do echo $line; done",
376
- app=running_app,
377
444
  )
378
- sandbox.stdin.write(b"foo\n")
379
- sandbox.stdin.write(b"bar\n")
380
- sandbox.stdin.write_eof()
381
-
382
- sandbox.stdin.drain()
383
- sandbox.wait()
445
+ proc.stdin.write(b"foo\n")
446
+ proc.stdin.write(b"bar\n")
447
+ proc.stdin.write_eof()
448
+ proc.stdin.drain()
384
449
  ```
385
450
  """
386
451
  ...