modal 1.0.4.dev12__py3-none-any.whl → 1.0.5__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 (67) hide show
  1. modal/_clustered_functions.pyi +13 -3
  2. modal/_functions.py +84 -46
  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/_serialization.py +25 -2
  7. modal/_tunnel.pyi +380 -12
  8. modal/_utils/async_utils.py +1 -1
  9. modal/_utils/blob_utils.py +56 -19
  10. modal/_utils/function_utils.py +33 -7
  11. modal/_utils/grpc_utils.py +11 -4
  12. modal/app.py +5 -5
  13. modal/app.pyi +658 -48
  14. modal/cli/run.py +2 -1
  15. modal/client.pyi +224 -36
  16. modal/cloud_bucket_mount.pyi +192 -4
  17. modal/cls.py +7 -7
  18. modal/cls.pyi +442 -35
  19. modal/container_process.pyi +103 -14
  20. modal/dict.py +4 -4
  21. modal/dict.pyi +453 -51
  22. modal/environments.pyi +41 -9
  23. modal/exception.py +6 -2
  24. modal/experimental/__init__.py +90 -0
  25. modal/experimental/ipython.py +11 -7
  26. modal/file_io.pyi +236 -45
  27. modal/functions.pyi +573 -65
  28. modal/gpu.py +1 -1
  29. modal/image.py +1 -1
  30. modal/image.pyi +1256 -74
  31. modal/io_streams.py +8 -4
  32. modal/io_streams.pyi +348 -38
  33. modal/mount.pyi +261 -31
  34. modal/network_file_system.py +3 -3
  35. modal/network_file_system.pyi +307 -26
  36. modal/object.pyi +48 -9
  37. modal/parallel_map.py +93 -19
  38. modal/parallel_map.pyi +160 -15
  39. modal/partial_function.pyi +255 -14
  40. modal/proxy.py +1 -1
  41. modal/proxy.pyi +28 -3
  42. modal/queue.py +4 -4
  43. modal/queue.pyi +447 -30
  44. modal/runner.pyi +160 -22
  45. modal/sandbox.py +8 -7
  46. modal/sandbox.pyi +310 -50
  47. modal/schedule.py +1 -1
  48. modal/secret.py +2 -2
  49. modal/secret.pyi +164 -15
  50. modal/snapshot.pyi +25 -4
  51. modal/token_flow.pyi +28 -8
  52. modal/volume.py +41 -4
  53. modal/volume.pyi +693 -59
  54. {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/METADATA +3 -3
  55. {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/RECORD +67 -67
  56. modal_proto/api.proto +56 -0
  57. modal_proto/api_grpc.py +48 -0
  58. modal_proto/api_pb2.py +874 -780
  59. modal_proto/api_pb2.pyi +194 -8
  60. modal_proto/api_pb2_grpc.py +100 -0
  61. modal_proto/api_pb2_grpc.pyi +32 -0
  62. modal_proto/modal_api_grpc.py +3 -0
  63. modal_version/__init__.py +1 -1
  64. {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/WHEEL +0 -0
  65. {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/entry_points.txt +0 -0
  66. {modal-1.0.4.dev12.dist-info → modal-1.0.5.dist-info}/licenses/LICENSE +0 -0
  67. {modal-1.0.4.dev12.dist-info → modal-1.0.5.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,16 +33,96 @@ 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]
104
+ _read_only: bool
105
+
106
+ def read_only(self) -> _Volume:
107
+ """Configure Volume to mount as read-only.
108
+
109
+ **Example**
110
+
111
+ ```python
112
+ import modal
113
+
114
+ volume = modal.Volume.from_name("my-volume", create_if_missing=True)
115
+
116
+ @app.function(volumes={"/mnt/items": volume.read_only()})
117
+ def f():
118
+ with open("/mnt/items/my-file.txt") as f:
119
+ return f.read()
120
+ ```
121
+
122
+ The Volume is mounted as a read-only volume in a function. Any file system write operation into the
123
+ mounted volume will result in an error.
124
+ """
125
+ ...
44
126
 
45
127
  async def _get_lock(self): ...
46
128
  @staticmethod
@@ -51,7 +133,26 @@ class _Volume(modal._object._Object):
51
133
  environment_name: typing.Optional[str] = None,
52
134
  create_if_missing: bool = False,
53
135
  version: typing.Optional[int] = None,
54
- ) -> _Volume: ...
136
+ ) -> _Volume:
137
+ """Reference a Volume by name, creating if necessary.
138
+
139
+ This is a lazy method that defers hydrating the local
140
+ object with metadata from Modal servers until the first
141
+ time is is actually used.
142
+
143
+ ```python
144
+ vol = modal.Volume.from_name("my-volume", create_if_missing=True)
145
+
146
+ app = modal.App()
147
+
148
+ # Volume refers to the same object, even across instances of `app`.
149
+ @app.function(volumes={"/data": vol})
150
+ def f():
151
+ pass
152
+ ```
153
+ """
154
+ ...
155
+
55
156
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
56
157
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
57
158
  @property
@@ -63,7 +164,23 @@ class _Volume(modal._object._Object):
63
164
  environment_name: typing.Optional[str] = None,
64
165
  version: typing.Optional[int] = None,
65
166
  _heartbeat_sleep: float = 300,
66
- ) -> typing.AsyncContextManager[_Volume]: ...
167
+ ) -> typing.AsyncContextManager[_Volume]:
168
+ """Creates a new ephemeral volume within a context manager:
169
+
170
+ Usage:
171
+ ```python
172
+ import modal
173
+ with modal.Volume.ephemeral() as vol:
174
+ assert vol.listdir("/") == []
175
+ ```
176
+
177
+ ```python notest
178
+ async with modal.Volume.ephemeral() as vol:
179
+ assert await vol.listdir("/") == []
180
+ ```
181
+ """
182
+ ...
183
+
67
184
  @staticmethod
