modal 1.0.5.dev2__py3-none-any.whl → 1.0.5.dev4__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.
Files changed (50) hide show
  1. modal/_clustered_functions.pyi +13 -3
  2. modal/_functions.py +5 -4
  3. modal/_partial_function.py +1 -1
  4. modal/_runtime/container_io_manager.pyi +222 -40
  5. modal/_runtime/execution_context.pyi +60 -6
  6. modal/_tunnel.pyi +380 -12
  7. modal/app.py +4 -4
  8. modal/app.pyi +658 -48
  9. modal/cli/run.py +2 -1
  10. modal/client.pyi +224 -28
  11. modal/cloud_bucket_mount.pyi +192 -4
  12. modal/cls.py +3 -3
  13. modal/cls.pyi +442 -35
  14. modal/container_process.pyi +103 -14
  15. modal/dict.py +1 -1
  16. modal/dict.pyi +453 -51
  17. modal/environments.pyi +41 -9
  18. modal/exception.py +2 -2
  19. modal/file_io.pyi +236 -45
  20. modal/functions.pyi +545 -56
  21. modal/gpu.py +1 -1
  22. modal/image.py +1 -1
  23. modal/image.pyi +1256 -74
  24. modal/io_streams.pyi +342 -39
  25. modal/mount.pyi +261 -31
  26. modal/network_file_system.pyi +307 -26
  27. modal/object.pyi +48 -9
  28. modal/parallel_map.pyi +144 -14
  29. modal/partial_function.pyi +255 -14
  30. modal/proxy.py +1 -1
  31. modal/proxy.pyi +28 -3
  32. modal/queue.py +1 -1
  33. modal/queue.pyi +447 -30
  34. modal/runner.pyi +160 -22
  35. modal/sandbox.py +7 -7
  36. modal/sandbox.pyi +310 -50
  37. modal/schedule.py +1 -1
  38. modal/secret.py +2 -2
  39. modal/secret.pyi +164 -15
  40. modal/snapshot.pyi +25 -4
  41. modal/token_flow.pyi +28 -8
  42. modal/volume.py +1 -1
  43. modal/volume.pyi +649 -59
  44. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/METADATA +1 -1
  45. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/RECORD +50 -50
  46. modal_version/__init__.py +1 -1
  47. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/WHEEL +0 -0
  48. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/entry_points.txt +0 -0
  49. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/licenses/LICENSE +0 -0
  50. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/top_level.txt +0 -0
modal/volume.pyi CHANGED
@@ -24,6 +24,8 @@ class FileEntryType(enum.IntEnum):
24
24
  SOCKET = 5
25
25
 
26
26
  class FileEntry:
27
+ """A file or directory entry listed from a Modal volume."""
28
+
27
29
  path: str
28
30
  type: FileEntryType
29
31
  mtime: int
@@ -31,14 +33,72 @@ class FileEntry:
31
33
 
32
34
  @classmethod
33
35
  def _from_proto(cls, proto: modal_proto.api_pb2.FileEntry) -> FileEntry: ...
34
- def __init__(self, path: str, type: FileEntryType, mtime: int, size: int) -> None: ...
35
- def __repr__(self): ...
36
- def __eq__(self, other): ...
37
- def __setattr__(self, name, value): ...
38
- def __delattr__(self, name): ...
39
- def __hash__(self): ...
36
+ def __init__(self, path: str, type: FileEntryType, mtime: int, size: int) -> None:
37
+ """Initialize self. See help(type(self)) for accurate signature."""
38
+ ...
39
+
40
+ def __repr__(self):
41
+ """Return repr(self)."""
42
+ ...
43
+
44
+ def __eq__(self, other):
45
+ """Return self==value."""
46
+ ...
47
+
48
+ def __setattr__(self, name, value):
49
+ """Implement setattr(self, name, value)."""
50
+ ...
51
+
52
+ def __delattr__(self, name):
53
+ """Implement delattr(self, name)."""
54
+ ...
55
+
56
+ def __hash__(self):
57
+ """Return hash(self)."""
58
+ ...
40
59
 
41
60
  class _Volume(modal._object._Object):
