modal 0.72.5__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 +3 -4
  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.5.dist-info → modal-0.72.48.dist-info}/METADATA +2 -2
  61. {modal-0.72.5.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.5.dist-info → modal-0.72.48.dist-info}/LICENSE +0 -0
  71. {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/WHEEL +0 -0
  72. {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/entry_points.txt +0 -0
  73. {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/top_level.txt +0 -0
@@ -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
@@ -47,7 +48,7 @@ class _NetworkFileSystem(modal.object._Object):
47
48
  self,
48
49
  remote_path: str,
49
50
  fp: typing.BinaryIO,
50
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
51
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
51
52
  ) -> int: ...
52
53
  def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
53
54
  def iterdir(self, path: str) -> collections.abc.AsyncIterator[modal.volume.FileEntry]: ...
@@ -55,17 +56,19 @@ class _NetworkFileSystem(modal.object._Object):
55
56
  self,
56
57
  local_path: typing.Union[pathlib.Path, str],
57
58
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
58
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
59
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
59
60
  ): ...
60
61
  async def add_local_dir(
61
62
  self,
62
63
  local_path: typing.Union[pathlib.Path, str],
63
64
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
64
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
65
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
65
66
  ): ...
66
67
  async def listdir(self, path: str) -> list[modal.volume.FileEntry]: ...
67
68
  async def remove_file(self, path: str, recursive=False): ...
68
69
 
70
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
71
+
69
72
  class NetworkFileSystem(modal.object.Object):
70
73
  def __init__(self, *args, **kwargs): ...
71
74
  @staticmethod
@@ -134,74 +137,74 @@ class NetworkFileSystem(modal.object.Object):
134
137
 
135
138
  delete: __delete_spec
136
139
 
137
- class __write_file_spec(typing_extensions.Protocol):
140
+ class __write_file_spec(typing_extensions.Protocol[SUPERSELF]):
138
141
  def __call__(
139
142
  self,
140
143
  remote_path: str,
141
144
  fp: typing.BinaryIO,
142
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
145
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
143
146
  ) -> int: ...
144
147
  async def aio(
145
148
  self,
146
149
  remote_path: str,
147
150
  fp: typing.BinaryIO,
148
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
151
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
149
152
  ) -> int: ...
150
153
 
151
- write_file: __write_file_spec
154
+ write_file: __write_file_spec[typing_extensions.Self]
152
155
 
153
- class __read_file_spec(typing_extensions.Protocol):
156
+ class __read_file_spec(typing_extensions.Protocol[SUPERSELF]):
154
157
  def __call__(self, path: str) -> typing.Iterator[bytes]: ...
155
158
  def aio(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
156
159
 
157
- read_file: __read_file_spec
160
+ read_file: __read_file_spec[typing_extensions.Self]
158
161
 
159
- class __iterdir_spec(typing_extensions.Protocol):
162
+ class __iterdir_spec(typing_extensions.Protocol[SUPERSELF]):
160
163
  def __call__(self, path: str) -> typing.Iterator[modal.volume.FileEntry]: ...
161
164
  def aio(self, path: str) -> collections.abc.AsyncIterator[modal.volume.FileEntry]: ...
162
165
 
163
- iterdir: __iterdir_spec
166
+ iterdir: __iterdir_spec[typing_extensions.Self]
164
167
 
165
- class __add_local_file_spec(typing_extensions.Protocol):
168
+ class __add_local_file_spec(typing_extensions.Protocol[SUPERSELF]):
166
169
  def __call__(
167
170
  self,
168
171
  local_path: typing.Union[pathlib.Path, str],
169
172
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
170
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
173
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
171
174
  ): ...
172
175
  async def aio(
173
176
  self,
174
177
  local_path: typing.Union[pathlib.Path, str],
175
178
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
176
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
179
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
177
180
  ): ...
178
181
 
179
- add_local_file: __add_local_file_spec
182
+ add_local_file: __add_local_file_spec[typing_extensions.Self]
180
183
 
181
- class __add_local_dir_spec(typing_extensions.Protocol):
184
+ class __add_local_dir_spec(typing_extensions.Protocol[SUPERSELF]):
182
185
  def __call__(
183
186
  self,
184
187
  local_path: typing.Union[pathlib.Path, str],
185
188
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
186
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
189
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
187
190
  ): ...
188
191
  async def aio(
189
192
  self,
190
193
  local_path: typing.Union[pathlib.Path, str],
191
194
  remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
192
- progress_cb: typing.Optional[typing.Callable[..., typing.Any]] = None,
195
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
193
196
  ): ...
194
197
 
195
- add_local_dir: __add_local_dir_spec
198
+ add_local_dir: __add_local_dir_spec[typing_extensions.Self]
196
199
 
197
- class __listdir_spec(typing_extensions.Protocol):
200
+ class __listdir_spec(typing_extensions.Protocol[SUPERSELF]):
198
201
  def __call__(self, path: str) -> list[modal.volume.FileEntry]: ...
199
202
  async def aio(self, path: str) -> list[modal.volume.FileEntry]: ...
200
203
 
201
- listdir: __listdir_spec
204
+ listdir: __listdir_spec[typing_extensions.Self]
202
205
 
203
- class __remove_file_spec(typing_extensions.Protocol):
206
+ class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
204
207
  def __call__(self, path: str, recursive=False): ...
205
208
  async def aio(self, path: str, recursive=False): ...
206
209
 
207
- remove_file: __remove_file_spec
210
+ remove_file: __remove_file_spec[typing_extensions.Self]
modal/object.py CHANGED
@@ -1,268 +1,5 @@
1
- # Copyright Modal Labs 2022
2
- import uuid
3
- from collections.abc import Awaitable, Hashable, Sequence
4
- from functools import wraps
5
- from typing import Callable, ClassVar, Optional, TypeVar
6
-
7
- from google.protobuf.message import Message
8
-
9
- from modal._utils.async_utils import aclosing
10
-
11
- from ._resolver import Resolver
1
+ # Copyright Modal Labs 2025
2
+ from ._object import _Object
12
3
  from ._utils.async_utils import synchronize_api
13
- from .client import _Client
14
- from .config import config, logger
15
- from .exception import ExecutionError, InvalidError
16
-
17
- O = TypeVar("O", bound="_Object")
18
-
19
- _BLOCKING_O = synchronize_api(O)
20
-
21
- EPHEMERAL_OBJECT_HEARTBEAT_SLEEP: int = 300
22
-
23
-
24
- def _get_environment_name(environment_name: Optional[str] = None, resolver: Optional[Resolver] = None) -> Optional[str]:
25
- if environment_name:
26
- return environment_name
27
- elif resolver and resolver.environment_name:
28
- return resolver.environment_name
29
- else:
30
- return config.get("environment")
31
-
32
-
33
- class _Object:
34
- _type_prefix: ClassVar[Optional[str]] = None
35
- _prefix_to_type: ClassVar[dict[str, type]] = {}
36
-
37
- # For constructors
38
- _load: Optional[Callable[[O, Resolver, Optional[str]], Awaitable[None]]]
39
- _preload: Optional[Callable[[O, Resolver, Optional[str]], Awaitable[None]]]
40
- _rep: str
41
- _is_another_app: bool
42
- _hydrate_lazily: bool
43
- _deps: Optional[Callable[..., list["_Object"]]]
44
- _deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None
45
-
46
- # For hydrated objects
47
- _object_id: str
48
- _client: _Client
49
- _is_hydrated: bool
50
- _is_rehydrated: bool
51
-
52
- @classmethod
53
- def __init_subclass__(cls, type_prefix: Optional[str] = None):
54
- super().__init_subclass__()
55
- if type_prefix is not None:
56
- cls._type_prefix = type_prefix
57
- cls._prefix_to_type[type_prefix] = cls
58
-
59
- def __init__(self, *args, **kwargs):
60
- raise InvalidError(f"Class {type(self).__name__} has no constructor. Use class constructor methods instead.")
61
-
62
- def _init(
63
- self,
64
- rep: str,
65
- load: Optional[Callable[[O, Resolver, Optional[str]], Awaitable[None]]] = None,
66
- is_another_app: bool = False,
67
- preload: Optional[Callable[[O, Resolver, Optional[str]], Awaitable[None]]] = None,
68
- hydrate_lazily: bool = False,
69
- deps: Optional[Callable[..., list["_Object"]]] = None,
70
- deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None,
71
- ):
72
- self._local_uuid = str(uuid.uuid4())
73
- self._load = load
74
- self._preload = preload
75
- self._rep = rep
76
- self._is_another_app = is_another_app
77
- self._hydrate_lazily = hydrate_lazily
78
- self._deps = deps
79
- self._deduplication_key = deduplication_key
80
-
81
- self._object_id = None
82
- self._client = None
83
- self._is_hydrated = False
84
- self._is_rehydrated = False
85
-
86
- self._initialize_from_empty()
87
-
88
- def _unhydrate(self):
89
- self._object_id = None
90
- self._client = None
91
- self._is_hydrated = False
92
-
93
- def _initialize_from_empty(self):
94
- # default implementation, can be overriden in subclasses
95
- pass
96
-
97
- def _initialize_from_other(self, other):
98
- # default implementation, can be overriden in subclasses
99
- self._object_id = other._object_id
100
- self._is_hydrated = other._is_hydrated
101
- self._client = other._client
102
-
103
- def _hydrate(self, object_id: str, client: _Client, metadata: Optional[Message]):
104
- assert isinstance(object_id, str)
105
- if not object_id.startswith(self._type_prefix):
106
- raise ExecutionError(
107
- f"Can not hydrate {type(self)}:"
108
- f" it has type prefix {self._type_prefix}"
109
- f" but the object_id starts with {object_id[:3]}"
110
- )
111
- self._object_id = object_id
112
- self._client = client
113
- self._hydrate_metadata(metadata)
114
- self._is_hydrated = True
115
-
116
- def _hydrate_metadata(self, metadata: Optional[Message]):
117
- # override this is subclasses that need additional data (other than an object_id) for a functioning Handle
118
- pass
119
-
120
- def _get_metadata(self) -> Optional[Message]:
121
- # return the necessary metadata from this handle to be able to re-hydrate in another context if one is needed
122
- # used to provide a handle's handle_metadata for serializing/pickling a live handle
123
- # the object_id is already provided by other means
124
- return
125
-
126
- def _validate_is_hydrated(self: O):
127
- if not self._is_hydrated:
128
- object_type = self.__class__.__name__.strip("_")
129
- if hasattr(self, "_app") and getattr(self._app, "_running_app", "") is None:
130
- # The most common cause of this error: e.g., user called a Function without using App.run()
131
- reason = ", because the App it is defined on is not running"
132
- else:
133
- # Technically possible, but with an ambiguous cause.
134
- reason = ""
135
- raise ExecutionError(
136
- f"{object_type} has not been hydrated with the metadata it needs to run on Modal{reason}."
137
- )
138
-
139
- def clone(self: O) -> O:
140
- """mdmd:hidden Clone a given hydrated object."""
141
-
142
- # Object to clone must already be hydrated, otherwise from_loader is more suitable.
143
- self._validate_is_hydrated()
144
- obj = type(self).__new__(type(self))
145
- obj._initialize_from_other(self)
146
- return obj
147
-
148
- @classmethod
149
- def _from_loader(
150
- cls,
151
- load: Callable[[O, Resolver, Optional[str]], Awaitable[None]],
152
- rep: str,
153
- is_another_app: bool = False,
154
- preload: Optional[Callable[[O, Resolver, Optional[str]], Awaitable[None]]] = None,
155
- hydrate_lazily: bool = False,
156
- deps: Optional[Callable[..., Sequence["_Object"]]] = None,
157
- deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None,
158
- ):
159
- # TODO(erikbern): flip the order of the two first arguments
160
- obj = _Object.__new__(cls)
161
- obj._init(rep, load, is_another_app, preload, hydrate_lazily, deps, deduplication_key)
162
- return obj
163
-
164
- @classmethod
165
- def _get_type_from_id(cls: type[O], object_id: str) -> type[O]:
166
- parts = object_id.split("-")
167
- if len(parts) != 2:
168
- raise InvalidError(f"Object id {object_id} has no dash in it")
169
- prefix = parts[0]
170
- if prefix not in cls._prefix_to_type:
171
- raise InvalidError(f"Object prefix {prefix} does not correspond to a type")
172
- return cls._prefix_to_type[prefix]
173
-
174
- @classmethod
175
- def _is_id_type(cls: type[O], object_id) -> bool:
176
- return cls._get_type_from_id(object_id) == cls
177
-
178
- @classmethod
179
- def _new_hydrated(
180
- cls: type[O], object_id: str, client: _Client, handle_metadata: Optional[Message], is_another_app: bool = False
181
- ) -> O:
182
- if cls._type_prefix is not None:
183
- # This is called directly on a subclass, e.g. Secret.from_id
184
- if not object_id.startswith(cls._type_prefix + "-"):
185
- raise InvalidError(f"Object {object_id} does not start with {cls._type_prefix}")
186
- obj_cls = cls
187
- else:
188
- # This is called on the base class, e.g. Handle.from_id
189
- obj_cls = cls._get_type_from_id(object_id)
190
-
191
- # Instantiate provider
192
- obj = _Object.__new__(obj_cls)
193
- rep = f"Object({object_id})" # TODO(erikbern): dumb
194
- obj._init(rep, is_another_app=is_another_app)
195
- obj._hydrate(object_id, client, handle_metadata)
196
-
197
- return obj
198
-
199
- def _hydrate_from_other(self, other: O):
200
- self._hydrate(other._object_id, other._client, other._get_metadata())
201
-
202
- def __repr__(self):
203
- return self._rep
204
-
205
- @property
206
- def local_uuid(self):
207
- """mdmd:hidden"""
208
- return self._local_uuid
209
-
210
- @property
211
- def object_id(self) -> str:
212
- """mdmd:hidden"""
213
- return self._object_id
214
-
215
- @property
216
- def is_hydrated(self) -> bool:
217
- """mdmd:hidden"""
218
- return self._is_hydrated
219
-
220
- @property
221
- def deps(self) -> Callable[..., list["_Object"]]:
222
- """mdmd:hidden"""
223
- return self._deps if self._deps is not None else lambda: []
224
-
225
- async def resolve(self, client: Optional[_Client] = None):
226
- """mdmd:hidden"""
227
- if self._is_hydrated:
228
- # memory snapshots capture references which must be rehydrated
229
- # on restore to handle staleness.
230
- if self._client._snapshotted and not self._is_rehydrated:
231
- logger.debug(f"rehydrating {self} after snapshot")
232
- self._is_hydrated = False # un-hydrate and re-resolve
233
- c = client if client is not None else await _Client.from_env()
234
- resolver = Resolver(c)
235
- await resolver.load(self)
236
- self._is_rehydrated = True
237
- logger.debug(f"rehydrated {self} with client {id(c)}")
238
- return
239
- elif not self._hydrate_lazily:
240
- self._validate_is_hydrated()
241
- else:
242
- # TODO: this client and/or resolver can't be changed by a caller to X.from_name()
243
- c = client if client is not None else await _Client.from_env()
244
- resolver = Resolver(c)
245
- await resolver.load(self)
246
-
247
4
 
248
5
  Object = synchronize_api(_Object, target_module=__name__)
249
-
250
-
251
- def live_method(method):
252
- @wraps(method)
253
- async def wrapped(self, *args, **kwargs):
254
- await self.resolve()
255
- return await method(self, *args, **kwargs)
256
-
257
- return wrapped
258
-
259
-
260
- def live_method_gen(method):
261
- @wraps(method)
262
- async def wrapped(self, *args, **kwargs):
263
- await self.resolve()
264
- async with aclosing(method(self, *args, **kwargs)) as stream:
265
- async for item in stream:
266
- yield item
267
-
268
- return wrapped
modal/object.pyi CHANGED
@@ -5,117 +5,30 @@ import modal.client
5
5
  import typing
6
6
  import typing_extensions
7
7
 
8
- O = typing.TypeVar("O", bound="_Object")
9
-
10
- _BLOCKING_O = typing.TypeVar("_BLOCKING_O", bound="Object")
11
-
12
- def _get_environment_name(
13
- environment_name: typing.Optional[str] = None, resolver: typing.Optional[modal._resolver.Resolver] = None
14
- ) -> typing.Optional[str]: ...
15
-
16
- class _Object:
17
- _type_prefix: typing.ClassVar[typing.Optional[str]]
18
- _prefix_to_type: typing.ClassVar[dict[str, type]]
19
- _load: typing.Optional[
20
- typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
21
- ]
22
- _preload: typing.Optional[
23
- typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
24
- ]
25
- _rep: str
26
- _is_another_app: bool
27
- _hydrate_lazily: bool
28
- _deps: typing.Optional[typing.Callable[..., list[_Object]]]
29
- _deduplication_key: typing.Optional[typing.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]]
30
- _object_id: str
31
- _client: modal.client._Client
32
- _is_hydrated: bool
33
- _is_rehydrated: bool
34
-
35
- @classmethod
36
- def __init_subclass__(cls, type_prefix: typing.Optional[str] = None): ...
37
- def __init__(self, *args, **kwargs): ...
38
- def _init(
39
- self,
40
- rep: str,
41
- load: typing.Optional[
42
- typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
43
- ] = None,
44
- is_another_app: bool = False,
45
- preload: typing.Optional[
46
- typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
47
- ] = None,
48
- hydrate_lazily: bool = False,
49
- deps: typing.Optional[typing.Callable[..., list[_Object]]] = None,
50
- deduplication_key: typing.Optional[
51
- typing.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
52
- ] = None,
53
- ): ...
54
- def _unhydrate(self): ...
55
- def _initialize_from_empty(self): ...
56
- def _initialize_from_other(self, other): ...
57
- def _hydrate(
58
- self, object_id: str, client: modal.client._Client, metadata: typing.Optional[google.protobuf.message.Message]
59
- ): ...
60
- def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
61
- def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
62
- def _validate_is_hydrated(self: O): ...
63
- def clone(self: O) -> O: ...
64
- @classmethod
65
- def _from_loader(
66
- cls,
67
- load: typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]],
68
- rep: str,
69
- is_another_app: bool = False,
70
- preload: typing.Optional[
71
- typing.Callable[[O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
72
- ] = None,
73
- hydrate_lazily: bool = False,
74
- deps: typing.Optional[typing.Callable[..., collections.abc.Sequence[_Object]]] = None,
75
- deduplication_key: typing.Optional[
76
- typing.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
77
- ] = None,
78
- ): ...
79
- @classmethod
80
- def _get_type_from_id(cls: type[O], object_id: str) -> type[O]: ...
81
- @classmethod
82
- def _is_id_type(cls: type[O], object_id) -> bool: ...
83
- @classmethod
84
- def _new_hydrated(
85
- cls: type[O],
86
- object_id: str,
87
- client: modal.client._Client,
88
- handle_metadata: typing.Optional[google.protobuf.message.Message],
89
- is_another_app: bool = False,
90
- ) -> O: ...
91
- def _hydrate_from_other(self, other: O): ...
92
- def __repr__(self): ...
93
- @property
94
- def local_uuid(self): ...
95
- @property
96
- def object_id(self) -> str: ...
97
- @property
98
- def is_hydrated(self) -> bool: ...
99
- @property
100
- def deps(self) -> typing.Callable[..., list[_Object]]: ...
101
- async def resolve(self, client: typing.Optional[modal.client._Client] = None): ...
8
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
102
9
 
103
10
  class Object:
104
11
  _type_prefix: typing.ClassVar[typing.Optional[str]]
105
12
  _prefix_to_type: typing.ClassVar[dict[str, type]]
106
13
  _load: typing.Optional[
107
- typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
14
+ collections.abc.Callable[
15
+ [typing_extensions.Self, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
16
+ ]
108
17
  ]
109
18
  _preload: typing.Optional[
110
- typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]]
19
+ collections.abc.Callable[
20
+ [typing_extensions.Self, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
21
+ ]
111
22
  ]
112
23
  _rep: str
113
24
  _is_another_app: bool
114
25
  _hydrate_lazily: bool
115
- _deps: typing.Optional[typing.Callable[..., list[Object]]]
116
- _deduplication_key: typing.Optional[typing.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]]
117
- _object_id: str
118
- _client: modal.client.Client
26
+ _deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]]
27
+ _deduplication_key: typing.Optional[
28
+ collections.abc.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
29
+ ]
30
+ _object_id: typing.Optional[str]
31
+ _client: typing.Optional[modal.client.Client]
119
32
  _is_hydrated: bool