68
185
  async def lookup(
69
186
  name: str,
@@ -72,7 +189,22 @@ class _Volume(modal._object._Object):
72
189
  environment_name: typing.Optional[str] = None,
73
190
  create_if_missing: bool = False,
74
191
  version: typing.Optional[int] = None,
75
- ) -> _Volume: ...
192
+ ) -> _Volume:
193
+ """mdmd:hidden
194
+ Lookup a named Volume.
195
+
196
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
197
+
198
+ In contrast to `modal.Volume.from_name`, this is an eager method
199
+ that will hydrate the local object with metadata from Modal servers.
200
+
201
+ ```python notest
202
+ vol = modal.Volume.from_name("my-volume")
203
+ print(vol.listdir("/"))
204
+ ```
205
+ """
206
+ ...
207
+
76
208
  @staticmethod
77
209
  async def create_deployed(
78
210
  deployment_name: str,
@@ -80,24 +212,127 @@ class _Volume(modal._object._Object):
80
212
  client: typing.Optional[modal.client._Client] = None,
81
213
  environment_name: typing.Optional[str] = None,
82
214
  version: typing.Optional[int] = None,
83
- ) -> str: ...
215
+ ) -> str:
216
+ """mdmd:hidden"""
217
+ ...
218
+
84
219
  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]: ...
