modal 0.72.4__py3-none-any.whl → 0.72.48__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 (73) hide show
  1. modal/_container_entrypoint.py +5 -10
  2. modal/_object.py +297 -0
  3. modal/_resolver.py +7 -5
  4. modal/_runtime/container_io_manager.py +0 -11
  5. modal/_runtime/user_code_imports.py +7 -7
  6. modal/_serialization.py +4 -3
  7. modal/_tunnel.py +1 -1
  8. modal/app.py +14 -61
  9. modal/app.pyi +25 -25
  10. modal/cli/app.py +3 -2
  11. modal/cli/container.py +1 -1
  12. modal/cli/import_refs.py +185 -113
  13. modal/cli/launch.py +10 -5
  14. modal/cli/programs/run_jupyter.py +2 -2
  15. modal/cli/programs/vscode.py +3 -3
  16. modal/cli/run.py +134 -68
  17. modal/client.py +1 -0
  18. modal/client.pyi +18 -14
  19. modal/cloud_bucket_mount.py +4 -0
  20. modal/cloud_bucket_mount.pyi +4 -0
  21. modal/cls.py +33 -5
  22. modal/cls.pyi +20 -5
  23. modal/container_process.pyi +8 -6
  24. modal/dict.py +1 -1
  25. modal/dict.pyi +32 -29
  26. modal/environments.py +1 -1
  27. modal/environments.pyi +2 -1
  28. modal/experimental.py +47 -11
  29. modal/experimental.pyi +29 -0
  30. modal/file_io.pyi +30 -28
  31. modal/file_pattern_matcher.py +32 -25
  32. modal/functions.py +31 -23
  33. modal/functions.pyi +57 -50
  34. modal/gpu.py +19 -26
  35. modal/image.py +47 -19
  36. modal/image.pyi +28 -21
  37. modal/io_streams.pyi +14 -12
  38. modal/mount.py +14 -5
  39. modal/mount.pyi +28 -25
  40. modal/network_file_system.py +7 -7
  41. modal/network_file_system.pyi +27 -24
  42. modal/object.py +2 -265
  43. modal/object.pyi +46 -130
  44. modal/parallel_map.py +2 -2
  45. modal/parallel_map.pyi +10 -7
  46. modal/partial_function.py +22 -3
  47. modal/partial_function.pyi +45 -27
  48. modal/proxy.py +1 -1
  49. modal/proxy.pyi +2 -1
  50. modal/queue.py +1 -1
  51. modal/queue.pyi +26 -23
  52. modal/runner.py +14 -3
  53. modal/sandbox.py +11 -7
  54. modal/sandbox.pyi +30 -27
  55. modal/secret.py +1 -1
  56. modal/secret.pyi +2 -1
  57. modal/token_flow.pyi +6 -4
  58. modal/volume.py +1 -1
  59. modal/volume.pyi +36 -33
  60. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/METADATA +2 -2
  61. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/RECORD +73 -71
  62. modal_proto/api.proto +151 -4
  63. modal_proto/api_grpc.py +113 -0
  64. modal_proto/api_pb2.py +998 -795
  65. modal_proto/api_pb2.pyi +430 -11
  66. modal_proto/api_pb2_grpc.py +233 -1
  67. modal_proto/api_pb2_grpc.pyi +75 -3
  68. modal_proto/modal_api_grpc.py +7 -0
  69. modal_version/_version_generated.py +1 -1
  70. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/LICENSE +0 -0
  71. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/WHEEL +0 -0
  72. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/entry_points.txt +0 -0
  73. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/top_level.txt +0 -0
modal/image.py CHANGED
@@ -26,6 +26,7 @@ from grpclib.exceptions import GRPCError, StreamTerminatedError
26
26
 
27
27
  from modal_proto import api_pb2
28
28
 
29
+ from ._object import _Object, live_method_gen
29
30
  from ._resolver import Resolver
30
31
  from ._serialization import serialize
31
32
  from ._utils.async_utils import synchronize_api
@@ -46,7 +47,6 @@ from .file_pattern_matcher import NON_PYTHON_FILES, FilePatternMatcher, _ignore_
46
47
  from .gpu import GPU_T, parse_gpu_config
47
48
  from .mount import _Mount, python_standalone_mount_name
48
49
  from .network_file_system import _NetworkFileSystem
49
- from .object import _Object, live_method_gen
50
50
  from .output import _get_output_manager
