modal 0.68.27__py3-none-any.whl → 0.68.28__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.
modal/client.pyi CHANGED
@@ -26,7 +26,7 @@ class _Client:
26
26
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
27
27
 
28
28
  def __init__(
29
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.27"
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.28"
30
30
  ): ...
31
31
  def is_closed(self) -> bool: ...
32
32
  @property
@@ -81,7 +81,7 @@ class Client:
81
81
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
82
82
 
83
83
  def __init__(
84
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.27"
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.28"
85
85
  ): ...
86
86
  def is_closed(self) -> bool: ...
87
87
  @property
modal/file_io.py CHANGED
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, AsyncIterator, Generic, Optional, Sequence, Ty
6
6
  if TYPE_CHECKING:
7
7
  import _typeshed
8
8
 
9
+ import json
10
+
9
11
  from grpclib.exceptions import GRPCError, StreamTerminatedError
10
12
 
11
13
  from modal._utils.grpc_utils import retry_transient_errors
@@ -267,12 +269,12 @@ class _FileIO(Generic[T]):
267
269
  output = await self._make_read_request(None)
268
270
  if self._binary:
269
271
  lines_bytes = output.split(b"\n")
270
- output = [line + b"\n" for line in lines_bytes[:-1]] + ([lines_bytes[-1]] if lines_bytes[-1] else [])
271
- return cast(Sequence[T], output)
272
+ return_bytes = [line + b"\n" for line in lines_bytes[:-1]] + ([lines_bytes[-1]] if lines_bytes[-1] else [])
273
+ return cast(Sequence[T], return_bytes)
272
274
  else:
273
275
  lines = output.decode("utf-8").split("\n")
274
- output = [line + "\n" for line in lines[:-1]] + ([lines[-1]] if lines[-1] else [])
275
- return cast(Sequence[T], output)
276
+ return_strs = [line + "\n" for line in lines[:-1]] + ([lines[-1]] if lines[-1] else [])
277
+ return cast(Sequence[T], return_strs)
276
278
 
277
279
  async def write(self, data: Union[bytes, str]) -> None:
278
280
  """Write data to the current position.
@@ -337,6 +339,52 @@ class _FileIO(Generic[T]):
337
339
  )
338
340
  await self._wait(resp.exec_id)
339
341
 
342
+ @classmethod
343
+ async def ls(cls, path: str, client: _Client, task_id: str) -> list[str]:
344
+ """List the contents of the provided directory."""
345
+ self = cls.__new__(cls)
346
+ self._client = client
347
+ self._task_id = task_id
348
+ resp = await self._make_request(
349
+ api_pb2.ContainerFilesystemExecRequest(
350
+ file_ls_request=api_pb2.ContainerFileLsRequest(path=path),
351
+ task_id=task_id,
352
+ )
353
+ )
354
+ output = await self._wait(resp.exec_id)
355
+ try:
356
+ return json.loads(output.decode("utf-8"))["paths"]
357
+ except json.JSONDecodeError:
358
+ raise FilesystemExecutionError("failed to parse list output")
359
+
360
+ @classmethod
361
+ async def mkdir(cls, path: str, client: _Client, task_id: str, parents: bool = False) -> None:
362
+ """Create a new directory."""
363
+ self = cls.__new__(cls)
364
+ self._client = client
365
+ self._task_id = task_id
366
+ resp = await self._make_request(
367
+ api_pb2.ContainerFilesystemExecRequest(
368
+ file_mkdir_request=api_pb2.ContainerFileMkdirRequest(path=path, make_parents=parents),
369
+ task_id=self._task_id,
370
+ )
371
+ )
372
+ await self._wait(resp.exec_id)
373
+
374
+ @classmethod
375
+ async def rm(cls, path: str, client: _Client, task_id: str, recursive: bool = False) -> None:
376
+ """Remove a file or directory in the Sandbox."""
377
+ self = cls.__new__(cls)
378
+ self._client = client
379
+ self._task_id = task_id
380
+ resp = await self._make_request(
381
+ api_pb2.ContainerFilesystemExecRequest(
382
+ file_rm_request=api_pb2.ContainerFileRmRequest(path=path, recursive=recursive),
383
+ task_id=self._task_id,
384
+ )
385
+ )
386
+ await self._wait(resp.exec_id)
387
+
340
388
  async def _close(self) -> None:
341
389
  # Buffer is flushed by the runner on close
342
390
  resp = await self._make_request(
modal/file_io.pyi CHANGED
@@ -43,6 +43,12 @@ class _FileIO(typing.Generic[T]):
43
43
  async def flush(self) -> None: ...
44
44
  def _get_whence(self, whence: int): ...
45
45
  async def seek(self, offset: int, whence: int = 0) -> None: ...
46
+ @classmethod
47
+ async def ls(cls, path: str, client: modal.client._Client, task_id: str) -> list[str]: ...
48
+ @classmethod
49
+ async def mkdir(cls, path: str, client: modal.client._Client, task_id: str, parents: bool = False) -> None: ...
50
+ @classmethod
51
+ async def rm(cls, path: str, client: modal.client._Client, task_id: str, recursive: bool = False) -> None: ...
46
52
  async def _close(self) -> None: ...
47
53
  async def close(self) -> None: ...
48
54
  def _check_writable(self) -> None: ...
@@ -161,6 +167,13 @@ class FileIO(typing.Generic[T]):
161
167
 
162
168
  seek: __seek_spec
163
169
 
170
+ @classmethod
171
+ def ls(cls, path: str, client: modal.client.Client, task_id: str) -> list[str]: ...
172
+ @classmethod
173
+ def mkdir(cls, path: str, client: modal.client.Client, task_id: str, parents: bool = False) -> None: ...
174
+ @classmethod
175
+ def rm(cls, path: str, client: modal.client.Client, task_id: str, recursive: bool = False) -> None: ...
176
+
164
177
  class ___close_spec(typing_extensions.Protocol):
165
178
  def __call__(self) -> None: ...
166
179
  async def aio(self) -> None: ...
modal/sandbox.py CHANGED
@@ -567,6 +567,21 @@ class _Sandbox(_Object, type_prefix="sb"):
567
567
  task_id = await self._get_task_id()
568
568
  return await _FileIO.create(path, mode, self._client, task_id)
569
569
 
570
+ async def ls(self, path: str) -> list[str]:
571
+ """List the contents of a directory in the Sandbox."""
572
+ task_id = await self._get_task_id()
573
+ return await _FileIO.ls(path, self._client, task_id)
574
+
575
+ async def mkdir(self, path: str, parents: bool = False) -> None:
576
+ """Create a new directory in the Sandbox."""
577
+ task_id = await self._get_task_id()
578
+ return await _FileIO.mkdir(path, self._client, task_id, parents)
579
+
580
+ async def rm(self, path: str, recursive: bool = False) -> None:
581
+ """Remove a file or directory in the Sandbox."""
582
+ task_id = await self._get_task_id()
583
+ return await _FileIO.rm(path, self._client, task_id, recursive)
584
+
570
585
  @property
571
586
  def stdout(self) -> _StreamReader[str]:
572
587
  """