220
+ async def commit(self):
221
+ """Commit changes to the volume.
222
+
223
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
224
+ the volume.
225
+ """
226
+ ...
227
+
228
+ async def reload(self):
229
+ """Make latest committed state of volume available in the running container.
230
+
231
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
232
+ reloading.
233
+
234
+ Reloading will fail if there are open files for the volume.
235
+ """
236
+ ...
237
+
238
+ def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
239
+ """Iterate over all files in a directory in the volume.
240
+
241
+ Passing a directory path lists all files in the directory. For a file path, return only that
242
+ file's description. If `recursive` is set to True, list all files and folders under the path
243
+ recursively.
244
+ """
245
+ ...
246
+
247
+ async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]:
248
+ """List all files under a path prefix in the modal.Volume.
249
+
250
+ Passing a directory path lists all files in the directory. For a file path, return only that
251
+ file's description. If `recursive` is set to True, list all files and folders under the path
252
+ recursively.
253
+ """
254
+ ...
255
+
256
+ def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]:
257
+ """Read a file from the modal.Volume.
258
+
259
+ Note - this function is primarily intended to be used outside of a Modal App.
260
+ For more information on downloading files from a Modal Volume, see
261
+ [the guide](https://modal.com/docs/guide/volumes).
262
+
263
+ **Example:**
264
+
265
+ ```python notest
266
+ vol = modal.Volume.from_name("my-modal-volume")
267
+ data = b""
268
+ for chunk in vol.read_file("1mb.csv"):
269
+ data += chunk
270
+ print(len(data)) # == 1024 * 1024
271
+ ```
272
+ """
273
+ ...
274
+
90
275
  async def read_file_into_fileobj(
91
276
  self,
92
277
  path: str,
93
278
  fileobj: typing.IO[bytes],
94
279
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
95
- ) -> int: ...
96
- async def remove_file(self, path: str, recursive: bool = False) -> None: ...
280
+ ) -> int:
281
+ """mdmd:hidden
282
+ Read volume file into file-like IO object.
283
+ """
284
+ ...
285
+
286
+ async def remove_file(self, path: str, recursive: bool = False) -> None:
287
+ """Remove a file or directory from a volume."""
288
+ ...
289
+
97
290
  async def copy_files(
98
291
  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: ...
292
+ ) -> None:
293
+ """Copy files within the volume from src_paths to dst_path.
294
+ The semantics of the copy operation follow those of the UNIX cp command.
295
+
296
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
297
+ single element.
298
+
299
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
300
+ the volume mount path.
301
+
302
+ **Usage**
303
+
304
+ ```python notest
305
+ vol = modal.Volume.from_name("my-modal-volume")
306
+
307
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
308
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
309
+ ```
310
+
311
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
312
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
313
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
314
+ """
315
+ ...
316
+
317
+ async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager:
318
+ """Initiate a batched upload to a volume.
319
+
320
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
321
+ uploaded files regardless).
322
+
323
+ **Example:**
324
+
325
+ ```python notest
326
+ vol = modal.Volume.from_name("my-modal-volume")
327
+
328
+ with vol.batch_upload() as batch:
329
+ batch.put_file("local-path.txt", "/remote-path.txt")
330
+ batch.put_directory("/local/directory/", "/remote/directory")
331
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
332
+ ```
333
+ """
334
+ ...
335
+
101
336
  async def _instance_delete(self): ...
102
337
  @staticmethod
103
338
  async def delete(
@@ -115,10 +350,75 @@ class _Volume(modal._object._Object):
115
350
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
116
351
 
117
352
  class Volume(modal.object.Object):
353
+ """A writeable volume that can be used to share files between one or more Modal functions.
354
+
355
+ The contents of a volume is exposed as a filesystem. You can use it to share data between different functions, or
356
+ to persist durable state across several instances of the same function.
357
+
358
+ Unlike a networked filesystem, you need to explicitly reload the volume to see changes made since it was mounted.
359
+ Similarly, you need to explicitly commit any changes you make to the volume for the changes to become visible
360
+ outside the current container.
361
+
362
+ Concurrent modification is supported, but concurrent modifications of the same files should be avoided! Last write
363
+ wins in case of concurrent modification of the same file - any data the last writer didn't have when committing
364
+ changes will be lost!
365
+
366
+ As a result, volumes are typically not a good fit for use cases where you need to make concurrent modifications to
367
+ the same file (nor is distributed file locking supported).
368
+
369
+ Volumes can only be reloaded if there are no open files for the volume - attempting to reload with open files
370
+ will result in an error.
371
+
372
+ **Usage**
373
+
374
+ ```python
375
+ import modal
376
+
377
+ app = modal.App()
378
+ volume = modal.Volume.from_name("my-persisted-volume", create_if_missing=True)
379
+
380
+ @app.function(volumes={"/root/foo": volume})
381
+ def f():
382
+ with open("/root/foo/bar.txt", "w") as f:
383
+ f.write("hello")
384
+ volume.commit() # Persist changes
385
+
386
+ @app.function(volumes={"/root/foo": volume})
387
+ def g():
388
+ volume.reload() # Fetch latest changes
389
+ with open("/root/foo/bar.txt", "r") as f:
390
+ print(f.read())
391
+ ```
392
+ """
393
+
118
394
  _lock: typing.Optional[asyncio.locks.Lock]
119
395
  _metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
396
+ _read_only: bool
397
+
398
+ def __init__(self, *args, **kwargs):
399
+ """mdmd:hidden"""
400
+ ...
401
+
402
+ def read_only(self) -> Volume:
403
+ """Configure Volume to mount as read-only.
120
404
 
121
- def __init__(self, *args, **kwargs): ...
405
+ **Example**
406
+
407
+ ```python
408
+ import modal
409
+
410
+ volume = modal.Volume.from_name("my-volume", create_if_missing=True)
411
+
412
+ @app.function(volumes={"/mnt/items": volume.read_only()})
413
+ def f():
414
+ with open("/mnt/items/my-file.txt") as f:
415
+ return f.read()
416
+ ```
417
+
418
+ The Volume is mounted as a read-only volume in a function. Any file system write operation into the
419
+ mounted volume will result in an error.
420
+ """
421
+ ...
122
422
 
123
423
  class ___get_lock_spec(typing_extensions.Protocol[SUPERSELF]):
124
424
  def __call__(self, /): ...
@@ -134,7 +434,26 @@ class Volume(modal.object.Object):
134
434
  environment_name: typing.Optional[str] = None,
135
435
  create_if_missing: bool = False,
136
436
  version: typing.Optional[int] = None,
137
- ) -> Volume: ...
437
+ ) -> Volume:
438
+ """Reference a Volume by name, creating if necessary.
439
+
440
+ This is a lazy method that defers hydrating the local
441
+ object with metadata from Modal servers until the first
442
+ time is is actually used.
443
+
444
+ ```python
445
+ vol = modal.Volume.from_name("my-volume", create_if_missing=True)
446
+
447
+ app = modal.App()
448
+
449
+ # Volume refers to the same object, even across instances of `app`.
450
+ @app.function(volumes={"/data": vol})
451
+ def f():
452
+ pass
453
+ ```
454
+ """
455
+ ...
456
+
138
457
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
139
458
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
140
459
  @property
