modAL 1.2.7.dev9__py3-none-any.whl → 1.2.7.dev10__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.
@@ -43,7 +43,6 @@ from ._runtime.container_io_manager import (
43
43
  )
44
44
 
45
45
  if TYPE_CHECKING:
46
- import modal._object
47
46
  import modal._runtime.container_io_manager
48
47
  import modal._runtime.user_code_imports
49
48
 
@@ -16,8 +16,6 @@ from typing import Any, Optional, Sequence, TypeVar
16
16
  import grpclib.client
17
17
  import grpclib.config
18
18
  import grpclib.events
19
- import grpclib.protocol
20
- import grpclib.stream
21
19
  from google.protobuf.message import Message
22
20
  from google.protobuf.symbol_database import SymbolDatabase
23
21
  from grpclib import GRPCError, Status
@@ -1,5 +1,4 @@
1
1
  # Copyright Modal Labs 2022
2
- import importlib
3
2
  import importlib.util
4
3
  import typing
5
4
  from importlib.metadata import PackageNotFoundError, files
@@ -521,3 +521,17 @@ class TaskCommandRouterClient:
521
521
  await sleep_and_update_delay_and_num_retries_remaining(e)
522
522
  else:
523
523
  raise ConnectionError(str(e))
524
+
525
+ async def mount_image(self, request: sr_pb2.TaskMountDirectoryRequest):
526
+ with grpc_error_converter():
527
+ return await call_with_retries_on_transient_errors(
528
+ lambda: self._call_with_auth_retry(self._stub.TaskMountDirectory, request)
529
+ )
530
+
531
+ async def snapshot_directory(
532
+ self, request: sr_pb2.TaskSnapshotDirectoryRequest
533
+ ) -> sr_pb2.TaskSnapshotDirectoryResponse:
534
+ with grpc_error_converter():
535
+ return await call_with_retries_on_transient_errors(
536
+ lambda: self._call_with_auth_retry(self._stub.TaskSnapshotDirectory, request)
537
+ )
modal/client.pyi CHANGED
@@ -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.dev9",
35
+ version: str = "1.2.7.dev10",
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.dev9",
166
+ version: str = "1.2.7.dev10",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
modal/image.py CHANGED
@@ -874,6 +874,7 @@ class _Image(_Object, type_prefix="im"):
874
874
 
875
875
  rep = f"Image.from_id({image_id!r})"
876
876
  obj = _Image._from_loader(_load, rep, load_context_overrides=LoadContext(client=client))
877
+ obj._object_id = image_id
877
878
 
878
879
  return obj
879
880
 
modal/sandbox.py CHANGED
@@ -6,6 +6,7 @@ import time
6
6
  import uuid
7
7
  from collections.abc import AsyncGenerator, Collection, Sequence
8
8
  from dataclasses import dataclass
9
+ from pathlib import PurePosixPath
9
10
  from typing import TYPE_CHECKING, Any, AsyncIterator, Literal, Optional, Union, overload
10
11
 
11
12
  from ._pty import get_pty_info
@@ -600,6 +601,49 @@ class _Sandbox(_Object, type_prefix="sb"):
600
601
 
601
602
  return image
602
603
 
604
+ async def _experimental_mount_image(self, path: Union[PurePosixPath, str], image: Optional[_Image]):
605
+ """Mount an Image at a path in the Sandbox filesystem."""
606
+
607
+ image_id = None
608
+
609
+ if image:
610
+ if not image._object_id:
611
+ # FIXME
612
+ raise InvalidError("Image has not been built.")
613
+ image_id = image._object_id
614
+ else:
615
+ image_id = "" # empty string indicates mount an empty dir
616
+
617
+ task_id = await self._get_task_id()
618
+ if (command_router_client := await self._get_command_router_client(task_id)) is None:
619
+ raise InvalidError("Mounting directories requires direct Sandbox control - please contact Modal support.")
620
+
621
+ posix_path = PurePosixPath(path)
622
+ if not posix_path.is_absolute():
623
+ raise InvalidError(f"Mount path must be absolute; got: {posix_path}")
624
+ path_bytes = posix_path.as_posix().encode("utf8")
625
+
626
+ req = sr_pb2.TaskMountDirectoryRequest(task_id=task_id, path=path_bytes, image_id=image_id)
627
+ await command_router_client.mount_image(req)
628
+
629
+ async def _experimental_snapshot_directory(self, path: Union[PurePosixPath, str]) -> _Image:
630
+ """Snapshot local changes to a previously mounted Image, creating a new Image."""
631
+
632
+ task_id = await self._get_task_id()
633
+ if (command_router_client := await self._get_command_router_client(task_id)) is None:
634
+ raise InvalidError(
635
+ "Snapshotting directories requires direct Sandbox control - please contact Modal support."
636
+ )
637
+
638
+ posix_path = PurePosixPath(path)
639
+ if not posix_path.is_absolute():
640
+ raise InvalidError(f"Snapshot path must be absolute; got: {posix_path}")
641
+ path_bytes = posix_path.as_posix().encode("utf8")
642
+
643
+ req = sr_pb2.TaskSnapshotDirectoryRequest(task_id=task_id, path=path_bytes)
644
+ res = await command_router_client.snapshot_directory(req)
645
+ return _Image._new_hydrated(res.image_id, self._client, None)
646
+
603
647
  # Live handle methods
