modal 0.72.20__py3-none-any.whl → 0.72.22__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
modal/functions.py CHANGED
@@ -26,6 +26,7 @@ from modal_proto import api_pb2
26
26
  from modal_proto.modal_api_grpc import ModalClientModal
27
27
 
28
28
  from ._location import parse_cloud_provider
29
+ from ._object import _get_environment_name, _Object, live_method, live_method_gen
29
30
  from ._pty import get_pty_info
30
31
  from ._resolver import Resolver
31
32
  from ._resources import convert_fn_config_to_resources_config
@@ -71,7 +72,6 @@ from .gpu import GPU_T, parse_gpu_config
71
72
  from .image import _Image
72
73
  from .mount import _get_client_mount, _Mount, get_auto_mounts
73
74
  from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
74
- from .object import _get_environment_name, _Object, live_method, live_method_gen
75
75
  from .output import _get_output_manager
76
76
  from .parallel_map import (
77
77
  _for_each_async,
@@ -388,7 +388,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
388
388
  _is_method: bool
389
389
  _spec: Optional[_FunctionSpec] = None
390
390
  _tag: str
391
- _raw_f: Callable[..., Any]
391
+ _raw_f: Optional[Callable[..., Any]] # this is set to None for a "class service [function]"
392
392
  _build_args: dict
393
393
 
394
394
  _is_generator: Optional[bool] = None
@@ -474,7 +474,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
474
474
  _experimental_buffer_containers: Optional[int] = None,
475
475
  _experimental_proxy_ip: Optional[str] = None,
476
476
  _experimental_custom_scaling_factor: Optional[float] = None,
477
- ) -> None:
477
+ ) -> "_Function":
478
478
  """mdmd:hidden"""
479
479
  # Needed to avoid circular imports
480
480
  from .partial_function import _find_partial_methods_for_user_cls, _PartialFunctionFlags
@@ -573,7 +573,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
573
573
  )
574
574
  image = _Image._from_args(
575
575
  base_images={"base": image},
576
- build_function=snapshot_function,
576
+ build_function=snapshot_function, # type: ignore # TODO: separate functions.py and _functions.py
577
577
  force_build=image.force_build or pf.force_build,
578
578
  )
579
579
 
@@ -962,7 +962,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
962
962
  f"The {identity} has not been hydrated with the metadata it needs to run on Modal{reason}."
963
963
  )
964
964
 
965
- assert parent._client.stub
965
+ assert parent._client and parent._client.stub
966
966
 
967
967
  if can_use_parent:
968
968
  # We can end up here if parent wasn't hydrated when class was instantiated, but has been since.
@@ -983,9 +983,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
983
983
  else:
984
984
  serialized_params = serialize((args, kwargs))
985
985
  environment_name = _get_environment_name(None, resolver)