@@ -146,7 +465,22 @@ class Volume(modal.object.Object):
146
465
  environment_name: typing.Optional[str] = None,
147
466
  version: typing.Optional[int] = None,
148
467
  _heartbeat_sleep: float = 300,
149
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]: ...
468
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
469
+ """Creates a new ephemeral volume within a context manager:
470
+
471
+ Usage:
472
+ ```python
473
+ import modal
474
+ with modal.Volume.ephemeral() as vol:
475
+ assert vol.listdir("/") == []
476
+ ```
477
+
478
+ ```python notest
479
+ async with modal.Volume.ephemeral() as vol:
480
+ assert await vol.listdir("/") == []
481
+ ```
482
+ """
483
+ ...
150
484
 
151
485
  class __lookup_spec(typing_extensions.Protocol):
152
486
  def __call__(
@@ -158,7 +492,22 @@ class Volume(modal.object.Object):
158
492
  environment_name: typing.Optional[str] = None,
159
493
  create_if_missing: bool = False,
160
494
  version: typing.Optional[int] = None,
161
- ) -> Volume: ...
495
+ ) -> Volume:
496
+ """mdmd:hidden
497
+ Lookup a named Volume.
498
+
499
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
500
+
501
+ In contrast to `modal.Volume.from_name`, this is an eager method
502
+ that will hydrate the local object with metadata from Modal servers.
503
+
504
+ ```python notest
505
+ vol = modal.Volume.from_name("my-volume")
506
+ print(vol.listdir("/"))
507
+ ```
508
+ """
509
+ ...
510
+
162
511
  async def aio(
163
512
  self,
164
513
  /,
@@ -168,7 +517,21 @@ class Volume(modal.object.Object):
168
517
  environment_name: typing.Optional[str] = None,
169
518
  create_if_missing: bool = False,
170
519
  version: typing.Optional[int] = None,
171
- ) -> Volume: ...
520
+ ) -> Volume:
521
+ """mdmd:hidden
522
+ Lookup a named Volume.
523
+
524
+ DEPRECATED: This method is deprecated in favor of `modal.Volume.from_name`.
525
+
526
+ In contrast to `modal.Volume.from_name`, this is an eager method
527
+ that will hydrate the local object with metadata from Modal servers.
528
+
529
+ ```python notest
530
+ vol = modal.Volume.from_name("my-volume")
531
+ print(vol.listdir("/"))
532
+ ```
533
+ """
534
+ ...
172
535
 
173
536
  lookup: __lookup_spec
174
537
 
@@ -181,7 +544,10 @@ class Volume(modal.object.Object):
181
544
  client: typing.Optional[modal.client.Client] = None,
182
545
  environment_name: typing.Optional[str] = None,
183
546
  version: typing.Optional[int] = None,