120
33
  _is_rehydrated: bool
121
34
 
@@ -123,43 +36,43 @@ class Object:
123
36
  @classmethod
124
37
  def __init_subclass__(cls, type_prefix: typing.Optional[str] = None): ...
125
38
 
126
- class ___init_spec(typing_extensions.Protocol):
39
+ class ___init_spec(typing_extensions.Protocol[SUPERSELF]):
127
40
  def __call__(
128
41
  self,
129
42
  rep: str,
130
43
  load: typing.Optional[
131
- typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], None]
44
+ collections.abc.Callable[[SUPERSELF, modal._resolver.Resolver, typing.Optional[str]], None]
132
45
  ] = None,
133
46
  is_another_app: bool = False,
134
47
  preload: typing.Optional[
135
- typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], None]
48
+ collections.abc.Callable[[SUPERSELF, modal._resolver.Resolver, typing.Optional[str]], None]
136
49
  ] = None,
137
50
  hydrate_lazily: bool = False,
138
- deps: typing.Optional[typing.Callable[..., list[Object]]] = None,
139
- deduplication_key: typing.Optional[typing.Callable[[], collections.abc.Hashable]] = None,
51
+ deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
52
+ deduplication_key: typing.Optional[collections.abc.Callable[[], collections.abc.Hashable]] = None,
140
53
  ): ...
