modal 1.2.1.dev12__py3-none-any.whl → 1.2.1.dev13__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/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.2.1.dev12",
36
+ version: str = "1.2.1.dev13",
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.2.1.dev12",
167
+ version: str = "1.2.1.dev13",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
modal/io_streams.py CHANGED
@@ -605,7 +605,7 @@ class _StreamReader(Generic[T]):
605
605
  MAX_BUFFER_SIZE = 2 * 1024 * 1024
606
606
 
607
607
 
608
- class _StreamWriter:
608
+ class _StreamWriterThroughServer:
609
609
  """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
610
610
 
611
611
  def __init__(self, object_id: str, object_type: Literal["sandbox", "container_process"], client: _Client) -> None:
@@ -627,25 +627,6 @@ class _StreamWriter:
627
627
 
628
628
  This is non-blocking and queues the data to an internal buffer. Must be
629
629
  used along with the `drain()` method, which flushes the buffer.
630
-
631
- **Usage**
632
-
633
- ```python fixture:running_app
634
- from modal import Sandbox
635
-
636
- sandbox = Sandbox.create(
637
- "bash",
638
- "-c",
639
- "while read line; do echo $line; done",
640
- app=running_app,
641
- )
642
- sandbox.stdin.write(b"foo\\n")
643
- sandbox.stdin.write(b"bar\\n")
644
- sandbox.stdin.write_eof()
645
-
646
- sandbox.stdin.drain()
647
- sandbox.wait()
648
- ```
649
630
  """
650
631
  if self._is_closed:
651
632
  raise ValueError("Stdin is closed. Cannot write to it.")
@@ -653,7 +634,7 @@ class _StreamWriter:
653
634
  if isinstance(data, str):
654
635
  data = data.encode("utf-8")
655
636
  if len(self._buffer) + len(data) > MAX_BUFFER_SIZE:
656
- raise BufferError("Buffer size exceed limit. Call drain to clear the buffer.")
637
+ raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.")
657
638
  self._buffer.extend(data)
658
639
  else:
659
640
  raise TypeError(f"data argument must be a bytes-like object, not {type(data).__name__}")
@@ -672,19 +653,6 @@ class _StreamWriter:
672
653
 
673
654
  This is a flow control method that blocks until data is sent. It returns
674
655
  when it is appropriate to continue writing data to the stream.
