modAL 1.4.3.dev18__py3-none-any.whl → 1.4.3.dev19__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.
@@ -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 ConflictError, ExecTimeoutError
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 try_init(
199
+ async def init(
200
200
  cls,
201
201
  server_client,
202
202
  task_id: str,
203
- ) -> Optional["TaskCommandRouterClient"]:
204
- """Attempt to initialize a TaskCommandRouterClient by fetching direct access.
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.try_init() instead."""
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
modal/app.py CHANGED
@@ -778,7 +778,6 @@ class _App:
778
778
  deprecation_warning(
779
779
  (2025, 12, 16),
780
780
  "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
781
- pending=True,
782
781
  )
783
782
  single_use_containers = max_inputs == 1
784
783
 
@@ -987,7 +986,6 @@ class _App:
987
986
  deprecation_warning(
988
987
  (2025, 12, 16),
989
988
  "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
990
- pending=True,
991
989
  )
992
990
  single_use_containers = max_inputs == 1
993
991
 
modal/cli/cluster.py CHANGED
@@ -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.try_init(client, task_id)
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
 
modal/cli/container.py CHANGED
@@ -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.try_init(client, container_id)
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
 
modal/client.pyi CHANGED
@@ -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.dev18",
38
+ version: str = "1.4.3.dev19",
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.dev18",
178
+ version: str = "1.4.3.dev19",
179
179
  ):