51
51
  from .scheduler_placement import SchedulerPlacement
52
52
  from .secret import _Secret
@@ -82,6 +82,11 @@ class _AutoDockerIgnoreSentinel:
82
82
 
83
83
  AUTO_DOCKERIGNORE = _AutoDockerIgnoreSentinel()
84
84
 
85
+ COPY_DEPRECATION_MESSAGE_PATTERN = """modal.Image.copy_* methods will soon be deprecated.
86
+
87
+ Use {replacement} instead, which is functionally and performance-wise equivalent.
88
+ """
89
+
85
90
 
86
91
  def _validate_python_version(
87
92
  python_version: Optional[str], builder_version: ImageBuilderVersion, allow_micro_granularity: bool = True
@@ -657,13 +662,16 @@ class _Image(_Object, type_prefix="im"):
657
662
  return obj
658
663
 
659
664
  def copy_mount(self, mount: _Mount, remote_path: Union[str, Path] = ".") -> "_Image":
660
- """Copy the entire contents of a `modal.Mount` into an image.
665
+ """
666
+ **Deprecated**: Use image.add_local_dir(..., copy=True) or similar instead.
667
+
668
+ Copy the entire contents of a `modal.Mount` into an image.
661
669
  Useful when files only available locally are required during the image
662
670
  build process.
663
671
 
664
672
  **Example**
665
673
 
666
- ```python
674
+ ```python notest
667
675
  static_images_dir = "./static"
668
676
  # place all static images in root of mount
669
677
  mount = modal.Mount.from_local_dir(static_images_dir, remote_path="/")
@@ -736,7 +744,6 @@ class _Image(_Object, type_prefix="im"):
736
744
  **Usage:**
737
745
 
738
746
  ```python
739
- from pathlib import Path
740
747
  from modal import FilePatternMatcher
741
748
 
742
749
  image = modal.Image.debian_slim().add_local_dir(
@@ -768,7 +775,7 @@ class _Image(_Object, type_prefix="im"):
768
775
  image = modal.Image.debian_slim().add_local_dir(
769
776
  "~/assets",
770
777
  remote_path="/assets",
771
- ignore=FilePatternMatcher.from_file(Path("/path/to/ignorefile")),
778
+ ignore=FilePatternMatcher.from_file("/path/to/ignorefile"),
772
779
  )
773
780
  ```
774
781
  """
@@ -786,7 +793,9 @@ class _Image(_Object, type_prefix="im"):
786
793
  This works in a similar way to [`COPY`](https://docs.docker.com/engine/reference/builder/#copy)
787
794
  works in a `Dockerfile`.
788
795
  """
789
- # TODO(elias): add pending deprecation with suggestion to use add_* instead
796
+ deprecation_warning(
797
+ (2024, 1, 13), COPY_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_file"), pending=True
798
+ )
790
799
  basename = str(Path(local_path).name)
791
800
 
792
801
  def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
@@ -845,14 +854,17 @@ class _Image(_Object, type_prefix="im"):
845
854
  # Which follows dockerignore syntax.
846
855
  ignore: Union[Sequence[str], Callable[[Path], bool]] = [],
847
856
  ) -> "_Image":
848
- """Copy a directory into the image as a part of building the image.
857
+ """
858
+ **Deprecated**: Use image.add_local_dir instead
859
+
860
+ Copy a directory into the image as a part of building the image.
849
861
 