675
-
676
- **Usage**
677
-
678
- ```python notest
679
- writer.write(data)
680
- writer.drain()
681
- ```
682
-
683
- Async usage:
684
- ```python notest
685
- writer.write(data) # not a blocking operation
686
- await writer.drain.aio()
687
- ```
688
656
  """
689
657
  data = bytes(self._buffer)
690
658
  self._buffer.clear()
@@ -713,5 +681,127 @@ class _StreamWriter:
713
681
  raise exc
714
682
 
715
683
 
684
+ class _StreamWriterThroughCommandRouter:
685
+ def __init__(
686
+ self,
687
+ object_id: str,
688
+ command_router_client: TaskCommandRouterClient,
689
+ task_id: str,
690
+ ) -> None:
691
+ self._object_id = object_id
692
+ self._command_router_client = command_router_client
693
+ self._task_id = task_id
694
+ self._is_closed = False
695
+ self._buffer = bytearray()
696
+ self._offset = 0
697
+
698
+ def write(self, data: Union[bytes, bytearray, memoryview, str]) -> None:
699
+ if self._is_closed:
700
+ raise ValueError("Stdin is closed. Cannot write to it.")
701
+ if isinstance(data, (bytes, bytearray, memoryview, str)):
702
+ if isinstance(data, str):
703
+ data = data.encode("utf-8")
704
+ if len(self._buffer) + len(data) > MAX_BUFFER_SIZE:
705
+ raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.")
706
+ self._buffer.extend(data)
707
+ else:
708
+ raise TypeError(f"data argument must be a bytes-like object, not {type(data).__name__}")
709
+
710
+ def write_eof(self) -> None:
711
+ self._is_closed = True
712
+
713
+ async def drain(self) -> None:
714
+ eof = self._is_closed
715
+ # NB: There's no need to prevent writing eof twice, because the command router will ignore the second EOF.
716
+ if self._buffer or eof:
717
+ data = bytes(self._buffer)
718
+ await self._command_router_client.exec_stdin_write(
719
+ task_id=self._task_id, exec_id=self._object_id, offset=self._offset, data=data, eof=eof
720
+ )
721
+ # Only clear the buffer after writing the data to the command router is successful.
722
+ # This allows the client to retry drain() in the event of an exception (though
723
+ # exec_stdin_write already retries on transient errors, so most users will probably
724
+ # not do this).
725
+ self._buffer.clear()
726
+ self._offset += len(data)
727
+
728
+
729
+ class _StreamWriter:
730
+ """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
731
+
732
+ def __init__(
733
+ self,
734
+ object_id: str,
735
+ object_type: Literal["sandbox", "container_process"],
736
+ client: _Client,
737
+ command_router_client: Optional[TaskCommandRouterClient] = None,
738
+ task_id: Optional[str] = None,
739
+ ) -> None:
740
+ """mdmd:hidden"""
741
+ if command_router_client is None:
742
+ self._impl = _StreamWriterThroughServer(object_id, object_type, client)
743
+ else:
744
+ assert task_id is not None
745
+ assert object_type == "container_process"
746
+ self._impl = _StreamWriterThroughCommandRouter(object_id, command_router_client, task_id=task_id)
747
+
748
+ def write(self, data: Union[bytes, bytearray, memoryview, str]) -> None:
749
+ """Write data to the stream but does not send it immediately.
750
+
751
+ This is non-blocking and queues the data to an internal buffer. Must be
752
+ used along with the `drain()` method, which flushes the buffer.
753
+
754
+ **Usage**
755
+
756
+ ```python fixture:running_app
757
+ from modal import Sandbox
758
+
759
+ sandbox = Sandbox.create(
760
+ "bash",
761
+ "-c",
762
+ "while read line; do echo $line; done",
763
+ app=running_app,
764
+ )
765
+ sandbox.stdin.write(b"foo\\n")
766
+ sandbox.stdin.write(b"bar\\n")
767
+ sandbox.stdin.write_eof()
768
+
769
+ sandbox.stdin.drain()
770
+ sandbox.wait()
771
+ ```
772
+ """
773
+ self._impl.write(data)
774
+
775
+ def write_eof(self) -> None:
776
+ """Close the write end of the stream after the buffered data is drained.
777
+
778
+ If the process was blocked on input, it will become unblocked after
779
+ `write_eof()`. This method needs to be used along with the `drain()`
780
+ method, which flushes the EOF to the process.
781
+ """
782
+ self._impl.write_eof()
783
+
784
+ async def drain(self) -> None:
785
+ """Flush the write buffer and send data to the running process.
786
+
787
+ This is a flow control method that blocks until data is sent. It returns
788
+ when it is appropriate to continue writing data to the stream.
789
+
790
+ **Usage**
791
+
792
+ ```python notest
793
+ writer.write(data)
794
+ writer.drain()
795
+ ```
796
+
797
+ Async usage:
798
+ ```python notest
799
+ writer.write(data) # not a blocking operation
800
+ await writer.drain.aio()
801
+ ```
802
+ """
803
+ await self._impl.drain()
804
+
805
+
716
806
  StreamReader = synchronize_api(_StreamReader)
717
807
  StreamWriter = synchronize_api(_StreamWriter)
modal/io_streams.pyi CHANGED
@@ -249,7 +249,7 @@ class _StreamReader(typing.Generic[T]):
249
249
  """mdmd:hidden"""
250
250
  ...
251
251
 