61
+ """A writeable volume that can be used to share files between one or more Modal functions.
62
+
63
+ The contents of a volume is exposed as a filesystem. You can use it to share data between different functions, or
64
+ to persist durable state across several instances of the same function.
65
+
66
+ Unlike a networked filesystem, you need to explicitly reload the volume to see changes made since it was mounted.
67
+ Similarly, you need to explicitly commit any changes you make to the volume for the changes to become visible
68
+ outside the current container.
69
+
70
+ Concurrent modification is supported, but concurrent modifications of the same files should be avoided! Last write
71
+ wins in case of concurrent modification of the same file - any data the last writer didn't have when committing
72
+ changes will be lost!
73
+
74
+ As a result, volumes are typically not a good fit for use cases where you need to make concurrent modifications to
75
+ the same file (nor is distributed file locking supported).
76
+
77
+ Volumes can only be reloaded if there are no open files for the volume - attempting to reload with open files
78
+ will result in an error.
79
+
80
+ **Usage**
81
+
82
+ ```python
83
+ import modal
84
+
85
+ app = modal.App()
86
+ volume = modal.Volume.from_name("my-persisted-volume", create_if_missing=True)
87
+
88
+ @app.function(volumes={"/root/foo": volume})
89
+ def f():
90
+ with open("/root/foo/bar.txt", "w") as f:
91
+ f.write("hello")
92
+ volume.commit() # Persist changes
93
+
94
+ @app.function(volumes={"/root/foo": volume})
95
+ def g():
96
+ volume.reload() # Fetch latest changes
97
+ with open("/root/foo/bar.txt", "r") as f:
98
+ print(f.read())
99
+ ```
100
+ """
101
+
42
102
  _lock: typing.Optional[asyncio.locks.Lock]
43
103
  _metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
44
104
 
@@ -51,7 +111,26 @@ class _Volume(modal._object._Object):
51
111
  environment_name: typing.Optional[str] = None,
52
112
  create_if_missing: bool = False,
53
113
  version: typing.Optional[int] = None,
54
- ) -> _Volume: ...
114
+ ) -> _Volume:
115
+ """Reference a Volume by name, creating if necessary.
116
+
117
+ In contrast to `modal.Volume.lookup`, this is a lazy method
118
+ that defers hydrating the local object with metadata from
119
+ Modal servers until the first time is is actually used.
120
+
121
+ ```python
122
+ vol = modal.Volume.from_name("my-volume", create_if_missing=True)
123
+
124
+ app = modal.App()
125
+
126
+ # Volume refers to the same object, even across instances of `app`.
127
+ @app.function(volumes={"/data": vol})
128
+ def f():
129
+ pass
130
+ ```
131
+ """
132
+ ...
133
+
55
134
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
56
135
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
57
136
  @property
@@ -63,7 +142,23 @@ class _Volume(modal._object._Object):
63
142
  environment_name: typing.Optional[str] = None,
64
143
  version: typing.Optional[int] = None,
65
144
  _heartbeat_sleep: float = 300,
66
- ) -> typing.AsyncContextManager[_Volume]: ...
145
+ ) -> typing.AsyncContextManager[_Volume]:
146
+ """Creates a new ephemeral volume within a context manager:
147
+
148
+ Usage:
149
+ ```python
150
+ import modal
151
+ with modal.Volume.ephemeral() as vol:
152
+ assert vol.listdir("/") == []
153
+ ```
154
+
155
+ ```python notest
156
+ async with modal.Volume.ephemeral() as vol:
157
+ assert await vol.listdir("/") == []
158
+ ```
159
+ """
160
+ ...
161
+
67
162
  @staticmethod
68
163
  async def lookup(
69
164
  name: str,
@@ -72,7 +167,22 @@ class _Volume(modal._object._Object):
72
167
  environment_name: typing.Optional[str] = None,
73
168
  create_if_missing: bool = False,
74
169
  version: typing.Optional[int] = None,
75
- ) -> _Volume: ...
170
+ ) -> _Volume:
171
+ """mdmd:hidden
172
+ Lookup a named Volume.
173
+
174
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
175
+
176
+ In contrast to `modal.Volume.from_name`, this is an eager method
177
+ that will hydrate the local object with metadata from Modal servers.
178
+
179
+ ```python notest
180
+ vol = modal.Volume.from_name("my-volume")
181
+ print(vol.listdir("/"))
182
+ ```
183
+ """
184
+ ...
185
+
76
186
  @staticmethod
77
187
  async def create_deployed(
78
188
  deployment_name: str,
@@ -80,24 +190,127 @@ class _Volume(modal._object._Object):
80
190
  client: typing.Optional[modal.client._Client] = None,
81
191
  environment_name: typing.Optional[str] = None,
82
192
  version: typing.Optional[int] = None,
83
- ) -> str: ...
193
+ ) -> str:
194
+ """mdmd:hidden"""
195
+ ...
196
+
84
197
  async def _do_reload(self, lock=True): ...