850
862
  This works in a similar way to [`COPY`](https://docs.docker.com/engine/reference/builder/#copy)
851
863
  works in a `Dockerfile`.
852
864
 
853
865
  **Usage:**
854
866
 
855
- ```python
867
+ ```python notest
856
868
  from pathlib import Path
857
869
  from modal import FilePatternMatcher
858
870
 
@@ -885,10 +897,13 @@ class _Image(_Object, type_prefix="im"):
885
897
  image = modal.Image.debian_slim().copy_local_dir(
886
898
  "~/assets",
887
899
  remote_path="/assets",
888
- ignore=FilePatternMatcher.from_file(Path("/path/to/ignorefile")),
900
+ ignore=FilePatternMatcher.from_file("/path/to/ignorefile"),
889
901
  )
890
902
  ```
891
903
  """
904
+ deprecation_warning(
905
+ (2024, 1, 13), COPY_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_dir"), pending=True
906
+ )
892
907
 
893
908
  def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
894
909
  return DockerfileSpec(commands=["FROM base", f"COPY . {remote_path}"], context_files={})
@@ -1282,7 +1297,6 @@ class _Image(_Object, type_prefix="im"):
1282
1297
  **Usage:**
1283
1298
 
1284
1299
  ```python
1285
- from pathlib import Path
1286
1300
  from modal import FilePatternMatcher
1287
1301
 
1288
1302
  # By default a .dockerignore file is used if present in the current working directory
@@ -1314,10 +1328,17 @@ class _Image(_Object, type_prefix="im"):
1314
1328
  # You can also read ignore patterns from a file.
1315
1329
  image = modal.Image.debian_slim().dockerfile_commands(
1316
1330
  ["COPY data /data"],
1317
- ignore=FilePatternMatcher.from_file(Path("/path/to/dockerignore")),
1331
+ ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
1318
1332
  )
1319
1333
  ```
1320
1334
  """
1335
+ if context_mount is not None:
1336
+ deprecation_warning(
1337
+ (2025, 1, 13),
1338
+ "`context_mount` is deprecated."
1339
+ + " Files are now automatically added to the build context based on the commands.",
1340
+ pending=True,
1341
+ )
1321
1342
  cmds = _flatten_str_args("dockerfile_commands", "dockerfile_commands", dockerfile_commands)
1322
1343
  if not cmds:
1323
1344
  return self
@@ -1506,7 +1527,8 @@ class _Image(_Object, type_prefix="im"):
1506
1527
  "COPY /python/. /usr/local",
1507
1528
  "ENV TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo:/usr/lib/terminfo",
1508
1529
  ]
1509
- if add_python < "3.13":
1530
+ python_minor = add_python.split(".")[1]
1531
+ if int(python_minor) < 13:
1510
1532
  # Previous versions did not include the `python` binary, but later ones do.
1511
1533
  # (The important factor is not the Python version itself, but the standalone dist version.)
1512
1534
  # We insert the command in the list at the position it was previously always added
@@ -1712,7 +1734,6 @@ class _Image(_Object, type_prefix="im"):
1712
1734
  **Usage:**
1713
1735
 
1714
1736
  ```python
1715
- from pathlib import Path
1716
1737
  from modal import FilePatternMatcher
1717
1738
 
1718
1739
  # By default a .dockerignore file is used if present in the current working directory
@@ -1750,10 +1771,17 @@ class _Image(_Object, type_prefix="im"):
1750
1771
  image = modal.Image.from_dockerfile(
1751
1772
  "./Dockerfile",
1752
1773
  add_python="3.12",
1753
- ignore=FilePatternMatcher.from_file(Path("/path/to/dockerignore")),
1774
+ ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
1754
1775
  )
1755
1776
  ```
1756
1777
  """
1778
+ if context_mount is not None:
1779
+ deprecation_warning(
1780
+ (2025, 1, 13),
1781
+ "`context_mount` is deprecated."
1782
+ + " Files are now automatically added to the build context based on the commands in the Dockerfile.",
1783
+ pending=True,
1784
+ )
1757
1785
 
1758
1786
  # --- Build the base dockerfile
1759
1787
 
@@ -2025,11 +2053,11 @@ class _Image(_Object, type_prefix="im"):
2025
2053
  try:
2026
2054
  yield
2027
2055
  except Exception as exc:
2028
- if self.object_id is None:
2029
- # Might be initialized later
2056
+ if not self.is_hydrated:
2057
+ # Might be hydrated later
2030
2058
  self.inside_exceptions.append(exc)
2031
2059
  elif env_image_id == self.object_id:
2032
- # Image is already initialized (we can remove this case later
2060
+ # Image is already hydrated (we can remove this case later
2033
2061
  # when we don't hydrate objects so early)
2034
2062
  raise
2035
2063
  if not isinstance(exc, ImportError):
@@ -2044,9 +2072,9 @@ class _Image(_Object, type_prefix="im"):
2044
2072
  last_entry_id: str = ""
2045
2073
 
2046
2074
  request = api_pb2.ImageJoinStreamingRequest(
2047
- image_id=self._object_id, timeout=55, last_entry_id=last_entry_id, include_logs_for_finished=True
2075
+ image_id=self.object_id, timeout=55, last_entry_id=last_entry_id, include_logs_for_finished=True
2048
2076
  )
2049
- async for response in self._client.stub.ImageJoinStreaming.unary_stream(request):
2077
+ async for response in self.client.stub.ImageJoinStreaming.unary_stream(request):
2050
2078
  if response.result.status:
2051
2079
  return
2052
2080
  if response.entry_id:
modal/image.pyi CHANGED
@@ -1,5 +1,6 @@
1
1
  import collections.abc
2
2
  import google.protobuf.message
3
+ import modal._object
3
4
  import modal.client
4
5
  import modal.cloud_bucket_mount
5
6
  import modal.functions
@@ -52,11 +53,11 @@ def _get_image_builder_version(
52
53
  ) -> typing.Literal["2023.12", "2024.04", "2024.10"]: ...
53
54
  def _create_context_mount(
54
55
  docker_commands: collections.abc.Sequence[str],
55
- ignore_fn: typing.Callable[[pathlib.Path], bool],
56
+ ignore_fn: collections.abc.Callable[[pathlib.Path], bool],
56
57
  context_dir: pathlib.Path,
57
58
  ) -> typing.Optional[modal.mount._Mount]: ...
58
59
  def _create_context_mount_function(
59
- ignore: typing.Union[collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]],
60
+ ignore: typing.Union[collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]],
60
61
  dockerfile_cmds: list[str] = [],
61
62
  dockerfile_path: typing.Optional[pathlib.Path] = None,
62
63
  context_mount: typing.Optional[modal.mount._Mount] = None,
@@ -78,7 +79,7 @@ async def _image_await_build_result(
78
79
  image_id: str, client: modal.client._Client
79
80
  ) -> modal_proto.api_pb2.ImageJoinStreamingResponse: ...
80
81
 
81
- class _Image(modal.object._Object):
82
+ class _Image(modal._object._Object):
82
83
  force_build: bool
83
84
  inside_exceptions: list[Exception]
84
85
  _serve_mounts: frozenset[modal.mount._Mount]
@@ -97,14 +98,16 @@ class _Image(modal.object._Object):
97
98
  *,
98
99
  base_images: typing.Optional[dict[str, _Image]] = None,
99
100
  dockerfile_function: typing.Optional[
100
- typing.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
101
+ collections.abc.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
101
102
  ] = None,
102
103
  secrets: typing.Optional[collections.abc.Sequence[modal.secret._Secret]] = None,
103
104
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
104
105
  build_function: typing.Optional[modal.functions._Function] = None,
105
106
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
106
107
  image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
107
- context_mount_function: typing.Optional[typing.Callable[[], typing.Optional[modal.mount._Mount]]] = None,
108
+ context_mount_function: typing.Optional[
109
+ collections.abc.Callable[[], typing.Optional[modal.mount._Mount]]
110
+ ] = None,
108
111
  force_build: bool = False,
109
112
  _namespace: int = 1,
110
113
  _do_assert_no_mount_layers: bool = True,
@@ -119,7 +122,7 @@ class _Image(modal.object._Object):
119
122
  remote_path: str,
120
123
  *,
121
124
  copy: bool = False,
122
- ignore: typing.Union[collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]] = [],
125
+ ignore: typing.Union[collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]] = [],
123
126
  ) -> _Image: ...
124
127
  def copy_local_file(
125
128
  self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
@@ -129,14 +132,14 @@ class _Image(modal.object._Object):
129
132
  *module_names: str,
130
133
  copy: bool = False,
131
134
  ignore: typing.Union[
132
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
135
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
133
136
  ] = modal.file_pattern_matcher.NON_PYTHON_FILES,
134
137
  ) -> _Image: ...
135
138
  def copy_local_dir(
136
139
  self,
137
140
  local_path: typing.Union[str, pathlib.Path],
138
141
  remote_path: typing.Union[str, pathlib.Path] = ".",
139
- ignore: typing.Union[collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]] = [],
142
+ ignore: typing.Union[collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]] = [],
140
143
  ) -> _Image: ...
141
144
  @staticmethod
142
145
  async def from_id(image_id: str, client: typing.Optional[modal.client._Client] = None) -> _Image: ...
@@ -215,7 +218,7 @@ class _Image(modal.object._Object):
215
218
  context_mount: typing.Optional[modal.mount._Mount] = None,
216
219
  force_build: bool = False,
217
220
  ignore: typing.Union[
218
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
221
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
219
222
  ] = modal.image.AUTO_DOCKERIGNORE,
220
223
  ) -> _Image: ...
221
224
  def entrypoint(self, entrypoint_commands: list[str]) -> _Image: ...
@@ -303,7 +306,7 @@ class _Image(modal.object._Object):
303
306
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
304
307
  add_python: typing.Optional[str] = None,
305
308
  ignore: typing.Union[
306
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
309
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
307
310
  ] = modal.image.AUTO_DOCKERIGNORE,
308
311
  ) -> _Image: ...
309
312
  @staticmethod
@@ -317,7 +320,7 @@ class _Image(modal.object._Object):
317
320
  ) -> _Image: ...
318
321
  def run_function(
319
322
  self,
320
- raw_f: typing.Callable[..., typing.Any],
323
+ raw_f: collections.abc.Callable[..., typing.Any],
321
324
  secrets: collections.abc.Sequence[modal.secret._Secret] = (),
322
325
  gpu: typing.Union[
323
326
  None, bool, str, modal.gpu._GPUConfig, list[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
@@ -344,6 +347,8 @@ class _Image(modal.object._Object):
344
347
  def imports(self): ...
345
348
  def _logs(self) -> typing.AsyncGenerator[str, None]: ...
346
349
 
350
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
351
+
347
352
  class Image(modal.object.Object):
348
353
  force_build: bool
349
354
  inside_exceptions: list[Exception]
@@ -364,14 +369,16 @@ class Image(modal.object.Object):
364
369
  *,
365
370
  base_images: typing.Optional[dict[str, Image]] = None,
366
371
  dockerfile_function: typing.Optional[
367
- typing.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
372
+ collections.abc.Callable[[typing.Literal["2023.12", "2024.04", "2024.10"]], DockerfileSpec]
368
373
  ] = None,
369
374
  secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
370
375
  gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
371
376
  build_function: typing.Optional[modal.functions.Function] = None,
372
377
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
373
378
  image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
374
- context_mount_function: typing.Optional[typing.Callable[[], typing.Optional[modal.mount.Mount]]] = None,
379
+ context_mount_function: typing.Optional[
380
+ collections.abc.Callable[[], typing.Optional[modal.mount.Mount]]
381
+ ] = None,
375
382
  force_build: bool = False,
376
383
  _namespace: int = 1,
377
384
  _do_assert_no_mount_layers: bool = True,
@@ -386,7 +393,7 @@ class Image(modal.object.Object):
386
393
  remote_path: str,
387
394
  *,
388
395
  copy: bool = False,
389
- ignore: typing.Union[collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]] = [],
396
+ ignore: typing.Union[collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]] = [],
390
397
  ) -> Image: ...
391
398
  def copy_local_file(
392
399
  self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
@@ -396,14 +403,14 @@ class Image(modal.object.Object):
396
403
  *module_names: str,
397
404
  copy: bool = False,
398
405
  ignore: typing.Union[
399
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
406
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
400
407
  ] = modal.file_pattern_matcher.NON_PYTHON_FILES,
401
408
  ) -> Image: ...
402
409
  def copy_local_dir(
403
410
  self,
404
411
  local_path: typing.Union[str, pathlib.Path],
405
412
  remote_path: typing.Union[str, pathlib.Path] = ".",
406
- ignore: typing.Union[collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]] = [],
413
+ ignore: typing.Union[collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]] = [],
407
414
  ) -> Image: ...
408
415
 
409
416
  class __from_id_spec(typing_extensions.Protocol):
@@ -487,7 +494,7 @@ class Image(modal.object.Object):
487
494
  context_mount: typing.Optional[modal.mount.Mount] = None,
488
495
  force_build: bool = False,
489
496
  ignore: typing.Union[
490
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
497
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
491
498
  ] = modal.image.AUTO_DOCKERIGNORE,
492
499
  ) -> Image: ...
493
500
  def entrypoint(self, entrypoint_commands: list[str]) -> Image: ...
@@ -575,7 +582,7 @@ class Image(modal.object.Object):
575
582
  gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
576
583
  add_python: typing.Optional[str] = None,
577
584
  ignore: typing.Union[
578
- collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
585
+ collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
579
586
  ] = modal.image.AUTO_DOCKERIGNORE,
580
587
  ) -> Image: ...
581
588
  @staticmethod
@@ -589,7 +596,7 @@ class Image(modal.object.Object):
589
596
  ) -> Image: ...
590
597
  def run_function(
591
598
  self,
592
- raw_f: typing.Callable[..., typing.Any],
599
+ raw_f: collections.abc.Callable[..., typing.Any],
593
600
  secrets: collections.abc.Sequence[modal.secret.Secret] = (),
594
601
  gpu: typing.Union[
595
602
  None, bool, str, modal.gpu._GPUConfig, list[typing.Union[None, bool, str, modal.gpu._GPUConfig]]
@@ -615,10 +622,10 @@ class Image(modal.object.Object):
615
622
  def workdir(self, path: typing.Union[str, pathlib.PurePosixPath]) -> Image: ...
616
623
  def imports(self): ...
617
624
 
618
- class ___logs_spec(typing_extensions.Protocol):
625
+ class ___logs_spec(typing_extensions.Protocol[SUPERSELF]):
619
626
  def __call__(self) -> typing.Generator[str, None, None]: ...
620
627
  def aio(self) -> typing.AsyncGenerator[str, None]: ...
621
628
 
622
- _logs: ___logs_spec
629
+ _logs: ___logs_spec[typing_extensions.Self]
623
630
 
624
631
  SUPPORTED_PYTHON_SERIES: dict[typing.Literal["2023.12", "2024.04", "2024.10"], list[str]]
modal/io_streams.pyi CHANGED
@@ -50,6 +50,8 @@ class _StreamWriter:
50
50
 
51
51
  T_INNER = typing.TypeVar("T_INNER", covariant=True)
52
52
 
53
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
54
+
53
55
  class StreamReader(typing.Generic[T]):
54
56
  _stream: typing.Optional[collections.abc.AsyncGenerator[typing.Optional[bytes], None]]
55
57
 
@@ -66,25 +68,25 @@ class StreamReader(typing.Generic[T]):
66
68
  @property
67
69
  def file_descriptor(self) -> int: ...
68
70
 
69
- class __read_spec(typing_extensions.Protocol[T_INNER]):
71
+ class __read_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
70
72
  def __call__(self) -> T_INNER: ...
71
73
  async def aio(self) -> T_INNER: ...
72
74
 
73
- read: __read_spec[T]
75
+ read: __read_spec[T, typing_extensions.Self]
74
76
 
75
- class ___consume_container_process_stream_spec(typing_extensions.Protocol):
77
+ class ___consume_container_process_stream_spec(typing_extensions.Protocol[SUPERSELF]):
76
78
  def __call__(self): ...
77
79
  async def aio(self): ...
78
80
 
79
- _consume_container_process_stream: ___consume_container_process_stream_spec
81
+ _consume_container_process_stream: ___consume_container_process_stream_spec[typing_extensions.Self]
80
82
 
81
- class ___stream_container_process_spec(typing_extensions.Protocol):
83
+ class ___stream_container_process_spec(typing_extensions.Protocol[SUPERSELF]):
82
84
  def __call__(self) -> typing.Generator[tuple[typing.Optional[bytes], str], None, None]: ...
83
85
  def aio(self) -> collections.abc.AsyncGenerator[tuple[typing.Optional[bytes], str], None]: ...
84
86
 
85
- _stream_container_process: ___stream_container_process_spec
87
+ _stream_container_process: ___stream_container_process_spec[typing_extensions.Self]
86
88
 
87
- class ___get_logs_spec(typing_extensions.Protocol):
89
+ class ___get_logs_spec(typing_extensions.Protocol[SUPERSELF]):
88
90
  def __call__(
89
91
  self, skip_empty_messages: bool = True
90
92
  ) -> typing.Generator[typing.Optional[bytes], None, None]: ...
@@ -92,13 +94,13 @@ class StreamReader(typing.Generic[T]):
92
94
  self, skip_empty_messages: bool = True
93
95
  ) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]: ...
94
96
 
95
- _get_logs: ___get_logs_spec
97
+ _get_logs: ___get_logs_spec[typing_extensions.Self]
96
98
 
97
- class ___get_logs_by_line_spec(typing_extensions.Protocol):
99
+ class ___get_logs_by_line_spec(typing_extensions.Protocol[SUPERSELF]):
98
100
  def __call__(self) -> typing.Generator[typing.Optional[bytes], None, None]: ...
99
101
  def aio(self) -> collections.abc.AsyncGenerator[typing.Optional[bytes], None]: ...
100
102
 
101
- _get_logs_by_line: ___get_logs_by_line_spec
103
+ _get_logs_by_line: ___get_logs_by_line_spec[typing_extensions.Self]
102
104
 
103
105
  def __iter__(self) -> typing.Iterator[T]: ...
104
106
  def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
@@ -115,8 +117,8 @@ class StreamWriter:
115
117
  def write(self, data: typing.Union[bytes, bytearray, memoryview, str]) -> None: ...
116
118
  def write_eof(self) -> None: ...
117
119
 
118
- class __drain_spec(typing_extensions.Protocol):
120
+ class __drain_spec(typing_extensions.Protocol[SUPERSELF]):
119
121
  def __call__(self) -> None: ...
120
122
  async def aio(self) -> None: ...
121
123
 
122
- drain: __drain_spec
124
+ drain: __drain_spec[typing_extensions.Self]
modal/mount.py CHANGED
@@ -20,6 +20,7 @@ import modal.file_pattern_matcher
20
20
  from modal_proto import api_pb2
21
21
  from modal_version import __version__
22
22
 
23
+ from ._object import _get_environment_name, _Object
23
24
  from ._resolver import Resolver
24
25
  from ._utils.async_utils import aclosing, async_map, synchronize_api
25
26
  from ._utils.blob_utils import FileUploadSpec, blob_upload_file, get_file_upload_spec_from_path
@@ -31,7 +32,6 @@ from .client import _Client
31
32
  from .config import config, logger
32
33
  from .exception import InvalidError, ModuleNotMountable
33
34
  from .file_pattern_matcher import FilePatternMatcher
34
- from .object import _get_environment_name, _Object
35
35
 
36
36
  ROOT_DIR: PurePosixPath = PurePosixPath("/root")
37
37
  MOUNT_PUT_FILE_CLIENT_TIMEOUT = 10 * 60 # 10 min max for transferring files
@@ -258,12 +258,15 @@ class NonLocalMountError(Exception):
258
258
 
259
259
 
260
260
  class _Mount(_Object, type_prefix="mo"):
261
- """Create a mount for a local directory or file that can be attached
261
+ """
262
+ **Deprecated**: Mounts should not be used explicitly anymore, use `Image.add_local_*` commands instead.
263
+
264
+ Create a mount for a local directory or file that can be attached
262
265
  to one or more Modal functions.
263
266
 
264
267
  **Usage**
265
268
 
266
- ```python
269
+ ```python notest
267
270
  import modal
268
271
  import os
269
272
  app = modal.App()
@@ -394,11 +397,13 @@ class _Mount(_Object, type_prefix="mo"):
394
397
  recursive: bool = True,
395
398
  ) -> "_Mount":
396
399
  """
400
+ **Deprecated:** Use image.add_local_dir() instead
401
+
397
402
  Create a `Mount` from a local directory.
398
403
 
399
404
  **Usage**
400
405
 
401
- ```python
406
+ ```python notest
402
407
  assets = modal.Mount.from_local_dir(
403
408
  "~/assets",
404
409
  condition=lambda pth: not ".venv" in pth,
@@ -449,11 +454,13 @@ class _Mount(_Object, type_prefix="mo"):
449
454
  @staticmethod
450
455
  def from_local_file(local_path: Union[str, Path], remote_path: Union[str, PurePosixPath, None] = None) -> "_Mount":
451
456
  """
457
+ **Deprecated**: Use image.add_local_file() instead
458
+
452
459
  Create a `Mount` mounting a single local file.
453
460
 
454
461
  **Usage**
455
462
 
456
- ```python
463
+ ```python notest
457
464
  # Mount the DBT profile in user's home directory into container.
458
465
  dbt_profiles = modal.Mount.from_local_file(
459
466
  local_path="~/profiles.yml",
@@ -611,6 +618,8 @@ class _Mount(_Object, type_prefix="mo"):
611
618
  ignore: Optional[Union[Sequence[str], Callable[[Path], bool]]] = None,
612
619
  ) -> "_Mount":
613
620
  """
621
+ **Deprecated**: Use image.add_local_python_source instead
622
+
614
623
  Returns a `modal.Mount` that makes local modules listed in `module_names` available inside the container.
615
624
  This works by mounting the local path of each module's package to a directory inside the container
616
625
  that's on `PYTHONPATH`.