252
- class _StreamWriter:
252
+ class _StreamWriterThroughServer:
253
253
  """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
254
254
  def __init__(
255
255
  self, object_id: str, object_type: typing.Literal["sandbox", "container_process"], client: modal.client._Client
@@ -258,6 +258,58 @@ class _StreamWriter:
258
258
  ...
259
259
 
260
260
  def _get_next_index(self) -> int: ...
261
+ def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None:
262
+ """Write data to the stream but does not send it immediately.
263
+
264
+ This is non-blocking and queues the data to an internal buffer. Must be
265
+ used along with the `drain()` method, which flushes the buffer.
266
+ """
267
+ ...
268
+
269
+ def write_eof(self) -> None:
270
+ """Close the write end of the stream after the buffered data is drained.
271
+
272
+ If the process was blocked on input, it will become unblocked after
273
+ `write_eof()`. This method needs to be used along with the `drain()`
274
+ method, which flushes the EOF to the process.
275
+ """
276
+ ...
277
+
278
+ async def drain(self) -> None:
279
+ """Flush the write buffer and send data to the running process.
280
+
281
+ This is a flow control method that blocks until data is sent. It returns
282
+ when it is appropriate to continue writing data to the stream.
283
+ """
284
+ ...
285
+
286
+ class _StreamWriterThroughCommandRouter:
287
+ def __init__(
288
+ self,
289
+ object_id: str,
290
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
291
+ task_id: str,
292
+ ) -> None:
293
+ """Initialize self. See help(type(self)) for accurate signature."""
294
+ ...
295
+
296
+ def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None: ...
297
+ def write_eof(self) -> None: ...
298
+ async def drain(self) -> None: ...
299
+
300
+ class _StreamWriter:
301
+ """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
302
+ def __init__(
303
+ self,
304
+ object_id: str,
305
+ object_type: typing.Literal["sandbox", "container_process"],
306
+ client: modal.client._Client,
307
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
308
+ task_id: typing.Optional[str] = None,
309
+ ) -> None:
310
+ """mdmd:hidden"""
311
+ ...
312
+
261
313
  def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None:
262
314
  """Write data to the stream but does not send it immediately.
263
315
 
@@ -423,12 +475,16 @@ class StreamReader(typing.Generic[T]):
423
475
  class StreamWriter:
424
476
  """Provides an interface to buffer and write logs to a sandbox or container process stream (`stdin`)."""
425
477
  def __init__(
426
- self, object_id: str, object_type: typing.Literal["sandbox", "container_process"], client: modal.client.Client
478
+ self,
479
+ object_id: str,
480
+ object_type: typing.Literal["sandbox", "container_process"],
481
+ client: modal.client.Client,
482
+ command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
483
+ task_id: typing.Optional[str] = None,
427
484
  ) -> None:
428
485
  """mdmd:hidden"""
429
486
  ...
430
487
 
431
- def _get_next_index(self) -> int: ...
432
488
  def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None:
433
489
  """Write data to the stream but does not send it immediately.
434
490
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.1.dev12
3
+ Version: 1.2.1.dev13
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -24,7 +24,7 @@ modal/app.pyi,sha256=AUV5Rp8qQrZJTP2waoKHFY7rYgsXNMYibMcCAQKuSeo,50544
24
24
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
25
25
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
26
26
  modal/client.py,sha256=kyAIVB3Ay-XKJizQ_1ufUFB__EagV0MLmHJpyYyJ7J0,18636
27
- modal/client.pyi,sha256=46-sOq2ZFWLaRP_M6tYYyxhIpAggNOg8AD2JAAzevN4,15831
27
+ modal/client.pyi,sha256=ly6S74QtNMbeJ0kqCRdVsoppgC7cgA_gFuw9Zd6H0UI,15831
28
28
  modal/cloud_bucket_mount.py,sha256=I2GRXYhOWLIz2kJZjXu75jAm9EJkBNcutGc6jR2ReUw,5928
29
29
  modal/cloud_bucket_mount.pyi,sha256=VuUOipMIHqFXMkD-3g2bsoqpSxV5qswlFHDOqPQzYAo,7405
30
30
  modal/cls.py,sha256=ZxzivE3fNci4-A5uyBYNAzXMXtdqDg3gnYvgbdy5fhg,40384