85
- async def commit(self): ...
86
- async def reload(self): ...
87
- def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
88
- async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
89
- def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
198
+ async def commit(self):
199
+ """Commit changes to the volume.
200
+
201
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
202
+ the volume.
203
+ """
204
+ ...
205
+
206
+ async def reload(self):
207
+ """Make latest committed state of volume available in the running container.
208
+
209
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
210
+ reloading.
211
+
212
+ Reloading will fail if there are open files for the volume.
213
+ """
214
+ ...
215
+
216
+ def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
217
+ """Iterate over all files in a directory in the volume.
218
+
219
+ Passing a directory path lists all files in the directory. For a file path, return only that
220
+ file's description. If `recursive` is set to True, list all files and folders under the path
221
+ recursively.
222
+ """
223
+ ...
224
+
225
+ async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]:
226
+ """List all files under a path prefix in the modal.Volume.
227
+
228
+ Passing a directory path lists all files in the directory. For a file path, return only that
229
+ file's description. If `recursive` is set to True, list all files and folders under the path
230
+ recursively.
231
+ """
232
+ ...
233
+
234
+ def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]:
235
+ """Read a file from the modal.Volume.
236
+
237
+ Note - this function is primarily intended to be used outside of a Modal App.
238
+ For more information on downloading files from a Modal Volume, see
239
+ [the guide](https://modal.com/docs/guide/volumes).
240
+
241
+ **Example:**
242
+
243
+ ```python notest
244
+ vol = modal.Volume.from_name("my-modal-volume")
245
+ data = b""
246
+ for chunk in vol.read_file("1mb.csv"):
247
+ data += chunk
248
+ print(len(data)) # == 1024 * 1024
249
+ ```
250
+ """
251
+ ...
252
+
90
253
  async def read_file_into_fileobj(
91
254
  self,
92
255
  path: str,
93
256
  fileobj: typing.IO[bytes],
94
257
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
95
- ) -> int: ...
96
- async def remove_file(self, path: str, recursive: bool = False) -> None: ...
258
+ ) -> int:
259
+ """mdmd:hidden
260
+ Read volume file into file-like IO object.
261
+ """
262
+ ...
263
+
264
+ async def remove_file(self, path: str, recursive: bool = False) -> None:
265
+ """Remove a file or directory from a volume."""
266
+ ...
267
+
97
268
  async def copy_files(
98
269
  self, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
99
- ) -> None: ...
100
- async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager: ...
270
+ ) -> None:
271
+ """Copy files within the volume from src_paths to dst_path.
272
+ The semantics of the copy operation follow those of the UNIX cp command.
273
+
274
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
275
+ single element.
276
+
277
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
278
+ the volume mount path.
279
+
280
+ **Usage**
281
+
282
+ ```python notest
283
+ vol = modal.Volume.from_name("my-modal-volume")
284
+
285
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
286
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
287
+ ```
288
+
289
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
290
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
291
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
292
+ """
293
+ ...
294
+
295
+ async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager:
296
+ """Initiate a batched upload to a volume.
297
+
298
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
299
+ uploaded files regardless).
300
+
301
+ **Example:**
302
+
303
+ ```python notest
304
+ vol = modal.Volume.from_name("my-modal-volume")
305
+
306
+ with vol.batch_upload() as batch:
307
+ batch.put_file("local-path.txt", "/remote-path.txt")
308
+ batch.put_directory("/local/directory/", "/remote/directory")
309
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
310
+ ```
311
+ """
312
+ ...
313
+
101
314
  async def _instance_delete(self): ...
102
315
  @staticmethod