604
648
 
605
649
  async def wait(self, raise_on_termination: bool = True):
modal/sandbox.pyi CHANGED
@@ -22,6 +22,7 @@ import modal.stream_type
22
22
  import modal.volume
23
23
  import modal_proto.api_pb2
24
24
  import os
25
+ import pathlib
25
26
  import typing
26
27
  import typing_extensions
27
28
 
@@ -254,6 +255,18 @@ class _Sandbox(modal._object._Object):
254
255
  """
255
256
  ...
256
257
 
258
+ async def _experimental_mount_image(
259
+ self, path: typing.Union[pathlib.PurePosixPath, str], image: typing.Optional[modal.image._Image]
260
+ ):
261
+ """Mount an Image at a path in the Sandbox filesystem."""
262
+ ...
263
+
264
+ async def _experimental_snapshot_directory(
265
+ self, path: typing.Union[pathlib.PurePosixPath, str]
266
+ ) -> modal.image._Image:
267
+ """Snapshot local changes to a previously mounted Image, creating a new Image."""
268
+ ...
269
+
257
270
  async def wait(self, raise_on_termination: bool = True):
258
271
  """Wait for the Sandbox to finish running."""
259
272
  ...
@@ -820,6 +833,32 @@ class Sandbox(modal.object.Object):
820
833
 
821
834
  snapshot_filesystem: __snapshot_filesystem_spec[typing_extensions.Self]
822
835
 
836
+ class ___experimental_mount_image_spec(typing_extensions.Protocol[SUPERSELF]):
837
+ def __call__(
838
+ self, /, path: typing.Union[pathlib.PurePosixPath, str], image: typing.Optional[modal.image.Image]
839
+ ):
840
+ """Mount an Image at a path in the Sandbox filesystem."""
841
+ ...
842
+
843
+ async def aio(
844
+ self, /, path: typing.Union[pathlib.PurePosixPath, str], image: typing.Optional[modal.image.Image]
845
+ ):
846
+ """Mount an Image at a path in the Sandbox filesystem."""
847
+ ...
848
+
849
+ _experimental_mount_image: ___experimental_mount_image_spec[typing_extensions.Self]
850
+
851
+ class ___experimental_snapshot_directory_spec(typing_extensions.Protocol[SUPERSELF]):
852
+ def __call__(self, /, path: typing.Union[pathlib.PurePosixPath, str]) -> modal.image.Image:
853
+ """Snapshot local changes to a previously mounted Image, creating a new Image."""
854
+ ...
855
+
856
+ async def aio(self, /, path: typing.Union[pathlib.PurePosixPath, str]) -> modal.image.Image:
857
+ """Snapshot local changes to a previously mounted Image, creating a new Image."""
858
+ ...
859
+
860
+ _experimental_snapshot_directory: ___experimental_snapshot_directory_spec[typing_extensions.Self]
861
+
823
862
  class __wait_spec(typing_extensions.Protocol[SUPERSELF]):
824
863
  def __call__(self, /, raise_on_termination: bool = True):
825
864
  """Wait for the Sandbox to finish running."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.7.dev9
3
+ Version: 1.2.7.dev10
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -3,7 +3,7 @@ modal/__main__.py,sha256=UTyagD6NHFl3hdmKVr4yXGGB5_Gzj3NjrpuTIwJ25Tw,1597
3
3
  modal/_billing.py,sha256=C1jUN9f_1WqozSZAt9EOk1nImXUdiLrgFeeAu3R23cI,3012
4
4
  modal/_clustered_functions.py,sha256=7amiOxErtlciWeBJBL2KvaxerUomJpTAJ3qElflFHQA,2884
5
5
  modal/_clustered_functions.pyi,sha256=JmYwAGOLEnD5AF-gYF9O5tu-SgGjeoJz-X1j48b1Ijg,1157
6
- modal/_container_entrypoint.py,sha256=aRuWG1xl3kDy28CfQrqQ-LU8liX2W_bwm2avVOE63Tw,19812
6
+ modal/_container_entrypoint.py,sha256=aatoboeM3fe9w7DVdCgVOhW4iW9EwduZwiVS3ri0HgA,19787
7
7
  modal/_functions.py,sha256=VqhEE8cctHkonWvtdXOCYnW06TvXPTV1ONeIMoR1SFA,91262