180
180
  """mdmd:hidden
181
181
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,7 +1,6 @@
1
1
  # Copyright Modal Labs 2024
2
2
  import asyncio
3
3
  import platform
4
- import time
5
4
  from typing import Generic, Optional, TypeVar
6
5
 
7
6
  from modal_proto import api_pb2
@@ -12,194 +11,17 @@ from ._utils.task_command_router_client import TaskCommandRouterClient
12
11
  from .client import _Client
13
12
  from .config import logger
14
13
  from .exception import ExecTimeoutError, InteractiveTimeoutError, InvalidError
15
- from .io_streams import _StreamReader, _StreamWriter
14
+ from .io_streams import (
15
+ _StreamReader,
16
+ _StreamReaderThroughCommandRouterParams,
17
+ _StreamWriter,
18
+ _StreamWriterThroughCommandRouterParams,
19
+ )
16
20
  from .stream_type import StreamType
17
21
 
18
22
  T = TypeVar("T", str, bytes)
19
23
 
20
24
 
21
- class _ContainerProcessThroughServer(Generic[T]):
22
- _process_id: Optional[str] = None
23
- _stdout: _StreamReader[T]
24
- _stderr: _StreamReader[T]
25
- _stdin: _StreamWriter
26
- _exec_deadline: Optional[float] = None
27
- _text: bool
28
- _by_line: bool
29
- _returncode: Optional[int] = None
30
-
31
- def __init__(
32
- self,
33
- process_id: str,
34
- task_id: str,
35
- client: _Client,
36
- stdout: StreamType = StreamType.PIPE,
37
- stderr: StreamType = StreamType.PIPE,
38
- exec_deadline: Optional[float] = None,
39
- text: bool = True,
40
- by_line: bool = False,
41
- ) -> None:
42
- self._process_id = process_id
43
- self._client = client
44
- self._exec_deadline = exec_deadline
45
- self._text = text
46
- self._by_line = by_line
47
- self._stdout = _StreamReader[T](
48
- api_pb2.FILE_DESCRIPTOR_STDOUT,
49
- process_id,
50
- "container_process",
51
- self._client,
52
- stream_type=stdout,
53
- text=text,
54
- by_line=by_line,
55
- deadline=exec_deadline,
56
- task_id=task_id,
57
- )
58
- self._stderr = _StreamReader[T](
59
- api_pb2.FILE_DESCRIPTOR_STDERR,
60
- process_id,
61
- "container_process",
62
- self._client,
63
- stream_type=stderr,
64
- text=text,
65
- by_line=by_line,
66
- deadline=exec_deadline,
67
- task_id=task_id,
68
- )
69
- self._stdin = _StreamWriter(process_id, "container_process", self._client)
70
-
71
- def __repr__(self) -> str:
72
- return f"ContainerProcess(process_id={self._process_id!r})"
73
-
74
- @property
75
- def stdout(self) -> _StreamReader[T]:
76
- """StreamReader for the container process's stdout stream."""
77
- return self._stdout
78
-
79
- @property
80
- def stderr(self) -> _StreamReader[T]:
81
- """StreamReader for the container process's stderr stream."""
82
- return self._stderr
83
-
84
- @property
85
- def stdin(self) -> _StreamWriter:
86
- """StreamWriter for the container process's stdin stream."""
87
- return self._stdin
88
-
89
- @property
90
- def returncode(self) -> int:
91
- if self._returncode is None:
92
- raise InvalidError(
93
- "You must call wait() before accessing the returncode. "
94
- "To poll for the status of a running process, use poll() instead."
95
- )
96
- return self._returncode
97
-
98
- async def poll(self) -> Optional[int]:
99
- """Check if the container process has finished running.
100
-
101
- Returns `None` if the process is still running, else returns the exit code.
102
- """
103
- assert self._process_id
104
- if self._returncode is not None:
105
- return self._returncode
106
- if self._exec_deadline and time.monotonic() >= self._exec_deadline:
107
- # TODO(matt): In the future, it would be nice to raise a ContainerExecTimeoutError to make it
108
- # clear to the user that their sandbox terminated due to a timeout
109
- self._returncode = -1
110
- return self._returncode
111
-
112
- req = api_pb2.ContainerExecWaitRequest(exec_id=self._process_id, timeout=0)
113
- resp = await self._client.stub.ContainerExecWait(req)
114
-
115
- if resp.completed:
116
- self._returncode = resp.exit_code
117
- return self._returncode
118
-
119
- return None
120
-
121
- async def _wait_for_completion(self) -> int:
122
- assert self._process_id
123
- while True:
124
- req = api_pb2.ContainerExecWaitRequest(exec_id=self._process_id, timeout=10)
125
- resp = await self._client.stub.ContainerExecWait(req)
126
- if resp.completed:
127
- return resp.exit_code
128
-
129
- async def wait(self) -> int:
130
- """Wait for the container process to finish running. Returns the exit code."""
131
- if self._returncode is not None:
132
- return self._returncode
133
-
134
- try:
135
- timeout = None
136
- if self._exec_deadline:
137
- timeout = self._exec_deadline - time.monotonic()
138
- if timeout <= 0:
139
- raise TimeoutError()
140
- self._returncode = await asyncio.wait_for(self._wait_for_completion(), timeout=timeout)
141
- except (asyncio.TimeoutError, TimeoutError):
142
- self._returncode = -1
143
- logger.debug(f"ContainerProcess {self._process_id} wait completed with returncode {self._returncode}")
144
- return self._returncode
145
-
146
- async def attach(self):
147
- """mdmd:hidden"""
148
- if platform.system() == "Windows":
149
- print("interactive exec is not currently supported on Windows.") # noqa: T201
150
- return
151
-
152
- from .output import OutputManager
153
-
154
- output = OutputManager.get()
155
- connecting_status = output.status("Connecting...")
156
- connecting_status.start()
157
- on_connect = asyncio.Event()
158
-
159
- async def _write_to_fd_loop(stream: _StreamReader):
160
- # This is required to make modal shell to an existing task work,
161
- # since that uses ContainerExec RPCs directly, but this is hacky.
162
- #
163
- # TODO(saltzm): Once we use the new exec path for that use case, this code can all be removed.
164
- from .io_streams import _StreamReaderThroughServer
165
-
166
- assert isinstance(stream._impl, _StreamReaderThroughServer)
167
- stream_impl = stream._impl
168
- # Don't skip empty messages so we can detect when the process has booted.
169
- async for chunk in stream_impl._get_logs(skip_empty_messages=False):
170
- if not on_connect.is_set():
171
- connecting_status.stop()
172
- on_connect.set()
173
-
174
- await write_to_fd(stream.file_descriptor, chunk)
175
-
176
- async def _handle_input(data: bytes, message_index: int):
177
- self.stdin.write(data)
178
- await self.stdin.drain()
179
-
180
- async with TaskContext() as tc:
181
- stdout_task = tc.create_task(_write_to_fd_loop(self.stdout))
182
- stderr_task = tc.create_task(_write_to_fd_loop(self.stderr))
183
-
184
- try:
185
- # time out if we can't connect to the server fast enough
186
- await asyncio.wait_for(on_connect.wait(), timeout=60)
187
-
188
- async with stream_from_stdin(_handle_input, use_raw_terminal=True):
189
- await stdout_task
190
- await stderr_task
191
-
192
- # TODO: this doesn't work right now.
193
- # if exit_status != 0:
194
- # raise ExecutionError(f"Process exited with status code {exit_status}")
195
-
196
- except (asyncio.TimeoutError, TimeoutError):
197
- connecting_status.stop()
198
- stdout_task.cancel()
199
- stderr_task.cancel()
200
- raise InteractiveTimeoutError("Failed to establish connection to container. Please try again.")
201
-
202
-
203
25
  async def _iter_stream_as_bytes(stream: _StreamReader[T]):
