modal 1.1.5.dev18__py3-none-any.whl → 1.1.5.dev20__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.
- modal/_pty.py +7 -3
- modal/client.pyi +2 -2
- modal/functions.pyi +6 -6
- modal/runner.py +2 -2
- modal/sandbox.py +85 -36
- modal/sandbox.pyi +119 -15
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/METADATA +1 -1
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/RECORD +16 -16
- modal_proto/api.proto +1 -0
- modal_proto/api_pb2.py +410 -410
- modal_proto/api_pb2.pyi +4 -1
- modal_version/__init__.py +1 -1
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/WHEEL +0 -0
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/entry_points.txt +0 -0
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/licenses/LICENSE +0 -0
- {modal-1.1.5.dev18.dist-info → modal-1.1.5.dev20.dist-info}/top_level.txt +0 -0
modal/_pty.py
CHANGED
|
@@ -7,8 +7,11 @@ from typing import Optional
|
|
|
7
7
|
from modal_proto import api_pb2
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def get_winsz(fd) -> tuple[Optional[int], Optional[int]]:
|
|
10
|
+
def get_winsz(fd=None) -> tuple[Optional[int], Optional[int]]:
|
|
11
11
|
try:
|
|
12
|
+
if fd is None:
|
|
13
|
+
fd = sys.stdin.fileno()
|
|
14
|
+
|
|
12
15
|
import fcntl
|
|
13
16
|
import struct
|
|
14
17
|
import termios
|
|
@@ -40,8 +43,8 @@ def raw_terminal():
|
|
|
40
43
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
41
44
|
|
|
42
45
|
|
|
43
|
-
def get_pty_info(shell: bool) -> api_pb2.PTYInfo:
|
|
44
|
-
rows, cols = get_winsz(
|
|
46
|
+
def get_pty_info(shell: bool, no_terminate_on_idle_stdin: bool = False) -> api_pb2.PTYInfo:
|
|
47
|
+
rows, cols = get_winsz()
|
|
45
48
|
return api_pb2.PTYInfo(
|
|
46
49
|
enabled=True, # TODO(erikbern): deprecated
|
|
47
50
|
winsz_rows=rows,
|
|
@@ -50,4 +53,5 @@ def get_pty_info(shell: bool) -> api_pb2.PTYInfo:
|
|
|
50
53
|
env_colorterm=os.environ.get("COLORTERM"),
|
|
51
54
|
env_term_program=os.environ.get("TERM_PROGRAM"),
|
|
52
55
|
pty_type=api_pb2.PTYInfo.PTY_TYPE_SHELL if shell else api_pb2.PTYInfo.PTY_TYPE_FUNCTION,
|
|
56
|
+
no_terminate_on_idle_stdin=no_terminate_on_idle_stdin,
|
|
53
57
|
)
|
modal/client.pyi
CHANGED
|
@@ -33,7 +33,7 @@ class _Client:
|
|
|
33
33
|
server_url: str,
|
|
34
34
|
client_type: int,
|
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.1.5.
|
|
36
|
+
version: str = "1.1.5.dev20",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -164,7 +164,7 @@ class Client:
|
|
|
164
164
|
server_url: str,
|
|
165
165
|
client_type: int,
|
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.1.5.
|
|
167
|
+
version: str = "1.1.5.dev20",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
modal/functions.pyi
CHANGED
|
@@ -445,7 +445,7 @@ class Function(
|
|
|
445
445
|
|
|
446
446
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
|
447
447
|
|
|
448
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
448
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
449
449
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
450
450
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
451
451
|
...
|
|
@@ -454,7 +454,7 @@ class Function(
|
|
|
454
454
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
455
455
|
...
|
|
456
456
|
|
|
457
|
-
remote: __remote_spec[modal._functions.
|
|
457
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
458
458
|
|
|
459
459
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
460
460
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -481,7 +481,7 @@ class Function(
|
|
|
481
481
|
"""
|
|
482
482
|
...
|
|
483
483
|
|
|
484
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
484
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
485
485
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
486
486
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
487
487
|
|
|
@@ -505,7 +505,7 @@ class Function(
|
|
|
505
505
|
...
|
|
506
506
|
|
|
507
507
|
_experimental_spawn: ___experimental_spawn_spec[
|
|
508
|
-
modal._functions.
|
|
508
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
|
509
509
|
]
|
|
510
510
|
|
|
511
511
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
|
@@ -514,7 +514,7 @@ class Function(
|
|
|
514
514
|
|
|
515
515
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
|
516
516
|
|
|
517
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
517
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
518
518
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
519
519
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
520
520
|
|
|
@@ -535,7 +535,7 @@ class Function(
|
|
|
535
535
|
"""
|
|
536
536
|
...
|
|
537
537
|
|
|
538
|
-
spawn: __spawn_spec[modal._functions.
|
|
538
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
539
539
|
|
|
540
540
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
541
541
|
"""Return the inner Python object wrapped by this Modal Function."""
|
modal/runner.py
CHANGED
|
@@ -607,12 +607,12 @@ async def _interactive_shell(
|
|
|
607
607
|
|
|
608
608
|
try:
|
|
609
609
|
if pty:
|
|
610
|
-
container_process = await sandbox.
|
|
610
|
+
container_process = await sandbox._exec(
|
|
611
611
|
*sandbox_cmds, pty_info=get_pty_info(shell=True) if pty else None
|
|
612
612
|
)
|
|
613
613
|
await container_process.attach()
|
|
614
614
|
else:
|
|
615
|
-
container_process = await sandbox.
|
|
615
|
+
container_process = await sandbox._exec(
|
|
616
616
|
*sandbox_cmds, stdout=StreamType.STDOUT, stderr=StreamType.STDOUT
|
|
617
617
|
)
|
|
618
618
|
await container_process.wait()
|
modal/sandbox.py
CHANGED
|
@@ -7,6 +7,7 @@ from collections.abc import AsyncGenerator, Sequence
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from typing import TYPE_CHECKING, Any, AsyncIterator, Literal, Optional, Union, overload
|
|
9
9
|
|
|
10
|
+
from ._pty import get_pty_info
|
|
10
11
|
from .config import config, logger
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
@@ -124,6 +125,10 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
124
125
|
_tunnels: Optional[dict[int, Tunnel]] = None
|
|
125
126
|
_enable_snapshot: bool = False
|
|
126
127
|
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _default_pty_info() -> api_pb2.PTYInfo:
|
|
130
|
+
return get_pty_info(shell=True, no_terminate_on_idle_stdin=True)
|
|
131
|
+
|
|
127
132
|
@staticmethod
|
|
128
133
|
def _new(
|
|
129
134
|
args: Sequence[str],
|
|
@@ -143,7 +148,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
143
148
|
block_network: bool = False,
|
|
144
149
|
cidr_allowlist: Optional[Sequence[str]] = None,
|
|
145
150
|
volumes: dict[Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]] = {},
|
|
146
|
-
|
|
151
|
+
pty: bool = False,
|
|
152
|
+
pty_info: Optional[api_pb2.PTYInfo] = None, # deprecated
|
|
147
153
|
encrypted_ports: Sequence[int] = [],
|
|
148
154
|
h2_ports: Sequence[int] = [],
|
|
149
155
|
unencrypted_ports: Sequence[int] = [],
|
|
@@ -177,6 +183,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
177
183
|
cloud_bucket_mounts = [(k, v) for k, v in validated_volumes if isinstance(v, _CloudBucketMount)]
|
|
178
184
|
validated_volumes = [(k, v) for k, v in validated_volumes if isinstance(v, _Volume)]
|
|
179
185
|
|
|
186
|
+
if pty:
|
|
187
|
+
pty_info = _Sandbox._default_pty_info()
|
|
188
|
+
|
|
180
189
|
def _deps() -> list[_Object]:
|
|
181
190
|
deps: list[_Object] = [image] + list(mounts) + list(secrets)
|
|
182
191
|
for _, vol in validated_network_file_systems:
|
|
@@ -301,7 +310,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
301
310
|
volumes: dict[
|
|
302
311
|
Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]
|
|
303
312
|
] = {}, # Mount points for Modal Volumes and CloudBucketMounts
|
|
304
|
-
|
|
313
|
+
pty: bool = False, # Enable a PTY for the Sandbox
|
|
305
314
|
# List of ports to tunnel into the sandbox. Encrypted ports are tunneled with TLS.
|
|
306
315
|
encrypted_ports: Sequence[int] = [],
|
|
307
316
|
# List of encrypted ports to tunnel into the sandbox, using HTTP/2.
|
|
@@ -320,6 +329,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
320
329
|
] = None, # Experimental controls over fine-grained scheduling (alpha).
|
|
321
330
|
client: Optional[_Client] = None,
|
|
322
331
|
environment_name: Optional[str] = None, # *DEPRECATED* Optionally override the default environment
|
|
332
|
+
pty_info: Optional[api_pb2.PTYInfo] = None, # *DEPRECATED* Use `pty` instead. `pty` will override `pty_info`.
|
|
323
333
|
) -> "_Sandbox":
|
|
324
334
|
"""
|
|
325
335
|
Create a new Sandbox to run untrusted, arbitrary code.
|
|
@@ -342,6 +352,13 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
342
352
|
"A sandbox's environment is determined by the app it is associated with.",
|
|
343
353
|
)
|
|
344
354
|
|
|
355
|
+
if pty_info is not None:
|
|
356
|
+
deprecation_warning(
|
|
357
|
+
(2025, 9, 12),
|
|
358
|
+
"The `pty_info` parameter is deprecated and will be removed in a future release. "
|
|
359
|
+
"Set the `pty` parameter to `True` instead.",
|
|
360
|
+
)
|
|
361
|
+
|
|
345
362
|
return await _Sandbox._create(
|
|
346
363
|
*args,
|
|
347
364
|
app=app,
|
|
@@ -360,7 +377,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
360
377
|
block_network=block_network,
|
|
361
378
|
cidr_allowlist=cidr_allowlist,
|
|
362
379
|
volumes=volumes,
|
|
363
|
-
|
|
380
|
+
pty=pty,
|
|
364
381
|
encrypted_ports=encrypted_ports,
|
|
365
382
|
h2_ports=h2_ports,
|
|
366
383
|
unencrypted_ports=unencrypted_ports,
|
|
@@ -370,59 +387,51 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
370
387
|
_experimental_scheduler_placement=_experimental_scheduler_placement,
|
|
371
388
|
client=client,
|
|
372
389
|
verbose=verbose,
|
|
390
|
+
pty_info=pty_info,
|
|
373
391
|
)
|
|
374
392
|
|
|
375
393
|
@staticmethod
|
|
376
394
|
async def _create(
|
|
377
|
-
*args: str,
|
|
378
|
-
# Associate the sandbox with an app. Required unless creating from a container.
|
|
395
|
+
*args: str,
|
|
379
396
|
app: Optional["modal.app._App"] = None,
|
|
380
|
-
name: Optional[str] = None,
|
|
381
|
-
image: Optional[_Image] = None,
|
|
382
|
-
secrets: Sequence[_Secret] = (),
|
|
397
|
+
name: Optional[str] = None,
|
|
398
|
+
image: Optional[_Image] = None,
|
|
399
|
+
secrets: Sequence[_Secret] = (),
|
|
383
400
|
mounts: Sequence[_Mount] = (),
|
|
384
401
|
network_file_systems: dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
|
|
385
|
-
timeout: int = 300,
|
|
386
|
-
# The amount of time in seconds that a sandbox can be idle before being terminated.
|
|
402
|
+
timeout: int = 300,
|
|
387
403
|
idle_timeout: Optional[int] = None,
|
|
388
|
-
workdir: Optional[str] = None,
|
|
404
|
+
workdir: Optional[str] = None,
|
|
389
405
|
gpu: GPU_T = None,
|
|
390
406
|
cloud: Optional[str] = None,
|
|
391
|
-
region: Optional[Union[str, Sequence[str]]] = None,
|
|
392
|
-
# Specify, in fractional CPU cores, how many CPU cores to request.
|
|
393
|
-
# Or, pass (request, limit) to additionally specify a hard limit in fractional CPU cores.
|
|
394
|
-
# CPU throttling will prevent a container from exceeding its specified limit.
|
|
407
|
+
region: Optional[Union[str, Sequence[str]]] = None,
|
|
395
408
|
cpu: Optional[Union[float, tuple[float, float]]] = None,
|
|
396
|
-
# Specify, in MiB, a memory request which is the minimum memory required.
|
|
397
|
-
# Or, pass (request, limit) to additionally specify a hard limit in MiB.
|
|
398
409
|
memory: Optional[Union[int, tuple[int, int]]] = None,
|
|
399
|
-
block_network: bool = False,
|
|
400
|
-
# List of CIDRs the sandbox is allowed to access. If None, all CIDRs are allowed.
|
|
410
|
+
block_network: bool = False,
|
|
401
411
|
cidr_allowlist: Optional[Sequence[str]] = None,
|
|
402
412
|
volumes: dict[
|
|
403
413
|
Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]
|
|
404
|
-
] = {},
|
|
405
|
-
|
|
406
|
-
# List of ports to tunnel into the sandbox. Encrypted ports are tunneled with TLS.
|
|
414
|
+
] = {},
|
|
415
|
+
pty: bool = False,
|
|
407
416
|
encrypted_ports: Sequence[int] = [],
|
|
408
|
-
# List of encrypted ports to tunnel into the sandbox, using HTTP/2.
|
|
409
417
|
h2_ports: Sequence[int] = [],
|
|
410
|
-
# List of ports to tunnel into the sandbox without encryption.
|
|
411
418
|
unencrypted_ports: Sequence[int] = [],
|
|
412
|
-
# Reference to a Modal Proxy to use in front of this Sandbox.
|
|
413
419
|
proxy: Optional[_Proxy] = None,
|
|
414
420
|
experimental_options: Optional[dict[str, bool]] = None,
|
|
415
|
-
# Enable memory snapshots.
|
|
416
421
|
_experimental_enable_snapshot: bool = False,
|
|
417
422
|
_experimental_scheduler_placement: Optional[
|
|
418
423
|
SchedulerPlacement
|
|
419
|
-
] = None,
|
|
424
|
+
] = None,
|
|
420
425
|
client: Optional[_Client] = None,
|
|
421
426
|
verbose: bool = False,
|
|
427
|
+
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
422
428
|
):
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
429
|
+
"""Private method used internally.
|
|
430
|
+
|
|
431
|
+
This method exposes some internal arguments (currently `mounts`) which are not in the public API.
|
|
432
|
+
`mounts` is currently only used by modal shell (cli) to provide a function's mounts to the
|
|
433
|
+
sandbox that runs the shell session.
|
|
434
|
+
"""
|
|
426
435
|
from .app import _App
|
|
427
436
|
|
|
428
437
|
_validate_exec_args(args)
|
|
@@ -451,6 +460,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
451
460
|
block_network=block_network,
|
|
452
461
|
cidr_allowlist=cidr_allowlist,
|
|
453
462
|
volumes=volumes,
|
|
463
|
+
pty=pty,
|
|
454
464
|
pty_info=pty_info,
|
|
455
465
|
encrypted_ports=encrypted_ports,
|
|
456
466
|
h2_ports=h2_ports,
|
|
@@ -703,7 +713,6 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
703
713
|
async def exec(
|
|
704
714
|
self,
|
|
705
715
|
*args: str,
|
|
706
|
-
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
707
716
|
stdout: StreamType = StreamType.PIPE,
|
|
708
717
|
stderr: StreamType = StreamType.PIPE,
|
|
709
718
|
timeout: Optional[int] = None,
|
|
@@ -711,6 +720,8 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
711
720
|
secrets: Sequence[_Secret] = (),
|
|
712
721
|
text: Literal[True] = True,
|
|
713
722
|
bufsize: Literal[-1, 1] = -1,
|
|
723
|
+
pty: bool = False,
|
|
724
|
+
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
714
725
|
_pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
715
726
|
) -> _ContainerProcess[str]: ...
|
|
716
727
|
|
|
@@ -718,7 +729,6 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
718
729
|
async def exec(
|
|
719
730
|
self,
|
|
720
731
|
*args: str,
|
|
721
|
-
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
722
732
|
stdout: StreamType = StreamType.PIPE,
|
|
723
733
|
stderr: StreamType = StreamType.PIPE,
|
|
724
734
|
timeout: Optional[int] = None,
|
|
@@ -726,13 +736,14 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
726
736
|
secrets: Sequence[_Secret] = (),
|
|
727
737
|
text: Literal[False] = False,
|
|
728
738
|
bufsize: Literal[-1, 1] = -1,
|
|
739
|
+
pty: bool = False,
|
|
740
|
+
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
729
741
|
_pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
730
742
|
) -> _ContainerProcess[bytes]: ...
|
|
731
743
|
|
|
732
744
|
async def exec(
|
|
733
745
|
self,
|
|
734
746
|
*args: str,
|
|
735
|
-
pty_info: Optional[api_pb2.PTYInfo] = None, # Deprecated: internal use only
|
|
736
747
|
stdout: StreamType = StreamType.PIPE,
|
|
737
748
|
stderr: StreamType = StreamType.PIPE,
|
|
738
749
|
timeout: Optional[int] = None,
|
|
@@ -743,8 +754,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
743
754
|
# Control line-buffered output.
|
|
744
755
|
# -1 means unbuffered, 1 means line-buffered (only available if `text=True`).
|
|
745
756
|
bufsize: Literal[-1, 1] = -1,
|
|
746
|
-
|
|
747
|
-
_pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
757
|
+
pty: bool = False, # Enable a PTY for the command
|
|
758
|
+
_pty_info: Optional[api_pb2.PTYInfo] = None, # *DEPRECATED* Use `pty` instead. `pty` will override `pty_info`.
|
|
759
|
+
pty_info: Optional[api_pb2.PTYInfo] = None, # *DEPRECATED* Use `pty` instead. `pty` will override `pty_info`.
|
|
748
760
|
):
|
|
749
761
|
"""Execute a command in the Sandbox and return a ContainerProcess handle.
|
|
750
762
|
|
|
@@ -764,7 +776,44 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
764
776
|
print(line)
|
|
765
777
|
```
|
|
766
778
|
"""
|
|
779
|
+
if pty_info is not None or _pty_info is not None:
|
|
780
|
+
deprecation_warning(
|
|
781
|
+
(2025, 9, 12),
|
|
782
|
+
"The `_pty_info` and `pty_info` parameters are deprecated and will be removed in a future release. "
|
|
783
|
+
"Set the `pty` parameter to `True` instead.",
|
|
784
|
+
)
|
|
785
|
+
pty_info = _pty_info or pty_info
|
|
786
|
+
if pty:
|
|
787
|
+
pty_info = self._default_pty_info()
|
|
767
788
|
|
|
789
|
+
return await self._exec(
|
|
790
|
+
*args,
|
|
791
|
+
pty_info=pty_info,
|
|
792
|
+
stdout=stdout,
|
|
793
|
+
stderr=stderr,
|
|
794
|
+
timeout=timeout,
|
|
795
|
+
workdir=workdir,
|
|
796
|
+
secrets=secrets,
|
|
797
|
+
text=text,
|
|
798
|
+
bufsize=bufsize,
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
async def _exec(
|
|
802
|
+
self,
|
|
803
|
+
*args: str,
|
|
804
|
+
pty_info: Optional[api_pb2.PTYInfo] = None,
|
|
805
|
+
stdout: StreamType = StreamType.PIPE,
|
|
806
|
+
stderr: StreamType = StreamType.PIPE,
|
|
807
|
+
timeout: Optional[int] = None,
|
|
808
|
+
workdir: Optional[str] = None,
|
|
809
|
+
secrets: Sequence[_Secret] = (),
|
|
810
|
+
text: bool = True,
|
|
811
|
+
bufsize: Literal[-1, 1] = -1,
|
|
812
|
+
) -> Union[_ContainerProcess[bytes], _ContainerProcess[str]]:
|
|
813
|
+
"""Private method used internally.
|
|
814
|
+
|
|
815
|
+
This method exposes some internal arguments (currently `pty_info`) which are not in the public API.
|
|
816
|
+
"""
|
|
768
817
|
if workdir is not None and not workdir.startswith("/"):
|
|
769
818
|
raise InvalidError(f"workdir must be an absolute path, got: {workdir}")
|
|
770
819
|
_validate_exec_args(args)
|
|
@@ -777,7 +826,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
|
777
826
|
req = api_pb2.ContainerExecRequest(
|
|
778
827
|
task_id=task_id,
|
|
779
828
|
command=args,
|
|
780
|
-
pty_info=
|
|
829
|
+
pty_info=pty_info,
|
|
781
830
|
runtime_debug=config.get("function_runtime_debug"),
|
|
782
831
|
timeout_secs=timeout or 0,
|
|
783
832
|
workdir=workdir,
|