@@ -45,8 +45,8 @@ modal/functions.pyi,sha256=Z6VuukLrjASAgf0kV9I6c09WvP_b2gCujX6f9j2bBaw,37988
45
45
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
46
46
  modal/image.py,sha256=HDkOnhIAN8g63a8LTN4J5SjC9ciReFQQJIxTS2z5KFM,107216
47
47
  modal/image.pyi,sha256=dMvMwAuvWkNN2BRYJFijkEy2m_xtEXgCKK0T7FVldsc,77514
48
- modal/io_streams.py,sha256=APkECBiP1-D7xeBdFLuWKYOZq6MZFNAhVwuiO1ymcNE,25835
49
- modal/io_streams.pyi,sha256=dnuWjJrkQsMla_twPTe05NnixsYtT2gjWYjVFnyff5I,15960
48
+ modal/io_streams.py,sha256=Kv-No6WcNBouwdoogwHafOsmPOKqxTpvVGLU0mM6xMc,29564
49
+ modal/io_streams.pyi,sha256=h7qtAbj8LsN-eJKAGjBhnMBegvWprc_0AmwVFi6rj2Y,18084
50
50
  modal/mount.py,sha256=G7_xhQMZqokgfsaFLMch0YR3fs-OUNqYUm3f4jHTSMQ,33161
51
51
  modal/mount.pyi,sha256=MD_zV2M7eCWxbOpQRjU60aHevN-bmbiywaCX82QoFlw,15380
52
52
  modal/network_file_system.py,sha256=ZdEIRgdcR-p_ILyw_AecEtPOhhrSWJeADYCtFnhtaHM,13509
@@ -156,7 +156,7 @@ modal/experimental/__init__.py,sha256=9gkVuDmu3m4TlKoU3MzEtTOemUSs8EEOWba40s7Aa0
156
156
  modal/experimental/flash.py,sha256=C4sef08rARYFllsgtqukFmYL18SZW0_JpMS0BejDcUs,28552
157
157
  modal/experimental/flash.pyi,sha256=vV_OQhtdrPn8SW0XrBK-aLLHHIvxAzLzwFbWrke-m74,15463
158
158
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
159
- modal-1.2.1.dev12.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
159
+ modal-1.2.1.dev13.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
160
160
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
161
161
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
162
162
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -184,10 +184,10 @@ modal_proto/task_command_router_pb2.py,sha256=_pD2ZpU0bNzhwBdzmLoLyLtAtftI_Agxwn
184
184
  modal_proto/task_command_router_pb2.pyi,sha256=EyDgXPLr7alqjXYERV8w_MPuO404x0uCppmSkrfE9IE,14589
185
185
  modal_proto/task_command_router_pb2_grpc.py,sha256=uEQ0HdrCp8v-9bB5yIic9muA8spCShLHY6Bz9cCgOUE,10114
186
186
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=s3Yxsrawdj4nr8vqQqsAxyX6ilWaGbdECy425KKbLIA,3301
187
- modal_version/__init__.py,sha256=OYOaillNG-YG3dph7s36QFNeIzc-mCx6s7KPhjISWzY,121
187
+ modal_version/__init__.py,sha256=BvNzpPJEyoSPk632gFDNj08JAPD46g42lR-teo7dEP8,121
188
188
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
189
- modal-1.2.1.dev12.dist-info/METADATA,sha256=aGlYkorlrlPY8h1THL2Ece76Z2PspqkANMRmy_F8xQk,2484
190
- modal-1.2.1.dev12.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
191
- modal-1.2.1.dev12.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
192
- modal-1.2.1.dev12.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
193
- modal-1.2.1.dev12.dist-info/RECORD,,
189
+ modal-1.2.1.dev13.dist-info/METADATA,sha256=4JrQuKWcdeqkB350h27KFdy5O4gp4c4rk8EBdsBKSz0,2484
190
+ modal-1.2.1.dev13.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
191
+ modal-1.2.1.dev13.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
192
+ modal-1.2.1.dev13.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
193
+ modal-1.2.1.dev13.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.1.dev12"
4
+ __version__ = "1.2.1.dev13"