204
26
  """Yield raw bytes from a StreamReader regardless of text mode/backend."""
205
27
  async for part in stream:
@@ -209,19 +31,19 @@ async def _iter_stream_as_bytes(stream: _StreamReader[T]):
209
31
  yield part
210
32
 
211
33
 
212
- class _ContainerProcessThroughCommandRouter(Generic[T]):
213
- """
214
- Container process implementation that works via direct communication with
34
+ class _ContainerProcess(Generic[T]):
35
+ """Represents a running process in a container.
36
+
37
+ Container processes communicate via direct communication with
215
38
  the Modal worker where the container is running.
216
39
  """
217
40
 
218
41
  def __init__(
219
42
  self,
220
43
  process_id: str,
44
+ task_id: str,
221
45
  client: _Client,
222
46
  command_router_client: TaskCommandRouterClient,
223
- task_id: str,
224
- *,
225
47
  stdout: StreamType = StreamType.PIPE,
226
48
  stderr: StreamType = StreamType.PIPE,
227
49
  exec_deadline: Optional[float] = None,
@@ -236,35 +58,35 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
236
58
  self._by_line = by_line
237
59
  self._task_id = task_id
238
60
  self._stdout = _StreamReader[T](
239
- api_pb2.FILE_DESCRIPTOR_STDOUT,
240
- process_id,
241
- "container_process",
242
- self._client,
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
+ ),
243
68
  stream_type=stdout,
244
69
  text=text,
245
70
  by_line=by_line,
246
- deadline=exec_deadline,
247
- command_router_client=self._command_router_client,
248
- task_id=self._task_id,
249
71
  )
250
72
  self._stderr = _StreamReader[T](
251
- api_pb2.FILE_DESCRIPTOR_STDERR,
252
- process_id,
253
- "container_process",
254
- self._client,
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
+ ),
255
80
  stream_type=stderr,
256
81
  text=text,
257
82
  by_line=by_line,
258
- deadline=exec_deadline,
259
- command_router_client=self._command_router_client,
260
- task_id=self._task_id,
261
83
  )
262
84
  self._stdin = _StreamWriter(
263
- process_id,
264
- "container_process",
265
- self._client,
266
- command_router_client=self._command_router_client,
267
- task_id=self._task_id,
85
+ _StreamWriterThroughCommandRouterParams(
86
+ task_id=self._task_id,
87
+ object_id=process_id,
88
+ command_router_client=self._command_router_client,
89
+ )
268
90
  )
269
91
  self._returncode = None
270
92
 
@@ -273,14 +95,17 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
273
95
 
274
96
  @property
275
97
  def stdout(self) -> _StreamReader[T]:
98
+ """StreamReader for the container process's stdout stream."""
276
99
  return self._stdout