103
316
  async def delete(
@@ -115,10 +328,53 @@ class _Volume(modal._object._Object):
115
328
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
116
329
 
117
330
  class Volume(modal.object.Object):
331
+ """A writeable volume that can be used to share files between one or more Modal functions.
332
+
333
+ The contents of a volume is exposed as a filesystem. You can use it to share data between different functions, or
334
+ to persist durable state across several instances of the same function.
335
+
336
+ Unlike a networked filesystem, you need to explicitly reload the volume to see changes made since it was mounted.
337
+ Similarly, you need to explicitly commit any changes you make to the volume for the changes to become visible
338
+ outside the current container.
339
+
340
+ Concurrent modification is supported, but concurrent modifications of the same files should be avoided! Last write
341
+ wins in case of concurrent modification of the same file - any data the last writer didn't have when committing
342
+ changes will be lost!
343
+
344
+ As a result, volumes are typically not a good fit for use cases where you need to make concurrent modifications to
345
+ the same file (nor is distributed file locking supported).
346
+
347
+ Volumes can only be reloaded if there are no open files for the volume - attempting to reload with open files
348
+ will result in an error.
349
+
350
+ **Usage**
351
+
352
+ ```python
353
+ import modal
354
+
355
+ app = modal.App()
356
+ volume = modal.Volume.from_name("my-persisted-volume", create_if_missing=True)
357
+
358
+ @app.function(volumes={"/root/foo": volume})
359
+ def f():
360
+ with open("/root/foo/bar.txt", "w") as f:
361
+ f.write("hello")
362
+ volume.commit() # Persist changes
363
+
364
+ @app.function(volumes={"/root/foo": volume})
365
+ def g():
366
+ volume.reload() # Fetch latest changes
367
+ with open("/root/foo/bar.txt", "r") as f:
368
+ print(f.read())
369
+ ```
370
+ """
371
+
118
372
  _lock: typing.Optional[asyncio.locks.Lock]
119
373
  _metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
120
374
 
121
- def __init__(self, *args, **kwargs): ...
375
+ def __init__(self, *args, **kwargs):
376
+ """mdmd:hidden"""
377
+ ...
122
378
 
123
379
  class ___get_lock_spec(typing_extensions.Protocol[SUPERSELF]):
124
380
  def __call__(self, /): ...
@@ -134,7 +390,26 @@ class Volume(modal.object.Object):
134
390
  environment_name: typing.Optional[str] = None,
135
391
  create_if_missing: bool = False,
136
392
  version: typing.Optional[int] = None,
137
- ) -> Volume: ...
393
+ ) -> Volume:
394
+ """Reference a Volume by name, creating if necessary.
395
+
396
+ In contrast to `modal.Volume.lookup`, this is a lazy method
397
+ that defers hydrating the local object with metadata from
398
+ Modal servers until the first time is is actually used.
399
+
400
+ ```python
401
+ vol = modal.Volume.from_name("my-volume", create_if_missing=True)
402
+
403
+ app = modal.App()
404
+
405
+ # Volume refers to the same object, even across instances of `app`.
406
+ @app.function(volumes={"/data": vol})
407
+ def f():
408
+ pass
409
+ ```
410
+ """
411
+ ...
412
+
138
413
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
139
414
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
140
415
  @property
@@ -146,7 +421,22 @@ class Volume(modal.object.Object):
146
421
  environment_name: typing.Optional[str] = None,
147
422
  version: typing.Optional[int] = None,
148
423
  _heartbeat_sleep: float = 300,
149
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]: ...
424
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
425
+ """Creates a new ephemeral volume within a context manager:
426
+
427
+ Usage:
428
+ ```python
429
+ import modal
430
+ with modal.Volume.ephemeral() as vol:
431
+ assert vol.listdir("/") == []
432
+ ```
433
+
434
+ ```python notest
435
+ async with modal.Volume.ephemeral() as vol:
436
+ assert await vol.listdir("/") == []
437
+ ```
438
+ """
439
+ ...
150
440
 
151
441
  class __lookup_spec(typing_extensions.Protocol):
152
442
  def __call__(
@@ -158,7 +448,22 @@ class Volume(modal.object.Object):
158
448
  environment_name: typing.Optional[str] = None,
159
449
  create_if_missing: bool = False,
160
450
  version: typing.Optional[int] = None,
161
- ) -> Volume: ...
451
+ ) -> Volume:
452
+ """mdmd:hidden
453
+ Lookup a named Volume.
454
+
455
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
456
+
457
+ In contrast to `modal.Volume.from_name`, this is an eager method
458
+ that will hydrate the local object with metadata from Modal servers.
459
+
460
+ ```python notest
461
+ vol = modal.Volume.from_name("my-volume")
462
+ print(vol.listdir("/"))
463
+ ```
464
+ """
465
+ ...
466
+
162
467
  async def aio(
163
468
  self,
164
469
  /,
@@ -168,7 +473,21 @@ class Volume(modal.object.Object):
168
473
  environment_name: typing.Optional[str] = None,
169
474
  create_if_missing: bool = False,
170
475
  version: typing.Optional[int] = None,
171
- ) -> Volume: ...
476
+ ) -> Volume:
477
+ """mdmd:hidden
478
+ Lookup a named Volume.
479
+
480
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
481
+
482
+ In contrast to `modal.Volume.from_name`, this is an eager method
483
+ that will hydrate the local object with metadata from Modal servers.
484
+
485
+ ```python notest
486
+ vol = modal.Volume.from_name("my-volume")
487
+ print(vol.listdir("/"))
488
+ ```
489
+ """
490
+ ...
172
491
 
