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.
- modal/_container_entrypoint.py +5 -10
- modal/_object.py +297 -0
- modal/_resolver.py +7 -5
- modal/_runtime/container_io_manager.py +0 -11
- modal/_runtime/user_code_imports.py +7 -7
- modal/_serialization.py +4 -3
- modal/_tunnel.py +1 -1
- modal/app.py +14 -61
- modal/app.pyi +25 -25
- modal/cli/app.py +3 -2
- modal/cli/container.py +1 -1
- modal/cli/import_refs.py +185 -113
- modal/cli/launch.py +10 -5
- modal/cli/programs/run_jupyter.py +2 -2
- modal/cli/programs/vscode.py +3 -3
- modal/cli/run.py +134 -68
- modal/client.py +1 -0
- modal/client.pyi +18 -14
- modal/cloud_bucket_mount.py +4 -0
- modal/cloud_bucket_mount.pyi +4 -0
- modal/cls.py +33 -5
- modal/cls.pyi +20 -5
- modal/container_process.pyi +8 -6
- modal/dict.py +1 -1
- modal/dict.pyi +32 -29
- modal/environments.py +1 -1
- modal/environments.pyi +2 -1
- modal/experimental.py +47 -11
- modal/experimental.pyi +29 -0
- modal/file_io.pyi +30 -28
- modal/file_pattern_matcher.py +3 -4
- modal/functions.py +31 -23
- modal/functions.pyi +57 -50
- modal/gpu.py +19 -26
- modal/image.py +47 -19
- modal/image.pyi +28 -21
- modal/io_streams.pyi +14 -12
- modal/mount.py +14 -5
- modal/mount.pyi +28 -25
- modal/network_file_system.py +7 -7
- modal/network_file_system.pyi +27 -24
- modal/object.py +2 -265
- modal/object.pyi +46 -130
- modal/parallel_map.py +2 -2
- modal/parallel_map.pyi +10 -7
- modal/partial_function.py +22 -3
- modal/partial_function.pyi +45 -27
- modal/proxy.py +1 -1
- modal/proxy.pyi +2 -1
- modal/queue.py +1 -1
- modal/queue.pyi +26 -23
- modal/runner.py +14 -3
- modal/sandbox.py +11 -7
- modal/sandbox.pyi +30 -27
- modal/secret.py +1 -1
- modal/secret.pyi +2 -1
- modal/token_flow.pyi +6 -4
- modal/volume.py +1 -1
- modal/volume.pyi +36 -33
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/METADATA +2 -2
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/RECORD +73 -71
- modal_proto/api.proto +151 -4
- modal_proto/api_grpc.py +113 -0
- modal_proto/api_pb2.py +998 -795
- modal_proto/api_pb2.pyi +430 -11
- modal_proto/api_pb2_grpc.py +233 -1
- modal_proto/api_pb2_grpc.pyi +75 -3
- modal_proto/modal_api_grpc.py +7 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/LICENSE +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/WHEEL +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/entry_points.txt +0 -0
- {modal-0.72.5.dist-info → modal-0.72.48.dist-info}/top_level.txt +0 -0
modal/network_file_system.pyi
CHANGED
@@ -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.
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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
|
2
|
-
import
|
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
|
-
|
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
|
-
|
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
|
-
|
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[
|
116
|
-
_deduplication_key: typing.Optional[
|
117
|
-
|
118
|
-
|
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
|
-
|
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
|
-
|
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[
|
139
|
-
deduplication_key: typing.Optional[
|
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
|
-
|
146
|
-
[
|
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
|
-
|
152
|
-
[
|
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[
|
69
|
+
deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
|
157
70
|
deduplication_key: typing.Optional[
|
158
|
-
|
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
|
173
|
-
def clone(self
|
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:
|
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
|
-
|
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[
|
185
|
-
deduplication_key: typing.Optional[
|
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
|
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
|
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
|
-
) ->
|
199
|
-
def _hydrate_from_other(self, other:
|
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) ->
|
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
|
-
|
217
|
-
def
|
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
|
-
|
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.
|
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()
|