184
- ) -> str: ...
547
+ ) -> str:
548
+ """mdmd:hidden"""
549
+ ...
550
+
185
551
  async def aio(
186
552
  self,
187
553
  /,
@@ -190,7 +556,9 @@ class Volume(modal.object.Object):
190
556
  client: typing.Optional[modal.client.Client] = None,
191
557
  environment_name: typing.Optional[str] = None,
192
558
  version: typing.Optional[int] = None,
193
- ) -> str: ...
559
+ ) -> str:
560
+ """mdmd:hidden"""
561
+ ...
194
562
 
195
563
  create_deployed: __create_deployed_spec
196
564
 
@@ -201,32 +569,127 @@ class Volume(modal.object.Object):
201
569
  _do_reload: ___do_reload_spec[typing_extensions.Self]
202
570
 
203
571
  class __commit_spec(typing_extensions.Protocol[SUPERSELF]):
204
- def __call__(self, /): ...
205
- async def aio(self, /): ...
572
+ def __call__(self, /):
573
+ """Commit changes to the volume.
574
+
575
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
576
+ the volume.
577
+ """
578
+ ...
579
+
580
+ async def aio(self, /):
581
+ """Commit changes to the volume.
582
+
583
+ If successful, the changes made are now persisted in durable storage and available to other containers accessing
584
+ the volume.
585
+ """
586
+ ...
206
587
 
207
588
  commit: __commit_spec[typing_extensions.Self]
208
589
 
209
590
  class __reload_spec(typing_extensions.Protocol[SUPERSELF]):
210
- def __call__(self, /): ...
211
- async def aio(self, /): ...
591
+ def __call__(self, /):
592
+ """Make latest committed state of volume available in the running container.
593
+
594
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
595
+ reloading.
596
+
597
+ Reloading will fail if there are open files for the volume.
598
+ """
599
+ ...
600
+
601
+ async def aio(self, /):
602
+ """Make latest committed state of volume available in the running container.
603
+
604
+ Any uncommitted changes to the volume, such as new or modified files, may implicitly be committed when
605
+ reloading.
606
+
607
+ Reloading will fail if there are open files for the volume.
608
+ """
609
+ ...
212
610
 
213
611
  reload: __reload_spec[typing_extensions.Self]
214
612
 
215
613
  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]: ...
614
+ def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]:
615
+ """Iterate over all files in a directory in the volume.
616
+
617
+ Passing a directory path lists all files in the directory. For a file path, return only that
618
+ file's description. If `recursive` is set to True, list all files and folders under the path
619
+ recursively.
620
+ """
621
+ ...
622
+
623
+ def aio(self, /, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]:
624
+ """Iterate over all files in a directory in the volume.
625
+
626
+ Passing a directory path lists all files in the directory. For a file path, return only that
627
+ file's description. If `recursive` is set to True, list all files and folders under the path
628
+ recursively.
629
+ """
630
+ ...
218
631
 
219
632
  iterdir: __iterdir_spec[typing_extensions.Self]
220
633
 
221
634
  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]: ...
635
+ def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
636
+ """List all files under a path prefix in the modal.Volume.
637
+
638
+ Passing a directory path lists all files in the directory. For a file path, return only that
639
+ file's description. If `recursive` is set to True, list all files and folders under the path
640
+ recursively.
641
+ """
642
+ ...
643
+
644
+ async def aio(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]:
645
+ """List all files under a path prefix in the modal.Volume.
646
+
647
+ Passing a directory path lists all files in the directory. For a file path, return only that
648
+ file's description. If `recursive` is set to True, list all files and folders under the path
649
+ recursively.
650
+ """
651
+ ...
224
652
 
225
653
  listdir: __listdir_spec[typing_extensions.Self]
226
654
 
227
655
  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]: ...