173
492
  lookup: __lookup_spec
174
493
 
@@ -181,7 +500,10 @@ class Volume(modal.object.Object):
181
500
  client: typing.Optional[modal.client.Client] = None,
182
501
  environment_name: typing.Optional[str] = None,
183
502
  version: typing.Optional[int] = None,
184
- ) -> str: ...
503
+ ) -> str:
504
+ """mdmd:hidden"""
505
+ ...
506
+
185
507
  async def aio(
186
508
  self,
187
509
  /,
@@ -190,7 +512,9 @@ class Volume(modal.object.Object):
190
512
  client: typing.Optional[modal.client.Client] = None,
191
513
  environment_name: typing.Optional[str] = None,
192
514
  version: typing.Optional[int] = None,
193
- ) -> str: ...
515
+ ) -> str:
516
+ """mdmd:hidden"""
517
+ ...
194
518
 
195
519
  create_deployed: __create_deployed_spec
196
520
 
@@ -201,32 +525,127 @@ class Volume(modal.object.Object):
201
525
  _do_reload: ___do_reload_spec[typing_extensions.Self]
202
526
 
203
527
  class __commit_spec(typing_extensions.Protocol[SUPERSELF]):
204
- def __call__(self, /): ...
205
- async def aio(self, /): ...
528
+ def __call__(self, /):
529
+ """Commit changes to the volume.
530
+
531
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
532
+ the volume.
533
+ """
534
+ ...
535
+
536
+ async def aio(self, /):
537
+ """Commit changes to the volume.
538
+
539
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
540
+ the volume.
541
+ """
542
+ ...
206
543
 
207
544
  commit: __commit_spec[typing_extensions.Self]
208
545
 
209
546
  class __reload_spec(typing_extensions.Protocol[SUPERSELF]):
210
- def __call__(self, /): ...
211
- async def aio(self, /): ...
547
+ def __call__(self, /):
548
+ """Make latest committed state of volume available in the running container.
549
+
550
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
551
+ reloading.
552
+
553
+ Reloading will fail if there are open files for the volume.
554
+ """
555
+ ...
556
+
557
+ async def aio(self, /):
558
+ """Make latest committed state of volume available in the running container.
559
+
560
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
561
+ reloading.
562
+
563
+ Reloading will fail if there are open files for the volume.
564
+ """
565
+ ...
212
566
 
213
567
  reload: __reload_spec[typing_extensions.Self]
214
568
 
215
569
  class __iterdir_spec(typing_extensions.Protocol[SUPERSELF]):