986
- assert parent is not None
986
+ assert parent is not None and parent.is_hydrated
987
987
  req = api_pb2.FunctionBindParamsRequest(
988
- function_id=parent._object_id,
988
+ function_id=parent.object_id,
989
989
  serialized_params=serialized_params,
990
990
  function_options=options,
991
991
  environment_name=environment_name
@@ -1032,11 +1032,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1032
1032
  """
1033
1033
  )
1034
1034
  )
1035
- assert self._client and self._client.stub
1036
1035
  request = api_pb2.FunctionUpdateSchedulingParamsRequest(
1037
- function_id=self._object_id, warm_pool_size_override=warm_pool_size
1036
+ function_id=self.object_id, warm_pool_size_override=warm_pool_size
1038
1037
  )
1039
- await retry_transient_errors(self._client.stub.FunctionUpdateSchedulingParams, request)
1038
+ await retry_transient_errors(self.client.stub.FunctionUpdateSchedulingParams, request)
1040
1039
 
1041
1040
  @classmethod
1042
1041
  @renamed_parameter((2024, 12, 18), "tag", "name")
@@ -1142,7 +1141,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1142
1141
  """mdmd:hidden"""
1143
1142
  # Plaintext source and arg definition for the function, so it's part of the image
1144
1143
  # hash. We can't use the cloudpickle hash because it's not very stable.
1145
- assert hasattr(self, "_raw_f") and hasattr(self, "_build_args")
1144
+ assert hasattr(self, "_raw_f") and hasattr(self, "_build_args") and self._raw_f is not None
1146
1145
  return f"{inspect.getsource(self._raw_f)}\n{repr(self._build_args)}"
1147
1146
 
1148
1147
  # Live handle methods
@@ -1248,7 +1247,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1248
1247
  _map_invocation(
1249
1248
  self, # type: ignore
1250
1249
  input_queue,
1251
- self._client,
1250
+ self.client,
1252
1251
  order_outputs,
1253
1252
  return_exceptions,
1254
1253
  count_update_callback,
@@ -1266,7 +1265,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1266
1265
  self,
1267
1266
  args,
1268
1267
  kwargs,
1269
- client=self._client,
1268
+ client=self.client,
1270
1269
  function_call_invocation_type=function_call_invocation_type,
1271
1270
  )
1272
1271
 
@@ -1276,7 +1275,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1276
1275
  self, args, kwargs, function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType"
1277
1276
  ) -> _Invocation:
1278
1277
  return await _Invocation.create(
1279
- self, args, kwargs, client=self._client, function_call_invocation_type=function_call_invocation_type
1278
+ self, args, kwargs, client=self.client, function_call_invocation_type=function_call_invocation_type
1280
1279
  )
1281
1280
 
1282
1281
  @warn_if_generator_is_not_consumed()
@@ -1287,7 +1286,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1287
1286
  self,
1288
1287
  args,
1289
1288
  kwargs,
1290
- client=self._client,
1289
+ client=self.client,
1291
1290
  function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_SYNC_LEGACY,
1292
1291
  )
1293
1292
  async for res in invocation.run_generator():
@@ -1303,7 +1302,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1303
1302
  self,
1304
1303
  args,
1305
1304
  kwargs,
1306
- client=self._client,
1305
+ client=self.client,
1307
1306
  function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC_LEGACY,
1308
1307
  )
1309
1308
 
@@ -1452,14 +1451,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1452
1451
 
1453
1452
  def get_raw_f(self) -> Callable[..., Any]:
1454
1453
  """Return the inner Python object wrapped by this Modal Function."""
1454
+ assert self._raw_f is not None
1455
1455
  return self._raw_f
1456
1456
 
1457
1457
  @live_method
1458
1458
  async def get_current_stats(self) -> FunctionStats:
1459
1459
  """Return a `FunctionStats` object describing the current function's queue and runner counts."""
1460
- assert self._client.stub
1461
1460
  resp = await retry_transient_errors(
1462
- self._client.stub.FunctionGetCurrentStats,
1461
+ self.client.stub.FunctionGetCurrentStats,
1463
1462
  api_pb2.FunctionGetCurrentStatsRequest(function_id=self.object_id),
1464
1463
  total_timeout=10.0,
1465
1464
  )
@@ -1491,8 +1490,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
1491
1490
  _is_generator: bool = False
1492
1491
 
1493
1492
  def _invocation(self):
1494
- assert self._client.stub
1495
- return _Invocation(self._client.stub, self.object_id, self._client)
1493
+ return _Invocation(self.client.stub, self.object_id, self.client)
1496
1494
 
1497
1495
  async def get(self, timeout: Optional[float] = None) -> ReturnType:
1498
1496
  """Get the result of the function call.
modal/functions.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._utils.async_utils
4
5
  import modal._utils.function_utils
5
6
  import modal.app
@@ -133,7 +134,7 @@ ReturnType = typing.TypeVar("ReturnType", covariant=True)
133
134
 
134
135
  OriginalReturnType = typing.TypeVar("OriginalReturnType", covariant=True)
135
136
 
136
- class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object._Object):
137
+ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal._object._Object):
137
138
  _info: typing.Optional[modal._utils.function_utils.FunctionInfo]
138
139
  _serve_mounts: frozenset[modal.mount._Mount]
139
140
  _app: typing.Optional[modal.app._App]
@@ -143,7 +144,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
143
144
  _is_method: bool
144
145
  _spec: typing.Optional[_FunctionSpec]
145
146
  _tag: str
146
- _raw_f: typing.Callable[..., typing.Any]
147
+ _raw_f: typing.Optional[typing.Callable[..., typing.Any]]
147
148
  _build_args: dict
148
149
  _is_generator: typing.Optional[bool]
149
150
  _cluster_size: typing.Optional[int]
@@ -197,7 +198,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
197
198
  _experimental_buffer_containers: typing.Optional[int] = None,
198
199
  _experimental_proxy_ip: typing.Optional[str] = None,
199
200
  _experimental_custom_scaling_factor: typing.Optional[float] = None,
200
- ) -> None: ...
201
+ ) -> _Function: ...
201
202
  def _bind_parameters(
202
203
  self,
203
204
  obj: modal.cls._Obj,
@@ -311,7 +312,7 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
311
312
  _is_method: bool
312
313
  _spec: typing.Optional[_FunctionSpec]
313
314
  _tag: str
314
- _raw_f: typing.Callable[..., typing.Any]
315
+ _raw_f: typing.Optional[typing.Callable[..., typing.Any]]
315
316
  _build_args: dict
316
317
  _is_generator: typing.Optional[bool]
317
318
  _cluster_size: typing.Optional[int]
@@ -366,7 +367,7 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
366
367
  _experimental_buffer_containers: typing.Optional[int] = None,
367
368
  _experimental_proxy_ip: typing.Optional[str] = None,
368
369
  _experimental_custom_scaling_factor: typing.Optional[float] = None,
369
- ) -> None: ...
370
+ ) -> Function: ...
370
371
  def _bind_parameters(
371
372
  self,
372
373
  obj: modal.cls.Obj,
@@ -539,7 +540,7 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
539
540
 
540
541
  for_each: __for_each_spec
541
542
 
542
- class _FunctionCall(typing.Generic[ReturnType], modal.object._Object):
543
+ class _FunctionCall(typing.Generic[ReturnType], modal._object._Object):
543
544
  _is_generator: bool
544
545
 
545
546
  def _invocation(self): ...
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
@@ -662,13 +662,16 @@ class _Image(_Object, type_prefix="im"):
662
662
  return obj
663
663
 
664
664
  def copy_mount(self, mount: _Mount, remote_path: Union[str, Path] = ".") -> "_Image":
665
- """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.
666
669
  Useful when files only available locally are required during the image
667
670
  build process.
668
671
 
669
672
  **Example**
670
673
 
671
- ```python
674
+ ```python notest
672
675
  static_images_dir = "./static"
673
676
  # place all static images in root of mount
674
677
  mount = modal.Mount.from_local_dir(static_images_dir, remote_path="/")
@@ -851,14 +854,17 @@ class _Image(_Object, type_prefix="im"):
851
854
  # Which follows dockerignore syntax.
852
855
  ignore: Union[Sequence[str], Callable[[Path], bool]] = [],
853
856
  ) -> "_Image":