656
+ def __call__(self, /, path: str) -> typing.Iterator[bytes]:
657
+ """Read a file from the modal.Volume.
658
+
659
+ Note - this function is primarily intended to be used outside of a Modal App.
660
+ For more information on downloading files from a Modal Volume, see
661
+ [the guide](https://modal.com/docs/guide/volumes).
662
+
663
+ **Example:**
664
+
665
+ ```python notest
666
+ vol = modal.Volume.from_name("my-modal-volume")
667
+ data = b""
668
+ for chunk in vol.read_file("1mb.csv"):
669
+ data += chunk
670
+ print(len(data)) # == 1024 * 1024
671
+ ```
672
+ """
673
+ ...
674
+
675
+ def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]:
676
+ """Read a file from the modal.Volume.
677
+
678
+ Note - this function is primarily intended to be used outside of a Modal App.
679
+ For more information on downloading files from a Modal Volume, see
680
+ [the guide](https://modal.com/docs/guide/volumes).
681
+
682
+ **Example:**
683
+
684
+ ```python notest
685
+ vol = modal.Volume.from_name("my-modal-volume")
686
+ data = b""
687
+ for chunk in vol.read_file("1mb.csv"):
688
+ data += chunk
689
+ print(len(data)) # == 1024 * 1024
690
+ ```
691
+ """
692
+ ...
230
693
 
231
694
  read_file: __read_file_spec[typing_extensions.Self]
232
695
 
@@ -237,36 +700,130 @@ class Volume(modal.object.Object):
237
700
  path: str,
238
701
  fileobj: typing.IO[bytes],
239
702
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
240
- ) -> int: ...
703
+ ) -> int:
704
+ """mdmd:hidden
705
+ Read volume file into file-like IO object.
706
+ """
707
+ ...
708
+
241
709
  async def aio(
242
710
  self,
243
711
  /,
244
712
  path: str,
245
713
  fileobj: typing.IO[bytes],
246
714
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
247
- ) -> int: ...
715
+ ) -> int:
716
+ """mdmd:hidden
717
+ Read volume file into file-like IO object.
718
+ """
719
+ ...
248
720
 
249
721
  read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
250
722
 
251
723
  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: ...
724
+ def __call__(self, /, path: str, recursive: bool = False) -> None:
725
+ """Remove a file or directory from a volume."""
726
+ ...
727
+
728
+ async def aio(self, /, path: str, recursive: bool = False) -> None:
729
+ """Remove a file or directory from a volume."""
730
+ ...
254
731
 
255
732
  remove_file: __remove_file_spec[typing_extensions.Self]
256
733
 
257
734
  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: ...
735
+ def __call__(self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False) -> None:
736
+ """Copy files within the volume from src_paths to dst_path.
737
+ The semantics of the copy operation follow those of the UNIX cp command.
738
+
739
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
740
+ single element.
741
+
742
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
743
+ the volume mount path.
744
+
745
+ **Usage**
746
+
747
+ ```python notest
748
+ vol = modal.Volume.from_name("my-modal-volume")
749
+
750
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
751
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
752
+ ```
753
+
754
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
755
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
756
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
757
+ """
758
+ ...
759
+
261
760
  async def aio(
262
761
  self, /, src_paths: collections.abc.Sequence[str], dst_path: str, recursive: bool = False
263
- ) -> None: ...
762
+ ) -> None:
763
+ """Copy files within the volume from src_paths to dst_path.
764
+ The semantics of the copy operation follow those of the UNIX cp command.
765
+
766
+ The `src_paths` parameter is a list. If you want to copy a single file, you should pass a list with a
767
+ single element.
768
+
769
+ `src_paths` and `dst_path` should refer to the desired location *inside* the volume. You do not need to prepend
770
+ the volume mount path.
771
+
772
+ **Usage**
773
+
774
+ ```python notest
775
+ vol = modal.Volume.from_name("my-modal-volume")
776
+
777
+ vol.copy_files(["bar/example.txt"], "bar2") # Copy files to another directory
778
+ vol.copy_files(["bar/example.txt"], "bar/example2.txt") # Rename a file by copying
779
+ ```
780
+
781
+ Note that if the volume is already mounted on the Modal function, you should use normal filesystem operations
782
+ like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
783
+ the volume mounted as a filesystem, e.g. when running a script on your local computer.
784
+ """
785
+ ...
264
786
 
265
787
  copy_files: __copy_files_spec[typing_extensions.Self]
266
788
 
267
789
  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: ...