216
- def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]: ...
217
- def aio(self, /, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
570
+ def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]:
571
+ """Iterate over all files in a directory in the volume.
572
+
573
+ Passing a directory path lists all files in the directory. For a file path, return only that
574
+ file's description. If `recursive` is set to True, list all files and folders under the path
575
+ recursively.
576
+ """
577
+ ...
578
+
579
+ def aio(self, /, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
580
+ """Iterate over all files in a directory in the volume.
581
+
582
+ Passing a directory path lists all files in the directory. For a file path, return only that
583
+ file's description. If `recursive` is set to True, list all files and folders under the path
584
+ recursively.
585
+ """
586
+ ...
218
587
 
219
588
  iterdir: __iterdir_spec[typing_extensions.Self]
220
589
 
221
590
  class __listdir_spec(typing_extensions.Protocol[SUPERSELF]):
222
- def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
223
- async def aio(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
591
+ def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
592
+ """List all files under a path prefix in the modal.Volume.
593
+
594
+ Passing a directory path lists all files in the directory. For a file path, return only that
595
+ file's description. If `recursive` is set to True, list all files and folders under the path
596
+ recursively.
597
+ """
598
+ ...
599
+
600
+ async def aio(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
601
+ """List all files under a path prefix in the modal.Volume.
602
+
603
+ Passing a directory path lists all files in the directory. For a file path, return only that
604
+ file's description. If `recursive` is set to True, list all files and folders under the path
605
+ recursively.
606
+ """
607
+ ...
224
608
 
225
609
  listdir: __listdir_spec[typing_extensions.Self]
226
610
 
227
611
  class __read_file_spec(typing_extensions.Protocol[SUPERSELF]):
228
- def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
229
- def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
612
+ def __call__(self, /, path: str) -> typing.Iterator[bytes]:
613
+ """Read a file from the modal.Volume.
614
+
615
+ Note - this function is primarily intended to be used outside of a Modal App.
616
+ For more information on downloading files from a Modal Volume, see
617
+ [the guide](https://modal.com/docs/guide/volumes).
618
+
619
+ **Example:**
620
+
621
+ ```python notest
622
+ vol = modal.Volume.from_name("my-modal-volume")
623
+ data = b""
624
+ for chunk in vol.read_file("1mb.csv"):
625
+ data += chunk
626
+ print(len(data)) # == 1024 * 1024
627
+ ```
628
+ """
629
+ ...
630
+
631
+ def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]:
632
+ """Read a file from the modal.Volume.
633
+
634
+ Note - this function is primarily intended to be used outside of a Modal App.
635
+ For more information on downloading files from a Modal Volume, see
636
+ [the guide](https://modal.com/docs/guide/volumes).
637
+
638
+ **Example:**
639
+
640
+ ```python notest
641
+ vol = modal.Volume.from_name("my-modal-volume")
642
+ data = b""
643
+ for chunk in vol.read_file("1mb.csv"):
644
+ data += chunk
645
+ print(len(data)) # == 1024 * 1024
646
+ ```
647
+ """
648
+ ...
230
649
 
231
650
  read_file: __read_file_spec[typing_extensions.Self]
232
651
 
@@ -237,36 +656,130 @@ class Volume(modal.object.Object):
237
656
  path: str,
238
657
  fileobj: typing.IO[bytes],
239
658
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
240
- ) -> int: ...
659
+ ) -> int:
660
+ """mdmd:hidden
661
+ Read volume file into file-like IO object.
662
+ """
663
+ ...
664
+
241
665
  async def aio(
242
666
  self,
243
667
  /,
244
668
  path: str,
245
669
  fileobj: typing.IO[bytes],
246
670
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
247
- ) -> int: ...
671
+ ) -> int:
672
+ """mdmd:hidden
673
+ Read volume file into file-like IO object.
674
+ """
675
+ ...
248
676
 
249
677
  read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
250
678
 
251
679
  class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
252
- def __call__(self, /, path: str, recursive: bool = False) -> None: ...
253
- async def aio(self, /, path: str, recursive: bool = False) -> None: ...
680
+ def __call__(self, /, path: str, recursive: bool = False) -> None:
681
+ """Remove a file or directory from a volume."""
682
+ ...
683
+
684
+ async def aio(self, /, path: str, recursive: bool = False) -> None:
685
+ """Remove a file or directory from a volume."""
686
+ ...
254
687
 
255
688
  remove_file: __remove_file_spec[typing_extensions.Self]
256
689
 
257
690
  class __copy_files_spec(typing_extensions.Protocol[SUPERSELF]):
258
- def __call__(
259
- self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
260
- ) -> None: ...
691
+ def __call__(self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False) -> None:
692
+ """Copy files within the volume from src_paths to dst_path.
693
+ The semantics of the copy operation follow those of the UNIX cp command.
694
+
695
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
696
+ single element.
697
+
698
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
699
+ the volume mount path.
700
+
701
+ **Usage**
702
+
703
+ ```python notest
704
+ vol = modal.Volume.from_name("my-modal-volume")
705
+
706
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
707
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
708
+ ```
709
+
710
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
711
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
712
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
713
+ """
714
+ ...
715
+
261
716
  async def aio(
262
717
  self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
263
- ) -> None: ...
718
+ ) -> None:
719
+ """Copy files within the volume from src_paths to dst_path.
720
+ The semantics of the copy operation follow those of the UNIX cp command.
721
+
722
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
723
+ single element.
724
+
725
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
726
+ the volume mount path.
727
+
728
+ **Usage**
729
+
730
+ ```python notest
731
+ vol = modal.Volume.from_name("my-modal-volume")
732
+
733
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
734
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
735
+ ```
736
+
737
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
738
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
739
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
740
+ """
741
+ ...
264
742
 