8
8
  modal/_grpc_client.py,sha256=9Yv_aidXWrvcbIYWNpRXUPqEHMvNsYRCSvRrGcVbtPc,6928
9
9
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
@@ -26,7 +26,7 @@ modal/app.pyi,sha256=rL-UAzRkwoQp-PA96fQAS4YfmjWBDyfasziCMq6NrHI,51015
26
26
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
27
27
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
28
  modal/client.py,sha256=k1CqQ_0t4-vHylkqYZgYCnClg-FT0QrTPAKivL01A_k,14354
29
- modal/client.pyi,sha256=vBmss5dgofpPZ-_mCYAZRaMenwB4uTsrogebAm7pQ0Y,13146
29
+ modal/client.pyi,sha256=aHO24dFfbr5m-aTqFZ-8VLgciKD9XRF1_VH_0Gno1rk,13148
30
30
  modal/cloud_bucket_mount.py,sha256=cvVj5Rt4nDHoQbTneoJPTaAcnl_Ab_mrsjnnzd0v9OI,6013
31
31
  modal/cloud_bucket_mount.pyi,sha256=vXusGMOUk9HjCs4lhxteuCK4xLQclPfugGWKo7LECEA,7539
32
32
  modal/cls.py,sha256=PIGe4ED28jcxC2HeKtMD3odMIb4qNmlRt3tj1dZstRo,41738
@@ -45,7 +45,7 @@ modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE
45
45
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
46
46
  modal/functions.pyi,sha256=CeSVZcrjsjh8Gqo5PK7QkbpfSlrKvnFzm3HLYqGEePw,38107
47
47
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
48
- modal/image.py,sha256=Pm26DwuD7RqfrxscAHxhoA9LGJYTt_k0wcsHRqbwnzE,107945
48
+ modal/image.py,sha256=_NsWCNzxCxgKko-1bYSwDpy9w8Jnj5Qxo5grNe6WdAY,107979
49
49
  modal/image.pyi,sha256=grkqK9BfyKDXmJYNme-lGa0O3u7pOU5GifyLOAhG3sc,78202
50
50
  modal/io_streams.py,sha256=jhBdJH080kPkNdkYyvtxJumcJt2nEDozsKDF5I0o3hM,30818
51
51
  modal/io_streams.pyi,sha256=GGRPHumOaTwr11GzmZPf4_aSXGGKsMvB6NoSGHp_F7Q,18633
@@ -69,8 +69,8 @@ modal/retries.py,sha256=IvNLDM0f_GLUDD5VgEDoN09C88yoxSrCquinAuxT1Sc,5205
69
69
  modal/runner.py,sha256=T-QUi4IykPxCu6TqzJtBdqQvwiIuysstaAL01SEQ9tA,25592
70
70
  modal/runner.pyi,sha256=HYBi04nUrZN9-L-cTlvDDHFiJS-N6M3usKk1_QzGnpc,8736
71
71
  modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
72
- modal/sandbox.py,sha256=IHiNLWYYryjO3kvwBIqwCD8bqdShsayKyiPeBB2C0g4,48715
73
- modal/sandbox.pyi,sha256=nIA8TrSQAM8IJemkLOROjLJ5vGg7bj-g0Cwm7RcZiEg,56475
72
+ modal/sandbox.py,sha256=i6ZgkxmmonuvBE-A-PCSjyO_EZrNAsOgLc39gcbb14k,50777
73
+ modal/sandbox.pyi,sha256=oFarWaYY39ZMX-vXpRJJIOPiAhhHB6EUyTpDp2mt19o,58214
74
74
  modal/schedule.py,sha256=ng0g0AqNY5GQI9KhkXZQ5Wam5G42glbkqVQsNpBtbDE,3078
75
75
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
76
76
  modal/secret.py,sha256=NrsC2sA4kkep1pI2GIS5mD0ah_zpu9Mvcw2rcHNCzlM,18178
@@ -105,18 +105,18 @@ modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,
105
105
  modal/_utils/function_utils.py,sha256=kBMS4tGarKSKomp4BoxnmaNQ3nu72nodZ05Puqq5gUs,28361
106
106
  modal/_utils/git_utils.py,sha256=qtUU6JAttF55ZxYq51y55OR58B0tDPZsZWK5dJe6W5g,3182
107
107
  modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
108
- modal/_utils/grpc_utils.py,sha256=Cs8KRV682_R6W25cbz-e5R4LRJ4WA5OR9Plj8qIh3x4,16325
108
+ modal/_utils/grpc_utils.py,sha256=2Q88G0u22q4yo91Ud1SfxPF9YoR7C-YDU2e__KTMNp8,16279
109
109
  modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