790
+ def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
791
+ """Initiate a batched upload to a volume.
792
+
793
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
794
+ uploaded files regardless).
795
+
796
+ **Example:**
797
+
798
+ ```python notest
799
+ vol = modal.Volume.from_name("my-modal-volume")
800
+
801
+ with vol.batch_upload() as batch:
802
+ batch.put_file("local-path.txt", "/remote-path.txt")
803
+ batch.put_directory("/local/directory/", "/remote/directory")
804
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
805
+ ```
806
+ """
807
+ ...
808
+
809
+ async def aio(self, /, force: bool = False) -> AbstractVolumeUploadContextManager:
810
+ """Initiate a batched upload to a volume.
811
+
812
+ To allow overwriting existing files, set `force` to `True` (you cannot overwrite existing directories with
813
+ uploaded files regardless).
814
+
815
+ **Example:**
816
+
817
+ ```python notest
818
+ vol = modal.Volume.from_name("my-modal-volume")
819
+
820
+ with vol.batch_upload() as batch:
821
+ batch.put_file("local-path.txt", "/remote-path.txt")
822
+ batch.put_directory("/local/directory/", "/remote/directory")
823
+ batch.put_file(io.BytesIO(b"some data"), "/foobar")
824
+ ```
825
+ """
826
+ ...
270
827
 
271
828
  batch_upload: __batch_upload_spec[typing_extensions.Self]
272
829
 
@@ -341,7 +898,10 @@ class _AbstractVolumeUploadContextManager:
341
898
  ) -> _AbstractVolumeUploadContextManager: ...
342
899
 
343
900
  class AbstractVolumeUploadContextManager:
344
- def __init__(self, /, *args, **kwargs): ...
901
+ def __init__(self, /, *args, **kwargs):
902
+ """Initialize self. See help(type(self)) for accurate signature."""
903
+ ...
904
+
345
905
  def __enter__(self): ...
346
906
  async def __aenter__(self): ...
347
907
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -368,6 +928,8 @@ class AbstractVolumeUploadContextManager:
368
928
  ) -> AbstractVolumeUploadContextManager: ...
369
929
 
370
930
  class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
931
+ """Context manager for batch-uploading files to a Volume."""
932
+
371
933
  _volume_id: str
372
934
  _client: modal.client._Client
373
935
  _force: bool
@@ -382,7 +944,10 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
382
944
  client: modal.client._Client,
383
945
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
384
946
  force: bool = False,
385
- ): ...
947
+ ):
948
+ """mdmd:hidden"""
949
+ ...
950
+
386
951
  async def __aenter__(self): ...
387
952
  async def __aexit__(self, exc_type, exc_val, exc_tb): ...
388
953
  def put_file(
@@ -390,18 +955,34 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
390
955
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
391
956
  remote_path: typing.Union[pathlib.PurePosixPath, str],
392
957
  mode: typing.Optional[int] = None,
393
- ): ...
958
+ ):
959
+ """Upload a file from a local file or file-like object.
960
+
961
+ Will create any needed parent directories automatically.
962
+
963
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
964
+ """
965
+ ...
966
+
394
967
  def put_directory(
395
968
  self,
396
969
  local_path: typing.Union[pathlib.Path, str],
397
970
  remote_path: typing.Union[pathlib.PurePosixPath, str],
398
971
  recursive: bool = True,
399
- ): ...
972
+ ):
973
+ """Upload all files in a local directory.
974
+
975
+ Will create any needed parent directories automatically.
976
+ """
977
+ ...
978
+
400
979
  async def _upload_file(
401
980
  self, file_spec: modal._utils.blob_utils.FileUploadSpec
402
981
  ) -> modal_proto.api_pb2.MountFile: ...
403
982
 
404
983
  class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
984
+ """Context manager for batch-uploading files to a Volume."""
985
+
405
986
  _volume_id: str
406
987
  _client: modal.client.Client
407
988
  _force: bool
@@ -416,7 +997,10 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
416
997
  client: modal.client.Client,
417
998
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
418
999
  force: bool = False,
419
- ): ...
1000
+ ):
1001
+ """mdmd:hidden"""
1002
+ ...
1003
+
420
1004
  def __enter__(self): ...
421
1005
  async def __aenter__(self): ...
422
1006
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -426,13 +1010,26 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
426
1010
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
427
1011
  remote_path: typing.Union[pathlib.PurePosixPath, str],
428
1012
  mode: typing.Optional[int] = None,