265
743
  copy_files: __copy_files_spec[typing_extensions.Self]
266
744
 
267
745
  class __batch_upload_spec(typing_extensions.Protocol[SUPERSELF]):
268
- def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager: ...
269
- async def aio(self, /, force: bool = False) -> AbstractVolumeUploadContextManager: ...
746
+ def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
747
+ """Initiate a batched upload to a volume.
748
+
749
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
750
+ uploaded files regardless).
751
+
752
+ **Example:**
753
+
754
+ ```python notest
755
+ vol = modal.Volume.from_name("my-modal-volume")
756
+
757
+ with vol.batch_upload() as batch:
758
+ batch.put_file("local-path.txt", "/remote-path.txt")
759
+ batch.put_directory("/local/directory/", "/remote/directory")
760
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
761
+ ```
762
+ """
763
+ ...
764
+
765
+ async def aio(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
766
+ """Initiate a batched upload to a volume.
767
+
768
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
769
+ uploaded files regardless).
770
+
771
+ **Example:**
772
+
773
+ ```python notest
774
+ vol = modal.Volume.from_name("my-modal-volume")
775
+
776
+ with vol.batch_upload() as batch:
777
+ batch.put_file("local-path.txt", "/remote-path.txt")
778
+ batch.put_directory("/local/directory/", "/remote/directory")
779
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
780
+ ```
781
+ """
782
+ ...
270
783
 
271
784
  batch_upload: __batch_upload_spec[typing_extensions.Self]
272
785
 
@@ -341,7 +854,10 @@ class _AbstractVolumeUploadContextManager:
341
854
  ) -> _AbstractVolumeUploadContextManager: ...
342
855
 
343
856
  class AbstractVolumeUploadContextManager:
344
- def __init__(self, /, *args, **kwargs): ...
857
+ def __init__(self, /, *args, **kwargs):
858
+ """Initialize self. See help(type(self)) for accurate signature."""
859
+ ...
860
+
345
861
  def __enter__(self): ...
346
862
  async def __aenter__(self): ...
347
863
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -368,6 +884,8 @@ class AbstractVolumeUploadContextManager:
368
884
  ) -> AbstractVolumeUploadContextManager: ...
369
885
 
370
886
  class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
887
+ """Context manager for batch-uploading files to a Volume."""
888
+
371
889
  _volume_id: str
372
890
  _client: modal.client._Client
373
891
  _force: bool
@@ -382,7 +900,10 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
382
900
  client: modal.client._Client,
383
901
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
384
902
  force: bool = False,
385
- ): ...
903
+ ):
904
+ """mdmd:hidden"""
905
+ ...
906
+
386
907
  async def __aenter__(self): ...
387
908
  async def __aexit__(self, exc_type, exc_val, exc_tb): ...
388
909
  def put_file(
@@ -390,18 +911,34 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
390
911
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
391
912
  remote_path: typing.Union[pathlib.PurePosixPath, str],
392
913
  mode: typing.Optional[int] = None,
393
- ): ...
914
+ ):
915
+ """Upload a file from a local file or file-like object.
916
+
917
+ Will create any needed parent directories automatically.
918
+
919
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
920
+ """
921
+ ...
922
+
394
923
  def put_directory(
395
924
  self,
396
925
  local_path: typing.Union[pathlib.Path, str],
397
926
  remote_path: typing.Union[pathlib.PurePosixPath, str],
398
927
  recursive: bool = True,
399
- ): ...
928
+ ):
929
+ """Upload all files in a local directory.
930
+
931
+ Will create any needed parent directories automatically.
932
+ """
933
+ ...
934
+
400
935
  async def _upload_file(
401
936
  self, file_spec: modal._utils.blob_utils.FileUploadSpec
402
937
  ) -> modal_proto.api_pb2.MountFile: ...
403
938
 
404
939
  class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
940
+ """Context manager for batch-uploading files to a Volume."""
941
+
405
942
  _volume_id: str
406
943
  _client: modal.client.Client
407
944
  _force: bool
@@ -416,7 +953,10 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
416
953
  client: modal.client.Client,
417
954
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
418
955
  force: bool = False,
419
- ): ...
956
+ ):
957
+ """mdmd:hidden"""
958
+ ...
959
+
420
960
  def __enter__(self): ...
421
961
  async def __aenter__(self): ...