854
- """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.
855
861
 
856
862
  This works in a similar way to [`COPY`](https://docs.docker.com/engine/reference/builder/#copy)
857
863
  works in a `Dockerfile`.
858
864
 
859
865
  **Usage:**
860
866
 
861
- ```python
867
+ ```python notest
862
868
  from pathlib import Path
863
869
  from modal import FilePatternMatcher
864
870
 
@@ -2047,11 +2053,11 @@ class _Image(_Object, type_prefix="im"):
2047
2053
  try:
2048
2054
  yield
2049
2055
  except Exception as exc:
2050
- if self.object_id is None:
2051
- # Might be initialized later
2056
+ if not self.is_hydrated:
2057
+ # Might be hydrated later
2052
2058
  self.inside_exceptions.append(exc)
2053
2059
  elif env_image_id == self.object_id:
2054
- # Image is already initialized (we can remove this case later
2060
+ # Image is already hydrated (we can remove this case later
2055
2061
  # when we don't hydrate objects so early)
2056
2062
  raise
2057
2063
  if not isinstance(exc, ImportError):
@@ -2066,9 +2072,9 @@ class _Image(_Object, type_prefix="im"):
2066
2072
  last_entry_id: str = ""
2067
2073
 
2068
2074
  request = api_pb2.ImageJoinStreamingRequest(
2069
- 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
2070
2076
  )
2071
- async for response in self._client.stub.ImageJoinStreaming.unary_stream(request):
2077
+ async for response in self.client.stub.ImageJoinStreaming.unary_stream(request):
2072
2078
  if response.result.status:
2073
2079
  return
2074
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
@@ -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]
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`.
modal/mount.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._resolver
4
5
  import modal._utils.blob_utils
5
6
  import modal.client
@@ -76,7 +77,7 @@ class _MountedPythonModule(_MountEntry):
76
77
 
77
78
  class NonLocalMountError(Exception): ...
78
79
 
79
- class _Mount(modal.object._Object):
80
+ class _Mount(modal._object._Object):
80
81
  _entries: typing.Optional[list[_MountEntry]]
81
82
  _deployment_name: typing.Optional[str]
82
83
  _namespace: typing.Optional[int]
@@ -12,6 +12,13 @@ from synchronicity.async_wrap import asynccontextmanager
12
12
  import modal
13
13
  from modal_proto import api_pb2
14
14
 
15
+ from ._object import (
16
+ EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
17
+ _get_environment_name,
18
+ _Object,
19
+ live_method,
20
+ live_method_gen,
21
+ )
15
22
  from ._resolver import Resolver
16
23
  from ._utils.async_utils import TaskContext, aclosing, async_map, sync_or_async_iter, synchronize_api
17
24
  from ._utils.blob_utils import LARGE_FILE_LIMIT, blob_iter, blob_upload_file
@@ -21,13 +28,6 @@ from ._utils.hash_utils import get_sha256_hex
21
28
  from ._utils.name_utils import check_object_name
22
29
  from .client import _Client
23
30
  from .exception import InvalidError
24
- from .object import (
25
- EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
26
- _get_environment_name,
27
- _Object,
28
- live_method,
29
- live_method_gen,
30
- )
31
31
  from .volume import FileEntry
32
32
 
33
33
  NETWORK_FILE_SYSTEM_PUT_FILE_CLIENT_TIMEOUT = (
@@ -1,4 +1,5 @@
1
1
  import collections.abc
2
+ import modal._object
2
3
  import modal.client
3
4
  import modal.object
4
5
  import modal.volume
@@ -12,7 +13,7 @@ def network_file_system_mount_protos(
12
13
  validated_network_file_systems: list[tuple[str, _NetworkFileSystem]], allow_cross_region_volumes: bool
13
14
  ) -> list[modal_proto.api_pb2.SharedVolumeMount]: ...
14
15
 
15
- class _NetworkFileSystem(modal.object._Object):
16
+ class _NetworkFileSystem(modal._object._Object):
16
17
  @staticmethod
17
18
  def from_name(
18
19
  name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False