modal/sandbox.pyi CHANGED
@@ -128,6 +128,9 @@ class _Sandbox(modal.object._Object):
128
128
  async def open(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io._FileIO[str]: ...
129
129
  @typing.overload
130
130
  async def open(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io._FileIO[bytes]: ...
131
+ async def ls(self, path: str) -> list[str]: ...
132
+ async def mkdir(self, path: str, parents: bool = False) -> None: ...
133
+ async def rm(self, path: str, recursive: bool = False) -> None: ...
131
134
  @property
132
135
  def stdout(self) -> modal.io_streams._StreamReader[str]: ...
133
136
  @property
@@ -367,6 +370,24 @@ class Sandbox(modal.object.Object):
367
370
 
368
371
  open: __open_spec
369
372
 
373
+ class __ls_spec(typing_extensions.Protocol):
374
+ def __call__(self, path: str) -> list[str]: ...
375
+ async def aio(self, path: str) -> list[str]: ...
376
+
377
+ ls: __ls_spec
378
+
379
+ class __mkdir_spec(typing_extensions.Protocol):
380
+ def __call__(self, path: str, parents: bool = False) -> None: ...
381
+ async def aio(self, path: str, parents: bool = False) -> None: ...
382
+
383
+ mkdir: __mkdir_spec
384
+
385
+ class __rm_spec(typing_extensions.Protocol):
386
+ def __call__(self, path: str, recursive: bool = False) -> None: ...
387
+ async def aio(self, path: str, recursive: bool = False) -> None: ...
388
+
389
+ rm: __rm_spec
390
+
370
391
  @property
371
392
  def stdout(self) -> modal.io_streams.StreamReader[str]: ...
372
393
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.68.27
3
+ Version: 0.68.28
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -19,7 +19,7 @@ modal/app.py,sha256=e-zwazkqvbtv0eXnIWQnfBW_v9g_6S9a8CGh1Bzr5HU,45423
19
19
  modal/app.pyi,sha256=3h538rJ0Z2opldsKLuQhDnvop05TfzNG-Uw_n9rEHa4,25197
20
20
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
21
21
  modal/client.py,sha256=JAnd4-GCN093BwkvOFAK5a6iy5ycxofjpUncMxlrIMw,15253
22
- modal/client.pyi,sha256=9YTYpc1W_gagxXekcHSXylx-k8T2kRj4dkH1mSU2fwM,7280
22
+ modal/client.pyi,sha256=TcmFzBzBi5CDGun6ZTmON8TFf7rBIS2_QCai9JTbUIA,7280
23
23
  modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
24
24
  modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
25
25
  modal/cls.py,sha256=ONnrfZ2vPcaY2JuKypPiBA9eTiyg8Qfg-Ull40nn9zs,30956
@@ -33,8 +33,8 @@ modal/environments.py,sha256=5cgA-zbm6ngKLsRA19zSOgtgo9-BarJK3FJK0BiF2Lo,6505
33
33
  modal/environments.pyi,sha256=XalNpiPkAtHWAvOU2Cotq0ozmtl-Jv0FDsR8h9mr27Q,3521
34
34
  modal/exception.py,sha256=dRK789TD1HaB63kHhu1yZuvS2vP_Vua3iLMBtA6dgqk,7128
35
35
  modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
36
- modal/file_io.py,sha256=2eoSYpMMYs-FRCQbU7joFvaObuYz6HtEtLBik1hz5Xw,14616
37
- modal/file_io.pyi,sha256=QSWGm35no2ApXiy3olLwUiT7jaNKVxIOz0rXQauCg4M,6897
36
+ modal/file_io.py,sha256=pDOFNQU5m-x-k3oJauck4fOp3bZ55Vc-_LvSaN5_Bow,16465
37
+ modal/file_io.pyi,sha256=GMhCCRyMftXYI3HqI9EdGPOx70CbCNi-VC5Sfy5TYnc,7631
38
38
  modal/file_pattern_matcher.py,sha256=vX6MjWRGdonE4I8QPdjFUnz6moBjSzvgD6417BNQrW4,4021
39
39
  modal/functions.py,sha256=IIdHw0FNOdoMksG1b2zvkn8f-xskhJu07ZvHMey9iq4,67667
40
40
  modal/functions.pyi,sha256=bHbJiWW5TbFKKjDn7bSCFvOcUcAjPFqTStS-NAHPSeM,25068
@@ -63,8 +63,8 @@ modal/retries.py,sha256=HKR2Q9aNPWkMjQ5nwobqYTuZaSuw0a8lI2zrtY5IW98,5230
63
63
  modal/runner.py,sha256=Q02VdfLCO7YKpnOSqqh58XL3hR2XHaDeiJVYW3MKz_8,24580
64
64
  modal/runner.pyi,sha256=BvMS1ZVzWSn8B8q0KnIZOJKPkN5L-i5b-USbV6SWWHQ,5177
65
65
  modal/running_app.py,sha256=CshNvGDJtagOdKW54uYjY8HY73j2TpnsL9jkPFZAsfA,560
66
- modal/sandbox.py,sha256=pPHRLcShPoexUaMJUJecsH6YJJVwQtAWc8ouxH3i4y4,27344
67
- modal/sandbox.pyi,sha256=QPNuiTLNoKwYf8JK_fmfUBXpdGYlukyaksFV1DpCd2g,18987
66
+ modal/sandbox.py,sha256=yoiBbUzUGnCnDauoMkHLNjzsyHenhM1NFk0poXq_Wv4,28043
67
+ modal/sandbox.pyi,sha256=k8_vHjN3oigxSCF13Cm2HfcSHuliGuSb8ryd3CGqwoA,19815
68
68
  modal/schedule.py,sha256=0ZFpKs1bOxeo5n3HZjoL7OE2ktsb-_oGtq-WJEPO4tY,2615
69
69
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
70
70
  modal/secret.py,sha256=Y1WgybQIkfkxdzH9CQ1h-Wd1DJJpzipigMhyyvSxTww,10007
@@ -163,10 +163,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
163
163
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
164
164
  modal_version/__init__.py,sha256=RT6zPoOdFO99u5Wcxxaoir4ZCuPTbQ22cvzFAXl3vUY,470
165
165
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
166
- modal_version/_version_generated.py,sha256=-fthnpx3dlNaui7c3f7RAZuXLNwCnL-H9fI359j-_3Y,149
167
- modal-0.68.27.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
168
- modal-0.68.27.dist-info/METADATA,sha256=jSpBvGP2tKY45IX_6MISrLQxLm1QI8OT8DRPVY5vzlQ,2329
169
- modal-0.68.27.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
170
- modal-0.68.27.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
171
- modal-0.68.27.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
172
- modal-0.68.27.dist-info/RECORD,,
166
+ modal_version/_version_generated.py,sha256=rCrKUIX9RlDkBXIhxZbfOSQ7GhlTnQx5fyFV4xz8l2M,149
167
+ modal-0.68.28.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
168
+ modal-0.68.28.dist-info/METADATA,sha256=4XYesHFd9NjVz8TZumQ1NyHdDGeO2Wphy-G_cWttZcI,2329
169
+ modal-0.68.28.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
170
+ modal-0.68.28.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
171
+ modal-0.68.28.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
172
+ modal-0.68.28.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2024
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 27 # git: 8342982
4
+ build_number = 28 # git: 3c875f8