422
962
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -426,13 +966,26 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
426
966
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
427
967
  remote_path: typing.Union[pathlib.PurePosixPath, str],
428
968
  mode: typing.Optional[int] = None,
429
- ): ...
969
+ ):
970
+ """Upload a file from a local file or file-like object.
971
+
972
+ Will create any needed parent directories automatically.
973
+
974
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
975
+ """
976
+ ...
977
+
430
978
  def put_directory(
431
979
  self,
432
980
  local_path: typing.Union[pathlib.Path, str],
433
981
  remote_path: typing.Union[pathlib.PurePosixPath, str],
434
982
  recursive: bool = True,
435
- ): ...
983
+ ):
984
+ """Upload all files in a local directory.
985
+
986
+ Will create any needed parent directories automatically.
987
+ """
988
+ ...
436
989
 
437
990
  class ___upload_file_spec(typing_extensions.Protocol[SUPERSELF]):
438
991
  def __call__(self, /, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
@@ -441,6 +994,8 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
441
994
  _upload_file: ___upload_file_spec[typing_extensions.Self]
442
995
 
443
996
  class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
997
+ """Context manager for batch-uploading files to a Volume version 2."""
998
+
444
999
  _volume_id: str
445
1000
  _client: modal.client._Client
446
1001
  _progress_cb: collections.abc.Callable[..., typing.Any]
@@ -463,7 +1018,10 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
463
1018
  force: bool = False,
464
1019
  hash_concurrency: int = 4,
465
1020
  put_concurrency: int = 128,
466
- ): ...
1021
+ ):
1022
+ """mdmd:hidden"""
1023
+ ...
1024
+
467
1025
  async def __aenter__(self): ...
468
1026
  async def __aexit__(self, exc_type, exc_val, exc_tb): ...
469
1027
  def put_file(
@@ -471,16 +1029,32 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
471
1029
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
472
1030
  remote_path: typing.Union[pathlib.PurePosixPath, str],
473
1031
  mode: typing.Optional[int] = None,
474
- ): ...
1032
+ ):
1033
+ """Upload a file from a local file or file-like object.
1034
+
1035
+ Will create any needed parent directories automatically.
1036
+
1037
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
1038
+ """
1039
+ ...
1040
+
475
1041
  def put_directory(
476
1042
  self,
477
1043
  local_path: typing.Union[pathlib.Path, str],
478
1044
  remote_path: typing.Union[pathlib.PurePosixPath, str],
479
1045
  recursive: bool = True,
480
- ): ...
1046
+ ):
1047
+ """Upload all files in a local directory.
1048
+
1049
+ Will create any needed parent directories automatically.
1050
+ """
1051
+ ...
1052
+
481
1053
  async def _put_file_specs(self, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
482
1054
 
483
1055
  class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
1056
+ """Context manager for batch-uploading files to a Volume version 2."""
1057
+
484
1058
  _volume_id: str
485
1059
  _client: modal.client.Client
486
1060
  _progress_cb: collections.abc.Callable[..., typing.Any]
@@ -503,7 +1077,10 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
503
1077
  force: bool = False,
504
1078
  hash_concurrency: int = 4,
505
1079
  put_concurrency: int = 128,
506
- ): ...
1080
+ ):
1081
+ """mdmd:hidden"""
1082
+ ...
1083
+
507
1084
  def __enter__(self): ...
508
1085
  async def __aenter__(self): ...
509
1086
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -513,13 +1090,26 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
513
1090
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
514
1091
  remote_path: typing.Union[pathlib.PurePosixPath, str],
515
1092
  mode: typing.Optional[int] = None,
516
- ): ...
1093
+ ):
1094
+ """Upload a file from a local file or file-like object.
1095
+
1096
+ Will create any needed parent directories automatically.
1097
+
1098
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
1099
+ """
1100
+ ...
1101
+
517
1102
  def put_directory(
518
1103
  self,
519
1104
  local_path: typing.Union[pathlib.Path, str],
520
1105
  remote_path: typing.Union[pathlib.PurePosixPath, str],
521
1106
  recursive: bool = True,
522
- ): ...
1107
+ ):
1108
+ """Upload all files in a local directory.
1109
+
1110
+ Will create any needed parent directories automatically.
1111
+ """
1112
+ ...
523
1113
 
524
1114
  class ___put_file_specs_spec(typing_extensions.Protocol[SUPERSELF]):
525
1115
  def __call__(self, /, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...