277
100
 
278
101
  @property
279
102
  def stderr(self) -> _StreamReader[T]:
103
+ """StreamReader for the container process's stderr stream."""
280
104
  return self._stderr
281
105
 
282
106
  @property
283
107
  def stdin(self) -> _StreamWriter:
108
+ """StreamWriter for the container process's stdin stream."""
284
109
  return self._stdin
285
110
 
286
111
  @property
@@ -293,6 +118,10 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
293
118
  return self._returncode
294
119
 
295
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
+ """
296
125
  if self._returncode is not None:
297
126
  return self._returncode
298
127
  try:
@@ -322,6 +151,7 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
322
151
  raise
323
152
 
324
153
  async def wait(self) -> int:
154
+ """Wait for the container process to finish running. Returns the exit code."""
325
155
  if self._returncode is not None:
326
156
  return self._returncode
327
157
 
@@ -345,6 +175,7 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
345
175
  return self._returncode
346
176
 
347
177
  async def attach(self):
178
+ """mdmd:hidden"""
348
179
  if platform.system() == "Windows":
349
180
  print("interactive exec is not currently supported on Windows.") # noqa: T201
350
181
  return
@@ -390,81 +221,4 @@ class _ContainerProcessThroughCommandRouter(Generic[T]):
390
221
  raise InteractiveTimeoutError("Failed to establish connection to container. Please try again.")
391
222
 
392
223
 
393
- class _ContainerProcess(Generic[T]):
394
- """Represents a running process in a container."""
395
-
396
- def __init__(
397
- self,
398
- process_id: str,
399
- task_id: str,
400
- client: _Client,
401
- stdout: StreamType = StreamType.PIPE,
402
- stderr: StreamType = StreamType.PIPE,
403
- exec_deadline: Optional[float] = None,
404
- text: bool = True,
405
- by_line: bool = False,
406
- command_router_client: Optional[TaskCommandRouterClient] = None,
407
- ) -> None:
408
- if command_router_client is None:
409
- self._impl = _ContainerProcessThroughServer(
410
- process_id,
411
- task_id,
412
- client,
413
- stdout=stdout,
414
- stderr=stderr,
415
- exec_deadline=exec_deadline,
416
- text=text,
417
- by_line=by_line,
418
- )
419
- else:
420
- self._impl = _ContainerProcessThroughCommandRouter(
421
- process_id,
422
- client,
423
- command_router_client,
424
- task_id,
425
- stdout=stdout,
426
- stderr=stderr,
427
- exec_deadline=exec_deadline,
428
- text=text,
429
- by_line=by_line,
430
- )
431
-
432
- def __repr__(self) -> str:
433
- return self._impl.__repr__()
434
-
435
- @property
436
- def stdout(self) -> _StreamReader[T]:
437
- """StreamReader for the container process's stdout stream."""
438
- return self._impl.stdout
439
-
440
- @property
441
- def stderr(self) -> _StreamReader[T]:
442
- """StreamReader for the container process's stderr stream."""
443
- return self._impl.stderr
444
-
445
- @property
446
- def stdin(self) -> _StreamWriter:
447
- """StreamWriter for the container process's stdin stream."""
448
- return self._impl.stdin
449
-
450
- @property
451
- def returncode(self) -> int:
452
- return self._impl.returncode
453
-
454
- async def poll(self) -> Optional[int]:
455
- """Check if the container process has finished running.
456
-
457
- Returns `None` if the process is still running, else returns the exit code.
458
- """
459
- return await self._impl.poll()
460
-
461
- async def wait(self) -> int:
462
- """Wait for the container process to finish running. Returns the exit code."""
463
- return await self._impl.wait()
464
-
465
- async def attach(self):
466
- """mdmd:hidden"""
467
- await self._impl.attach()
468
-
469
-
470
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 _ContainerProcessThroughCommandRouter(typing.Generic[T]):
96
- """Container process implementation that works via direct communication with
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