429
- ): ...
1013
+ ):
1014
+ """Upload a file from a local file or file-like object.
1015
+
1016
+ Will create any needed parent directories automatically.
1017
+
1018
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
1019
+ """
1020
+ ...
1021
+
430
1022
  def put_directory(
431
1023
  self,
432
1024
  local_path: typing.Union[pathlib.Path, str],
433
1025
  remote_path: typing.Union[pathlib.PurePosixPath, str],
434
1026
  recursive: bool = True,
435
- ): ...
1027
+ ):
1028
+ """Upload all files in a local directory.
1029
+
1030
+ Will create any needed parent directories automatically.
1031
+ """
1032
+ ...
436
1033
 
437
1034
  class ___upload_file_spec(typing_extensions.Protocol[SUPERSELF]):
438
1035
  def __call__(self, /, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
@@ -441,6 +1038,8 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
441
1038
  _upload_file: ___upload_file_spec[typing_extensions.Self]
442
1039
 
443
1040
  class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
1041
+ """Context manager for batch-uploading files to a Volume version 2."""
1042
+
444
1043
  _volume_id: str
445
1044
  _client: modal.client._Client
446
1045
  _progress_cb: collections.abc.Callable[..., typing.Any]
@@ -463,7 +1062,10 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
463
1062
  force: bool = False,
464
1063
  hash_concurrency: int = 4,
465
1064
  put_concurrency: int = 128,
466
- ): ...
1065
+ ):
1066
+ """mdmd:hidden"""
1067
+ ...
1068
+
467
1069
  async def __aenter__(self): ...
468
1070
  async def __aexit__(self, exc_type, exc_val, exc_tb): ...
469
1071
  def put_file(
@@ -471,16 +1073,32 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
471
1073
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
472
1074
  remote_path: typing.Union[pathlib.PurePosixPath, str],
473
1075
  mode: typing.Optional[int] = None,
474
- ): ...
1076
+ ):
1077
+ """Upload a file from a local file or file-like object.
1078
+
1079
+ Will create any needed parent directories automatically.
1080
+
1081
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
1082
+ """
1083
+ ...
1084
+
475
1085
  def put_directory(
476
1086
  self,
477
1087
  local_path: typing.Union[pathlib.Path, str],
478
1088
  remote_path: typing.Union[pathlib.PurePosixPath, str],
479
1089
  recursive: bool = True,
480
- ): ...
1090
+ ):
1091
+ """Upload all files in a local directory.
1092
+
1093
+ Will create any needed parent directories automatically.
1094
+ """
1095
+ ...
1096
+
481
1097
  async def _put_file_specs(self, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
482
1098
 
483
1099
  class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
1100
+ """Context manager for batch-uploading files to a Volume version 2."""
1101
+
484
1102
  _volume_id: str
485
1103
  _client: modal.client.Client
486
1104
  _progress_cb: collections.abc.Callable[..., typing.Any]
@@ -503,7 +1121,10 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
503
1121
  force: bool = False,
504
1122
  hash_concurrency: int = 4,
505
1123
  put_concurrency: int = 128,
506
- ): ...
1124
+ ):
1125
+ """mdmd:hidden"""
1126
+ ...
1127
+
507
1128
  def __enter__(self): ...
508
1129
  async def __aenter__(self): ...
509
1130
  def __exit__(self, exc_type, exc_val, exc_tb): ...
@@ -513,13 +1134,26 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
513
1134
  local_file: typing.Union[pathlib.Path, str, typing.BinaryIO, _io.BytesIO],
514
1135
  remote_path: typing.Union[pathlib.PurePosixPath, str],
515
1136
  mode: typing.Optional[int] = None,
516
- ): ...
1137
+ ):
1138
+ """Upload a file from a local file or file-like object.
1139
+
1140
+ Will create any needed parent directories automatically.
1141
+
1142
+ If `local_file` is a file-like object it must remain readable for the lifetime of the batch.
1143
+ """
1144
+ ...
1145
+
517
1146
  def put_directory(
518
1147
  self,
519
1148
  local_path: typing.Union[pathlib.Path, str],
520
1149
  remote_path: typing.Union[pathlib.PurePosixPath, str],
521
1150
  recursive: bool = True,
522
- ): ...
1151
+ ):
1152
+ """Upload all files in a local directory.
1153
+
1154
+ Will create any needed parent directories automatically.
1155
+ """
1156
+ ...
523
1157
 
524
1158
  class ___put_file_specs_spec(typing_extensions.Protocol[SUPERSELF]):
525
1159
  def __call__(self, /, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...