110
110
  modal/_utils/http_utils.py,sha256=yeTFsXYr0rYMEhB7vBP7audG9Uc7OLhzKBANFDZWVt0,2451
111
111
  modal/_utils/jwt_utils.py,sha256=fxH9plyrbAemTbjSsQtzIdDXE9QXxvMC4DiUZ16G0aA,1360
112
112
  modal/_utils/logger.py,sha256=NgbMKFT9chYYt_TU01DdIior5ByYr2gZtrWIk1SFRLc,1782
113
113
  modal/_utils/mount_utils.py,sha256=3v6wbSvEjOhIlnLGbyXKOzdoXaG-FwNAZ_Rr0io_v2o,4113
114
114
  modal/_utils/name_utils.py,sha256=CIQL0Y4z47tq0rfNL2Kdo5dxsGrxoN2hR9Ko0HapeE4,2517
115
- modal/_utils/package_utils.py,sha256=LcL2olGN4xaUzu2Tbv-C-Ft9Qp6bsLxEfETOAVd-mjU,2073
115
+ modal/_utils/package_utils.py,sha256=jYE7WFVzYWIfO0ZJhno_BYHb6Skp48uupOiw1a3yWzw,2056
116
116
  modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
117
117
  modal/_utils/rand_pb_testing.py,sha256=mmVPk1rZldHwHZx0DnHTuHQlRLAiiAYdxjwEJpxvT9c,3900
118
118
  modal/_utils/shell_utils.py,sha256=hWHzv730Br2Xyj6cGPiMZ-198Z3RZuOu3pDXhFSZ22c,2157
119
- modal/_utils/task_command_router_client.py,sha256=amgcxvgJoVC0zkwyOoa3kQg6UXxdt3DlcnbxZRIU6iQ,23328
119
+ modal/_utils/task_command_router_client.py,sha256=Ua1WoNEH0Tdi1skLFZ8dJ5I2aq95zaup9MLlL0hsIdw,23961
120
120
  modal/_utils/time_utils.py,sha256=43tpFVwT7ykOjlETIFLVt9auMsRZqYYRYBEKxGCrRSA,1212
121
121
  modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
122
122
  modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,21878
@@ -158,7 +158,7 @@ modal/experimental/__init__.py,sha256=GOuo3t4-Le0ZVddiHIOw0MQxkdTJge-bVbhDvgnblm
158
158
  modal/experimental/flash.py,sha256=GJZwSr-4OHlP6gCIZdcbsSb8D8WX9xJwMphjwcfsRoU,31262
159
159
  modal/experimental/flash.pyi,sha256=q4a3mdgL0jzATPL2tS7LpuyulYzF1vUSE453S9xc8n8,16965
160
160
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
161
- modal-1.2.7.dev9.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
161
+ modal-1.2.7.dev10.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
162
162
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
163
163
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
164
164
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -180,10 +180,10 @@ modal_proto/task_command_router_pb2.py,sha256=2Y8wRvnq4_7RiQ-5ZAPIgXaxrHv_MXzgS5
180
180
  modal_proto/task_command_router_pb2.pyi,sha256=yI4tFteErLlonFA2HyaWtONfhY_VGSe7KCWFIspvmkE,16323
181
181
  modal_proto/task_command_router_pb2_grpc.py,sha256=q2VWfK2-4uDcPaPLhAqPZkgxhuQRYnWmwUcnEbPbGuk,13895
182
182
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=e1MM10VGUgDkm041v53QeTglGQPzTpA0FUsFtezjp74,4536
183
- modal_version/__init__.py,sha256=Nj3_Nk88t3yPscVokW4lp7tcogFqwHgva5YAz4Zww3Y,120
183
+ modal_version/__init__.py,sha256=zyKrlYMjpwrwiya16pRb-7K3NCD1dIssFEmawq1m2aI,121
184
184
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
185
- modal-1.2.7.dev9.dist-info/METADATA,sha256=R8W0pUmCnIhbGcyymb9v_tlZiuQiUX--xbI-f39wpq0,2491
186
- modal-1.2.7.dev9.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
187
- modal-1.2.7.dev9.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
188
- modal-1.2.7.dev9.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
189
- modal-1.2.7.dev9.dist-info/RECORD,,
185
+ modal-1.2.7.dev10.dist-info/METADATA,sha256=Frl579v54TJjA_50lpnE7SAv75E-o6CCuLSjU3V2ibM,2492
186
+ modal-1.2.7.dev10.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
187
+ modal-1.2.7.dev10.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
188
+ modal-1.2.7.dev10.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
189
+ modal-1.2.7.dev10.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.7.dev9"
4
+ __version__ = "1.2.7.dev10"