141
54
  def aio(
142
55
  self,
143
56
  rep: str,
144
57
  load: typing.Optional[
145
- typing.Callable[
146
- [_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
58
+ collections.abc.Callable[
59
+ [SUPERSELF, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
147
60
  ]
148
61
  ] = None,
149
62
  is_another_app: bool = False,
150
63
  preload: typing.Optional[
151
- typing.Callable[
152
- [_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
64
+ collections.abc.Callable[
65
+ [SUPERSELF, modal._resolver.Resolver, typing.Optional[str]], collections.abc.Awaitable[None]
153
66
  ]
154
67
  ] = None,
155
68
  hydrate_lazily: bool = False,
156
- deps: typing.Optional[typing.Callable[..., list[Object]]] = None,
69
+ deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
157
70
  deduplication_key: typing.Optional[
158
- typing.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
71
+ collections.abc.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
159
72
  ] = None,
160
73
  ): ...
161
74
 
162
- _init: ___init_spec
75
+ _init: ___init_spec[typing_extensions.Self]
163
76
 
164
77
  def _unhydrate(self): ...
165
78
  def _initialize_from_empty(self): ...
@@ -169,51 +82,54 @@ class Object:
169
82
  ): ...
170
83
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
171
84
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
172
- def _validate_is_hydrated(self: _BLOCKING_O): ...
173
- def clone(self: _BLOCKING_O) -> _BLOCKING_O: ...
85
+ def _validate_is_hydrated(self): ...
86
+ def clone(self) -> typing_extensions.Self: ...
174
87
  @classmethod
175
88
  def _from_loader(
176
89
  cls,
177
- load: typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], None],
90
+ load: collections.abc.Callable[[typing_extensions.Self, modal._resolver.Resolver, typing.Optional[str]], None],
178
91
  rep: str,
179
92
  is_another_app: bool = False,
180
93
  preload: typing.Optional[
181
- typing.Callable[[_BLOCKING_O, modal._resolver.Resolver, typing.Optional[str]], None]
94
+ collections.abc.Callable[[typing_extensions.Self, modal._resolver.Resolver, typing.Optional[str]], None]
182
95
  ] = None,
183
96
  hydrate_lazily: bool = False,
184
- deps: typing.Optional[typing.Callable[..., collections.abc.Sequence[Object]]] = None,
185
- deduplication_key: typing.Optional[typing.Callable[[], collections.abc.Hashable]] = None,
97
+ deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
98
+ deduplication_key: typing.Optional[collections.abc.Callable[[], collections.abc.Hashable]] = None,
186
99
  ): ...
100
+ @staticmethod
101
+ def _get_type_from_id(object_id: str) -> type[Object]: ...
187
102
  @classmethod
188
- def _get_type_from_id(cls: type[_BLOCKING_O], object_id: str) -> type[_BLOCKING_O]: ...
189
- @classmethod
190
- def _is_id_type(cls: type[_BLOCKING_O], object_id) -> bool: ...
103
+ def _is_id_type(cls, object_id) -> bool: ...
191
104
  @classmethod
192
105
  def _new_hydrated(
193
- cls: type[_BLOCKING_O],
106
+ cls,
194
107
  object_id: str,
195
108
  client: modal.client.Client,
196
109
  handle_metadata: typing.Optional[google.protobuf.message.Message],
197
110
  is_another_app: bool = False,
198
- ) -> _BLOCKING_O: ...
199
- def _hydrate_from_other(self, other: _BLOCKING_O): ...
111
+ ) -> typing_extensions.Self: ...
112
+ def _hydrate_from_other(self, other: typing_extensions.Self): ...
200
113
  def __repr__(self): ...
201
114
  @property
202
115
  def local_uuid(self): ...
203
116
  @property
204
117
  def object_id(self) -> str: ...
205
118
  @property
119
+ def client(self) -> modal.client.Client: ...
120
+ @property
206
121
  def is_hydrated(self) -> bool: ...
207
122
  @property
208
- def deps(self) -> typing.Callable[..., list[Object]]: ...
123
+ def deps(self) -> collections.abc.Callable[..., collections.abc.Sequence[Object]]: ...
209
124
 
210
- class __resolve_spec(typing_extensions.Protocol):
125
+ class __resolve_spec(typing_extensions.Protocol[SUPERSELF]):
211
126
  def __call__(self, client: typing.Optional[modal.client.Client] = None): ...
212
127
  async def aio(self, client: typing.Optional[modal.client.Client] = None): ...
213
128
 
214
- resolve: __resolve_spec
129
+ resolve: __resolve_spec[typing_extensions.Self]
215
130
 
216
- def live_method(method): ...
217
- def live_method_gen(method): ...
131
+ class __hydrate_spec(typing_extensions.Protocol[SUPERSELF]):
132
+ def __call__(self, client: typing.Optional[modal.client.Client] = None) -> SUPERSELF: ...
133
+ async def aio(self, client: typing.Optional[modal.client.Client] = None) -> SUPERSELF: ...
218
134
 
219
- EPHEMERAL_OBJECT_HEARTBEAT_SLEEP: int
135
+ hydrate: __hydrate_spec[typing_extensions.Self]
modal/parallel_map.py CHANGED
@@ -151,8 +151,8 @@ async def _map_invocation(
151
151
  if err.status != Status.RESOURCE_EXHAUSTED:
152
152
  raise err
153
153
  logger.warning(
154
- "Warning: map progress is limited. Common bottlenecks "
155
- "include slow iteration over results, or function backlogs."
154
+ f"Warning: map progress for function {function._function_name} is limited."
155
+ " Common bottlenecks include slow iteration over results, or function backlogs."
156
156
  )
157
157
 
158
158
  count_update()